From 58e7c47c3162567317a8132bf2ff2179c33ac05d Mon Sep 17 00:00:00 2001 From: Empireo <32214977+Empireo@users.noreply.github.com> Date: Wed, 13 Oct 2021 18:02:48 +0800 Subject: [PATCH 01/16] feat: update swiper to be same with Swiper.js 6.8.4 (#402) * feat: update swiper to be same with Swiper.js 6.8.4 fix: event handler can't update * docs: changelog * docs: annotation for `get-changed-params` * chore: version * WIP: modify engineer config * Revert "WIP: modify engineer config" This reverts commit 276db2a6c29e8dc5ea0072535cd28b4b0652614d. Co-authored-by: wjq990112 <1163585385@qq.com> --- packages/rax-swiper/CHANGELOG.md | 6 + packages/rax-swiper/package.json | 6 +- packages/rax-swiper/src/get-changed-params.js | 30 +- packages/rax-swiper/src/get-children.js | 43 +- packages/rax-swiper/src/get-params.js | 10 +- packages/rax-swiper/src/init-swiper.js | 31 +- packages/rax-swiper/src/loop.js | 16 +- packages/rax-swiper/src/params-list.js | 7 + packages/rax-swiper/src/swiper-slide.js | 13 +- packages/rax-swiper/src/swiper.css | 1228 ++++++++++------- packages/rax-swiper/src/swiper.js | 135 +- packages/rax-swiper/src/update-swiper.js | 59 +- packages/rax-swiper/src/utils.js | 27 +- packages/rax-swiper/src/virtual.js | 11 +- 14 files changed, 982 insertions(+), 640 deletions(-) diff --git a/packages/rax-swiper/CHANGELOG.md b/packages/rax-swiper/CHANGELOG.md index c575972e..64907837 100644 --- a/packages/rax-swiper/CHANGELOG.md +++ b/packages/rax-swiper/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.1.5 + +- [chore] update swiper dependency version to 6.8.4 +- [feat] keep the same with Swiper.js 6.8.4 +- [fix] event handler can't update + ## 0.1.4 - [chore] update swiper version to 6.7.1 diff --git a/packages/rax-swiper/package.json b/packages/rax-swiper/package.json index ebc858ef..074281f6 100644 --- a/packages/rax-swiper/package.json +++ b/packages/rax-swiper/package.json @@ -1,7 +1,7 @@ { "name": "rax-swiper", "author": "rax", - "version": "0.1.4", + "version": "0.1.5", "description": "Swiper component for Rax.", "license": "BSD-3-Clause", "main": "lib/index.js", @@ -30,7 +30,7 @@ "rax-children": "^1.0.0", "rax-clone-element": "^1.0.0", "rax-view": "^2.0.0", - "swiper": "^6.7.1", + "swiper": "^6.8.4", "universal-env": "^3.2.0" }, "peerDependencies": { @@ -44,4 +44,4 @@ "rax-test-renderer": "^1.0.0", "typescript": "^3.7.5" } -} \ No newline at end of file +} diff --git a/packages/rax-swiper/src/get-changed-params.js b/packages/rax-swiper/src/get-changed-params.js index 5cd7bba0..d1cb6dc7 100644 --- a/packages/rax-swiper/src/get-changed-params.js +++ b/packages/rax-swiper/src/get-changed-params.js @@ -1,16 +1,38 @@ import { paramsList } from './params-list'; +import { isObject } from './utils'; function getChangedParams(swiperParams, oldParams, children, oldChildren) { const keys = []; if (!oldParams) return keys; + const addKey = (key) => { + if (keys.indexOf(key) < 0) keys.push(key); + }; const oldChildrenKeys = oldChildren.map((child) => child.key); const childrenKeys = children.map((child) => child.key); - if (oldChildrenKeys.join('') !== childrenKeys.join('')) keys.push('children'); - if (oldChildren.length !== children.length) keys.push('children'); + if (oldChildrenKeys.join('') !== childrenKeys.join('')) addKey('children'); + if (oldChildren.length !== children.length) addKey('children'); const watchParams = paramsList.filter((key) => key[0] === '_').map((key) => key.replace(/_/, '')); watchParams.forEach((key) => { - if (key in swiperParams && key in oldParams && swiperParams[key] !== oldParams[key]) { - keys.push(key); + if (key in swiperParams && key in oldParams) { + // deep watch for changed params + if (isObject(swiperParams[key]) && isObject(oldParams[key])) { + const newKeys = Object.keys(swiperParams[key]); + const oldKeys = Object.keys(oldParams[key]); + if (newKeys.length !== oldKeys.length) { + addKey(key); + } else { + newKeys.forEach((newKey) => { + if (swiperParams[key][newKey] !== oldParams[key][newKey]) { + addKey(key); + } + }); + oldKeys.forEach((oldKey) => { + if (swiperParams[key][oldKey] !== oldParams[key][oldKey]) addKey(key); + }); + } + } else if (swiperParams[key] !== oldParams[key]) { + addKey(key); + } } }); return keys; diff --git a/packages/rax-swiper/src/get-children.js b/packages/rax-swiper/src/get-children.js index 49bf8857..396ce1c0 100644 --- a/packages/rax-swiper/src/get-children.js +++ b/packages/rax-swiper/src/get-children.js @@ -1,7 +1,18 @@ -import { Fragment } from 'rax'; import Children from 'rax-children'; -function getChildren(children) { +function processChildren(c) { + const slides = []; + Children.toArray(c).forEach((child) => { + if (child.type && child.type.displayName === 'SwiperSlide') { + slides.push(child); + } else if (child.props && child.props.children) { + processChildren(child.props.children).forEach((slide) => slides.push(slide)); + } + }); + return slides; +} + +function getChildren(c) { const slides = []; const slots = { @@ -10,22 +21,24 @@ function getChildren(children) { 'wrapper-start': [], 'wrapper-end': [], }; - function processChildren(c) { - Children.toArray(c).forEach((child) => { - if (child.type === Fragment && child.props.children) { - processChildren(child.props.children); - return; - } - if (child.type && child.type.displayName === 'SwiperSlide') { - slides.push(child); - } else if (child.props && child.props.slot && slots[child.props.slot]) { - slots[child.props.slot].push(child); + + Children.toArray(c).forEach((child) => { + if (child.type && child.type.displayName === 'SwiperSlide') { + slides.push(child); + } else if (child.props && child.props.slot && slots[child.props.slot]) { + slots[child.props.slot].push(child); + } else if (child.props && child.props.children) { + const foundSlides = processChildren(child.props.children); + if (foundSlides.length > 0) { + foundSlides.forEach((slide) => slides.push(slide)); } else { slots['container-end'].push(child); } - }); - } - processChildren(children); + } else { + slots['container-end'].push(child); + } + }); + return { slides, slots }; } diff --git a/packages/rax-swiper/src/get-params.js b/packages/rax-swiper/src/get-params.js index 171b07c0..c9e8dc10 100644 --- a/packages/rax-swiper/src/get-params.js +++ b/packages/rax-swiper/src/get-params.js @@ -7,10 +7,12 @@ function getParams(obj = {}) { const params = { on: {}, }; + const events = {}; const passedParams = {}; extend(params, Swiper.defaults); extend(params, Swiper.extendedDefaults); params._emitClasses = true; + params.init = false; const rest = {}; const allowedParams = paramsList.map((key) => key.replace(/_/, '')); @@ -26,13 +28,17 @@ function getParams(obj = {}) { passedParams[key] = obj[key]; } } else if (key.search(/on[A-Z]/) === 0 && typeof obj[key] === 'function') { - params.on[`${key[2].toLowerCase()}${key.substr(3)}`] = obj[key]; + events[`${key[2].toLowerCase()}${key.substr(3)}`] = obj[key]; } else { rest[key] = obj[key]; } }); + ['navigation', 'pagination', 'scrollbar'].forEach((key) => { + if (params[key] === true) params[key] = {}; + if (params[key] === false) delete params[key]; + }); - return { params, passedParams, rest }; + return { params, passedParams, rest, events }; } export { getParams }; diff --git a/packages/rax-swiper/src/init-swiper.js b/packages/rax-swiper/src/init-swiper.js index b5a6c57f..6b639db3 100644 --- a/packages/rax-swiper/src/init-swiper.js +++ b/packages/rax-swiper/src/init-swiper.js @@ -5,27 +5,26 @@ import { needsNavigation, needsPagination, needsScrollbar } from './utils'; import './swiper.css'; Swiper.use([Autoplay, Pagination]); -function initSwiper({ el, nextEl, prevEl, paginationEl, scrollbarEl }, swiperParams) { +function initSwiper(swiperParams) { + return new Swiper(swiperParams); +} + +function mountSwiper({ el, nextEl, prevEl, paginationEl, scrollbarEl, swiper }, swiperParams) { if (needsNavigation(swiperParams) && nextEl && prevEl) { - if (swiperParams.navigation === true) { - swiperParams.navigation = {}; - } - swiperParams.navigation.nextEl = nextEl; - swiperParams.navigation.prevEl = prevEl; + swiper.params.navigation.nextEl = nextEl; + swiper.originalParams.navigation.nextEl = nextEl; + swiper.params.navigation.prevEl = prevEl; + swiper.originalParams.navigation.prevEl = prevEl; } if (needsPagination(swiperParams) && paginationEl) { - if (swiperParams.pagination === true) { - swiperParams.pagination = {}; - } - swiperParams.pagination.el = paginationEl; + swiper.params.pagination.el = paginationEl; + swiper.originalParams.pagination.el = paginationEl; } if (needsScrollbar(swiperParams) && scrollbarEl) { - if (swiperParams.scrollbar === true) { - swiperParams.scrollbar = {}; - } - swiperParams.scrollbar.el = scrollbarEl; + swiper.params.scrollbar.el = scrollbarEl; + swiper.originalParams.scrollbar.el = scrollbarEl; } - return new Swiper(el, swiperParams); + swiper.init(el); } -export { initSwiper }; +export { initSwiper, mountSwiper }; diff --git a/packages/rax-swiper/src/loop.js b/packages/rax-swiper/src/loop.js index b3ed7ad3..d00a8dfe 100644 --- a/packages/rax-swiper/src/loop.js +++ b/packages/rax-swiper/src/loop.js @@ -1,10 +1,18 @@ import { createElement } from 'rax'; import cloneElement from 'rax-clone-element'; +import Swiper from 'swiper'; function calcLoopedSlides(slides, swiperParams) { - let loopedSlides = Math.ceil( - parseFloat(swiperParams.loopedSlides || swiperParams.slidesPerView, 10), - ); + let slidesPerViewParams = swiperParams.slidesPerView; + if (swiperParams.breakpoints) { + const breakpoint = Swiper.prototype.getBreakpoint(swiperParams.breakpoints); + const breakpointOnlyParams = + breakpoint in swiperParams.breakpoints ? swiperParams.breakpoints[breakpoint] : undefined; + if (breakpointOnlyParams && breakpointOnlyParams.slidesPerView) { + slidesPerViewParams = breakpointOnlyParams.slidesPerView; + } + } + let loopedSlides = Math.ceil(parseFloat(swiperParams.loopedSlides || slidesPerViewParams, 10)); loopedSlides += swiperParams.loopAdditionalSlides; @@ -28,7 +36,7 @@ function renderLoop(swiper, slides, swiperParams) { if (swiperParams.loopFillGroupWithBlank) { const blankSlidesNum = - swiperParams.slidesPerGroup - modifiedSlides.length % swiperParams.slidesPerGroup; + swiperParams.slidesPerGroup - (modifiedSlides.length % swiperParams.slidesPerGroup); if (blankSlidesNum !== swiperParams.slidesPerGroup) { for (let i = 0; i < blankSlidesNum; i += 1) { const blankSlide = ( diff --git a/packages/rax-swiper/src/params-list.js b/packages/rax-swiper/src/params-list.js index ed04fd05..1b55b605 100644 --- a/packages/rax-swiper/src/params-list.js +++ b/packages/rax-swiper/src/params-list.js @@ -7,6 +7,10 @@ const paramsList = [ '_speed', 'cssMode', 'updateOnWindowResize', + 'resizeObserver', + 'nested', + 'focusableElements', + '_enabled', '_width', '_height', 'preventInteractionOnTransition', @@ -91,6 +95,9 @@ const paramsList = [ 'slideDuplicatePrevClass', 'wrapperClass', 'runCallbacksOnInit', + 'observer', + 'observeParents', + 'observeSlideChildren', // modules 'a11y', diff --git a/packages/rax-swiper/src/swiper-slide.js b/packages/rax-swiper/src/swiper-slide.js index 00e78b48..e6c90450 100644 --- a/packages/rax-swiper/src/swiper-slide.js +++ b/packages/rax-swiper/src/swiper-slide.js @@ -3,7 +3,10 @@ import { uniqueClasses } from './utils'; import { useIsomorphicLayoutEffect } from './use-isomorphic-layout-effect'; const SwiperSlide = forwardRef( - ({ tag: Tag = 'div', children, className = '', swiper, zoom, ...rest } = {}, externalRef) => { + ( + { tag: Tag = 'div', children, className = '', swiper, zoom, virtualIndex, ...rest } = {}, + externalRef, + ) => { const slideElRef = useRef(null); const [slideClasses, setSlideClasses] = useState('swiper-slide'); @@ -31,6 +34,11 @@ const SwiperSlide = forwardRef( swiper.off('_slideClass', updateClasses); }; }); + useIsomorphicLayoutEffect(() => { + if (swiper && slideElRef.current) { + setSlideClasses(swiper.getSlideClasses(slideElRef.current)); + } + }, [swiper]); let slideData; if (typeof children === 'function') { @@ -45,7 +53,7 @@ const SwiperSlide = forwardRef( slideClasses.indexOf('swiper-slide-duplicate-prev') >= 0, isNext: slideClasses.indexOf('swiper-slide-next') >= 0 || - slideClasses.indexOf('swiper-slide-duplicate next') >= 0, + slideClasses.indexOf('swiper-slide-duplicate-next') >= 0, }; } @@ -57,6 +65,7 @@ const SwiperSlide = forwardRef( {zoom ? ( diff --git a/packages/rax-swiper/src/swiper.css b/packages/rax-swiper/src/swiper.css index 49f9d528..01de26ae 100644 --- a/packages/rax-swiper/src/swiper.css +++ b/packages/rax-swiper/src/swiper.css @@ -1,533 +1,697 @@ -/** - * Swiper 6.1.2 - * Most modern mobile touch slider and framework with hardware accelerated transitions - * http://swiperjs.com - * - * Copyright 2014-2020 Vladimir Kharlampidi - * - * Released under the MIT License - * - * Released on: August 17, 2020 - */ - - @font-face { - font-family: 'swiper-icons'; - src: url('data:application/font-woff;charset=utf-8;base64,d09GRgABAAAAAAZgABAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAAGRAAAABoAAAAci6qHkUdERUYAAAWgAAAAIwAAACQAYABXR1BPUwAABhQAAAAuAAAANuAY7+xHU1VCAAAFxAAAAFAAAABm2fPczU9TLzIAAAHcAAAASgAAAGBP9V5RY21hcAAAAkQAAACIAAABYt6F0cBjdnQgAAACzAAAAAQAAAAEABEBRGdhc3AAAAWYAAAACAAAAAj//wADZ2x5ZgAAAywAAADMAAAD2MHtryVoZWFkAAABbAAAADAAAAA2E2+eoWhoZWEAAAGcAAAAHwAAACQC9gDzaG10eAAAAigAAAAZAAAArgJkABFsb2NhAAAC0AAAAFoAAABaFQAUGG1heHAAAAG8AAAAHwAAACAAcABAbmFtZQAAA/gAAAE5AAACXvFdBwlwb3N0AAAFNAAAAGIAAACE5s74hXjaY2BkYGAAYpf5Hu/j+W2+MnAzMYDAzaX6QjD6/4//Bxj5GA8AuRwMYGkAPywL13jaY2BkYGA88P8Agx4j+/8fQDYfA1AEBWgDAIB2BOoAeNpjYGRgYNBh4GdgYgABEMnIABJzYNADCQAACWgAsQB42mNgYfzCOIGBlYGB0YcxjYGBwR1Kf2WQZGhhYGBiYGVmgAFGBiQQkOaawtDAoMBQxXjg/wEGPcYDDA4wNUA2CCgwsAAAO4EL6gAAeNpj2M0gyAACqxgGNWBkZ2D4/wMA+xkDdgAAAHjaY2BgYGaAYBkGRgYQiAHyGMF8FgYHIM3DwMHABGQrMOgyWDLEM1T9/w8UBfEMgLzE////P/5//f/V/xv+r4eaAAeMbAxwIUYmIMHEgKYAYjUcsDAwsLKxc3BycfPw8jEQA/gZBASFhEVExcQlJKWkZWTl5BUUlZRVVNXUNTQZBgMAAMR+E+gAEQFEAAAAKgAqACoANAA+AEgAUgBcAGYAcAB6AIQAjgCYAKIArAC2AMAAygDUAN4A6ADyAPwBBgEQARoBJAEuATgBQgFMAVYBYAFqAXQBfgGIAZIBnAGmAbIBzgHsAAB42u2NMQ6CUAyGW568x9AneYYgm4MJbhKFaExIOAVX8ApewSt4Bic4AfeAid3VOBixDxfPYEza5O+Xfi04YADggiUIULCuEJK8VhO4bSvpdnktHI5QCYtdi2sl8ZnXaHlqUrNKzdKcT8cjlq+rwZSvIVczNiezsfnP/uznmfPFBNODM2K7MTQ45YEAZqGP81AmGGcF3iPqOop0r1SPTaTbVkfUe4HXj97wYE+yNwWYxwWu4v1ugWHgo3S1XdZEVqWM7ET0cfnLGxWfkgR42o2PvWrDMBSFj/IHLaF0zKjRgdiVMwScNRAoWUoH78Y2icB/yIY09An6AH2Bdu/UB+yxopYshQiEvnvu0dURgDt8QeC8PDw7Fpji3fEA4z/PEJ6YOB5hKh4dj3EvXhxPqH/SKUY3rJ7srZ4FZnh1PMAtPhwP6fl2PMJMPDgeQ4rY8YT6Gzao0eAEA409DuggmTnFnOcSCiEiLMgxCiTI6Cq5DZUd3Qmp10vO0LaLTd2cjN4fOumlc7lUYbSQcZFkutRG7g6JKZKy0RmdLY680CDnEJ+UMkpFFe1RN7nxdVpXrC4aTtnaurOnYercZg2YVmLN/d/gczfEimrE/fs/bOuq29Zmn8tloORaXgZgGa78yO9/cnXm2BpaGvq25Dv9S4E9+5SIc9PqupJKhYFSSl47+Qcr1mYNAAAAeNptw0cKwkAAAMDZJA8Q7OUJvkLsPfZ6zFVERPy8qHh2YER+3i/BP83vIBLLySsoKimrqKqpa2hp6+jq6RsYGhmbmJqZSy0sraxtbO3sHRydnEMU4uR6yx7JJXveP7WrDycAAAAAAAH//wACeNpjYGRgYOABYhkgZgJCZgZNBkYGLQZtIJsFLMYAAAw3ALgAeNolizEKgDAQBCchRbC2sFER0YD6qVQiBCv/H9ezGI6Z5XBAw8CBK/m5iQQVauVbXLnOrMZv2oLdKFa8Pjuru2hJzGabmOSLzNMzvutpB3N42mNgZGBg4GKQYzBhYMxJLMlj4GBgAYow/P/PAJJhLM6sSoWKfWCAAwDAjgbRAAB42mNgYGBkAIIbCZo5IPrmUn0hGA0AO8EFTQAA') format('woff'); - font-weight: 400; - font-style: normal; - } - :root { - --swiper-theme-color: #007aff; - } - .swiper-container { - margin-left: auto; - margin-right: auto; - position: relative; - overflow: hidden; - list-style: none; - padding: 0; - /* Fix of Webkit flickering */ - z-index: 1; - } - .swiper-container-vertical > .swiper-wrapper { - flex-direction: column; - } - .swiper-wrapper { - position: relative; - width: 100%; - height: 100%; - z-index: 1; - display: flex; - transition-property: transform; - box-sizing: content-box; - } - .swiper-container-android .swiper-slide, - .swiper-wrapper { - transform: translate3d(0px, 0, 0); - } - .swiper-container-multirow > .swiper-wrapper { - flex-wrap: wrap; - } - .swiper-container-multirow-column > .swiper-wrapper { - flex-wrap: wrap; - flex-direction: column; - } - .swiper-container-free-mode > .swiper-wrapper { - transition-timing-function: ease-out; - margin: 0 auto; - } - .swiper-slide { - flex-shrink: 0; - width: 100%; - height: 100%; - position: relative; - transition-property: transform; - } - .swiper-slide-invisible-blank { - visibility: hidden; - } - /* Auto Height */ - .swiper-container-autoheight, - .swiper-container-autoheight .swiper-slide { - height: auto; - } - .swiper-container-autoheight .swiper-wrapper { - align-items: flex-start; - transition-property: transform, height; - } - /* 3D Effects */ - .swiper-container-3d { - perspective: 1200px; - } - .swiper-container-3d .swiper-wrapper, - .swiper-container-3d .swiper-slide, - .swiper-container-3d .swiper-slide-shadow-left, - .swiper-container-3d .swiper-slide-shadow-right, - .swiper-container-3d .swiper-slide-shadow-top, - .swiper-container-3d .swiper-slide-shadow-bottom, - .swiper-container-3d .swiper-cube-shadow { - transform-style: preserve-3d; - } - .swiper-container-3d .swiper-slide-shadow-left, - .swiper-container-3d .swiper-slide-shadow-right, - .swiper-container-3d .swiper-slide-shadow-top, - .swiper-container-3d .swiper-slide-shadow-bottom { - position: absolute; - left: 0; - top: 0; - width: 100%; - height: 100%; - pointer-events: none; - z-index: 10; - } - .swiper-container-3d .swiper-slide-shadow-left { - background-image: linear-gradient(to left, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0)); - } - .swiper-container-3d .swiper-slide-shadow-right { - background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0)); - } - .swiper-container-3d .swiper-slide-shadow-top { - background-image: linear-gradient(to top, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0)); - } - .swiper-container-3d .swiper-slide-shadow-bottom { - background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0)); - } - /* CSS Mode */ - .swiper-container-css-mode > .swiper-wrapper { - overflow: auto; - scrollbar-width: none; - /* For Firefox */ - -ms-overflow-style: none; - /* For Internet Explorer and Edge */ - } - .swiper-container-css-mode > .swiper-wrapper::-webkit-scrollbar { - display: none; - } - .swiper-container-css-mode > .swiper-wrapper > .swiper-slide { - scroll-snap-align: start start; - } - .swiper-container-horizontal.swiper-container-css-mode > .swiper-wrapper { - scroll-snap-type: x mandatory; - } - .swiper-container-vertical.swiper-container-css-mode > .swiper-wrapper { - scroll-snap-type: y mandatory; - } - :root { - --swiper-navigation-size: 44px; - /* - --swiper-navigation-color: var(--swiper-theme-color); - */ - } - .swiper-button-prev, - .swiper-button-next { - position: absolute; - top: 50%; - width: calc(var(--swiper-navigation-size) / 44 * 27); - height: var(--swiper-navigation-size); - margin-top: calc(-1 * var(--swiper-navigation-size) / 2); - z-index: 10; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - color: var(--swiper-navigation-color, var(--swiper-theme-color)); - } - .swiper-button-prev.swiper-button-disabled, - .swiper-button-next.swiper-button-disabled { - opacity: 0.35; - cursor: auto; - pointer-events: none; - } - .swiper-button-prev:after, - .swiper-button-next:after { - font-family: swiper-icons; - font-size: var(--swiper-navigation-size); - text-transform: none !important; - letter-spacing: 0; - text-transform: none; - font-variant: initial; - line-height: 1; - } - .swiper-button-prev, - .swiper-container-rtl .swiper-button-next { - left: 10px; - right: auto; - } - .swiper-button-prev:after, - .swiper-container-rtl .swiper-button-next:after { - content: 'prev'; - } - .swiper-button-next, - .swiper-container-rtl .swiper-button-prev { - right: 10px; - left: auto; - } - .swiper-button-next:after, - .swiper-container-rtl .swiper-button-prev:after { - content: 'next'; - } - .swiper-button-prev.swiper-button-white, - .swiper-button-next.swiper-button-white { - --swiper-navigation-color: #ffffff; - } - .swiper-button-prev.swiper-button-black, - .swiper-button-next.swiper-button-black { - --swiper-navigation-color: #000000; - } - .swiper-button-lock { - display: none; - } - :root { - /* - --swiper-pagination-color: var(--swiper-theme-color); - */ - } - .swiper-pagination { - position: absolute; - text-align: center; - transition: 300ms opacity; - transform: translate3d(0, 0, 0); - z-index: 10; - } - .swiper-pagination.swiper-pagination-hidden { - opacity: 0; - } - /* Common Styles */ - .swiper-pagination-fraction, - .swiper-pagination-custom, - .swiper-container-horizontal > .swiper-pagination-bullets { - bottom: 10px; - left: 0; - width: 100%; - } - /* Bullets */ - .swiper-pagination-bullets-dynamic { - overflow: hidden; - font-size: 0; - } - .swiper-pagination-bullets-dynamic .swiper-pagination-bullet { - transform: scale(0.33); - position: relative; - } - .swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active { - transform: scale(1); - } - .swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-main { - transform: scale(1); - } - .swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-prev { - transform: scale(0.66); - } - .swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-prev-prev { - transform: scale(0.33); - } - .swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-next { - transform: scale(0.66); - } - .swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-next-next { - transform: scale(0.33); - } - .swiper-pagination-bullet { - width: 8px; - height: 8px; - display: inline-block; - border-radius: 100%; - background: #000; - opacity: 0.2; - } - button.swiper-pagination-bullet { - border: none; - margin: 0; - padding: 0; - box-shadow: none; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - } - .swiper-pagination-clickable .swiper-pagination-bullet { - cursor: pointer; - } - .swiper-pagination-bullet-active { - opacity: 1; - background: var(--swiper-pagination-color, var(--swiper-theme-color)); - } - .swiper-container-vertical > .swiper-pagination-bullets { - right: 10px; - top: 50%; - transform: translate3d(0px, -50%, 0); - } - .swiper-container-vertical > .swiper-pagination-bullets .swiper-pagination-bullet { - margin: 6px 0; - display: block; - } - .swiper-container-vertical > .swiper-pagination-bullets.swiper-pagination-bullets-dynamic { - top: 50%; - transform: translateY(-50%); - width: 8px; - } - .swiper-container-vertical > .swiper-pagination-bullets.swiper-pagination-bullets-dynamic .swiper-pagination-bullet { - display: inline-block; - transition: 200ms transform, 200ms top; - } - .swiper-container-horizontal > .swiper-pagination-bullets .swiper-pagination-bullet { - margin: 0 4px; - } - .swiper-container-horizontal > .swiper-pagination-bullets.swiper-pagination-bullets-dynamic { - left: 50%; - transform: translateX(-50%); - white-space: nowrap; - } - .swiper-container-horizontal > .swiper-pagination-bullets.swiper-pagination-bullets-dynamic .swiper-pagination-bullet { - transition: 200ms transform, 200ms left; - } - .swiper-container-horizontal.swiper-container-rtl > .swiper-pagination-bullets-dynamic .swiper-pagination-bullet { - transition: 200ms transform, 200ms right; - } - /* Progress */ - .swiper-pagination-progressbar { - background: rgba(0, 0, 0, 0.25); - position: absolute; - } - .swiper-pagination-progressbar .swiper-pagination-progressbar-fill { - background: var(--swiper-pagination-color, var(--swiper-theme-color)); - position: absolute; - left: 0; - top: 0; - width: 100%; - height: 100%; - transform: scale(0); - transform-origin: left top; - } - .swiper-container-rtl .swiper-pagination-progressbar .swiper-pagination-progressbar-fill { - transform-origin: right top; - } - .swiper-container-horizontal > .swiper-pagination-progressbar, - .swiper-container-vertical > .swiper-pagination-progressbar.swiper-pagination-progressbar-opposite { - width: 100%; - height: 4px; - left: 0; - top: 0; - } - .swiper-container-vertical > .swiper-pagination-progressbar, - .swiper-container-horizontal > .swiper-pagination-progressbar.swiper-pagination-progressbar-opposite { - width: 4px; - height: 100%; - left: 0; - top: 0; - } - .swiper-pagination-white { - --swiper-pagination-color: #ffffff; - } - .swiper-pagination-black { - --swiper-pagination-color: #000000; - } - .swiper-pagination-lock { - display: none; - } - /* Scrollbar */ - .swiper-scrollbar { - border-radius: 10px; - position: relative; - -ms-touch-action: none; - background: rgba(0, 0, 0, 0.1); - } - .swiper-container-horizontal > .swiper-scrollbar { - position: absolute; - left: 1%; - bottom: 3px; - z-index: 50; - height: 5px; - width: 98%; - } - .swiper-container-vertical > .swiper-scrollbar { - position: absolute; - right: 3px; - top: 1%; - z-index: 50; - width: 5px; - height: 98%; - } - .swiper-scrollbar-drag { - height: 100%; - width: 100%; - position: relative; - background: rgba(0, 0, 0, 0.5); - border-radius: 10px; - left: 0; - top: 0; - } - .swiper-scrollbar-cursor-drag { - cursor: move; - } - .swiper-scrollbar-lock { - display: none; - } - .swiper-zoom-container { - width: 100%; - height: 100%; - display: flex; - justify-content: center; - align-items: center; - text-align: center; - } - .swiper-zoom-container > img, - .swiper-zoom-container > svg, - .swiper-zoom-container > canvas { - max-width: 100%; - max-height: 100%; - object-fit: contain; - } - .swiper-slide-zoomed { - cursor: move; - } - /* Preloader */ - :root { - /* - --swiper-preloader-color: var(--swiper-theme-color); - */ - } - .swiper-lazy-preloader { - width: 42px; - height: 42px; - position: absolute; - left: 50%; - top: 50%; - margin-left: -21px; - margin-top: -21px; - z-index: 10; - transform-origin: 50%; - animation: swiper-preloader-spin 1s infinite linear; - box-sizing: border-box; - border: 4px solid var(--swiper-preloader-color, var(--swiper-theme-color)); - border-radius: 50%; - border-top-color: transparent; - } - .swiper-lazy-preloader-white { - --swiper-preloader-color: #fff; - } - .swiper-lazy-preloader-black { - --swiper-preloader-color: #000; - } - @keyframes swiper-preloader-spin { - 100% { - transform: rotate(360deg); - } - } - /* a11y */ - .swiper-container .swiper-notification { - position: absolute; - left: 0; - top: 0; - pointer-events: none; - opacity: 0; - z-index: -1000; - } - .swiper-container-fade.swiper-container-free-mode .swiper-slide { - transition-timing-function: ease-out; - } - .swiper-container-fade .swiper-slide { - pointer-events: none; - transition-property: opacity; - } - .swiper-container-fade .swiper-slide .swiper-slide { - pointer-events: none; - } - .swiper-container-fade .swiper-slide-active, - .swiper-container-fade .swiper-slide-active .swiper-slide-active { - pointer-events: auto; - } - .swiper-container-cube { - overflow: visible; - } - .swiper-container-cube .swiper-slide { - pointer-events: none; - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - z-index: 1; - visibility: hidden; - transform-origin: 0 0; - width: 100%; - height: 100%; - } - .swiper-container-cube .swiper-slide .swiper-slide { - pointer-events: none; - } - .swiper-container-cube.swiper-container-rtl .swiper-slide { - transform-origin: 100% 0; - } - .swiper-container-cube .swiper-slide-active, - .swiper-container-cube .swiper-slide-active .swiper-slide-active { - pointer-events: auto; - } - .swiper-container-cube .swiper-slide-active, - .swiper-container-cube .swiper-slide-next, - .swiper-container-cube .swiper-slide-prev, - .swiper-container-cube .swiper-slide-next + .swiper-slide { - pointer-events: auto; - visibility: visible; - } - .swiper-container-cube .swiper-slide-shadow-top, - .swiper-container-cube .swiper-slide-shadow-bottom, - .swiper-container-cube .swiper-slide-shadow-left, - .swiper-container-cube .swiper-slide-shadow-right { - z-index: 0; - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - } - .swiper-container-cube .swiper-cube-shadow { - position: absolute; - left: 0; - bottom: 0px; - width: 100%; - height: 100%; - background: #000; - opacity: 0.6; - -webkit-filter: blur(50px); - filter: blur(50px); - z-index: 0; - } - .swiper-container-flip { - overflow: visible; - } - .swiper-container-flip .swiper-slide { - pointer-events: none; - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - z-index: 1; - } - .swiper-container-flip .swiper-slide .swiper-slide { - pointer-events: none; - } - .swiper-container-flip .swiper-slide-active, - .swiper-container-flip .swiper-slide-active .swiper-slide-active { - pointer-events: auto; - } - .swiper-container-flip .swiper-slide-shadow-top, - .swiper-container-flip .swiper-slide-shadow-bottom, - .swiper-container-flip .swiper-slide-shadow-left, - .swiper-container-flip .swiper-slide-shadow-right { - z-index: 0; - -webkit-backface-visibility: hidden; - backface-visibility: hidden; +@font-face { + font-family: "swiper-icons"; + src: url("data:application/font-woff;charset=utf-8;base64, d09GRgABAAAAAAZgABAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAAGRAAAABoAAAAci6qHkUdERUYAAAWgAAAAIwAAACQAYABXR1BPUwAABhQAAAAuAAAANuAY7+xHU1VCAAAFxAAAAFAAAABm2fPczU9TLzIAAAHcAAAASgAAAGBP9V5RY21hcAAAAkQAAACIAAABYt6F0cBjdnQgAAACzAAAAAQAAAAEABEBRGdhc3AAAAWYAAAACAAAAAj//wADZ2x5ZgAAAywAAADMAAAD2MHtryVoZWFkAAABbAAAADAAAAA2E2+eoWhoZWEAAAGcAAAAHwAAACQC9gDzaG10eAAAAigAAAAZAAAArgJkABFsb2NhAAAC0AAAAFoAAABaFQAUGG1heHAAAAG8AAAAHwAAACAAcABAbmFtZQAAA/gAAAE5AAACXvFdBwlwb3N0AAAFNAAAAGIAAACE5s74hXjaY2BkYGAAYpf5Hu/j+W2+MnAzMYDAzaX6QjD6/4//Bxj5GA8AuRwMYGkAPywL13jaY2BkYGA88P8Agx4j+/8fQDYfA1AEBWgDAIB2BOoAeNpjYGRgYNBh4GdgYgABEMnIABJzYNADCQAACWgAsQB42mNgYfzCOIGBlYGB0YcxjYGBwR1Kf2WQZGhhYGBiYGVmgAFGBiQQkOaawtDAoMBQxXjg/wEGPcYDDA4wNUA2CCgwsAAAO4EL6gAAeNpj2M0gyAACqxgGNWBkZ2D4/wMA+xkDdgAAAHjaY2BgYGaAYBkGRgYQiAHyGMF8FgYHIM3DwMHABGQrMOgyWDLEM1T9/w8UBfEMgLzE////P/5//f/V/xv+r4eaAAeMbAxwIUYmIMHEgKYAYjUcsDAwsLKxc3BycfPw8jEQA/gZBASFhEVExcQlJKWkZWTl5BUUlZRVVNXUNTQZBgMAAMR+E+gAEQFEAAAAKgAqACoANAA+AEgAUgBcAGYAcAB6AIQAjgCYAKIArAC2AMAAygDUAN4A6ADyAPwBBgEQARoBJAEuATgBQgFMAVYBYAFqAXQBfgGIAZIBnAGmAbIBzgHsAAB42u2NMQ6CUAyGW568x9AneYYgm4MJbhKFaExIOAVX8ApewSt4Bic4AfeAid3VOBixDxfPYEza5O+Xfi04YADggiUIULCuEJK8VhO4bSvpdnktHI5QCYtdi2sl8ZnXaHlqUrNKzdKcT8cjlq+rwZSvIVczNiezsfnP/uznmfPFBNODM2K7MTQ45YEAZqGP81AmGGcF3iPqOop0r1SPTaTbVkfUe4HXj97wYE+yNwWYxwWu4v1ugWHgo3S1XdZEVqWM7ET0cfnLGxWfkgR42o2PvWrDMBSFj/IHLaF0zKjRgdiVMwScNRAoWUoH78Y2icB/yIY09An6AH2Bdu/UB+yxopYshQiEvnvu0dURgDt8QeC8PDw7Fpji3fEA4z/PEJ6YOB5hKh4dj3EvXhxPqH/SKUY3rJ7srZ4FZnh1PMAtPhwP6fl2PMJMPDgeQ4rY8YT6Gzao0eAEA409DuggmTnFnOcSCiEiLMgxCiTI6Cq5DZUd3Qmp10vO0LaLTd2cjN4fOumlc7lUYbSQcZFkutRG7g6JKZKy0RmdLY680CDnEJ+UMkpFFe1RN7nxdVpXrC4aTtnaurOnYercZg2YVmLN/d/gczfEimrE/fs/bOuq29Zmn8tloORaXgZgGa78yO9/cnXm2BpaGvq25Dv9S4E9+5SIc9PqupJKhYFSSl47+Qcr1mYNAAAAeNptw0cKwkAAAMDZJA8Q7OUJvkLsPfZ6zFVERPy8qHh2YER+3i/BP83vIBLLySsoKimrqKqpa2hp6+jq6RsYGhmbmJqZSy0sraxtbO3sHRydnEMU4uR6yx7JJXveP7WrDycAAAAAAAH//wACeNpjYGRgYOABYhkgZgJCZgZNBkYGLQZtIJsFLMYAAAw3ALgAeNolizEKgDAQBCchRbC2sFER0YD6qVQiBCv/H9ezGI6Z5XBAw8CBK/m5iQQVauVbXLnOrMZv2oLdKFa8Pjuru2hJzGabmOSLzNMzvutpB3N42mNgZGBg4GKQYzBhYMxJLMlj4GBgAYow/P/PAJJhLM6sSoWKfWCAAwDAjgbRAAB42mNgYGBkAIIbCZo5IPrmUn0hGA0AO8EFTQAA") format("woff"); + font-weight: 400; + font-style: normal; +} +:root { + --swiper-theme-color: #007aff; +} + +.swiper-container { + margin-left: auto; + margin-right: auto; + position: relative; + overflow: hidden; + list-style: none; + padding: 0; + /* Fix of Webkit flickering */ + z-index: 1; +} + +.swiper-container-vertical > .swiper-wrapper { + flex-direction: column; +} + +.swiper-wrapper { + position: relative; + width: 100%; + height: 100%; + z-index: 1; + display: flex; + transition-property: transform; + box-sizing: content-box; +} + +.swiper-container-android .swiper-slide, +.swiper-wrapper { + transform: translate3d(0px, 0, 0); +} + +.swiper-container-multirow > .swiper-wrapper { + flex-wrap: wrap; +} + +.swiper-container-multirow-column > .swiper-wrapper { + flex-wrap: wrap; + flex-direction: column; +} + +.swiper-container-free-mode > .swiper-wrapper { + transition-timing-function: ease-out; + margin: 0 auto; +} + +.swiper-container-pointer-events { + touch-action: pan-y; +} +.swiper-container-pointer-events.swiper-container-vertical { + touch-action: pan-x; +} + +.swiper-slide { + flex-shrink: 0; + width: 100%; + height: 100%; + position: relative; + transition-property: transform; +} + +.swiper-slide-invisible-blank { + visibility: hidden; +} + +/* Auto Height */ +.swiper-container-autoheight, +.swiper-container-autoheight .swiper-slide { + height: auto; +} +.swiper-container-autoheight .swiper-wrapper { + align-items: flex-start; + transition-property: transform, height; +} + +/* 3D Effects */ +.swiper-container-3d { + perspective: 1200px; +} +.swiper-container-3d .swiper-wrapper, +.swiper-container-3d .swiper-slide, +.swiper-container-3d .swiper-slide-shadow-left, +.swiper-container-3d .swiper-slide-shadow-right, +.swiper-container-3d .swiper-slide-shadow-top, +.swiper-container-3d .swiper-slide-shadow-bottom, +.swiper-container-3d .swiper-cube-shadow { + transform-style: preserve-3d; +} +.swiper-container-3d .swiper-slide-shadow-left, +.swiper-container-3d .swiper-slide-shadow-right, +.swiper-container-3d .swiper-slide-shadow-top, +.swiper-container-3d .swiper-slide-shadow-bottom { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: 10; +} +.swiper-container-3d .swiper-slide-shadow-left { + background-image: linear-gradient(to left, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0)); +} +.swiper-container-3d .swiper-slide-shadow-right { + background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0)); +} +.swiper-container-3d .swiper-slide-shadow-top { + background-image: linear-gradient(to top, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0)); +} +.swiper-container-3d .swiper-slide-shadow-bottom { + background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0)); +} + +/* CSS Mode */ +.swiper-container-css-mode > .swiper-wrapper { + overflow: auto; + scrollbar-width: none; + /* For Firefox */ + -ms-overflow-style: none; + /* For Internet Explorer and Edge */ +} +.swiper-container-css-mode > .swiper-wrapper::-webkit-scrollbar { + display: none; +} +.swiper-container-css-mode > .swiper-wrapper > .swiper-slide { + scroll-snap-align: start start; +} + +.swiper-container-horizontal.swiper-container-css-mode > .swiper-wrapper { + scroll-snap-type: x mandatory; +} + +.swiper-container-vertical.swiper-container-css-mode > .swiper-wrapper { + scroll-snap-type: y mandatory; +} + +/* a11y */ +.swiper-container .swiper-notification { + position: absolute; + left: 0; + top: 0; + pointer-events: none; + opacity: 0; + z-index: -1000; +} + +.swiper-container { + margin-left: auto; + margin-right: auto; + position: relative; + overflow: hidden; + list-style: none; + padding: 0; + /* Fix of Webkit flickering */ + z-index: 1; +} + +.swiper-container-vertical > .swiper-wrapper { + flex-direction: column; +} + +.swiper-wrapper { + position: relative; + width: 100%; + height: 100%; + z-index: 1; + display: flex; + transition-property: transform; + box-sizing: content-box; +} + +.swiper-container-android .swiper-slide, +.swiper-wrapper { + transform: translate3d(0px, 0, 0); +} + +.swiper-container-multirow > .swiper-wrapper { + flex-wrap: wrap; +} + +.swiper-container-multirow-column > .swiper-wrapper { + flex-wrap: wrap; + flex-direction: column; +} + +.swiper-container-free-mode > .swiper-wrapper { + transition-timing-function: ease-out; + margin: 0 auto; +} + +.swiper-container-pointer-events { + touch-action: pan-y; +} +.swiper-container-pointer-events.swiper-container-vertical { + touch-action: pan-x; +} + +.swiper-slide { + flex-shrink: 0; + width: 100%; + height: 100%; + position: relative; + transition-property: transform; +} + +.swiper-slide-invisible-blank { + visibility: hidden; +} + +/* Auto Height */ +.swiper-container-autoheight, +.swiper-container-autoheight .swiper-slide { + height: auto; +} +.swiper-container-autoheight .swiper-wrapper { + align-items: flex-start; + transition-property: transform, height; +} + +/* 3D Effects */ +.swiper-container-3d { + perspective: 1200px; +} +.swiper-container-3d .swiper-wrapper, +.swiper-container-3d .swiper-slide, +.swiper-container-3d .swiper-slide-shadow-left, +.swiper-container-3d .swiper-slide-shadow-right, +.swiper-container-3d .swiper-slide-shadow-top, +.swiper-container-3d .swiper-slide-shadow-bottom, +.swiper-container-3d .swiper-cube-shadow { + transform-style: preserve-3d; +} +.swiper-container-3d .swiper-slide-shadow-left, +.swiper-container-3d .swiper-slide-shadow-right, +.swiper-container-3d .swiper-slide-shadow-top, +.swiper-container-3d .swiper-slide-shadow-bottom { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: 10; +} +.swiper-container-3d .swiper-slide-shadow-left { + background-image: linear-gradient(to left, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0)); +} +.swiper-container-3d .swiper-slide-shadow-right { + background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0)); +} +.swiper-container-3d .swiper-slide-shadow-top { + background-image: linear-gradient(to top, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0)); +} +.swiper-container-3d .swiper-slide-shadow-bottom { + background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0)); +} + +/* CSS Mode */ +.swiper-container-css-mode > .swiper-wrapper { + overflow: auto; + scrollbar-width: none; + /* For Firefox */ + -ms-overflow-style: none; + /* For Internet Explorer and Edge */ +} +.swiper-container-css-mode > .swiper-wrapper::-webkit-scrollbar { + display: none; +} +.swiper-container-css-mode > .swiper-wrapper > .swiper-slide { + scroll-snap-align: start start; +} + +.swiper-container-horizontal.swiper-container-css-mode > .swiper-wrapper { + scroll-snap-type: x mandatory; +} + +.swiper-container-vertical.swiper-container-css-mode > .swiper-wrapper { + scroll-snap-type: y mandatory; +} + +.swiper-container-cube { + overflow: visible; +} +.swiper-container-cube .swiper-slide { + pointer-events: none; + backface-visibility: hidden; + z-index: 1; + visibility: hidden; + transform-origin: 0 0; + width: 100%; + height: 100%; +} +.swiper-container-cube .swiper-slide .swiper-slide { + pointer-events: none; +} +.swiper-container-cube.swiper-container-rtl .swiper-slide { + transform-origin: 100% 0; +} +.swiper-container-cube .swiper-slide-active, .swiper-container-cube .swiper-slide-active .swiper-slide-active { + pointer-events: auto; +} +.swiper-container-cube .swiper-slide-active, +.swiper-container-cube .swiper-slide-next, +.swiper-container-cube .swiper-slide-prev, +.swiper-container-cube .swiper-slide-next + .swiper-slide { + pointer-events: auto; + visibility: visible; +} +.swiper-container-cube .swiper-slide-shadow-top, +.swiper-container-cube .swiper-slide-shadow-bottom, +.swiper-container-cube .swiper-slide-shadow-left, +.swiper-container-cube .swiper-slide-shadow-right { + z-index: 0; + backface-visibility: hidden; +} +.swiper-container-cube .swiper-cube-shadow { + position: absolute; + left: 0; + bottom: 0px; + width: 100%; + height: 100%; + opacity: 0.6; + z-index: 0; +} +.swiper-container-cube .swiper-cube-shadow:before { + content: ""; + background: #000; + position: absolute; + left: 0; + top: 0; + bottom: 0; + right: 0; + -webkit-filter: blur(50px); + filter: blur(50px); +} + +.swiper-container-fade.swiper-container-free-mode .swiper-slide { + transition-timing-function: ease-out; +} +.swiper-container-fade .swiper-slide { + pointer-events: none; + transition-property: opacity; +} +.swiper-container-fade .swiper-slide .swiper-slide { + pointer-events: none; +} +.swiper-container-fade .swiper-slide-active, .swiper-container-fade .swiper-slide-active .swiper-slide-active { + pointer-events: auto; +} + +.swiper-container-flip { + overflow: visible; +} +.swiper-container-flip .swiper-slide { + pointer-events: none; + backface-visibility: hidden; + z-index: 1; +} +.swiper-container-flip .swiper-slide .swiper-slide { + pointer-events: none; +} +.swiper-container-flip .swiper-slide-active, .swiper-container-flip .swiper-slide-active .swiper-slide-active { + pointer-events: auto; +} +.swiper-container-flip .swiper-slide-shadow-top, +.swiper-container-flip .swiper-slide-shadow-bottom, +.swiper-container-flip .swiper-slide-shadow-left, +.swiper-container-flip .swiper-slide-shadow-right { + z-index: 0; + backface-visibility: hidden; +} + +.swiper-lazy-preloader { + width: 42px; + height: 42px; + position: absolute; + left: 50%; + top: 50%; + margin-left: -21px; + margin-top: -21px; + z-index: 10; + transform-origin: 50%; + animation: swiper-preloader-spin 1s infinite linear; + box-sizing: border-box; + border: 4px solid var(--swiper-preloader-color, var(--swiper-theme-color)); + border-radius: 50%; + border-top-color: transparent; +} + +.swiper-lazy-preloader-white { + --swiper-preloader-color: #fff; +} + +.swiper-lazy-preloader-black { + --swiper-preloader-color: #000; +} + +@keyframes swiper-preloader-spin { + 100% { + transform: rotate(360deg); } - \ No newline at end of file +} + +:root { + --swiper-navigation-size: 44px; + /* + --swiper-navigation-color: var(--swiper-theme-color); + */ +} + +.swiper-button-prev, +.swiper-button-next { + position: absolute; + top: 50%; + width: calc(var(--swiper-navigation-size) / 44 * 27); + height: var(--swiper-navigation-size); + margin-top: calc(0px - var(--swiper-navigation-size) / 2); + z-index: 10; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + color: var(--swiper-navigation-color, var(--swiper-theme-color)); +} +.swiper-button-prev.swiper-button-disabled, +.swiper-button-next.swiper-button-disabled { + opacity: 0.35; + cursor: auto; + pointer-events: none; +} +.swiper-button-prev:after, +.swiper-button-next:after { + font-family: swiper-icons; + font-size: var(--swiper-navigation-size); + text-transform: none !important; + letter-spacing: 0; + text-transform: none; + font-variant: initial; + line-height: 1; +} + +.swiper-button-prev, +.swiper-container-rtl .swiper-button-next { + left: 10px; + right: auto; +} +.swiper-button-prev:after, +.swiper-container-rtl .swiper-button-next:after { + content: "prev"; +} + +.swiper-button-next, +.swiper-container-rtl .swiper-button-prev { + right: 10px; + left: auto; +} +.swiper-button-next:after, +.swiper-container-rtl .swiper-button-prev:after { + content: "next"; +} + +.swiper-button-prev.swiper-button-white, +.swiper-button-next.swiper-button-white { + --swiper-navigation-color: #ffffff; +} + +.swiper-button-prev.swiper-button-black, +.swiper-button-next.swiper-button-black { + --swiper-navigation-color: #000000; +} + +.swiper-button-lock { + display: none; +} + +.swiper-pagination { + position: absolute; + text-align: center; + transition: 300ms opacity; + transform: translate3d(0, 0, 0); + z-index: 10; +} +.swiper-pagination.swiper-pagination-hidden { + opacity: 0; +} + +/* Common Styles */ +.swiper-pagination-fraction, +.swiper-pagination-custom, +.swiper-container-horizontal > .swiper-pagination-bullets { + bottom: 10px; + left: 0; + width: 100%; +} + +/* Bullets */ +.swiper-pagination-bullets-dynamic { + overflow: hidden; + font-size: 0; +} +.swiper-pagination-bullets-dynamic .swiper-pagination-bullet { + transform: scale(0.33); + position: relative; +} +.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active { + transform: scale(1); +} +.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-main { + transform: scale(1); +} +.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-prev { + transform: scale(0.66); +} +.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-prev-prev { + transform: scale(0.33); +} +.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-next { + transform: scale(0.66); +} +.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-next-next { + transform: scale(0.33); +} + +.swiper-pagination-bullet { + width: 8px; + height: 8px; + display: inline-block; + border-radius: 50%; + background: #000; + opacity: 0.2; +} +button.swiper-pagination-bullet { + border: none; + margin: 0; + padding: 0; + box-shadow: none; + appearance: none; +} + +.swiper-pagination-clickable .swiper-pagination-bullet { + cursor: pointer; +} +.swiper-pagination-bullet:only-child { + display: none !important; +} + +.swiper-pagination-bullet-active { + opacity: 1; + background: var(--swiper-pagination-color, var(--swiper-theme-color)); +} + +.swiper-container-vertical > .swiper-pagination-bullets { + right: 10px; + top: 50%; + transform: translate3d(0px, -50%, 0); +} +.swiper-container-vertical > .swiper-pagination-bullets .swiper-pagination-bullet { + margin: 6px 0; + display: block; +} +.swiper-container-vertical > .swiper-pagination-bullets.swiper-pagination-bullets-dynamic { + top: 50%; + transform: translateY(-50%); + width: 8px; +} +.swiper-container-vertical > .swiper-pagination-bullets.swiper-pagination-bullets-dynamic .swiper-pagination-bullet { + display: inline-block; + transition: 200ms transform, 200ms top; +} + +.swiper-container-horizontal > .swiper-pagination-bullets .swiper-pagination-bullet { + margin: 0 4px; +} +.swiper-container-horizontal > .swiper-pagination-bullets.swiper-pagination-bullets-dynamic { + left: 50%; + transform: translateX(-50%); + white-space: nowrap; +} +.swiper-container-horizontal > .swiper-pagination-bullets.swiper-pagination-bullets-dynamic .swiper-pagination-bullet { + transition: 200ms transform, 200ms left; +} +.swiper-container-horizontal.swiper-container-rtl > .swiper-pagination-bullets-dynamic .swiper-pagination-bullet { + transition: 200ms transform, 200ms right; +} + +/* Progress */ +.swiper-pagination-progressbar { + background: rgba(0, 0, 0, 0.25); + position: absolute; +} +.swiper-pagination-progressbar .swiper-pagination-progressbar-fill { + background: var(--swiper-pagination-color, var(--swiper-theme-color)); + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + transform: scale(0); + transform-origin: left top; +} +.swiper-container-rtl .swiper-pagination-progressbar .swiper-pagination-progressbar-fill { + transform-origin: right top; +} +.swiper-container-horizontal > .swiper-pagination-progressbar, .swiper-container-vertical > .swiper-pagination-progressbar.swiper-pagination-progressbar-opposite { + width: 100%; + height: 4px; + left: 0; + top: 0; +} +.swiper-container-vertical > .swiper-pagination-progressbar, .swiper-container-horizontal > .swiper-pagination-progressbar.swiper-pagination-progressbar-opposite { + width: 4px; + height: 100%; + left: 0; + top: 0; +} + +.swiper-pagination-white { + --swiper-pagination-color: #ffffff; +} + +.swiper-pagination-black { + --swiper-pagination-color: #000000; +} + +.swiper-pagination-lock { + display: none; +} + +/* Scrollbar */ +.swiper-scrollbar { + border-radius: 10px; + position: relative; + -ms-touch-action: none; + background: rgba(0, 0, 0, 0.1); +} +.swiper-container-horizontal > .swiper-scrollbar { + position: absolute; + left: 1%; + bottom: 3px; + z-index: 50; + height: 5px; + width: 98%; +} +.swiper-container-vertical > .swiper-scrollbar { + position: absolute; + right: 3px; + top: 1%; + z-index: 50; + width: 5px; + height: 98%; +} + +.swiper-scrollbar-drag { + height: 100%; + width: 100%; + position: relative; + background: rgba(0, 0, 0, 0.5); + border-radius: 10px; + left: 0; + top: 0; +} + +.swiper-scrollbar-cursor-drag { + cursor: move; +} + +.swiper-scrollbar-lock { + display: none; +} + +.swiper-zoom-container { + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + text-align: center; +} +.swiper-zoom-container > img, +.swiper-zoom-container > svg, +.swiper-zoom-container > canvas { + max-width: 100%; + max-height: 100%; + object-fit: contain; +} + +.swiper-slide-zoomed { + cursor: move; +} diff --git a/packages/rax-swiper/src/swiper.js b/packages/rax-swiper/src/swiper.js index 18efaf45..6acd5b05 100644 --- a/packages/rax-swiper/src/swiper.js +++ b/packages/rax-swiper/src/swiper.js @@ -1,8 +1,8 @@ import { createElement, useRef, useState, useEffect, forwardRef } from 'rax'; import cloneElement from 'rax-clone-element'; import { getParams } from './get-params'; -import { initSwiper } from './init-swiper'; -import { needsScrollbar, needsNavigation, needsPagination, uniqueClasses } from './utils'; +import { initSwiper, mountSwiper } from './init-swiper'; +import { needsScrollbar, needsNavigation, needsPagination, uniqueClasses, extend } from './utils'; import { renderLoop, calcLoopedSlides } from './loop'; import { getChangedParams } from './get-changed-params'; import { getChildren } from './get-children'; @@ -22,8 +22,10 @@ const Swiper = forwardRef( } = {}, externalElRef, ) => { + let eventsAssigned = false; const [containerClasses, setContainerClasses] = useState('swiper-container'); const [virtualData, setVirtualData] = useState(null); + const [breakpointChanged, setBreakpointChanged] = useState(false); const initializedRef = useRef(false); const swiperElRef = useRef(null); const swiperRef = useRef(null); @@ -35,38 +37,65 @@ const Swiper = forwardRef( const paginationElRef = useRef(null); const scrollbarElRef = useRef(null); - const { params: swiperParams, passedParams, rest: restProps } = getParams(rest); + const { params: swiperParams, passedParams, rest: restProps, events } = getParams(rest); const { slides, slots } = getChildren(children); - const changedParams = getChangedParams( - passedParams, - oldPassedParamsRef.current, - slides, - oldSlides.current, - ); - - oldPassedParamsRef.current = passedParams; - oldSlides.current = slides; + const onBeforeBreakpoint = () => { + setBreakpointChanged(!breakpointChanged); + }; Object.assign(swiperParams.on, { _containerClasses(swiper, classes) { setContainerClasses(classes); }, - _swiper(swiper) { - swiper.loopCreate = () => {}; - swiper.loopDestroy = () => {}; - if (swiperParams.loop) { - swiper.loopedSlides = calcLoopedSlides(slides, swiperParams); - } - swiperRef.current = swiper; - if (swiper.virtual && swiper.params.virtual.enabled) { - swiper.virtual.slides = slides; - swiper.params.virtual.cache = false; - swiper.params.virtual.renderExternal = setVirtualData; - swiper.params.virtual.renderExternalUpdate = false; - } - }, + }); + + if (!swiperElRef.current) { + // init swiper + Object.assign(swiperParams.on, events); + eventsAssigned = true; + swiperRef.current = initSwiper(swiperParams); + swiperRef.current.loopCreate = () => {}; + swiperRef.current.loopDestroy = () => {}; + if (swiperParams.loop) { + swiperRef.current.loopedSlides = calcLoopedSlides(slides, swiperParams); + } + if (swiperRef.current.virtual && swiperRef.current.params.virtual.enabled) { + swiperRef.current.virtual.slides = slides; + const extendWith = { + cache: false, + renderExternal: setVirtualData, + renderExternalUpdate: false, + }; + extend(swiperRef.current.params.virtual, extendWith); + extend(swiperRef.current.originalParams.virtual, extendWith); + } + } + + // Listen for breakpoints change + if (swiperRef.current) { + swiperRef.current.on('_beforeBreakpoint', onBeforeBreakpoint); + } + + const attachEvents = () => { + if (eventsAssigned || !events || !swiperRef.current) return; + Object.keys(events).forEach((eventName) => { + swiperRef.current.on(eventName, events[eventName]); + }); + }; + + const detachEvents = () => { + if (!events || !swiperRef.current) return; + Object.keys(events).forEach((eventName) => { + swiperRef.current.off(eventName, events[eventName]); + }); + }; + + useEffect(() => { + return () => { + if (swiperRef.current) swiperRef.current.off('_beforeBreakpoint', onBeforeBreakpoint); + }; }); // set initialized flag @@ -77,32 +106,21 @@ const Swiper = forwardRef( } }); - // watch for params change - useIsomorphicLayoutEffect(() => { - if (changedParams.length && swiperRef.current && !swiperRef.current.destroyed) { - updateSwiper(swiperRef.current, slides, passedParams, changedParams); - } - }); - - // update on virtual update - useIsomorphicLayoutEffect(() => { - updateOnVirtualData(swiperRef.current); - }, [virtualData]); - - // init swiper + // mount swiper useIsomorphicLayoutEffect(() => { if (externalElRef) { externalElRef.current = swiperElRef.current; } if (!swiperElRef.current) return; - initSwiper( + mountSwiper( { el: swiperElRef.current, nextEl: nextElRef.current, prevEl: prevElRef.current, paginationEl: paginationElRef.current, scrollbarEl: scrollbarElRef.current, + swiper: swiperRef.current, }, swiperParams, ); @@ -111,17 +129,50 @@ const Swiper = forwardRef( // eslint-disable-next-line return () => { if (swiperRef.current && !swiperRef.current.destroyed) { - swiperRef.current.destroy(); + swiperRef.current.destroy(true, false); } }; }, []); + // watch for params change + useIsomorphicLayoutEffect(() => { + attachEvents(); + const changedParams = getChangedParams( + passedParams, + oldPassedParamsRef.current, + slides, + oldSlides.current, + ); + oldPassedParamsRef.current = passedParams; + oldSlides.current = slides; + if (changedParams.length && swiperRef.current && !swiperRef.current.destroyed) { + updateSwiper({ + swiper: swiperRef.current, + slides, + passedParams, + changedParams, + nextEl: nextElRef.current, + prevEl: prevElRef.current, + scrollbarEl: scrollbarElRef.current, + paginationEl: paginationElRef.current, + }); + } + return () => { + detachEvents(); + }; + }); + + // update on virtual update + useIsomorphicLayoutEffect(() => { + updateOnVirtualData(swiperRef.current); + }, [virtualData]); + // bypass swiper instance to slides function renderSlides() { if (swiperParams.virtual) { return renderVirtual(swiperRef.current, slides, virtualData); } - if (!swiperParams.loop || swiperRef.current && swiperRef.current.destroyed) { + if (!swiperParams.loop || (swiperRef.current && swiperRef.current.destroyed)) { return slides.map((child) => { return cloneElement(child, { swiper: swiperRef.current }); }); diff --git a/packages/rax-swiper/src/update-swiper.js b/packages/rax-swiper/src/update-swiper.js index 3dfafa28..0b813ca3 100644 --- a/packages/rax-swiper/src/update-swiper.js +++ b/packages/rax-swiper/src/update-swiper.js @@ -1,6 +1,15 @@ import { isObject, extend } from './utils'; -function updateSwiper(swiper, slides, passedParams, changedParams) { +function updateSwiper({ + swiper, + slides, + passedParams, + changedParams, + nextEl, + prevEl, + scrollbarEl, + paginationEl, + }) { const updateParams = changedParams.filter((key) => key !== 'children' && key !== 'direction'); const { params: currentParams, pagination, navigation, scrollbar, virtual, thumbs } = swiper; let needThumbsInit; @@ -8,6 +17,7 @@ function updateSwiper(swiper, slides, passedParams, changedParams) { let needPaginationInit; let needScrollbarInit; let needNavigationInit; + if ( changedParams.includes('thumbs') && passedParams.thumbs && @@ -29,8 +39,8 @@ function updateSwiper(swiper, slides, passedParams, changedParams) { if ( changedParams.includes('pagination') && passedParams.pagination && - passedParams.pagination.el && - currentParams.pagination && + (passedParams.pagination.el || paginationEl) && + (currentParams.pagination || currentParams.pagination === false) && pagination && !pagination.el ) { @@ -40,20 +50,19 @@ function updateSwiper(swiper, slides, passedParams, changedParams) { if ( changedParams.includes('scrollbar') && passedParams.scrollbar && - passedParams.scrollbar.el && - currentParams.scrollbar && + (passedParams.scrollbar.el || scrollbarEl) && + (currentParams.scrollbar || currentParams.scrollbar === false) && scrollbar && !scrollbar.el ) { needScrollbarInit = true; } - if ( changedParams.includes('navigation') && passedParams.navigation && - passedParams.navigation.prevEl && - passedParams.navigation.nextEl && - currentParams.navigation && + (passedParams.navigation.prevEl || prevEl) && + (passedParams.navigation.nextEl || nextEl) && + (currentParams.navigation || currentParams.navigation === false) && navigation && !navigation.prevEl && !navigation.nextEl @@ -61,17 +70,43 @@ function updateSwiper(swiper, slides, passedParams, changedParams) { needNavigationInit = true; } + const destroyModule = (mod) => { + if (!swiper[mod]) return; + swiper[mod].destroy(); + if (mod === 'navigation') { + currentParams[mod].prevEl = undefined; + currentParams[mod].nextEl = undefined; + swiper[mod].prevEl = undefined; + swiper[mod].nextEl = undefined; + } else { + currentParams[mod].el = undefined; + swiper[mod].el = undefined; + } + }; + updateParams.forEach((key) => { if (isObject(currentParams[key]) && isObject(passedParams[key])) { extend(currentParams[key], passedParams[key]); } else { - currentParams[key] = passedParams[key]; + const newValue = passedParams[key]; + if ( + (newValue === true || newValue === false) && + (key === 'navigation' || key === 'pagination' || key === 'scrollbar') + ) { + if (newValue === false) { + destroyModule(key); + } + } else { + currentParams[key] = passedParams[key]; + } } }); if (changedParams.includes('children') && virtual && currentParams.virtual.enabled) { virtual.slides = slides; virtual.update(true); + } else if (changedParams.includes('children') && swiper.lazy && swiper.params.lazy.enabled) { + swiper.lazy.load(); } if (needThumbsInit) { @@ -84,18 +119,22 @@ function updateSwiper(swiper, slides, passedParams, changedParams) { } if (needPaginationInit) { + if (paginationEl) currentParams.pagination.el = paginationEl; pagination.init(); pagination.render(); pagination.update(); } if (needScrollbarInit) { + if (scrollbarEl) currentParams.scrollbar.el = scrollbarEl; scrollbar.init(); scrollbar.updateSize(); scrollbar.setTranslate(); } if (needNavigationInit) { + if (nextEl) currentParams.navigation.nextEl = nextEl; + if (prevEl) currentParams.navigation.prevEl = prevEl; navigation.init(); navigation.update(); } diff --git a/packages/rax-swiper/src/utils.js b/packages/rax-swiper/src/utils.js index 7993804f..bea381fe 100644 --- a/packages/rax-swiper/src/utils.js +++ b/packages/rax-swiper/src/utils.js @@ -1,16 +1,25 @@ function isObject(o) { - return typeof o === 'object' && o !== null && o.constructor && o.constructor === Object; + return ( + typeof o === 'object' && + o !== null && + o.constructor && + Object.prototype.toString.call(o).slice(8, -1) === 'Object' + ); } function extend(target, src) { - Object.keys(src).forEach((key) => { - if (typeof target[key] === 'undefined') target[key] = src[key]; - else if (isObject(src[key]) && isObject(target[key]) && Object.keys(src[key]).length > 0) { - extend(target[key], src[key]); - } else { - target[key] = src[key]; - } - }); + const noExtend = ['__proto__', 'constructor', 'prototype']; + Object.keys(src) + .filter((key) => noExtend.indexOf(key) < 0) + .forEach((key) => { + if (typeof target[key] === 'undefined') target[key] = src[key]; + else if (isObject(src[key]) && isObject(target[key]) && Object.keys(src[key]).length > 0) { + if (src[key].__swiper__) target[key] = src[key]; + else extend(target[key], src[key]); + } else { + target[key] = src[key]; + } + }); } function needsNavigation(params = {}) { diff --git a/packages/rax-swiper/src/virtual.js b/packages/rax-swiper/src/virtual.js index f29accdb..6dffe83b 100644 --- a/packages/rax-swiper/src/virtual.js +++ b/packages/rax-swiper/src/virtual.js @@ -1,13 +1,22 @@ import cloneElement from 'rax-clone-element'; function updateOnVirtualData(swiper) { - if (!swiper || swiper.destroyed) return; + if ( + !swiper || + swiper.destroyed || + !swiper.params.virtual || + (swiper.params.virtual && !swiper.params.virtual.enabled) + ) + return; swiper.updateSlides(); swiper.updateProgress(); swiper.updateSlidesClasses(); if (swiper.lazy && swiper.params.lazy.enabled) { swiper.lazy.load(); } + if (swiper.parallax && swiper.params.parallax && swiper.params.parallax.enabled) { + swiper.parallax.setTranslate(); + } } function renderVirtual(swiper, slides, virtualData) { From 99cbefcb18e41fe32fa856017bf3854097532435 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8B=92=E7=8B=92=E7=A5=9E?= Date: Thu, 14 Oct 2021 17:33:27 +0800 Subject: [PATCH 02/16] refactor: group (#405) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: update swiper to be same with Swiper.js 6.8.4 fix: event handler can't update * docs: changelog * docs: annotation for `get-changed-params` * chore: version * WIP: modify engineer config * refactor: group * chore: add changelog * chore: typo * chore: update deps * chore: remove shouldBuild * chore: lint * chore: update ci node version * chore: remove install component deps * fix: build order * fix: conditional statement Co-authored-by: qingjue Co-authored-by: wjq990112 <1163585385@qq.com> Co-authored-by: 炽翎 <45777252+wjq990112@users.noreply.github.com> --- .github/workflows/auto-publisher.yml | 8 +- .github/workflows/ci.yml | 7 +- babel.config.js | 8 +- jest.config.js | 21 +- lerna.json | 11 +- package.json | 20 +- packages/rax-barcode/package.json | 7 +- packages/rax-canvas/package.json | 9 +- packages/rax-countdown/CHANGELOG.md | 4 + .../__tests__/__snapshots__/index.js.snap | 251 ++++++++++-------- packages/rax-countdown/package.json | 13 +- packages/rax-embed/package.json | 7 +- packages/rax-gesture-view/package.json | 7 +- packages/rax-icon/package.json | 9 +- packages/rax-image/package.json | 9 +- packages/rax-link/package.json | 8 +- packages/rax-modal/package.json | 2 +- packages/rax-parallax/package.json | 7 +- packages/rax-portal/package.json | 2 +- packages/rax-qrcode/package.json | 6 +- packages/rax-recyclerview/package.json | 11 +- packages/rax-refreshcontrol/package.json | 7 +- packages/rax-scrollview/package.json | 3 +- packages/rax-slider/package.json | 7 +- packages/rax-swiper/package.json | 3 +- packages/rax-swiper/src/loop.js | 2 +- packages/rax-swiper/src/swiper.js | 2 +- packages/rax-swiper/src/update-swiper.js | 18 +- packages/rax-tab-panel/package.json | 7 +- packages/rax-text/package.json | 10 +- packages/rax-textinput/package.json | 5 +- packages/rax-video/package.json | 5 +- packages/rax-view/__tests__/View.js | 21 +- packages/rax-view/__tests__/View.weex.js | 13 +- packages/rax-view/package.json | 9 +- packages/rax-waterfall/package.json | 17 +- packages/rax-xslider/package.json | 7 +- scripts/check-and-publish.js | 21 +- scripts/compile.js | 26 +- scripts/generateGraph.js | 44 +++ scripts/traverseGraph.js | 13 + 41 files changed, 382 insertions(+), 285 deletions(-) create mode 100644 scripts/generateGraph.js create mode 100644 scripts/traverseGraph.js diff --git a/.github/workflows/auto-publisher.yml b/.github/workflows/auto-publisher.yml index d36e6d08..c2975a17 100644 --- a/.github/workflows/auto-publisher.yml +++ b/.github/workflows/auto-publisher.yml @@ -1,6 +1,6 @@ name: Auto Publisher -on: +on: push: branches: - master @@ -12,9 +12,9 @@ jobs: - uses: actions/checkout@v1 - uses: actions/setup-node@v1 with: - node-version: 10 + node-version: 12 registry-url: https://registry.npmjs.org/ - - run: npm i + - run: npm run setup - run: npm run check-and-publish env: - NODE_AUTH_TOKEN: ${{secrets.npm_token}} \ No newline at end of file + NODE_AUTH_TOKEN: ${{secrets.npm_token}} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4da4b6a6..91c1c400 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,9 +9,8 @@ jobs: - uses: actions/checkout@v1 - uses: actions/setup-node@v1 with: - node-version: 10 + node-version: 12 registry-url: https://registry.npmjs.org/ - - run: npm i + - run: npm run setup - run: npm run lint - - run: npm run build - - run: npm run test \ No newline at end of file + - run: npm run test diff --git a/babel.config.js b/babel.config.js index 8e8b2355..a0b71727 100644 --- a/babel.config.js +++ b/babel.config.js @@ -5,6 +5,12 @@ module.exports = function(api) { return { 'presets': [ '@babel/preset-flow', + [ + '@babel/preset-typescript', + { + 'onlyRemoveTypeImports': true, + } + ], ['@babel/preset-env', { 'loose': true }], @@ -24,4 +30,4 @@ module.exports = function(api) { 'dist' ], }; -}; \ No newline at end of file +}; diff --git a/jest.config.js b/jest.config.js index 961b03d7..63ed4691 100644 --- a/jest.config.js +++ b/jest.config.js @@ -4,19 +4,9 @@ // yarn add --dev babel-jest 'babel-core@^7.0.0-bridge' @babel/core module.exports = { - 'collectCoverage': true, - 'verbose': true, - 'setupFiles': [ - './scripts/jest/setupEnvironment.js', - 'jest-localstorage-mock' - ], - 'setupFilesAfterEnv': [ - './scripts/jest/setupEnzyme.js', - ], - 'moduleNameMapper': { - '\\.(css|less|scss|sass)$': 'jest-transform-css' - }, - 'testPathIgnorePatterns': [ + setupFiles: ['./scripts/jest/setupEnvironment.js', 'jest-localstorage-mock'], + setupFilesAfterEnv: ['./scripts/jest/setupEnzyme.js'], + testPathIgnorePatterns: [ '/node_modules/', '/fixtures/', '/__modules__/', @@ -25,5 +15,8 @@ module.exports = { '/es/', '/build/', '/dist/', - ] + ], + 'moduleNameMapper': { + '\\.(css|less|scss|sass)$': 'jest-transform-css' + }, }; diff --git a/lerna.json b/lerna.json index d6707ca0..a6104a99 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,13 @@ { + "version": "1.7.0", + "npmClient": "yarn", + "useWorkspaces": true, + "command": { + "bootstrap": { + "npmClientArgs": ["--no-lockfile"] + } + }, "packages": [ "packages/*" - ], - "version": "0.0.0" + ] } diff --git a/package.json b/package.json index 4650c783..8f0ac231 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@babel/preset-env": "7.2.0", "@babel/preset-flow": "7.0.0", "@babel/preset-react": "7.0.0", + "@babel/preset-typescript": "^7.15.0", "@types/ali-app": "^1.0.0", "@types/jest": "^24.0.12", "@typescript-eslint/eslint-plugin": "^1.7.0", @@ -41,10 +42,10 @@ "eslint-plugin-import": "^2.14.0", "eslint-plugin-react": "~7.11.1", "findup": "^0.1.5", + "husky": "^4.2.5", "jest": "^26.0.1", "jest-diff": "^26.0.1", "jest-environment-node": "^26.0.1", - "husky": "^4.2.5", "jest-localstorage-mock": "^2.3.0", "jest-transform-css": "^2.0.0", "lerna": "^3.18.2", @@ -54,18 +55,22 @@ "rax-test-renderer": "^1.0.0", "rax-types": "^1.0.0", "semver": "^6.3.0", - "semver-regex": "^2.0.0", + "semver-regex": "^3.1.3", "shelljs": "0.8.3", "typescript": "^3.8.3" }, + "resolutions": { + "rax": "1.2.3-beta.1", + "rax-test-renderer": "1.1.0-beta.1" + }, "scripts": { - "bootstrap": "lerna bootstrap --no-ci", + "setup": "rm -rf node_modules && yarn install && npm run clean && npm run build", "build": "node ./scripts/compile.js", "lint:fix": "npm run lint -- --fix", "lint": "eslint --ext .js --ext .jsx --ext .ts --ext .tsx ./", "check-and-publish": "node ./scripts/check-and-publish", - "clean:compile": "rm -rf ./packages/*/lib", - "test": "jest --logHeapUsage --detectOpenHandles" + "test": "build-scripts test", + "clean": "rm -rf packages/*/node_modules && rm -rf ./packages/*/lib ./packages/*/es ./packages/*/dist" }, "config": { "ghooks": { @@ -74,5 +79,8 @@ "commitizen": { "path": "./node_modules/cz-conventional-changelog" } - } + }, + "workspaces": [ + "packages/*" + ] } diff --git a/packages/rax-barcode/package.json b/packages/rax-barcode/package.json index 13c92df7..69cb21c4 100644 --- a/packages/rax-barcode/package.json +++ b/packages/rax-barcode/package.json @@ -36,9 +36,10 @@ "rax": "^1.0.0" }, "devDependencies": { - "@types/rax": "^1.0.0", + "rax-test-renderer": "^1.0.0", "@types/ali-app": "^1.0.0", "@types/jest": "^24.0.12", + "@types/rax": "^1.0.0", "@typescript-eslint/eslint-plugin": "^1.7.0", "@typescript-eslint/parser": "^1.7.0", "csstype": "^2.6.4", @@ -48,8 +49,6 @@ "eslint": "^5.16.0", "eslint-config-rax": "^0.0.0", "miniprogram-api-typings": "^2.9.3", - "rax": "^1.0.0", - "rax-test-renderer": "^1.0.0", "rax-view": "^1.0.1" } -} \ No newline at end of file +} diff --git a/packages/rax-canvas/package.json b/packages/rax-canvas/package.json index f18df8ec..cf00d7e8 100644 --- a/packages/rax-canvas/package.json +++ b/packages/rax-canvas/package.json @@ -38,11 +38,10 @@ "rax": "^1.1.0" }, "devDependencies": { - "driver-universal": "^3.1.0", + "rax-test-renderer": "^1.0.0", "csstype": "^2.6.4", + "driver-universal": "^3.1.0", "eslint": "^5.16.0", - "eslint-config-rax": "^0.0.3", - "rax": "^1.1.0", - "rax-test-renderer": "^1.0.0" + "eslint-config-rax": "^0.0.3" } -} \ No newline at end of file +} diff --git a/packages/rax-countdown/CHANGELOG.md b/packages/rax-countdown/CHANGELOG.md index e1d3ea53..3db4e7fc 100644 --- a/packages/rax-countdown/CHANGELOG.md +++ b/packages/rax-countdown/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## v1.2.0 + +- Update rax-view and rax-text version to v2 + ## v1.1.7 - Keep miniapp native code in older dir diff --git a/packages/rax-countdown/__tests__/__snapshots__/index.js.snap b/packages/rax-countdown/__tests__/__snapshots__/index.js.snap index c46fbf2a..ac4c0b39 100644 --- a/packages/rax-countdown/__tests__/__snapshots__/index.js.snap +++ b/packages/rax-countdown/__tests__/__snapshots__/index.js.snap @@ -3,35 +3,37 @@ exports[`CountDown basic render 1`] = ` Object { "attributes": Object { - "class": "rax-view rax-countdown-main", + "class": "rax-view-v2 rax-countdown-main", }, "children": Array [ Object { "attributes": Object { - "class": "rax-view rax-countdown-item", + "class": "rax-view-v2 rax-countdown-item", }, "children": Array [ Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "0", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "0", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, @@ -40,42 +42,45 @@ Object { }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "天", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, Object { "attributes": Object { - "class": "rax-view rax-countdown-item", + "class": "rax-view-v2 rax-countdown-item", }, "children": Array [ Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "0", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "0", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, @@ -84,42 +89,45 @@ Object { }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "时", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, Object { "attributes": Object { - "class": "rax-view rax-countdown-item", + "class": "rax-view-v2 rax-countdown-item", }, "children": Array [ Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "0", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "0", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, @@ -128,42 +136,45 @@ Object { }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "分", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, Object { "attributes": Object { - "class": "rax-view rax-countdown-item", + "class": "rax-view-v2 rax-countdown-item", }, "children": Array [ Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "0", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "1", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, @@ -172,13 +183,14 @@ Object { }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "秒", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, @@ -190,59 +202,63 @@ Object { exports[`CountDown render days bigger than 99 1`] = ` Object { "attributes": Object { - "class": "rax-view rax-countdown-main", + "class": "rax-view-v2 rax-countdown-main", }, "children": Array [ Object { "attributes": Object { - "class": "rax-view rax-countdown-item", + "class": "rax-view-v2 rax-countdown-item", }, "children": Array [ Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "1", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "1", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "5", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "7", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, @@ -251,42 +267,45 @@ Object { }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "天", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, Object { "attributes": Object { - "class": "rax-view rax-countdown-item", + "class": "rax-view-v2 rax-countdown-item", }, "children": Array [ Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "0", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "9", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, @@ -295,42 +314,45 @@ Object { }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "时", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, Object { "attributes": Object { - "class": "rax-view rax-countdown-item", + "class": "rax-view-v2 rax-countdown-item", }, "children": Array [ Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "4", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "6", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, @@ -339,42 +361,45 @@ Object { }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "分", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, Object { "attributes": Object { - "class": "rax-view rax-countdown-item", + "class": "rax-view-v2 rax-countdown-item", }, "children": Array [ Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "4", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "0", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, @@ -383,13 +408,14 @@ Object { }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "秒", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, @@ -401,35 +427,37 @@ Object { exports[`CountDown render timeRemaining smaller than 0 1`] = ` Object { "attributes": Object { - "class": "rax-view rax-countdown-main", + "class": "rax-view-v2 rax-countdown-main", }, "children": Array [ Object { "attributes": Object { - "class": "rax-view rax-countdown-item", + "class": "rax-view-v2 rax-countdown-item", }, "children": Array [ Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "0", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "0", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, @@ -438,42 +466,45 @@ Object { }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "分", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, Object { "attributes": Object { - "class": "rax-view rax-countdown-item", + "class": "rax-view-v2 rax-countdown-item", }, "children": Array [ Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "0", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "0", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, @@ -482,13 +513,14 @@ Object { }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "秒", ], "style": Object { - "webkitLineClamp": undefined, + "WebkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, @@ -500,40 +532,42 @@ Object { exports[`CountDown render with custom style 1`] = ` Object { "attributes": Object { - "class": "rax-view rax-countdown-main", + "class": "rax-view-v2 rax-countdown-main", }, "children": Array [ Object { "attributes": Object { - "class": "rax-view rax-countdown-item", + "class": "rax-view-v2 rax-countdown-item", }, "children": Array [ Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "0", ], "style": Object { + "WebkitLineClamp": undefined, "backgroundColor": "red", "color": "#007457", + "lineClamp": undefined, "marginLeft": "2rpx", "marginRight": "2rpx", - "webkitLineClamp": undefined, }, "tagName": "SPAN", }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "0", ], "style": Object { + "WebkitLineClamp": undefined, "backgroundColor": "yellow", - "webkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, @@ -542,48 +576,51 @@ Object { }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "-", ], "style": Object { + "WebkitLineClamp": undefined, "backgroundColor": "blue", - "webkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, Object { "attributes": Object { - "class": "rax-view rax-countdown-item", + "class": "rax-view-v2 rax-countdown-item", }, "children": Array [ Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "0", ], "style": Object { + "WebkitLineClamp": undefined, "backgroundColor": "red", "color": "#007457", + "lineClamp": undefined, "marginLeft": "2rpx", "marginRight": "2rpx", - "webkitLineClamp": undefined, }, "tagName": "SPAN", }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "0", ], "style": Object { + "WebkitLineClamp": undefined, "backgroundColor": "yellow", - "webkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, @@ -592,48 +629,51 @@ Object { }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "-", ], "style": Object { + "WebkitLineClamp": undefined, "backgroundColor": "blue", - "webkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, Object { "attributes": Object { - "class": "rax-view rax-countdown-item", + "class": "rax-view-v2 rax-countdown-item", }, "children": Array [ Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "0", ], "style": Object { + "WebkitLineClamp": undefined, "backgroundColor": "red", "color": "#007457", + "lineClamp": undefined, "marginLeft": "2rpx", "marginRight": "2rpx", - "webkitLineClamp": undefined, }, "tagName": "SPAN", }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "1", ], "style": Object { + "WebkitLineClamp": undefined, "backgroundColor": "yellow", - "webkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, @@ -642,48 +682,51 @@ Object { }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "-", ], "style": Object { + "WebkitLineClamp": undefined, "backgroundColor": "blue", - "webkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, Object { "attributes": Object { - "class": "rax-view rax-countdown-item", + "class": "rax-view-v2 rax-countdown-item", }, "children": Array [ Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "4", ], "style": Object { + "WebkitLineClamp": undefined, "backgroundColor": "red", "color": "#007457", + "lineClamp": undefined, "marginLeft": "2rpx", "marginRight": "2rpx", - "webkitLineClamp": undefined, }, "tagName": "SPAN", }, Object { "attributes": Object { - "class": "rax-text ", + "class": "rax-text-v2 ", }, "children": Array [ "0", ], "style": Object { + "WebkitLineClamp": undefined, "backgroundColor": "yellow", - "webkitLineClamp": undefined, + "lineClamp": undefined, }, "tagName": "SPAN", }, diff --git a/packages/rax-countdown/package.json b/packages/rax-countdown/package.json index 112cf23a..29472141 100644 --- a/packages/rax-countdown/package.json +++ b/packages/rax-countdown/package.json @@ -1,6 +1,6 @@ { "name": "rax-countdown", - "version": "1.1.7", + "version": "1.2.0", "description": "Countdown component for Rax.", "license": "BSD-3-Clause", "main": "lib/index.js", @@ -34,18 +34,19 @@ "react-component" ], "dependencies": { - "rax-image": "^1.0.0", "prop-types": "^15.7.2", - "rax-text": "^1.0.0", - "rax-view": "^1.0.0" + "rax-image": "^1.0.0", + "rax-text": "^2.0.0", + "rax-view": "^2.0.0" }, "peerDependencies": { "rax": "^1.0.0" }, "devDependencies": { - "@types/rax": "^1.0.0", + "rax-test-renderer": "^1.0.0", "@types/ali-app": "^1.0.0", "@types/jest": "^24.0.12", + "@types/rax": "^1.0.0", "@typescript-eslint/eslint-plugin": "^1.7.0", "@typescript-eslint/parser": "^1.7.0", "csstype": "^2.6.4", @@ -54,8 +55,6 @@ "driver-weex": "^1.0.0", "eslint": "^5.16.0", "eslint-config-rax": "^0.0.0", - "rax": "^1.0.0", - "rax-test-renderer": "^1.0.0", "typescript": "^3.9.2" } } diff --git a/packages/rax-embed/package.json b/packages/rax-embed/package.json index bac01238..8461334b 100644 --- a/packages/rax-embed/package.json +++ b/packages/rax-embed/package.json @@ -11,8 +11,8 @@ "lib", "dist" ], - "exports":{ - ".":{ + "exports": { + ".": { "web": "./es/web/index.js", "weex": "./es/weex/index.js", "miniapp": "./es/miniapp/index.js", @@ -55,6 +55,7 @@ "rax": "^1.0.0" }, "devDependencies": { + "rax-test-renderer": "^1.0.0", "@types/ali-app": "^1.0.0", "@types/jest": "^24.0.12", "@types/rax": "^1.0.0", @@ -64,8 +65,6 @@ "driver-universal": "^3.0.0", "eslint": "^5.16.0", "eslint-config-rax": "^0.0.0", - "rax": "^1.0.0", - "rax-test-renderer": "^1.0.0", "typescript": "^3.6.2" } } diff --git a/packages/rax-gesture-view/package.json b/packages/rax-gesture-view/package.json index 88d42b8e..07a86ca5 100644 --- a/packages/rax-gesture-view/package.json +++ b/packages/rax-gesture-view/package.json @@ -36,17 +36,16 @@ "rax-types": "^1.0.0" }, "devDependencies": { - "@types/rax": "^1.0.0", + "rax-test-renderer": "^1.0.0", "@types/ali-app": "^1.0.0", "@types/jest": "^24.0.12", + "@types/rax": "^1.0.0", "@typescript-eslint/eslint-plugin": "^1.7.0", "@typescript-eslint/parser": "^1.7.0", "csstype": "^2.6.4", "driver-universal": "^1.0.2", "eslint": "^5.16.0", "eslint-config-rax": "^0.0.0", - "rax": "^1.0.0", - "rax-test-renderer": "^1.0.0", "rax-text": "^1.1.6" } -} \ No newline at end of file +} diff --git a/packages/rax-icon/package.json b/packages/rax-icon/package.json index 803aaa20..c0e7419f 100644 --- a/packages/rax-icon/package.json +++ b/packages/rax-icon/package.json @@ -11,8 +11,8 @@ "lib", "dist" ], - "exports":{ - ".":{ + "exports": { + ".": { "web": "./es/web/index.js", "weex": "./es/weex/index.js", "miniapp": "./es/miniapp-runtime/index.js", @@ -57,8 +57,9 @@ "rax": "^1.0.0" }, "devDependencies": { - "@types/rax": "^1.0.0", + "rax-test-renderer": "^1.0.0", "@types/jest": "^24.0.12", + "@types/rax": "^1.0.0", "@typescript-eslint/eslint-plugin": "^1.7.0", "@typescript-eslint/parser": "^1.7.0", "csstype": "^2.6.4", @@ -68,8 +69,6 @@ "eslint-config-rax": "^0.0.0", "mini-types": "^0.1.1", "miniprogram-api-typings": "^2.10.1", - "rax": "^1.0.0", - "rax-test-renderer": "^1.0.0", "rax-view": "^1.0.2" } } diff --git a/packages/rax-image/package.json b/packages/rax-image/package.json index 67e06511..8b3ca3d8 100644 --- a/packages/rax-image/package.json +++ b/packages/rax-image/package.json @@ -11,8 +11,8 @@ "lib", "dist" ], - "exports":{ - ".":{ + "exports": { + ".": { "web": "./es/web/index.js", "weex": "./es/weex/index.js", "miniapp": "./es/miniapp-runtime/index.js", @@ -56,17 +56,16 @@ "rax": "^1.0.0" }, "devDependencies": { - "@types/rax": "^1.0.0", + "rax-test-renderer": "^1.0.0", "@types/ali-app": "^1.0.0", "@types/jest": "^24.0.12", + "@types/rax": "^1.0.0", "@typescript-eslint/eslint-plugin": "^1.7.0", "@typescript-eslint/parser": "^1.7.0", "csstype": "^2.6.4", "driver-universal": "^1.0.0-beta.5", "eslint": "^5.16.0", "eslint-config-rax": "^0.0.0", - "rax": "^1.0.0", - "rax-test-renderer": "^1.0.0", "rax-text": "^1.0.1", "typescript": "^3.5.3" } diff --git a/packages/rax-link/package.json b/packages/rax-link/package.json index 81b82c92..d24097ac 100644 --- a/packages/rax-link/package.json +++ b/packages/rax-link/package.json @@ -11,8 +11,8 @@ "lib", "dist" ], - "exports":{ - ".":{ + "exports": { + ".": { "web": "./es/common/index.js", "weex": "./es/common/index.js", "miniapp": "./es/miniapp-runtime/index.js", @@ -24,7 +24,6 @@ }, "./*": "./*" }, - "miniprogram": ".", "miniappConfig": { "main": "lib/miniapp-native/ali-miniapp/index", @@ -58,6 +57,7 @@ "rax": "^1.0.0" }, "devDependencies": { + "rax-test-renderer": "^1.0.0", "@types/ali-app": "^1.0.0", "@types/jest": "^24.0.12", "@types/rax": "^1.0.0", @@ -67,8 +67,6 @@ "driver-universal": "^1.0.0", "eslint": "^5.16.0", "eslint-config-rax": "^0.0.0", - "rax": "^1.0.0", - "rax-test-renderer": "^1.0.0", "typescript": "^3.6.2" } } diff --git a/packages/rax-modal/package.json b/packages/rax-modal/package.json index d58a4357..97d76788 100644 --- a/packages/rax-modal/package.json +++ b/packages/rax-modal/package.json @@ -45,6 +45,7 @@ "rax": "^1.0.4" }, "devDependencies": { + "rax-test-renderer": "^1.0.0", "@types/rax": "^1.0.0", "@typescript-eslint/eslint-plugin": "^1.7.0", "@typescript-eslint/parser": "^1.7.0", @@ -53,7 +54,6 @@ "eslint": "^5.16.0", "eslint-config-rax": "^0.0.0", "lerna": "^3.16.4", - "rax-test-renderer": "^1.0.0", "typescript": "^3.6.2" } } diff --git a/packages/rax-parallax/package.json b/packages/rax-parallax/package.json index b218b87a..cb8bd168 100644 --- a/packages/rax-parallax/package.json +++ b/packages/rax-parallax/package.json @@ -34,9 +34,10 @@ "rax": "^1.0.0" }, "devDependencies": { - "@types/rax": "^1.0.0", + "rax-test-renderer": "^1.0.0", "@types/ali-app": "^1.0.0", "@types/jest": "^24.0.12", + "@types/rax": "^1.0.0", "@typescript-eslint/eslint-plugin": "^1.7.0", "@typescript-eslint/parser": "^1.7.0", "babel-eslint": "^10.0.3", @@ -46,11 +47,9 @@ "driver-weex": "^1.0.0", "eslint": "^5.16.0", "eslint-config-rax": "^0.0.0", - "rax": "^1.0.0", "rax-image": "^1.0.0", "rax-recyclerview": "^1.1.0-4", "rax-slider": "^1.1.0-3", - "rax-test-renderer": "^1.0.0", "rax-text": "^1.0.1" } -} \ No newline at end of file +} diff --git a/packages/rax-portal/package.json b/packages/rax-portal/package.json index aaa12696..0fe07fca 100644 --- a/packages/rax-portal/package.json +++ b/packages/rax-portal/package.json @@ -37,6 +37,7 @@ "rax": "^1.0.4" }, "devDependencies": { + "rax-test-renderer": "^1.0.0", "@types/rax": "^1.0.0", "@typescript-eslint/eslint-plugin": "^1.7.0", "@typescript-eslint/parser": "^1.7.0", @@ -45,7 +46,6 @@ "eslint": "^5.16.0", "eslint-config-rax": "^0.0.0", "lerna": "^3.16.4", - "rax-test-renderer": "^1.0.0", "rax-text": "^1.1.0", "rax-view": "^1.1.0", "typescript": "^3.6.2" diff --git a/packages/rax-qrcode/package.json b/packages/rax-qrcode/package.json index c3a9fd9f..d57e6daa 100644 --- a/packages/rax-qrcode/package.json +++ b/packages/rax-qrcode/package.json @@ -37,9 +37,10 @@ "rax": "^1.0.0" }, "devDependencies": { - "@types/rax": "^1.0.0", + "rax-test-renderer": "^1.0.0", "@types/ali-app": "^1.0.0", "@types/jest": "^24.0.12", + "@types/rax": "^1.0.0", "@typescript-eslint/eslint-plugin": "^1.7.0", "@typescript-eslint/parser": "^1.7.0", "csstype": "^2.6.4", @@ -49,7 +50,6 @@ "eslint": "^5.16.0", "eslint-config-rax": "^0.0.0", "miniprogram-api-typings": "^2.9.3", - "rax-test-renderer": "^1.0.0", "rax-view": "^1.0.1" } -} \ No newline at end of file +} diff --git a/packages/rax-recyclerview/package.json b/packages/rax-recyclerview/package.json index 83ab765d..6c669149 100644 --- a/packages/rax-recyclerview/package.json +++ b/packages/rax-recyclerview/package.json @@ -51,9 +51,9 @@ "react-component" ], "dependencies": { + "prop-types": "^15.7.2", "rax-children": "^1.0.0", "rax-find-dom-node": "^1.0.0", - "prop-types": "^15.7.2", "rax-refreshcontrol": "^1.0.0", "rax-scrollview": "^3.3.3", "rax-view": "^2.0.0", @@ -63,9 +63,10 @@ "rax": "^1.0.0" }, "devDependencies": { - "@types/rax": "^1.0.0", + "rax-test-renderer": "^1.0.0", "@types/ali-app": "^1.0.0", "@types/jest": "^24.0.12", + "@types/rax": "^1.0.0", "@typescript-eslint/eslint-plugin": "^1.7.0", "@typescript-eslint/parser": "^1.7.0", "babel-eslint": "^10.0.3", @@ -75,13 +76,11 @@ "driver-weex": "^1.0.0", "eslint": "^5.16.0", "eslint-config-rax": "^0.0.0", - "rax": "^1.0.0", "rax-picture": "^1.0.1", "rax-slider": "^1.1.0-0", "rax-tab-panel": "^1.0.1", - "rax-test-renderer": "^1.0.0", "rax-text": "^2.0.0", - "universal-transition": "^1.0.0", - "typescript": "^3.5.3" + "typescript": "^3.5.3", + "universal-transition": "^1.0.0" } } diff --git a/packages/rax-refreshcontrol/package.json b/packages/rax-refreshcontrol/package.json index 68455b64..f6770f50 100644 --- a/packages/rax-refreshcontrol/package.json +++ b/packages/rax-refreshcontrol/package.json @@ -34,9 +34,10 @@ "rax": "^1.0.0" }, "devDependencies": { - "@types/rax": "^1.0.0", + "rax-test-renderer": "^1.0.0", "@types/ali-app": "^1.0.0", "@types/jest": "^24.0.12", + "@types/rax": "^1.0.0", "@typescript-eslint/eslint-plugin": "^1.7.0", "@typescript-eslint/parser": "^1.7.0", "csstype": "^2.6.4", @@ -45,10 +46,8 @@ "driver-weex": "^1.0.0", "eslint": "^5.16.0", "eslint-config-rax": "^0.0.0", - "rax": "^1.0.0", "rax-recyclerview": "^1.0.0-beta.2", - "rax-test-renderer": "^1.0.0", "rax-text": "^1.0.1", "rax-view": "^1.0.1" } -} \ No newline at end of file +} diff --git a/packages/rax-scrollview/package.json b/packages/rax-scrollview/package.json index 4ad85fca..f746f172 100644 --- a/packages/rax-scrollview/package.json +++ b/packages/rax-scrollview/package.json @@ -65,6 +65,7 @@ "rax": "^1.0.0" }, "devDependencies": { + "rax-test-renderer": "^1.0.0", "@types/ali-app": "^1.0.0", "@types/classnames": "^2.2.9", "@types/jest": "^24.0.12", @@ -76,8 +77,6 @@ "driver-weex": "^1.0.0", "eslint": "^5.16.0", "eslint-config-rax": "^0.0.0", - "rax": "^1.1.0", - "rax-test-renderer": "^1.0.0", "typescript": "^3.5.3" } } diff --git a/packages/rax-slider/package.json b/packages/rax-slider/package.json index 78d612c8..c204dc1a 100644 --- a/packages/rax-slider/package.json +++ b/packages/rax-slider/package.json @@ -11,8 +11,8 @@ "lib", "dist" ], - "exports":{ - ".":{ + "exports": { + ".": { "web": "./es/web/index.js", "weex": "./es/weex/index.js", "miniapp": "./es/miniapp-runtime/index.js", @@ -68,11 +68,10 @@ "rax": "^1.1.0" }, "devDependencies": { + "rax-test-renderer": "^1.0.0", "@types/rax": "^1.0.0", "driver-universal": "^3.1.0", - "rax": "^1.1.0", "rax-image": "^2.2.0", - "rax-test-renderer": "^1.0.0", "typescript": "^3.7.5" } } diff --git a/packages/rax-swiper/package.json b/packages/rax-swiper/package.json index 074281f6..8aaeb7da 100644 --- a/packages/rax-swiper/package.json +++ b/packages/rax-swiper/package.json @@ -37,11 +37,10 @@ "rax": "^1.1.0" }, "devDependencies": { + "rax-test-renderer": "^1.0.0", "@types/rax": "^1.0.0", "driver-universal": "^3.1.0", - "rax": "^1.1.0", "rax-image": "^2.2.0", - "rax-test-renderer": "^1.0.0", "typescript": "^3.7.5" } } diff --git a/packages/rax-swiper/src/loop.js b/packages/rax-swiper/src/loop.js index d00a8dfe..93ef00b3 100644 --- a/packages/rax-swiper/src/loop.js +++ b/packages/rax-swiper/src/loop.js @@ -36,7 +36,7 @@ function renderLoop(swiper, slides, swiperParams) { if (swiperParams.loopFillGroupWithBlank) { const blankSlidesNum = - swiperParams.slidesPerGroup - (modifiedSlides.length % swiperParams.slidesPerGroup); + swiperParams.slidesPerGroup - modifiedSlides.length % swiperParams.slidesPerGroup; if (blankSlidesNum !== swiperParams.slidesPerGroup) { for (let i = 0; i < blankSlidesNum; i += 1) { const blankSlide = ( diff --git a/packages/rax-swiper/src/swiper.js b/packages/rax-swiper/src/swiper.js index 6acd5b05..56100b4c 100644 --- a/packages/rax-swiper/src/swiper.js +++ b/packages/rax-swiper/src/swiper.js @@ -172,7 +172,7 @@ const Swiper = forwardRef( if (swiperParams.virtual) { return renderVirtual(swiperRef.current, slides, virtualData); } - if (!swiperParams.loop || (swiperRef.current && swiperRef.current.destroyed)) { + if (!swiperParams.loop || swiperRef.current && swiperRef.current.destroyed) { return slides.map((child) => { return cloneElement(child, { swiper: swiperRef.current }); }); diff --git a/packages/rax-swiper/src/update-swiper.js b/packages/rax-swiper/src/update-swiper.js index 0b813ca3..f9006632 100644 --- a/packages/rax-swiper/src/update-swiper.js +++ b/packages/rax-swiper/src/update-swiper.js @@ -1,15 +1,15 @@ import { isObject, extend } from './utils'; function updateSwiper({ - swiper, - slides, - passedParams, - changedParams, - nextEl, - prevEl, - scrollbarEl, - paginationEl, - }) { + swiper, + slides, + passedParams, + changedParams, + nextEl, + prevEl, + scrollbarEl, + paginationEl, +}) { const updateParams = changedParams.filter((key) => key !== 'children' && key !== 'direction'); const { params: currentParams, pagination, navigation, scrollbar, virtual, thumbs } = swiper; let needThumbsInit; diff --git a/packages/rax-tab-panel/package.json b/packages/rax-tab-panel/package.json index 3fd7869a..a7e76eff 100644 --- a/packages/rax-tab-panel/package.json +++ b/packages/rax-tab-panel/package.json @@ -51,6 +51,7 @@ "rax": "^1.0.0" }, "devDependencies": { + "rax-test-renderer": "^1.0.0", "@types/ali-app": "^1.0.1", "@types/jest": "^24.0.12", "@types/rax": "^1.0.0", @@ -62,8 +63,6 @@ "driver-weex": "^1.0.0", "eslint": "^5.16.0", "eslint-config-rax": "^0.0.0", - "rax": "^1.0.0", - "rax-scrollview": "^1.0.0", - "rax-test-renderer": "^1.0.0" + "rax-scrollview": "^1.0.0" } -} \ No newline at end of file +} diff --git a/packages/rax-text/package.json b/packages/rax-text/package.json index 50458ad8..e7db466a 100644 --- a/packages/rax-text/package.json +++ b/packages/rax-text/package.json @@ -11,8 +11,8 @@ "lib", "dist" ], - "exports":{ - ".":{ + "exports": { + ".": { "web": "./es/web/index.js", "weex": "./es/weex/index.js", "miniapp": "./es/miniapp-runtime/index.js", @@ -54,9 +54,10 @@ "rax": "^1.0.0" }, "devDependencies": { - "@types/rax": "^1.0.0", + "rax-test-renderer": "^1.0.0", "@types/ali-app": "^1.0.0", "@types/jest": "^24.0.12", + "@types/rax": "^1.0.0", "@typescript-eslint/eslint-plugin": "^1.7.0", "@typescript-eslint/parser": "^1.7.0", "csstype": "^2.6.4", @@ -66,9 +67,6 @@ "eslint": "^5.16.0", "eslint-config-rax": "^0.0.0", "jest-transform-css": "^2.0.0", - "rax": "^1.0.0", - "rax-server-renderer": "^1.1.5", - "rax-test-renderer": "^1.0.0", "rax-view": "^1.1.0", "universal-env": "^3.0.0" } diff --git a/packages/rax-textinput/package.json b/packages/rax-textinput/package.json index ece83da1..6fff1265 100644 --- a/packages/rax-textinput/package.json +++ b/packages/rax-textinput/package.json @@ -41,10 +41,11 @@ "rax": "^1.0.0" }, "devDependencies": { - "@types/rax": "^1.0.0", + "rax-test-renderer": "^1.0.0", "@types/ali-app": "^1.0.0", "@types/classnames": "^2.2.9", "@types/jest": "^24.0.12", + "@types/rax": "^1.0.0", "@typescript-eslint/eslint-plugin": "^1.7.0", "@typescript-eslint/parser": "^1.7.0", "csstype": "^2.6.4", @@ -52,8 +53,6 @@ "eslint": "^5.16.0", "eslint-config-rax": "^0.0.0", "jest-transform-css": "^2.0.0", - "rax": "^1.0.0", - "rax-test-renderer": "^1.0.0", "rax-text": "^1.0.1", "rax-view": "^1.0.1" } diff --git a/packages/rax-video/package.json b/packages/rax-video/package.json index e5a32deb..4f00b9c3 100644 --- a/packages/rax-video/package.json +++ b/packages/rax-video/package.json @@ -59,16 +59,15 @@ "rax": "^1.0.0" }, "devDependencies": { - "@types/rax": "^1.0.0", + "rax-test-renderer": "^1.0.0", "@types/ali-app": "^1.0.0", "@types/classnames": "^2.2.9", "@types/jest": "^24.0.12", + "@types/rax": "^1.0.0", "@typescript-eslint/eslint-plugin": "^1.7.0", "@typescript-eslint/parser": "^1.7.0", "csstype": "^2.6.4", "driver-universal": "^1.0.2", - "rax": "^1.0.0", - "rax-test-renderer": "^1.0.0", "rax-text": "^1.3.1", "rax-view": "^1.1.0-0", "typescript": "^3.6.2" diff --git a/packages/rax-view/__tests__/View.js b/packages/rax-view/__tests__/View.js index 4e48e07b..e1310089 100644 --- a/packages/rax-view/__tests__/View.js +++ b/packages/rax-view/__tests__/View.js @@ -1,13 +1,16 @@ global.callNative = null; -import {createElement} from 'rax'; +import { createElement } from 'rax'; import renderer from 'rax-test-renderer'; -import View from '../lib'; +import View from '../src'; describe('View', () => { + beforeEach(function() { + jest.useFakeTimers(); + }); it('render tag view', () => { - const component = renderer.create( - Example - ); + const el = Example; + const component = renderer.create(el); + jest.runAllTimers(); let tree = component.toJSON(); expect(tree.tagName).toEqual('DIV'); expect(tree.children[0]).toEqual('Example'); @@ -15,18 +18,14 @@ describe('View', () => { it('turn onPress to onClick', () => { const mockPress = jest.fn(); - const component = renderer.create( - Example - ); + const component = renderer.create(Example); let tree = component.toJSON(); expect(tree.eventListeners.click).toBe(mockPress); }); // not work it.skip('style in View', () => { - const component = renderer.create( - Example - ); + const component = renderer.create(Example); let tree = component.toJSON(); expect(tree.style.border).toBe('0 solid black'); expect(tree.style.boxSizing).toBe('border-box'); diff --git a/packages/rax-view/__tests__/View.weex.js b/packages/rax-view/__tests__/View.weex.js index 0d6d4b12..b57814bd 100644 --- a/packages/rax-view/__tests__/View.weex.js +++ b/packages/rax-view/__tests__/View.weex.js @@ -1,18 +1,19 @@ -import {createElement} from 'rax'; +import { createElement } from 'rax'; import renderer from 'rax-test-renderer'; -import View from '../lib/'; +import View from '../lib'; + +console.log('==>', View); jest.mock('universal-env', () => { return { - isWeex: true + isWeex: true, }; }); describe('View in weex', () => { it('render tag view', () => { - const component = renderer.create( - Example - ); + const component = renderer.create(Example); + let tree = component.toJSON(); expect(tree.tagName).toEqual('DIV'); diff --git a/packages/rax-view/package.json b/packages/rax-view/package.json index 056a5e60..c7c05017 100644 --- a/packages/rax-view/package.json +++ b/packages/rax-view/package.json @@ -11,8 +11,8 @@ "lib", "dist" ], - "exports":{ - ".":{ + "exports": { + ".": { "web": "./es/common/index.js", "weex": "./es/common/index.js", "wechat-miniprogram": "./es/miniapp/wechat.js", @@ -50,10 +50,11 @@ "rax": "^1.0.0" }, "devDependencies": { - "@types/rax": "^1.0.0", + "rax-test-renderer": "^1.0.0", "@types/ali-app": "^1.0.0", "@types/classnames": "^2.2.9", "@types/jest": "^24.0.12", + "@types/rax": "^1.0.0", "@typescript-eslint/eslint-plugin": "^1.7.0", "@typescript-eslint/parser": "^1.7.0", "babel-eslint": "^10.0.3", @@ -64,8 +65,6 @@ "eslint": "^5.16.0", "eslint-config-rax": "^0.0.0", "eslint-plugin-import": "^2.18.2", - "rax": "^1.0.0", - "rax-test-renderer": "^1.0.0", "typescript": "^3.5.3" } } diff --git a/packages/rax-waterfall/package.json b/packages/rax-waterfall/package.json index f17ec170..35cac6e3 100644 --- a/packages/rax-waterfall/package.json +++ b/packages/rax-waterfall/package.json @@ -11,8 +11,8 @@ "lib", "dist" ], - "exports":{ - ".":{ + "exports": { + ".": { "web": "./es/common/index.js", "miniapp": "./es/common/index.js", "wechat-miniprogram": "./es/common/index.js", @@ -43,20 +43,21 @@ "react-component" ], "dependencies": { + "rax-clone-element": "^1.0.0", "rax-refreshcontrol": "^1.0.0", "rax-scrollview": "^1.0.0", - "rax-view": "^1.0.1", "rax-text": "^1.0.1", - "universal-env": "^3.0.0", - "rax-clone-element": "^1.0.0" + "rax-view": "^1.0.1", + "universal-env": "^3.0.0" }, "peerDependencies": { "rax": "^1.0.0" }, "devDependencies": { - "@types/rax": "^1.0.0", + "rax-test-renderer": "^1.0.0", "@types/ali-app": "^1.0.0", "@types/jest": "^24.0.12", + "@types/rax": "^1.0.0", "@typescript-eslint/eslint-plugin": "^1.7.0", "@typescript-eslint/parser": "^1.7.0", "csstype": "^2.6.4", @@ -64,8 +65,6 @@ "driver-universal": "^1.0.0", "driver-weex": "^1.0.0", "eslint": "^5.16.0", - "eslint-config-rax": "^0.0.0", - "rax": "^1.0.0", - "rax-test-renderer": "^1.0.0" + "eslint-config-rax": "^0.0.0" } } diff --git a/packages/rax-xslider/package.json b/packages/rax-xslider/package.json index e1c47a04..2101d0f6 100644 --- a/packages/rax-xslider/package.json +++ b/packages/rax-xslider/package.json @@ -46,9 +46,10 @@ "rax": "^1.0.0" }, "devDependencies": { - "@types/rax": "^1.0.0", + "rax-test-renderer": "^1.0.0", "@types/ali-app": "^1.0.0", "@types/jest": "^24.0.12", + "@types/rax": "^1.0.0", "@typescript-eslint/eslint-plugin": "^1.7.0", "@typescript-eslint/parser": "^1.7.0", "csstype": "^2.6.4", @@ -56,8 +57,6 @@ "driver-universal": "^1.0.0", "eslint": "^5.16.0", "eslint-config-rax": "^0.0.0", - "rax": "^1.0.0", - "rax-test-renderer": "^1.0.0", "typescript": "^3.5.2" } -} \ No newline at end of file +} diff --git a/scripts/check-and-publish.js b/scripts/check-and-publish.js index 1201dbd6..98e3d10a 100644 --- a/scripts/check-and-publish.js +++ b/scripts/check-and-publish.js @@ -8,7 +8,7 @@ const axios = require('axios'); const semver = require('semver'); function checkVersion(folder, callback) { - const ret = []; // { name: 'foo', workDir, latest: 'x.x.x', local: 'x.x.x', shouldBuild } + const ret = []; // { name: 'foo', workDir, latest: 'x.x.x', local: 'x.x.x' } if (existsSync(folder)) { const packages = readdirSync(folder); console.log('[PUBLISH] Start check with following packages:'); @@ -35,8 +35,6 @@ function checkVersion(folder, callback) { workDir: join(folder, packageFolderName), main: packageInfo.main, local: packageInfo.version, - // If exists scripts.build, then run it. - shouldBuild: !!(packageInfo.scripts && packageInfo.scripts.build), }); } @@ -59,7 +57,7 @@ function checkBuildSuccess(workDir, main) { return existsSync(join(workDir, main)); } -function publish(pkg, workDir, main, version, shouldBuild, tag) { +function publish(pkg, workDir, main, version, tag) { console.log('[PUBLISH]', `${pkg}@${version}`); // npm install @@ -70,17 +68,6 @@ function publish(pkg, workDir, main, version, shouldBuild, tag) { cwd: workDir, }); - if (shouldBuild) { - // npm run build - spawnSync('npm', [ - 'run', - 'build', - ], { - stdio: 'inherit', - cwd: workDir, - }); - } - // npm publish if (checkBuildSuccess(workDir, main)) { spawnSync('npm', [ @@ -112,10 +99,10 @@ function checkVersionAndPublish() { } for (let i = 0; i < ret.length; i++) { - const { name, workDir, main, local, shouldBuild } = ret[i]; + const { name, workDir, main, local } = ret[i]; const tag = isPrerelease(local) ? 'beta' : 'latest'; console.log(`--- ${name}@${local} current tag: ${tag} ---`); - publish(name, workDir, main, local, shouldBuild, tag); + publish(name, workDir, main, local, tag); } }); } diff --git a/scripts/compile.js b/scripts/compile.js index e7dadf69..beb20ef4 100644 --- a/scripts/compile.js +++ b/scripts/compile.js @@ -10,6 +10,8 @@ const path = require('path'); const shell = require('shelljs'); const chalk = require('chalk'); const parseArgs = require('minimist'); +const generateGraph = require('./generateGraph'); +const traverseGraph = require('./traverseGraph'); const args = parseArgs(process.argv); const customPackages = args.packages; @@ -18,20 +20,34 @@ const packagesDir = path.resolve(__dirname, '../packages'); (async function compile() { process.stdout.write(chalk.bold.inverse('Compiling packages\n')); - const packages = getPackages(packagesDir, customPackages); + const packages = getPackages(packagesDir, customPackages) + .map((packageName) => { + const pkgData = JSON.parse(fs.readFileSync(path.join(packagesDir, packageName, 'package.json'))); + return { + name: pkgData.name, + dependencies: pkgData.dependencies, + localDependencies: [], + localDependents: [], + }; + }); + + const graph = generateGraph(packages); + const needBuildPackages = []; - for (const packageName of packages) { + graph.forEach((pkgInfo) => { + traverseGraph(pkgInfo, needBuildPackages); + }); + + for (const packageName of needBuildPackages) { await buildPackage(packageName); } })(); function buildPackage(packageName) { const packagePath = path.join(packagesDir, packageName); - const npm = process.env.NPM || 'npm'; console.log(`\n Start compile ${packageName}`); shell.cd(packagePath); - shell.exec(npm === 'tnpm' ? 'tnpm update' : `${npm} install`); shell.exec('npm run build'); console.log(`\n Success compile ${packageName}`); } @@ -48,4 +64,4 @@ function getPackages(packagesDir, customPackages) { return true; } }); -} \ No newline at end of file +} diff --git a/scripts/generateGraph.js b/scripts/generateGraph.js new file mode 100644 index 00000000..bef9e95d --- /dev/null +++ b/scripts/generateGraph.js @@ -0,0 +1,44 @@ +function checkDependent(source, target) { + const { dependencies: sourceDependencies, name: sourceName } = source; + const { dependencies: targetDependencies, name: tagetName } = target; + return [ + Object.keys(sourceDependencies).includes(tagetName), + Object.keys(targetDependencies).includes(sourceName), + ]; +} + +/** + * @param {*} packageEntities package + * @returns packageEntities + */ +function generateGraph(packageEntities) { + const graphs = [...packageEntities]; + + for (let i = 0; i < graphs.length; i++) { + for (let j = i + 1; j < graphs.length; j++) { + const source = graphs[i]; + const target = graphs[j]; + + const [targetExist, sourceExist] = checkDependent(source, target); + + if (targetExist && sourceExist) { + const message = `[BUILDER] Circular dependencies between ${source.name} and ${target.name}`; + throw new Error(message); + } + + if (targetExist) { + source.localDependencies.push(target); + target.localDependents.push(source); + } + + if (sourceExist) { + source.localDependents.push(target); + target.localDependencies.push(source); + } + } + } + + return graphs; +} + +module.exports = generateGraph; diff --git a/scripts/traverseGraph.js b/scripts/traverseGraph.js new file mode 100644 index 00000000..e42abe3e --- /dev/null +++ b/scripts/traverseGraph.js @@ -0,0 +1,13 @@ +module.exports = function traverseGraph(packages, completedPackages) { + const { name, localDependencies } = packages; + if (completedPackages.includes(name)) return; + + if (localDependencies.length) { + for (let i = 0; i < localDependencies.length; i++) { + // eslint-disable-next-line no-await-in-loop + traverseGraph(localDependencies[i], completedPackages); + } + } + + completedPackages.push(name); +} From db7c8b6ace6310a5f2044e51abcc1adcc1c19ca6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=82=BD=E7=BF=8E?= <45777252+wjq990112@users.noreply.github.com> Date: Thu, 14 Oct 2021 17:47:45 +0800 Subject: [PATCH 03/16] fix: conditional statement --- packages/rax-swiper/src/virtual.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rax-swiper/src/virtual.js b/packages/rax-swiper/src/virtual.js index 6dffe83b..531e626f 100644 --- a/packages/rax-swiper/src/virtual.js +++ b/packages/rax-swiper/src/virtual.js @@ -5,7 +5,7 @@ function updateOnVirtualData(swiper) { !swiper || swiper.destroyed || !swiper.params.virtual || - (swiper.params.virtual && !swiper.params.virtual.enabled) + swiper.params.virtual && !swiper.params.virtual.enabled ) return; swiper.updateSlides(); From 850a8cd4fb6085bbd1ad2a3ae0774533bd016317 Mon Sep 17 00:00:00 2001 From: "chenrongyan.cry" Date: Mon, 25 Oct 2021 21:06:16 +0800 Subject: [PATCH 04/16] refactor/recyclerview --- packages/rax-recyclerview/CHANGELOG.md | 7 + packages/rax-recyclerview/README.md | 215 +++++------- packages/rax-recyclerview/demo/base.jsx | 72 +++-- packages/rax-recyclerview/demo/index.md | 170 +++++----- packages/rax-recyclerview/package.json | 18 +- .../src/VirtualizedList/BaseList.js | 306 ------------------ .../src/VirtualizedList/NestedList.js | 52 --- .../src/VirtualizedList/NoRecycleList.tsx | 24 ++ .../VirtualizedList/SizeAndPositionManager.js | 277 ---------------- .../VirtualizedList/SizeAndPositionManager.ts | 152 +++++++++ .../src/VirtualizedList/constants.js | 41 --- .../src/VirtualizedList/index.js | 209 ------------ .../src/VirtualizedList/index.tsx | 133 ++++++++ .../{throttle.js => throttle.ts} | 11 +- .../src/VirtualizedList/timer.js | 119 ------- .../src/VirtualizedList/types.ts | 21 ++ .../src/__tests__/RecyclerView.js | 2 +- .../src/__tests__/RecyclerView.weex.js | 2 +- packages/rax-recyclerview/src/index.js | 176 ---------- packages/rax-recyclerview/src/index.tsx | 19 ++ .../src/miniapp-runtime/getInfoSync.ts | 41 +++ .../src/miniapp-runtime/index.ts | 14 + packages/rax-recyclerview/src/web/index.tsx | 12 + packages/rax-recyclerview/src/weex/index.js | 139 ++++++++ packages/rax-scrollview/CHANGELOG.md | 4 + packages/rax-scrollview/package.json | 2 +- packages/rax-scrollview/src/miniapp/index.tsx | 8 +- packages/rax-scrollview/src/utils/omit.ts | 12 + packages/rax-scrollview/src/web/index.tsx | 5 +- 29 files changed, 813 insertions(+), 1450 deletions(-) delete mode 100644 packages/rax-recyclerview/src/VirtualizedList/BaseList.js delete mode 100644 packages/rax-recyclerview/src/VirtualizedList/NestedList.js create mode 100644 packages/rax-recyclerview/src/VirtualizedList/NoRecycleList.tsx delete mode 100644 packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.js create mode 100644 packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.ts delete mode 100644 packages/rax-recyclerview/src/VirtualizedList/constants.js delete mode 100644 packages/rax-recyclerview/src/VirtualizedList/index.js create mode 100644 packages/rax-recyclerview/src/VirtualizedList/index.tsx rename packages/rax-recyclerview/src/VirtualizedList/{throttle.js => throttle.ts} (57%) delete mode 100644 packages/rax-recyclerview/src/VirtualizedList/timer.js create mode 100644 packages/rax-recyclerview/src/VirtualizedList/types.ts delete mode 100644 packages/rax-recyclerview/src/index.js create mode 100644 packages/rax-recyclerview/src/index.tsx create mode 100644 packages/rax-recyclerview/src/miniapp-runtime/getInfoSync.ts create mode 100644 packages/rax-recyclerview/src/miniapp-runtime/index.ts create mode 100644 packages/rax-recyclerview/src/web/index.tsx create mode 100644 packages/rax-recyclerview/src/weex/index.js create mode 100644 packages/rax-scrollview/src/utils/omit.ts diff --git a/packages/rax-recyclerview/CHANGELOG.md b/packages/rax-recyclerview/CHANGELOG.md index 3723c0cb..2e8bb85b 100644 --- a/packages/rax-recyclerview/CHANGELOG.md +++ b/packages/rax-recyclerview/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 2.0.0 + +- Refactor `rax-recyclerview` in web and runtime miniapp: + - Replace `rax-view` with `rax-scrollview`; + - Update placeholder boxes size instead of updating item style: top or left ; +- upgrade deps: `rax-scrollview` from `^3.3.3` to `^3.6.0` + ## 1.3.6 - Fix RecyclerView error: "Uncaught TypeError: Cannot add property style, object is not extensible", when use virtual list mode. diff --git a/packages/rax-recyclerview/README.md b/packages/rax-recyclerview/README.md index 5932379f..21bf90b1 100644 --- a/packages/rax-recyclerview/README.md +++ b/packages/rax-recyclerview/README.md @@ -4,7 +4,9 @@ ## 描述 -`ScrollView` 的同门师兄,在 Weex 下是对 `list` 与 `cell` 的包装,其具有复用内部组件来提供性能的机制。 +在 `ScrollView` 的基础上,按需渲染视图内的元素,并回收视图外元素,以优化长列表场景下的性能优化。在 Weex 下是对 `list` 与 `cell` 的包装,其具有复用内部组件来提供性能的机制。 + +使用时,如果为垂直方向滚动时,**必须设置高度**,否则元素回收功能和滚动相关的功能将失效。 ![](https://gw.alicdn.com/tfs/TB1Cf_ZRVXXXXa8XVXXXXXXXXXX-255-265.gif) @@ -14,16 +16,17 @@ npm install rax-recyclerview --save ``` - - ## 属性 +在 rax-recyclerview@2.0.0 及以上版本中,可以使用所有 rax-scrollview 的所有属性,具体文档请看:[rax-scrollview](https://github.com/raxjs/rax-components/blob/master/packages/rax-scrollview/README.md) + | 属性 | 类型 | 默认值 | 必填 | 描述 | 支持 | | --------------------- | ----------------- | ------ | ---- | ----------------------------------------------- | ------------------------------------------------------------ | -| onEndReachedThreshold | `number` | 500 | ✘ | 设置加载更多的偏移 | browser weex miniAppbytedanceMicroApp | +| itemSize | `function/number` | - | √ | 单位为`rpx`, 返回每个子元素的高度(节点回收时需要,不需要传递 RecyclerView.Header 元素的高度),若 itemSize 未传,则不进行回收。 如果每个子元素的高度不固定,则可使用函数的方式,例如 itemSize={(index) => {return 100;}} 其中 `index` 为参与回收的子元素的数组下标位置 | browser | +| horizontal | `boolean` | - | false | 设置为横向滚动 | browser weexminiApp wechatMiniprogrambytedanceMicroApp quickApp| +| onEndReachedThreshold | `string/number` | 500 | ✘ | 设置加载更多的偏移, 推荐使用 string 格式来指指定尺寸单位,如`100rpx` | browser weex miniAppbytedanceMicroApp | | onEndReached | `function` | - | ✘ | 滚动区域还剩`onEndReachedThreshold`的长度时触发 | browser weex miniApp wechatMiniprogrambytedanceMicroApp | | onScroll | `function` | - | ✘ | 滚动时触发的事件,返回当前滚动的水平垂直距离 | browser weex miniApp wechatMiniprogrambytedanceMicroApp | -| itemSize | `function/number` | - | ✘ | 返回每个 cell 的高度(节点回收时需要) | browser | | totalSize | `number` | - | ✘ | 当前列表总高度(在 cell 高度可变的列表中需要传) | browser | | onTouchStart | `function` | - | false | touchStart触发的事件,返回触摸点数据(touches、changedTouches) | browser weexminiApp | | onTouchMove | `function` | - | false | touchMove触发的事件,返回触摸点数据(touches、changedTouches) | browser weexminiApp | @@ -31,8 +34,17 @@ npm install rax-recyclerview --save | onTouchCancel | `function` | - | false | touchCancel触发的事件,返回触摸点数据(touches、changedTouches) | miniApp | | disableScroll | `boolean` | - | false | 是否禁止滚动,是否禁止滚动, rax-recyclerview@1.3.4 及以上版本支 | browser miniApp | +## 子组件 +### RecyclerView.Header + +头部子元素需要用 `RecycerView.Header` 包裹,头部元素**不参与**元素回收。 + +### RecyclerView.Cell + +除了头部元素之外的子元素可以被 `RecyclerView.Cell` 包裹,在 Weex 中该组件为 Weex 的 `cell` 组件,在 Web 和小程序中该组件是 `Fragment` 空节点。该节点没有实际意义,所以不要在该组件上设置样式和绑定事件。如果在 Web 和 小程序中使用,不需要包裹 `RecyclerView.Cell`。 + ## 方法 -### scrollTo({x:number,y:number}) +### scrollTo({x:number|string,y:number|string}) #### 参数 @@ -40,13 +52,13 @@ npm install rax-recyclerview --save | **属性** | **类型** | **默认值** | **必填** | **描述** | | -------- | -------- | ---------- | -------- | ------------ | -| x | `number` | - | ✘ | 横向的偏移量 | -| y | `number` | - | ✘ | 纵向的偏移量 | +| x | `number/string` | - | ✘ | 横向的偏移量, 推荐使用 string 格式来指定尺寸单位 | +| y | `number/string` | - | ✘ | 纵向的偏移量, 推荐使用 string 格式来指定尺寸单位 | ### 示例 ```jsx -import { createElement, Component, render } from "rax"; +import { createElement, Component, render, useRef } from "rax"; import View from "rax-view"; @@ -56,157 +68,80 @@ import DriverUniversal from "driver-universal"; import RecyclerView from "rax-recyclerview"; -class Thumb extends Component { - shouldComponentUpdate(nextProps, nextState) { - return false; - } - - render() { - return ( - - - - - - ); - } -} - -class Row extends Component { - handleClick = e => { - this.props.onClick(this.props.data); - }; - - render() { - return ( - - - - {this.props.data.text + " (" + this.props.data.clicks + " clicks)"} - - +function Thumb({val}) { + return ( + + + {val} - ); - } + + ); } - -const THUMBS = []; - -for (let i = 0; i < 20; i++) THUMBS.push(i); - -const createThumbRow = (val, i) => ; - -class App extends Component { - state = { - horizontalScrollViewEventLog: false, - scrollViewEventLog: false - }; - - render() { - return ( - - - { - this.scrollView = scrollView; - }} - style={{ - height: 500 - }} - onEndReached={() => alert("reach end")} - > - - Sticky view is not header​{" "} - - - - Sticky view must in header root - - - {THUMBS.map(createThumbRow)} - - this.scrollView.scrollTo({ y: 0 })} - > - Scroll to top - - - {this.state.scrollViewEventLog ? "onEndReached" : ""} +let THUMBS = []; +for (let i = 0; i < 100; i++) THUMBS.push(`box_${i}`); +let createThumbRow = (val) => ; + +function App() { + const viewRef = useRef(null); + + return ( + + {console.log(e.nativeEvent.contentOffset.y)}} + > + + Sticky view is not header + + + + Sticky view must in header root - + + {THUMBS.map(createThumbRow)} + + viewRef.current.scrollTo({ y: 0 })} + > + Scroll to top - ); - } + + ); } -const styles = { +let styles = { root: { - width: 750, - paddingTop: 20 + display: 'block' }, - sticky: { - position: "sticky", + position: 'sticky', width: 750, - backgroundColor: "#cccccc" + top: 0, + backgroundColor: '#cccccc' }, - container: { - padding: 20, - borderStyle: "solid", - borderColor: "#dddddd", - borderWidth: 1, - marginLeft: 20, - height: 1000, - marginRight: 20, - marginBottom: 10 + height: '100vh' }, - button: { margin: 7, padding: 5, - alignItems: "center", - backgroundColor: "#eaeaea", - borderRadius: 3 + alignItems: 'center', + backgroundColor: '#eaeaea', + borderRadius: 3, }, - box: { width: 64, - height: 64 - }, - - eventLogBox: { - padding: 10, - margin: 10, - height: 80, - borderWidth: 1, - borderColor: "#f0f0f0", - backgroundColor: "#f9f9f9" + height: 64, }, - - row: { - borderColor: "grey", - borderWidth: 1, - padding: 20, - margin: 5 - }, - - text: { - alignSelf: "center", - color: "black" - }, - - refreshView: { - height: 80, - width: 750, - justifyContent: "center", - alignItems: "center" - }, - - refreshArrow: { - fontSize: 30, - color: "#45b5f0" + fixButton: { + position: 'fixed', + bottom: 20, + right: 20, + border: 1, + backgroundColor: '#fff' } }; diff --git a/packages/rax-recyclerview/demo/base.jsx b/packages/rax-recyclerview/demo/base.jsx index 5be6c6a0..280e9003 100644 --- a/packages/rax-recyclerview/demo/base.jsx +++ b/packages/rax-recyclerview/demo/base.jsx @@ -1,44 +1,49 @@ -import { createElement, Component, render } from 'rax'; +import { createElement, render, useRef } from 'rax'; import View from 'rax-view'; import Text from 'rax-text'; import DriverUniversal from "driver-universal" import RecyclerView from 'rax-recyclerview'; -function Thumb() { + +function Thumb({val}) { return ( - + {val} ); } let THUMBS = []; -for (let i = 0; i < 20; i++) THUMBS.push(i); -let createThumbRow = (val, i) => ; +for (let i = 0; i < 100; i++) THUMBS.push(`box_${i}`); +let createThumbRow = (val) => ; function App() { + const viewRef = useRef(null); + return ( - - - - - Sticky view is not header - - - - - Sticky view must in header root - - - - {THUMBS.map(createThumbRow)} - - + {console.log(e.nativeEvent.contentOffset.y)}} + > + + Sticky view is not header + + + + Sticky view must in header root + + + {THUMBS.map(createThumbRow)} + + viewRef.current.scrollTo({ y: 0 })} + > + Scroll to top ); @@ -46,8 +51,7 @@ function App() { let styles = { root: { - width: 750, - paddingTop: 20 + display: 'block' }, sticky: { position: 'sticky', @@ -56,14 +60,7 @@ let styles = { backgroundColor: '#cccccc' }, container: { - padding: 20, - borderStyle: 'solid', - borderColor: '#dddddd', - borderWidth: 1, - marginLeft: 20, - height: 1000, - marginRight: 20, - marginBottom: 10, + height: '100vh' }, button: { margin: 7, @@ -75,6 +72,13 @@ let styles = { box: { width: 64, height: 64, + }, + fixButton: { + position: 'fixed', + bottom: 20, + right: 20, + border: 1, + backgroundColor: '#fff' } }; diff --git a/packages/rax-recyclerview/demo/index.md b/packages/rax-recyclerview/demo/index.md index ad17ea01..80b092b5 100644 --- a/packages/rax-recyclerview/demo/index.md +++ b/packages/rax-recyclerview/demo/index.md @@ -7,14 +7,15 @@ basic usage ```jsx -import {createElement, Component, render} from 'rax'; -import DriverUniversal from 'driver-universal'; -import View from 'rax-view'; -import Text from 'rax-text'; -import {isWeex} from 'universal-env'; -import RecyclerView from '../src/index'; +import { createElement, Component, render } from "rax"; -const vwh = isWeex ? 667 * 2 : document.documentElement.clientHeight * 750 / document.documentElement.clientWidth; +import View from "rax-view"; + +import Text from "rax-text"; + +import DriverUniversal from "driver-universal"; + +import RecyclerView from "rax-recyclerview"; class Thumb extends Component { shouldComponentUpdate(nextProps, nextState) { @@ -23,25 +24,43 @@ class Thumb extends Component { render() { return ( - + - - {this.props.index} - + ); } } -let THUMBS = []; -for (let i = 0; i < 30; i++) THUMBS.push(i); -let createThumbRow = (val, i) => ; +class Row extends Component { + handleClick = e => { + this.props.onClick(this.props.data); + }; -export default class App extends Component { + render() { + return ( + + + + {this.props.data.text + " (" + this.props.data.clicks + " clicks)"} + + + + ); + } +} + +const THUMBS = []; + +for (let i = 0; i < 20; i++) THUMBS.push(i); + +const createThumbRow = (val, i) => ; + +class App extends Component { state = { horizontalScrollViewEventLog: false, - scrollViewEventLog: false, + scrollViewEventLog: false }; render() { @@ -49,25 +68,32 @@ export default class App extends Component { { + ref={scrollView => { this.scrollView = scrollView; }} style={{ - height: vwh + height: 500 }} - onEndReached={() => console.log('reach end')}> - - Simple Header + onEndReached={() => alert("reach end")} + > + + Sticky view is not header​{" "} + + + + Sticky view must in header root + - {THUMBS.map(createThumbRow)} - - this.scrollView.scrollTo({y: 0})}> - Top + style={styles.button} + onClick={() => this.scrollView.scrollTo({ y: 0 })} + > + Scroll to top + + + {this.state.scrollViewEventLog ? "onEndReached" : ""} @@ -75,91 +101,73 @@ export default class App extends Component { } } -let styles = { +const styles = { root: { - width: 750 - }, - header: { - display: 'flex', - alignItems: 'center', - justifyContent: 'center', width: 750, - height: 350, - backgroundColor: 'tomato', - }, - headerText: { - color: 'white', - fontSize: 40, + paddingTop: 20 }, + sticky: { - position: 'sticky', + position: "sticky", width: 750, - backgroundColor: '#cccccc' + backgroundColor: "#cccccc" }, + container: { - borderStyle: 'solid', - borderColor: '#dddddd', - // borderWidth: 1, - height: vwh, - backgroundColor: '#eeeeee', + padding: 20, + borderStyle: "solid", + borderColor: "#dddddd", + borderWidth: 1, + marginLeft: 20, + height: 1000, + marginRight: 20, + marginBottom: 10 }, + button: { - backgroundColor: '#ffffff', - width: 710, - height: 250, - marginTop: 20, - marginLeft: 20, - justifyContent: 'center', - alignItems: 'center', - borderRadius: 10, + margin: 7, + padding: 5, + alignItems: "center", + backgroundColor: "#eaeaea", + borderRadius: 3 }, + box: { width: 64, - height: 64, + height: 64 }, + eventLogBox: { padding: 10, margin: 10, height: 80, borderWidth: 1, - borderColor: '#f0f0f0', - backgroundColor: '#f9f9f9', + borderColor: "#f0f0f0", + backgroundColor: "#f9f9f9" }, + row: { - borderColor: 'grey', + borderColor: "grey", borderWidth: 1, padding: 20, - margin: 5, + margin: 5 }, + text: { - alignSelf: 'center', - color: 'black', + alignSelf: "center", + color: "black" }, + refreshView: { height: 80, width: 750, - justifyContent: 'center', - alignItems: 'center' + justifyContent: "center", + alignItems: "center" }, + refreshArrow: { fontSize: 30, - color: '#45b5f0' - }, - topIcon: { - position: 'fixed', - right: 40, - bottom: 40, - width: 100, - height: 100, - borderWidth: '1px', - borderStyle: 'solid', - borderColor: '#cccccc', - borderRadius: 100, - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - backgroundColor: 'white', - cursor: 'pointer', + color: "#45b5f0" } }; diff --git a/packages/rax-recyclerview/package.json b/packages/rax-recyclerview/package.json index 83ab765d..7ed66e37 100644 --- a/packages/rax-recyclerview/package.json +++ b/packages/rax-recyclerview/package.json @@ -1,11 +1,24 @@ { "name": "rax-recyclerview", - "version": "1.3.6", + "version": "2.0.0-5", "description": "RecyclerView component for Rax.", "license": "BSD-3-Clause", "main": "lib/index.js", "module": "es/index.js", "types": "lib/index.d.ts", + "exports": { + ".": { + "web": "./es/web/index.js", + "weex": "./es/weex/index.js", + "miniapp": "./es/miniapp-runtime/index.js", + "wechat-miniprogram": "./es/miniapp-runtime/index.js", + "bytedance-microapp": "./es/miniapp-runtime/index.js", + "baidu-smartprogram": "./es/miniapp-runtime/index.js", + "kuaishou-miniprogram": "./es/miniapp-runtime/index.js", + "default": "./es/index.js" + }, + "./*": "./*" + }, "miniprogram": ".", "miniappConfig": { "main": "lib/miniapp/index", @@ -53,9 +66,8 @@ "dependencies": { "rax-children": "^1.0.0", "rax-find-dom-node": "^1.0.0", - "prop-types": "^15.7.2", "rax-refreshcontrol": "^1.0.0", - "rax-scrollview": "^3.3.3", + "rax-scrollview": "3.7.1-1", "rax-view": "^2.0.0", "universal-env": "^3.0.0" }, diff --git a/packages/rax-recyclerview/src/VirtualizedList/BaseList.js b/packages/rax-recyclerview/src/VirtualizedList/BaseList.js deleted file mode 100644 index f417313b..00000000 --- a/packages/rax-recyclerview/src/VirtualizedList/BaseList.js +++ /dev/null @@ -1,306 +0,0 @@ -import { createElement, PureComponent } from 'rax'; -import PropTypes from 'prop-types'; - -import SizeAndPositionManager from './SizeAndPositionManager'; - -import { - ALIGNMENT, - DIRECTION, - SCROLL_CHANGE_REASON, - marginProp, - oppositeMarginProp, - positionProp, - scrollProp, - sizeProp, -} from './constants'; - -const DEFAULT_VIEWPORT = 750; - -const STYLE_ITEM = { - position: 'absolute', - top: 0, - left: 0, - width: '100%', -}; - -const STYLE_STICKY_ITEM = { - ...STYLE_ITEM, - position: 'sticky', -}; - -const STYLE_INNER = { - position: 'relative', - width: '100%', - minHeight: '100%', -}; - -const STYLE_WRAPPER = { - overflow: 'auto', - willChange: 'transform', - WebkitOverflowScrolling: 'touch', -}; - -export default class BaseList extends PureComponent { - pixelRatio = 1; - styleCache = {}; - - static defaultProps = { - overscanCount: 3, - }; - - static propTypes = { - estimatedItemSize: PropTypes.number, - itemSize: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.array, - PropTypes.func, - ]).isRequired, - overscanCount: PropTypes.number, - scrollOffset: PropTypes.number, - scrollToIndex: PropTypes.number, - scrollToAlignment: PropTypes.oneOf([ - ALIGNMENT.AUTO, - ALIGNMENT.START, - ALIGNMENT.CENTER, - ALIGNMENT.END, - ]), - horizontal: PropTypes.bool, - stickyIndices: PropTypes.arrayOf(PropTypes.number), - style: PropTypes.object, - width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - totalSize: PropTypes.number, - }; - - state = { - offset: - this.props.scrollOffset || - this.props.scrollToIndex != null && - this.getOffsetForIndex(this.props.scrollToIndex) || - 0, - scrollChangeReason: SCROLL_CHANGE_REASON.REQUESTED, - }; - - pixelRatio = DEFAULT_VIEWPORT / this.getClientWidth(); - - getEstimatedItemSize(props = this.props) { - return ( - props.estimatedItemSize || - typeof props.itemSize === 'number' && props.itemSize || - 50 - ); - } - - getSize(index, itemSize) { - if (typeof itemSize === 'function') { - return itemSize(index); - } - - return Array.isArray(itemSize) ? itemSize[index] : itemSize; - } - - getStyle(index, sticky) { - const style = this.styleCache[index]; - - if (style) { - return style; - } - - const { - size, - offset, - } = this.sizeAndPositionManager.getSizeAndPositionForIndex(index); - - return this.styleCache[index] = sticky ? { - ...STYLE_STICKY_ITEM, - [sizeProp[this.scrollDirection]]: size, - [marginProp[this.scrollDirection]]: offset, - [oppositeMarginProp[this.scrollDirection]]: -(offset + size), - zIndex: 1, - } : { - ...STYLE_ITEM, - [sizeProp[this.scrollDirection]]: size, - [positionProp[this.scrollDirection]]: offset, - }; - } - - itemSizeGetter = (itemSize) => { - return index => this.getSize(index, itemSize); - }; - - sizeAndPositionManager = new SizeAndPositionManager({ - itemCount: this.props.children.length, - itemSizeGetter: this.itemSizeGetter(this.props.itemSize), - estimatedItemSize: this.getEstimatedItemSize(), - }); - scrollDirection = this.props.horizontal ? DIRECTION.HORIZONTAL : DIRECTION.VERTICAL; - - componentWillReceiveProps(nextProps) { - const { - estimatedItemSize, - children, - itemSize, - scrollOffset, - scrollToAlignment, - scrollToIndex, - horizontal, - nestedList, - active, - } = this.props; - const scrollPropsHaveChanged = - nextProps.scrollToIndex !== scrollToIndex || - nextProps.scrollToAlignment !== scrollToAlignment; - const nestedActiveChanged = nestedList && nextProps.active !== active; - const itemPropsHaveChanged = - nestedActiveChanged || - nextProps.children.length !== children.length || - nextProps.itemSize !== itemSize || - nextProps.estimatedItemSize !== estimatedItemSize; - - if (nextProps.itemSize !== itemSize) { - this.sizeAndPositionManager.updateConfig({ - itemSizeGetter: this.itemSizeGetter(nextProps.itemSize), - }); - } - - this.scrollDirection = horizontal ? DIRECTION.HORIZONTAL : DIRECTION.VERTICAL; - if ( - nestedActiveChanged || - nextProps.children.length !== children.length || - nextProps.estimatedItemSize !== estimatedItemSize - ) { - this.sizeAndPositionManager.updateConfig({ - itemCount: nextProps.children.length, - estimatedItemSize: this.getEstimatedItemSize(nextProps), - }); - } - - if (itemPropsHaveChanged) { - this.recomputeSizes(); - } - - if (nextProps.scrollOffset !== scrollOffset) { - this.setState({ - offset: nextProps.scrollOffset || 0, - scrollChangeReason: SCROLL_CHANGE_REASON.REQUESTED, - }); - } else if ( - typeof nextProps.scrollToIndex === 'number' && - (scrollPropsHaveChanged || itemPropsHaveChanged) - ) { - this.setState({ - offset: this.getOffsetForIndex( - nextProps.scrollToIndex, - nextProps.scrollToAlignment, - nextProps.children.length, - ), - scrollChangeReason: SCROLL_CHANGE_REASON.REQUESTED, - }); - } - } - - getOffsetForIndex( - index, - scrollToAlignment = this.props.scrollToAlignment, - itemCount = this.props.children.length, - ) { - const {style = {}} = this.props; - - if (index < 0 || index >= itemCount) { - index = 0; - } - - return this.sizeAndPositionManager.getUpdatedOffsetForIndex({ - align: scrollToAlignment, - containerSize: style[sizeProp[this.scrollDirection]], - currentOffset: this.state && this.state.offset || 0, - targetIndex: index, - }); - } - - recomputeSizes(startIndex = 0) { - this.styleCache = {}; - this.sizeAndPositionManager.resetItem(startIndex); - } - - getClientWidth() { - return document.documentElement.clientWidth; - } - - getRenderProps(options) { - const { - style = {}, - offset, - overscanCount, - totalSize, - stickyIndices, - children, - width, - } = options; - const items = []; - const wrapperStyle = {...STYLE_WRAPPER, ...style, width}; - - const { start, stop } = this.sizeAndPositionManager.getVisibleRange({ - containerSize: options[sizeProp[this.scrollDirection]] || style[sizeProp[this.scrollDirection]] || 0, - offset, - overscanCount, - }); - const innerStyle = { - ...STYLE_INNER, - [sizeProp[this.scrollDirection]]: this.sizeAndPositionManager.getTotalSize(totalSize), - }; - let renderChildren = []; - - if (stickyIndices != null && stickyIndices.length !== 0) { - stickyIndices.forEach((index) => { - const child = children[index]; - /** - * child.props.style = newStyle cause: Uncaught TypeError: Cannot add property style, object is not extensible - */ - items.push({ - ...child, - props: { - ...child.props, - style: { - ...child.props.style, - ...this.getStyle(index, true) - } - } - }); - }); - - if (this.scrollDirection === DIRECTION.HORIZONTAL) { - innerStyle.display = 'flex'; - } - } - - /** - * solve children is not an array. - */ - if (typeof start !== 'undefined' && typeof stop !== 'undefined' && children.slice) { - let index = start; - renderChildren = children.slice(start, stop + 1); - renderChildren.forEach((child) => { - /** - * child.props.style = newStyle cause: Uncaught TypeError: Cannot add property style, object is not extensible - */ - items.push({ - ...child, - props: { - ...child.props, - style: { - ...child.props.style, - ...this.getStyle(index, false) - } - } - }); - index++; - }); - } - - return { - wrapperStyle, - innerStyle, - nodeItems: items, - }; - } -} diff --git a/packages/rax-recyclerview/src/VirtualizedList/NestedList.js b/packages/rax-recyclerview/src/VirtualizedList/NestedList.js deleted file mode 100644 index 6185edb9..00000000 --- a/packages/rax-recyclerview/src/VirtualizedList/NestedList.js +++ /dev/null @@ -1,52 +0,0 @@ -import { createElement } from 'rax'; -import View from 'rax-view'; -import BaseList from './BaseList'; -import { - SCROLL_CHANGE_REASON, -} from './constants'; - -let cacheOffset = 0; -export default class NestedList extends BaseList { - state = { - offset: 0, - }; - - componentDidMount() { - window.addEventListener('recyclerViewScroll', this.handleScroll); - } - - componentWillUnMount() { - window.removeEventListener('recyclerViewScroll', this.handleScroll); - } - - handleScroll = (event) => { - const { detail } = event; - const { offset } = detail; - const { active } = this.props; - - cacheOffset = offset; - if (active) { - this.setState({ - offset, - scrollChangeReason: SCROLL_CHANGE_REASON.OBSERVED, - }); - } - } - - render() { - const { - className, - } = this.props; - - const { innerStyle, nodeItems, wrapperStyle } = this.getRenderProps({ - ...this.props, - offset: cacheOffset, - }); - - return ( - - {nodeItems} - - ); - } -} diff --git a/packages/rax-recyclerview/src/VirtualizedList/NoRecycleList.tsx b/packages/rax-recyclerview/src/VirtualizedList/NoRecycleList.tsx new file mode 100644 index 00000000..4dbd9cbe --- /dev/null +++ b/packages/rax-recyclerview/src/VirtualizedList/NoRecycleList.tsx @@ -0,0 +1,24 @@ +/** + * backward compatibility: itemSize is optional + */ + +import { createElement, forwardRef } from 'rax'; +import ScrollView from 'rax-scrollview'; + +import { VirtualizedList } from './types'; + +const NoRecycleList: VirtualizedList = forwardRef((props, ref) => { + const { children, ...rest } = props; + + return ( + + {children} + + ); +}); + +export default NoRecycleList; \ No newline at end of file diff --git a/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.js b/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.js deleted file mode 100644 index 9dfb66d7..00000000 --- a/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.js +++ /dev/null @@ -1,277 +0,0 @@ -import {ALIGNMENT} from './constants'; - -export default class SizeAndPositionManager { - itemSizeGetter; - itemCount; - estimatedItemSize; - lastMeasuredIndex; - itemSizeAndPositionData; - - constructor({itemCount, itemSizeGetter, estimatedItemSize}) { - this.itemSizeGetter = itemSizeGetter; - this.itemCount = itemCount; - this.estimatedItemSize = estimatedItemSize; - - // Cache of size and position data for items, mapped by item index. - this.itemSizeAndPositionData = {}; - - // Measurements for items up to this index can be trusted; items afterward should be estimated. - this.lastMeasuredIndex = -1; - } - - updateConfig({ - itemCount, - itemSizeGetter, - estimatedItemSize, - }) { - if (itemCount != null) { - this.itemCount = itemCount; - } - - if (estimatedItemSize != null) { - this.estimatedItemSize = estimatedItemSize; - } - - if (itemSizeGetter != null) { - this.itemSizeGetter = itemSizeGetter; - } - } - - getLastMeasuredIndex() { - return this.lastMeasuredIndex; - } - - /** - * This method returns the size and position for the item at the specified index. - * It just-in-time calculates (or used cached values) for items leading up to the index. - */ - getSizeAndPositionForIndex(index) { - if (index < 0 || index >= this.itemCount) { - throw Error( - `Requested index ${index} is outside of range 0..${this.itemCount}`, - ); - } - - if (index > this.lastMeasuredIndex) { - const lastMeasuredSizeAndPosition = this.getSizeAndPositionOfLastMeasuredItem(); - let offset = - lastMeasuredSizeAndPosition.offset + lastMeasuredSizeAndPosition.size; - - for (let i = this.lastMeasuredIndex + 1; i <= index; i++) { - const size = this.itemSizeGetter(i); - - if (size == null || isNaN(size)) { - throw Error(`Invalid size returned for index ${i} of value ${size}`); - } - - this.itemSizeAndPositionData[i] = { - offset, - size, - }; - - offset += size; - } - - this.lastMeasuredIndex = index; - } - - return this.itemSizeAndPositionData[index]; - } - - getSizeAndPositionOfLastMeasuredItem() { - return this.lastMeasuredIndex >= 0 - ? this.itemSizeAndPositionData[this.lastMeasuredIndex] - : {offset: 0, size: 0}; - } - - /** - * Total size of all items being measured. - * This value will be completedly estimated initially. - * As items as measured the estimate will be updated. - */ - getTotalSize(totalSize) { - if (totalSize) { - return totalSize; - } else { - const lastMeasuredSizeAndPosition = this.getSizeAndPositionOfLastMeasuredItem(); - - return ( - lastMeasuredSizeAndPosition.offset + - lastMeasuredSizeAndPosition.size + - (this.itemCount - this.lastMeasuredIndex - 1) * this.estimatedItemSize - ); - } - } - - /** - * Determines a new offset that ensures a certain item is visible, given the alignment. - * - * @param align Desired alignment within container; one of "start" (default), "center", or "end" - * @param containerSize Size (width or height) of the container viewport - * @return Offset to use to ensure the specified item is visible - */ - getUpdatedOffsetForIndex({ - align = ALIGNMENT.START, - containerSize, - currentOffset, - targetIndex, - }) { - if (containerSize <= 0) { - return 0; - } - - const datum = this.getSizeAndPositionForIndex(targetIndex); - const maxOffset = datum.offset; - const minOffset = maxOffset - containerSize + datum.size; - - let idealOffset; - - switch (align) { - case ALIGNMENT.END: - idealOffset = minOffset; - break; - case ALIGNMENT.CENTER: - idealOffset = maxOffset - (containerSize - datum.size) / 2; - break; - case ALIGNMENT.START: - idealOffset = maxOffset; - break; - default: - idealOffset = Math.max(minOffset, Math.min(maxOffset, currentOffset)); - } - - const totalSize = this.getTotalSize(); - - return Math.max(0, Math.min(totalSize - containerSize, idealOffset)); - } - - getVisibleRange({ - containerSize, - offset, - overscanCount, - }) { - const totalSize = this.getTotalSize(); - - if (totalSize === 0) { - return {}; - } - - const maxOffset = offset + containerSize; - let start = this.findNearestItem(offset); - - if (typeof start === 'undefined') { - throw Error(`Invalid offset ${offset} specified`); - } - - const datum = this.getSizeAndPositionForIndex(start); - offset = datum.offset + datum.size; - - let stop = start; - - while (offset < maxOffset && stop < this.itemCount - 1) { - stop++; - offset += this.getSizeAndPositionForIndex(stop).size; - } - - if (overscanCount) { - start = Math.max(0, start - overscanCount); - stop = Math.min(stop + overscanCount, this.itemCount - 1); - } - - return { - start, - stop, - }; - } - - /** - * Clear all cached values for items after the specified index. - * This method should be called for any item that has changed its size. - * It will not immediately perform any calculations; they'll be performed the next time getSizeAndPositionForIndex() is called. - */ - resetItem(index) { - this.lastMeasuredIndex = Math.min(this.lastMeasuredIndex, index - 1); - } - - /** - * Searches for the item (index) nearest the specified offset. - * - * If no exact match is found the next lowest item index will be returned. - * This allows partially visible items (with offsets just before/above the fold) to be visible. - */ - findNearestItem(offset) { - if (isNaN(offset)) { - throw Error(`Invalid offset ${offset} specified`); - } - - // Our search algorithms find the nearest match at or below the specified offset. - // So make sure the offset is at least 0 or no match will be found. - offset = Math.max(0, offset); - - const lastMeasuredSizeAndPosition = this.getSizeAndPositionOfLastMeasuredItem(); - const lastMeasuredIndex = Math.max(0, this.lastMeasuredIndex); - - if (lastMeasuredSizeAndPosition.offset >= offset) { - // If we've already measured items within this range just use a binary search as it's faster. - return this.binarySearch({ - high: lastMeasuredIndex, - low: 0, - offset, - }); - } else { - // If we haven't yet measured this high, fallback to an exponential search with an inner binary search. - // The exponential search avoids pre-computing sizes for the full set of items as a binary search would. - // The overall complexity for this approach is O(log n). - return this.exponentialSearch({ - index: lastMeasuredIndex, - offset, - }); - } - } - - binarySearch({ - low, - high, - offset, - }) { - let middle = 0; - let currentOffset = 0; - - while (low <= high) { - middle = low + Math.floor((high - low) / 2); - currentOffset = this.getSizeAndPositionForIndex(middle).offset; - - if (currentOffset === offset) { - return middle; - } else if (currentOffset < offset) { - low = middle + 1; - } else if (currentOffset > offset) { - high = middle - 1; - } - } - - if (low > 0) { - return low - 1; - } - - return 0; - } - - exponentialSearch({index, offset}) { - let interval = 1; - - while ( - index < this.itemCount && - this.getSizeAndPositionForIndex(index).offset < offset - ) { - index += interval; - interval *= 2; - } - - return this.binarySearch({ - high: Math.min(index, this.itemCount - 1), - low: Math.floor(index / 2), - offset, - }); - } -} diff --git a/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.ts b/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.ts new file mode 100644 index 00000000..1cedb55d --- /dev/null +++ b/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.ts @@ -0,0 +1,152 @@ +import { TItemSize } from './types'; + +type TSizeGetter = (i: number) => number; +type TRenderedIndexGetter = (distance: number) => { startIndex: number; endIndex: number }; +type TPlaceholderSizeGetter = (startIndex: number, endIndex: number) => { front: number; back: number; show?: string }; + +class SizeAndPositionManager { + private bufferSize: number; + private readonly length: number; + private getSize: TSizeGetter; + + public totalSize: number; + + public getRenderedIndex: TRenderedIndexGetter; + public getPlaceholderSize: TPlaceholderSizeGetter; + + public static bufferRatio = 3; + public static clientSize; + public static pixelRatio: number; + + public constructor({ itemSize, horizontal, length, bufferSize, totalSize }: { + itemSize: TItemSize; + horizontal: boolean; + length: number; + bufferSize?: number; + totalSize?: number; + }) { + this.length = length; + this.bufferSize = this.getBufferSize(bufferSize, horizontal); + this.getSize = this.initSizeGetter(itemSize); + this.getRenderedIndex = this.initRenderedIndexGetter(itemSize); + this.getPlaceholderSize = this.initPlaceholderSizeGetter(itemSize); + this.totalSize = totalSize ? totalSize : this.getTotalSize(itemSize, length); + } + + private initSizeGetter(itemSize): TSizeGetter { + if (typeof itemSize === 'number') { + return () => { + return itemSize; + }; + } + + if (typeof itemSize === 'function') { + const sizeCache = new Map(); + return (i: number) => { + if (sizeCache.has(i)) { + return sizeCache.get(i); + } else { + const singleSize = itemSize(i); + sizeCache.set(i, singleSize); + return singleSize; + } + }; + } + + throw new Error('itemSize is inValid'); + } + + private initRenderedIndexGetter(itemSize): TRenderedIndexGetter { + if (typeof itemSize === 'number') { + return (scrollDistance: number) => { + const distance = scrollDistance / SizeAndPositionManager.pixelRatio; + let startIndex, endIndex; + if (distance < this.bufferSize) { + startIndex = 0; + } else { + startIndex = Math.floor((distance - this.bufferSize) / itemSize); + } + endIndex = Math.ceil(this.bufferSize * SizeAndPositionManager.bufferRatio / itemSize) + startIndex; + + return { + startIndex, + endIndex: Math.min(endIndex, this.length - 1) + }; + }; + } + return (scrollDistance: number) => { + const distance = scrollDistance / SizeAndPositionManager.pixelRatio; + let startIndex = -1; + let endIndex = -1; + let size = 0; + const frontSize = this.bufferSize + distance; + const backSize = this.bufferSize * SizeAndPositionManager.bufferRatio + distance; + for (let i = 0; i < this.length; i++) { + size += this.getSize(i); + if (startIndex === -1 && size >= frontSize) { + startIndex = i; + } + if (size >= backSize) { + endIndex = i; + return { + startIndex, + endIndex + }; + } + } + return { + startIndex, + endIndex + }; + }; + } + + private initPlaceholderSizeGetter(itemSize) { + if (typeof itemSize === 'number') { + return (startIndex, endIndex) => { + return { + front: startIndex * itemSize, + back: (this.length - endIndex - 1) * itemSize, + }; + }; + } + return (startIndex, endIndex) => { + let front = 0; + let back = 0; + for (let i = 0; i < startIndex; i++) { + front += this.getSize(i); + } + for (let i = endIndex + 1; i < this.length; i++) { + back += this.getSize(i); + } + + return { + front: front, + back: back, + }; + }; + } + + private getBufferSize(bufferSize, horizontal) { + if (bufferSize) { + return bufferSize; + } + if (horizontal) { + return SizeAndPositionManager.clientSize.width / SizeAndPositionManager.pixelRatio; + } + return SizeAndPositionManager.clientSize.height / SizeAndPositionManager.pixelRatio; + } + + private getTotalSize(itemSize: TItemSize, length: number): number { + if (typeof itemSize === 'number') { + return itemSize * length; + } + let size; + for (let i = 0; i < length; i++) { + size += this.getSize(i); + } + return size; + } +} + +export default SizeAndPositionManager; \ No newline at end of file diff --git a/packages/rax-recyclerview/src/VirtualizedList/constants.js b/packages/rax-recyclerview/src/VirtualizedList/constants.js deleted file mode 100644 index 83d4411f..00000000 --- a/packages/rax-recyclerview/src/VirtualizedList/constants.js +++ /dev/null @@ -1,41 +0,0 @@ -export const ALIGNMENT = { - AUTO: 'auto', - START: 'start', - CENTER: 'center', - END: 'end', -}; - -export const DIRECTION = { - HORIZONTAL: 'horizontal', - VERTICAL: 'vertical', -}; - -export const SCROLL_CHANGE_REASON = { - OBSERVED: 'observed', - REQUESTED: 'requested', -}; - -export const scrollProp = { - [DIRECTION.VERTICAL]: 'scrollTop', - [DIRECTION.HORIZONTAL]: 'scrollLeft', -}; - -export const sizeProp = { - [DIRECTION.VERTICAL]: 'height', - [DIRECTION.HORIZONTAL]: 'width', -}; - -export const positionProp = { - [DIRECTION.VERTICAL]: 'top', - [DIRECTION.HORIZONTAL]: 'left', -}; - -export const marginProp = { - [DIRECTION.VERTICAL]: 'marginTop', - [DIRECTION.HORIZONTAL]: 'marginLeft', -}; - -export const oppositeMarginProp = { - [DIRECTION.VERTICAL]: 'marginBottom', - [DIRECTION.HORIZONTAL]: 'marginRight', -}; diff --git a/packages/rax-recyclerview/src/VirtualizedList/index.js b/packages/rax-recyclerview/src/VirtualizedList/index.js deleted file mode 100644 index 7c6e0a0a..00000000 --- a/packages/rax-recyclerview/src/VirtualizedList/index.js +++ /dev/null @@ -1,209 +0,0 @@ -import { PureComponent, createElement } from 'rax'; -import PropTypes from 'prop-types'; -import BaseList from './BaseList'; -import { - SCROLL_CHANGE_REASON, - scrollProp, -} from './constants'; -import Timer from './timer'; -import NestedList from './NestedList'; -import throttle from './throttle'; - -export {DIRECTION as ScrollDirection} from './constants'; - -const STYLE_NODE_ID = 'rax-virtualized-list-style'; -const DEFAULT_SCROLL_CALLBACK_THROTTLE = 50; -const DEFAULT_END_REACHED_THRESHOLD = 500; - -const STYLE_WRAPPER = { - overflow: 'auto', - willChange: 'transform', - WebkitOverflowScrolling: 'touch', -}; - -export default class VirtualizedList extends BaseList { - lastScrollDistance = 0; - lastScrollContentSize = 0; - loadmoreretry = 1; - - static defaultProps = { - width: '100%', - scrollEventThrottle: DEFAULT_SCROLL_CALLBACK_THROTTLE, - onEndReachedThreshold: DEFAULT_END_REACHED_THRESHOLD, - showsHorizontalScrollIndicator: true, - showsVerticalScrollIndicator: true, - className: 'rax-virtualized-list', - nestedList: false, - }; - - static propTypes = { - onScroll: PropTypes.func, - style: PropTypes.object, - width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - onEndReached: PropTypes.func, - onEndReachedThreshold: PropTypes.number, - scrollEventThrottle: PropTypes.number, - className: PropTypes.string, - nestedList: PropTypes.bool, - }; - - rootNode; - - componentDidMount() { - const { scrollOffset, scrollToIndex, scrollEventThrottle } = this.props; - let handleScroll = this.handleScroll; - - if (scrollEventThrottle) { - handleScroll = throttle(handleScroll, scrollEventThrottle); - } - this.rootNode.addEventListener('scroll', handleScroll, { - passive: true, - }); - - if (scrollOffset != null) { - this.scrollTo(scrollOffset); - } else if (scrollToIndex != null) { - this.scrollTo(this.getOffsetForIndex(scrollToIndex)); - } - } - - componentDidUpdate(_, prevState) { - const {offset, scrollChangeReason} = this.state; - - if ( - prevState.offset !== offset && - scrollChangeReason === SCROLL_CHANGE_REASON.REQUESTED - ) { - this.scrollTo(offset); - } - } - - componentWillUnmount() { - this.rootNode.removeEventListener('scroll', this.handleScroll); - } - - scrollTo(value, animated) { - if (animated) { - const timer = new Timer({ - duration: 400, - easing: 'easeOutSine', - onRun: (e) => { - if (value >= 0) { - const currentValue = this.rootNode[scrollProp[this.scrollDirection]]; - this.rootNode[scrollProp[this.scrollDirection]] = currentValue + e.percent * (value / this.pixelRatio - currentValue); - } - } - }); - timer.run(); - } else { - this.rootNode[scrollProp[this.scrollDirection]] = value / this.pixelRatio; - } - } - - render() { - const { - children, - style = {}, - width, - horizontal, - showsHorizontalScrollIndicator, - showsVerticalScrollIndicator, - className, - nestedList, - ...props - } = this.props; - const { offset } = this.state; - const wrapperStyle = {...STYLE_WRAPPER, ...style, width}; - - let showsScrollIndicator = horizontal ? showsHorizontalScrollIndicator : showsVerticalScrollIndicator; - - if (!showsScrollIndicator && typeof document !== 'undefined' && !document.getElementById(STYLE_NODE_ID)) { - let styleNode = document.createElement('style'); - styleNode.id = STYLE_NODE_ID; - document.head.appendChild(styleNode); - styleNode.innerHTML = `.${this.props.className}::-webkit-scrollbar{display: none;}`; - } - - // return children when has nested list - if (nestedList) { - return ( -
- {children} -
- ); - } - const { innerStyle, nodeItems } = this.getRenderProps({ - ...this.props, - offset, - }); - - return ( -
-
{nodeItems}
-
- ); - } - - getRef = (node) => { - this.rootNode = node; - }; - - resetScroll = () => { - this.lastScrollContentSize = 0; - this.lastScrollDistance = 0; - } - - handleScroll = (event) => { - const { onScroll, onEndReached, onEndReachedThreshold, totalSize, horizontal: isHorizontal, nestedList } = this.props; - const offset = this.getNodeOffset(); - const realOffset = offset * this.pixelRatio; - - if ( - offset < 0 || - this.state.offset === realOffset || - event.target !== this.rootNode - ) { - return; - } - - if (!nestedList) { - this.setState({ - offset: realOffset, - scrollChangeReason: SCROLL_CHANGE_REASON.OBSERVED, - }); - } - - if (typeof onScroll === 'function') { - onScroll(event, offset); - } - - if (typeof onEndReached === 'function') { - const { scrollLeft, scrollTop, scrollHeight, scrollWidth, offsetWidth, offsetHeight } = this.rootNode; - const scrollerNodeSize = isHorizontal ? offsetWidth : offsetHeight; - const scrollContentSize = totalSize / this.pixelRatio || (isHorizontal ? scrollWidth : scrollHeight); - const scrollDistance = isHorizontal ? scrollLeft : scrollTop; - const isEndReached = scrollContentSize - scrollDistance - scrollerNodeSize < onEndReachedThreshold; - - const isScrollToEnd = scrollDistance > this.lastScrollDistance; - const isLoadedMoreContent = scrollContentSize != this.lastScrollContentSize; - - if (isEndReached && isScrollToEnd && isLoadedMoreContent) { - this.lastScrollContentSize = scrollContentSize; - this.props.onEndReached(); - } - this.lastScrollDistance = scrollDistance; - window.dispatchEvent(new CustomEvent('recyclerViewEndReached')); - } - window.dispatchEvent(new CustomEvent('recyclerViewScroll', { - detail: { - offset: realOffset - } - })); - }; - - getNodeOffset() { - return this.rootNode[scrollProp[this.scrollDirection]]; - } -} - -VirtualizedList.NestedList = NestedList; diff --git a/packages/rax-recyclerview/src/VirtualizedList/index.tsx b/packages/rax-recyclerview/src/VirtualizedList/index.tsx new file mode 100644 index 00000000..d19886e7 --- /dev/null +++ b/packages/rax-recyclerview/src/VirtualizedList/index.tsx @@ -0,0 +1,133 @@ +import { createElement, forwardRef, useState, useMemo, memo, Fragment } from 'rax'; +import ScrollView from 'rax-scrollview'; +import View from 'rax-view'; +import Children from 'rax-children'; + +import NoRecycleList from './NoRecycleList'; +import throttle from './throttle'; + +import { VirtualizedList } from './types'; + +function createArray(length) { + if (length > 0) { + return new Array(length).fill(1); + } + return []; +} + +function getConstantKey(horizontal: boolean) { + if (horizontal) { + return { + contentOffset: 'x', + placeholderStyle: 'width' + }; + } + return { + contentOffset: 'y', + placeholderStyle: 'height' + }; +} + +const Cell = memo(({children}) => { + return (<>{children}); +}); +Cell.displayName = 'Cell'; + +const Header = memo(({children}) => { + return (<>{children}); +}); +Header.displayName = 'Header'; + +const NestedList = memo( + forwardRef((props, ref) => { + return ( + + ); + }) +); +NestedList.displayName = 'NestedList'; + +function getVirtualizedList(SizeAndPositionManager): VirtualizedList { + const VirtualizedList: VirtualizedList = forwardRef((props, ref) => { + const { itemSize, horizontal, children, bufferSize, totalSize, scrollEventThrottle, ...rest } = props; + if (!itemSize) { + return ({children}); + } + const { + headers, + cells, + cellLength + } = useMemo(() => { + const flattenChildren = Children.toArray(children); + + // header and cell must list in order + let headerIndex = 0; + for (let i = 0; i < flattenChildren.length; i ++) { + if (flattenChildren[i].type !== Header) { + break; + } + headerIndex++; + } + + const headers = flattenChildren.slice(0, headerIndex); + const cells = flattenChildren.slice(headerIndex); + return { + headers, + cells, + cellLength: cells.length + }; + }, [children]); + + const constantKey = getConstantKey(horizontal); + const manager = useMemo(() => { + return new SizeAndPositionManager({ + itemSize, + horizontal, + bufferSize, + length: cellLength, + totalSize + }); + }, [itemSize, horizontal, cellLength, bufferSize]); + const [renderedIndex, setRenderedIndex] = useState(() => manager.getRenderedIndex(0)); + const { + front, + back + } = useMemo(() => { + return manager.getPlaceholderSize(renderedIndex.startIndex, renderedIndex.endIndex); + }, [renderedIndex.startIndex, renderedIndex.endIndex]); + + function handleScroll(e) { + const offset = e.nativeEvent.contentOffset[constantKey.contentOffset]; + const newRenderedIndex = manager.getRenderedIndex(offset); + setRenderedIndex(newRenderedIndex); + props.onScroll && props.onScroll(e); + } + + return ( + + {headers} + + {createArray(renderedIndex.startIndex).map((v, index) => )} + {cells.slice(renderedIndex.startIndex, renderedIndex.endIndex + 1).map((child, index) => {child})} + {createArray(cellLength - renderedIndex.endIndex - 1).map((v, index) => )} + + + ); + }); + + VirtualizedList.Header = Header; + VirtualizedList.Cell = Cell; + VirtualizedList.NestedList = NestedList; + VirtualizedList.displayName = 'RecyclerView'; + + return VirtualizedList; +} + +export default getVirtualizedList; \ No newline at end of file diff --git a/packages/rax-recyclerview/src/VirtualizedList/throttle.js b/packages/rax-recyclerview/src/VirtualizedList/throttle.ts similarity index 57% rename from packages/rax-recyclerview/src/VirtualizedList/throttle.js rename to packages/rax-recyclerview/src/VirtualizedList/throttle.ts index ed62d7ca..1db4ccd2 100644 --- a/packages/rax-recyclerview/src/VirtualizedList/throttle.js +++ b/packages/rax-recyclerview/src/VirtualizedList/throttle.ts @@ -1,6 +1,9 @@ -export default function throttle(func, wait) { - var ctx, args, rtn, timeoutID; - var last = 0; +export default function throttle(func: (...args: any[]) => void, wait: number) { + let ctx: any; + let args: any; + let rtn: any; + let timeoutID: number | ReturnType; + let last = 0; function call() { timeoutID = 0; @@ -13,7 +16,7 @@ export default function throttle(func, wait) { return function throttled() { ctx = this; args = arguments; - var delta = new Date() - last; + var delta = new Date().getTime() - last; if (!timeoutID) if (delta >= wait) call(); else timeoutID = setTimeout(call, wait - delta); diff --git a/packages/rax-recyclerview/src/VirtualizedList/timer.js b/packages/rax-recyclerview/src/VirtualizedList/timer.js deleted file mode 100644 index cb693427..00000000 --- a/packages/rax-recyclerview/src/VirtualizedList/timer.js +++ /dev/null @@ -1,119 +0,0 @@ -const requestAnimationF = typeof requestAnimationFrame === 'undefined' ? - typeof webkitRequestAnimationFrame === 'undefined' ? - job => setTimeout(job, 16) : - webkitRequestAnimationFrame : requestAnimationFrame; - -const cancelAnimationF = typeof cancelAnimationFrame === 'undefined' ? - typeof webkitCancelAnimationFrame === 'undefined' ? - clearTimeout : - webkitCancelAnimationFrame : cancelAnimationFrame; - -const TYPES = { - START: 'start', - END: 'end', - RUN: 'run', - STOP: 'stop' -}; - -const easing = { - easeOutSine(x) { - return Math.sin(x * Math.PI / 2); - } -}; - -const MIN_DURATION = 1; - -const noop = () => {}; - -class Timer { - constructor(config) { - this.config = { - easing: 'linear', - duration: Infinity, - onStart: noop, - onRun: noop, - onStop: noop, - onEnd: noop, - ...config, - }; - } - - run() { - let {duration, onStart, onRun} = this.config; - if (duration <= MIN_DURATION) { - this.isfinished = true; - onRun({percent: 1}); - this.stop(); - } - if (this.isfinished) return; - this._hasFinishedPercent = this._stop && this._stop.percent || 0; - this._stop = null; - this.start = Date.now(); - this.percent = 0; - onStart({percent: 0, type: TYPES.START}); - // epsilon determines the precision of the solved values - let epsilon = 1000 / 60 / duration / 4; - this.easingFn = easing[this.config.easing]; - this._run(); - } - - _run() { - let {onRun, onStop} = this.config; - this._raf && cancelAnimationF(this._raf); - this._raf = requestAnimationF(() => { - this.now = Date.now(); - this.t = this.now - this.start; - this.duration = this.now - this.start >= this.config.duration ? this.config.duration : this.now - this.start; - this.progress = this.easingFn(this.duration / this.config.duration); - this.percent = this.duration / this.config.duration + this._hasFinishedPercent; - if (this.percent >= 1 || this._stop) { - this.percent = this._stop && this._stop.percent ? this._stop.percent : 1; - this.duration = this._stop && this._stop.duration ? this._stop.duration : this.duration; - - onRun({ - percent: this.progress, - originPercent: this.percent, - t: this.t, - type: TYPES.RUN - }); - - onStop({ - percent: this.percent, - t: this.t, - type: TYPES.STOP - }); - - if (this.percent >= 1) { - this.isfinished = true; - this.stop(); - } - return; - } - - onRun({ - percent: this.progress, - originPercent: this.percent, - t: this.t, - type: TYPES.RUN - }); - - this._run(); - }); - } - - stop() { - let {onEnd} = this.config; - this._stop = { - percent: this.percent, - now: this.now - }; - onEnd({ - percent: 1, - t: this.t, - type: TYPES.END - }); - cancelAnimationF(this._raf); - } -} - -export default Timer; \ No newline at end of file diff --git a/packages/rax-recyclerview/src/VirtualizedList/types.ts b/packages/rax-recyclerview/src/VirtualizedList/types.ts new file mode 100644 index 00000000..26e76540 --- /dev/null +++ b/packages/rax-recyclerview/src/VirtualizedList/types.ts @@ -0,0 +1,21 @@ +import { ScrollViewProps } from 'rax-scrollview'; +import { RefAttributes, HTMLAttributes, ForwardRefExoticComponent } from 'rax'; + +export type TItemSize = number | ((e: number) => number); + +export interface RecyclerViewRefObject extends ScrollViewProps { + itemSize: TItemSize; + totalSize?: number; + bufferSize?: number; + horizontal: boolean; + bufferRatio?: number; + scrollEventThrottle?: number; +} + +export interface LegacyRefObject extends RefAttributes, HTMLAttributes {} + +export interface VirtualizedList extends ForwardRefExoticComponent { + Header?: Rax.MemoExoticComponent> | Rax.NamedExoticComponent; + Cell?: Rax.MemoExoticComponent> | Rax.NamedExoticComponent; + NestedList?: Rax.MemoExoticComponent>; +} \ No newline at end of file diff --git a/packages/rax-recyclerview/src/__tests__/RecyclerView.js b/packages/rax-recyclerview/src/__tests__/RecyclerView.js index bc79daf6..630aaf8a 100644 --- a/packages/rax-recyclerview/src/__tests__/RecyclerView.js +++ b/packages/rax-recyclerview/src/__tests__/RecyclerView.js @@ -1,6 +1,6 @@ import {createElement, Component} from 'rax'; import renderer from 'rax-test-renderer'; -import RecyclerView from '../'; +import RecyclerView from '../../lib'; class RecyclerViewTest extends Component { renderHeader() { diff --git a/packages/rax-recyclerview/src/__tests__/RecyclerView.weex.js b/packages/rax-recyclerview/src/__tests__/RecyclerView.weex.js index dd6fabd5..09dce1f9 100644 --- a/packages/rax-recyclerview/src/__tests__/RecyclerView.weex.js +++ b/packages/rax-recyclerview/src/__tests__/RecyclerView.weex.js @@ -1,6 +1,6 @@ import {createElement, Component} from 'rax'; import renderer from 'rax-test-renderer'; -import RecyclerView from '../'; +import RecyclerView from '../../lib'; jest.mock('universal-env', () => { return { diff --git a/packages/rax-recyclerview/src/index.js b/packages/rax-recyclerview/src/index.js deleted file mode 100644 index e0fde969..00000000 --- a/packages/rax-recyclerview/src/index.js +++ /dev/null @@ -1,176 +0,0 @@ -import { - createElement, - createContext, - useContext, - forwardRef, - memo, - useState, - useRef, - useImperativeHandle -} from 'rax'; -import { isWeex } from 'universal-env'; -import View from 'rax-view'; -import findDOMNode from 'rax-find-dom-node'; -import RefreshControl from 'rax-refreshcontrol'; -import ScrollView from 'rax-scrollview'; -import Children from 'rax-children'; -import VirtualizedList from './VirtualizedList/index'; - -const Context = createContext(true); - -const Cell = memo( - forwardRef(({ className, style, ...rest }, ref) => { - const isInARecyclerView = useContext(Context); - return isWeex && isInARecyclerView ? ( - - ) : ( - - ); - }) -); -Cell.displayName = 'Cell'; - -const Header = memo( - forwardRef(({ className, style, ...rest }, ref) => { - const isInARecyclerView = useContext(Context); - return isWeex && isInARecyclerView ? ( -
- ) : ( - - ); - }) -); -Header.displayName = 'Header'; - -const NestedList = memo( - forwardRef(({ className, style, ...rest }, ref) => { - const isInARecyclerView = useContext(Context); - return !isWeex && isInARecyclerView ? ( - - ) : ( - - ); - }) -); -NestedList.displayName = 'NestedList'; - -const RecyclerView = forwardRef((props, ref) => { - const { className, style, ...rest } = props; - const [loadmoreretry, setLoadmoreretry] = useState(0); - const scrollview = useRef(null); - const list = useRef(null); - const firstNodePlaceholder = useRef(null); - const needRecycler = props.itemSize || props.nestedList ? true : false; - - const handleScroll = e => { - e.nativeEvent = { - contentOffset: { - // HACK: weex scroll event value is opposite of web - x: -e.contentOffset.x, - y: -e.contentOffset.y - }, - contentSize: e.contentSize - ? { - width: e.contentSize.width, - height: e.contentSize.height - } - : null - }; - props.onScroll(e); - }; - - - useImperativeHandle(ref, () => ({ - _nativeNode: isWeex ? list.current : needRecycler ? scrollview.current : scrollview.current._nativeNode, - resetScroll() { - if (isWeex) { - setLoadmoreretry(loadmoreretry + 1); // for weex 0.9- - list.current.resetLoadmore && list.current.resetLoadmore(); // for weex 0.9+ - } else { - scrollview.current.resetScroll(); - } - }, - scrollTo(options) { - let x = parseInt(options.x); - let y = parseInt(options.y); - let animated = - options && typeof options.animated !== 'undefined' - ? options.animated - : true; - - if (isWeex) { - let dom = __weex_require__('@weex-module/dom'); - let firstNode = findDOMNode(firstNodePlaceholder.current); - dom.scrollToElement(firstNode, { - offset: x || y || 0, - animated - }); - } else if (needRecycler) { - scrollview.current.scrollTo(x || y, animated); - } else { - scrollview.current.scrollTo(options); - } - } - })); - - if (isWeex) { - let cells = Children.map(props.children, (child, index) => { - if (child) { - let hasOnRefresh = - child.props && typeof child.props.onRefresh == 'function'; - if ( - props._autoWrapCell && - child.type != RefreshControl && - child.type != Header && - !hasOnRefresh - ) { - return {child}; - } else { - return child; - } - } else { - return ; - } - }); - - // add firstNodePlaceholder after refreshcontrol - if (cells && cells.length) { - let addIndex = cells[0].type == Cell || cells[0].type == Header ? 0 : 1; - cells.splice(addIndex, 0, ); - } - - return ( - - - {cells} - - - ); - } else { - if (needRecycler) { - return ( - - ); - } else { - return ( - - ); - } - } -}); - -RecyclerView.Header = Header; -RecyclerView.Cell = Cell; -RecyclerView.NestedList = NestedList; -RecyclerView.displayName = 'RecyclerView'; - -export default RecyclerView; diff --git a/packages/rax-recyclerview/src/index.tsx b/packages/rax-recyclerview/src/index.tsx new file mode 100644 index 00000000..8dd8e42d --- /dev/null +++ b/packages/rax-recyclerview/src/index.tsx @@ -0,0 +1,19 @@ +import { isWeb, isWeex, isMiniApp, isWeChatMiniProgram, isByteDanceMicroApp, isBaiduSmartProgram, isKuaiShouMiniProgram } from 'universal-env'; + +let RecyclerView = null; + +if (isWeex) { + RecyclerView = require('./weex'); +} else if (!isWeb && (isMiniApp || isWeChatMiniProgram || isByteDanceMicroApp || isBaiduSmartProgram || isKuaiShouMiniProgram)) { + RecyclerView = require('./miniapp-runtime'); +} else { + RecyclerView = require('./web'); +} + +if (RecyclerView.default) { + RecyclerView = RecyclerView.default; +} + +export * from './VirtualizedList/types'; +export default RecyclerView; + diff --git a/packages/rax-recyclerview/src/miniapp-runtime/getInfoSync.ts b/packages/rax-recyclerview/src/miniapp-runtime/getInfoSync.ts new file mode 100644 index 00000000..31d8f312 --- /dev/null +++ b/packages/rax-recyclerview/src/miniapp-runtime/getInfoSync.ts @@ -0,0 +1,41 @@ +/* eslint-disable */ +import { isMiniApp, isWeChatMiniProgram, isByteDanceMicroApp, isBaiduSmartProgram, isKuaiShouMiniProgram } from 'universal-env'; + + +export default function getInfoSync(): { + windowHeight: number; + windowWidth: number; +} { + if (isWeChatMiniProgram) { + // @ts-ignore + return wx.getSystemInfoSync(); + } + if (isByteDanceMicroApp) { + // @ts-ignore + return tt.getSystemInfoSync(); + } + + if (isMiniApp) { + // @ts-ignore + const isDingdingMiniapp = typeof dd !== 'undefined' && dd !== null && typeof dd.alert !== 'undefined'; + if (isDingdingMiniapp) { + // @ts-ignore + return dd.getSystemInfoSync(); + } + return my.getSystemInfoSync(); + } + + if (isBaiduSmartProgram) { + // @ts-ignore + return swan.getSystemInfoSync(); + } + + if (isKuaiShouMiniProgram) { + // @ts-ignore + return ks.getSystemInfoSync(); + } + + throw new Error('getInfoSync 暂不支持'); +} + + diff --git a/packages/rax-recyclerview/src/miniapp-runtime/index.ts b/packages/rax-recyclerview/src/miniapp-runtime/index.ts new file mode 100644 index 00000000..7ea424fe --- /dev/null +++ b/packages/rax-recyclerview/src/miniapp-runtime/index.ts @@ -0,0 +1,14 @@ +import getInfoSync from './getInfoSync'; +import getVirtualizedList from '../VirtualizedList'; +import SizeAndPositionManager from '../VirtualizedList/SizeAndPositionManager'; + +const FULL_WIDTH = 750; +const { windowHeight, windowWidth } = getInfoSync(); +SizeAndPositionManager.clientSize = { + width: windowWidth, + height: windowHeight +}; +SizeAndPositionManager.pixelRatio = windowWidth / FULL_WIDTH; + +const VirtualizedList = getVirtualizedList(SizeAndPositionManager); +export default VirtualizedList; \ No newline at end of file diff --git a/packages/rax-recyclerview/src/web/index.tsx b/packages/rax-recyclerview/src/web/index.tsx new file mode 100644 index 00000000..a076b000 --- /dev/null +++ b/packages/rax-recyclerview/src/web/index.tsx @@ -0,0 +1,12 @@ +import SizeAndPositionManager from '../VirtualizedList/SizeAndPositionManager'; +import getVirtualizedList from '../VirtualizedList'; + +const FULL_WIDTH = 750; +SizeAndPositionManager.clientSize = { + width: document.documentElement.clientWidth, + height: document.documentElement.clientHeight +}; +SizeAndPositionManager.pixelRatio = document.documentElement.clientWidth / FULL_WIDTH; + +const VirtualizedList = getVirtualizedList(SizeAndPositionManager); +export default VirtualizedList; \ No newline at end of file diff --git a/packages/rax-recyclerview/src/weex/index.js b/packages/rax-recyclerview/src/weex/index.js new file mode 100644 index 00000000..b2114c3e --- /dev/null +++ b/packages/rax-recyclerview/src/weex/index.js @@ -0,0 +1,139 @@ +import { + createElement, + createContext, + useContext, + forwardRef, + memo, + useState, + useRef, + useImperativeHandle +} from 'rax'; +import View from 'rax-view'; +import findDOMNode from 'rax-find-dom-node'; +import RefreshControl from 'rax-refreshcontrol'; +import Children from 'rax-children'; + +const Context = createContext(true); + +const Cell = memo( + forwardRef(({ className, style, ...rest }, ref) => { + return ( + + ); + }) +); +Cell.displayName = 'Cell'; + +const Header = memo( + forwardRef(({ className, style, ...rest }, ref) => { + const isInARecyclerView = useContext(Context); + return ( +
+ ); + }) +); +Header.displayName = 'Header'; + +const NestedList = memo( + forwardRef(({ className, style, ...rest }, ref) => { + return ( + + ); + }) +); +NestedList.displayName = 'NestedList'; + +const RecyclerView = forwardRef((props, ref) => { + const { className, style, ...rest } = props; + const [loadmoreretry, setLoadmoreretry] = useState(0); + const list = useRef(null); + const firstNodePlaceholder = useRef(null); + + const handleScroll = e => { + e.nativeEvent = { + contentOffset: { + // HACK: weex scroll event value is opposite of web + x: -e.contentOffset.x, + y: -e.contentOffset.y + }, + contentSize: e.contentSize + ? { + width: e.contentSize.width, + height: e.contentSize.height + } + : null + }; + props.onScroll(e); + }; + + + useImperativeHandle(ref, () => ({ + _nativeNode: list.current, + resetScroll() { + setLoadmoreretry(loadmoreretry + 1); // for weex 0.9- + list.current.resetLoadmore && list.current.resetLoadmore(); // for weex 0.9+ + }, + scrollTo(options) { + let x = parseInt(options.x); + let y = parseInt(options.y); + let animated = + options && typeof options.animated !== 'undefined' + ? options.animated + : true; + + let dom = __weex_require__('@weex-module/dom'); + let firstNode = findDOMNode(firstNodePlaceholder.current); + dom.scrollToElement(firstNode, { + offset: x || y || 0, + animated + }); + } + })); + + let cells = Children.map(props.children, (child, index) => { + if (child) { + let hasOnRefresh = + child.props && typeof child.props.onRefresh == 'function'; + if ( + props._autoWrapCell && + child.type != RefreshControl && + child.type != Header && + !hasOnRefresh + ) { + return {child}; + } else { + return child; + } + } else { + return ; + } + }); + + // add firstNodePlaceholder after refreshcontrol + if (cells && cells.length) { + let addIndex = cells[0].type == Cell || cells[0].type == Header ? 0 : 1; + cells.splice(addIndex, 0, ); + } + + return ( + + {cells} + + ); +}); + +RecyclerView.Header = Header; +RecyclerView.Cell = Cell; +RecyclerView.NestedList = NestedList; +RecyclerView.displayName = 'RecyclerView'; + +export default RecyclerView; diff --git a/packages/rax-scrollview/CHANGELOG.md b/packages/rax-scrollview/CHANGELOG.md index 0eef046b..292f0ed9 100644 --- a/packages/rax-scrollview/CHANGELOG.md +++ b/packages/rax-scrollview/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 3.7.1 + +- Replace `ref` with `forwardRef` in `useImperativeHandle` if `forwardRef` exists + ## 3.7.0 - Support weex v2 diff --git a/packages/rax-scrollview/package.json b/packages/rax-scrollview/package.json index 4ad85fca..3d169147 100644 --- a/packages/rax-scrollview/package.json +++ b/packages/rax-scrollview/package.json @@ -1,6 +1,6 @@ { "name": "rax-scrollview", - "version": "3.7.0", + "version": "3.7.1-1", "description": "ScrollView component for Rax.", "license": "BSD-3-Clause", "main": "lib/index.js", diff --git a/packages/rax-scrollview/src/miniapp/index.tsx b/packages/rax-scrollview/src/miniapp/index.tsx index 01ff0038..2422d85a 100644 --- a/packages/rax-scrollview/src/miniapp/index.tsx +++ b/packages/rax-scrollview/src/miniapp/index.tsx @@ -12,6 +12,7 @@ import { ScrollViewProps } from '../types'; import wrapDefaultProperties from '../utils/wrapDefaultProperties'; import { getInfoSync } from '@uni/system-info'; import '../index.css'; +import omit from '../utils/omit'; const FULL_WIDTH = 750; const ANIMATION_DURATION = 400; @@ -56,7 +57,8 @@ const ScrollView: ForwardRefExoticComponent = forwardRef( onScroll, children, disableScroll = false, - onEndReachedThreshold + onEndReachedThreshold, + forwardRef } = props; const [scrollTop] = useState(0); const [scrollLeft] = useState(0); @@ -83,7 +85,7 @@ const ScrollView: ForwardRefExoticComponent = forwardRef( onScroll(e); } }; - useImperativeHandle(ref, () => ({ + useImperativeHandle(forwardRef ? forwardRef : ref, () => ({ _nativeNode: scrollerRef.current, resetScroll() { if (horizontal) { @@ -142,7 +144,7 @@ const ScrollView: ForwardRefExoticComponent = forwardRef( return ( , fields: string[]): Record { + const shallowCopy = Object.assign({}, obj); + + for (let i = 0; i < fields.length; i++) { + const key = fields[i]; + if (shallowCopy.hasOwnProperty(key)) { + delete shallowCopy[key]; + } + } + + return shallowCopy; +} \ No newline at end of file diff --git a/packages/rax-scrollview/src/web/index.tsx b/packages/rax-scrollview/src/web/index.tsx index 3a966776..74bec582 100644 --- a/packages/rax-scrollview/src/web/index.tsx +++ b/packages/rax-scrollview/src/web/index.tsx @@ -102,7 +102,8 @@ const ScrollView: ForwardRefExoticComponent = forwardRef( onEndReached, onEndReachedThreshold, onScroll, - children + children, + forwardRef } = props; const lastScrollDistance = useRef(0); const lastScrollContentSize = useRef(0); @@ -159,7 +160,7 @@ const ScrollView: ForwardRefExoticComponent = forwardRef( lastScrollDistance.current = scrollDistance; } }; - useImperativeHandle(ref, () => ({ + useImperativeHandle(forwardRef ? forwardRef : ref, () => ({ _nativeNode: scrollerRef.current, resetScroll() { lastScrollContentSize.current = 0; From 2e7473c6cfaaea6c91f4b741986690c895900226 Mon Sep 17 00:00:00 2001 From: "chenrongyan.cry" Date: Thu, 4 Nov 2021 15:08:26 +0800 Subject: [PATCH 05/16] fix: calc endIndex --- .../VirtualizedList/SizeAndPositionManager.ts | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.ts b/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.ts index 1cedb55d..6da08c29 100644 --- a/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.ts +++ b/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.ts @@ -66,7 +66,7 @@ class SizeAndPositionManager { } else { startIndex = Math.floor((distance - this.bufferSize) / itemSize); } - endIndex = Math.ceil(this.bufferSize * SizeAndPositionManager.bufferRatio / itemSize) + startIndex; + endIndex = Math.ceil(this.bufferSize * SizeAndPositionManager.bufferRatio / itemSize) + startIndex - 1; return { startIndex, @@ -77,23 +77,29 @@ class SizeAndPositionManager { return (scrollDistance: number) => { const distance = scrollDistance / SizeAndPositionManager.pixelRatio; let startIndex = -1; - let endIndex = -1; + let endIndex = this.length - 1; let size = 0; - const frontSize = this.bufferSize + distance; + if (distance < this.bufferSize) { + startIndex = 0; + } else { + const frontSize = distance - this.bufferSize; + for (let i = 0; i < this.length; i++) { + size += this.getSize(i); + if (size >= frontSize) { + startIndex = i; + break; + } + } + } const backSize = this.bufferSize * SizeAndPositionManager.bufferRatio + distance; - for (let i = 0; i < this.length; i++) { + for (let i = startIndex; i < this.length; i++) { size += this.getSize(i); - if (startIndex === -1 && size >= frontSize) { - startIndex = i; - } if (size >= backSize) { endIndex = i; - return { - startIndex, - endIndex - }; + break; } } + return { startIndex, endIndex From 0a0de41aac6e234da4c8dcad086e96c047915a66 Mon Sep 17 00:00:00 2001 From: "chenrongyan.cry" Date: Wed, 24 Nov 2021 14:28:46 +0800 Subject: [PATCH 06/16] chore: require > import --- packages/rax-recyclerview/src/index.tsx | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/rax-recyclerview/src/index.tsx b/packages/rax-recyclerview/src/index.tsx index 8dd8e42d..16f6980f 100644 --- a/packages/rax-recyclerview/src/index.tsx +++ b/packages/rax-recyclerview/src/index.tsx @@ -1,17 +1,18 @@ import { isWeb, isWeex, isMiniApp, isWeChatMiniProgram, isByteDanceMicroApp, isBaiduSmartProgram, isKuaiShouMiniProgram } from 'universal-env'; +import RecyclerViewWeb from './web'; +import RecyclerViewWeex from './weex'; +import RecyclerViewMiniProgram from './miniapp-runtime'; let RecyclerView = null; if (isWeex) { - RecyclerView = require('./weex'); -} else if (!isWeb && (isMiniApp || isWeChatMiniProgram || isByteDanceMicroApp || isBaiduSmartProgram || isKuaiShouMiniProgram)) { - RecyclerView = require('./miniapp-runtime'); + RecyclerView = RecyclerViewWeex; +} else if (isWeb) { + RecyclerView = RecyclerViewWeb; +} else if (isMiniApp || isWeChatMiniProgram || isByteDanceMicroApp || isBaiduSmartProgram || isKuaiShouMiniProgram) { + RecyclerView = RecyclerViewMiniProgram; } else { - RecyclerView = require('./web'); -} - -if (RecyclerView.default) { - RecyclerView = RecyclerView.default; + RecyclerView = RecyclerViewWeb; } export * from './VirtualizedList/types'; From 3d138b12235bdf6c54c0c3196701a7e1ff928ed5 Mon Sep 17 00:00:00 2001 From: "chenrongyan.cry" Date: Wed, 8 Dec 2021 16:44:03 +0800 Subject: [PATCH 07/16] fix: sticky --- packages/rax-recyclerview/package.json | 2 +- .../src/VirtualizedList/index.tsx | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/packages/rax-recyclerview/package.json b/packages/rax-recyclerview/package.json index d5c8a1d4..2ac6fbfc 100644 --- a/packages/rax-recyclerview/package.json +++ b/packages/rax-recyclerview/package.json @@ -1,6 +1,6 @@ { "name": "rax-recyclerview", - "version": "2.0.0-5", + "version": "2.0.0-7", "description": "RecyclerView component for Rax.", "license": "BSD-3-Clause", "main": "lib/index.js", diff --git a/packages/rax-recyclerview/src/VirtualizedList/index.tsx b/packages/rax-recyclerview/src/VirtualizedList/index.tsx index d19886e7..c336080a 100644 --- a/packages/rax-recyclerview/src/VirtualizedList/index.tsx +++ b/packages/rax-recyclerview/src/VirtualizedList/index.tsx @@ -33,8 +33,8 @@ const Cell = memo(({children}) => { }); Cell.displayName = 'Cell'; -const Header = memo(({children}) => { - return (<>{children}); +const Header = memo(({children, ...rest}) => { + return ({children}); }); Header.displayName = 'Header'; @@ -112,12 +112,15 @@ function getVirtualizedList(SizeAndPositionManager): VirtualizedList { onScroll={scrollEventThrottle ? throttle(handleScroll, scrollEventThrottle) : handleScroll} scroll-anchoring={true} > - {headers} - - {createArray(renderedIndex.startIndex).map((v, index) => )} - {cells.slice(renderedIndex.startIndex, renderedIndex.endIndex + 1).map((child, index) => {child})} - {createArray(cellLength - renderedIndex.endIndex - 1).map((v, index) => )} - + {/* fix sticky by adding view */} + + {headers} + + {createArray(renderedIndex.startIndex).map((v, index) => )} + {cells.slice(renderedIndex.startIndex, renderedIndex.endIndex + 1).map((child, index) => {child})} + {createArray(cellLength - renderedIndex.endIndex - 1).map((v, index) => )} + + ); }); From 87f0a15ca12743be53830cfa21c067fc30f87127 Mon Sep 17 00:00:00 2001 From: "chenrongyan.cry" Date: Wed, 8 Dec 2021 20:19:03 +0800 Subject: [PATCH 08/16] fix: compat updating --- packages/rax-recyclerview/package.json | 2 +- .../src/VirtualizedList/SizeAndPositionManager.ts | 2 ++ .../src/VirtualizedList/index.tsx | 15 +++++++++++---- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/rax-recyclerview/package.json b/packages/rax-recyclerview/package.json index 2ac6fbfc..562f9f57 100644 --- a/packages/rax-recyclerview/package.json +++ b/packages/rax-recyclerview/package.json @@ -1,6 +1,6 @@ { "name": "rax-recyclerview", - "version": "2.0.0-7", + "version": "2.0.0-8", "description": "RecyclerView component for Rax.", "license": "BSD-3-Clause", "main": "lib/index.js", diff --git a/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.ts b/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.ts index 6da08c29..990e8e27 100644 --- a/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.ts +++ b/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.ts @@ -18,6 +18,8 @@ class SizeAndPositionManager { public static clientSize; public static pixelRatio: number; + public static initRenderedIndex = { startIndex: -1, endIndex: -1 } + public constructor({ itemSize, horizontal, length, bufferSize, totalSize }: { itemSize: TItemSize; horizontal: boolean; diff --git a/packages/rax-recyclerview/src/VirtualizedList/index.tsx b/packages/rax-recyclerview/src/VirtualizedList/index.tsx index c336080a..318b967d 100644 --- a/packages/rax-recyclerview/src/VirtualizedList/index.tsx +++ b/packages/rax-recyclerview/src/VirtualizedList/index.tsx @@ -1,4 +1,4 @@ -import { createElement, forwardRef, useState, useMemo, memo, Fragment } from 'rax'; +import { createElement, forwardRef, useState, useMemo, memo, Fragment, useRef } from 'rax'; import ScrollView from 'rax-scrollview'; import View from 'rax-view'; import Children from 'rax-children'; @@ -78,17 +78,23 @@ function getVirtualizedList(SizeAndPositionManager): VirtualizedList { }; }, [children]); + const offsetRef = useRef(0); + const constantKey = getConstantKey(horizontal); - const manager = useMemo(() => { - return new SizeAndPositionManager({ + const [renderedIndex, setRenderedIndex] = useState(SizeAndPositionManager.initRenderedIndex); + + const manager = useMemo(() => { + const manager = new SizeAndPositionManager({ itemSize, horizontal, bufferSize, length: cellLength, totalSize }); + setRenderedIndex(manager.getRenderedIndex(offsetRef.current)) + return manager; }, [itemSize, horizontal, cellLength, bufferSize]); - const [renderedIndex, setRenderedIndex] = useState(() => manager.getRenderedIndex(0)); + const { front, back @@ -98,6 +104,7 @@ function getVirtualizedList(SizeAndPositionManager): VirtualizedList { function handleScroll(e) { const offset = e.nativeEvent.contentOffset[constantKey.contentOffset]; + offsetRef.current = offset; const newRenderedIndex = manager.getRenderedIndex(offset); setRenderedIndex(newRenderedIndex); props.onScroll && props.onScroll(e); From bf132852724976c42580594ecaed3503c3777cc8 Mon Sep 17 00:00:00 2001 From: "chenrongyan.cry" Date: Tue, 25 Jan 2022 11:54:04 +0800 Subject: [PATCH 09/16] wip --- .../VirtualizedList/SizeAndPositionManager.ts | 75 ++++++++++++------- .../src/VirtualizedList/index.tsx | 13 +++- 2 files changed, 60 insertions(+), 28 deletions(-) diff --git a/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.ts b/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.ts index 990e8e27..75b577ff 100644 --- a/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.ts +++ b/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.ts @@ -14,7 +14,7 @@ class SizeAndPositionManager { public getRenderedIndex: TRenderedIndexGetter; public getPlaceholderSize: TPlaceholderSizeGetter; - public static bufferRatio = 3; + public static bufferRatio = 1; public static clientSize; public static pixelRatio: number; @@ -62,46 +62,71 @@ class SizeAndPositionManager { if (typeof itemSize === 'number') { return (scrollDistance: number) => { const distance = scrollDistance / SizeAndPositionManager.pixelRatio; - let startIndex, endIndex; - if (distance < this.bufferSize) { - startIndex = 0; - } else { - startIndex = Math.floor((distance - this.bufferSize) / itemSize); - } - endIndex = Math.ceil(this.bufferSize * SizeAndPositionManager.bufferRatio / itemSize) + startIndex - 1; - + const pageIndex = Math.floor(distance / this.bufferSize); + const startIndex = pageIndex > SizeAndPositionManager.bufferRatio ? Math.floor((pageIndex - SizeAndPositionManager.bufferRatio) * this.bufferSize / itemSize) : 0; + const endIndex = Math.ceil((pageIndex + SizeAndPositionManager.bufferRatio + 1) * this.bufferSize / itemSize); + return { startIndex, endIndex: Math.min(endIndex, this.length - 1) }; }; } - return (scrollDistance: number) => { - const distance = scrollDistance / SizeAndPositionManager.pixelRatio; - let startIndex = -1; - let endIndex = this.length - 1; - let size = 0; - if (distance < this.bufferSize) { - startIndex = 0; - } else { - const frontSize = distance - this.bufferSize; - for (let i = 0; i < this.length; i++) { + + // key: pageIndex + // value: { + // start: [startIndex, startSize], + // end: [endIndex, endSize] + // } + const pageMap = new Map(); + + function getPage(index) { + if (pageMap.has(index)) { + return pageMap.get(index); + } + const firstPageSize = this.bufferSize * SizeAndPositionManager.bufferRatio; + if (index === 0) { + let endIndex = this.length - 1; + let size = 0; + for (let i = 0; i < this.length; i++){ size += this.getSize(i); - if (size >= frontSize) { - startIndex = i; + if (size >= firstPageSize) { + endIndex = i; break; } } + pageMap.set(0, { + start: [0, 0], + end: [endIndex, size] + }); + return pageMap.get(0); } - const backSize = this.bufferSize * SizeAndPositionManager.bufferRatio + distance; - for (let i = startIndex; i < this.length; i++) { + const { end } = getPage(index - 1); + + const [preEndIndex, preEndSize] = end; + let size = preEndSize; + let endSize = this.length - 1; + const pageSize = firstPageSize * (index + 1); + for (let i = preEndIndex + 1; i < this.length; i++) { size += this.getSize(i); - if (size >= backSize) { - endIndex = i; + if (size >= pageSize) { + endSize = i; break; } } + pageMap.set(index, { + start: [preEndSize - 1, preEndSize - this.getSize(preEndSize)], + end: [endSize, size] + }); + } + return (scrollDistance: number) => { + const distance = scrollDistance / SizeAndPositionManager.pixelRatio; + const pageIndex = Math.floor(distance / this.bufferSize); + const frontPageIndex = Math.max(0, pageIndex - SizeAndPositionManager.bufferRatio); + const { start: [ startIndex ] } = getPage(frontPageIndex); + const backPageIndex = pageIndex + SizeAndPositionManager.bufferRatio + 1; + const { end: [ endIndex ] } = getPage(backPageIndex); return { startIndex, endIndex diff --git a/packages/rax-recyclerview/src/VirtualizedList/index.tsx b/packages/rax-recyclerview/src/VirtualizedList/index.tsx index 318b967d..fb688c58 100644 --- a/packages/rax-recyclerview/src/VirtualizedList/index.tsx +++ b/packages/rax-recyclerview/src/VirtualizedList/index.tsx @@ -49,7 +49,7 @@ NestedList.displayName = 'NestedList'; function getVirtualizedList(SizeAndPositionManager): VirtualizedList { const VirtualizedList: VirtualizedList = forwardRef((props, ref) => { - const { itemSize, horizontal, children, bufferSize, totalSize, scrollEventThrottle, ...rest } = props; + const { itemSize, horizontal, children, bufferSize, totalSize, scrollEventThrottle = 50, ...rest } = props; if (!itemSize) { return ({children}); } @@ -110,13 +110,20 @@ function getVirtualizedList(SizeAndPositionManager): VirtualizedList { props.onScroll && props.onScroll(e); } + const scrollRef = useRef(handleScroll); + useMemo(() => { + scrollRef.current = handleScroll; + }, [manager, props.onScroll]); + + const throttleScroll = useMemo(() => throttle((e) => scrollRef.current(e), scrollEventThrottle), [scrollEventThrottle]); + return ( {/* fix sticky by adding view */} @@ -126,7 +133,7 @@ function getVirtualizedList(SizeAndPositionManager): VirtualizedList { {createArray(renderedIndex.startIndex).map((v, index) => )} {cells.slice(renderedIndex.startIndex, renderedIndex.endIndex + 1).map((child, index) => {child})} {createArray(cellLength - renderedIndex.endIndex - 1).map((v, index) => )} - + ); From 8da31f532b5286f355864a0ca46f149f3d1d6c2d Mon Sep 17 00:00:00 2001 From: "chenrongyan.cry" Date: Wed, 9 Feb 2022 11:31:02 +0800 Subject: [PATCH 10/16] wip --- packages/rax-recyclerview/package.json | 1 + .../VirtualizedList/SizeAndPositionManager.ts | 218 +++++++----------- .../src/VirtualizedList/index.tsx | 94 ++++++-- .../src/VirtualizedList/throttle.ts | 7 +- 4 files changed, 162 insertions(+), 158 deletions(-) diff --git a/packages/rax-recyclerview/package.json b/packages/rax-recyclerview/package.json index 562f9f57..4c51c5cd 100644 --- a/packages/rax-recyclerview/package.json +++ b/packages/rax-recyclerview/package.json @@ -64,6 +64,7 @@ "react-component" ], "dependencies": { + "@uni/intersection-observer": "^1.0.7", "prop-types": "^15.7.2", "rax-children": "^1.0.0", "rax-find-dom-node": "^1.0.0", diff --git a/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.ts b/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.ts index 75b577ff..0155381f 100644 --- a/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.ts +++ b/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.ts @@ -1,166 +1,71 @@ import { TItemSize } from './types'; type TSizeGetter = (i: number) => number; -type TRenderedIndexGetter = (distance: number) => { startIndex: number; endIndex: number }; -type TPlaceholderSizeGetter = (startIndex: number, endIndex: number) => { front: number; back: number; show?: string }; - class SizeAndPositionManager { private bufferSize: number; private readonly length: number; private getSize: TSizeGetter; + private pages: Array<{ + startIndex: number; + endIndex: number; + startPos: number; + endPos: number; + }> = []; public totalSize: number; - public getRenderedIndex: TRenderedIndexGetter; - public getPlaceholderSize: TPlaceholderSizeGetter; - public static bufferRatio = 1; public static clientSize; public static pixelRatio: number; - public static initRenderedIndex = { startIndex: -1, endIndex: -1 } - - public constructor({ itemSize, horizontal, length, bufferSize, totalSize }: { + public constructor({ itemSize, horizontal, length, bufferSize, totalSize, estimateSize }: { itemSize: TItemSize; horizontal: boolean; length: number; bufferSize?: number; totalSize?: number; + estimateSize?: number; }) { this.length = length; this.bufferSize = this.getBufferSize(bufferSize, horizontal); this.getSize = this.initSizeGetter(itemSize); - this.getRenderedIndex = this.initRenderedIndexGetter(itemSize); - this.getPlaceholderSize = this.initPlaceholderSizeGetter(itemSize); - this.totalSize = totalSize ? totalSize : this.getTotalSize(itemSize, length); - } - - private initSizeGetter(itemSize): TSizeGetter { - if (typeof itemSize === 'number') { - return () => { - return itemSize; - }; - } - - if (typeof itemSize === 'function') { - const sizeCache = new Map(); - return (i: number) => { - if (sizeCache.has(i)) { - return sizeCache.get(i); - } else { - const singleSize = itemSize(i); - sizeCache.set(i, singleSize); - return singleSize; - } - }; - } - - throw new Error('itemSize is inValid'); + this.initManager(); } - private initRenderedIndexGetter(itemSize): TRenderedIndexGetter { - if (typeof itemSize === 'number') { - return (scrollDistance: number) => { - const distance = scrollDistance / SizeAndPositionManager.pixelRatio; - const pageIndex = Math.floor(distance / this.bufferSize); - const startIndex = pageIndex > SizeAndPositionManager.bufferRatio ? Math.floor((pageIndex - SizeAndPositionManager.bufferRatio) * this.bufferSize / itemSize) : 0; - const endIndex = Math.ceil((pageIndex + SizeAndPositionManager.bufferRatio + 1) * this.bufferSize / itemSize); - - return { - startIndex, - endIndex: Math.min(endIndex, this.length - 1) - }; - }; - } - - // key: pageIndex - // value: { - // start: [startIndex, startSize], - // end: [endIndex, endSize] - // } - const pageMap = new Map(); - - function getPage(index) { - if (pageMap.has(index)) { - return pageMap.get(index); - } - const firstPageSize = this.bufferSize * SizeAndPositionManager.bufferRatio; - if (index === 0) { - let endIndex = this.length - 1; - let size = 0; - for (let i = 0; i < this.length; i++){ - size += this.getSize(i); - if (size >= firstPageSize) { - endIndex = i; - break; - } - } - pageMap.set(0, { - start: [0, 0], - end: [endIndex, size] - }); - return pageMap.get(0); - } - const { end } = getPage(index - 1); - - const [preEndIndex, preEndSize] = end; - let size = preEndSize; - let endSize = this.length - 1; - const pageSize = firstPageSize * (index + 1); - for (let i = preEndIndex + 1; i < this.length; i++) { - size += this.getSize(i); - if (size >= pageSize) { - endSize = i; - break; - } - } - pageMap.set(index, { - start: [preEndSize - 1, preEndSize - this.getSize(preEndSize)], - end: [endSize, size] + private initManager() { + this.pages = []; + // sort items in pages + let size = 0; + let prevExtraSize = 0; + for (let i = 0; i < this.length;) { + const { endIndex, pageSize, extraSize } = this.calPageInfo(i, prevExtraSize); + this.pages.push({ + startIndex: i, + endIndex, + startPos: size, + endPos: size + pageSize }); + i = endIndex + 1; + size += pageSize; + prevExtraSize = extraSize; } - - return (scrollDistance: number) => { - const distance = scrollDistance / SizeAndPositionManager.pixelRatio; - const pageIndex = Math.floor(distance / this.bufferSize); - const frontPageIndex = Math.max(0, pageIndex - SizeAndPositionManager.bufferRatio); - const { start: [ startIndex ] } = getPage(frontPageIndex); - const backPageIndex = pageIndex + SizeAndPositionManager.bufferRatio + 1; - const { end: [ endIndex ] } = getPage(backPageIndex); - return { - startIndex, - endIndex - }; - }; + this.totalSize = size; } - private initPlaceholderSizeGetter(itemSize) { - if (typeof itemSize === 'number') { - return (startIndex, endIndex) => { - return { - front: startIndex * itemSize, - back: (this.length - endIndex - 1) * itemSize, - }; - }; + private calPageInfo(index: number, prevExtraSize: number) { + let size = 0; + while(index < this.length && size < this.bufferSize - prevExtraSize) { + size += this.getSize(index); + index++; } - return (startIndex, endIndex) => { - let front = 0; - let back = 0; - for (let i = 0; i < startIndex; i++) { - front += this.getSize(i); - } - for (let i = endIndex + 1; i < this.length; i++) { - back += this.getSize(i); - } - - return { - front: front, - back: back, - }; + return { + endIndex: index - 1, + pageSize: size, + extraSize: size - this.bufferSize + prevExtraSize }; } - private getBufferSize(bufferSize, horizontal) { + private getBufferSize(bufferSize, horizontal): number { if (bufferSize) { return bufferSize; } @@ -170,15 +75,56 @@ class SizeAndPositionManager { return SizeAndPositionManager.clientSize.height / SizeAndPositionManager.pixelRatio; } - private getTotalSize(itemSize: TItemSize, length: number): number { + private initSizeGetter(itemSize) { if (typeof itemSize === 'number') { - return itemSize * length; + return () => { + return itemSize; + } + } + return (i) => { + const size = itemSize(i); + if (typeof size !== 'number') { + console.error(`rax-recyclerview: ${i} 节点的 size 为 ${size},不是 number 类型`); + return 0; + } + return size; + } + } + + calCurrent(scrollDistance: number) { + const distance = scrollDistance / SizeAndPositionManager.pixelRatio; + const pageIndex = distance === 0 ? 0 : Math.ceil(distance / this.bufferSize) - 1; + const prevPageIndex = Math.max(0, pageIndex - 1); + const nextPageIndex = Math.min(this.pages.length, pageIndex + 1); + const prevPage = this.pages[prevPageIndex]; + const nextPage = this.pages[nextPageIndex]; + console.log(distance) + if (prevPage && nextPage) { + return { + startIndex: prevPage.startIndex, + endIndex: nextPage.endIndex, + front: prevPage.startPos, + back: this.totalSize - nextPage.endPos + }; } - let size; - for (let i = 0; i < length; i++) { - size += this.getSize(i); + return { + startIndex: 0, + endIndex: this.length, + front: 0, + back: 0 + }; + } + + setPageSize(index, size) { + const page = this.pages[index]; + if (page) { + const errorSize = page.endPos - page.startPos - size; + page.endPos = page.startPos + size; + for (let i = index + 1; i < this.pages.length; i ++) { + this.pages[i].startPos = this.pages[i].startPos - errorSize; + this.pages[i].endPos = this.pages[i].endPos - errorSize; + } } - return size; } } diff --git a/packages/rax-recyclerview/src/VirtualizedList/index.tsx b/packages/rax-recyclerview/src/VirtualizedList/index.tsx index fb688c58..30d4bcee 100644 --- a/packages/rax-recyclerview/src/VirtualizedList/index.tsx +++ b/packages/rax-recyclerview/src/VirtualizedList/index.tsx @@ -1,12 +1,15 @@ -import { createElement, forwardRef, useState, useMemo, memo, Fragment, useRef } from 'rax'; +import { createElement, forwardRef, useState, useMemo, memo, Fragment, useRef, useEffect } from 'rax'; import ScrollView from 'rax-scrollview'; import View from 'rax-view'; import Children from 'rax-children'; +import createIntersectionObserver from '@uni/intersection-observer'; import NoRecycleList from './NoRecycleList'; import throttle from './throttle'; import { VirtualizedList } from './types'; +import SizeAndPositionManager from './SizeAndPositionManager'; +import { isWeb } from '@uni/env'; function createArray(length) { if (length > 0) { @@ -49,7 +52,7 @@ NestedList.displayName = 'NestedList'; function getVirtualizedList(SizeAndPositionManager): VirtualizedList { const VirtualizedList: VirtualizedList = forwardRef((props, ref) => { - const { itemSize, horizontal, children, bufferSize, totalSize, scrollEventThrottle = 50, ...rest } = props; + const { itemSize, horizontal, children, bufferSize, scrollEventThrottle = 50, ...rest } = props; if (!itemSize) { return ({children}); } @@ -81,32 +84,39 @@ function getVirtualizedList(SizeAndPositionManager): VirtualizedList { const offsetRef = useRef(0); const constantKey = getConstantKey(horizontal); - const [renderedIndex, setRenderedIndex] = useState(SizeAndPositionManager.initRenderedIndex); + const [renderedIndex, setRenderedIndex] = useState([0, 0]); + const [placeholderSize, setPlaceholderSize] = useState([0, 0]); + const [observer, setObserver] = useState(null); - const manager = useMemo(() => { - const manager = new SizeAndPositionManager({ + const manager: SizeAndPositionManager = useMemo(() => { + const manager: SizeAndPositionManager = new SizeAndPositionManager({ itemSize, horizontal, bufferSize, length: cellLength, - totalSize }); - setRenderedIndex(manager.getRenderedIndex(offsetRef.current)) + const { + startIndex, + endIndex, + front, + back + } = manager.calCurrent(offsetRef.current); + setRenderedIndex([startIndex, endIndex]); + setPlaceholderSize([front, back]); return manager; }, [itemSize, horizontal, cellLength, bufferSize]); - const { - front, - back - } = useMemo(() => { - return manager.getPlaceholderSize(renderedIndex.startIndex, renderedIndex.endIndex); - }, [renderedIndex.startIndex, renderedIndex.endIndex]); - function handleScroll(e) { const offset = e.nativeEvent.contentOffset[constantKey.contentOffset]; offsetRef.current = offset; - const newRenderedIndex = manager.getRenderedIndex(offset); - setRenderedIndex(newRenderedIndex); + const { + startIndex, + endIndex, + front, + back + } = manager.calCurrent(offsetRef.current); + setRenderedIndex([startIndex, endIndex]); + setPlaceholderSize([front, back]); props.onScroll && props.onScroll(e); } @@ -117,6 +127,48 @@ function getVirtualizedList(SizeAndPositionManager): VirtualizedList { const throttleScroll = useMemo(() => throttle((e) => scrollRef.current(e), scrollEventThrottle), [scrollEventThrottle]); + function resetObservation() { + observer && observer.disconnect(); + } + + function intiateScrollObserver() { + const intersectionObserver = createIntersectionObserver({ + threshold: [0.1] + }); + intersectionObserver.observe('#rax-recyclerview-back', () => { + console.log('到底了'); + }); + intersectionObserver.observe('#rax-recyclerview-front', () => { + console.log('到头了') + }); + setObserver(intersectionObserver); + } + + useEffect(() => { + resetObservation(); + intiateScrollObserver(); + }, [renderedIndex[1]]) + + if (isWeb) { + return ( + + {/* fix sticky by adding view */} + + {headers} +
+ {cells.slice(renderedIndex[0], renderedIndex[1] + 1)} +
+ + + ); + } return ( {headers} - - {createArray(renderedIndex.startIndex).map((v, index) => )} - {cells.slice(renderedIndex.startIndex, renderedIndex.endIndex + 1).map((child, index) => {child})} - {createArray(cellLength - renderedIndex.endIndex - 1).map((v, index) => )} - + + {createArray(renderedIndex[0]).map((v, index) => )} + {cells.slice(renderedIndex[0], renderedIndex[1] + 1).map((child, index) => {child})} + {createArray(cellLength - renderedIndex[1] - 1).map((v, index) => )} + ); diff --git a/packages/rax-recyclerview/src/VirtualizedList/throttle.ts b/packages/rax-recyclerview/src/VirtualizedList/throttle.ts index 1db4ccd2..9e48640f 100644 --- a/packages/rax-recyclerview/src/VirtualizedList/throttle.ts +++ b/packages/rax-recyclerview/src/VirtualizedList/throttle.ts @@ -5,6 +5,8 @@ export default function throttle(func: (...args: any[]) => void, wait: number) { let timeoutID: number | ReturnType; let last = 0; + console.log('init'); + function call() { timeoutID = 0; last = +new Date(); @@ -19,7 +21,10 @@ export default function throttle(func: (...args: any[]) => void, wait: number) { var delta = new Date().getTime() - last; if (!timeoutID) if (delta >= wait) call(); - else timeoutID = setTimeout(call, wait - delta); + else { + timeoutID = setTimeout(call, wait - delta); + console.log('wait') + } return rtn; }; } From ad474bc45b1f132a3833ea77c974822dbb584b32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=82=BC=E7=8E=89?= Date: Tue, 22 Feb 2022 11:22:39 +0800 Subject: [PATCH 11/16] feat: Weex support scrollIntoView and Weex 2.0 --- packages/rax-recyclerview/package.json | 1 + packages/rax-recyclerview/src/weex/index.js | 48 +++++++++++++++------ 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/packages/rax-recyclerview/package.json b/packages/rax-recyclerview/package.json index 562f9f57..14fc21f9 100644 --- a/packages/rax-recyclerview/package.json +++ b/packages/rax-recyclerview/package.json @@ -67,6 +67,7 @@ "prop-types": "^15.7.2", "rax-children": "^1.0.0", "rax-find-dom-node": "^1.0.0", + "rax-get-element-by-id": "^1.0.0", "rax-refreshcontrol": "^1.0.0", "rax-scrollview": "3.7.1-1", "rax-view": "^2.0.0", diff --git a/packages/rax-recyclerview/src/weex/index.js b/packages/rax-recyclerview/src/weex/index.js index b2114c3e..528d850b 100644 --- a/packages/rax-recyclerview/src/weex/index.js +++ b/packages/rax-recyclerview/src/weex/index.js @@ -11,8 +11,11 @@ import { import View from 'rax-view'; import findDOMNode from 'rax-find-dom-node'; import RefreshControl from 'rax-refreshcontrol'; +import getElementById from 'rax-get-element-by-id'; import Children from 'rax-children'; +const isWeexV2 = typeof __weex_v2__ === 'object'; + const Context = createContext(true); const Cell = memo( @@ -74,19 +77,40 @@ const RecyclerView = forwardRef((props, ref) => { list.current.resetLoadmore && list.current.resetLoadmore(); // for weex 0.9+ }, scrollTo(options) { - let x = parseInt(options.x); - let y = parseInt(options.y); - let animated = - options && typeof options.animated !== 'undefined' - ? options.animated - : true; - - let dom = __weex_require__('@weex-module/dom'); let firstNode = findDOMNode(firstNodePlaceholder.current); - dom.scrollToElement(firstNode, { - offset: x || y || 0, - animated - }); + if (isWeexV2) { + list.current.scrollTo(firstNode, options); + } else { + let x = parseInt(options.x); + let y = parseInt(options.y); + let animated = + options && typeof options.animated !== 'undefined' + ? options.animated + : true; + + let dom = __weex_require__('@weex-module/dom'); + dom.scrollToElement(firstNode, { + offset: x || y || 0, + animated + }); + } + }, + scrollIntoView(options) { + const { id, animated = true } = options || {}; + if (!id) { + throw new Error('Params missing id.'); + } + const node = getElementById(id); + if (node) { + if (isWeexV2) { + list.current.scrollTo(node, options); + } else { + const dom = __weex_require__('@weex-module/dom'); + dom.scrollToElement(node, { + animated + }); + } + } } })); From 91e169e41c0cbdf927a76fb4c54e8a2b0ecb0f36 Mon Sep 17 00:00:00 2001 From: "chenrongyan.cry" Date: Thu, 24 Feb 2022 11:58:23 +0800 Subject: [PATCH 12/16] fix: remove useless opt in web and miniapp --- packages/rax-recyclerview/package.json | 2 +- .../src/VirtualizedList/index.tsx | 50 +++++-------------- 2 files changed, 13 insertions(+), 39 deletions(-) diff --git a/packages/rax-recyclerview/package.json b/packages/rax-recyclerview/package.json index f4a6aaa5..827e2cdf 100644 --- a/packages/rax-recyclerview/package.json +++ b/packages/rax-recyclerview/package.json @@ -1,6 +1,6 @@ { "name": "rax-recyclerview", - "version": "2.0.0-8", + "version": "2.0.0-9", "description": "RecyclerView component for Rax.", "license": "BSD-3-Clause", "main": "lib/index.js", diff --git a/packages/rax-recyclerview/src/VirtualizedList/index.tsx b/packages/rax-recyclerview/src/VirtualizedList/index.tsx index 30d4bcee..94f0b238 100644 --- a/packages/rax-recyclerview/src/VirtualizedList/index.tsx +++ b/packages/rax-recyclerview/src/VirtualizedList/index.tsx @@ -1,15 +1,14 @@ -import { createElement, forwardRef, useState, useMemo, memo, Fragment, useRef, useEffect } from 'rax'; +import { createElement, forwardRef, useState, useMemo, memo, Fragment, useRef } from 'rax'; import ScrollView from 'rax-scrollview'; import View from 'rax-view'; import Children from 'rax-children'; -import createIntersectionObserver from '@uni/intersection-observer'; import NoRecycleList from './NoRecycleList'; import throttle from './throttle'; import { VirtualizedList } from './types'; import SizeAndPositionManager from './SizeAndPositionManager'; -import { isWeb } from '@uni/env'; +import { isWeChatMiniProgram } from '@uni/env'; function createArray(length) { if (length > 0) { @@ -37,7 +36,7 @@ const Cell = memo(({children}) => { Cell.displayName = 'Cell'; const Header = memo(({children, ...rest}) => { - return ({children}); + return ({children}); }); Header.displayName = 'Header'; @@ -86,7 +85,6 @@ function getVirtualizedList(SizeAndPositionManager): VirtualizedList { const constantKey = getConstantKey(horizontal); const [renderedIndex, setRenderedIndex] = useState([0, 0]); const [placeholderSize, setPlaceholderSize] = useState([0, 0]); - const [observer, setObserver] = useState(null); const manager: SizeAndPositionManager = useMemo(() => { const manager: SizeAndPositionManager = new SizeAndPositionManager({ @@ -127,29 +125,7 @@ function getVirtualizedList(SizeAndPositionManager): VirtualizedList { const throttleScroll = useMemo(() => throttle((e) => scrollRef.current(e), scrollEventThrottle), [scrollEventThrottle]); - function resetObservation() { - observer && observer.disconnect(); - } - - function intiateScrollObserver() { - const intersectionObserver = createIntersectionObserver({ - threshold: [0.1] - }); - intersectionObserver.observe('#rax-recyclerview-back', () => { - console.log('到底了'); - }); - intersectionObserver.observe('#rax-recyclerview-front', () => { - console.log('到头了') - }); - setObserver(intersectionObserver); - } - - useEffect(() => { - resetObservation(); - intiateScrollObserver(); - }, [renderedIndex[1]]) - - if (isWeb) { + if (isWeChatMiniProgram) { return ( {headers} -
- {cells.slice(renderedIndex[0], renderedIndex[1] + 1)} -
+ + {createArray(renderedIndex[0]).map((v, index) => )} + {cells.slice(renderedIndex[0], renderedIndex[1] + 1).map((child, index) => {child})} + {createArray(cellLength - renderedIndex[1] - 1).map((v, index) => )} + ); @@ -178,17 +156,13 @@ function getVirtualizedList(SizeAndPositionManager): VirtualizedList { onScroll={throttleScroll} scroll-anchoring={true} > - {/* fix sticky by adding view */} - {headers} - {createArray(renderedIndex[0]).map((v, index) => )} - {cells.slice(renderedIndex[0], renderedIndex[1] + 1).map((child, index) => {child})} - {createArray(cellLength - renderedIndex[1] - 1).map((v, index) => )} - - + {cells.slice(renderedIndex[0], renderedIndex[1] + 1)} + ); + }); VirtualizedList.Header = Header; From 486056d8a24fda12707b86e165bde2bd3ad42763 Mon Sep 17 00:00:00 2001 From: "chenrongyan.cry" Date: Thu, 24 Feb 2022 14:39:19 +0800 Subject: [PATCH 13/16] fix: remove console.log --- packages/rax-recyclerview/package.json | 2 +- .../src/VirtualizedList/SizeAndPositionManager.ts | 1 - packages/rax-recyclerview/src/VirtualizedList/throttle.ts | 3 --- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/rax-recyclerview/package.json b/packages/rax-recyclerview/package.json index 827e2cdf..61fa3805 100644 --- a/packages/rax-recyclerview/package.json +++ b/packages/rax-recyclerview/package.json @@ -1,6 +1,6 @@ { "name": "rax-recyclerview", - "version": "2.0.0-9", + "version": "2.0.0-10", "description": "RecyclerView component for Rax.", "license": "BSD-3-Clause", "main": "lib/index.js", diff --git a/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.ts b/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.ts index 0155381f..7c1951ae 100644 --- a/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.ts +++ b/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.ts @@ -98,7 +98,6 @@ class SizeAndPositionManager { const nextPageIndex = Math.min(this.pages.length, pageIndex + 1); const prevPage = this.pages[prevPageIndex]; const nextPage = this.pages[nextPageIndex]; - console.log(distance) if (prevPage && nextPage) { return { startIndex: prevPage.startIndex, diff --git a/packages/rax-recyclerview/src/VirtualizedList/throttle.ts b/packages/rax-recyclerview/src/VirtualizedList/throttle.ts index 9e48640f..30f57307 100644 --- a/packages/rax-recyclerview/src/VirtualizedList/throttle.ts +++ b/packages/rax-recyclerview/src/VirtualizedList/throttle.ts @@ -5,8 +5,6 @@ export default function throttle(func: (...args: any[]) => void, wait: number) { let timeoutID: number | ReturnType; let last = 0; - console.log('init'); - function call() { timeoutID = 0; last = +new Date(); @@ -23,7 +21,6 @@ export default function throttle(func: (...args: any[]) => void, wait: number) { if (delta >= wait) call(); else { timeoutID = setTimeout(call, wait - delta); - console.log('wait') } return rtn; }; From 6b8fcffe014c771b034e0ae78f39f4d5a37d5a52 Mon Sep 17 00:00:00 2001 From: "chenrongyan.cry" Date: Thu, 3 Mar 2022 20:38:26 +0800 Subject: [PATCH 14/16] feat: support different itemSize --- packages/rax-recyclerview/README.md | 3 +- packages/rax-recyclerview/package.json | 8 +- .../VirtualizedList/SizeAndPositionManager.ts | 123 +++++++++++++----- .../src/VirtualizedList/index.tsx | 101 ++++++++------ .../src/VirtualizedList/types.ts | 3 +- packages/rax-recyclerview/src/index.tsx | 2 +- 6 files changed, 161 insertions(+), 79 deletions(-) diff --git a/packages/rax-recyclerview/README.md b/packages/rax-recyclerview/README.md index 21bf90b1..f8308afc 100644 --- a/packages/rax-recyclerview/README.md +++ b/packages/rax-recyclerview/README.md @@ -22,7 +22,8 @@ npm install rax-recyclerview --save | 属性 | 类型 | 默认值 | 必填 | 描述 | 支持 | | --------------------- | ----------------- | ------ | ---- | ----------------------------------------------- | ------------------------------------------------------------ | -| itemSize | `function/number` | - | √ | 单位为`rpx`, 返回每个子元素的高度(节点回收时需要,不需要传递 RecyclerView.Header 元素的高度),若 itemSize 未传,则不进行回收。 如果每个子元素的高度不固定,则可使用函数的方式,例如 itemSize={(index) => {return 100;}} 其中 `index` 为参与回收的子元素的数组下标位置 | browser | +| itemSize | `function/number` | - | × | 单位为`rpx`, 返回每个子元素的高度(节点回收时需要,不需要传递 RecyclerView.Header 元素的高度),若 itemSize 未传,则不进行回收。 如果每个子元素的高度不固定,则可使用函数的方式,例如 itemSize={(index) => {return 100;}} 其中 `index` 为参与回收的子元素的数组下标位置 | browser | +| itemEstimateSize | `number` | - | × | 单位为`rpx`, 当子元素不是固定高度时,可以传入该值作为元素的估计值,本属性只支持 Web | browser | | horizontal | `boolean` | - | false | 设置为横向滚动 | browser weexminiApp wechatMiniprogrambytedanceMicroApp quickApp| | onEndReachedThreshold | `string/number` | 500 | ✘ | 设置加载更多的偏移, 推荐使用 string 格式来指指定尺寸单位,如`100rpx` | browser weex miniAppbytedanceMicroApp | | onEndReached | `function` | - | ✘ | 滚动区域还剩`onEndReachedThreshold`的长度时触发 | browser weex miniApp wechatMiniprogrambytedanceMicroApp | diff --git a/packages/rax-recyclerview/package.json b/packages/rax-recyclerview/package.json index 61fa3805..58e15a3d 100644 --- a/packages/rax-recyclerview/package.json +++ b/packages/rax-recyclerview/package.json @@ -1,6 +1,6 @@ { "name": "rax-recyclerview", - "version": "2.0.0-10", + "version": "2.0.0-12", "description": "RecyclerView component for Rax.", "license": "BSD-3-Clause", "main": "lib/index.js", @@ -64,15 +64,15 @@ "react-component" ], "dependencies": { + "@uni/env": "^1.0.7", "@uni/intersection-observer": "^1.0.7", "prop-types": "^15.7.2", "rax-children": "^1.0.0", - "rax-find-dom-node": "^1.0.0", + "rax-find-dom-node": "^1.0.1", "rax-get-element-by-id": "^1.0.0", "rax-refreshcontrol": "^1.0.0", "rax-scrollview": "3.7.1-1", - "rax-view": "^2.0.0", - "universal-env": "^3.0.0" + "rax-view": "^2.0.0" }, "peerDependencies": { "rax": "^1.0.0" diff --git a/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.ts b/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.ts index 7c1951ae..46763946 100644 --- a/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.ts +++ b/packages/rax-recyclerview/src/VirtualizedList/SizeAndPositionManager.ts @@ -1,16 +1,21 @@ import { TItemSize } from './types'; type TSizeGetter = (i: number) => number; +interface RenderInfo { + renderedIndexs: [number, number]; + placeholderSizes: [number, number]; + pageIndexs: [number, number]; +} class SizeAndPositionManager { private bufferSize: number; private readonly length: number; private getSize: TSizeGetter; - private pages: Array<{ + private pages: { startIndex: number; endIndex: number; startPos: number; endPos: number; - }> = []; + }[] = []; public totalSize: number; @@ -18,23 +23,26 @@ class SizeAndPositionManager { public static clientSize; public static pixelRatio: number; - public constructor({ itemSize, horizontal, length, bufferSize, totalSize, estimateSize }: { + private isExactly: boolean = true; + private isCorrecting: boolean = false; + + public constructor({ itemSize, horizontal, length, bufferSize, itemEstimateSize }: { itemSize: TItemSize; horizontal: boolean; length: number; bufferSize?: number; - totalSize?: number; - estimateSize?: number; + itemEstimateSize?: number; }) { this.length = length; this.bufferSize = this.getBufferSize(bufferSize, horizontal); - this.getSize = this.initSizeGetter(itemSize); + this.isExactly = !!itemSize; + this.getSize = this.initSizeGetter(itemSize || itemEstimateSize); this.initManager(); } private initManager() { this.pages = []; - // sort items in pages + // sort items in pages let size = 0; let prevExtraSize = 0; for (let i = 0; i < this.length;) { @@ -52,9 +60,43 @@ class SizeAndPositionManager { this.totalSize = size; } - private calPageInfo(index: number, prevExtraSize: number) { + public correctSize(options: {targetNode: HTMLElement; headerLength: number; pageIndexs: [number, number]; styleString: string}) { + const { + targetNode, + headerLength, + pageIndexs: [prePageIndex, nextPageIndex], + styleString + } = options; + if (this.isExactly || this.isCorrecting) { + return; + } + this.isCorrecting = true; + const cellRoot = targetNode.parentNode; + const heights = []; + cellRoot.childNodes.forEach((child: HTMLElement) => heights.push(child.getBoundingClientRect()[styleString])); + let errorSize = 0; + let firstIndex = this.pages[prePageIndex].startIndex - headerLength - 1; + for (let i = prePageIndex; i <= nextPageIndex; i++) { + const page = this.pages[i]; + const height = heights.slice(page.startIndex - firstIndex, page.endIndex + 1 - firstIndex).reduce((prev, cur) => { + return prev + cur; + }, 0) / SizeAndPositionManager.pixelRatio; + const pageErrorSize = height - page.endPos + page.startPos; + page.startPos = errorSize + page.startPos; + errorSize += pageErrorSize; + page.endPos = errorSize + page.endPos; + } + for (let i = nextPageIndex + 1; i < this.pages.length; i ++) { + this.pages[i].startPos += errorSize; + this.pages[i].endPos += errorSize; + }; + this.totalSize += errorSize; + this.isCorrecting = false; + } + + private calPageInfo(index: number, prevExtraSize: number) { let size = 0; - while(index < this.length && size < this.bufferSize - prevExtraSize) { + while (index < this.length && size < this.bufferSize - prevExtraSize) { size += this.getSize(index); index++; } @@ -79,7 +121,7 @@ class SizeAndPositionManager { if (typeof itemSize === 'number') { return () => { return itemSize; - } + }; } return (i) => { const size = itemSize(i); @@ -88,43 +130,54 @@ class SizeAndPositionManager { return 0; } return size; - } + }; } - calCurrent(scrollDistance: number) { + public calCurrent(scrollDistance: number): RenderInfo|void { + if (this.isCorrecting) { + return; + } const distance = scrollDistance / SizeAndPositionManager.pixelRatio; - const pageIndex = distance === 0 ? 0 : Math.ceil(distance / this.bufferSize) - 1; - const prevPageIndex = Math.max(0, pageIndex - 1); - const nextPageIndex = Math.min(this.pages.length, pageIndex + 1); + const bufferFont = distance - this.bufferSize; + const bufferEnd = distance + this.bufferSize * 2; + + // TODO 二分查找 + let prevPageIndex: number, nextPageIndex: number; + for (let i = 0; i < this.pages.length; i ++) { + if (this.pages[i].endPos > bufferFont) { + prevPageIndex = i; + break; + } + } + if (prevPageIndex === undefined) { + prevPageIndex = 0; + } + for (let i = prevPageIndex; i < this.pages.length; i++) { + if (this.pages[i].endPos >= bufferEnd) { + nextPageIndex = i; + break; + } + } + if (nextPageIndex === undefined) { + nextPageIndex = this.pages.length - 1; + } + const prevPage = this.pages[prevPageIndex]; const nextPage = this.pages[nextPageIndex]; + if (prevPage && nextPage) { return { - startIndex: prevPage.startIndex, - endIndex: nextPage.endIndex, - front: prevPage.startPos, - back: this.totalSize - nextPage.endPos + pageIndexs: [prevPageIndex, nextPageIndex], + placeholderSizes: [prevPage.startPos, this.totalSize - nextPage.endPos], + renderedIndexs: [prevPage.startIndex, nextPage.endIndex] }; } return { - startIndex: 0, - endIndex: this.length, - front: 0, - back: 0 + pageIndexs: [0, 0], + placeholderSizes: [0, 0], + renderedIndexs: [0, this.length - 1] }; } - - setPageSize(index, size) { - const page = this.pages[index]; - if (page) { - const errorSize = page.endPos - page.startPos - size; - page.endPos = page.startPos + size; - for (let i = index + 1; i < this.pages.length; i ++) { - this.pages[i].startPos = this.pages[i].startPos - errorSize; - this.pages[i].endPos = this.pages[i].endPos - errorSize; - } - } - } } export default SizeAndPositionManager; \ No newline at end of file diff --git a/packages/rax-recyclerview/src/VirtualizedList/index.tsx b/packages/rax-recyclerview/src/VirtualizedList/index.tsx index 94f0b238..b762727f 100644 --- a/packages/rax-recyclerview/src/VirtualizedList/index.tsx +++ b/packages/rax-recyclerview/src/VirtualizedList/index.tsx @@ -1,14 +1,15 @@ -import { createElement, forwardRef, useState, useMemo, memo, Fragment, useRef } from 'rax'; +import { createElement, forwardRef, useState, useMemo, memo, Fragment, useRef, useLayoutEffect } from 'rax'; import ScrollView from 'rax-scrollview'; import View from 'rax-view'; import Children from 'rax-children'; +import findDOMNode from 'rax-find-dom-node'; import NoRecycleList from './NoRecycleList'; import throttle from './throttle'; import { VirtualizedList } from './types'; import SizeAndPositionManager from './SizeAndPositionManager'; -import { isWeChatMiniProgram } from '@uni/env'; +import { isWeChatMiniProgram, isWeb } from '@uni/env'; function createArray(length) { if (length > 0) { @@ -21,12 +22,12 @@ function getConstantKey(horizontal: boolean) { if (horizontal) { return { contentOffset: 'x', - placeholderStyle: 'width' + style: 'width' }; } return { contentOffset: 'y', - placeholderStyle: 'height' + style: 'height' }; } @@ -36,7 +37,7 @@ const Cell = memo(({children}) => { Cell.displayName = 'Cell'; const Header = memo(({children, ...rest}) => { - return ({children}); + return ({children}); }); Header.displayName = 'Header'; @@ -51,8 +52,8 @@ NestedList.displayName = 'NestedList'; function getVirtualizedList(SizeAndPositionManager): VirtualizedList { const VirtualizedList: VirtualizedList = forwardRef((props, ref) => { - const { itemSize, horizontal, children, bufferSize, scrollEventThrottle = 50, ...rest } = props; - if (!itemSize) { + const { itemSize, itemEstimateSize, horizontal, children, bufferSize, scrollEventThrottle = 50, ...rest } = props; + if (!itemSize && (!isWeb || !itemEstimateSize)) { return ({children}); } const { @@ -81,40 +82,56 @@ function getVirtualizedList(SizeAndPositionManager): VirtualizedList { }, [children]); const offsetRef = useRef(0); + const preNodeRef = useRef(null); const constantKey = getConstantKey(horizontal); - const [renderedIndex, setRenderedIndex] = useState([0, 0]); - const [placeholderSize, setPlaceholderSize] = useState([0, 0]); + const [renderInfo, setRenderInfo] = useState({ + placeholderSizes: [0, 0], + renderedIndexs: [0, 0], + pageIndexs: [0, 0] + }); - const manager: SizeAndPositionManager = useMemo(() => { + const manager: SizeAndPositionManager = useMemo(() => { const manager: SizeAndPositionManager = new SizeAndPositionManager({ itemSize, horizontal, bufferSize, + itemEstimateSize, length: cellLength, }); - const { - startIndex, - endIndex, - front, - back - } = manager.calCurrent(offsetRef.current); - setRenderedIndex([startIndex, endIndex]); - setPlaceholderSize([front, back]); + const result = manager.calCurrent(offsetRef.current); + if (result) { + const { + renderedIndexs, + placeholderSizes, + pageIndexs + } = result; + setRenderInfo({ + renderedIndexs, + placeholderSizes, + pageIndexs + }); + } + return manager; - }, [itemSize, horizontal, cellLength, bufferSize]); + }, [itemSize, horizontal, cellLength, bufferSize, itemEstimateSize]); function handleScroll(e) { const offset = e.nativeEvent.contentOffset[constantKey.contentOffset]; offsetRef.current = offset; - const { - startIndex, - endIndex, - front, - back - } = manager.calCurrent(offsetRef.current); - setRenderedIndex([startIndex, endIndex]); - setPlaceholderSize([front, back]); + const result = manager.calCurrent(offsetRef.current); + if (result) { + const { + renderedIndexs, + placeholderSizes, + pageIndexs + } = result; + setRenderInfo({ + renderedIndexs, + placeholderSizes, + pageIndexs + }); + } props.onScroll && props.onScroll(e); } @@ -125,6 +142,17 @@ function getVirtualizedList(SizeAndPositionManager): VirtualizedList { const throttleScroll = useMemo(() => throttle((e) => scrollRef.current(e), scrollEventThrottle), [scrollEventThrottle]); + useLayoutEffect(() => { + if (isWeb) { + manager.correctSize({ + targetNode: findDOMNode(preNodeRef.current), + headerLength: headers.length, + pageIndexs: renderInfo.pageIndexs as [number, number], + styleString: constantKey.style + }); + } + }, [renderInfo.pageIndexs[0], renderInfo.pageIndexs[1]]); + if (isWeChatMiniProgram) { return ( {headers} - - {createArray(renderedIndex[0]).map((v, index) => )} - {cells.slice(renderedIndex[0], renderedIndex[1] + 1).map((child, index) => {child})} - {createArray(cellLength - renderedIndex[1] - 1).map((v, index) => )} - + + {createArray(renderInfo.renderedIndexs[0]).map((v, index) => )} + {cells.slice(renderInfo.renderedIndexs[0], renderInfo.renderedIndexs[1] + 1).map((child, index) => {child})} + {createArray(cellLength - renderInfo.renderedIndexs[1] - 1).map((v, index) => )} + ); @@ -156,13 +184,12 @@ function getVirtualizedList(SizeAndPositionManager): VirtualizedList { onScroll={throttleScroll} scroll-anchoring={true} > - {headers} - - {cells.slice(renderedIndex[0], renderedIndex[1] + 1)} - + {headers} + + {cells.slice(renderInfo.renderedIndexs[0], renderInfo.renderedIndexs[1] + 1)} + ); - }); VirtualizedList.Header = Header; diff --git a/packages/rax-recyclerview/src/VirtualizedList/types.ts b/packages/rax-recyclerview/src/VirtualizedList/types.ts index 26e76540..5e940836 100644 --- a/packages/rax-recyclerview/src/VirtualizedList/types.ts +++ b/packages/rax-recyclerview/src/VirtualizedList/types.ts @@ -4,12 +4,13 @@ import { RefAttributes, HTMLAttributes, ForwardRefExoticComponent } from 'rax'; export type TItemSize = number | ((e: number) => number); export interface RecyclerViewRefObject extends ScrollViewProps { - itemSize: TItemSize; + itemSize?: TItemSize; totalSize?: number; bufferSize?: number; horizontal: boolean; bufferRatio?: number; scrollEventThrottle?: number; + itemEstimateSize?: number; } export interface LegacyRefObject extends RefAttributes, HTMLAttributes {} diff --git a/packages/rax-recyclerview/src/index.tsx b/packages/rax-recyclerview/src/index.tsx index 16f6980f..83f19d5e 100644 --- a/packages/rax-recyclerview/src/index.tsx +++ b/packages/rax-recyclerview/src/index.tsx @@ -1,4 +1,4 @@ -import { isWeb, isWeex, isMiniApp, isWeChatMiniProgram, isByteDanceMicroApp, isBaiduSmartProgram, isKuaiShouMiniProgram } from 'universal-env'; +import { isWeb, isWeex, isMiniApp, isWeChatMiniProgram, isByteDanceMicroApp, isBaiduSmartProgram, isKuaiShouMiniProgram } from '@uni/env'; import RecyclerViewWeb from './web'; import RecyclerViewWeex from './weex'; import RecyclerViewMiniProgram from './miniapp-runtime'; From 2067bc6c296e10ba97270f7646c313b6d8f4e765 Mon Sep 17 00:00:00 2001 From: "chenrongyan.cry" Date: Fri, 1 Apr 2022 20:04:25 +0800 Subject: [PATCH 15/16] docs: up readme --- packages/rax-recyclerview/README.md | 101 +++++++++++++++++++--------- 1 file changed, 71 insertions(+), 30 deletions(-) diff --git a/packages/rax-recyclerview/README.md b/packages/rax-recyclerview/README.md index f8308afc..89aa9112 100644 --- a/packages/rax-recyclerview/README.md +++ b/packages/rax-recyclerview/README.md @@ -20,22 +20,23 @@ npm install rax-recyclerview --save 在 rax-recyclerview@2.0.0 及以上版本中,可以使用所有 rax-scrollview 的所有属性,具体文档请看:[rax-scrollview](https://github.com/raxjs/rax-components/blob/master/packages/rax-scrollview/README.md) -| 属性 | 类型 | 默认值 | 必填 | 描述 | 支持 | -| --------------------- | ----------------- | ------ | ---- | ----------------------------------------------- | ------------------------------------------------------------ | -| itemSize | `function/number` | - | × | 单位为`rpx`, 返回每个子元素的高度(节点回收时需要,不需要传递 RecyclerView.Header 元素的高度),若 itemSize 未传,则不进行回收。 如果每个子元素的高度不固定,则可使用函数的方式,例如 itemSize={(index) => {return 100;}} 其中 `index` 为参与回收的子元素的数组下标位置 | browser | -| itemEstimateSize | `number` | - | × | 单位为`rpx`, 当子元素不是固定高度时,可以传入该值作为元素的估计值,本属性只支持 Web | browser | -| horizontal | `boolean` | - | false | 设置为横向滚动 | browser weexminiApp wechatMiniprogrambytedanceMicroApp quickApp| -| onEndReachedThreshold | `string/number` | 500 | ✘ | 设置加载更多的偏移, 推荐使用 string 格式来指指定尺寸单位,如`100rpx` | browser weex miniAppbytedanceMicroApp | -| onEndReached | `function` | - | ✘ | 滚动区域还剩`onEndReachedThreshold`的长度时触发 | browser weex miniApp wechatMiniprogrambytedanceMicroApp | -| onScroll | `function` | - | ✘ | 滚动时触发的事件,返回当前滚动的水平垂直距离 | browser weex miniApp wechatMiniprogrambytedanceMicroApp | -| totalSize | `number` | - | ✘ | 当前列表总高度(在 cell 高度可变的列表中需要传) | browser | -| onTouchStart | `function` | - | false | touchStart触发的事件,返回触摸点数据(touches、changedTouches) | browser weexminiApp | -| onTouchMove | `function` | - | false | touchMove触发的事件,返回触摸点数据(touches、changedTouches) | browser weexminiApp | -| onTouchEnd | `function` | - | false | touchEnd触发的事件,返回触摸点数据(touches、changedTouches) | browser weexminiApp | -| onTouchCancel | `function` | - | false | touchCancel触发的事件,返回触摸点数据(touches、changedTouches) | miniApp | -| disableScroll | `boolean` | - | false | 是否禁止滚动,是否禁止滚动, rax-recyclerview@1.3.4 及以上版本支 | browser miniApp | +| 属性 | 类型 | 默认值 | 必填 | 描述 | 支持 | +| --------------------- | ----------------- | ------ | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| itemSize | `function/number` | - | × | 单位为`rpx`, 返回每个子元素的高度(节点回收时需要,不需要传递 RecyclerView.Header 元素的高度),若 itemSize 未传,则不进行回收。 如果每个子元素的高度不固定,则可使用函数的方式,例如 itemSize={(index) => {return 100;}} 其中 `index` 为参与回收的子元素的数组下标位置 | browser | +| itemEstimateSize | `number` | - | × | 单位为`rpx`, 当子元素不是固定高度时,可以传入该值作为元素的估计值,本属性只支持 Web | browser | +| horizontal | `boolean` | - | false | 设置为横向滚动 | browser weexminiApp wechatMiniprogrambytedanceMicroApp quickApp | +| onEndReachedThreshold | `string/number` | 500 | ✘ | 设置加载更多的偏移, 推荐使用 string 格式来指指定尺寸单位,如`100rpx` | browser weex miniAppbytedanceMicroApp | +| onEndReached | `function` | - | ✘ | 滚动区域还剩`onEndReachedThreshold`的长度时触发 | browser weex miniApp wechatMiniprogrambytedanceMicroApp | +| onScroll | `function` | - | ✘ | 滚动时触发的事件,返回当前滚动的水平垂直距离 | browser weex miniApp wechatMiniprogrambytedanceMicroApp | +| totalSize | `number` | - | ✘ | 当前列表总高度(在 cell 高度可变的列表中需要传) | browser | +| onTouchStart | `function` | - | false | touchStart 触发的事件,返回触摸点数据(touches、changedTouches) | browser weexminiApp | +| onTouchMove | `function` | - | false | touchMove 触发的事件,返回触摸点数据(touches、changedTouches) | browser weexminiApp | +| onTouchEnd | `function` | - | false | touchEnd 触发的事件,返回触摸点数据(touches、changedTouches) | browser weexminiApp | +| onTouchCancel | `function` | - | false | touchCancel 触发的事件,返回触摸点数据(touches、changedTouches) | miniApp | +| disableScroll | `boolean` | - | false | 是否禁止滚动,是否禁止滚动, rax-recyclerview@1.3.4 及以上版本支 | browser miniApp | ## 子组件 + ### RecyclerView.Header 头部子元素需要用 `RecycerView.Header` 包裹,头部元素**不参与**元素回收。 @@ -45,17 +46,58 @@ npm install rax-recyclerview --save 除了头部元素之外的子元素可以被 `RecyclerView.Cell` 包裹,在 Weex 中该组件为 Weex 的 `cell` 组件,在 Web 和小程序中该组件是 `Fragment` 空节点。该节点没有实际意义,所以不要在该组件上设置样式和绑定事件。如果在 Web 和 小程序中使用,不需要包裹 `RecyclerView.Cell`。 ## 方法 + ### scrollTo({x:number|string,y:number|string}) #### 参数 参数为 `object`,包含以下属性 -| **属性** | **类型** | **默认值** | **必填** | **描述** | -| -------- | -------- | ---------- | -------- | ------------ | +| **属性** | **类型** | **默认值** | **必填** | **描述** | +| -------- | --------------- | ---------- | -------- | ------------------------------------------------ | | x | `number/string` | - | ✘ | 横向的偏移量, 推荐使用 string 格式来指定尺寸单位 | | y | `number/string` | - | ✘ | 纵向的偏移量, 推荐使用 string 格式来指定尺寸单位 | +## 优化 + +无论是在 web 还是在小程序中,节点的数量会影响页面的渲染性能,这也是 `rax-recyclerview` 组件通过回收视图外的节点来做到优化性能的原因。 + +但是需要注意的是,`rax-recyclerview` 并不是万能的,相反,由于已经渲染的组件离开视图外之后会被回收,因此当其重新进入视图的时候需要再次渲染。如果组件中的节点比较复杂,也会再次导致渲染性能问题。 + +特别在运行时小程序中,由于其原理性问题,这种情况会更加突出。 + +这里介绍几种有效提升性能的方法: + +1. 请务必减少被回收区块的节点数量和层级,并避免使用内联样式等任何增加节点信息的用法。 + +```jsx +// 修改前 + + + + 名称: + {name} + + + +``` + +例如以上片段,减少层级和数量之后: + +```jsx +// 修改后 + + 名称:{name} + +``` + +2. 减少 `rax-recyclerview` 组件所在的层级,在小程序中,`setData` 的 `path` 层级会影响到渲染效率,因此也应当减少组件所在的层级。 + +3. 可以适当把可回收组件修改为编译时组件,方法可查看:[使用编译时组件](https://rax.alibaba-inc.com/docs/guide/use-miniapp-compile-components)。本方法不适用于: + +- 回收组件中使用了运行时组件的,例如 `fusion mobile`; +- 回收组件接受的数据量较大; + ### 示例 ```jsx @@ -69,7 +111,7 @@ import DriverUniversal from "driver-universal"; import RecyclerView from "rax-recyclerview"; -function Thumb({val}) { +function Thumb({ val }) { return ( @@ -91,7 +133,9 @@ function App() { ref={viewRef} style={styles.container} itemSize={88} - onScroll={(e) => {console.log(e.nativeEvent.contentOffset.y)}} + onScroll={(e) => { + console.log(e.nativeEvent.contentOffset.y); + }} > Sticky view is not header @@ -115,22 +159,22 @@ function App() { let styles = { root: { - display: 'block' + display: "block", }, sticky: { - position: 'sticky', + position: "sticky", width: 750, top: 0, - backgroundColor: '#cccccc' + backgroundColor: "#cccccc", }, container: { - height: '100vh' + height: "100vh", }, button: { margin: 7, padding: 5, - alignItems: 'center', - backgroundColor: '#eaeaea', + alignItems: "center", + backgroundColor: "#eaeaea", borderRadius: 3, }, box: { @@ -138,16 +182,13 @@ let styles = { height: 64, }, fixButton: { - position: 'fixed', + position: "fixed", bottom: 20, right: 20, border: 1, - backgroundColor: '#fff' - } + backgroundColor: "#fff", + }, }; render(, document.body, { driver: DriverUniversal }); - ``` - - From ef20c1382fb0af90d3ff10ba9122892ab65c58e6 Mon Sep 17 00:00:00 2001 From: "chenrongyan.cry" Date: Wed, 11 May 2022 16:21:38 +0800 Subject: [PATCH 16/16] chore: up docs --- packages/rax-recyclerview/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/rax-recyclerview/README.md b/packages/rax-recyclerview/README.md index 89aa9112..218eaff9 100644 --- a/packages/rax-recyclerview/README.md +++ b/packages/rax-recyclerview/README.md @@ -22,13 +22,13 @@ npm install rax-recyclerview --save | 属性 | 类型 | 默认值 | 必填 | 描述 | 支持 | | --------------------- | ----------------- | ------ | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| itemSize | `function/number` | - | × | 单位为`rpx`, 返回每个子元素的高度(节点回收时需要,不需要传递 RecyclerView.Header 元素的高度),若 itemSize 未传,则不进行回收。 如果每个子元素的高度不固定,则可使用函数的方式,例如 itemSize={(index) => {return 100;}} 其中 `index` 为参与回收的子元素的数组下标位置 | browser | +| itemSize | `function/number` | - | × | 单位为`rpx`, 返回每个子元素的高度(节点回收时需要,不需要传递 RecyclerView.Header 元素的高度),若 itemSize 未传,则不进行回收。 如果每个子元素的高度不固定,则可使用函数的方式,例如 itemSize={(index) => {return 100;}} 其中 `index` 为参与回收的子元素的数组下标位置 | browserminiApp wechatMiniprogrambytedanceMicroApp | | itemEstimateSize | `number` | - | × | 单位为`rpx`, 当子元素不是固定高度时,可以传入该值作为元素的估计值,本属性只支持 Web | browser | | horizontal | `boolean` | - | false | 设置为横向滚动 | browser weexminiApp wechatMiniprogrambytedanceMicroApp quickApp | | onEndReachedThreshold | `string/number` | 500 | ✘ | 设置加载更多的偏移, 推荐使用 string 格式来指指定尺寸单位,如`100rpx` | browser weex miniAppbytedanceMicroApp | | onEndReached | `function` | - | ✘ | 滚动区域还剩`onEndReachedThreshold`的长度时触发 | browser weex miniApp wechatMiniprogrambytedanceMicroApp | | onScroll | `function` | - | ✘ | 滚动时触发的事件,返回当前滚动的水平垂直距离 | browser weex miniApp wechatMiniprogrambytedanceMicroApp | -| totalSize | `number` | - | ✘ | 当前列表总高度(在 cell 高度可变的列表中需要传) | browser | +| bufferSize | `number` | - | ✘ | 缓冲区单位尺寸,单位为 rpx,默认为当前视图的尺寸(水如水平方向滚动时,值为 750), recyclerview 会默认渲染 3 屏缓冲区尺寸。注意在小程序中,慎用该属性! | browserminiApp wechatMiniprogrambytedanceMicroApp | | onTouchStart | `function` | - | false | touchStart 触发的事件,返回触摸点数据(touches、changedTouches) | browser weexminiApp | | onTouchMove | `function` | - | false | touchMove 触发的事件,返回触摸点数据(touches、changedTouches) | browser weexminiApp | | onTouchEnd | `function` | - | false | touchEnd 触发的事件,返回触摸点数据(touches、changedTouches) | browser weexminiApp |