Showing preview only (4,929K chars total). Download the full file or copy to clipboard to get everything.
Repository: thundersdata-frontend/td-design
Branch: master
Commit: f803d835ae93
Files: 998
Total size: 4.4 MB
Directory structure:
gitextract_oitcubhd/
├── .changeset/
│ ├── README.md
│ └── config.json
├── .commitlintrc.js
├── .cz-config.js
├── .editorconfig
├── .github/
│ └── workflows/
│ ├── changeset-version.yml
│ ├── gh-pages.yml
│ └── issue-close-require.yml
├── .gitignore
├── .husky/
│ ├── .gitignore
│ ├── commit-msg
│ └── pre-commit
├── .npmrc
├── .prettierignore
├── .prettierrc
├── .umirc.ts
├── .vscode/
│ └── settings.json
├── LICENSE
├── README.md
├── docs/
│ ├── development.md
│ ├── friendlink.md
│ ├── index.md
│ ├── pattern/
│ │ ├── Strategy.md
│ │ ├── index.md
│ │ ├── signleton.md
│ │ └── visitor.md
│ ├── react-faq.md
│ ├── react-native/
│ │ ├── index.md
│ │ ├── restyle.md
│ │ └── theme.md
│ ├── react-perf.md
│ ├── rn-faq.md
│ ├── screen.md
│ └── structure/
│ ├── array.md
│ ├── index.md
│ └── linkedList.md
├── jest.config.js
├── jest.setup.js
├── package.json
├── packages/
│ ├── cli/
│ │ ├── README.md
│ │ ├── bin/
│ │ │ └── index.js
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── setupApp.ts
│ │ │ ├── setupSpa.ts
│ │ │ └── utils/
│ │ │ ├── deleteOldDirs.ts
│ │ │ ├── replaceProject.ts
│ │ │ ├── translateFilePath.ts
│ │ │ └── walk.ts
│ │ ├── tsconfig.build.json
│ │ └── types.d.ts
│ ├── eslint-plugin-replace-hooks/
│ │ ├── .eslintrc.js
│ │ ├── README.md
│ │ ├── docs/
│ │ │ └── rules/
│ │ │ └── no-forbidden-hooks.md
│ │ ├── gulpfile.js
│ │ ├── package.json
│ │ └── src/
│ │ ├── index.js
│ │ └── rules/
│ │ └── no-forbidden-hooks.js
│ ├── hooks/
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── rollup.config.js
│ │ ├── src/
│ │ │ ├── createUpdateEffect/
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── index.ts
│ │ │ ├── useAccessibilityInfo/
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useAppState/
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useAsyncEffect/
│ │ │ │ ├── demo/
│ │ │ │ │ └── demo1.tsx
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useBackHandler/
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useBoolean/
│ │ │ │ ├── demo/
│ │ │ │ │ └── demo1.tsx
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useControllableValue/
│ │ │ │ ├── demo/
│ │ │ │ │ ├── demo1.tsx
│ │ │ │ │ ├── demo2.tsx
│ │ │ │ │ └── demo3.tsx
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useCountdown/
│ │ │ │ └── index.ts
│ │ │ ├── useCounter/
│ │ │ │ ├── demo/
│ │ │ │ │ └── demo1.tsx
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useCreation/
│ │ │ │ ├── demo/
│ │ │ │ │ └── demo1.tsx
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useDebounce/
│ │ │ │ ├── DebounceOptions.ts
│ │ │ │ ├── demo/
│ │ │ │ │ └── demo1.tsx
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useDebounceEffect/
│ │ │ │ ├── demo/
│ │ │ │ │ └── demo1.tsx
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useDebounceFn/
│ │ │ │ ├── demo/
│ │ │ │ │ └── demo1.tsx
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useDeepCompareEffect/
│ │ │ │ ├── demo/
│ │ │ │ │ └── demo1.tsx
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useDeviceOrientation/
│ │ │ │ └── index.ts
│ │ │ ├── useDimensions/
│ │ │ │ └── index.ts
│ │ │ ├── useDynamicList/
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useEventEmitter/
│ │ │ │ └── index.ts
│ │ │ ├── useGetState/
│ │ │ │ ├── __tests__/
│ │ │ │ │ └── index.test.ts
│ │ │ │ ├── demo/
│ │ │ │ │ └── demo1.tsx
│ │ │ │ └── index.ts
│ │ │ ├── useHistoryTravel/
│ │ │ │ └── index.ts
│ │ │ ├── useInfiniteScroll/
│ │ │ │ ├── __tests__/
│ │ │ │ │ └── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useInterval/
│ │ │ │ ├── demo/
│ │ │ │ │ ├── demo1.tsx
│ │ │ │ │ ├── demo2.tsx
│ │ │ │ │ └── demo3.tsx
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useKeyboard/
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useLatest/
│ │ │ │ ├── demo/
│ │ │ │ │ └── demo1.tsx
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useLayout/
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useLockFn/
│ │ │ │ ├── demo/
│ │ │ │ │ └── demo1.tsx
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useMap/
│ │ │ │ ├── demo/
│ │ │ │ │ └── demo1.tsx
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useMemoizedFn/
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useMount/
│ │ │ │ ├── demo/
│ │ │ │ │ └── demo1.tsx
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── usePagination/
│ │ │ │ ├── index.test.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── testingHelper.ts
│ │ │ │ └── types.ts
│ │ │ ├── usePrevious/
│ │ │ │ ├── demo/
│ │ │ │ │ ├── demo1.tsx
│ │ │ │ │ └── demo2.tsx
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useRafInterval/
│ │ │ │ ├── demo/
│ │ │ │ │ ├── demo1.tsx
│ │ │ │ │ └── demo2.tsx
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useRafState/
│ │ │ │ ├── demo/
│ │ │ │ │ └── demo1.tsx
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useRafTimeout/
│ │ │ │ ├── demo/
│ │ │ │ │ ├── demo1.tsx
│ │ │ │ │ └── demo2.tsx
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useRequest/
│ │ │ │ ├── Fetch.ts
│ │ │ │ ├── index.test.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── plugins/
│ │ │ │ │ ├── useAutoRunPlugin.ts
│ │ │ │ │ ├── useCachePlugin.ts
│ │ │ │ │ ├── useDebouncePlugin.ts
│ │ │ │ │ ├── useLoadingDelayPlugin.ts
│ │ │ │ │ ├── usePollingPlugin.ts
│ │ │ │ │ ├── useRetryPlugin.ts
│ │ │ │ │ └── useThrottlePlugin.ts
│ │ │ │ ├── types.ts
│ │ │ │ ├── useRequestImpl.ts
│ │ │ │ └── utils/
│ │ │ │ ├── cache.ts
│ │ │ │ ├── cachePromise.ts
│ │ │ │ ├── cacheSubscribe.ts
│ │ │ │ └── testingHelper.ts
│ │ │ ├── useResetState/
│ │ │ │ ├── __tests__/
│ │ │ │ │ └── index.test.ts
│ │ │ │ ├── demo/
│ │ │ │ │ └── demo1.tsx
│ │ │ │ └── index.ts
│ │ │ ├── useSafeState/
│ │ │ │ ├── demo/
│ │ │ │ │ └── demo1.tsx
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useSet/
│ │ │ │ ├── demo/
│ │ │ │ │ └── demo1.tsx
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useSetState/
│ │ │ │ └── index.ts
│ │ │ ├── useSms/
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useThrottle/
│ │ │ │ ├── ThrottleOptions.ts
│ │ │ │ ├── demo/
│ │ │ │ │ └── demo1.tsx
│ │ │ │ └── index.ts
│ │ │ ├── useThrottleEffect/
│ │ │ │ ├── demo/
│ │ │ │ │ └── demo1.tsx
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useThrottleFn/
│ │ │ │ ├── demo/
│ │ │ │ │ └── demo1.tsx
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useTimeout/
│ │ │ │ ├── demo/
│ │ │ │ │ └── demo1.tsx
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useToggle/
│ │ │ │ ├── demo/
│ │ │ │ │ ├── demo1.tsx
│ │ │ │ │ └── demo2.tsx
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useTrackedEffect/
│ │ │ │ └── index.ts
│ │ │ ├── useUnmount/
│ │ │ │ ├── demo/
│ │ │ │ │ └── demo1.tsx
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useUnmountedRef/
│ │ │ │ ├── demo/
│ │ │ │ │ └── demo1.tsx
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useUpdate/
│ │ │ │ ├── demo/
│ │ │ │ │ └── demo1.tsx
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ ├── useUpdateEffect/
│ │ │ │ └── index.ts
│ │ │ ├── useUpdateLayoutEffect/
│ │ │ │ └── index.ts
│ │ │ ├── useWhyDidYouUpdate/
│ │ │ │ ├── demo/
│ │ │ │ │ └── demo1.tsx
│ │ │ │ ├── index.test.ts
│ │ │ │ └── index.ts
│ │ │ └── utils/
│ │ │ ├── index.ts
│ │ │ ├── platform.ts
│ │ │ └── testHelpers.ts
│ │ └── tsconfig.json
│ ├── lego/
│ │ ├── CHANGELOG.md
│ │ ├── example/
│ │ │ ├── AccumulatedDataDemo/
│ │ │ │ ├── demo1.tsx
│ │ │ │ └── demo2.tsx
│ │ │ ├── BarLineDemo/
│ │ │ │ ├── demo1.tsx
│ │ │ │ ├── demo10.tsx
│ │ │ │ ├── demo11.tsx
│ │ │ │ ├── demo12.tsx
│ │ │ │ ├── demo2.tsx
│ │ │ │ ├── demo3.tsx
│ │ │ │ ├── demo4.tsx
│ │ │ │ ├── demo5.tsx
│ │ │ │ ├── demo6.tsx
│ │ │ │ ├── demo7.tsx
│ │ │ │ ├── demo8.tsx
│ │ │ │ └── demo9.tsx
│ │ │ ├── BasePieDemo/
│ │ │ │ ├── demo1.tsx
│ │ │ │ ├── demo2.tsx
│ │ │ │ ├── demo3.tsx
│ │ │ │ ├── demo4.tsx
│ │ │ │ ├── demo5.tsx
│ │ │ │ ├── demo6.tsx
│ │ │ │ └── demo7.tsx
│ │ │ ├── CircularSolidPieDemo/
│ │ │ │ ├── demo1.tsx
│ │ │ │ ├── demo2.tsx
│ │ │ │ ├── demo3.tsx
│ │ │ │ └── demo4.tsx
│ │ │ ├── CuboidBarDemo/
│ │ │ │ ├── demo1.tsx
│ │ │ │ ├── demo2.tsx
│ │ │ │ ├── demo3.tsx
│ │ │ │ ├── demo4.tsx
│ │ │ │ ├── demo5.tsx
│ │ │ │ ├── demo6.tsx
│ │ │ │ ├── demo7.tsx
│ │ │ │ └── demo8.tsx
│ │ │ ├── CylinderBarDemo/
│ │ │ │ ├── demo1.tsx
│ │ │ │ ├── demo2.tsx
│ │ │ │ ├── demo3.tsx
│ │ │ │ ├── demo4.tsx
│ │ │ │ ├── demo5.tsx
│ │ │ │ ├── demo6.tsx
│ │ │ │ └── demo7.tsx
│ │ │ ├── CylinderShadowBarDemo/
│ │ │ │ ├── demo1.tsx
│ │ │ │ ├── demo2.tsx
│ │ │ │ ├── demo3.tsx
│ │ │ │ ├── demo4.tsx
│ │ │ │ ├── demo5.tsx
│ │ │ │ ├── demo6.tsx
│ │ │ │ ├── demo7.tsx
│ │ │ │ └── demo8.tsx
│ │ │ ├── DataShowDemo/
│ │ │ │ └── demo1.tsx
│ │ │ ├── DataShowSimpleDemo/
│ │ │ │ └── demo1.tsx
│ │ │ ├── FlipNumberDemo/
│ │ │ │ ├── demo1.tsx
│ │ │ │ ├── demo2.tsx
│ │ │ │ ├── demo3.tsx
│ │ │ │ └── demo4.tsx
│ │ │ ├── FloatBallDemo/
│ │ │ │ ├── demo1.tsx
│ │ │ │ ├── demo2.tsx
│ │ │ │ ├── demo3.tsx
│ │ │ │ └── demo4.tsx
│ │ │ ├── GaugeDemo/
│ │ │ │ └── demo1.tsx
│ │ │ ├── HorizontalBarDemo/
│ │ │ │ ├── demo1.tsx
│ │ │ │ ├── demo2.tsx
│ │ │ │ ├── demo3.tsx
│ │ │ │ ├── demo4.tsx
│ │ │ │ ├── demo5.tsx
│ │ │ │ ├── demo6.tsx
│ │ │ │ └── demo7.tsx
│ │ │ ├── ImgLineDemo/
│ │ │ │ ├── demo1.tsx
│ │ │ │ ├── demo2.tsx
│ │ │ │ ├── demo3.tsx
│ │ │ │ ├── demo4.tsx
│ │ │ │ ├── demo5.tsx
│ │ │ │ └── demo6.tsx
│ │ │ ├── ImgPieDemo/
│ │ │ │ ├── demo1.tsx
│ │ │ │ ├── demo2.tsx
│ │ │ │ └── demo3.tsx
│ │ │ ├── ImgRosePieDemo/
│ │ │ │ ├── demo1.tsx
│ │ │ │ └── demo2.tsx
│ │ │ ├── ModalDemo/
│ │ │ │ └── demo1.tsx
│ │ │ ├── MultiHorizontalBarDemo/
│ │ │ │ ├── demo1.tsx
│ │ │ │ ├── demo2.tsx
│ │ │ │ ├── demo3.tsx
│ │ │ │ └── demo4.tsx
│ │ │ ├── MultiLineDemo/
│ │ │ │ ├── demo1.tsx
│ │ │ │ ├── demo10.tsx
│ │ │ │ ├── demo11.tsx
│ │ │ │ ├── demo2.tsx
│ │ │ │ ├── demo3.tsx
│ │ │ │ ├── demo4.tsx
│ │ │ │ ├── demo5.tsx
│ │ │ │ ├── demo6.tsx
│ │ │ │ ├── demo7.tsx
│ │ │ │ ├── demo8.tsx
│ │ │ │ └── demo9.tsx
│ │ │ ├── PictorialBarDemo/
│ │ │ │ ├── demo1.tsx
│ │ │ │ ├── demo2.tsx
│ │ │ │ ├── demo3.tsx
│ │ │ │ ├── demo4.tsx
│ │ │ │ ├── demo5.tsx
│ │ │ │ ├── demo6.tsx
│ │ │ │ ├── demo7.tsx
│ │ │ │ └── demo8.tsx
│ │ │ ├── ProgressDemo/
│ │ │ │ ├── demo1.tsx
│ │ │ │ ├── demo2.tsx
│ │ │ │ └── demo3.tsx
│ │ │ ├── RadarDemo/
│ │ │ │ ├── demo1.tsx
│ │ │ │ ├── demo2.tsx
│ │ │ │ └── demo3.tsx
│ │ │ ├── ScatterDemo/
│ │ │ │ ├── demo1.tsx
│ │ │ │ ├── demo2.tsx
│ │ │ │ ├── demo3.tsx
│ │ │ │ ├── demo4.tsx
│ │ │ │ ├── demo5.tsx
│ │ │ │ └── demo6.tsx
│ │ │ ├── ScrollNumberDemo/
│ │ │ │ ├── demo1.tsx
│ │ │ │ ├── demo2.tsx
│ │ │ │ └── demo3.tsx
│ │ │ ├── SliceBarDemo/
│ │ │ │ ├── demo1.tsx
│ │ │ │ ├── demo2.tsx
│ │ │ │ ├── demo3.tsx
│ │ │ │ ├── demo4.tsx
│ │ │ │ ├── demo5.tsx
│ │ │ │ ├── demo6.tsx
│ │ │ │ └── demo7.tsx
│ │ │ ├── StackBarDemo/
│ │ │ │ ├── demo1.tsx
│ │ │ │ ├── demo2.tsx
│ │ │ │ ├── demo3.tsx
│ │ │ │ ├── demo4.tsx
│ │ │ │ ├── demo5.tsx
│ │ │ │ ├── demo6.tsx
│ │ │ │ ├── demo7.tsx
│ │ │ │ └── demo8.tsx
│ │ │ ├── SwiperDemo/
│ │ │ │ ├── demo1.tsx
│ │ │ │ ├── demo2.tsx
│ │ │ │ └── demo3.tsx
│ │ │ ├── TableDemo/
│ │ │ │ ├── demo1.tsx
│ │ │ │ ├── demo2.tsx
│ │ │ │ ├── demo3.tsx
│ │ │ │ ├── demo4.tsx
│ │ │ │ ├── demo5.tsx
│ │ │ │ ├── demo6.tsx
│ │ │ │ └── index.less
│ │ │ ├── TextScrollDemo/
│ │ │ │ ├── demo1.tsx
│ │ │ │ ├── demo2.tsx
│ │ │ │ └── demo3.tsx
│ │ │ ├── ThreeDimensionalPieDemo/
│ │ │ │ ├── demo1.tsx
│ │ │ │ ├── demo2.tsx
│ │ │ │ └── demo3.tsx
│ │ │ └── WordCloudDemo/
│ │ │ ├── demo1.tsx
│ │ │ └── demo2.tsx
│ │ ├── package.json
│ │ ├── rollup.config.js
│ │ ├── src/
│ │ │ ├── accumulated-data/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── bar-line/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── base-pie/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── circular-solid-pie/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── context/
│ │ │ │ └── ThemeContext.tsx
│ │ │ ├── cuboid-bar/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── cylinder-bar/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── cylinder-shadow-bar/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── data-show/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── data-show-simple/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── flip-number/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── float-ball/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── gauge/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── hooks/
│ │ │ │ ├── useBaseBarConfig.ts
│ │ │ │ ├── useBaseChartConfig.ts
│ │ │ │ ├── useBaseLineConfig.ts
│ │ │ │ ├── useBasePieConfig.ts
│ │ │ │ ├── useChartLoop.ts
│ │ │ │ ├── useEchartsRef.ts
│ │ │ │ ├── useNodeBoundingRect.ts
│ │ │ │ ├── useRAF.ts
│ │ │ │ ├── useStyle.ts
│ │ │ │ └── useTheme.ts
│ │ │ ├── horizontal-bar/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── img-line/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── img-pie/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── img-rose-pie/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── index.ts
│ │ │ ├── modal/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── multi-horizontal-bar/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── multi-line/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── pictorial-bar/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── progress/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── radar/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── registerShape.ts
│ │ │ ├── scatter/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── scroll-number/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── slice-bar/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── stack-bar/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── style/
│ │ │ │ ├── font/
│ │ │ │ │ └── pmzd.otf
│ │ │ │ └── index.less
│ │ │ ├── swiper/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── table/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── text-scroll/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── theme.ts
│ │ │ ├── three-dimensional-pie/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── utils/
│ │ │ │ ├── RAF.ts
│ │ │ │ ├── createCuboidSeries.ts
│ │ │ │ ├── createCylinderSeries.ts
│ │ │ │ ├── createCylinderShadowSeries.ts
│ │ │ │ ├── createLinearGradient.ts
│ │ │ │ ├── createSliceSeries.ts
│ │ │ │ └── createStackSeries.ts
│ │ │ └── word-cloud/
│ │ │ ├── index.md
│ │ │ └── index.tsx
│ │ ├── tsconfig.json
│ │ └── typings.d.ts
│ ├── lego-map/
│ │ ├── CHANGELOG.md
│ │ ├── example/
│ │ │ ├── BasicMapDemo/
│ │ │ │ ├── demo1.tsx
│ │ │ │ ├── demo2.tsx
│ │ │ │ ├── demo3.tsx
│ │ │ │ ├── demo4.tsx
│ │ │ │ ├── demo5.tsx
│ │ │ │ ├── demo6.tsx
│ │ │ │ ├── demo7.tsx
│ │ │ │ └── sichuan.json
│ │ │ ├── DrillMapDemo/
│ │ │ │ ├── demo1.tsx
│ │ │ │ ├── demo2.tsx
│ │ │ │ ├── demo3.tsx
│ │ │ │ ├── demo4.tsx
│ │ │ │ └── demo5.tsx
│ │ │ └── SimpleMapDemo/
│ │ │ ├── demo1.tsx
│ │ │ ├── demo2.tsx
│ │ │ ├── demo3.tsx
│ │ │ ├── demo4.tsx
│ │ │ ├── demo5.tsx
│ │ │ ├── demo6.tsx
│ │ │ └── sichuan.json
│ │ ├── package.json
│ │ ├── rollup.config.js
│ │ ├── src/
│ │ │ ├── assets/
│ │ │ │ ├── bgImage.js
│ │ │ │ └── china.js
│ │ │ ├── basic/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── drill/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── index.ts
│ │ │ ├── simple/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ └── utils/
│ │ │ ├── baseSeries.ts
│ │ │ ├── constant.ts
│ │ │ └── index.ts
│ │ ├── tsconfig.json
│ │ └── typings.d.ts
│ ├── lego-video/
│ │ ├── CHANGELOG.md
│ │ ├── example/
│ │ │ └── VideoDemo/
│ │ │ ├── demo1.tsx
│ │ │ ├── demo2.tsx
│ │ │ ├── demo3.tsx
│ │ │ ├── demo4.tsx
│ │ │ ├── demo5.tsx
│ │ │ ├── demo6.tsx
│ │ │ ├── demo7.tsx
│ │ │ ├── demo8.tsx
│ │ │ └── demo9.tsx
│ │ ├── package.json
│ │ ├── rollup.config.js
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── video/
│ │ │ ├── index.md
│ │ │ └── index.tsx
│ │ └── tsconfig.json
│ ├── react-native/
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── accordion/
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ ├── type.ts
│ │ │ │ └── useAccordion.ts
│ │ │ ├── action-sheet/
│ │ │ │ ├── ActionSheetItem.tsx
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── avatar/
│ │ │ │ ├── Accessory/
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── Avatar/
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── useAvatar.ts
│ │ │ │ ├── AvatarGroup/
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ └── type.ts
│ │ │ ├── badge/
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ └── useBadge.ts
│ │ │ ├── box/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── box-shadow/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── brief/
│ │ │ │ └── index.tsx
│ │ │ ├── button/
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ └── useButton.ts
│ │ │ ├── button-group/
│ │ │ │ ├── Item.tsx
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── card/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── carousel/
│ │ │ │ ├── Bullets.tsx
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ ├── type.ts
│ │ │ │ └── useCarousel.ts
│ │ │ ├── center/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── checkbox/
│ │ │ │ ├── CheckboxItem.tsx
│ │ │ │ ├── CheckboxList.tsx
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ ├── type.ts
│ │ │ │ └── useCheckbox.ts
│ │ │ ├── collapse-text/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── count-down/
│ │ │ │ ├── CountDownItem.tsx
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ └── type.ts
│ │ │ ├── divider/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── empty/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── error-block/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── flex/
│ │ │ │ ├── FlexItem.tsx
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── float-button/
│ │ │ │ ├── PlusIcon.tsx
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── flow/
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ └── step.tsx
│ │ │ ├── form/
│ │ │ │ ├── FormItem.tsx
│ │ │ │ ├── FormListItem.tsx
│ │ │ │ ├── context.ts
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ └── type.ts
│ │ │ ├── helpers/
│ │ │ │ ├── index.ts
│ │ │ │ ├── normalize.ts
│ │ │ │ └── renderNode.tsx
│ │ │ ├── image/
│ │ │ │ └── index.md
│ │ │ ├── image-header/
│ │ │ │ ├── AnimateHeader.tsx
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── index.ts
│ │ │ ├── indicator/
│ │ │ │ ├── BallIndicator.tsx
│ │ │ │ ├── Indicator.tsx
│ │ │ │ ├── MaterialIndicator.tsx
│ │ │ │ ├── UIActivityIndicator.tsx
│ │ │ │ ├── index.md
│ │ │ │ ├── index.ts
│ │ │ │ └── type.ts
│ │ │ ├── input/
│ │ │ │ ├── InputItem.tsx
│ │ │ │ ├── TextArea.tsx
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ ├── useInput.ts
│ │ │ │ ├── useInputItem.ts
│ │ │ │ └── useTextArea.ts
│ │ │ ├── label/
│ │ │ │ └── index.tsx
│ │ │ ├── link/
│ │ │ │ ├── index.md
│ │ │ │ └── index.ts
│ │ │ ├── list/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── list-item/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── menu/
│ │ │ │ ├── Chevron.tsx
│ │ │ │ ├── MenuGroup.tsx
│ │ │ │ ├── MenuItem.tsx
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ ├── type.ts
│ │ │ │ ├── useGroup.ts
│ │ │ │ └── useMenu.ts
│ │ │ ├── modal/
│ │ │ │ ├── Modal/
│ │ │ │ │ ├── ModalView.tsx
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── useModal.ts
│ │ │ │ ├── alert/
│ │ │ │ │ ├── AlertContainer.tsx
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── confirm/
│ │ │ │ │ ├── ConfirmContainer.tsx
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── useConfirm.ts
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ ├── prompt/
│ │ │ │ │ ├── PromptContainer.tsx
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── usePrompt.ts
│ │ │ │ ├── show/
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── tip/
│ │ │ │ │ ├── TipContainer.tsx
│ │ │ │ │ └── index.tsx
│ │ │ │ └── type.ts
│ │ │ ├── notice-bar/
│ │ │ │ ├── AnimatedNotice.tsx
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ └── type.ts
│ │ │ ├── notify/
│ │ │ │ ├── NotifyRoot.tsx
│ │ │ │ ├── constant.ts
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ ├── type.ts
│ │ │ │ └── useNotify.ts
│ │ │ ├── number-keyboard/
│ │ │ │ ├── NumberKeyboardInput.tsx
│ │ │ │ ├── NumberKeyboardItem.tsx
│ │ │ │ ├── NumberKeyboardModal.tsx
│ │ │ │ ├── NumberKeyboardView.tsx
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ ├── type.ts
│ │ │ │ ├── useNumberKeyboard.ts
│ │ │ │ ├── useNumberKeyboardModal.ts
│ │ │ │ └── util.ts
│ │ │ ├── pagination/
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ └── usePagination.ts
│ │ │ ├── passcode/
│ │ │ │ ├── PasscodeItem.tsx
│ │ │ │ ├── helpers.ts
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ ├── reducer.ts
│ │ │ │ ├── type.ts
│ │ │ │ └── usePasscode.ts
│ │ │ ├── portal/
│ │ │ │ ├── PortalConsumer.tsx
│ │ │ │ ├── PortalContext.ts
│ │ │ │ ├── PortalGuard.ts
│ │ │ │ ├── PortalHost.tsx
│ │ │ │ ├── PortalManager.tsx
│ │ │ │ └── index.tsx
│ │ │ ├── pressable/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── progress/
│ │ │ │ ├── CircleProgress.tsx
│ │ │ │ ├── LineProgress.tsx
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ ├── type.ts
│ │ │ │ ├── useCircleProgress.ts
│ │ │ │ └── useLineProgress.ts
│ │ │ ├── radio/
│ │ │ │ ├── RadioItem.tsx
│ │ │ │ ├── RadioList.tsx
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ ├── type.ts
│ │ │ │ └── useRadio.ts
│ │ │ ├── result/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── scroll-number/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── search-bar/
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ └── useSearchBar.ts
│ │ │ ├── slider/
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ └── useSlider.ts
│ │ │ ├── stepper/
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ └── useStepper.ts
│ │ │ ├── svg-icon/
│ │ │ │ ├── IconArrowdown.tsx
│ │ │ │ ├── IconBells.tsx
│ │ │ │ ├── IconCheck.tsx
│ │ │ │ ├── IconCheckboxChecked.tsx
│ │ │ │ ├── IconCheckboxHalfchecked.tsx
│ │ │ │ ├── IconCheckboxUnchecked.tsx
│ │ │ │ ├── IconCheckcircle.tsx
│ │ │ │ ├── IconCheckcircleo.tsx
│ │ │ │ ├── IconClockcircleo.tsx
│ │ │ │ ├── IconClose.tsx
│ │ │ │ ├── IconClosecircleo.tsx
│ │ │ │ ├── IconDate.tsx
│ │ │ │ ├── IconDown.tsx
│ │ │ │ ├── IconEllipsis.tsx
│ │ │ │ ├── IconEyeclose.tsx
│ │ │ │ ├── IconEyeopen.tsx
│ │ │ │ ├── IconLeft.tsx
│ │ │ │ ├── IconMinus.tsx
│ │ │ │ ├── IconPlus.tsx
│ │ │ │ ├── IconRadioChecked.tsx
│ │ │ │ ├── IconRadioUnchecked.tsx
│ │ │ │ ├── IconReload.tsx
│ │ │ │ ├── IconRight.tsx
│ │ │ │ ├── IconSearch.tsx
│ │ │ │ ├── IconUp.tsx
│ │ │ │ ├── helper.ts
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── swipe-row/
│ │ │ │ ├── context.tsx
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ └── useSwipeRow.ts
│ │ │ ├── switch/
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ └── useSwitch.ts
│ │ │ ├── table/
│ │ │ │ ├── Cell.tsx
│ │ │ │ ├── Head.tsx
│ │ │ │ ├── Rows.tsx
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ ├── type.ts
│ │ │ │ ├── useTable.ts
│ │ │ │ └── utils.ts
│ │ │ ├── tag/
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ └── useTag.ts
│ │ │ ├── text/
│ │ │ │ ├── ReText.tsx
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── theme/
│ │ │ │ └── index.ts
│ │ │ ├── theme-provider/
│ │ │ │ └── index.tsx
│ │ │ ├── timeline/
│ │ │ │ ├── HorizontalTimeline.tsx
│ │ │ │ ├── VerticalTimeline.tsx
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ └── type.ts
│ │ │ ├── toast/
│ │ │ │ ├── ToastRoot.tsx
│ │ │ │ ├── constant.ts
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ ├── type.ts
│ │ │ │ └── useToast.ts
│ │ │ ├── tooltip/
│ │ │ │ ├── Triangle.tsx
│ │ │ │ ├── getTooltipCoordinate.tsx
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── tree/
│ │ │ │ ├── Checkbox.tsx
│ │ │ │ ├── Chevron.tsx
│ │ │ │ ├── TreeGroup.tsx
│ │ │ │ ├── TreeNode.tsx
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ ├── type.ts
│ │ │ │ ├── useGroup.ts
│ │ │ │ ├── useTree.ts
│ │ │ │ └── util.ts
│ │ │ ├── utils/
│ │ │ │ ├── redash.ts
│ │ │ │ └── ref-util.ts
│ │ │ ├── vehicle-keyboard/
│ │ │ │ ├── VehicleKeyboardInput.tsx
│ │ │ │ ├── VehicleKeyboardItem.tsx
│ │ │ │ ├── VehicleKeyboardModal.tsx
│ │ │ │ ├── VehicleKeyboardView.tsx
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ ├── type.ts
│ │ │ │ ├── useVehicleKeyboard.ts
│ │ │ │ └── useVehicleKeyboardModal.ts
│ │ │ ├── white-space/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ └── wing-blank/
│ │ │ ├── index.md
│ │ │ └── index.tsx
│ │ ├── tsconfig.build.json
│ │ ├── tsconfig.json
│ │ └── typings.d.ts
│ ├── react-native-alipay/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── android/
│ │ │ ├── README.md
│ │ │ ├── build.gradle
│ │ │ ├── libs/
│ │ │ │ └── alipaysdk-15.8.06.211122170115.aar
│ │ │ └── src/
│ │ │ └── main/
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── thundersdata/
│ │ │ └── alipay/
│ │ │ ├── ReactNativeAlipayModule.java
│ │ │ └── ReactNativeAlipayPackage.java
│ │ ├── index.js
│ │ ├── ios/
│ │ │ ├── ReactNativeAlipay.h
│ │ │ ├── ReactNativeAlipay.m
│ │ │ ├── ReactNativeAlipay.xcodeproj/
│ │ │ │ └── project.pbxproj
│ │ │ └── ReactNativeAlipay.xcworkspace/
│ │ │ └── contents.xcworkspacedata
│ │ ├── package.json
│ │ └── react-native-alipay.podspec
│ ├── react-native-amap-search/
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── android/
│ │ │ ├── build.gradle
│ │ │ ├── gradle/
│ │ │ │ └── wrapper/
│ │ │ │ ├── gradle-wrapper.jar
│ │ │ │ └── gradle-wrapper.properties
│ │ │ ├── gradlew
│ │ │ ├── gradlew.bat
│ │ │ └── src/
│ │ │ └── main/
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── reactnativeamapsearch/
│ │ │ ├── AmapSearchModule.java
│ │ │ └── AmapSearchPackage.java
│ │ ├── ios/
│ │ │ ├── AMapSearchManager.h
│ │ │ ├── AMapSearchManager.m
│ │ │ └── AmapSearch.xcodeproj/
│ │ │ └── project.pbxproj
│ │ ├── package.json
│ │ ├── react-native-amap-search.podspec
│ │ ├── scripts/
│ │ │ └── bootstrap.js
│ │ ├── src/
│ │ │ ├── constant.ts
│ │ │ ├── index.tsx
│ │ │ ├── typing.d.ts
│ │ │ └── useSearch.tsx
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── react-native-calendar/
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── components/
│ │ │ │ ├── Agenda/
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── useAgenda.ts
│ │ │ │ ├── Calendar/
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── useCalendar.tsx
│ │ │ │ ├── CalendarList/
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── useCalendarList.ts
│ │ │ │ ├── Day/
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── Header/
│ │ │ │ │ └── index.tsx
│ │ │ │ └── Period/
│ │ │ │ ├── index.tsx
│ │ │ │ └── usePeriod.tsx
│ │ │ ├── constant.ts
│ │ │ ├── dateUtils.ts
│ │ │ ├── index.md
│ │ │ ├── index.tsx
│ │ │ └── type.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── react-native-echarts/
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── readMe.md
│ │ ├── src/
│ │ │ ├── index.md
│ │ │ ├── index.tsx
│ │ │ ├── tmp/
│ │ │ │ └── tpl.html
│ │ │ └── utils/
│ │ │ └── builder.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── react-native-image-picker/
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.md
│ │ │ ├── index.tsx
│ │ │ ├── type.ts
│ │ │ └── useImagePicker.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── react-native-password/
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── Password.tsx
│ │ │ ├── PasswordModal.tsx
│ │ │ ├── index.md
│ │ │ ├── index.tsx
│ │ │ ├── usePassword.ts
│ │ │ └── usePasswordModal.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── react-native-picker/
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── cascade-picker/
│ │ │ │ ├── index.tsx
│ │ │ │ └── useCascader.ts
│ │ │ ├── components/
│ │ │ │ ├── DatePicker/
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── type.ts
│ │ │ │ │ └── useDatePicker.ts
│ │ │ │ └── WheelPicker/
│ │ │ │ ├── index.tsx
│ │ │ │ └── type.ts
│ │ │ ├── date-period-input/
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ └── useDatePeriodInput.tsx
│ │ │ ├── date-picker/
│ │ │ │ ├── index.md
│ │ │ │ ├── index.tsx
│ │ │ │ └── useDatePicker.ts
│ │ │ ├── date-picker-input/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── date-picker-item/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── index.ts
│ │ │ ├── normal-picker/
│ │ │ │ ├── index.tsx
│ │ │ │ └── useNormalPicker.ts
│ │ │ ├── picker-input/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── picker-item/
│ │ │ │ ├── index.md
│ │ │ │ └── index.tsx
│ │ │ ├── type.ts
│ │ │ ├── useDatePicker.tsx
│ │ │ ├── usePicker.tsx
│ │ │ └── utils.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── react-native-rating/
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── SwipeRating.tsx
│ │ │ ├── TapRating.tsx
│ │ │ ├── components/
│ │ │ │ ├── Star.tsx
│ │ │ │ └── SwipeStar.tsx
│ │ │ ├── index.md
│ │ │ ├── index.tsx
│ │ │ ├── type.ts
│ │ │ ├── useSwipeRating.ts
│ │ │ └── useTapRating.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── react-native-share/
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.md
│ │ │ ├── index.tsx
│ │ │ └── svg/
│ │ │ ├── alipay.tsx
│ │ │ ├── dingding.tsx
│ │ │ ├── moments.tsx
│ │ │ ├── qq.tsx
│ │ │ ├── qqmail.tsx
│ │ │ ├── refresh.tsx
│ │ │ ├── sina.tsx
│ │ │ ├── sms.tsx
│ │ │ ├── wechat.tsx
│ │ │ └── zhihu.tsx
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── react-native-skeleton/
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── ShiverBone.tsx
│ │ │ ├── StaticBone.tsx
│ │ │ ├── helper.ts
│ │ │ ├── index.md
│ │ │ ├── index.tsx
│ │ │ └── type.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── react-native-tabs/
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── SceneView.tsx
│ │ │ ├── ScrollBar.tsx
│ │ │ ├── TabBar.tsx
│ │ │ ├── TabBarIndicator.tsx
│ │ │ ├── TabBarItem.tsx
│ │ │ ├── index.md
│ │ │ ├── index.tsx
│ │ │ ├── types.tsx
│ │ │ └── usePagerView.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ └── svgicon-cli/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── gulpfile.js
│ ├── package.json
│ ├── src/
│ │ ├── commands/
│ │ │ ├── createIcon.ts
│ │ │ └── createJson.ts
│ │ ├── libs/
│ │ │ ├── copyTemplate.ts
│ │ │ ├── generateComponent.ts
│ │ │ ├── getConfig.ts
│ │ │ ├── getTemplate.ts
│ │ │ ├── parseLocalSvg.ts
│ │ │ ├── replace.ts
│ │ │ ├── svgicon.json
│ │ │ └── whitespace.ts
│ │ └── templates/
│ │ ├── Icon.tsx.template
│ │ ├── LocalSingleIcon.tsx.template
│ │ └── helper.ts.template
│ ├── svgicon.json
│ └── tsconfig.build.json
├── pnpm-workspace.yaml
├── tsconfig.json
├── turbo.json
└── typings.d.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: .changeset/README.md
================================================
# Changesets
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works with multi-package repos, or single-package repos to help you version and publish your code. You can find the full documentation for it [in our repository](https://github.com/changesets/changesets)
We have a quick list of common questions to get you started engaging with this project in [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
================================================
FILE: .changeset/config.json
================================================
{
"$schema": "https://unpkg.com/@changesets/config@2.2.0/schema.json",
"changelog": [
"@changesets/changelog-github",
{
"repo": "thundersdata-frontend/td-design"
}
],
"commit": false,
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "master",
"updateInternalDependencies": "patch",
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
"updateInternalDependents": "always"
}
}
================================================
FILE: .commitlintrc.js
================================================
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {},
};
================================================
FILE: .cz-config.js
================================================
'use strict';
module.exports = {
types: [
{
value: 'feat',
name: '✨ feat: 新功能',
},
{
value: 'fix',
name: '🐛 fix: 修复bug',
},
{
value: 'refactor',
name: '♻️ refactor: 代码重构(既不是新功能也不是改bug)',
},
{
value: 'chore',
name: '🎫 chore: 修改流程配置',
},
{
value: 'docs',
name: '📝 docs: 修改了文档',
},
{
value: 'test',
name: '✅ test: 更新了测试用例',
},
{
value: 'style',
name: '💄 style: 修改了样式文件',
},
{
value: 'perf',
name: '⚡️ perf: 新能优化',
},
{
value: 'revert',
name: '⏪ revert: 回退提交',
},
],
scopes: [],
allowCustomScopes: true,
allowBreakingChanges: ['feat', 'fix'],
subjectLimit: 50,
messages: {
type: '请选择你本次改动的修改类型',
customScope: '\n请明确本次改动的范围(可填):',
subject: '简短描述本次改动:\n',
body: '详细描述本次改动 (可填). 使用 "|" 换行:\n',
breaking: '请列出任何 BREAKING CHANGES (可填):\n',
footer: '请列出本次改动关闭的ISSUE (可填). 比如: #31, #34:\n',
confirmCommit: '你确定提交本次改动吗?',
},
};
================================================
FILE: .editorconfig
================================================
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[Makefile]
indent_style = tab
================================================
FILE: .github/workflows/changeset-version.yml
================================================
name: Changesets
on:
push:
branches:
- master
- v4
- v3
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
release:
name: upgrade-version
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v3
with:
# This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
fetch-depth: 0
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 16
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
- name: Setup pnpm cache
uses: actions/cache@v3
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm i --no-frozen-lockfile
- name: Create Release Pull Request
uses: changesets/action@v1
with:
publish: pnpm release
commit: 'chore: release'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
================================================
FILE: .github/workflows/gh-pages.yml
================================================
name: Build Doc Site
env:
NODE_OPTIONS: --max-old-space-size=6144
on:
push:
branches:
- master
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 16
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
- name: Setup pnpm cache
uses: actions/cache@v3
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm i --no-frozen-lockfile
- name: Build Doc
run: pnpm doc:build
- name: Deploy
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs-dist
================================================
FILE: .github/workflows/issue-close-require.yml
================================================
name: Issue Close Require
on:
schedule:
- cron: "0 0 * * *"
permissions:
contents: read
jobs:
issue-close-require:
permissions:
issues: write # for actions-cool/issues-helper to update issues
pull-requests: write # for actions-cool/issues-helper to update PRs
runs-on: ubuntu-latest
steps:
- name: need reproduce
uses: actions-cool/issues-helper@v3
with:
actions: 'close-issues'
labels: 'need reproduce'
inactive-day: 3
body: |
Since the issue was labeled with `need reproduce`, but no response in 3 days. This issue will be closed. If you have any questions, you can comment and reply.
由于该 issue 被标记为需要提供复现,却 3 天未收到回应。现关闭 issue,若有任何问题,可评论回复。
================================================
FILE: .gitignore
================================================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
packages/**/node_modules
/npm-debug.log*
/yarn-error.log
/yarn.lock
/package-lock.json
# production
/dist
# misc
.DS_Store
# umi
.umi
.umi-production
.env.local
.turbo
packages/**/lib/**
build/**
dist/**
.next/**
.jest
docs-dist
.idea
.gradle
================================================
FILE: .husky/.gitignore
================================================
_
================================================
FILE: .husky/commit-msg
================================================
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx commitlint --edit $1
================================================
FILE: .husky/pre-commit
================================================
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged
================================================
FILE: .npmrc
================================================
registry=https://registry.npmmirror.com/
strict-peer-dependencies=false
================================================
FILE: .prettierignore
================================================
**/*.svg
**/*.ejs
**/*.html
**/*.js
**/*.json
**/*.jsx
package.json
*.yaml
.umi
.umi-production
example
lib
.changeset
docs
================================================
FILE: .prettierrc
================================================
{
"semi": true,
"trailingComma": "es5",
"singleQuote": true,
"printWidth": 120,
"tabWidth": 2,
"bracketSpacing": true,
"arrowParens": "avoid",
"proseWrap": "never",
"endOfLine": "auto",
"importOrder": [
"^react(.*)",
"<THIRD_PARTY_MODULES>",
"@/(.*)",
"^[./]"
],
"importOrderSeparation": true,
"importOrderSortSpecifiers": true,
"importOrderGroupNamespaceSpecifiers": true,
"importOrderCaseInsensitive": true,
"overrides": [
{
"files": ".prettierrc",
"options": {
"parser": "json"
}
}
],
"plugins": [
"@trivago/prettier-plugin-sort-imports"
]
}
================================================
FILE: .umirc.ts
================================================
import { defineConfig } from 'dumi';
export default defineConfig({
title: '雷数前端',
favicon: 'https://avatars0.githubusercontent.com/u/56826119',
logo: 'https://avatars0.githubusercontent.com/u/56826119',
outputPath: 'docs-dist',
mode: 'site',
hash: true,
base: '/td-design',
publicPath: '/td-design/',
exportStatic: {},
dynamicImport: {},
webpack5: {},
// define: {
// AMAP_DRILL_SERVER_KEY: '23979b50fbe00d2e05c1cf4f6300d028',
// AMAP_DRILL_JS_KEY: 'dfc9eee6a8e7bff31451ce22e3689e09',
// AMAP_DRILL_JS_SECRET: 'ac1294b1351220044a66c1023f7d5226',
// },
extraBabelPlugins: [
[
'import',
{
libraryName: 'antd',
libraryDirectory: 'es',
style: 'css',
},
],
],
chainWebpack(config) {
config.module
.rule('ttf')
.test(/.(ttf|otf)$/)
.use('file-loader')
.loader('file-loader');
config.module
.rule('media')
.test(/\.(mp4)$/)
.use('file-loader')
.loader(require.resolve('file-loader'));
},
navs: [
{
title: 'RN组件库',
path: '/react-native',
},
{
title: '大屏素材库',
path: '/screen',
},
{
title: '设计模式',
path: '/pattern',
},
{
title: '数据结构',
path: '/structure',
},
{
title: '软件开发',
path: '/development',
},
{
title: '常见问题',
path: '/faq',
},
{
title: '友情链接',
path: '/friendlink',
},
{
title: 'GitHub',
path: 'https://github.com/thundersdata-frontend/td-design',
},
],
});
================================================
FILE: .vscode/settings.json
================================================
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"eslint.validate": [
"javascript",
"typescript",
"json",
"md"
],
"cSpell.words": [
"dayjs"
]
}
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
# dumi app
## Getting Started
Install dependencies,
```bash
$ npm i
```
Start the dev server,
```bash
$ npm start
```
Build site app,
```bash
$ npm run build
```
================================================
FILE: docs/development.md
================================================
---
sidemenu: false
nav:
title: 软件开发
path: /development
---
## 软件开发
收集的一些软件开发相关的术语和概念。
================================================
FILE: docs/friendlink.md
================================================
---
sidemenu: false
nav:
title: 友情链接
path: /friendlink
---
# 友情链接
## react-native 组件库
- [vant-react-native](https://vant-react-native.js.org/vant-react-native/)
- [ant-design-mobile-rn](https://rn.mobile.ant.design/index-cn)
- [react-native-element](https://reactnativeelements.com/)
- [react-native-paper](https://callstack.github.io/react-native-paper/)
- [nativebase](https://nativebase.io/)
- [react-native-ui-kitten](https://akveo.github.io/react-native-ui-kitten/)
## react-native 相关网站
- [官网地址](https://reactnative.dev/)
- [react navigation 导航库](https://reactnavigation.org/)
- [restyle 样式系统](https://github.com/Shopify/restyle)
## react-native 优秀组件
## react-native 优秀文章
================================================
FILE: docs/index.md
================================================
---
title: td-design 雷数科技前端团队
hero:
title: 雷数科技-前端文档
desc: 本网站包含雷数科技前端团队开发的所有组件库、脚手架、模板等内容
actions:
- text: 开始探索
link: /react-native
features:
- icon: https://gw.alipayobjects.com/zos/bmw-prod/881dc458-f20b-407b-947a-95104b5ec82b/k79dm8ih_w144_h144.png
title: 组件丰富 | 功能全面
desc: 提供了54+基础组件,覆盖各类场景,组件特性丰富,满足各种功能需求
- icon: https://gw.alipayobjects.com/zos/bmw-prod/d60657df-0822-4631-9d7c-e7a869c2f21c/k79dmz3q_w126_h126.png
title: 体积小巧 | 性能出众
desc: 在不损失功能的基础上,尽量保证了单个组件的体积最小、性能最优
- icon: https://gw.alipayobjects.com/zos/bmw-prod/d1ee0c6f-5aed-4a45-a507-339a4bfe076c/k7bjsocq_w144_h144.png
title: 简易定制 | 多种风格
desc: 支持灵活的样式定制,简易生成多种风格,满足个性化产品需求
footer: Open-source MIT Licensed | Copyright © 2020<br />Powered by [thundersdata](https://github.com/thundersdata)
---
================================================
FILE: docs/pattern/Strategy.md
================================================
---
nav:
title: 策略模式
path: /pattern
group:
title: 设计模式
path: /
order: 3
---
# 策略模式 - Strategy
策略模式是一种行为设计模式, 它能让你定义一系列算法, 并将每种算法分别放入独立的类中, 以使算法的对象能够相互替换。
## 问题
简单的说在一些时候,我们需要根据传入的参数不同来执行不同的代码,比如说当用户认证状态分别为:未认证,已认证,审核中等状态时,我们需要进行不同的处理。或许我们写以下的代码:
```js
function navigation(status) {
switch (status) {
case '未认证':
history.push('未认证');
break;
case '已认证':
history.push('已认证');
break;
case '审核中':
history.push('审核中');
break;
}
}
```
当我们需要增加一个新的状态审核失败时,就需要对 navigation 进行修改增加一个分支,或许一个状态不算什么,但是在未来我们也不确定会增加多少个状态,整个函数的分支就会非常的多,整个方法就会非常的庞大,导致可读性也十分的差,同时它也违反了开放-封闭原则。这一块的代码也无法进行很好的复用,如果还有一个地方需要未认证的逻辑,那我们只能复制粘贴了。
## 解决方案
在对分支进行拆解,将其拆解成多个小的策略,并且有一个中间函数做上下文的链接,而不是直接去使用
```js
interface Strategy {
navigation: () => void;
}
class StrategyA implements Strategy {
public navigation() {
// history.push('未认证');
console.log('未认证');
}
}
class StrategyB implements Strategy {
public navigation() {
// history.push('已认证');
console.log('已认证');
}
}
class StrategyC implements Strategy {
public navigation() {
// history.push('审核中');
console.log('审核中');
}
}
class Context {
private strategy: Strategy;
constructor(strategy: Strategy) {
this.strategy = strategy;
}
public setStrategy(strategy: Strategy) {
this.strategy = strategy;
}
public doStrategy() {
this.strategy.navigation();
}
}
const navigation = new Context(new StrategyA());
navigation.doStrategy();
console.log('-----');
navigation.setStrategy(new StrategyB());
navigation.doStrategy();
console.log('-----');
navigation.setStrategy(new StrategyC());
navigation.doStrategy();
```
策略模式建议找出负责用许多不同方式完成特定任务的类, 然后将其中的算法抽取到一组被称为策略的独立类中。
名为上下文的原始类必须包含一个成员变量来存储对于每种策略的引用。 上下文并不执行任务, 而是将工作委派给已连接的策略对象。
就是将各种互不相关的行为进行独立封装,然后使用中间的上下文进行应用,使调用者本身和方法没有直接的关联,这时候需要增加一个新的状态审核失败只需要新增一种策略就可以解决,符合了开放-封闭原则。不会对原有的代码进行改动。同时也增加了代码的复用性。当然这是面向对象的策略模式,在 js 中可以进行一些省略,方法本身也是一个对象,我们可以直接使用一个对象进行对策略的存放。
```js
const Context{
"StrategyA":()=>{
console.log('未认证');
},
"StrategyB":()=>{
console.log('已认证');
},
"StrategyC":()=>{
console.log('审核中');
},
}
function navigation(name){
Context[name]?.()
}
```
## 策略模式
1. 将一些独立的逻辑或者业务进行封装
2. 拥有一个上下文链接封装的逻辑和调用者
3. 调用者通过对上下文来访问相应的方法
## 优缺点
优点:
1. 提高了代码的复用率
2. 通过测量将判断分支给剪切了,使得代码中没有那么庞大的 if 或 switch。
3. 开闭原则。 你无需对上下文进行修改就能够引入新的策略
缺点:
1. 增加了对策略了解的成本
2. 代码中会存在很多策略方法和上下文
================================================
FILE: docs/pattern/index.md
================================================
---
nav:
title: 设计模式
path: /pattern
group:
title: 设计模式
path: /
order: 0
---
# 设计模式介绍
================================================
FILE: docs/pattern/signleton.md
================================================
---
nav:
title: 单例模式
path: /pattern
group:
title: 设计模式
path: /
order: 1
---
# 单例模式 - Singleton
================================================
FILE: docs/pattern/visitor.md
================================================
---
nav:
title: 访问者模式
path: /pattern
group:
title: 设计模式
path: /
order: 2
---
# 访问者模式 - Visitor
================================================
FILE: docs/react-faq.md
================================================
---
nav:
title: React应用开发常见问题
path: /faq
group:
title: 开发常见问题
path: /
order: 1
---
# React 应用开发常见问题
## 单点登录
### 新项目接入单点登录
新项目里直接接入单点登录比较简单,按照我们目前用的 `umi-request` 库,只需要在 `common.ts`中配置 `credentials: include`即可,不需要太多额外的操作
### 老项目改造接入单点登录
1. 首先先找到项目中配置 `request` 的文件,在配置中加入 `credentials: include`,如果老项目中用的是 `axios`库,在配置中加入 `withCredentials: true`
2. 在请求头中去掉携带 `token`
3. 找到判断是否登录的地方(一般都是通过是否有 token 判断是否已经登录),直接设置成已经登录的状态
4. 找到退出登录的地方,把 `logoutUrl`设置为 `域名 + '/logout?redirect_url=' + 域名`
5. 部署应用的时候,把单点登录域配置上去,具体的 `key` 可以找后端同学
`tips: 一般我们的token都是通过写了一个方法去获取的,所以你可以在上面第二步去掉token的地方,找到那个方法然后进行全局搜索,不出意外的话,可以顺着这个方法找到判断是否登录的方法,以及一些类似于上传文件、上传图片需要token的地方,也设置为 withCredentials=true 即可 `
================================================
FILE: docs/react-native/index.md
================================================
---
title: react-native组件库
toc: menu
order: 1
---
# @td-design/react-native 组件库
## 特性和优势
- 全面兼容 react 和 react-native
- UI 样式高度可配置,拓展性强,轻松适应各类产品风格
- 基于 React Native 的 iOS / Android 多平台支持,组件丰富,能全面覆盖各类场景
- 使用 TypeScript 开发,提供类型定义文件,支持类型及属性智能提示,方便业务开发
## 适用场景
- 适合于中大型产品应用
- 适合于基于 react-native 的终端应用
- 适合不同 UI 风格的定制需求的应用
## 版本要求
- `react-native` 0.63.0 版本以上
## 快速上手
_在开始之前,推荐先学习 [React](https://reactjs.org/) 和 [React Native](http://reactnative.dev/)。并确认 Node.js 已经升级到 v14.x 或以上。_
## 新建项目
推荐使用[rn-template](https://github.com/thundersdata-frontend/rn-template)作为模板进行开发。该模板内置了很多常用的功能,可以极大地提高你的项目开发进度,不需要再为搭建环境发愁。
```js | pure
npm install -g @td-design/cli
td-cli init <projectName>
```
根据提示,模板选择`app`, 分支目前支持: `main`/`0.66`/`0.67`/`0.68`/`0.69`/`0.70`/`0.71`/`0.72`。
创建成功后,全局搜索`rntemplate`,替换成你的项目名称即可。
================================================
FILE: docs/react-native/restyle.md
================================================
---
title: 主题和样式
toc: menu
order: 2
---
# 主题和样式
先看效果图:
<center>
<figure>
<img
alt=""
src="https://user-images.githubusercontent.com/688415/75268245-91084b80-57f7-11ea-905b-2a9046aa5ca3.gif"
/>
<div>图片来自:<a href="https://github.com/Shopify/restyle">@shopify/restyle</a></div>
</figure>
</center>
## 介绍
`@shopify/restyle` 是由 shopify 公司开发的一个用于开发 react-native UI 组件库的工具。主要聚焦在组件库的样式上。它假设我们的组件库是基于一套设计体系,定义了一系列的颜色和间距常量。我们可以通过定义主题的方式来方便地实现对组件样式的定义和自由切换(如暗黑模式)。
## 使用
### 定义主题
任何使用此工具开发的组件库都要有一个全局的样式对象。它明确地定义了一系列的颜色(color)、间距(spacing)、断点(breakpoints)以及其他更多的定义。UI 组件可以直接使用这个样式对象中的 key 值。比如`backgroundColor="primaryColor"`。在`TypeScript`的加持下,我们可以非常放心,它会约束我们可以使用的值。示例如下:
```js
import { createTheme } from '@shopify/restyle';
const palette = {
purpleLight: '#8C6FF7',
purplePrimary: '#5A31F4',
purpleDark: '#3F22AB',
greenLight: '#56DCBA',
greenPrimary: '#0ECD9D',
greenDark: '#0A906E',
black: '#0B0B0B',
white: '#F0F2F3',
};
const theme = createTheme({
colors: {
mainBackground: palette.white,
cardPrimaryBackground: palette.purplePrimary,
},
spacing: {
s: 8,
m: 16,
l: 24,
xl: 40,
},
breakpoints: {
phone: 0,
tablet: 768,
},
});
export type Theme = typeof theme;
export default theme;
```
有了这个主题样式对象之后,我们应该如何在项目中使用呢?
1. 在项目的`app.tsx`里面,用`ThemeProvider`包裹你的组件:
```tsx | pure
import { ThemeProvider } from '@shopify/restyle';
import theme from './theme';
const App = () => <ThemeProvider theme={theme}>{/* Rest of the app */}</ThemeProvider>;
```
### Colors 颜色
通常设计师会使用一个调色板(palette)来定义组件设计体系中涉及到的各种颜色。通常而言,我们不建议直接在主题里面使用颜色值,而是建议使用调色板中颜色对应的语义化的名字。
```js
// 定义一个调色板
const palette = {
purpleLight: '#8C6FF7',
purplePrimary: '#5A31F4',
purpleDark: '#3F22AB',
greenLight: '#56DCBA',
greenPrimary: '#0ECD9D',
greenDark: '#0A906E',
black: '#0B0B0B',
white: '#F0F2F3',
};
// 在主题中使用调色板里颜色对应的名字
const theme = createTheme({
colors: {
mainBackground: palette.white,
mainForeground: palette.black,
cardPrimaryBackground: palette.purplePrimary,
buttonPrimaryBackground: palette.purplePrimary,
},
});
```
_为什么选择使用调色板中颜色对应的名字更有好处呢?_
1. 很容易理解整个 APP 中在何处应用了颜色。
2. 如果调色板中颜色发生了改变,我们只需要把颜色名称对应的值改掉就可以了,不用去整个 APP 里找到颜色值并替换。比如把紫色换成了深黑色。
```js
const palette = {
// ...
// purpleDark: '#3F22AB', // 原值
purpleDark: '#0B0B0B', // 改后的值
};
```
3. 如上面的主题中,`cardPrimaryBackground`和`buttonPrimaryBackground`虽然都指向的是同一个颜色,但是如果需要把`buttonPrimaryBackground`换成绿色,改动也是非常简单方便的。
```js
const theme = createTheme({
colors: {
// ...
// buttonPrimaryBackground: palette.purplePrimary, // 原先值
buttonPrimaryBackground: palette.greenPrimary, // 改后的值
},
});
```
4. 可以在 APP 使用过程中自由地切换主题样式。
### Spacing 间距
间距是按照衣服尺寸定义的一些常量数值。示例如下:
```js
const theme = createTheme({
spacing: {
s: 8,
m: 16,
l: 24,
xl: 40,
},
});
```
### Breakpoints 断点
断点是我们设置的为应用不同样式的不同目标屏幕尺寸的最小宽度。断点可以定义为单个值(width),或者一个宽高值对象。示例如下:
```js
const theme = createTheme({
breakpoints: {
phone: 0,
longPhone: {
width: 0,
height: 812,
},
tablet: 768,
largeTablet: 1024,
},
});
```
具体可以参见下面的[Responsive 响应式](#responsive-响应式)章节。
### 使用主题样式对象
借助`useTheme`,我们可以非常方便地在一个组件内使用主题内定义的内容。示例如下:
```tsx | pure
const Component = () => {
const theme = useTheme<Theme>();
const { cardPrimaryBackground } = theme.colors;
// ...
};
```
_之所以不直接引入 theme 文件,是为了方便我们实现主题切换效果。_
### 内置的 Box/Text 组件
1. Box 组件:
Box 组件可以定义的属性由这几个大类组成:`backgroundColor`, `opacity`, `visible`, `layout`, `spacing`, `border`, `shadow`, `position`。具体大类里有哪些属性,可以参见下方的[Restyle Function](#restyle-functions)章节。
```tsx | pure
import { createBox } from '@shopify/restyle';
import { Theme } from './theme';
const Box = createBox<Theme>();
export default Box;
```
2. Text 组件:
Text 组件可以定义的属性由这几个大类组成:`color`, `opacity`, `visible`, `typography`, `textShadow`, `spacing`。具体大类里有哪些属性,可以参见下方的[Restyle Function](#restyle-functions)章节。
```tsx | pure
import { createText } from '@shopify/restyle';
import { Theme } from './theme';
const Text = createText<Theme>();
export default Text;
```
值得注意的是,Text 组件还可以接受一个`variant`属性,它对应的是 theme 对象里`textVariants`下的 key 值。
```tsx | pure
const theme = createTheme({
...,
textVariants: {
header: {
fontWeight: 'bold',
fontSize: 34,
lineHeight: 42.5,
color: 'black',
},
subheader: {
fontWeight: '600',
fontSize: 28,
lineHeight: 36,
color: 'black',
},
body: {
fontSize: 16,
lineHeight: 24,
color: 'black',
},
},
});
// In a component
<Text variant="header">Header</Text>
```
### 自定义组件
如果我们想要自己创建一个类似`Box`和`Text`这样的组件,但是我们自己决定组件可以接受那些类别的属性,我们可以使用`createRestyleComponent`。示例如下:
```tsx | pure
import { createRestyleComponent, createVariant, spacing, SpacingProps, VariantProps } from '@shopify/restyle';
import { Theme } from './theme';
type Props = SpacingProps<Theme> & VariantProps<Theme, 'cardVariants'>;
const Card = createRestyleComponent<Props>([spacing, createVariant({ themeKey: 'cardVariants' })]);
export default Card;
```
如果觉得这还不够,也可以使用`useRestyle`这个 hooks 来实现更强大的定制功能。示例如下:
```tsx | pure
import {
backgroundColor,
BackgroundColorProps,
border,
BorderProps,
composeRestyleFunctions,
spacing,
SpacingProps,
useRestyle,
} from '@shopify/restyle';
import { TouchableOpacity, View } from 'react-native';
import Text from './Text';
import { Theme } from './theme';
const restyleFunctions = composeRestyleFunctions([spacing, border, backgroundColor]);
type Props = SpacingProps<Theme> &
BorderProps<Theme> &
BackgroundColorProps<Theme> & {
onPress: () => void;
};
const Button = ({ onPress, label, ...rest }: Props) => {
const props = useRestyle(restyleFunctions, rest);
return (
<TouchableOpacity onPress={onPress}>
<View {...props}>
<Text variant="buttonLabel">{label}</Text>
</View>
</TouchableOpacity>
);
};
```
### Restyle Functions
**Restyle Functions**把组件允许接受的属性做了一下分类,同时映射到主题对象,我们可以非常方便地使用他们来约束组件的属性以及快速开发基于主题对象的组件。
| 类别 | 属性 | 对应的主体对象 Key 值 |
| --- | --- | --- |
| backgroundColor | `backgroundColor` | `colors` |
| color | `color` | `colors` |
| opacity | `opacity` | |
| visible | `display` | |
| spacing | `margin`, `marginTop`, `marginRight`, `marginBottom`, `marginLeft`, `marginStart`, `marginEnd`, `marginHorizontal`, `marginVertical`, `padding`, `paddingTop`, `paddingRight`, `paddingBottom`, `paddingLeft`, `paddingStart`, `paddingEnd`, `paddingHorizontal`, `paddingVertical` | `spacing` |
| layout | `width`, `height`, `minWidth`, `maxWidth`, `minHeight`, `maxHeight`, `overflow`, `aspectRatio`, `alignContent`, `alignItems`, `alignSelf`, `justifyContent`, `flex`, `flexBasis`, `flexDirection`, `flexGrow`, `flexShrink`, `flexWrap` | |
| position | `position`, `top`, `right`, `bottom`, `left`, `start`, `end` | |
| position | `zIndex` | `zIndices` |
| border | `borderBottomWidth`, `borderLeftWidth`, `borderRightWidth`, `borderStartWidth`, `borderEndWidth`, `borderStyle`, `borderTopWidth`, `borderWidth` | |
| border | `borderColor`, `borderTopColor`, `borderRightColor`, `borderLeftColor`, `borderStartColor`, `borderEndColor`, `borderBottomColor` | `colors` |
| border | `borderRadius`, `borderBottomLeftRadius`, `borderBottomRightRadius`, `borderBottomStartRadius`, `borderBottomEndRadius`, `borderTopLeftRadius`, `borderTopRightRadius`, `borderTopStartRadius`, `borderTopEndRadius` | `borderRadii` |
| shadow | `shadowOpacity`, `shadowOffset`, `shadowRadius`, `elevation` | |
| shadow | `shadowColor` | `colors` |
| textShadow | `textShadowOffset`, `textShadowRadius` | |
| textShadow | `textShadowColor` | `colors` |
| typography | `fontFamily`, `fontSize`, `fontStyle`, `fontWeight`, `letterSpacing`, `lineHeight`, `textAlign`, `textDecorationLine`, `textDecorationStyle`, `textTransform` | |
### Variants
`variant` 是 `Restyle Function`的一种形式。它可以将一个属性映射到多个其他属性。`variant`需要对应到主题对象中的 key 值。示例如下:
```tsx | pure
// In theme
const theme = createTheme({
// ...
spacing: {
s: 8,
m: 16,
l: 24,
},
colors: {
cardRegularBackground: '#EEEEEE',
},
breakpoints: {
phone: 0,
tablet: 768,
},
cardVariants: {
defaults: {
// We can define defaults for the variant here.
// This will be applied after the defaults passed to createVariant and before the variant defined below.
},
regular: {
// We can refer to other values in the theme here, and use responsive props
padding: {
phone: 'x2',
tablet: 'x3',
},
}
elevated: {
padding: {
phone: 'x2',
tablet: 'x3',
},
shadowColor: '#000',
shadowOpacity: 0.2,
shadowOffset: {width: 0, height: 5},
shadowRadius: 15,
elevation: 5,
}
}
})
import {createVariant, createRestyleComponent, VariantProps} from '@shopify/restyle'
const variant = createVariant<Theme>({themeKey: 'cardVariants', defaults: {
margin: {
phone: 'x2',
tablet: 'x3',
},
backgroundColor: 'cardRegularBackground',
}})
const Card = createRestyleComponent<VariantProps<Theme, 'cardVariants'>>([variant])
// variant的值必须是`cardVariants`对象下的key值。TypeScript会自动给我们提示。
<Card variant="elevated" />
```
### Responsive 响应式
我们通过在主题对象中定义 breakpoints(断点),来实现响应式效果。每个 Restyle 支持的属性都可以使用响应式写法来支持不同屏幕尺寸下的不同效果。示例如下:
```tsx | pure
const theme = createTheme({
// ...
breakpoints: {
phone: 0,
tablet: 768,
}
})
// 用法1:不关心断点
<Box flexDirection="row" />
// 用法2:不同尺寸不同效果
<Box flexDirection={{ phone: 'column', tablet: 'row' }} />
```
另外,如果我们想要把一些属性跟响应式关联,我们可以借助`useResponsiveProp`这个 hooks。示例如下:
```tsx | pure
const Button = ({ color = { phone: 'purple', tablet: 'blue' }, ...restProps }) => {
// 设备是phone的时候,值为purple;设备是tablet的时候,值为blue
const textColorProp = useResponsiveProp(color);
const bgColor = textColorProp === 'purple' ? 'lightPurple' : 'lightBlue';
return (
<Button backgroundColor={bgColor} {...restProps}>
{/* 这里不需要转,因为Text会自动转 */}
<Text color={color}>click me</Text>
<ActivityIndicator color={theme.colors[textColorProp]} />
</Button>
);
};
```
### 主题切换
基于`Restyle`实现主题切换效果很简单,我们只需要提供两套或者多套不同的主题即可。因为主题中并没有直接使用颜色,而是使用的调色板中的颜色,所以我们可以自由地切换主题。比如我们想要实现暗黑模式,示例如下:
```tsx | pure
// 定义主题对象
export const palette = {
purple: '#5A31F4',
white: '#FFF',
black: '#111',
darkGray: '#333',
lightGray: '#EEE',
};
const theme = createTheme({
spacing: {
s: 8,
m: 16,
},
colors: {
mainBackground: palette.lightGray,
mainForeground: palette.black,
primaryCardBackground: palette.purple,
secondaryCardBackground: palette.white,
primaryCardText: palette.white,
secondaryCardText: palette.black,
},
breakpoints: {},
textVariants: {
body: {
fontSize: 16,
lineHeight: 24,
color: 'mainForeground',
},
},
cardVariants: {
primary: {
backgroundColor: 'primaryCardBackground',
shadowOpacity: 0.3,
},
secondary: {
backgroundColor: 'secondaryCardBackground',
shadowOpacity: 0.1,
},
},
});
type Theme = typeof theme;
// 定义暗黑主题。区别只是颜色不一样。
const darkTheme: Theme = {
...theme,
colors: {
...theme.colors,
mainBackground: palette.black,
mainForeground: palette.white,
secondaryCardBackground: palette.darkGray,
secondaryCardText: palette.white,
},
};
// 组件中使用
const App = () => {
const [darkMode, setDarkMode] = useState(false);
return (
<ThemeProvider theme={darkMode ? darkTheme : theme}>
<Box padding="x3" backgroundColor="mainBackground" flex={1}>
<Box backgroundColor="primaryCardBackground" margin="x2" padding="x3" flexGrow={1}>
<Text variant="body" color="primaryCardText">
Primary Card
</Text>
</Box>
<Box backgroundColor="secondaryCardBackground" margin="x2" padding="x3" flexGrow={1}>
<Text variant="body" color="secondaryCardText">
Secondary Card
</Text>
</Box>
<Box marginTop="x3">
<Switch value={darkMode} onValueChange={(value: boolean) => setDarkMode(value)} />
</Box>
</Box>
</ThemeProvider>
);
};
export default App;
```
================================================
FILE: docs/react-native/theme.md
================================================
---
toc: menu
order: 3
---
# Theme 主题
`@shopify/restyle`库给我们提供了完备的主题定制功能。
## Spacing 间距
| 名称 | 说明 | 值 |
| ----- | ----- | ---- |
| `x0` | 1 倍 | `0` |
| `x1` | 1 倍 | `4` |
| `x2` | 2 倍 | `8` |
| `x3` | 3 倍 | `12` |
| `x4` | 4 倍 | `16` |
| `x5` | 5 倍 | `20` |
| `x6` | 6 倍 | `24` |
| `x7` | 7 倍 | `28` |
| `x8` | 8 倍 | `32` |
| `x9` | 9 倍 | `36` |
| `x10` | 10 倍 | `40` |
## 圆角
| 名称 | 说明 | 值 |
| ----- | ----- | ---- |
| `x0` | 1 倍 | `0` |
| `x1` | 1 倍 | `4` |
| `x2` | 2 倍 | `8` |
| `x3` | 3 倍 | `12` |
| `x4` | 4 倍 | `16` |
| `x5` | 5 倍 | `20` |
| `x6` | 6 倍 | `24` |
| `x7` | 7 倍 | `28` |
| `x8` | 8 倍 | `32` |
| `x9` | 9 倍 | `36` |
| `x10` | 10 倍 | `40` |
## 媒体查询断点
| 名称 | 说明 | 值 |
| ------------- | ------ | ------ |
| `phone` | 手机 | `0` |
| `tablet` | 平板 | `768` |
| `largeTablet` | 大平板 | `1024` |
## 通用颜色
| 名称 | 说明 | 值 |
| ------------- | ---------- | ------------- |
| `transparent` | 透明 | `transparent` |
| `white` | 白色 | `#FFFFFF` |
| `black` | 黑色 | `#000000` |
| `func50` | 功能色 0 | `#FBF5F5` |
| `func100` | 功能色 1 | `#FFF7E3` |
| `func200` | 功能色 2 | `#FFD21D` |
| `func300` | 功能色 3 | `#52C41A` |
| `func400` | 功能色 4 | `#1890FF` |
| `func500` | 功能色 5 | `#F86E21` |
| `func600` | 功能色 6 | `#F4333C` |
| `func700` | 保留功能色 | `transparent` |
| `func800` | 保留功能色 | `transparent` |
| `func900` | 保留功能色 | `transparent` |
**我们设置了`func700`/ `func800`/ `func900`作为保留功能色,方便开发者进行扩展**
## 亮色模式颜色(继承通用颜色)
| 名称 | 说明 | 值 |
| ------------- | ---------- | ----------------------- |
| `primary50` | 主色 | `#E5F1FF` |
| `primary100` | 主色 | `#3AA3FF` |
| `primary200` | 主色 | `#005DFF` |
| `primary300` | 主色 | `rgba(0, 93, 255, 0.7)` |
| `primary400` | 主色 | `rgba(0, 93, 255, 0.4)` |
| `primary500` | 保留主色 | `transparent` |
| `primary600` | 保留主色 | `transparent` |
| `primary700` | 保留主色 | `transparent` |
| `primary800` | 保留主色 | `transparent` |
| `primary900` | 保留主色 | `transparent` |
| `gray50` | 中性色 | `#F5F5F5` |
| `gray100` | 中性色 | `#E5E5E5` |
| `gray200` | 中性色 | `#CCCCCC` |
| `gray300` | 中性色 | `#999999` |
| `gray400` | 中性色 | `#666666` |
| `gray500` | 中性色 | `#333333` |
| `gray600` | 中性色 | `rgba(0, 0, 0, 0.4)` |
| `gray700` | 中性色 | `rgba(0, 0, 0, 0.04)` |
| `gray800` | 保留中性色 | `transparent` |
| `gray900` | 保留中性色 | `transparent` |
| `background` | 背景色 | 同`gray50` |
| `mask` | 遮罩 | 同`gray600` |
| `border` | 边框 | 同`gray200` |
| `icon` | 图标 | 同`gray300` |
| `disabled` | 禁用 | 同`gray200` |
| `text` | 文本 | 同`gray500` |
| `text_active` | 当前文本 | 同`white` |
**我们设置了`primary600`/ `primary700`/ `primary800`/ `primary900`作为保留主色, `gray800` / `gray900`作为保留中性色,方便开发者进行扩展**
## 暗色模式颜色(继承通用颜色)
| 名称 | 说明 | 值 |
| ------------- | ---------- | --------------------------- |
| `primary50` | 主色 | `rgba(0, 93, 255, 0.3)` |
| `primary100` | 主色 | `#3AA3FF` |
| `primary200` | 主色 | `#005DFF` |
| `primary300` | 主色 | `rgba(0, 93, 255, 0.7)` |
| `primary400` | 主色 | `rgba(0, 93, 255, 0.4)` |
| `primary500` | 保留主色 | `transparent` |
| `primary600` | 保留主色 | `transparent` |
| `primary700` | 保留主色 | `transparent` |
| `primary800` | 保留主色 | `transparent` |
| `primary900` | 保留主色 | `transparent` |
| `gray50` | 中性色 | `#131C22` |
| `gray100` | 中性色 | `rgba(255, 255, 255, 0.15)` |
| `gray200` | 中性色 | `rgba(255, 255, 255, 0.25)` |
| `gray300` | 中性色 | `rgba(255, 255, 255, 0.4)` |
| `gray400` | 中性色 | `rgba(255, 255, 255, 0.6)` |
| `gray500` | 中性色 | `rgba(255, 255, 255, 0.8)` |
| `gray600` | 中性色 | `rgba(0, 0, 0, 0.4)` |
| `gray700` | 中性色 | `rgba(0, 0, 0, 0.04)` |
| `gray800` | 保留中性色 | `transparent` |
| `gray900` | 保留中性色 | `transparent` |
| `background` | 背景色 | 同`gray50` |
| `mask` | 遮罩 | 同`gray600` |
| `border` | 边框 | 同`gray400` |
| `icon` | 图标 | 同`gray300` |
| `disabled` | 禁用 | 同`gray300` |
| `text` | 文本 | 同`gray500` |
| `text_active` | 当前文本 | 同`white` |
## TextVariant
| 名称 | 字体大小(fontSize) | 字重(fontWeight) | 行高(lineHeight) |
| ---- | ------------------ | ---------------- | ---------------- |
| `h0` | 24 | `bold` | 39 |
| `h1` | 18 | `500` | 25 |
| `h2` | 16 | `500` | 22 |
| `h3` | 14 | `500` | 19 |
| `h4` | | | |
| `h5` | | | |
| `h6` | | | |
| `h7` | | | |
| `h8` | | | |
| `h9` | | | |
| `p0` | 16 | | 22 |
| `p1` | 14 | | 19 |
| `p2` | 12 | | 16 |
| `p3` | 10 | | 14 |
| `p4` | | | |
| `p5` | | | |
| `p6` | | | |
| `p7` | | | |
| `p8` | | | |
| `p9` | | | |
| `d0` | 24 | | 28 |
| `d1` | 18 | | 21 |
| `d2` | 12 | | 14 |
| `d3` | | | |
| `d4` | | | |
| `d5` | | | |
| `d6` | | | |
| `d7` | | | |
| `d8` | | | |
| `d9` | | | |
**我们设置了`h4`/`h5`/`h6`/`h7`/`h8`/`h9`/`p4`/`p5`/`p6`/`p7`/`p8`/`p9`/`d3`/`d4`/`d5`/`d6`/`d7`/`d8`/`d9`作为保留的字体样式,方便开发者进行扩展**
## 如何复写应用主题
想要复写应用主题很简单,只需要如下操作就可以实现:
### 1. 在应用中定义你自己的主题颜色文件:
```ts | pure
import { theme, Theme } from '@td-design/react-native';
export const lightTheme: Theme = {
...theme.lightTheme,
colors: {
...theme.lightTheme.colors,
// 复写需要覆盖的颜色
},
};
export const darkTheme: Theme = {
...theme.darkTheme,
colors: {
...theme.darkTheme.colors,
// 复写需要覆盖的颜色
},
};
```
### 2. 把自定义主题注入到`app.tsx`里的`ThemeProvider`里:
```jsx | pure
// 其他import
import { ThemeProvider } from '@td-design/react-native';
import { lightTheme, darkTheme } from './theme';
export default () => {
// 其他代码
return <ThemeProvider theme={lightTheme}>{/** 其他Provider */}</ThemeProvider>;
};
```
### 3. 实现亮色模式和暗色模式切换
```jsx | pure
// 其他import
import { ThemeProvider } from '@td-design/react-native';
import { lightTheme, darkTheme } from './theme';
export default () => {
const [dark, setDark] = useState(false);
// 其他代码
return (
<ThemeProvider theme={dark ? darkTheme : lightTheme}>
{/** 把setDark方法通过context或者其他全局变量的方式传递到应用里在需要的地方调用即可。 */}
{/** 其他Provider */}
</ThemeProvider>
);
};
```
================================================
FILE: docs/react-perf.md
================================================
---
nav:
title: React应用开发性能优化手段
path: /faq
group:
title: 开发常见问题
path: /
order: 2
---
# React 应用开发常见性能优化手段
**性能优化应该遵循的基本法则:将变的部分和不变的部分分离。**
- 变的部分:
- state
- props
- context
`props`和`context`都是基于`state`演变过来的。父组件的`state`传给子组件,就成为了子组件的`props`; 父组件的`state`传到了`context`里,就成为一个子孙组件的`context`了。
## 1. state 尽可能地下放到子组件
```jsx | pure
import { useState } from 'react';
function ExpensiveCpn() {
let now = performance.now();
while (performance.now() - now < 100) {}
console.log('耗时的组件 render');
return <p>耗时的组件</p>;
}
export default function App() {
const [num, updateNum] = useState(0);
return (
<div>
<input value={num} onChange={e => updateNum(+e.target.value)} />
<p>num is {num}</p>
<ExpensiveCpn />
</div>
);
}
```
以上面的代码为例,当`num`发生变化的时候,`ExpensiveCpn`就会被重新渲染。因为`num`是`App`组件的 state,它的改变会让`App`组件重新渲染,`ExpensiveCpn`作为`App`的子组件,自然也就重新渲染了。
所以,我们应该将`num`下放到跟`ExpensiveCpn`不相关的子组件中:
```jsx | pure
import { useState } from 'react';
function ExpensiveCpn() {
let now = performance.now();
while (performance.now() - now < 100) {}
console.log('耗时的组件 render');
return <p>耗时的组件</p>;
}
function Input() {
const [num, updateNum] = useState(0);
return (
<>
<input value={num} onChange={e => updateNum(+e.target.value)} />
<p>num is {num}</p>
</>
);
}
export default function App() {
return (
<div>
<Input />
<ExpensiveCpn />
</div>
);
}
```
如上所示,现在`num`状态位于`Input`组件中,它的改变只会引起`Input`组件自身的重新渲染,而不会引起`ExpensiveCpn`的重新渲染。
假如在`App`里面用到了`num`这个`state`,那我们又能怎么优化代码呢? 答案是我们可以借助`children`来实现:
优化前的代码:
```jsx | pure
import { useState } from 'react';
function ExpensiveCpn() {
let now = performance.now();
while (performance.now() - now < 100) {}
console.log('耗时的组件 render');
return <p>耗时的组件</p>;
}
export default function App() {
const [num, updateNum] = useState(0);
return (
<div title={num + ''}>
<input value={num} onChange={e => updateNum(+e.target.value)} />
<p>num is {num}</p>
<ExpensiveCpn />
</div>
);
}
```
优化后的代码:
```jsx | pure
import { ReactNode, useState } from 'react';
function ExpensiveCpn() {
let now = performance.now();
while (performance.now() - now < 100) {}
console.log('耗时的组件 render');
return <p>耗时的组件</p>;
}
function InputWrapper({ children }: { children: ReactNode }) {
const [num, updateNum] = useState(0);
return (
<div title={num + ''}>
<input value={num} onChange={e => updateNum(+e.target.value)} />
<p>num is {num}</p>
{children}
</div>
);
}
export default function App() {
return (
<InputWrapper>
<ExpensiveCpn /> // <- 把`ExpensiveCpn`做为`InputWrapper`的`children`
</InputWrapper>
);
}
```
## 2. 避免不必要的 re-render
以`FlatList.renderItem`的使用为例:
```jsx | pure
const renderItem = useCallback(
({ item: { node } }) => {
const slug = node.slug;
const handlePress = () => {
push('SignedOutAssetScreen', { assetSlug: slug });
};
return <MarketCell onPress={handlePress} key={node.uuid} asset={node} />;
},
[push]
);
// const MarketCell = memo((props) => ...)
```
上面的代码,即便是`renderItem`方法被`useCallback`包裹了,但`MarketCell`组件还是会重新渲染。因为`useCallback`里面定义的函数每次都会重新创建,导致`MarketCell`组件重新渲染。
所以解决办法也很简单,我们只要把`handlePress`方法用`useCallback`包裹起来,就不会重新创建,这样就不会重新渲染了。同时我们应该把整个`renderItem`里面的函数封装成一个自定义组件:
```jsx | pure
const Item = memo(({ asset }: { asset: MarketCellProps['asset'] & { slug: string } }) => {
const slug = asset.slug;
// 将push方法从renderItem的依赖,转移到自定义组件内部
const { push } = useStackNavigation();
// handlePress 不会被重新创建
const handlePress = useCallback(() => {
push('SignedOutAssetScreen', { assetSlug: slug });
}, [slug, push]);
return <MarketCell onPress={handlePress} asset={asset} />;
});
// renderItem 返回一个组件
const renderItem = useCallback(({ item: { node } }) => {
return <Item key={node.uuid} asset={node} />;
}, []);
```
## 3. 使用`useMemo`来缓存计算耗时的结果
如果在一个组件里面会调用一个计算特别耗时的函数,那么我们应该使用`useMemo`来缓存计算结果,以避免重复计算。
```jsx | pure
// 避免这样做
function Component(props) {
const calcResult = heavyCalculation(props.item);
return <AnotherComponent value={calcResult} />;
}
// 只有 `props.item` 改变时someProp的值才会被重新计算
function Component(props) {
const calcResult = useMemo(() => heavyCalculation(props.item), [props.item]);
return <AnotherComponent value={calcResult} />;
}
```
## 4. 避免使用内联对象和内联函数
使用内联对象时,react 会在每次渲染时重新创建对此对象的引用,这会导致接收此对象的组件将其视为不同的对象。因此,该组件对于 prop 的浅层比较始终返回 false,导致组件一直重新渲染。
```jsx | pure
function Component(props) {
return <AnotherComponent style={{ margin: 0 }} {...props} />;
}
```
- 如果这个对象跟 props 无关,那么我们可以把它抽取出作为一个变量定义在组件外部;
- 如果这个对象跟 props 有关,那么我们可以用`useMemo`帮我们缓存这个对象,以避免重复创建。
```jsx | pure
// 跟props无关
const style = { margin: 0 };
function Component(props) {
return <AnotherComponent style={style} {...props} />;
}
// 跟props有关
function Component(props) {
const style = useMemo(() => ({ margin: props.margin }), [props.margin]);
return <AnotherComponent style={style} {...props} />;
}
```
内联函数也是一样,组件每次渲染时都会创建一个新的引用。这时我们可以用`useCallback`进行包裹,以避免重复创建。
```jsx | pure
// bad
function Component(props) {
return (
<AnotherComponent
{...props}
onPress={() => {
// do something...
}}
/>
);
}
// good
function Component(props) {
const handlePress = useCallback(() => {
// do something...
}, []);
return <AnotherComponent {...props} onPress={handlePress} />;
}
```
**推荐使用[ahooks/useMemoizedFn](https://ahooks-next.surge.sh/zh-CN/hooks/use-memoized-fn),它会自动帮你缓存函数的返回值,并且可以接受一个参数,用于指定缓存的依赖。**
## 5. 遍历生成组件时使用 key
老生常谈的话题,这也是在渲染列表时必须要做的事儿。可以讨论的是到底可不可以用`index`作为 key 呢?首先,官方文档里面推荐的使用列表项里面自带一个能够唯一标识的属性作为 key;但是,官网文档里面并没有说不能用 index 作为 key,[官方文档链接](https://reactjs.org/docs/lists-and-keys.html)。只是提醒说如果列表项会改变的情况下不能用 index 作为 key。
所以,我们如果能够保证列表项不会改变,那么我们就可以用`index`作为 key。
```jsx | pure
const todoItems = todos.map(todo => (
// 优先使用todo里面能唯一标识这条数据的属性作为key
<li key={todo.id}>{todo.text}</li>
));
// 或者
const todoItems = todos.map((todo, index) => (
// 用index作为key需要保证todos不会改变
<li key={index}>{todo.text}</li>
));
```
## 6. 延迟加载不是立即需要的组件
常见做法:
- 使用`React.lazy`,在组件加载时才加载组件;
```jsx | pure
const Component = React.lazy(() => import('./Component'));
```
- 基于路由进行代码分割,如`react-router` / `umi`
- 组件按需加载, 配合插件`babel-plugin-import`使用
```jsx | pure
import { Button } from 'antd';
```
## 7. Context 性能优化
跨层级传递数据,通常会使用 React Context 作为媒介,hooks 提出之后,使用 context 前所未有的方便,但同样地,context payload 变更会触发所有用到了这个 Context 的组件重新渲染,即使这个组件用到的那部分数据没有发生变化。
```jsx | pure
import React, { useContext, useState } from 'react';
const ThemeContext = React.createContext();
export function ChildNonTheme() {
console.log('不关心皮肤的子组件渲染了');
return <div>我不关心皮肤,皮肤改变的时候别让我重新渲染!</div>;
}
export function ChildWithTheme() {
const theme = useContext(ThemeContext);
return <div>我是有皮肤的哦~ {theme}</div>;
}
export default function App() {
const [theme, setTheme] = useState('light');
const onChangeTheme = () => setTheme(theme === 'light' ? 'dark' : 'light');
return (
<ThemeContext.Provider value={theme}>
<button onClick={onChangeTheme}>改变皮肤</button>
<ChildWithTheme />
<ChildNonTheme />
</ThemeContext.Provider>
);
}
```
上面的代码中,`theme`发生改变的时候,会让`ChildNonTheme`组件也跟着被重新渲染(虽然它压根儿就不关心 theme,但谁让它在 Context 下呢...)
怎么解这个问题呢?我们可以巧妙地利用`children`来解决。
```jsx | pure
import React, { useContext, useState } from 'react';
const ThemeContext = React.createContext();
function ChildNonTheme() {
console.log('不关心皮肤的子组件渲染了');
return <div>我不关心皮肤,皮肤改变的时候别让我重新渲染!</div>;
}
function ChildWithTheme() {
const theme = useContext(ThemeContext);
return <div>我是有皮肤的哦~ {theme}</div>;
}
// 定义一个组件,把`children`传进去
function ThemeApp({ children }) {
const [theme, setTheme] = useState('light');
const onChangeTheme = () => setTheme(theme === 'light' ? 'dark' : 'light');
return (
<ThemeContext.Provider value={theme}>
<button onClick={onChangeTheme}>改变皮肤</button>
{children}
</ThemeContext.Provider>
);
}
export default function App() {
return (
<ThemeApp>
{/* 这两个组件作为ThemeApp组件的chilren传进去 */}
<ChildWithTheme />
<ChildNonTheme />
</ThemeApp>
);
}
```
对`ThemeApp`组件而言, `ChildWithTheme`和`ChildNonTheme`是作为`children`传进来的,也就意味着`ThemeApp`组件在重新渲染时,是不会对 children 造成影响的。可以直接复用。
## 8. 多用 Fragment
react 规定返回多个元素必须要有一个父元素,这样一来就会导致应用中多出现了很多无用的父元素,这样会导致性能问题。
```jsx | pure
function Component() {
return (
<div>
<h1>Hello world!</h1>
<h1>Hello there!</h1>
<h1>Hello there again!</h1>
</div>
);
}
```
我们可以使用`Fragment`或者`<></>`来避免创建不必要的元素
```jsx | pure
function Component() {
return (
<>
<h1>Hello world!</h1>
<h1>Hello there!</h1>
<h1>Hello there again!</h1>
</>
);
}
```
## 9. 正确认识 props
我们知道,父组件传值给子组件有两种方式,第一种是通过`props`,第二种是通过`context`。
但是,即便一个子组件不接收任何`props`,这并不意味着它的 props 不存在,相反,props 一直存在,这种情况下它是一个`{}`。
React 中是默认使用全等来判断`props`是否相等的,所以即便是`{}`也不会被认为是相等的。这就导致 react 的性能优化没有被命中,从而子组件还是发生了重新渲染。
```jsx | pure
import React, { useState, useContext } from 'react';
const numCtx = React.createContext < number > 0;
const updateNumCtx = React.createContext < React.Dispatch < number >> (() => {});
function Button() {
const updateNum = useContext(updateNumCtx); // updateNum是一个dispatch,它是不变的。
console.log('btn render');
return <button onClick={() => updateNum(Math.random())}>产生随机数</button>;
}
function Show() {
const num = useContext(numCtx); // 用到了context里面的num,所以num改变时会触发重新渲染。
return <p>num is: {num}</p>;
}
const Middle = () => {
return (
<>
<Button />
<Show />
</>
);
};
export default function App() {
const [num, updateNum] = useState(0);
return (
<numCtx.Provider value={num}>
<updateNumCtx.Provider value={updateNum}>
<Middle />
</updateNumCtx.Provider>
</numCtx.Provider>
);
}
```
如上代码所示,虽然 Middle 不接收任何 props,但是实际上 props 是{}, react 在用全等比较的时候,{}和{}不会认为是相等的,所以 APP 组件发生重新渲染的时候,Middle 组件也会跟着重新渲染,从而使它的 Button 子组件也跟着重新渲染。
我们可以使用`memo`或者`useMemo`来解决 Middle 组件的重新渲染问题。
```jsx | pure
// 使用memo
const Middle = React.memo(() => {
return (
<>
<Button />
<Show />
</>
);
});
// 使用useMemo
const Middle = () => {
return useMemo(
() => (
<>
<Button />
<Show />
</>
),
[]
);
};
```
================================================
FILE: docs/rn-faq.md
================================================
---
nav:
title: RN应用开发常见问题
path: /faq
group:
title: 开发常见问题
path: /
order: 0
---
# RN 应用开发常见问题
## 1. 自定义图标
## 2. 集成极光推送
## 3. 集成热更新
### 安装与注册 CodePush
- 在终端输入`npm install -g code-push-cli`
- 安装完毕后,输入`code-push -v`查看版本,如看到版本号则代表安装成功
- 在终端输入 `code-push register`,会在浏览器自动打开注册页面让你选择授权账号,授权通过之后 CodePush 会告诉你 access key,复制此可以到终端即可完成注册
- 然后输入`code-push login`进行登录,登录成功会提示 successfully logged-in
- 为了让 CodePush 服务器知道你的 app,我们需要向它注册 app:在终端输入`code-push app add <appName> <os> react-native`,如: `code-push app add animalHusbandryCollectorApp android react-native`。一般`appName`就是项目中`app.json`文件中的`name`。注册完成之后会返回一套 `deploymentKey`,请记录下该 key,会在后面步骤中用到。如果 android 和 iOS 都需要热更新功能,需要分别生成一套 key
```code
code-push app add animalHusbandryCollectorApp-android android react-native
code-push app add animalHusbandryCollectorApp-ios ios react-native
```
### 项目中安装依赖
```code
yarn add react-native-code-push
```
### Android 配置
- `android/app/build.gradle。把上面生成的 android 的 deploymentKey 放到下面 buildTypes 中`
```code
apply from: "../../node_modules/react-native/react.gradle"
apply from: "../../node_modules/react-native-code-push/android/codepush.gradle" // <- 添加这行
android {
...
signingConfigs {
debug {
storeFile file('debug.keystore')
storePassword 'android'
keyAlias 'androiddebugkey'
keyPassword 'android'
}
releaseStaging {
storeFile file('staging.keystore')
storePassword 'android'
keyAlias 'androidstagingkey'
keyPassword 'android'
}
release {
if (project.hasProperty('RELEASE_STORE_FILE')) {
storeFile file(RELEASE_STORE_FILE)
storePassword RELEASE_STORE_PASSWORD
keyAlias RELEASE_KEY_ALIAS
keyPassword RELEASE_KEY_PASSWORD
}
}
}
buildTypes {
debug {
...
// Note: CodePush updates should not be tested in Debug mode as they are overriden by the RN packager. However, because CodePush checks for updates in all modes, we must supply a key.
resValue "string", "CodePushDeploymentKey", '""'
...
}
releaseStaging.initWith(release)
releaseStaging {
...
signingConfig signingConfigs.releaseStaging
resValue "string", "CodePushDeploymentKey", '"<INSERT_STAGING_KEY>"'
// Note: It is a good idea to provide matchingFallbacks for the new buildType you create to prevent build issues
// Add the following line if not already there
matchingFallbacks = ['release']
...
}
release {
...
resValue "string", "CodePushDeploymentKey", '"<INSERT_PRODUCTION_KEY>"'
...
}
}
...
}
```
- `android/settings.gradle`
```code
include ':app', ':react-native-code-push'
project(':react-native-code-push').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-code-push/android/app')
```
- `MainApplication.java`
```java
import com.microsoft.codepush.react.CodePush;
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
...
@Override
protected String getJSBundleFile() {
return CodePush.getJSBundleFile();
}
};
```
### IOS 配置
```code
cd ios && pod install
```
- `AppDelegate.m`
```code
#import <CodePush/CodePush.h>
// 把下面这行代码:
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
// 改成:
return [CodePush bundleURL];
// 最后的sourceURLForBridge方法如下:
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
return [CodePush bundleURL];
#endif
}
```
- Info.plist 执行命令:`appcenter codepush deployment list -a <ownerName>/<appName> -k`查看 server 上的 deployment key 在`Info.plist`文件里面新建一个键值对:
```code
<key>CodePushDeploymentKey</key>
<string>Your Deployment Key</string>
```
- 打开 xcode,选中自己的项目,选中**project**下的项目名称,选择`Info`标签页,点击`Configurations`下的加号,选择`Duplicate "Release" Configuration` 命名为`Staging`.
<center>
<img src="https://td-dev-public.oss-cn-hangzhou.aliyuncs.com/maoyes-app/1644829195776339205.png" alt="数组插入" width="400" />
</center>
- 选择`Build Settings`标签页,点击加号,选择`Add User Defined Setting`用.xcodeproj 打开项目,先把 Debug/Release/Staging 的值配置好: Debug: `$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)` Release: `$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)` Staging: `$(BUILD_DIR)/Release$(EFFECTIVE_PLATFORM_NAME)` 然后把配置名称改成:`CONFIGURATION_BUILD_DIR`,然后再切换回.xcworkspace 打开项目
<center>
<img src="https://td-dev-public.oss-cn-hangzhou.aliyuncs.com/maoyes-app/1644829210496443837.png" alt="数组插入" width="400" />
</center>
- 再次选择`Add User Defined Setting`,输入`CODEPUSH_KEY`,通过命令`appcenter codepush deployment list -a <ownerName>/<appName> -k`把`Staging`和`Production`的 deployment key 分别填到`CODEPUSH_KEY`下匹配的属性里。
- 打开`Info.plist`,把之前的`CodePushDeploymentKey`的值改成`$(CODEPUSH_KEY)`
- 重新运行`pod install`
- 运行代码,能够正常启动模拟器,就表示集成成功
### 代码中使用
1. 在项目中增加文件(可以直接拷贝):`scripts/codePush/index.js`
```code
'use strict';
const { makePush } = require('./makePush');
const inquirer = require('inquirer');
const ENV_PARAMS = {
message: '请选择您要部署环境',
type: 'list',
name: 'environment',
items: {
1: 'Production',
2: 'Staging'
},
choices: ['Staging', 'Production']
};
const PLATFORM_PARAMS = {
message: '请选择您要部署的平台',
type: 'list',
name: 'platform',
choices: ['both', 'android', 'ios'],
default: 'both'
};
const VERSION_PARAMS = {
name: 'version',
type: 'input',
message: '请输入要更新的目标版本号'
};
const DES_PARAMS = {
name: 'description',
type: 'editor',
message: '请输入本次修改的描述'
};
const MANDATORY_PARAMS = {
message: '本次更新是否为强制更新',
type: 'list',
name: 'mandatory',
choices: ['true', 'false'],
default: 'false',
};
const QUESTIONS = [ENV_PARAMS, PLATFORM_PARAMS, DES_PARAMS, VERSION_PARAMS, MANDATORY_PARAMS];
function main (){
inquirer.prompt(QUESTIONS).then(option => {
const { description, environment, platform, version, mandatory } = option;
if (!description) {
console.error('你没有输入改动描述');
process.exit(1);
}
makePush({
environment,
platform,
version,
description,
mandatory,
});
});
}
main();
```
2. 在项目中增加文件:`scripts/codePush/makePush.js`
`注意:需要把下面的的包的名字给替换掉 thundersdata/rn-template-android ---> ownerName/在之前步骤注册的对应平台的app的名字,如果android和ios都需要热更新,则都要替换 `
```code
'use strict';
const shell = require('shelljs');
function makePush({ environment, platform, description, mandatory, version }) {
let androidCmd = `appcenter codepush release-react -a thundersdata/rn-template-android -d ${environment} --description "${description}" -m "${mandatory}"`;
let iosCmd = `appcenter codepush release-react -a thundersdata/rn-template-ios -d ${environment} --description "${description}" -m "${mandatory}"`;
if (version) {
androidCmd += ` -t "${version}"`;
iosCmd += ` -t "${version}"`;
}
let finalCmd = '';
if (platform === 'both') {
finalCmd = `${androidCmd} && ${iosCmd}`;
} else if (platform === 'android') {
finalCmd = androidCmd;
} else {
finalCmd = iosCmd;
}
shell.exec(finalCmd);
}
module.exports = {
makePush,
};
```
3. 在项目中添加文件:`hooks/useCodePush.ts`
```code
import codePush, { DownloadProgress } from 'react-native-code-push';
import { useMemoizedFn, useSafeState } from '@td-design/rn-hooks';
import { useRef } from 'react';
import { Modal, Toast } from '@td-design/react-native';
export function useCodePush() {
const key = useRef(-1);
const [progress, setProgress] = useSafeState<string>();
// eslint-disable-next-line complexity
const codePushStatusDidChange = async (syncStatus: codePush.SyncStatus) => {
switch (syncStatus) {
case codePush.SyncStatus.CHECKING_FOR_UPDATE:
// 0 - 正在查询CodePush服务器以进行更新。
console.info('[CodePush] Checking for update.');
break;
case codePush.SyncStatus.AWAITING_USER_ACTION:
// 1 - 有可用的更新,并且向最终用户显示了一个确认对话框。(仅在updateDialog使用时适用)
console.info('[CodePush] Awaiting user action.');
break;
case codePush.SyncStatus.DOWNLOADING_PACKAGE:
// 2 - 正在从CodePush服务器下载可用更新。
console.info('[CodePush] Downloading package.');
break;
case codePush.SyncStatus.INSTALLING_UPDATE:
// 3 - 已下载一个可用的更新,并将其安装。
console.info('[CodePush] Installing update.');
break;
case codePush.SyncStatus.UP_TO_DATE:
// 4 - 应用程序已配置的部署完全最新。
console.info('[CodePush] App is up to date.');
break;
case codePush.SyncStatus.UPDATE_IGNORED:
// 5 该应用程序具有可选更新,最终用户选择忽略该更新。(仅在updateDialog使用时适用)
console.info('[CodePush] User cancelled the update.');
break;
case codePush.SyncStatus.UPDATE_INSTALLED:
// 6 - 安装了一个可用的更新,它将根据 SyncOptions 中的 InstallMode指定在 syncStatusChangedCallback 函数返回后立即或在下次应用恢复/重新启动时立即运行。
console.info('[CodePush] Installed update.');
break;
case codePush.SyncStatus.SYNC_IN_PROGRESS:
// 7 - 正在执行的 sync 操作
console.info('[CodePush] Sync already in progress.');
break;
case codePush.SyncStatus.UNKNOWN_ERROR:
// -1 - 同步操作遇到未知错误。
console.info('[CodePush] An unknown error occurred.');
break;
}
};
const codePushDownloadDidProgress = (progress: DownloadProgress) => {
const curPercent = ((progress.receivedBytes / progress.totalBytes) * 100).toFixed(0);
console.info('[CodePushUtils] Downloading Progress', `${curPercent}%`);
setProgress(`${curPercent}%`);
};
const syncImmediate = async () => {
codePush.sync(
{
updateDialog: {
// 是否显示更新描述
appendReleaseDescription: true,
// 更新描述的前缀。 默认为"Description"
descriptionPrefix: '\n\n更新内容:\n',
// 强制更新按钮文字,默认为continue
mandatoryContinueButtonLabel: '立即更新',
// 强制更新时的信息. 默认为"An update is available that must be installed."
mandatoryUpdateMessage: '必须更新后才能使用',
// 非强制更新时,按钮文字,默认为"ignore"
optionalIgnoreButtonLabel: '稍后',
// 非强制更新时,确认按钮文字. 默认为"Install"
optionalInstallButtonLabel: '后台更新',
// 非强制更新时,检查到更新的消息文本
optionalUpdateMessage: '有新版本了,是否更新?',
// Alert窗口的标题
title: '更新',
},
installMode: codePush.InstallMode.IMMEDIATE,
},
codePushStatusDidChange,
codePushDownloadDidProgress,
);
};
/** 个人中心页面用 */
const checkForUpdate = async () => {
key.current = Toast.submitting({ content: '检查中...' });
const update = await codePush.checkForUpdate();
Toast.remove(key.current);
if (!update) {
Modal.alert({
title: '提示',
content: '当前版本没有发现需要安装的更新',
});
} else {
syncImmediate();
}
};
/** app启动时不需要弹窗提示 */
const checkForUpdateWithoutMessage = async () => {
const update = await codePush.checkForUpdate();
if (!update) {
// 如果没有在app启动时调用sync,则需要显式调用notifyAppReady,否则插件将认为更新失败并回滚回以前版本
codePush.notifyAppReady();
} else {
syncImmediate();
}
};
return {
progress,
checkForUpdate: useMemoizedFn(checkForUpdate),
checkForUpdateWithoutMessage: useMemoizedFn(checkForUpdateWithoutMessage),
sync: syncImmediate,
};
}
```
4. `由于热更新时我们只能针对特定的版本进行热更新,所以为了保证后续用户手机上 app 版本的一致性,我们推荐使用强更新和热更新相结合的方式。当有版本更新时,我们推荐使用强更新功能,当需要对某个版本进行 bug 修复时,我们推荐使用热更新功能。`
`注意:热更新要求app版本必须是三段式的,如: 1.1.0。`
添加文件:`hooks/useCheckUpdate.ts`
```code
import { useRef } from 'react';
import { helpers, Image, Modal, Toast } from '@td-design/react-native';
import { ForceUpdateEnum } from 'enums';
import RNFetchBlob from 'rn-fetch-blob';
import { PERMISSIONS, request } from 'react-native-permissions';
import { useCustomRequest } from './useCustomRequest';
import { useToast } from './useToast';
import { useCodePush } from './useCodePush';
import { Linking, Platform } from 'react-native';
const { px } = helpers;
export function useCheckUpdate(showMessage = false) {
const { toastFail } = useToast();
const { checkForUpdate, checkForUpdateWithoutMessage } = useCodePush();
const key = useRef(-1);
const appUpdate = async (androidDownloadUrl: string, appStoreUrl: string, version: string) => {
if (Platform.OS === 'android') {
const result = await request(PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE);
if (result !== 'granted') {
toastFail('请授权APP下载文件的权限');
return;
}
key.current = Toast.submitting({ content: '下载中...' });
const downloadPath = RNFetchBlob.fs.dirs.DownloadDir + `/animalHusbandryCollectorApp-${version}.apk`;
const android = RNFetchBlob.android;
RNFetchBlob.config({
addAndroidDownloads: {
// 调起原生下载管理
useDownloadManager: true,
// 下载的安装包保存的名字
title: `animalHusbandryCollectorApp-${version}.apk`,
// 下载时候顶部通知栏的描述
description: '下载完成之后将会自动安装',
// 下载的文件格式
mime: 'application/vnd.android.package-archive',
// 下载完成之后扫描下载的文件
mediaScannable: true,
// 通知栏显示下载情况
notification: true,
// 下载地址
path: downloadPath,
},
})
.fetch('GET', androidDownloadUrl)
.then(res => {
android.actionViewIntent(res.path(), 'application/vnd.android.package-archive');
})
.catch(() => {
toastFail('下载失败');
})
.finally(() => {
Toast.remove(key.current);
});
} else {
Linking.canOpenURL(appStoreUrl)
.then(supported => {
if (!supported) {
console.warn('找不到对应的应用');
return false;
}
return Linking.openURL(appStoreUrl);
})
.catch(err => {
console.error('跳转到APP_STORE失败,失败原因:', err);
});
}
};
/**
* 根据版本号对比是否需要强制更新,默认版本号为三段 如:1.2.3
* 和后端约定:第一段和第二段为强制更新,第三段为非强制更新
* 如果是非强制更新的话,走热更新功能
*/
const { run: checkUpdate } = useCustomRequest(API.collector.appVersion.check.fetch, {
manual: true,
onSuccess: data => {
if (data?.forcedUpdate === ForceUpdateEnum.强制) {
Modal.alert({
icon: (
<Image
source={require('modules/offline/assets/modal_confirm.webp')}
style={{ width: px(55), height: px(55), marginTop: px(36) }}
/>
),
title: '版本更新',
content: '检测到有新版本请立即升级',
onPress: () => appUpdate(data?.downloadUrl ?? '', data?.appStoreUrl ?? '', data?.version ?? ''),
});
return;
}
// 热更新,判断是否需要显示检查更新的弹窗
if (showMessage) {
checkForUpdate();
} else {
checkForUpdateWithoutMessage();
}
},
});
return { checkUpdate };
}
```
配置文件:`android/app/src/main/AndroidManifest.xml`,否则 android apk 下载失败
```code
...
<!-- 文件权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
...
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.DOWNLOAD_COMPLETE"/> ---->添加这行
...
</intent-filter>
```
5. 添加 `npm scripts`
```code
{
"scripts": {
...
"codePush": "node scripts/codePush/index.js",
"android:staging": "yarn bundle && cd ./android && ./gradlew app:assembleReleaseStaging",
...
},
}
```
6. 在 `App.tsx`中使用
```code
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { NavigationContainer, DefaultTheme } from '@react-navigation/native';
import { hide as hideSplash } from 'react-native-bootsplash';
import { ThemeProvider } from '@td-design/react-native';
import { useMount } from '@td-design/rn-hooks';
import { getVersion } from 'react-native-device-info';
import { Stack } from './stacks';
import { authAtom } from 'atoms';
import { Fallback } from 'components';
import { lightTheme } from 'theme';
import { linking } from 'linking';
import { isSignedIn } from 'utils/auth';
import { useUpdateAtom } from 'jotai/utils';
import { useCheckUpdate } from 'hooks/useCheckUpdate';
export function App() {
const updateAuth = useUpdateAtom(authAtom);
const { checkUpdate } = useCheckUpdate();
useMount(() => {
const init = async () => {
// 判断用户是否已经登录
const signedIn = await isSignedIn();
updateAuth({ signedIn });
};
init().finally(async () => {
await hideSplash({ fade: true });
checkUpdate({ version: getVersion() });
});
});
return (
<SafeAreaProvider>
<ThemeProvider theme={lightTheme}>
<NavigationContainer linking={linking} fallback={<Fallback />} theme={DefaultTheme}>
<Stack />
</NavigationContainer>
</ThemeProvider>
</SafeAreaProvider>
);
}
```
7. `发布热更新:在项目下执行命令:yarn codePush 然后按照步骤执行即可`
## 4. 外链唤醒 APP
================================================
FILE: docs/screen.md
================================================
---
nav:
title: 大屏素材库
path: /screen
group:
title: 概要
order: 1
---
# 概要
大屏素材库概要,集成一些常见的大屏组件及图表组件,统一样式。
## 安装方式:
```sh
yarn add @td-design/lego
```
如果你要使用地图功能,请安装
```sh
yarn add @td-design/lego-map
```
如果你要使用视频播放功能,请安装
```sh
yarn add @td-design/lego-video
```
================================================
FILE: docs/structure/array.md
================================================
---
toc: menu
---
# 数组 - Array
## 什么是数组
数组是一种线性结构,是一个存储相同类型的变量所组成的一个有序集合,数组中每一个变量称为元素,每个元素都有一个对应的索引(索引从 0 开始)。数组还有另一个特点,就是在内存中是顺序存储的。 内存是由一个个连续的内存单元所组成的,每一个内存单元都对应一个地址,在内存单元中,有些被占用了,有些是空闲的。 数组中的每一个元素,都存储在内存单元中,并且这些内存单元之间紧密排列,既不能打乱存储顺序,也不能跳过某个内存单元进行存储。
> 线性表数据结构 从字面意思理解线性表数据结构是数据呈现线性存储的一种方式,有前后两个方向,数据呈一对一的关系,除头尾外,元素都是相连的。线性表数据结构还有链表、栈、队列。

## js 中的数组
基本上所有的语言都内置了数组这个结构,在 js 中也不例外。在 js 中数组是基于 对象来实现的,所有跟传统的数组有一些不同
典型的数组:
- 元素的数据类型相同
- 使用连续的内存存储
- 通过数字下标获取元素
js 中的数组
- 元素的数据类型可以不同
- 内存不一定是连续的(对象是随机存储的)
- 不能通过数字下标,而是通过字符串下标
- 数组可以有任何 key。
> 在内存中的存储不一定是连续的,可能根据不同环境的实现有不同的体现。在 V8 引擎中,数组有两种,一种是内存连续的快数组,一种是以堆栈存储的慢数组,快数组存储的是一些简单的数据,可以保证查询时候的性能,但是更耗费存储空间。慢数组牺牲了查询的时间节约了存储空间。引擎会根据数据的情况自动切换快慢数组。
## 数组的优缺点
### 数组的优点
由于数组定义的空间是连续的,数组可以很方便的进行遍历,同时也可以通过数组的下标进行访问例如:
```js
const a = [1, 2, 3, 4, 5, 6, 7, 8];
a[1]; // 2
```
> 同时由于数组连续的特性,遍历的时候有着一定的优势
### 数组的缺点
数组是有序的线性表,为了保证其有序,所以在对数组进行操作的时候会有更多的耗费,基本上对于数组的操作都是 O(n)复杂度的。
- 在数组中插入元素
如果我们希望在一个数组中的某一位置进行插入一个元素,由于数组是连续的,我们需要把插入位置给腾出来,需要把插入位置右侧的所有数据进行相应的后移一位,简单的说就是依次的把数组(i-1)位的值赋值给(i),再把元素赋值到指定索引上。
<center>
<img src="https://td-dev-public.oss-cn-hangzhou.aliyuncs.com/maoyes-app/1619015200254077566.png" alt="数组插入" width="400" />
</center>
```js
Array.prototype.insert = function (index, node) {
if (index < 0 || index > this.length) {
return undefined;
}
for (let i = this.length - 1; i >= index; i -= 1) {
this[i + 1] = this[i];
}
this[index] = element;
return this;
};
```
- 删除数组中元素
删除数组的逻辑跟插入类似
```js
Array.prototype.delete = function (index) {
if (index < 0 || index > this.length) {
return undefined;
}
for (let i = index; i < this.length; i += 1) {
this[i] = this[i + 1];
}
this.pop();
return this;
};
```
<center>
<img src="https://td-dev-public.oss-cn-hangzhou.aliyuncs.com/maoyes-app/1619015726909083485.png" alt="数组删除" width="400" />
</center>
我们把数组里所有的元素都左移了一位,但数组的长度依没有改变,这意味着数组中有额外的一个元素(值是 undefined)。在最后一次循环里,i+1 引用了数组里还未初始化的一个位置。在 Java、C/C+或 C#等一些语言里,这样写可能会抛出异常,因此不得不在 numbers.length-1 处停止循环。最后用 pop 方法将数组最后一位移除,也可以将数组所有不是 undefined 给赋值一个新数组。
## 多维数组
多维数组是数组的元素也为一个数组
```js
// 一个简单的多维数组
var arr = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
```
多维数组的扁平化
```js
// ES6 中提供的方法flat
let a = [1, [2, 3]];
a.flat(); // [1,2,3]
// 循环
let arr1 = [1, 2, [3], [1, 2, 3, [4, [2, 3, 4]]]];
function flatten(arr) {
while (arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
}
flatten(arr1); //[1, 2, 3, 1, 2, 3, 4, 2, 3, 4]
```
## 总结
js 原本没有真正的数组,因为 js 的数组可以存放不同类型的数据,不同数据类型存储所需要的空间大小不同,这就导致用来存放数组的内存地址不可能是连续的。所以 js 的数组是按照类似哈希映射的方法存储的,这种方式的数组读写操作相对低效。
数组是编程中极重要的一种数据结构,大部分语言都原生实现了数组数据结构。js 的数组更加灵活,而且原生实现了众多数组的操作方法,这些方法要经常使用务必做到烂熟于心。
================================================
FILE: docs/structure/index.md
================================================
---
title: 数据结构介绍
toc: menu
nav:
title: Structure
---
# 数据结构介绍
> Data dominates. If you've chosen the right data structures and organized things well, the algorithms will almost always be self-evident. Data structures, not algorithms, are central to programming. <br/>数据占主导。如果您选择了正确的数据结构并组织得当,那么这些算法几乎总是不言而喻的。数据结构而非算法是编程的核心。
================================================
FILE: docs/structure/linkedList.md
================================================
---
toc: menu
---
# 链表 - LinkedList
链表
================================================
FILE: jest.config.js
================================================
module.exports = {
preset: 'ts-jest/presets/js-with-ts',
testEnvironment: 'jsdom',
clearMocks: true,
coverageDirectory: 'coverage',
cacheDirectory: '.jest/cache',
testPathIgnorePatterns: ['/.history/'],
modulePathIgnorePatterns: ['<rootDir>/package.json'],
resetMocks: false,
setupFiles: ['raf/polyfill', './jest.setup.js'],
transform: {
'\\.ts?$': [
'ts-jest',
{
tsconfig: 'tsconfig.json',
},
],
},
transformIgnorePatterns: ['^.+\\.js$'],
collectCoverageFrom: [
'<rootDir>/**/src/**/*.{js,jsx,ts,tsx}',
'!**/demo/**',
'!**/example/**',
'!**/es/**',
'!**/lib/**',
'!**/dist/**',
],
moduleNameMapper: {
'^lodash-es$': 'lodash',
},
globals: {
'__DEV__': true,
}
};
================================================
FILE: jest.setup.js
================================================
jest.mock('react-native', () => {
return {
Platform: {
OS: 'ios',
Version: 123,
isTesting: true,
select: objs => objs['ios'],
},
BackHandler: {
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
},
AccessibilityInfo: {
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
isBoldTextEnabled: jest.fn(),
isGrayscaleEnabled: jest.fn(),
isInvertColorsEnabled: jest.fn(),
isReduceMotionEnabled: jest.fn(),
isReduceTransparencyEnabled: jest.fn(),
isScreenReaderEnabled: jest.fn(),
},
AppState: {
currentState: 'mock-currentState',
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
},
Keyboard: {
emit: jest.fn(),
addListener: jest.fn(),
},
};
});
================================================
FILE: package.json
================================================
{
"license": "Apache-2.0",
"name": "thundersdata-frontend",
"author": "陈杰 <chj_0507_dz@sina.com>",
"homepage": "https://github.com/thundersdata-frontend/td-design#readme",
"scripts": {
"preinstall": "npx only-allow pnpm",
"prepare": "husky install",
"build": "turbo run build",
"commit": "git-cz",
"test": "jest --passWithNoTests --detectOpenHandles --watch",
"coverage": "jest --coverage",
"prettier": "prettier --write --cache --cache-strategy metadata \"**/*.{tsx,ts,md}\"",
"doc:dev": "dumi dev",
"doc:build": "dumi build",
"changeset": "changeset",
"release": "pnpm build && changeset publish"
},
"config": {
"commitizen": {
"path": "node_modules/cz-customizable"
}
},
"lint-staged": {
"*.{ts,tsx,md}": [
"npm run prettier"
]
},
"devDependencies": {
"@changesets/changelog-github": "^0.4.8",
"@changesets/cli": "^2.26.2",
"@commitlint/cli": "^17.6.7",
"@commitlint/config-conventional": "^17.6.7",
"@testing-library/react-hooks": "^8.0.1",
"@trivago/prettier-plugin-sort-imports": "^4.2.0",
"@types/jest": "^29.5.3",
"@umijs/plugin-esbuild": "^1.4.2",
"antd": "^4.23.6",
"babel-plugin-import": "^1.13.6",
"commitizen": "^4.3.0",
"cz-customizable": "^7.0.0",
"dumi": "^1.1.48",
"file-loader": "^6.2.0",
"husky": "^8.0.3",
"jest": "^29.6.1",
"jest-environment-jsdom": "^29.6.1",
"lint-staged": "^13.2.3",
"prettier": "^3.0.0",
"prettier-plugin-packagejson": "^2.4.5",
"raf": "^3.4.1",
"react": "^17.0.0",
"react-native": "^0.72.9",
"react-test-renderer": "^17.0.0",
"ts-jest": "^29.1.1",
"ts-node": "^10.9.1",
"turbo": "^1.10.9",
"yorkie": "^2.0.0"
},
"packageManager": "pnpm@7.3.0",
"engines": {
"node": ">=16",
"pnpm": ">=7.0.0"
},
"resolutions": {
"@types/react": "17.0.43"
}
}
================================================
FILE: packages/cli/README.md
================================================
# `@td-design/cli`
> TODO: description
## Usage
```
const cli = require('@td-design/cli');
// TODO: DEMONSTRATE API
```
================================================
FILE: packages/cli/bin/index.js
================================================
#!/usr/bin/env node
require('../lib/index.js');
================================================
FILE: packages/cli/package.json
================================================
{
"name": "@td-design/cli",
"version": "2.6.2",
"description": "脚手架工具,用于创建项目,创建页面等功能",
"keywords": [
"thundersdata",
"frontend",
"cli",
"nodejs"
],
"author": "陈杰 <chenjie@thundersdata.com>",
"homepage": "https://github.com/thundersdata-frontend/td-design#readme",
"license": "Apache-2.0",
"files": [
"bin/",
"lib/"
],
"main": "lib/index.js",
"bin": {
"td-cli": "bin/index.js"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/thundersdata-frontend/td-design.git"
},
"scripts": {
"build": "tsc -p ./tsconfig.build.json"
},
"bugs": {
"url": "https://github.com/thundersdata-frontend/td-design/issues"
},
"gitHead": "a230ae795ff160ad5a251dc51dbe6aab56f6eb61",
"dependencies": {
"chalk": "^5.1.2",
"commander": "^9.4.1",
"download-git-repo": "^3.0.2",
"handlebars": "^4.7.7",
"inquirer": "^9.1.4",
"log-symbols": "^5.1.0",
"minimist": "^1.2.7",
"ora": "^6.1.2",
"prompt": "^1.3.0",
"semver": "^7.3.8"
},
"devDependencies": {
"@types/inquirer": "^9.0.2",
"typescript": "^4.8.4"
}
}
================================================
FILE: packages/cli/src/index.ts
================================================
import chalk from 'chalk';
import { program } from 'commander';
import fs from 'fs';
import inquirer from 'inquirer';
import setupApp from './setupApp';
import setupSpa from './setupSpa';
program
.version('1.0.0', '-v, --version')
.command('init <name>')
.action(name => {
if (!fs.existsSync(name)) {
validateProjectName(name);
inquirer
.prompt([
{
name: 'platform',
message: '请选择项目类型',
type: 'list',
choices: ['spa', 'app'],
},
{
name: 'branch',
message: '请选择项目分支',
type: 'input',
default: 'main',
},
])
.then((answers: { platform: string; branch: string }) => {
if (answers.platform === 'spa') {
setupSpa.init(name, answers.branch);
} else if (answers.platform === 'app') {
setupApp.init(name, answers.branch);
}
});
} else {
console.log(chalk.red('项目已存在'));
}
});
program.parse(process.argv);
/**
* 对项目名进行校验
* @param {*} name
*/
function validateProjectName(name: string) {
if (!String(name).match(/^[$A-Z_][0-9A-Z_$]*$/i)) {
console.error(
'"%s" is not a valid name for a project. Please use a valid identifier ' + 'name (alphanumeric).',
name
);
process.exit(1);
}
if (name === 'React') {
console.error('"%s" is not a valid name for a project. Please do not use the ' + 'reserved word "React".', name);
process.exit(1);
}
}
================================================
FILE: packages/cli/src/setupApp.ts
================================================
import chalk from 'chalk';
import download from 'download-git-repo';
import fs from 'fs';
import ora from 'ora';
import path from 'path';
import deleteOldDirs from './utils/deleteOldDirs';
import replaceProject from './utils/replaceProject';
import translateFilePath from './utils/translateFilePath';
import walk from './utils/walk';
export default {
init(projectName: string, branch: string) {
const repository = `thundersdata-frontend/rn-template#${branch}`;
console.log(chalk.green(`模板地址:${repository}`));
const spinner = ora('正在下载模板,请稍候...');
spinner.start();
download(repository, projectName, { clone: true }, (err: string) => {
if (err) {
spinner.fail();
console.log(chalk.red(err));
} else {
spinner.succeed();
const rootPath = path.resolve(projectName);
// 替换和生成所有重命名之后的文件和文件夹
walk(rootPath).forEach(absoluteFilePath => {
const relativeFilePath = path.relative(rootPath, absoluteFilePath);
const relativeRenamedPath = translateFilePath(relativeFilePath)
.replace(/rnTemplate/g, projectName)
.replace(/rntemplate/g, projectName.toLowerCase());
replaceProject(absoluteFilePath, path.resolve(rootPath, relativeRenamedPath), {
'Hello App Display Name': projectName,
rnTemplate: projectName,
rntemplate: projectName.toLowerCase(),
});
});
// 删除以前的旧的文件夹和文件夹下的内容
walk(rootPath).forEach(absoluteFilePath => {
if (fs.existsSync(absoluteFilePath) && fs.statSync(absoluteFilePath).isDirectory()) {
if (absoluteFilePath.includes('rnTemplate') || absoluteFilePath.includes('rntemplate')) {
deleteOldDirs(absoluteFilePath);
}
}
});
console.log(chalk.green('项目初始化完成'));
}
});
},
};
================================================
FILE: packages/cli/src/setupSpa.ts
================================================
import chalk from 'chalk';
import download from 'download-git-repo';
import ora from 'ora';
export default {
init: function (name: string, branch: string) {
const repository = `thundersdata-frontend/spa-template#${branch}`;
console.log(chalk.green(`模板地址:${repository}`));
const spinner = ora('正在下载模板,请稍候...');
spinner.start();
download(repository, name, { clone: true }, (err: string) => {
if (err) {
spinner.fail();
console.log(chalk.red(err));
} else {
spinner.succeed();
console.log(chalk.green('项目初始化完成'));
}
});
},
};
================================================
FILE: packages/cli/src/utils/deleteOldDirs.ts
================================================
import fs from 'fs';
export default function delDir(path: string) {
let files = [];
if (fs.existsSync(path)) {
files = fs.readdirSync(path);
files.forEach(file => {
const curPath = path + '/' + file;
if (fs.statSync(curPath).isDirectory()) {
delDir(curPath); //递归删除文件夹
} else {
fs.unlinkSync(curPath); //删除文件
}
});
fs.rmdirSync(path);
}
}
================================================
FILE: packages/cli/src/utils/replaceProject.ts
================================================
import fs from 'fs';
import path from 'path';
const binaryExtensions = ['.png', '.jar', '.webp', '.ttf', '.svg', '.bat', '.properties', '.keystore'];
/**
* Copy a file to given destination, replacing parts of its contents.
* @param srcPath Path to a file to be copied.
* @param destPath Destination path.
* @param replacements: e.g. {'TextToBeReplaced': 'Replacement'}
*/
export default function replaceProject(srcPath: string, destPath: string, replacements: Record<string, string>) {
if (fs.lstatSync(srcPath).isDirectory()) {
if (!fs.existsSync(destPath)) {
fs.mkdirSync(destPath);
}
// Not recursive
return;
}
const extension = path.extname(srcPath);
if (binaryExtensions.indexOf(extension) === -1) {
// Text file
const srcPermissions = fs.statSync(srcPath).mode;
let content = fs.readFileSync(srcPath, 'utf8');
Object.keys(replacements).forEach(
regex => (content = content.replace(new RegExp(regex, 'g'), replacements[regex]))
);
fs.writeFileSync(destPath, content, {
encoding: 'utf8',
mode: srcPermissions,
});
}
}
================================================
FILE: packages/cli/src/utils/translateFilePath.ts
================================================
'use strict';
/**
* There are various files in the templates folder in the RN repo. We want
* these to be ignored by tools when working with React Native itself.
* Example: _babelrc file is ignored by Babel, renamed to .babelrc inside
* a real app folder.
* This is especially important for .gitignore because npm has some special
* behavior of automatically renaming .gitignore to .npmignore.
*/
export default function translateFilePath(path: string) {
if (!path) {
return path;
}
return path
.replace('_BUCK', 'BUCK')
.replace('_gitignore', '.gitignore')
.replace('_gitattributes', '.gitattributes')
.replace('_babel.config.js', 'babel.config.js')
.replace('_babelrc', '.babelrc')
.replace('_flowconfig', '.flowconfig')
.replace('_buckconfig', '.buckconfig')
.replace('_watchmanconfig', '.watchmanconfig');
}
================================================
FILE: packages/cli/src/utils/walk.ts
================================================
import fs from 'fs';
import path from 'path';
('use strict');
export default function walk(current: string): string[] {
if (!fs.lstatSync(current).isDirectory()) {
return [current];
}
const files = fs.readdirSync(current).map(child => {
child = path.join(current, child);
return walk(child);
});
return [current].concat(...files);
}
================================================
FILE: packages/cli/tsconfig.build.json
================================================
{
"compilerOptions": {
"target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */,
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
"outDir": "./lib" /* 输出目录 */,
"strict": true /* 启用所有严格类型检查选项。 */,
"baseUrl": "./" /* 解析非绝对模块名称的基目录。 */,
"esModuleInterop": true /* 通过为所有导入创建命名空间对象,实现CommonJS和ES模块之间的互操作性。意味着“allowSyntheticDefaultImports”。 */,
"experimentalDecorators": true /* 启用对ES7装饰器的实验性支持。 */,
"forceConsistentCasingInFileNames": true /* 不允许对同一文件进行大小写不一致的引用。 */,
"resolveJsonModule": true /* 允许导入JSON文件 */,
"skipLibCheck": true
}
// "exclude": ["node_modules",]
}
================================================
FILE: packages/cli/types.d.ts
================================================
declare module 'download-git-repo';
================================================
FILE: packages/eslint-plugin-replace-hooks/.eslintrc.js
================================================
'use strict';
module.exports = {
root: true,
extends: ['eslint:recommended', 'plugin:eslint-plugin/recommended', 'plugin:node/recommended'],
env: {
es6: true,
},
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
},
overrides: [
{
files: ['tests/**/*.js'],
env: { mocha: true },
},
],
};
================================================
FILE: packages/eslint-plugin-replace-hooks/README.md
================================================
# eslint-plugin-replace-hooks
replace some hooks by other hooks
## Installation
You'll first need to install [ESLint](https://eslint.org/):
```sh
npm i eslint --save-dev
```
Next, install `eslint-plugin-replace-hooks`:
```sh
npm install eslint-plugin-replace-hooks --save-dev
```
## Usage
Add `replace-hooks` to the plugins section of your `.eslintrc` configuration file. You can omit the `eslint-plugin-` prefix:
```json
{
"plugins": [
"replace-hooks" // this must be placed before `import`
]
}
```
Then configure the rules you want to use under the rules section.
```json
{
"rules": {
"replace-hooks/no-forbidden-hooks": [
"error",
{ "useState": { "tip": "your custom hooks", "dependency": "your module" } }
]
}
}
```
## Supported Rules
- no-forbidden-hooks
## Caution
- this plugin should work with `eslint-plugin-import`, especially the rules `import/no-duplicates` to help merge import from same modules.
- the plugin must be placed before `eslint-plugin-import`
plugins: [ // other plugins, 'replace-hooks', 'import', ]
================================================
FILE: packages/eslint-plugin-replace-hooks/docs/rules/no-forbidden-hooks.md
================================================
# no forbidden hooks in project (no-forbidden-hooks)
## Rule Details
可以通过配置,把项目中不允许使用的 hooks 标记出来,同时提示用指定的其他 hooks 替换
Examples of **incorrect** code for this rule:
```js
// 其他配置
rules: {
// 其他规则
'replace-hooks/no-forbidden-hooks': 'error',
}
```
Examples of **correct** code for this rule:
```js
// 其他配置
rules: {
// 其他规则
'replace-hooks/no-forbidden-hooks': ['error', { useState: 'useSafeState' }],
}
```
### Options
要提示错误的 hooks,是一个键值对。键是要提示错误的 hooks,值是提示要替换的 hooks。比如:
```js
{
useState: 'useSafeState';
}
```
## When Not To Use It
如果你的项目里不需要用一些自定义的 hooks 封装,替换默认的 hooks
================================================
FILE: packages/eslint-plugin-replace-hooks/gulpfile.js
================================================
const gulp = require('gulp');
const del = require('del');
/**
* 清理构建目录
*/
async function clean() {
/** 删除构建目录 */
await del('lib');
}
async function copy() {
await gulp.src('./src/**/*').pipe(gulp.dest('lib/'));
}
exports.default = gulp.series(clean, copy);
================================================
FILE: packages/eslint-plugin-replace-hooks/package.json
================================================
{
"name": "eslint-plugin-replace-hooks",
"version": "1.0.3",
"description": "replace some hooks by other hooks",
"keywords": [
"eslint",
"eslintplugin",
"eslint-plugin"
],
"author": "chjdamon<chj_0507_dz@sina.com>",
"main": "lib/index.js",
"scripts": {
"build": "gulp",
"lint": "eslint .",
"test": "mocha tests --recursive"
},
"devDependencies": {
"del": "^6.0.0",
"eslint": "^8.25.0",
"eslint-plugin-es": "^4.1.0",
"eslint-plugin-eslint-plugin": "^5.0.6",
"eslint-plugin-node": "^11.1.0",
"gulp": "^4.0.2",
"mocha": "^10.1.0"
},
"license": "MIT",
"dependencies": {
"requireindex": "^1.2.0"
}
}
================================================
FILE: packages/eslint-plugin-replace-hooks/src/index.js
================================================
/**
* @fileoverview replace some hooks by other hooks
* @author chjdamon
*/
'use strict';
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const requireIndex = require('requireindex');
//------------------------------------------------------------------------------
// Plugin Definition
//------------------------------------------------------------------------------
// import all rules in lib/rules
module.exports.rules = requireIndex(__dirname + '/rules');
================================================
FILE: packages/eslint-plugin-replace-hooks/src/rules/no-forbidden-hooks.js
================================================
/**
* @fileoverview no forbidden hooks in project
* @author chjdamon
*/
'use strict';
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = {
meta: {
type: 'suggestion', // `problem`, `suggestion`, or `layout`
docs: {
description: 'no forbidden hooks in project',
recommended: true,
url: 'https://github.com/thundersdata-frontend/td-design/tree/main/packages/eslint-plugin-replace-hooks#readme', // URL to the documentation page for this rule
},
fixable: 'code', // Or `code` or `whitespace`
schema: [
{
type: 'object',
},
], // Add a schema if the rule has options
messages: {
empty: '',
forbidden: '`{{name}}` is forbidden. try to use `{{tip}}` from `{{dependency}}` alternatively',
},
},
create(context) {
// variables should be defined here
if (!context.options || !Array.isArray(context.options) || context.options.length === 0) return {};
const pattern = context.options[0]; // {useState: {tip: 'useSafeState', dependency: '@td-design/rn-hooks'}}
//----------------------------------------------------------------------
// Helpers
//----------------------------------------------------------------------
// any helper functions should go here or else delete this section
//----------------------------------------------------------------------
// Public
//----------------------------------------------------------------------
if (!pattern) return {};
return {
// 删除要替换的hooks,插入代替的hooks的import
ImportSpecifier: node => {
Object.entries(pattern).forEach(([key, value]) => {
if (node.imported.name === key) {
context.report({
node,
messageId: 'forbidden',
data: {
name: key,
tip: value.tip,
dependency: value.dependency,
},
fix: fixer => [
fixer.remove(node),
fixer.insertTextAfterRange(
[node.parent.range[0], node.parent.range[1] + 1],
`import { ${value.tip} } from '${value.dependency}';\n`
),
],
});
}
});
},
// 删除空的import
ImportDeclaration: node => {
if (node.specifiers.length === 0) {
context.report({
node,
messageId: 'empty',
fix: fixer => fixer.remove(node),
});
}
},
// 替换文本
'VariableDeclaration VariableDeclarator CallExpression Identifier': node => {
Object.entries(pattern).forEach(([key, value]) => {
if (node.name === key) {
context.report({
node,
messageId: 'forbidden',
data: {
name: key,
tip: value.tip,
dependency: value.dependency,
},
fix: fixer => fixer.replaceText(node, value.tip),
});
}
});
},
};
},
};
================================================
FILE: packages/hooks/CHANGELOG.md
================================================
# @td-design/rn-hooks
## 2.8.1
### Patch Changes
- [#892](https://github.com/thundersdata-frontend/td-design/pull/892) [`0648f3c23`](https://github.com/thundersdata-frontend/td-design/commit/0648f3c23a361f5aa2a01c436554d4c10933256f) Thanks [@chj-damon](https://github.com/chj-damon)! - refactor: 优化代码性能
- [#892](https://github.com/thundersdata-frontend/td-design/pull/892) [`0648f3c23`](https://github.com/thundersdata-frontend/td-design/commit/0648f3c23a361f5aa2a01c436554d4c10933256f) Thanks [@chj-damon](https://github.com/chj-damon)! - fix: 修复useCounter的bug
## 2.8.0
### Minor Changes
- [#831](https://github.com/thundersdata-frontend/td-design/pull/831) [`85fcb2d49`](https://github.com/thundersdata-frontend/td-design/commit/85fcb2d49ad1a15f5db089b769ecfb610988c0d1) Thanks [@chj-damon](https://github.com/chj-damon)! - feat: 新增useInfiniteScroll
## 2.7.4
### Patch Changes
- [#812](https://github.com/thundersdata-frontend/td-design/pull/812) [`603414f12`](https://github.com/thundersdata-frontend/td-design/commit/603414f12353bdd2ae1ffa1655fcddf8b799029b) Thanks [@chj-damon](https://github.com/chj-damon)! - fix: 修改RN里面判断开发环境的方式
## 2.7.3
### Patch Changes
- [#698](https://github.com/thundersdata-frontend/td-design/pull/698) [`52e472893`](https://github.com/thundersdata-frontend/td-design/commit/52e472893db980bbbc0131fe45da2188ec681542) Thanks [@chj-damon](https://github.com/chj-damon)! - 优化 useRequest 组件在开发环境的提示
## 2.7.2
### Patch Changes
- [#694](https://github.com/thundersdata-frontend/td-design/pull/694) [`8edd617ee`](https://github.com/thundersdata-frontend/td-design/commit/8edd617eee2d62d4c6daa6aa1c348464325cd358) Thanks [@chj-damon](https://github.com/chj-damon)! - 优化部分 hooks 的代码
## 2.7.1
### Patch Changes
- [#660](https://github.com/thundersdata-frontend/td-design/pull/660) [`94e1422ba`](https://github.com/thundersdata-frontend/td-design/commit/94e1422babc364a236695e12a80718336a039e70) Thanks [@chj-damon](https://github.com/chj-damon)! - 1. 修复 Modal 的几个方法在关闭后 Portal 没有被销毁的问题 2. 优化 ImagePicker 两个方法的写法 3. 修复 ActionSheet 的一些问题
## 2.7.0
### Minor Changes
- [#654](https://github.com/thundersdata-frontend/td-design/pull/654) [`a27035f58`](https://github.com/thundersdata-frontend/td-design/commit/a27035f58266c625742c9d03171cedbb913ac199) Thanks [@chj-damon](https://github.com/chj-damon)! - 1. 对组件进行性能优化; 2.重写 Accordion 组件; 3. 修复一些发现的 bug
## 2.6.1
### Patch Changes
- [#643](https://github.com/thundersdata-frontend/td-design/pull/643) [`694c4666c`](https://github.com/thundersdata-frontend/td-design/commit/694c4666c116869ee9321dd579113136603b8ba3) Thanks [@chj-damon](https://github.com/chj-damon)! - 修复 pollingInterval 没有类型定义的问题
## 2.6.0
### Minor Changes
- [#641](https://github.com/thundersdata-frontend/td-design/pull/641) [`68cfd28b1`](https://github.com/thundersdata-frontend/td-design/commit/68cfd28b1f3bc9bebfe8b6eda4979102054fb8df) Thanks [@chj-damon](https://github.com/chj-damon)! - 优化 hooks
## 2.5.1
### Patch Changes
- [`1cffe53a5`](https://github.com/thundersdata-frontend/td-design/commit/1cffe53a553b66a78b09a34ad091afae5cfc56c7) Thanks [@chj-damon](https://github.com/chj-damon)! - 修复 loading 在 ready 为 falsy 的时候一直都是 true 的问题
## 2.5.0
### Minor Changes
- [`4d1d4db4c`](https://github.com/thundersdata-frontend/td-design/commit/4d1d4db4c83b1d37810af6c289adfade573364d8) Thanks [@chj-damon](https://github.com/chj-damon)! - 1. 移除某些 hooks 对 react-native-background-timer 的依赖
2. 重写 useSms
================================================
FILE: packages/hooks/package.json
================================================
{
"name": "@td-design/rn-hooks",
"version": "2.8.1",
"description": "从ahooks里面抽取的一些在rn项目里可以使用的hooks。提高开发效率",
"keywords": [
"hooks",
"ahooks"
],
"homepage": "https://github.com/thundersdata-frontend/td-design/tree/main/packages/hooks#readme",
"author": "thundersdata-frontend",
"license": "Apache-2.0",
"main": "lib/commonjs/index.js",
"module": "lib/module/index.js",
"types": "lib/typescript/index.d.ts",
"files": [
"lib"
],
"publishConfig": {
"registry": "https://registry.npmjs.org/",
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/thundersdata-frontend/td-design.git"
},
"scripts": {
"build": "rollup -c"
},
"bugs": {
"url": "https://github.com/thundersdata-frontend/td-design/issues"
},
"dependencies": {
"lodash-es": "^4.17.21"
},
"devDependencies": {
"@types/lodash-es": "^4.17.8",
"@types/node": "^20.4.4",
"@types/react": "^18.2.15",
"@types/react-native": "^0.72.2",
"typescript": "^5.1.6",
"@rollup/plugin-babel": "^6.0.3",
"@rollup/plugin-commonjs": "^25.0.3",
"@rollup/plugin-node-resolve": "^15.1.0",
"rollup": "^3.26.3",
"rollup-plugin-typescript2": "^0.35.0",
"tslib": "^2.6.0"
}
}
================================================
FILE: packages/hooks/rollup.config.js
================================================
const commonjs = require('@rollup/plugin-commonjs');
const resolve = require('@rollup/plugin-node-resolve');
const typescript = require('rollup-plugin-typescript2')
const fs = require("fs")
const noDeclarationFiles = { compilerOptions: { declaration: false } };
let pkg = JSON.parse(fs.readFileSync('./package.json')),
external = Object.keys(pkg.dependencies || {});
module.exports = [
{
input: ['./src/index.ts'],
output: {
dir: 'lib/commonjs',
format: 'cjs',
name: '[name].js',
preserveModules: true,
preserveModulesRoot: 'src',
},
plugins: [
resolve(),
commonjs(),
typescript({tsconfig: "tsconfig.json",tsconfigOverride:{ compilerOptions: { declaration: true ,declarationDir:'./lib/typescript'} },useTsconfigDeclarationDir: true}),
],
external: ['react','react-native',...external,/node_modules/],
},
{
input: ['./src/index.ts'],
output: {
dir: 'lib/module',
format: 'esm',
entryFileNames: '[name].js',
preserveModules: true,
preserveModulesRoot: 'src',
},
plugins: [
resolve(),
commonjs({ mainFields: ["module", "main", "jsnext:main", "browser"],
extensions: [".js", ".mjs", ".json"],
sourceMap: false,
browser: true,}),
typescript({ tsconfigOverride: noDeclarationFiles}),
],
external: ['react','react-native',...external,/node_modules/],
}
]
================================================
FILE: packages/hooks/src/createUpdateEffect/index.test.ts
================================================
import { useEffect, useLayoutEffect } from 'react';
import { renderHook } from '@testing-library/react-hooks';
import { createUpdateEffect } from './index';
describe('createUpdateEffect', () => {
it('should be defined', () => {
expect(createUpdateEffect).toBeDefined();
});
it('should work for useEffect', () => {
const useUpdateEffect = createUpdateEffect(useEffect);
let mountedState = 1;
const hook = renderHook(() =>
useUpdateEffect(() => {
mountedState = 2;
})
);
expect(mountedState).toEqual(1);
hook.rerender();
expect(mountedState).toEqual(2);
});
it('should work for useLayoutEffect', () => {
const useUpdateLayoutEffect = createUpdateEffect(useLayoutEffect);
let mountedState = 1;
const hook = renderHook(() =>
useUpdateLayoutEffect(() => {
mountedState = 2;
})
);
expect(mountedState).toEqual(1);
hook.rerender();
expect(mountedState).toEqual(2);
});
});
================================================
FILE: packages/hooks/src/createUpdateEffect/index.ts
================================================
import type { useEffect, useLayoutEffect } from 'react';
import { useRef } from 'react';
type effectHookType = typeof useEffect | typeof useLayoutEffect;
export const createUpdateEffect: (hook: effectHookType) => effectHookType = hook => (effect, deps) => {
const isMounted = useRef(false);
// for react-refresh
hook(() => {
return () => {
isMounted.current = false;
};
}, []);
hook(() => {
if (!isMounted.current) {
isMounted.current = true;
} else {
return effect();
}
}, deps);
};
================================================
FILE: packages/hooks/src/index.ts
================================================
import { default as useAccessibilityInfo } from './useAccessibilityInfo';
import { default as useAppState } from './useAppState';
import { default as useAsyncEffect } from './useAsyncEffect';
import { default as useBackHandler } from './useBackHandler';
import { default as useBoolean } from './useBoolean';
import { default as useControllableValue } from './useControllableValue';
import { default as useCountdown } from './useCountdown';
import { default as useCounter } from './useCounter';
import { default as useCreation } from './useCreation';
import { default as useDebounce } from './useDebounce';
import { default as useDebounceEffect } from './useDebounceEffect';
import { default as useDebounceFn } from './useDebounceFn';
import { default as useDeepCompareEffect } from './useDeepCompareEffect';
import { default as useDeviceOrientation } from './useDeviceOrientation';
import { default as useDimensions } from './useDimensions';
import { default as useDynamicList } from './useDynamicList';
import { default as useEventEmitter } from './useEventEmitter';
import { default as useHistoryTravel } from './useHistoryTravel';
import { default as useInfiniteScroll } from './useInfiniteScroll';
import { default as useInterval } from './useInterval';
import { default as useKeyboard } from './useKeyboard';
import { default as useLatest } from './useLatest';
import { default as useLayout } from './useLayout';
import { default as useLockFn } from './useLockFn';
import { default as useMap } from './useMap';
import { default as useMemoizedFn } from './useMemoizedFn';
import { default as useMount } from './useMount';
import { default as usePagination } from './usePagination';
import { default as usePrevious } from './usePrevious';
import { clearCache, default as useRequest } from './useRequest';
import { default as useSafeState } from './useSafeState';
import { default as useSet } from './useSet';
import { default as useSetState } from './useSetState';
import { default as useSms } from './useSms';
import { default as useThrottle } from './useThrottle';
import { default as useThrottleEffect } from './useThrottleEffect';
import { default as useThrottleFn } from './useThrottleFn';
import { default as useTimeout } from './useTimeout';
import { default as useToggle } from './useToggle';
import { default as useTrackedEffect } from './useTrackedEffect';
import { default as useUnmount } from './useUnmount';
import { default as useUnmountedRef } from './useUnmountedRef';
import { default as useUpdate } from './useUpdate';
import { default as useUpdateEffect } from './useUpdateEffect';
import { default as useUpdateLayoutEffect } from './useUpdateLayoutEffect';
import { default as useWhyDidYouUpdate } from './useWhyDidYouUpdate';
export {
useAccessibilityInfo,
useAppState,
useBackHandler,
useDeviceOrientation,
useDimensions,
useKeyboard,
useLayout,
useAsyncEffect,
useBoolean,
useControllableValue,
useCounter,
useCreation,
useDebounce,
useDebounceEffect,
useDebounceFn,
useDeepCompareEffect,
useDynamicList,
useEventEmitter,
useHistoryTravel,
useInterval,
useLatest,
useLockFn,
useMap,
useMemoizedFn,
useMount,
usePagination,
usePrevious,
useRequest,
useSafeState,
useSet,
useSetState,
useCountdown,
useThrottle,
useThrottleEffect,
useThrottleFn,
useTimeout,
useToggle,
useTrackedEffect,
useUnmount,
useUpdate,
useUnmountedRef,
useUpdateEffect,
useUpdateLayoutEffect,
useWhyDidYouUpdate,
useSms,
clearCache,
useInfiniteScroll,
};
================================================
FILE: packages/hooks/src/useAccessibilityInfo/index.test.ts
================================================
import { AccessibilityChangeEventName, AccessibilityInfo } from 'react-native';
import { act, renderHook } from '@testing-library/react-hooks';
import useAccessibilityInfo from './index';
describe('useAccessibilityInfo', () => {
const mockAddEventListener = AccessibilityInfo.addEventListener as jest.Mock;
const mockIsBoldTextEnabled = AccessibilityInfo.isBoldTextEnabled as jest.Mock;
const mockIsGrayscaleEnabled = AccessibilityInfo.isGrayscaleEnabled as jest.Mock;
const mockIsInvertColorsEnabled = AccessibilityInfo.isInvertColorsEnabled as jest.Mock;
const mockIsReduceMotionEnabled = AccessibilityInfo.isReduceMotionEnabled as jest.Mock;
const mockIsReduceTransparencyEnabled = AccessibilityInfo.isReduceTransparencyEnabled as jest.Mock;
const mockIsScreenReaderEnabled = AccessibilityInfo.isScreenReaderEnabled as jest.Mock;
const createEmitChangeEvent = (event: AccessibilityChangeEventName) => {
let handler: (value: boolean) => void;
mockAddEventListener.mockImplementation((eventName, fn) => {
if (eventName === event) {
handler = fn;
}
});
return (value: boolean) => handler(value);
};
beforeAll(() => {
mockIsBoldTextEnabled.mockResolvedValue(false);
mockIsGrayscaleEnabled.mockResolvedValue(false);
mockIsInvertColorsEnabled.mockResolvedValue(false);
mockIsReduceMotionEnabled.mockResolvedValue(false);
mockIsReduceTransparencyEnabled.mockResolvedValue(false);
mockIsScreenReaderEnabled.mockResolvedValue(false);
});
describe('screenReaderEnabled', () => {
it('should return undefined until promise will be resolved', async () => {
const { result } = renderHook(() => useAccessibilityInfo().screenReaderEnabled);
expect(result.current).toBeUndefined();
});
it('should return default value', async () => {
const defaultValue = true;
mockIsScreenReaderEnabled.mockResolvedValueOnce(defaultValue);
const { result, waitForNextUpdate } = renderHook(() => useAccessibilityInfo().screenReaderEnabled);
await waitForNextUpdate(); // wait when promise will be resolved
expect(result.current).toBe(defaultValue);
});
it('should update value when it change', async () => {
const newValue = true;
const emit = createEmitChangeEvent('screenReaderChanged');
const { result, waitForNextUpdate } = renderHook(() => useAccessibilityInfo().screenReaderEnabled);
await waitForNextUpdate(); // wait when promise will be resolved
const { current: initial } = result;
act(() => emit(newValue));
const { current: afterUpdate } = result;
expect({ initial, afterUpdate }).toEqual({
initial: false,
afterUpdate: newValue,
});
});
});
describe('grayscaleEnabled', () => {
it('should return undefined until promise will be resolved', async () => {
const { result } = renderHook(() => useAccessibilityInfo().grayscaleEnabled);
expect(result.current).toBeUndefined();
});
it('should return default value', async () => {
const defaultValue = true;
mockIsGrayscaleEnabled.mockResolvedValueOnce(defaultValue);
const { result, waitForNextUpdate } = renderHook(() => useAccessibilityInfo().grayscaleEnabled);
await waitForNextUpdate(); // wait when promise will be resolved
expect(result.current).toBe(defaultValue);
});
it('should update value when it change', async () => {
const newValue = true;
const emit = createEmitChangeEvent('grayscaleChanged');
const { result, waitForNextUpdate } = renderHook(() => useAccessibilityInfo().grayscaleEnabled);
await waitForNextUpdate(); // wait when promise will be resolved
const { current: initial } = result;
act(() => emit(newValue));
const { current: afterUpdate } = result;
expect({ initial, afterUpdate }).toEqual({
initial: false,
afterUpdate: newValue,
});
});
});
describe('invertColorsEnabled', () => {
it('should return undefined until promise will be resolved', async () => {
const { result } = renderHook(() => useAccessibilityInfo().invertColorsEnabled);
expect(result.current).toBeUndefined();
});
it('should return default value', async () => {
const defaultValue = true;
mockIsInvertColorsEnabled.mockResolvedValueOnce(defaultValue);
const { result, waitForNextUpdate } = renderHook(() => useAccessibilityInfo().invertColorsEnabled);
await waitForNextUpdate(); // wait when promise will be resolved
expect(result.current).toBe(defaultValue);
});
it('should update value when it change', async () => {
const newValue = true;
const emit = createEmitChangeEvent('invertColorsChanged');
const { result, waitForNextUpdate } = renderHook(() => useAccessibilityInfo().invertColorsEnabled);
await waitForNextUpdate(); // wait when promise will be resolved
const { current: initial } = result;
act(() => emit(newValue));
const { current: afterUpdate } = result;
expect({ initial, afterUpdate }).toEqual({
initial: false,
afterUpdate: newValue,
});
});
});
describe('reduceMotionEnabled', () => {
it('should return undefined until promise will be resolved', async () => {
const { result } = renderHook(() => useAccessibilityInfo().reduceMotionEnabled);
expect(result.current).toBeUndefined();
});
it('should return default value', async () => {
const defaultValue = true;
mockIsReduceMotionEnabled.mockResolvedValueOnce(defaultValue);
const { result, waitForNextUpdate } = renderHook(() => useAccessibilityInfo().reduceMotionEnabled);
await waitForNextUpdate(); // wait when promise will be resolved
expect(result.current).toBe(defaultValue);
});
it('should update value when it change', async () => {
const newValue = true;
const emit = createEmitChangeEvent('reduceMotionChanged');
const { result, waitForNextUpdate } = renderHook(() => useAccessibilityInfo().reduceMotionEnabled);
await waitForNextUpdate(); // wait when promise will be resolved
const { current: initial } = result;
act(() => emit(newValue));
const { current: afterUpdate } = result;
expect({ initial, afterUpdate }).toEqual({
initial: false,
afterUpdate: newValue,
});
});
});
describe('reduceTransparencyEnabled', () => {
it('should return undefined until promise will be resolved', async () => {
const { result } = renderHook(() => useAccessibilityInfo().reduceTransparencyEnabled);
expect(result.current).toBeUndefined();
});
it('should return default value', async () => {
const defaultValue = true;
mockIsReduceTransparencyEnabled.mockResolvedValueOnce(defaultValue);
const { result, waitForNextUpdate } = renderHook(() => useAccessibilityInfo().reduceTransparencyEnabled);
await waitForNextUpdate(); // wait when promise will be resolved
expect(result.current).toBe(defaultValue);
});
it('should update value when it change', async () => {
const newValue = true;
const emit = createEmitChangeEvent('reduceTransparencyChanged');
const { result, waitForNextUpdate } = renderHook(() => useAccessibilityInfo().reduceTransparencyEnabled);
await waitForNextUpdate(); // wait when promise will be resolved
const { current: initial } = result;
act(() => emit(newValue));
const { current: afterUpdate } = result;
expect({ initial, afterUpdate }).toEqual({
initial: false,
afterUpdate: newValue,
});
});
});
describe('boldTextEnabled', () => {
it('should return undefined until promise will be resolved', async () => {
const { result } = renderHook(() => useAccessibilityInfo().boldTextEnabled);
expect(result.current).toBeUndefined();
});
it('should return default value', async () => {
const defaultValue = true;
mockIsBoldTextEnabled.mockResolvedValueOnce(defaultValue);
const { result, waitForNextUpdate } = renderHook(() => useAccessibilityInfo().boldTextEnabled);
await waitForNextUpdate(); // wait when promise will be resolved
expect(result.current).toBe(defaultValue);
});
it('should update value when it change', async () => {
const newValue = true;
const emit = createEmitChangeEvent('boldTextChanged');
const { result, waitForNextUpdate } = renderHook(() => useAccessibilityInfo().boldTextEnabled);
await waitForNextUpdate(); // wait when promise will be resolved
const { current: initial } = result;
act(() => emit(newValue));
const { current: afterUpdate } = result;
expect({ initial, afterUpdate }).toEqual({
initial: false,
afterUpdate: newValue,
});
});
});
});
================================================
FILE: packages/hooks/src/useAccessibilityInfo/index.ts
================================================
import { useEffect } from 'react';
import { AccessibilityChangeEventName, AccessibilityInfo } from 'react-native';
import useSafeState from '../useSafeState';
type AccessibilityInfoStaticInitializers =
| 'isBoldTextEnabled'
| 'isScreenReaderEnabled'
| 'isGrayscaleEnabled'
| 'isInvertColorsEnabled'
| 'isReduceMotionEnabled'
| 'isReduceTransparencyEnabled';
function useAccessibilityStateListener(
eventName: AccessibilityChangeEventName,
initializerName: AccessibilityInfoStaticInitializers
) {
const [isEnabled, setIsEnabled] = useSafeState<boolean | undefined>(undefined);
useEffect(() => {
if (!AccessibilityInfo[initializerName]) return;
AccessibilityInfo[initializerName]().then(setIsEnabled);
const subscription = AccessibilityInfo.addEventListener(eventName, setIsEnabled);
return () => {
if (typeof subscription?.remove === 'function') {
subscription.remove();
} else {
// @ts-ignore - need update @types/react-native@0.65.x
AccessibilityInfo.removeEventListener(eventName, setIsEnabled);
}
};
}, [eventName, initializerName, setIsEnabled]);
return isEnabled;
}
export default function useAccessibilityInfo() {
// 字体加粗(IOS)
const boldTextEnabled = useAccessibilityStateListener('boldTextChanged', 'isBoldTextEnabled');
// 灰色模式(IOS。色盲使用)
const grayscaleEnabled = useAccessibilityStateListener('grayscaleChanged', 'isGrayscaleEnabled');
// 反转显示屏的颜色(IOS)
const invertColorsEnabled = useAccessibilityStateListener('invertColorsChanged', 'isInvertColorsEnabled');
// 减弱动态效果
const reduceMotionEnabled = useAccessibilityStateListener('reduceMotionChanged', 'isReduceMotionEnabled');
// 降低透明度 (IOS)
const reduceTransparencyEnabled = useAccessibilityStateListener(
'reduceTransparencyChanged',
'isReduceTransparencyEnabled'
);
// 是否启用读屏应用
const screenReaderEnabled = useAccessibilityStateListener('screenReaderChanged', 'isScreenReaderEnabled');
return {
screenReaderEnabled,
grayscaleEnabled,
invertColorsEnabled,
reduceMotionEnabled,
reduceTransparencyEnabled,
boldTextEnabled,
};
}
================================================
FILE: packages/hooks/src/useAppState/index.test.ts
================================================
import { AppState, AppStateStatus } from 'react-native';
import { act, renderHook } from '@testing-library/react-hooks';
import useAppState from './index';
describe('useAppState', () => {
const addEventListenerMock = AppState.addEventListener as jest.Mock;
const createEmitAppStateChange = () => {
let listener: (newStatus: AppStateStatus) => any;
addEventListenerMock.mockImplementationOnce((_, fn) => {
listener = fn;
});
return (newStatus: AppStateStatus) => listener(newStatus);
};
it('should return current state by default', () => {
const { result } = renderHook(() => useAppState());
expect(result.current).toBe(AppState.currentState);
});
it('should update state when it change', () => {
const newStatus = 'background';
const emit = createEmitAppStateChange();
const { result } = renderHook(() => useAppState());
const { current: initialStatus } = result;
act(() => {
emit(newStatus);
});
const { current: statusAfterUpdate } = result;
expect({ initialStatus, statusAfterUpdate }).toEqual({
initialStatus: AppState.currentState,
statusAfterUpdate: newStatus,
});
});
});
================================================
FILE: packages/hooks/src/useAppState/index.ts
================================================
import { useEffect } from 'react';
import { AppState } from 'react-native';
import useSafeState from '../useSafeState';
export default function useAppState() {
const [appState, setAppState] = useSafeState(AppState.currentState);
useEffect(() => {
const subscription = AppState.addEventListener('change', setAppState);
return () => {
if (typeof subscription?.remove === 'function') {
subscription.remove();
} else {
// @ts-ignore - need update @types/react-native@0.65.x
AppState.removeEventListener('change', setAppState);
}
};
}, [setAppState]);
return appState;
}
================================================
FILE: packages/hooks/src/useAsyncEffect/demo/demo1.tsx
================================================
import React, { useState } from 'react';
import { useAsyncEffect } from '@td-design/rn-hooks';
function mockCheck(): Promise<boolean> {
return new Promise(resolve => {
setTimeout(() => {
resolve(true);
}, 3000);
});
}
export default () => {
const [pass, setPass] = useState<boolean>();
useAsyncEffect(async () => {
setPass(await mockCheck());
}, []);
return (
<div>
<button disabled={!pass}>Submit</button>
<p>
{pass === null && 'Checking...'}
{pass === true && 'Check passed.'}
</p>
</div>
);
};
================================================
FILE: packages/hooks/src/useAsyncEffect/index.test.ts
================================================
import { useState } from 'react';
import { renderHook } from '@testing-library/react-hooks';
import { sleep } from '../utils/testHelpers';
import useAsyncEffect from './index';
describe('useAsyncEffect', () => {
test('useAsyncEffect should be defined', () => {
expect(useAsyncEffect).toBeDefined();
});
test('should work without cleanup', async () => {
const { result, waitForNextUpdate } = renderHook(() => {
const [count, setCount] = useState(0);
useAsyncEffect(async () => {
await sleep(100);
setCount(1);
}, []);
return count;
});
expect(result.current).toBe(0);
await waitForNextUpdate();
expect(result.current).toBe(1);
});
});
================================================
FILE: packages/hooks/src/useAsyncEffect/index.ts
================================================
import type { DependencyList } from 'react';
import { useEffect } from 'react';
import { isFunction } from '../utils';
function isAsyncGenerator(
val: AsyncGenerator<void, void, void> | Promise<void>
): val is AsyncGenerator<void, void, void> {
return isFunction(val[Symbol.asyncIterator]);
}
function useAsyncEffect(effect: () => AsyncGenerator<void, void, void> | Promise<void>, deps?: DependencyList) {
useEffect(() => {
const e = effect();
let cancelled = false;
async function execute() {
if (isAsyncGenerator(e)) {
while (true) {
const result = await e.next();
if (result.done || cancelled) {
break;
}
}
} else {
await e;
}
}
execute();
return () => {
cancelled = true;
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, deps);
}
export default useAsyncEffect;
================================================
FILE: packages/hooks/src/useBackHandler/index.test.ts
================================================
import { BackHandler } from 'react-native';
import { renderHook } from '@testing-library/react-hooks';
import useBackHandler from './index';
describe('useBackHandler', () => {
test('useBackHandler should be defined', () => {
expect(useBackHandler).toBeDefined();
});
const addEventListenerMock = BackHandler.addEventListener as jest.Mock;
const removeEventListenerMock = BackHandler.removeEventListener as jest.Mock;
beforeEach(() => {
jest.clearAllMocks();
});
it('should add back press listener on mount', () => {
const handler = jest.fn();
renderHook(props => useBackHandler(props.handler), {
initialProps: { handler },
});
expect(addEventListenerMock).toBeCalledTimes(1);
expect(addEventListenerMock).toBeCalledWith('hardwareBackPress', handler);
});
it('should resubscribe when passed handler will change', () => {
const handler = jest.fn();
const handler2 = jest.fn();
const { rerender } = renderHook(props => useBackHandler(props.handler), {
initialProps: { handler },
});
expect(addEventListenerMock).toBeCalledWith('hardwareBackPress', handler);
rerender({ handler: handler2 });
expect(removeEventListenerMock).toBeCalledWith('hardwareBackPress', handler);
expect(addEventListenerMock).toBeCalledWith('hardwareBackPress', handler2);
});
it('should remove back press listener on unmount', () => {
const handler = jest.fn();
const { unmount } = renderHook(props => useBackHandler(props.handler), {
initialProps: { handler },
});
expect(removeEventListenerMock).toBeCalledTimes(0);
unmount();
expect(removeEventListenerMock).toBeCalledTimes(1);
expect(removeEventListenerMock).toBeCalledWith('hardwareBackPress', handler);
});
});
================================================
FILE: packages/hooks/src/useBackHandler/index.ts
================================================
import { useEffect } from 'react';
import { BackHandler } from 'react-native';
export default function useBackHandler(handler: () => boolean) {
useEffect(() => {
const subscription = BackHandler.addEventListener('hardwareBackPress', handler);
return () => {
if (typeof subscription?.remove === 'function') {
subscription.remove();
} else {
// React Native < 0.65
BackHandler.removeEventListener('hardwareBackPress', handler);
}
};
}, [handler]);
}
================================================
FILE: packages/hooks/src/useBoolean/demo/demo1.tsx
================================================
import React from 'react';
import { useBoolean } from '@td-design/rn-hooks';
export default () => {
const [state, { toggle, setTrue, setFalse }] = useBoolean(true);
return (
<div>
<p>Effects:{JSON.stringify(state)}</p>
<p>
<button type="button" onClick={toggle}>
Toggle
</button>
<button type="button" onClick={setFalse} style={{ margin: '0 16px' }}>
Set false
</button>
<button type="button" onClick={setTrue}>
Set true
</button>
</p>
</div>
);
};
================================================
FILE: packages/hooks/src/useBoolean/index.test.ts
================================================
import { act, renderHook } from '@testing-library/react-hooks';
import useBoolean from './index';
describe('useBoolean', () => {
it('should be defined', () => {
expect(useBoolean).toBeDefined();
});
it('test on methods', async () => {
const { result } = renderHook(() => useBoolean());
expect(result.current[0]).toBeFalsy();
const [, { setTrue, toggle, set }] = result.current;
act(() => {
setTrue();
});
expect(result.current[0]).toBeTruthy();
act(() => {
toggle();
});
expect(result.current[0]).toBeFalsy();
act(() => {
toggle();
});
expect(result.current[0]).toBeTruthy();
act(() => {
set(false);
});
expect(result.current[0]).toBeFalsy();
// act(() => {
// result.current[1].set(true);
// });
// expect(result.current[0]).toBe(true);
// act(() => {
// // @ts-ignore
// result.current[1].set(0);
// });
// expect(result.current[0]).toBe(false);
// act(() => {
// // @ts-ignore
// result.current[1].set('a');
// });
// expect(result.current[0]).toBe(true);
});
});
================================================
FILE: packages/hooks/src/useBoolean/index.ts
================================================
import { useMemo } from 'react';
import useToggle from '../useToggle';
/**
* 优雅的管理 boolean 状态的 Hook。
* @param defaultValue 默认值为false
* @returns
*/
export default function useBoolean(defaultValue = false) {
const [state, { toggle, set }] = useToggle(defaultValue);
const actions = useMemo(() => {
const setTrue = () => set(true);
const setFalse = () => set(false);
return {
toggle,
set: (v: boolean) => set(!!v),
setTrue,
setFalse,
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return [state, actions] as const;
}
================================================
FILE: packages/hooks/src/useControllableValue/demo/demo1.tsx
================================================
import React from 'react';
import { useControllableValue } from '@td-design/rn-hooks';
export default (props: any) => {
const [state, setState] = useControllableValue<string>(props, {
defaultValue: '',
});
return (
<>
<input value={state} onChange={e => setState(e.target.value)} style={{ width: 300 }} />
<button type="button" onClick={() => setState('')} style={{ margin: '0 4px' }}>
Clear
</button>
</>
);
};
================================================
FILE: packages/hooks/src/useControllableValue/demo/demo2.tsx
================================================
import React, { useState } from 'react';
import { useControllableValue } from '@td-design/rn-hooks';
const ControllableComponent = (props: any) => {
const [state, setState] = useControllableValue<string>(props);
return <input value={state} onChange={e => setState(e.target.value)} style={{ width: 300 }} />;
};
const Parent = () => {
const [state, setState] = useState<string>('');
const clear = () => {
setState('');
};
return (
<>
<ControllableComponent value={state} onChange={setState} />
<button type="button" onClick={clear} style={{ margin: '0 4px' }}>
Clear
</button>
</>
);
};
export default Parent;
================================================
FILE: packages/hooks/src/useControllableValue/demo/demo3.tsx
================================================
import React, { useState } from 'react';
import { useControllableValue } from '@td-design/rn-hooks';
const ControllableComponent = (props: any) => {
const [state, setState] = useControllableValue<string>(props);
return (
<input
value={state}
onChange={e => {
setState(e.target.value);
}}
style={{ width: 300 }}
/>
);
};
const Parent = () => {
const [state, setState] = useState<number>(0);
return (
<>
<div style={{ marginBottom: 8 }}>state:{state}</div>
<ControllableComponent onChange={setState} />
</>
);
};
export default Parent;
================================================
FILE: packages/hooks/src/useControllableValue/index.test.ts
================================================
import { act, renderHook } from '@testing-library/react-hooks';
import useControllableValue from './index';
describe('useControllableValue', () => {
test('useControllableValue should be defined', () => {
expect(useControllableValue).toBeDefined();
});
test('defaultValue should work like a charm', () => {
const { result } = renderHook(() => useControllableValue({ defaultValue: 1 }));
expect(result.current[0]).toEqual(1);
});
test('value should override defaultValue', () => {
const { result } = renderHook(() => useControllableValue({ defaultValue: 1, value: 2 }));
expect(result.current[0]).toEqual(2);
});
test('value should be undefined if nothing passed', () => {
const { result } = renderHook(() => useControllableValue());
expect(result.current[0]).toBeUndefined();
});
test('onChange should work like a charm', () => {
let extraParam = '';
const props = {
value: 2,
onChange(v: number, extra: any) {
this.value = v;
extraParam = extra;
},
};
const { result } = renderHook(() => useControllableValue(props));
expect(result.current[0]).toEqual(2);
act(() => {
result.current[1](3, 'test');
});
expect(props.value).toEqual(3);
expect(extraParam).toEqual('test');
});
test('state should update when rerender', () => {
const { result, rerender } = renderHook(props => useControllableValue(props), {
initialProps: {
value: 2,
},
});
rerender({ value: 3 });
expect(result.current[0]).toEqual(3);
rerender({ value: 1 });
expect(result.current[0]).toEqual(1);
});
test('state should update when set was triggered', () => {
const { result } = renderHook(() => useControllableValue({ newValue: 1 }));
const [, setValue] = result.current;
act(() => setValue(undefined));
expect(result.current[0]).toBeUndefined();
act(() => setValue(null));
expect(result.current[0]).toBeNull();
act(() => setValue(55));
expect(result.current[0]).toBe(55);
act(() => setValue((prev: number) => prev + 1));
expect(result.current[0]).toEqual(56);
});
});
================================================
FILE: packages/hooks/src/useControllableValue/index.ts
================================================
import type { SetStateAction } from 'react';
import { useMemo, useRef } from 'react';
import useMemoizedFn from '../useMemoizedFn';
import useUpdate from '../useUpdate';
import { isFunction } from '../utils';
interface Options<T> {
/** 默认值,会被 props.defaultValue 和 props.value 覆盖 */
defaultValue?: T;
/** 默认值的属性名。默认为 defaultValue */
defaultValuePropName?: string;
/** 值的属性名。默认为 value */
valuePropName?: string;
/** 修改值时触发的函数。默认为 onChange */
trigger?: string;
}
interface StandardProps<T> {
value: T;
defaultValue?: T;
onChange?: (value: T) => void;
}
export type Props = Record<string, any>;
function useControllableValue<T = any>(props: StandardProps<T>): [T, (v: SetStateAction<T>) => void];
function useControllableValue<T = any>(
props?: Props,
options?: Options<T>
): [T, (v: SetStateAction<T>, ...args: any[]) => void];
function useControllableValue<T = any>(props: Props = {}, options: Options<T> = {}) {
const {
defaultValue,
defaultValuePropName = 'defaultValue',
valuePropName = 'value',
trigger = 'onChange',
} = options;
const value = props[valuePropName] as T;
const isControlled = props.hasOwnProperty(valuePropName);
const initialValue = useMemo(() => {
if (isControlled) {
return value;
}
if (props.hasOwnProperty(defaultValuePropName)) {
return props[defaultValuePropName];
}
return defaultValue;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const stateRef = useRef(initialValue);
if (isControlled) {
stateRef.current = value;
}
const update = useUpdate();
function setState<T>(v: SetStateAction<T>, ...args: any[]) {
const r = isFunction(v) ? v(stateRef.current) : v;
if (!isControlled) {
stateRef.current = r;
update();
}
if (props[trigger]) {
props[trigger](r, ...args);
}
}
return [stateRef.current, useMemoizedFn(setState)] as const;
}
export default useControllableValue;
================================================
FILE: packages/hooks/src/useCountdown/index.ts
================================================
import { useEffect, useRef, useState } from 'react';
import useAppState from '../useAppState';
import useMemoizedFn from '../useMemoizedFn';
export default function useCountDown(seconds) {
const appState = useAppState();
const timer = useRef<ReturnType<typeof setInterval>>();
const [target, setTarget] = useState<Date | null>(null);
const [count, setCount] = useState(0);
const start = useMemoizedFn(() => {
setTarget(add(new Date(), seconds));
});
const stop = useMemoizedFn(() => {
setTarget(null);
setCount(0);
});
useEffect(() => {
if (target === null || appState !== 'active') return;
setCount(diff(new Date(), target));
timer.current = setInterval(() => {
setCount(diff(new Date(), target));
}, 1000);
return () => {
if (timer.current) {
clearInterval(timer.current);
timer.current = undefined;
}
};
}, [target]);
useEffect(() => {
if (count === 0) {
stop();
}
}, [count, stop]);
return {
count,
start,
stop,
};
}
function add(date: Date, seconds: number) {
return new Date(date.getTime() + seconds * 1000);
}
function diff(now: Date, target: Date) {
return Math.max(Math.trunc((target.getTime() - now.getTime()) / 1000 + 0.5), 0);
}
================================================
FILE: packages/hooks/src/useCounter/demo/demo1.tsx
================================================
import React from 'react';
import { useCounter } from '@td-design/rn-hooks';
export default () => {
const [current, { inc, dec, set, reset }] = useCounter(100, { min: 1, max: 10 });
return (
<div>
<p>{current} [max: 10; min: 1;]</p>
<div>
<button
type="button"
onClick={() => {
inc();
}}
style={{ marginRight: 8 }}
>
inc()
</button>
<button
type="button"
onClick={() => {
dec();
}}
style={{ marginRight: 8 }}
>
dec()
</button>
<button
type="button"
onClick={() => {
set(3);
}}
style={{ marginRight: 8 }}
>
set(3)
</button>
<button type="button" onClick={reset} style={{ marginRight: 8 }}>
reset()
</button>
</div>
</div>
);
};
================================================
FILE: packages/hooks/src/useCounter/index.test.ts
================================================
import { act, renderHook } from '@testing-library/react-hooks';
import useCounter from './index';
describe('useCounter', () => {
test('useCounter should be defined', () => {
expect(useCounter).toBeDefined();
});
test('initialValue should work like a charm', () => {
const { result } = renderHook(() => useCounter(2));
expect(result.current[0]).toBe(2);
});
test('state should be empty string if no initialValue passed', () => {
const { result } = renderHook(() => useCounter());
expect(result.current[0]).toBe(0);
});
test('min value should be 2', () => {
const { result } = renderHook(() => useCounter(1, { min: 2 }));
expect(result.current[0]).toBe(2);
});
test('max value should be 5', () => {
const { result } = renderHook(() => useCounter(7, { max: 5 }));
expect(result.current[0]).toBe(5);
});
test('actions should work like a charm', () => {
const { result } = renderHook(() => useCounter(100, { min: 1, max: 10 }));
expect(result.current[0]).toEqual(10);
const [, { inc, dec, reset, set }] = result.current;
act(() => {
inc(1);
});
expect(result.current[0]).toEqual(10);
act(() => {
dec(100);
});
expect(result.current[0]).toEqual(1);
act(() => {
inc(2);
});
expect(result.current[0]).toEqual(3);
act(() => {
reset();
});
expect(result.current[0]).toEqual(10);
act(() => {
set(6);
});
expect(result.current[0]).toEqual(6);
act(() => {
set(60);
});
expect(result.current[0]).toEqual(10);
act(() => {
set(-100);
});
expect(result.current[0]).toEqual(1);
act(() => {
inc();
inc();
inc();
});
expect(result.current[0]).toEqual(4);
});
});
================================================
FILE: packages/hooks/src/useCounter/index.ts
================================================
import { useEffect } from 'react';
import useMemoizedFn from '../useMemoizedFn';
import useSafeState from '../useSafeState';
type Options = { min?: number; max?: number };
export type ValueParam = number | ((c: number) => number);
/**
* 步进器效果
* @param initialValue
* @param options
* @returns
*/
export default function useCounter(initialValue = 0, options: Options = {}) {
const [current, setCurrent] = useSafeState(0);
useEffect(() => {
setCurrent(getTargetValue(initialValue, options));
}, [initialValue, options]);
const setValue = (value: ValueParam) => {
setCurrent(c => {
const target = typeof value === 'number' ? value : value(c);
return getTargetValue(target, options);
});
};
const inc = (delta = 1) => {
setValue(c => c + delta);
};
const dec = (delta = 1) => {
setValue(c => c - delta);
};
const set = (value: ValueParam) => {
setValue(value);
};
const reset = () => {
setValue(initialValue);
};
const actions = {
inc: useMemoizedFn(inc),
dec: useMemoizedFn(dec),
set: useMemoizedFn(set),
reset: useMemoizedFn(reset),
};
return [current, actions] as const;
}
function getTargetValue(val: number, options: Options = {}) {
const { min, max } = options;
let target = val;
if (typeof max === 'number') {
target = Math.min(max, target);
}
if (typeof min === 'number') {
target = Math.max(min, target);
}
return target;
}
================================================
FILE: packages/hooks/src/useCreation/demo/demo1.tsx
================================================
import React, { useState } from 'react';
import { useCreation } from '@td-design/rn-hooks';
class Foo {
constructor() {
this.data = Math.random();
}
data: number;
}
export default function () {
const foo = useCreation(() => new Foo(), []);
const [, setFlag] = useState({});
return (
<>
<p>{foo.data}</p>
<button
type="button"
onClick={() => {
setFlag({});
}}
>
Rerender
</button>
</>
);
}
================================================
FILE: packages/hooks/src/useCreation/index.test.ts
================================================
import { useState } from 'react';
import { act, renderHook } from '@testing-library/react-hooks';
import useCreation from './index';
describe('useCreation', () => {
test('useCreation should be defined', () => {
expect(useCreation).toBeDefined();
});
class Foo {
data: number;
constructor() {
this.data = Math.random();
}
}
const hook = () =>
renderHook(() => {
const [count, setCount] = useState(0);
const [, setFlag] = useState({});
const foo = useCreation(() => new Foo(), [count]); // count发生变化之后返回的foo和之前的不是同一个
return {
foo,
count,
setCount,
setFlag,
};
});
test('useCreation should work like a charm', () => {
const { result } = hook();
const foo = result.current.foo;
act(() => {
result.current.setFlag({});
});
expect(result.current.foo).toBe(foo);
act(() => {
result.current.setCount(1);
});
expect(result.current.foo).not.toBe(foo);
});
});
================================================
FILE: packages/hooks/src/useCreation/index.ts
================================================
import { useRef } from 'react';
import { depsAreSame } from '../utils';
/**
* `useCreation` 是 `useMemo` 或 `useRef` 的替代品。
* 因为 `useMemo` 不能保证被 memo 的值一定不会被重计算,而 `useCreation` 可以保证这一点。
* 相比于 `useRef`,你可以使用 `useCreation` 创建一些常量,这些常量和 `useRef` 创建出来的 ref 有很多使用场景上的相似,但对于复杂常量的创建,`useRef` 却容易出现潜在的性能隐患。
* const a = useRef(new Subject()) // 每次重渲染,都会执行实例化 Subject 的过程,即便这个实例立刻就被扔掉了
* const b = useCreation(() => new Subject(), []) // 通过 factory 函数,可以避免性能隐患
*/
export default function useCreation<T>(factory: () => T, deps: any[]) {
const { current } = useRef({
deps,
initialized: false,
obj: undefined as T | undefined,
});
if (current.initialized === false || !depsAreSame(current.deps, deps)) {
current.deps = deps;
current.obj = factory();
current.initialized = true;
}
return current.obj as T;
}
================================================
FILE: packages/hooks/src/useDebounce/DebounceOptions.ts
================================================
export interface DebounceOptions {
/** 超时时间,单位为毫秒 */
wait?: number;
/** 是否在延迟开始前调用函数 */
leading?: boolean;
/** 是否在延迟开始后调用函数 */
trailing?: boolean;
}
================================================
FILE: packages/hooks/src/useDebounce/demo/demo1.tsx
================================================
import React, { useState } from 'react';
import { useDebounce } from '@td-design/rn-hooks';
export default () => {
const [value, setValue] = useState<string>();
const debouncedValue = useDebounce(value, { wait: 500 });
return (
<div>
<input value={value} onChange={e => setValue(e.target.value)} placeholder="Typed value" style={{ width: 280 }} />
<p style={{ marginTop: 16 }}>DebouncedValue: {debouncedValue}</p>
</div>
);
};
================================================
FILE: packages/hooks/src/useDebounce/index.test.ts
================================================
import { act, renderHook } from '@testing-library/react-hooks';
import { sleep } from '../utils/testHelpers';
import useDebounce from './index';
describe('useDebounce', () => {
it('should be defined', () => {
expect(useDebounce).toBeDefined();
});
it('useDebounce wait:200ms', async () => {
let mountedState = 0;
const { result, rerender } = renderHook(() => useDebounce(mountedState, { wait: 200 }));
expect(result.current).toEqual(0);
await act(async () => {
mountedState = 1;
rerender();
await sleep(50);
expect(result.current).toEqual(0);
mountedState = 2;
rerender();
await sleep(100);
expect(result.current).toEqual(0);
mountedState = 3;
rerender();
await sleep(150);
expect(result.current).toEqual(0);
mountedState = 4;
rerender();
await sleep(250);
expect(result.current).toEqual(4);
});
});
});
================================================
FILE: packages/hooks/src/useDebounce/index.ts
================================================
import { useEffect, useState } from 'react';
import useDebounceFn from '../useDebounceFn';
import type { DebounceOptions } from './DebounceOptions';
/**
* 用来处理防抖值的 Hook。
* @param value 需要防抖的值
* @param options 配置防抖的行为
* @returns
*/
export default function useDebounce<T>(value: T, options?: DebounceOptions) {
const [debounced, setDebounced] = useState(value);
const { run } = useDebounceFn(() => {
setDebounced(value);
}, options);
useEffect(() => {
run();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [value]); // 每次value变化的时候,都触发debounce的run方法
return debounced;
}
================================================
FILE: packages/hooks/src/useDebounceEffect/demo/demo1.tsx
================================================
import React, { useState } from 'react';
import { useDebounceEffect } from '@td-design/rn-hooks';
export default () => {
const [value, setValue] = useState('hello');
const [records, setRecords] = useState<string[]>([]);
useDebounceEffect(
() => {
setRecords(val => [...val, value]);
},
[value],
{
wait: 1000,
}
);
return (
<div>
<input value={value} onChange={e => setValue(e.target.value)} placeholder="Typed value" style={{ width: 280 }} />
<p style={{ marginTop: 16 }}>
<ul>
{records.map((record, index) => (
<li key={index}>{record}</li>
))}
</ul>
</p>
</div>
);
};
================================================
FILE: packages/hooks/src/useDebounceEffect/index.test.ts
================================================
import { act, renderHook } from '@testing-library/react-hooks';
import { sleep } from '../utils/testHelpers';
import useDebounceEffect from './index';
describe('useDebounceEffect', () => {
test('useDebounceEffect should be defined', () => {
expect(useDebounceEffect).toBeDefined();
});
test('useDebounceEffect should work like a charm', async () => {
const mockEffect = jest.fn();
const mockCleanup = jest.fn();
const { rerender } = renderHook(props =>
useDebounceEffect(
() => {
mockEffect();
return () => mockCleanup();
},
[props],
{ wait: 200 }
)
);
expect(mockEffect).not.toBeCalled();
expect(mockCleanup).not.toBeCalled();
await act(async () => {
rerender(2);
await sleep(50);
});
expect(mockEffect).not.toBeCalled();
expect(mockCleanup).not.toBeCalled();
await act(async () => {
rerender(3);
await sleep(400);
});
expect(mockEffect).toBeCalled();
expect(mockCleanup).not.toBeCalled();
});
test('useDebounceEffect should cancel timeout on unmount', async () => {
const mockEffect = jest.fn();
const mockCleanup = jest.fn();
const { rerender, unmount } = renderHook(props =>
useDebounceEffect(
() => {
mockEffect();
return () => mockCleanup();
},
[props],
{ wait: 200 }
)
);
await act(async () => {
rerender(4);
await sleep(400);
unmount();
});
expect(mockCleanup.mock.calls.length).toEqual(1);
});
});
================================================
FILE: packages/hooks/src/useDebounceEffect/index.ts
================================================
import { DependencyList, EffectCallback, useEffect, useState } from 'react';
import type { DebounceOptions } from '../useDebounce/DebounceOptions';
import useDebounceFn from '../useDebounceFn';
import useUpdateEffect from '../useUpdateEffect';
/**
* 为 `useEffect` 增加防抖的能力。
* @param effect 执行函数
* @param deps 依赖数组
* @param options 配置防抖的行为
*/
export default function useDebounceEffect(effect: EffectCallback, deps?: DependencyList, options?: DebounceOptions) {
const [flag, setFlag] = useState({});
const { run } = useDebounceFn(() => {
setFlag({});
}, options);
useEffect(() => {
return run();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, deps);
useUpdateEffect(effect, [flag]);
}
================================================
FILE: packages/hooks/src/useDebounceFn/demo/demo1.tsx
================================================
import React, { useState } from 'react';
import { useDebounceFn } from '@td-design/rn-hooks';
export default () => {
const [value, setValue] = useState(0);
const { run } = useDebounceFn(
() => {
setValue(value + 1);
},
{
wait: 500,
}
);
return (
<div>
<p style={{ marginTop: 16 }}> Clicked count: {value} </p>
<button type="button" onClick={run}>
Click fast!
</button>
</div>
);
};
================================================
FILE: packages/hooks/src/useDebounceFn/index.test.ts
================================================
import { act, renderHook } from '@testing-library/react-hooks';
import { sleep } from '../utils/testHelpers';
import useDebounceFn from './index';
describe('useDebounceFn', () => {
test('useDebounceFn should be defined', () => {
expect(useDebounceFn).toBeDefined();
});
test('useDebounceFn parameter should be function', () => {
const { result } = renderHook(() => useDebounceFn(12 as any, { wait: 200 }));
expect(result.error).toBeDefined();
});
test('debounce.run should work like a charm', async () => {
let count = 0;
function debounceFn(delta: number) {
count += delta;
}
const { result } = renderHook(() => useDebounceFn(debounceFn, { wait: 200 }));
act(() => {
result.current.run(2);
result.current.run(2);
result.current.run(2);
result.current.run(2);
});
expect(count).toBe(0);
await sleep(300);
expect(count).toBe(2);
act(() => {
result.current.run(4);
});
expect(count).toBe(2);
await sleep(300);
expect(count).toBe(6);
});
test('debounce.cancel should work like a charm', async () => {
let count = 0;
function debounceFn(delta: number) {
count += delta;
}
const { result } = renderHook(() => useDebounceFn(debounceFn, { wait: 200 }));
act(() => {
result.current.run(2);
result.current.run(2);
result.current.run(2);
result.current.run(2);
});
expect(count).toBe(0);
await sleep(300);
expect(count).toBe(2);
act(() => {
result.current.run(4);
});
expect(count).toBe(2);
act(() => {
result.current.cancel(); // result.current.run(4) 被取消,所以 count 还是 2
});
expect(count).toBe(2);
await sleep(300);
expect(count).toBe(2);
});
test('debounce.flush should work like a charm', async () => {
let count = 0;
function debounceFn(delta: number) {
count += delta;
}
const { result } = renderHook(() => useDebounceFn(debounceFn));
act(() => {
result.current.run(2);
result.current.run(2);
result.current.run(2);
result.current.run(2);
});
expect(count).toBe(0);
await sleep(1100);
expect(count).toBe(2);
act(() => {
result.current.run(4);
});
expect(count).toBe(2);
act(() => {
result.current.flush(); // 立即执行
});
expect(count).toBe(6);
await sleep(300);
expect(count).toBe(6);
});
});
================================================
FILE: packages/hooks/src/useDebounceFn/index.ts
================================================
import { debounce } from 'lodash-es';
import useCreation from '../useCreation';
import type { DebounceOptions } from '../useDebounce/DebounceOptions';
import useMemoizedFn from '../useMemoizedFn';
import useUnmount from '../useUnmount';
type noop = (...args: any) => any;
/**
* 用来处理防抖函数的 Hook。
* @param fn 需要防抖的函数
* @param options 配置防抖的行为
*/
export default function useDebounceFn<T extends noop>(fn: T, options?: DebounceOptions) {
if (__DEV__) {
if (typeof fn !== 'function') {
throw new Error(`useDebounceFn expected parameter is a function, got ${typeof fn}`);
}
}
const fnRef = useMemoizedFn(fn);
const wait = options?.wait ?? 1000;
const debounced = useCreation(
() =>
debounce(
(...args: Parameters<T>): ReturnType<T> => {
return fnRef(...args);
},
wait,
options
),
[]
);
useUnmount(() => {
debounced.cancel();
});
return {
run: debounced,
cancel: debounced.cancel,
flush: debounced.flush,
};
}
================================================
FILE: packages/hooks/src/useDeepCompareEffect/demo/demo1.tsx
================================================
import React, { useEffect, useRef, useState } from 'react';
import { useDeepCompareEffect } from '@td-design/rn-hooks';
export default () => {
const [count, setCount] = useState(0);
const effectCountRef = useRef(0);
const deepCompareCountRef = useRef(0);
useEffect(() => {
effectCountRef.current += 1;
}, [{}]);
useDeepCompareEffect(() => {
deepCompareCountRef.current += 1;
return () => {
// do something
};
}, [{}]);
return (
<div>
<p>effectCount: {effectCountRef.current}</p>
<p>deepCompareCount: {deepCompareCountRef.current}</p>
<p>
<button type="button" onClick={() => setCount(c => c + 1)}>
reRender
</button>
</p>
</div>
);
};
================================================
FILE: packages/hooks/src/useDeepCompareEffect/index.test.ts
================================================
import { useState } from 'react';
import { act, renderHook } from '@testing-library/react-hooks';
import useDeepCompareEffect from './index';
describe('useDeepCompareEffect', () => {
test('useDeepCompareEffect should be defined', () => {
expect(useDeepCompareEffect).toBeDefined();
});
test('useDeepCompareEffect should work like a charm', () => {
const { result } = renderHook(() => {
const [x, setX] = useState(0);
const [y, setY] = useState({});
useDeepCompareEffect(() => {
setX(x => x + 1);
}, [y]);
return { x, setY };
});
expect(result.current.x).toBe(1);
act(() => {
result.current.setY({}); // 对象的深比较会失败,所以 setX 不会执行
});
expect(result.current.x).toBe(1);
});
});
================================================
FILE: packages/hooks/src/useDeepCompareEffect/index.ts
================================================
/* eslint-disable */
import { DependencyList, EffectCallback, useEffect, useRef } from 'react';
import { isEqual } from 'lodash-es';
export default function useDeepCompareEffect(effect: EffectCallback, deps: DependencyList) {
const depsRef = useRef<DependencyList>();
const signalRef = useRef(0);
if (!isEqual(deps, depsRef.current)) {
depsRef.current = deps;
signalRef.current += 1;
}
useEffect(effect, [signalRef.current]);
}
================================================
FILE: packages/hooks/src/useDeviceOrientation/index.ts
================================================
import { useEffect } from 'react';
import { Dimensions, ScaledSize } from 'react-native';
import useDimensions from '../useDimensions';
import useSafeState from '../useSafeState';
const isOrientationPortrait = ({ width, height }: ScaledSize) => height >= width;
const isOrientationLandscape = ({ width, height }: ScaledSize) => width >= height;
export default function useDeviceOrientation() {
const { screen } = useDimensions();
const initialState = {
portrait: isOrientationPortrait(screen),
landscape: isOrientationLandscape(screen),
};
const [orientation, setOrientation] = useSafeState(initialState);
useEffect(() => {
const onChange = ({ screen }: { screen: ScaledSize }) => {
setOrientation({
portrait: isOrientationPortrait(screen),
landscape: isOrientationLandscape(screen),
});
};
const subscription = Dimensions.addEventListener('change', onChange);
return () => {
// @ts-ignore - React Native >= 0.65
if (typeof subscription?.remove === 'function') {
// @ts-ignore - need update @types/react-native@0.65.x
subscription.remove();
} else {
// @ts-ignore React Native < 0.65
Dimensions.removeEventListener('change', onChange);
}
};
}, [setOrientation]);
return orientation;
}
================================================
FILE: packages/hooks/src/useDimensions/index.ts
================================================
import { useEffect } from 'react';
import { Dimensions, ScaledSize } from 'react-native';
import useSafeState from '../useSafeState';
const initialValue = {
window: Dimensions.get('window'),
screen: Dimensions.get('screen'),
};
export default function useDimensions() {
const [dimensions, setDimensions] = useSafeState(initialValue);
useEffect(() => {
function onChange({ window, screen }: { window: ScaledSize; screen: ScaledSize }) {
setDimensions({ window, screen });
}
const subscription = Dimensions.addEventListener('change', onChange);
return () => {
// @ts-ignore - React Native >= 0.65
if (typeof subscription?.remove === 'function') {
// @ts-ignore - need update @types/react-native@0.65.x
subscription.remove();
} else {
// @ts-ignore React Native < 0.65
Dimensions.removeEventListener('change', onChange);
}
};
}, [setDimensions]);
return dimensions;
}
================================================
FILE: packages/hooks/src/useDynamicList/index.test.ts
================================================
import { act, renderHook } from '@testing-library/react-hooks';
import useDynamicList from './index';
describe('useDynamicList', () => {
const setUp = (props: any): any => renderHook(() => useDynamicList(props));
it('getKey should work', () => {
const hook = setUp([1, 2, 3]);
expect(hook.result.current.list[0]).toEqual(1);
expect(hook.result.current.getKey(0)).toEqual(0);
expect(hook.result.current.getKey(1)).toEqual(1);
expect(hook.result.current.getKey(2)).toEqual(2);
});
it('methods should work', () => {
const hook = setUp([
{ name: 'aaa', age: 18 },
{ name: 'bbb', age: 19 },
{ name: 'ccc', age: 20 },
]);
expect(hook.result.current.list[0].age).toEqual(18);
expect(hook.result.current.list[1].age).toEqual(19);
expect(hook.result.current.list[2].age).toEqual(20);
expect(hook.result.current.getKey(0)).toEqual(0);
expect(hook.result.current.getKey(1)).toEqual(1);
expect(hook.result.current.getKey(2)).toEqual(2);
// unshift
act(() => {
hook.result.current.unshift({ name: 'ddd', age: 21 });
});
expect(hook.result.current.list[0].name).toEqual('ddd');
expect(hook.result.current.getKey(0)).toEqual(3);
// push
act(() => {
hook.result.current.push({ name: 'ddd', age: 21 });
});
expect(hook.result.current.list[4].name).toEqual('ddd');
expect(hook.result.current.getKey(0)).toEqual(3);
expect(hook.result.current.getKey(4)).toEqual(4);
// insert
act(() => {
hook.result.current.insert(1, { name: 'eee', age: 22 });
});
expect(hook.result.current.list[1].name).toEqual('eee');
expect(hook.result.current.getKey(1)).toEqual(5);
// merge
act(() => {
hook.result.current.merge(0, [1, 2, 3, 4]);
});
expect(hook.result.current.list[0]).toEqual(1);
expect(hook.result.current.getKey(0)).toEqual(6);
// move
act(() => {
hook.result.current.move(0, 1);
});
expect(hook.result.current.list[0]).toEqual(2);
expect(hook.result.current.getKey(0)).toEqual(7);
// move without changes
act(() => {
hook.result.current.move(2, 2);
});
expect(hook.result.current.list[0]).toEqual(2);
expect(hook.result.current.getKey(0)).toEqual(7);
// shift
act(() => {
hook.result.current.shift();
});
expect(hook.result.current.list[0]).toEqual(1);
expect(hook.result.current.getKey(0)).toEqual(6);
expect(hook.result.current.list.length).toEqual(9);
// pop
act(() => {
hook.result.current.pop();
});
expect(hook.result.current.list.length).toEqual(8);
// replace
act(() => {
hook.result.current.replace(7, { value: 8 });
});
expect(hook.result.current.list[7].value).toEqual(8);
// remove
act(() => {
hook.result.current.remove(7);
});
expect(hook.result.current.list.length).toEqual(7);
});
it('same items should have different keys', () => {
const hook = setUp([1, 1, 1, 1]);
expect(hook.result.current.getKey(0)).toEqual(0);
expect(hook.result.current.getKey(1)).toEqual(1);
expect(hook.result.current.getKey(2)).toEqual(2);
expect(hook.result.current.getKey(3)).toEqual(3);
act(() => {
hook.result.current.push(1);
});
expect(hook.result.current.getKey(4)).toEqual(4);
const testObj = {};
act(() => {
hook.result.current.push({});
hook.result.current.push(testObj);
hook.result.current.push(testObj);
});
expect(hook.result.current.getKey(5)).toEqual(5);
expect(hook.result.current.getKey(6)).toEqual(6);
expect(hook.result.current.getKey(7)).toEqual(7);
});
it('initialValue changes', () => {
const hook = renderHook(({ initialValue }) => useDynamicList(initialValue), {
initialProps: {
initialValue: [1],
},
});
expect(hook.result.current.list[0]).toEqual(1);
expect(hook.result.current.getKey(0)).toEqual(0);
act(() => {
hook.result.current.reset([2]);
});
expect(hook.result.current.list[0]).toEqual(2);
expect(hook.result.current.getKey(0)).toEqual(1);
act(() => {
hook.result.current.reset([3]);
});
expect(hook.result.current.list[0]).toEqual(3);
expect(hook.result.current.getKey(0)).toEqual(2);
});
it('sortList', () => {
const hook = setUp([1, 2, 3, 4]);
const formData = [
{
name: 'my bro',
age: '23',
memo: "he's my bro",
},
{
name: 'my sis',
age: '21',
memo: "she's my sis",
},
null,
{
name: '新增行',
age: '25',
},
];
let sorted = hook.result.current.sort(formData);
expect(sorted.length).toEqual(3);
expect(sorted[0].name).toEqual('my bro');
act(() => {
hook.result.current.move(3, 0);
});
sorted = hook.result.current.sort(formData);
expect(sorted[0].name).toEqual('新增行');
});
});
================================================
FILE: packages/hooks/src/useDynamicList/index.ts
================================================
import { useCallback, useRef, useState } from 'react';
import useMemoizedFn from '../useMemoizedFn';
export default function useDynamicList<T>(initialList: T[] = []) {
const counterRef = useRef(-1);
const keyListRef = useRef<number[]>([]);
const setKey = useCallback((index: number) => {
counterRef.current += 1;
keyListRef.current.splice(index, 0, counterRef.current);
}, []);
const [list, setList] = useState(() => {
initialList.forEach((_, index) => {
setKey(index);
});
return initialList;
});
/**
* 重设List
* @param newList
*/
const reset = (newList: T[]) => {
keyListRef.current = [];
setList(() => {
newList.forEach((_, index) => {
setKey(index);
});
return newList;
});
};
/**
* 在指定位置插入一个新数据
* @param index
* @param item
*/
const insert = (index: number, item: T) => {
setList(l => {
const temp = [...l];
temp.splice(index, 0, item);
setKey(index);
return temp;
});
};
const getKey = (index: number) => {
return keyListRef.current[index];
};
const getIndex = (key: number) => {
return keyListRef.current.findIndex(item => item === key);
};
/**
* 从指定位置开始,合并数据
* @param index
* @param items
*/
const merge = (index: number, items: T[]) => {
setList(l => {
const temp = [...l];
items.forEach((_, i) => {
setKey(index + i);
});
temp.splice(index, 0, ...items);
return temp;
});
};
/**
* 替换指定位置的数据
* @param index
* @param item
*/
const replace = (index: number, item: T) => {
setList(l => {
const temp = [...l];
temp[index] = item;
return temp;
});
};
/**
* 删除指定位置的数据
* @param index
*/
const remove = (index: number) => {
setList(l => {
const temp = [...l];
temp.splice(index, 1);
try {
keyListRef.current.splice(index, 1);
} catch (e) {
console.error(e);
}
return temp;
});
};
/**
* 移动数据到新位置
* @param oldIndex
* @param newIndex
*/
const move = (oldIndex: number, newIndex: number) => {
if (oldIndex === newIndex) return;
setList(l => {
const newList = [...l];
const temp = newList.filter((_, index) => index !== oldIndex);
temp.splice(newIndex, 0, newList[oldIndex]);
try {
const keyTemp = keyListRef.current.filter((_, index) => index !== oldIndex);
keyTemp.splice(newIndex, 0, keyListRef.current[oldIndex]);
keyListRef.current = keyTemp;
} catch (error) {
console.error(error);
}
return temp;
});
};
/**
* 在当前数组中插入一个新的数据
* @param item
*/
const push = (item: T) => {
setList(l => {
setKey(l.length);
return l.concat([item]);
});
};
/**
* 从数组中删除最后一个数据,返回新数组
*/
const pop = () => {
try {
keyListRef.current = keyListRef.current.slice(0, keyListRef.current.length - 1);
} catch (error) {
console.error(error);
}
setList(l => l.slice(0, l.length - 1));
};
/**
* 在数组最前面插入一条新数据
* @param item
*/
const unshift = (item: T) => {
setList(l => {
setKey(0);
return [item].concat(l);
});
};
/**
* 删除数组的第一条数据,并返回新数组
*/
const shift = () => {
try {
keyListRef.current = keyListRef.current.slice(1, keyListRef.current.length);
} catch (error) {
console.error(error);
}
setList(l => l.slice(1, l.length));
};
const sort = (result: T[]) => {
return result
.map((item, index) => ({ key: index, item }))
.sort((a, b) => getIndex(a.key) - getIndex(b.key))
.filter(item => !!item.item)
.map(item => item.item);
};
return {
list,
insert: useMemoizedFn(insert),
merge: useMemoizedFn(merge),
replace: useMemoizedFn(replace),
remove: useMemoizedFn(remove),
getKey: useMemoizedFn(getKey),
getIndex: useMemoizedFn(getIndex),
move: useMemoizedFn(move),
push: useMemoizedFn(push),
pop: useMemoizedFn(pop),
unshift: useMemoizedFn(unshift),
shift: useMemoizedFn(shift),
sort: useMemoizedFn(sort),
reset: useMemoizedFn(reset),
};
}
================================================
FILE: packages/hooks/src/useEventEmitter/index.ts
================================================
/* eslint-disable */
import { useEffect, useRef } from 'react';
type Subscription<T> = (val: T) => void;
export class EventEmitter<T> {
private subscriptions = new Set<Subscription<T>>();
emit(val: T) {
for (const subscription of this.subscriptions) {
subscription(val);
}
}
useSubscription(cb: Subscription<T>) {
const callbackRef = useRef<Subscription<T>>();
callbackRef.current = cb;
useEffect(() => {
function subscription(val: T) {
if (callbackRef.current) {
callbackRef.current(val);
}
}
this.subscriptions.add(subscription);
return () => {
this.subscriptions.delete(subscription);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
}
}
export default function useEventEmitter<T = void>() {
const ref = useRef<EventEmitter<T>>();
if (!ref.current) {
ref.current = new EventEmitter();
}
return ref.current;
}
================================================
FILE: packages/hooks/src/useGetState/__tests__/index.test.ts
================================================
import { act, renderHook } from '@testing-library/react-hooks';
import useGetState from '../index';
describe('useGetState', () => {
const setUp = <T>(initialValue: T) =>
renderHook(() => {
const [state, setState, getState] = useGetState<T>(initialValue);
return {
state,
setState,
getState,
} as const;
});
it('should support initialValue', () => {
const hook = setUp(() => 0);
expect(hook.result.current.state).toEqual(0);
});
it('should support update', () => {
const hook = setUp(0);
act(() => {
hook.result.current.setState(1);
});
expect(hook.result.current.getState()).toEqual(1);
});
it('should getState frozen', () => {
const hook = setUp(0);
const prevGetState = hook.result.current.getState;
act(() => {
hook.result.current.setState(1);
});
expect(hook.result.current.getState).toEqual(prevGetState);
});
});
================================================
FILE: packages/hooks/src/useGetState/demo/demo1.tsx
================================================
/**
* title: Open console to view logs
* desc: The counter prints the value every 3 seconds
*
* title.zh-CN: 打开控制台查看输出
* desc.zh-CN: 计数器每 3 秒打印一次值
*/
import React, { useEffect } from 'react';
import { useGetState } from 'ahooks';
export default () => {
const [count, setCount, getCount] = useGetState<number>(0);
useEffect(() => {
const interval = setInterval(() => {
console.log('interval count', getCount());
}, 3000);
return () => {
clearInterval(interval);
};
}, []);
return <button onClick={() => setCount(count => count + 1)}>count: {count}</button>;
};
================================================
FILE: packages/hooks/src/useGetState/index.ts
================================================
import type { Dispatch, SetStateAction } from 'react';
import { useRef } from 'react';
import useMemoizedFn from '../useMemoizedFn';
import useSafeState from '../useSafeState';
type GetStateAction<S> = () => S;
function useGetState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>, GetStateAction<S>];
function useGetState<S = undefined>(): [
S | undefined,
Dispatch<SetStateAction<S | undefined>>,
GetStateAction<S | undefined>,
];
function useGetState<S>(initialState?: S) {
const [state, setState] = useSafeState(initialState);
const stateRef = useRef(state);
stateRef.current = state;
const getState = useMemoizedFn(() => stateRef.current);
return [state, setState, getState];
}
export default useGetState;
================================================
FILE: packages/hooks/src/useHistoryTravel/index.ts
================================================
import { useRef, useState } from 'react';
import { isNumber } from 'lodash-es';
import useMemoizedFn from '../useMemoizedFn';
type HistoryData<T> = {
present?: T;
past: T[];
future: T[];
};
export default function useHistoryTravel<T>(initialValue?: T, maxLength = 0) {
const [history, setHistory] = useState<HistoryData<T | undefined>>({
present: initialValue,
past: [],
future: [],
});
const { present, past, future } = history;
const initialValueRef = useRef(initialValue);
const reset = (...params: any[]) => {
const _initialValue = params.length > 0 ? params[0] : initialValueRef.current;
initialValueRef.current = _initialValue;
setHistory({
present: _initialValue,
past: [],
future: [],
});
};
const updateValue = (val: T) => {
const _past = [...past, present];
const maxLengthNum = isNumber(maxLength) ? maxLength : Number(maxLength);
// maximum number of records exceeded
if (maxLengthNum > 0 && _past.length > maxLengthNum) {
//delete first
_past.splice(0, 1);
}
setHistory({
present: val,
future: [],
past: _past,
});
};
// 内部方法,不对外暴露
const _forward = (step = 1) => {
if (future.length === 0) return;
const { _before, _current, _after } = split(step, future);
setHistory({
past: [...past, present, ..._before],
present: _current,
future: _after,
});
};
// 内部方法,不对外暴露
const _backward = (step = -1) => {
if (past.length === 0) return;
const { _before, _current, _after } = split(step, past);
setHistory({
past: _before,
present: _current,
future: [..._after, present, ...future],
});
};
const go = (step: number) => {
const stepNum = isNumber(step) ? step : Number(step);
if (stepNum === 0) {
return;
}
if (stepNum > 0) {
return _forward(stepNum);
}
_backward(stepNum);
};
return {
value: present,
backLength: past.length,
forwardLength: future.length,
setValue: useMemoizedFn(updateValue),
go: useMemoizedFn(go),
back: useMemoizedFn(() => {
go(-1);
}),
forward: useMemoizedFn(() => {
go(1);
}),
reset: useMemoizedFn(reset),
};
}
function split<T>(step: number, targetArr: T[]) {
const index = dumpIndex(step, targetArr);
return {
_current: targetArr[index],
_before: targetArr.slice(0, index),
_after: targetArr.slice(index + 1),
};
}
function dumpIndex<T>(step: number, arr: T[]) {
let index = step > 0 ? step - 1 : arr.length + step;
if (index >= arr.length - 1) {
index = arr.length - 1;
}
if (index < 0) {
index = 0;
}
return index;
}
================================================
FILE: packages/hooks/src/useInfiniteScroll/__tests__/index.test.ts
================================================
import { useState } from 'react';
import { act, renderHook } from '@testing-library/react-hooks';
import { sleep } from '../../utils/testHelpers';
import useInfiniteScroll from '../index';
async function mockRequest({ page, pageSize }: { page: number; pageSize: number }) {
await sleep(1000);
return {
page,
pageSize,
total: 30,
list: Array(10)
.fill('')
.map((_, index) => ({ id: (page - 1) * pageSize + index, name: `Cell${(page - 1) * pageSize + index}` })),
};
}
const setup = (service, options?: any) => renderHook(() => useInfiniteScroll(service, options));
describe('useInfiniteScroll', () => {
beforeAll(() => {
jest.useFakeTimers();
});
afterAll(() => {
jest.useRealTimers();
});
it('should auto load', async () => {
const { result } = setup(mockRequest);
expect(result.current.loading).toBeTruthy();
await act(async () => {
jest.advanceTimersByTime(1000);
});
expect(result.current.loading).toBeFalsy();
});
it('loadMore should work', async () => {
const { result } = setup(mockRequest);
expect(result.current.loading).toBeTruthy();
act(() => {
result.current.loadMore();
});
expect(result.current.loadingMore).toBeTruthy();
await act(async () => {
jest.advanceTimersByTime(1000);
});
expect(result.current.loadingMore).toBeFalsy();
});
it('refresh should work', async () => {
const fn = jest.fn(() => Promise.resolve({ list: [] }));
const { result } = setup(fn);
const { refresh } = result.current;
expect(fn).toBeCalledTimes(1);
await act(async () => {
refresh();
});
expect(fn).toBeCalledTimes(2);
});
it('refresh should be triggered when refreshDeps change', async () => {
const fn = jest.fn(() => Promise.resolve({ list: [], page: 1, pageSize: 10, total: 30 }));
const { result } = renderHook(() => {
const [value, setValue] = useState('');
const res = useInfiniteScroll(fn, {
refreshDeps: [value],
});
return {
...res,
setValue,
};
});
expect(fn).toBeCalledTimes(1);
act(() => {
result.current.setValue('ahooks');
});
expect(fn).toBeCalledTimes(2);
});
it('cancel should be work', () => {
const onSuccess = jest.fn();
const { result } = setup(mockRequest, {
onSuccess,
});
const { cancel } = result.current;
expect(result.current.loading).toBe(true);
act(() => cancel());
expect(result.current.loading).toBe(false);
expect(onSuccess).not.toBeCalled();
});
it('onBefore/onSuccess/onFinally should be called', async () => {
const onBefore = jest.fn();
const onSuccess = jest.fn();
const onFinally = jest.fn();
const { result } = setup(mockRequest, {
onBefore,
onSuccess,
onFinally,
});
await act(async () => {
jest.advanceTimersByTime(1000);
});
expect(onBefore).toBeCalled();
expect(onSuccess).toBeCalled();
expect(onFinally).toBeCalled();
});
it('onError should be called when throw error', async () => {
const onError = jest.fn();
const mockRequestError = () => {
return Promise.reject('error');
};
setup(mockRequestError, {
onError,
});
await act(async () => {
Promise.resolve();
});
expect(onError).toBeCalled();
});
it('loading should be true when refresh after loadMore', async () => {
const { result } = setup(mockRequest);
expect(result.current.loading).toBeTruthy();
const { refresh, loadMore } = result.current;
await act(async () => {
jest.advanceTimersByTime(1000);
});
expect(result.current.loading).toBeFalsy();
act(() => {
loadMore();
refresh();
});
expect(result.current.loading).toBeTruthy();
await act(async () => {
jest.advanceTimersByTime(1000);
});
expect(result.current.loading).toBeFalsy();
});
});
================================================
FILE: packages/hooks/src/useInfiniteScroll/index.ts
================================================
import { DependencyList } from 'react';
import useMemoizedFn from '../useMemoizedFn';
import useRequest from '../useRequest';
import useSafeState from '../useSafeState';
import useUpdateEffect from '../useUpdateEffect';
interface PageParams {
page: number;
pageSize: number;
}
interface Page<T> extends PageParams {
list: T[];
total: number;
totalPage?: number;
}
interface InfiniteScrollOptions<TData> {
manual?: boolean;
refreshDeps?: DependencyList;
onBefore?: () => void;
onSuccess?: (data: TData) => void;
onError?: (error: Error) => void;
onFinally?: (data?: TData, error?: Error) => void;
}
const INITIAL_PAGE = 1;
const INITIAL_PAGE_SIZE = 10;
function useInfiniteScroll<T>(
service: (data: PageParams) => Promise<Page<T>>,
options?: InfiniteScrollOptions<Page<T>>
) {
const { manual = false, refreshDeps = [], onBefore, onSuccess, onError, onFinally } = options || {};
const [data, setData] = useSafeState<Page<T>>();
const [loadingMore, setLoadingMore] = useSafeState(false);
const [noMoreData, setNoMoreData] = useSafeState(false);
const { loading, error, run, runAsync, cancel } = useRequest(
async (lastData: Page<T>) => {
const currentData = await service(
lastData
? {
page: lastData.page + 1,
pageSize: lastData.pageSize,
}
: {
page: INITIAL_PAGE,
pageSize: INITIAL_PAGE_SIZE,
}
);
if (!currentData) {
setNoMoreData(true);
return currentData;
}
setNoMoreData(currentData.page * currentData.pageSize >= currentData.total);
if (!lastData) {
setData({
...currentData,
list: [...(currentData.list || [])],
});
} else {
setData({
...currentData,
list: [...(lastData.list || []), ...(currentData.list || [])],
});
}
return currentData;
},
{
manual,
onBefore,
onSuccess,
onError,
onFinally(_, d, e) {
setLoadingMore(false);
onFinally?.(d, e);
},
}
);
const loadMore = useMemoizedFn(() => {
if (noMoreData) return;
setLoadingMore(true);
return run(data);
});
const refresh = useMemoizedFn(() => {
setLoadingMore(false);
return runAsync();
});
useUpdateEffect(() => {
run();
}, [...refreshDeps]);
return {
data: data?.list || [],
loading,
loadingMore,
noMoreData,
error,
loadMore,
refresh,
cancel,
mutate: setData,
};
}
export default useInfiniteScroll;
================================================
FILE: packages/hooks/src/useInterval/demo/demo1.tsx
================================================
import React, { useState } from 'react';
import { useInterval } from '@td-design/rn-hooks';
export default () => {
const [count, setCount] = useState(0);
useInterval(() => {
setCount(count + 1);
}, 1000);
return <div>count: {count}</div>;
};
================================================
FILE: packages/hooks/src/useInterval/demo/demo2.tsx
================================================
import React, { useState } from 'react';
import { useInterval } from '@td-design/rn-hooks';
export default () => {
const [count, setCount] = useState(0);
const [interval, setInterval] = useState(1000);
useInterval(
() => {
setCount(count + 1);
},
interval,
{ immediate: true }
);
return (
<div>
<p> count: {count} </p>
<p style={{ marginTop: 16 }}> interval: {interval} </p>
<button onClick={() => setInterval(interval + 1000)} style={{ marginRight: 8 }}>
interval + 1000
</button>
<button
style={{ marginRight: 8 }}
onClick={() => {
setInterval(1000);
}}
>
reset interval
</button>
<button
onClick={() => {
setInterval(null);
}}
>
clear
</button>
</div>
);
};
================================================
FILE: packages/hooks/src/useInterval/demo/demo3.tsx
================================================
import React, { Dispatch, SetStateAction, useState } from 'react';
import { useInterval } from '@td-design/rn-hooks';
const useManualInterval = (
fn: any,
interval: number | null | undefined,
options?: { immediate?: boolean }
): [boolean, Dispatch<SetStateAction<boolean>>] => {
const [enable, setEnable] = useState<boolean>(true);
useInterval(fn, enable ? interval : null, options);
return [enable, setEnable];
};
export default () => {
const [count, setCount] = useState<number>(0);
const [interval, setInterval] = useState<number>(500);
const [status, setStatus] = useManualInterval(
() => {
setCount(count + 1);
},
interval,
{ immediate: true }
);
return (
<>
<p style={{ marginTop: 16 }}> count: {count} </p>
<p style={{ marginTop: 16 }}> interval: {interval} </p>
<p style={{ marginTop: 16 }}> status: {status ? 'alive' : 'stopped'} </p>
<button onClick={() => setInterval(interval + 500)} style={{ marginRight: 12 }}>
interval + 500
</button>
<button
style={{ marginRight: 12 }}
onClick={() => {
setInterval(500);
}}
>
reset interval
</button>
<button
onClick={() => {
setStatus(!status);
}}
>
{status ? 'stop' : 'start'} interval
</button>
</>
);
};
================================================
FILE: packages/hooks/src/useInterval/index.test.ts
================================================
import { renderHook } from '@testing-library/react-hooks';
import useInterval from './index';
describe('useInterval', () => {
beforeEach(() => {
jest.useFakeTimers();
});
test('useInterval should be defined', () => {
expect(useInterval).toBeDefined();
});
test('useInterval should work like a charm', () => {
const callback = jest.fn();
renderHook(() => useInterval(callback, 20));
expect(callback).not.toBeCalled();
jest.advanceTimersByTime(200);
expect(callback).toHaveBeenCalledTimes(10);
});
test('userInterval should not work when delay is undefined', () => {
const callback = jest.fn();
renderHook(() => useInterval(callback));
expect(callback).not.toBeCalled();
jest.advanceTimersByTime(200);
expect(callback).not.toBeCalled();
});
test('immediate in options should work', () => {
const callback = jest.fn();
renderHook(() => useInterval(callback, 20, { immediate: true }));
expect(callback).toBeCalled();
expect(callback).toHaveBeenCalledTimes(1);
jest.advanceTimersByTime(50);
expect(callback).toHaveBeenCalledTimes(3);
});
});
================================================
FILE: packages/hooks/src/useInterval/index.ts
================================================
import { useEffect, useRef } from 'react';
import { isNumber } from 'lodash-es';
import useMemoizedFn from '../useMemoizedFn';
type Func = (...args: any[]) => any;
export default function useInterval(fn: Func, delay?: number, options?: { immediate: boolean }) {
const immediate = options?.immediate ?? false;
const timerCallback = useMemoizedFn(fn);
const timer = useRef<ReturnType<typeof setInterval>>();
useEffect(() => {
if (!isNumber(delay) || delay < 0) return;
if (immediate) {
timerCallback();
}
timer.current = setInterval(timerCallback, delay);
return clear;
}, [delay, immediate]);
const clear = useMemoizedFn(() => {
if (timer.current) {
clearInterval(timer.current);
}
});
return clear;
}
================================================
FILE: packages/hooks/src/useKeyboard/index.test.ts
================================================
import useKeyboard from './index';
describe('useKeyboard', () => {
test('useKeyboard should be defined', () => {
expect(useKeyboard).toBeDefined();
});
});
================================================
FILE: packages/hooks/src/useKeyboard/index.ts
================================================
import { useEffect } from 'react';
import { Keyboard, KeyboardMetrics } from 'react-native';
import useBoolean from '../useBoolean';
import useSafeState from '../useSafeState';
const emptyCoord = Object.freeze({
screenX: 0,
screenY: 0,
width: 0,
height: 0,
});
const initialValue = {
start: emptyCoord,
end: emptyCoord,
};
export default function useKeyboard() {
const [shown, { setTrue, setFalse }] = useBoolean(false);
const [coords, setCoords] = useSafeState<{ start?: KeyboardMetrics; end?: KeyboardMetrics }>(initialValue);
const [keyboardHeight, setKeyboardHeight] = useSafeState(0);
useEffect(() => {
const subscriptions = [
Keyboard.addListener('keyboardWillShow', e => {
setCoords({ start: e.startCoordinates, end: e.endCoordinates });
}),
Keyboard.addListener('keyboardDidShow', e => {
setTrue();
setCoords({ start: e.startCoordinates, end: e.endCoordinates });
setKeyboardHeight(e.endCoordinates.height);
}),
Keyboard.addListener('keyboardWillHide', e => {
setCoords({ start: e.startCoordinates, end: e.endCoordinates });
}),
Keyboard.addListener('keyboardDidHide', e => {
setFalse();
if (e) {
setCoords({ start: e.startCoordinates, end: e.endCoordinates });
} else {
setCoords(initialValue);
setKeyboardHeight(0);
}
}),
];
return () => {
subscriptions.forEach(subscription => subscription.remove());
};
}, [setCoords, setFalse, setKeyboardHeight, setTrue]);
return {
keyboardShown: shown,
keyboardHeight,
coords,
gitextract_oitcubhd/ ├── .changeset/ │ ├── README.md │ └── config.json ├── .commitlintrc.js ├── .cz-config.js ├── .editorconfig ├── .github/ │ └── workflows/ │ ├── changeset-version.yml │ ├── gh-pages.yml │ └── issue-close-require.yml ├── .gitignore ├── .husky/ │ ├── .gitignore │ ├── commit-msg │ └── pre-commit ├── .npmrc ├── .prettierignore ├── .prettierrc ├── .umirc.ts ├── .vscode/ │ └── settings.json ├── LICENSE ├── README.md ├── docs/ │ ├── development.md │ ├── friendlink.md │ ├── index.md │ ├── pattern/ │ │ ├── Strategy.md │ │ ├── index.md │ │ ├── signleton.md │ │ └── visitor.md │ ├── react-faq.md │ ├── react-native/ │ │ ├── index.md │ │ ├── restyle.md │ │ └── theme.md │ ├── react-perf.md │ ├── rn-faq.md │ ├── screen.md │ └── structure/ │ ├── array.md │ ├── index.md │ └── linkedList.md ├── jest.config.js ├── jest.setup.js ├── package.json ├── packages/ │ ├── cli/ │ │ ├── README.md │ │ ├── bin/ │ │ │ └── index.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ ├── setupApp.ts │ │ │ ├── setupSpa.ts │ │ │ └── utils/ │ │ │ ├── deleteOldDirs.ts │ │ │ ├── replaceProject.ts │ │ │ ├── translateFilePath.ts │ │ │ └── walk.ts │ │ ├── tsconfig.build.json │ │ └── types.d.ts │ ├── eslint-plugin-replace-hooks/ │ │ ├── .eslintrc.js │ │ ├── README.md │ │ ├── docs/ │ │ │ └── rules/ │ │ │ └── no-forbidden-hooks.md │ │ ├── gulpfile.js │ │ ├── package.json │ │ └── src/ │ │ ├── index.js │ │ └── rules/ │ │ └── no-forbidden-hooks.js │ ├── hooks/ │ │ ├── CHANGELOG.md │ │ ├── package.json │ │ ├── rollup.config.js │ │ ├── src/ │ │ │ ├── createUpdateEffect/ │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── useAccessibilityInfo/ │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useAppState/ │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useAsyncEffect/ │ │ │ │ ├── demo/ │ │ │ │ │ └── demo1.tsx │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useBackHandler/ │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useBoolean/ │ │ │ │ ├── demo/ │ │ │ │ │ └── demo1.tsx │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useControllableValue/ │ │ │ │ ├── demo/ │ │ │ │ │ ├── demo1.tsx │ │ │ │ │ ├── demo2.tsx │ │ │ │ │ └── demo3.tsx │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useCountdown/ │ │ │ │ └── index.ts │ │ │ ├── useCounter/ │ │ │ │ ├── demo/ │ │ │ │ │ └── demo1.tsx │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useCreation/ │ │ │ │ ├── demo/ │ │ │ │ │ └── demo1.tsx │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useDebounce/ │ │ │ │ ├── DebounceOptions.ts │ │ │ │ ├── demo/ │ │ │ │ │ └── demo1.tsx │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useDebounceEffect/ │ │ │ │ ├── demo/ │ │ │ │ │ └── demo1.tsx │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useDebounceFn/ │ │ │ │ ├── demo/ │ │ │ │ │ └── demo1.tsx │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useDeepCompareEffect/ │ │ │ │ ├── demo/ │ │ │ │ │ └── demo1.tsx │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useDeviceOrientation/ │ │ │ │ └── index.ts │ │ │ ├── useDimensions/ │ │ │ │ └── index.ts │ │ │ ├── useDynamicList/ │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useEventEmitter/ │ │ │ │ └── index.ts │ │ │ ├── useGetState/ │ │ │ │ ├── __tests__/ │ │ │ │ │ └── index.test.ts │ │ │ │ ├── demo/ │ │ │ │ │ └── demo1.tsx │ │ │ │ └── index.ts │ │ │ ├── useHistoryTravel/ │ │ │ │ └── index.ts │ │ │ ├── useInfiniteScroll/ │ │ │ │ ├── __tests__/ │ │ │ │ │ └── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useInterval/ │ │ │ │ ├── demo/ │ │ │ │ │ ├── demo1.tsx │ │ │ │ │ ├── demo2.tsx │ │ │ │ │ └── demo3.tsx │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useKeyboard/ │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useLatest/ │ │ │ │ ├── demo/ │ │ │ │ │ └── demo1.tsx │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useLayout/ │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useLockFn/ │ │ │ │ ├── demo/ │ │ │ │ │ └── demo1.tsx │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useMap/ │ │ │ │ ├── demo/ │ │ │ │ │ └── demo1.tsx │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useMemoizedFn/ │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useMount/ │ │ │ │ ├── demo/ │ │ │ │ │ └── demo1.tsx │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── usePagination/ │ │ │ │ ├── index.test.ts │ │ │ │ ├── index.ts │ │ │ │ ├── testingHelper.ts │ │ │ │ └── types.ts │ │ │ ├── usePrevious/ │ │ │ │ ├── demo/ │ │ │ │ │ ├── demo1.tsx │ │ │ │ │ └── demo2.tsx │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useRafInterval/ │ │ │ │ ├── demo/ │ │ │ │ │ ├── demo1.tsx │ │ │ │ │ └── demo2.tsx │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useRafState/ │ │ │ │ ├── demo/ │ │ │ │ │ └── demo1.tsx │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useRafTimeout/ │ │ │ │ ├── demo/ │ │ │ │ │ ├── demo1.tsx │ │ │ │ │ └── demo2.tsx │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useRequest/ │ │ │ │ ├── Fetch.ts │ │ │ │ ├── index.test.ts │ │ │ │ ├── index.ts │ │ │ │ ├── plugins/ │ │ │ │ │ ├── useAutoRunPlugin.ts │ │ │ │ │ ├── useCachePlugin.ts │ │ │ │ │ ├── useDebouncePlugin.ts │ │ │ │ │ ├── useLoadingDelayPlugin.ts │ │ │ │ │ ├── usePollingPlugin.ts │ │ │ │ │ ├── useRetryPlugin.ts │ │ │ │ │ └── useThrottlePlugin.ts │ │ │ │ ├── types.ts │ │ │ │ ├── useRequestImpl.ts │ │ │ │ └── utils/ │ │ │ │ ├── cache.ts │ │ │ │ ├── cachePromise.ts │ │ │ │ ├── cacheSubscribe.ts │ │ │ │ └── testingHelper.ts │ │ │ ├── useResetState/ │ │ │ │ ├── __tests__/ │ │ │ │ │ └── index.test.ts │ │ │ │ ├── demo/ │ │ │ │ │ └── demo1.tsx │ │ │ │ └── index.ts │ │ │ ├── useSafeState/ │ │ │ │ ├── demo/ │ │ │ │ │ └── demo1.tsx │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useSet/ │ │ │ │ ├── demo/ │ │ │ │ │ └── demo1.tsx │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useSetState/ │ │ │ │ └── index.ts │ │ │ ├── useSms/ │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useThrottle/ │ │ │ │ ├── ThrottleOptions.ts │ │ │ │ ├── demo/ │ │ │ │ │ └── demo1.tsx │ │ │ │ └── index.ts │ │ │ ├── useThrottleEffect/ │ │ │ │ ├── demo/ │ │ │ │ │ └── demo1.tsx │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useThrottleFn/ │ │ │ │ ├── demo/ │ │ │ │ │ └── demo1.tsx │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useTimeout/ │ │ │ │ ├── demo/ │ │ │ │ │ └── demo1.tsx │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useToggle/ │ │ │ │ ├── demo/ │ │ │ │ │ ├── demo1.tsx │ │ │ │ │ └── demo2.tsx │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useTrackedEffect/ │ │ │ │ └── index.ts │ │ │ ├── useUnmount/ │ │ │ │ ├── demo/ │ │ │ │ │ └── demo1.tsx │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useUnmountedRef/ │ │ │ │ ├── demo/ │ │ │ │ │ └── demo1.tsx │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useUpdate/ │ │ │ │ ├── demo/ │ │ │ │ │ └── demo1.tsx │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── useUpdateEffect/ │ │ │ │ └── index.ts │ │ │ ├── useUpdateLayoutEffect/ │ │ │ │ └── index.ts │ │ │ ├── useWhyDidYouUpdate/ │ │ │ │ ├── demo/ │ │ │ │ │ └── demo1.tsx │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ └── utils/ │ │ │ ├── index.ts │ │ │ ├── platform.ts │ │ │ └── testHelpers.ts │ │ └── tsconfig.json │ ├── lego/ │ │ ├── CHANGELOG.md │ │ ├── example/ │ │ │ ├── AccumulatedDataDemo/ │ │ │ │ ├── demo1.tsx │ │ │ │ └── demo2.tsx │ │ │ ├── BarLineDemo/ │ │ │ │ ├── demo1.tsx │ │ │ │ ├── demo10.tsx │ │ │ │ ├── demo11.tsx │ │ │ │ ├── demo12.tsx │ │ │ │ ├── demo2.tsx │ │ │ │ ├── demo3.tsx │ │ │ │ ├── demo4.tsx │ │ │ │ ├── demo5.tsx │ │ │ │ ├── demo6.tsx │ │ │ │ ├── demo7.tsx │ │ │ │ ├── demo8.tsx │ │ │ │ └── demo9.tsx │ │ │ ├── BasePieDemo/ │ │ │ │ ├── demo1.tsx │ │ │ │ ├── demo2.tsx │ │ │ │ ├── demo3.tsx │ │ │ │ ├── demo4.tsx │ │ │ │ ├── demo5.tsx │ │ │ │ ├── demo6.tsx │ │ │ │ └── demo7.tsx │ │ │ ├── CircularSolidPieDemo/ │ │ │ │ ├── demo1.tsx │ │ │ │ ├── demo2.tsx │ │ │ │ ├── demo3.tsx │ │ │ │ └── demo4.tsx │ │ │ ├── CuboidBarDemo/ │ │ │ │ ├── demo1.tsx │ │ │ │ ├── demo2.tsx │ │ │ │ ├── demo3.tsx │ │ │ │ ├── demo4.tsx │ │ │ │ ├── demo5.tsx │ │ │ │ ├── demo6.tsx │ │ │ │ ├── demo7.tsx │ │ │ │ └── demo8.tsx │ │ │ ├── CylinderBarDemo/ │ │ │ │ ├── demo1.tsx │ │ │ │ ├── demo2.tsx │ │ │ │ ├── demo3.tsx │ │ │ │ ├── demo4.tsx │ │ │ │ ├── demo5.tsx │ │ │ │ ├── demo6.tsx │ │ │ │ └── demo7.tsx │ │ │ ├── CylinderShadowBarDemo/ │ │ │ │ ├── demo1.tsx │ │ │ │ ├── demo2.tsx │ │ │ │ ├── demo3.tsx │ │ │ │ ├── demo4.tsx │ │ │ │ ├── demo5.tsx │ │ │ │ ├── demo6.tsx │ │ │ │ ├── demo7.tsx │ │ │ │ └── demo8.tsx │ │ │ ├── DataShowDemo/ │ │ │ │ └── demo1.tsx │ │ │ ├── DataShowSimpleDemo/ │ │ │ │ └── demo1.tsx │ │ │ ├── FlipNumberDemo/ │ │ │ │ ├── demo1.tsx │ │ │ │ ├── demo2.tsx │ │ │ │ ├── demo3.tsx │ │ │ │ └── demo4.tsx │ │ │ ├── FloatBallDemo/ │ │ │ │ ├── demo1.tsx │ │ │ │ ├── demo2.tsx │ │ │ │ ├── demo3.tsx │ │ │ │ └── demo4.tsx │ │ │ ├── GaugeDemo/ │ │ │ │ └── demo1.tsx │ │ │ ├── HorizontalBarDemo/ │ │ │ │ ├── demo1.tsx │ │ │ │ ├── demo2.tsx │ │ │ │ ├── demo3.tsx │ │ │ │ ├── demo4.tsx │ │ │ │ ├── demo5.tsx │ │ │ │ ├── demo6.tsx │ │ │ │ └── demo7.tsx │ │ │ ├── ImgLineDemo/ │ │ │ │ ├── demo1.tsx │ │ │ │ ├── demo2.tsx │ │ │ │ ├── demo3.tsx │ │ │ │ ├── demo4.tsx │ │ │ │ ├── demo5.tsx │ │ │ │ └── demo6.tsx │ │ │ ├── ImgPieDemo/ │ │ │ │ ├── demo1.tsx │ │ │ │ ├── demo2.tsx │ │ │ │ └── demo3.tsx │ │ │ ├── ImgRosePieDemo/ │ │ │ │ ├── demo1.tsx │ │ │ │ └── demo2.tsx │ │ │ ├── ModalDemo/ │ │ │ │ └── demo1.tsx │ │ │ ├── MultiHorizontalBarDemo/ │ │ │ │ ├── demo1.tsx │ │ │ │ ├── demo2.tsx │ │ │ │ ├── demo3.tsx │ │ │ │ └── demo4.tsx │ │ │ ├── MultiLineDemo/ │ │ │ │ ├── demo1.tsx │ │ │ │ ├── demo10.tsx │ │ │ │ ├── demo11.tsx │ │ │ │ ├── demo2.tsx │ │ │ │ ├── demo3.tsx │ │ │ │ ├── demo4.tsx │ │ │ │ ├── demo5.tsx │ │ │ │ ├── demo6.tsx │ │ │ │ ├── demo7.tsx │ │ │ │ ├── demo8.tsx │ │ │ │ └── demo9.tsx │ │ │ ├── PictorialBarDemo/ │ │ │ │ ├── demo1.tsx │ │ │ │ ├── demo2.tsx │ │ │ │ ├── demo3.tsx │ │ │ │ ├── demo4.tsx │ │ │ │ ├── demo5.tsx │ │ │ │ ├── demo6.tsx │ │ │ │ ├── demo7.tsx │ │ │ │ └── demo8.tsx │ │ │ ├── ProgressDemo/ │ │ │ │ ├── demo1.tsx │ │ │ │ ├── demo2.tsx │ │ │ │ └── demo3.tsx │ │ │ ├── RadarDemo/ │ │ │ │ ├── demo1.tsx │ │ │ │ ├── demo2.tsx │ │ │ │ └── demo3.tsx │ │ │ ├── ScatterDemo/ │ │ │ │ ├── demo1.tsx │ │ │ │ ├── demo2.tsx │ │ │ │ ├── demo3.tsx │ │ │ │ ├── demo4.tsx │ │ │ │ ├── demo5.tsx │ │ │ │ └── demo6.tsx │ │ │ ├── ScrollNumberDemo/ │ │ │ │ ├── demo1.tsx │ │ │ │ ├── demo2.tsx │ │ │ │ └── demo3.tsx │ │ │ ├── SliceBarDemo/ │ │ │ │ ├── demo1.tsx │ │ │ │ ├── demo2.tsx │ │ │ │ ├── demo3.tsx │ │ │ │ ├── demo4.tsx │ │ │ │ ├── demo5.tsx │ │ │ │ ├── demo6.tsx │ │ │ │ └── demo7.tsx │ │ │ ├── StackBarDemo/ │ │ │ │ ├── demo1.tsx │ │ │ │ ├── demo2.tsx │ │ │ │ ├── demo3.tsx │ │ │ │ ├── demo4.tsx │ │ │ │ ├── demo5.tsx │ │ │ │ ├── demo6.tsx │ │ │ │ ├── demo7.tsx │ │ │ │ └── demo8.tsx │ │ │ ├── SwiperDemo/ │ │ │ │ ├── demo1.tsx │ │ │ │ ├── demo2.tsx │ │ │ │ └── demo3.tsx │ │ │ ├── TableDemo/ │ │ │ │ ├── demo1.tsx │ │ │ │ ├── demo2.tsx │ │ │ │ ├── demo3.tsx │ │ │ │ ├── demo4.tsx │ │ │ │ ├── demo5.tsx │ │ │ │ ├── demo6.tsx │ │ │ │ └── index.less │ │ │ ├── TextScrollDemo/ │ │ │ │ ├── demo1.tsx │ │ │ │ ├── demo2.tsx │ │ │ │ └── demo3.tsx │ │ │ ├── ThreeDimensionalPieDemo/ │ │ │ │ ├── demo1.tsx │ │ │ │ ├── demo2.tsx │ │ │ │ └── demo3.tsx │ │ │ └── WordCloudDemo/ │ │ │ ├── demo1.tsx │ │ │ └── demo2.tsx │ │ ├── package.json │ │ ├── rollup.config.js │ │ ├── src/ │ │ │ ├── accumulated-data/ │ │ │ │ ├── index.less │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── bar-line/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── base-pie/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── circular-solid-pie/ │ │ │ │ ├── index.less │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── context/ │ │ │ │ └── ThemeContext.tsx │ │ │ ├── cuboid-bar/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── cylinder-bar/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── cylinder-shadow-bar/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── data-show/ │ │ │ │ ├── index.less │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── data-show-simple/ │ │ │ │ ├── index.less │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── flip-number/ │ │ │ │ ├── index.less │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── float-ball/ │ │ │ │ ├── index.less │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── gauge/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── hooks/ │ │ │ │ ├── useBaseBarConfig.ts │ │ │ │ ├── useBaseChartConfig.ts │ │ │ │ ├── useBaseLineConfig.ts │ │ │ │ ├── useBasePieConfig.ts │ │ │ │ ├── useChartLoop.ts │ │ │ │ ├── useEchartsRef.ts │ │ │ │ ├── useNodeBoundingRect.ts │ │ │ │ ├── useRAF.ts │ │ │ │ ├── useStyle.ts │ │ │ │ └── useTheme.ts │ │ │ ├── horizontal-bar/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── img-line/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── img-pie/ │ │ │ │ ├── index.less │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── img-rose-pie/ │ │ │ │ ├── index.less │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── index.ts │ │ │ ├── modal/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── multi-horizontal-bar/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── multi-line/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── pictorial-bar/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── progress/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── radar/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── registerShape.ts │ │ │ ├── scatter/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── scroll-number/ │ │ │ │ ├── index.less │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── slice-bar/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── stack-bar/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── style/ │ │ │ │ ├── font/ │ │ │ │ │ └── pmzd.otf │ │ │ │ └── index.less │ │ │ ├── swiper/ │ │ │ │ ├── index.less │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── table/ │ │ │ │ ├── index.less │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── text-scroll/ │ │ │ │ ├── index.less │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── theme.ts │ │ │ ├── three-dimensional-pie/ │ │ │ │ ├── index.less │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── utils/ │ │ │ │ ├── RAF.ts │ │ │ │ ├── createCuboidSeries.ts │ │ │ │ ├── createCylinderSeries.ts │ │ │ │ ├── createCylinderShadowSeries.ts │ │ │ │ ├── createLinearGradient.ts │ │ │ │ ├── createSliceSeries.ts │ │ │ │ └── createStackSeries.ts │ │ │ └── word-cloud/ │ │ │ ├── index.md │ │ │ └── index.tsx │ │ ├── tsconfig.json │ │ └── typings.d.ts │ ├── lego-map/ │ │ ├── CHANGELOG.md │ │ ├── example/ │ │ │ ├── BasicMapDemo/ │ │ │ │ ├── demo1.tsx │ │ │ │ ├── demo2.tsx │ │ │ │ ├── demo3.tsx │ │ │ │ ├── demo4.tsx │ │ │ │ ├── demo5.tsx │ │ │ │ ├── demo6.tsx │ │ │ │ ├── demo7.tsx │ │ │ │ └── sichuan.json │ │ │ ├── DrillMapDemo/ │ │ │ │ ├── demo1.tsx │ │ │ │ ├── demo2.tsx │ │ │ │ ├── demo3.tsx │ │ │ │ ├── demo4.tsx │ │ │ │ └── demo5.tsx │ │ │ └── SimpleMapDemo/ │ │ │ ├── demo1.tsx │ │ │ ├── demo2.tsx │ │ │ ├── demo3.tsx │ │ │ ├── demo4.tsx │ │ │ ├── demo5.tsx │ │ │ ├── demo6.tsx │ │ │ └── sichuan.json │ │ ├── package.json │ │ ├── rollup.config.js │ │ ├── src/ │ │ │ ├── assets/ │ │ │ │ ├── bgImage.js │ │ │ │ └── china.js │ │ │ ├── basic/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── drill/ │ │ │ │ ├── index.less │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── index.ts │ │ │ ├── simple/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ └── utils/ │ │ │ ├── baseSeries.ts │ │ │ ├── constant.ts │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── typings.d.ts │ ├── lego-video/ │ │ ├── CHANGELOG.md │ │ ├── example/ │ │ │ └── VideoDemo/ │ │ │ ├── demo1.tsx │ │ │ ├── demo2.tsx │ │ │ ├── demo3.tsx │ │ │ ├── demo4.tsx │ │ │ ├── demo5.tsx │ │ │ ├── demo6.tsx │ │ │ ├── demo7.tsx │ │ │ ├── demo8.tsx │ │ │ └── demo9.tsx │ │ ├── package.json │ │ ├── rollup.config.js │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── video/ │ │ │ ├── index.md │ │ │ └── index.tsx │ │ └── tsconfig.json │ ├── react-native/ │ │ ├── CHANGELOG.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── accordion/ │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ ├── type.ts │ │ │ │ └── useAccordion.ts │ │ │ ├── action-sheet/ │ │ │ │ ├── ActionSheetItem.tsx │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── avatar/ │ │ │ │ ├── Accessory/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── Avatar/ │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── useAvatar.ts │ │ │ │ ├── AvatarGroup/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ └── type.ts │ │ │ ├── badge/ │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ └── useBadge.ts │ │ │ ├── box/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── box-shadow/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── brief/ │ │ │ │ └── index.tsx │ │ │ ├── button/ │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ └── useButton.ts │ │ │ ├── button-group/ │ │ │ │ ├── Item.tsx │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── card/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── carousel/ │ │ │ │ ├── Bullets.tsx │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ ├── type.ts │ │ │ │ └── useCarousel.ts │ │ │ ├── center/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── checkbox/ │ │ │ │ ├── CheckboxItem.tsx │ │ │ │ ├── CheckboxList.tsx │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ ├── type.ts │ │ │ │ └── useCheckbox.ts │ │ │ ├── collapse-text/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── count-down/ │ │ │ │ ├── CountDownItem.tsx │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ └── type.ts │ │ │ ├── divider/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── empty/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── error-block/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── flex/ │ │ │ │ ├── FlexItem.tsx │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── float-button/ │ │ │ │ ├── PlusIcon.tsx │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── flow/ │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ └── step.tsx │ │ │ ├── form/ │ │ │ │ ├── FormItem.tsx │ │ │ │ ├── FormListItem.tsx │ │ │ │ ├── context.ts │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ └── type.ts │ │ │ ├── helpers/ │ │ │ │ ├── index.ts │ │ │ │ ├── normalize.ts │ │ │ │ └── renderNode.tsx │ │ │ ├── image/ │ │ │ │ └── index.md │ │ │ ├── image-header/ │ │ │ │ ├── AnimateHeader.tsx │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── index.ts │ │ │ ├── indicator/ │ │ │ │ ├── BallIndicator.tsx │ │ │ │ ├── Indicator.tsx │ │ │ │ ├── MaterialIndicator.tsx │ │ │ │ ├── UIActivityIndicator.tsx │ │ │ │ ├── index.md │ │ │ │ ├── index.ts │ │ │ │ └── type.ts │ │ │ ├── input/ │ │ │ │ ├── InputItem.tsx │ │ │ │ ├── TextArea.tsx │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ ├── useInput.ts │ │ │ │ ├── useInputItem.ts │ │ │ │ └── useTextArea.ts │ │ │ ├── label/ │ │ │ │ └── index.tsx │ │ │ ├── link/ │ │ │ │ ├── index.md │ │ │ │ └── index.ts │ │ │ ├── list/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── list-item/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── menu/ │ │ │ │ ├── Chevron.tsx │ │ │ │ ├── MenuGroup.tsx │ │ │ │ ├── MenuItem.tsx │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ ├── type.ts │ │ │ │ ├── useGroup.ts │ │ │ │ └── useMenu.ts │ │ │ ├── modal/ │ │ │ │ ├── Modal/ │ │ │ │ │ ├── ModalView.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── useModal.ts │ │ │ │ ├── alert/ │ │ │ │ │ ├── AlertContainer.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── confirm/ │ │ │ │ │ ├── ConfirmContainer.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── useConfirm.ts │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ ├── prompt/ │ │ │ │ │ ├── PromptContainer.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── usePrompt.ts │ │ │ │ ├── show/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── tip/ │ │ │ │ │ ├── TipContainer.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── type.ts │ │ │ ├── notice-bar/ │ │ │ │ ├── AnimatedNotice.tsx │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ └── type.ts │ │ │ ├── notify/ │ │ │ │ ├── NotifyRoot.tsx │ │ │ │ ├── constant.ts │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ ├── type.ts │ │ │ │ └── useNotify.ts │ │ │ ├── number-keyboard/ │ │ │ │ ├── NumberKeyboardInput.tsx │ │ │ │ ├── NumberKeyboardItem.tsx │ │ │ │ ├── NumberKeyboardModal.tsx │ │ │ │ ├── NumberKeyboardView.tsx │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ ├── type.ts │ │ │ │ ├── useNumberKeyboard.ts │ │ │ │ ├── useNumberKeyboardModal.ts │ │ │ │ └── util.ts │ │ │ ├── pagination/ │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ └── usePagination.ts │ │ │ ├── passcode/ │ │ │ │ ├── PasscodeItem.tsx │ │ │ │ ├── helpers.ts │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ ├── reducer.ts │ │ │ │ ├── type.ts │ │ │ │ └── usePasscode.ts │ │ │ ├── portal/ │ │ │ │ ├── PortalConsumer.tsx │ │ │ │ ├── PortalContext.ts │ │ │ │ ├── PortalGuard.ts │ │ │ │ ├── PortalHost.tsx │ │ │ │ ├── PortalManager.tsx │ │ │ │ └── index.tsx │ │ │ ├── pressable/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── progress/ │ │ │ │ ├── CircleProgress.tsx │ │ │ │ ├── LineProgress.tsx │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ ├── type.ts │ │ │ │ ├── useCircleProgress.ts │ │ │ │ └── useLineProgress.ts │ │ │ ├── radio/ │ │ │ │ ├── RadioItem.tsx │ │ │ │ ├── RadioList.tsx │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ ├── type.ts │ │ │ │ └── useRadio.ts │ │ │ ├── result/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── scroll-number/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── search-bar/ │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ └── useSearchBar.ts │ │ │ ├── slider/ │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ └── useSlider.ts │ │ │ ├── stepper/ │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ └── useStepper.ts │ │ │ ├── svg-icon/ │ │ │ │ ├── IconArrowdown.tsx │ │ │ │ ├── IconBells.tsx │ │ │ │ ├── IconCheck.tsx │ │ │ │ ├── IconCheckboxChecked.tsx │ │ │ │ ├── IconCheckboxHalfchecked.tsx │ │ │ │ ├── IconCheckboxUnchecked.tsx │ │ │ │ ├── IconCheckcircle.tsx │ │ │ │ ├── IconCheckcircleo.tsx │ │ │ │ ├── IconClockcircleo.tsx │ │ │ │ ├── IconClose.tsx │ │ │ │ ├── IconClosecircleo.tsx │ │ │ │ ├── IconDate.tsx │ │ │ │ ├── IconDown.tsx │ │ │ │ ├── IconEllipsis.tsx │ │ │ │ ├── IconEyeclose.tsx │ │ │ │ ├── IconEyeopen.tsx │ │ │ │ ├── IconLeft.tsx │ │ │ │ ├── IconMinus.tsx │ │ │ │ ├── IconPlus.tsx │ │ │ │ ├── IconRadioChecked.tsx │ │ │ │ ├── IconRadioUnchecked.tsx │ │ │ │ ├── IconReload.tsx │ │ │ │ ├── IconRight.tsx │ │ │ │ ├── IconSearch.tsx │ │ │ │ ├── IconUp.tsx │ │ │ │ ├── helper.ts │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── swipe-row/ │ │ │ │ ├── context.tsx │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ └── useSwipeRow.ts │ │ │ ├── switch/ │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ └── useSwitch.ts │ │ │ ├── table/ │ │ │ │ ├── Cell.tsx │ │ │ │ ├── Head.tsx │ │ │ │ ├── Rows.tsx │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ ├── type.ts │ │ │ │ ├── useTable.ts │ │ │ │ └── utils.ts │ │ │ ├── tag/ │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ └── useTag.ts │ │ │ ├── text/ │ │ │ │ ├── ReText.tsx │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── theme/ │ │ │ │ └── index.ts │ │ │ ├── theme-provider/ │ │ │ │ └── index.tsx │ │ │ ├── timeline/ │ │ │ │ ├── HorizontalTimeline.tsx │ │ │ │ ├── VerticalTimeline.tsx │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ └── type.ts │ │ │ ├── toast/ │ │ │ │ ├── ToastRoot.tsx │ │ │ │ ├── constant.ts │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ ├── type.ts │ │ │ │ └── useToast.ts │ │ │ ├── tooltip/ │ │ │ │ ├── Triangle.tsx │ │ │ │ ├── getTooltipCoordinate.tsx │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── tree/ │ │ │ │ ├── Checkbox.tsx │ │ │ │ ├── Chevron.tsx │ │ │ │ ├── TreeGroup.tsx │ │ │ │ ├── TreeNode.tsx │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ ├── type.ts │ │ │ │ ├── useGroup.ts │ │ │ │ ├── useTree.ts │ │ │ │ └── util.ts │ │ │ ├── utils/ │ │ │ │ ├── redash.ts │ │ │ │ └── ref-util.ts │ │ │ ├── vehicle-keyboard/ │ │ │ │ ├── VehicleKeyboardInput.tsx │ │ │ │ ├── VehicleKeyboardItem.tsx │ │ │ │ ├── VehicleKeyboardModal.tsx │ │ │ │ ├── VehicleKeyboardView.tsx │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ ├── type.ts │ │ │ │ ├── useVehicleKeyboard.ts │ │ │ │ └── useVehicleKeyboardModal.ts │ │ │ ├── white-space/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ └── wing-blank/ │ │ │ ├── index.md │ │ │ └── index.tsx │ │ ├── tsconfig.build.json │ │ ├── tsconfig.json │ │ └── typings.d.ts │ ├── react-native-alipay/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── android/ │ │ │ ├── README.md │ │ │ ├── build.gradle │ │ │ ├── libs/ │ │ │ │ └── alipaysdk-15.8.06.211122170115.aar │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── AndroidManifest.xml │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── thundersdata/ │ │ │ └── alipay/ │ │ │ ├── ReactNativeAlipayModule.java │ │ │ └── ReactNativeAlipayPackage.java │ │ ├── index.js │ │ ├── ios/ │ │ │ ├── ReactNativeAlipay.h │ │ │ ├── ReactNativeAlipay.m │ │ │ ├── ReactNativeAlipay.xcodeproj/ │ │ │ │ └── project.pbxproj │ │ │ └── ReactNativeAlipay.xcworkspace/ │ │ │ └── contents.xcworkspacedata │ │ ├── package.json │ │ └── react-native-alipay.podspec │ ├── react-native-amap-search/ │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── android/ │ │ │ ├── build.gradle │ │ │ ├── gradle/ │ │ │ │ └── wrapper/ │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ │ ├── gradlew │ │ │ ├── gradlew.bat │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── AndroidManifest.xml │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── reactnativeamapsearch/ │ │ │ ├── AmapSearchModule.java │ │ │ └── AmapSearchPackage.java │ │ ├── ios/ │ │ │ ├── AMapSearchManager.h │ │ │ ├── AMapSearchManager.m │ │ │ └── AmapSearch.xcodeproj/ │ │ │ └── project.pbxproj │ │ ├── package.json │ │ ├── react-native-amap-search.podspec │ │ ├── scripts/ │ │ │ └── bootstrap.js │ │ ├── src/ │ │ │ ├── constant.ts │ │ │ ├── index.tsx │ │ │ ├── typing.d.ts │ │ │ └── useSearch.tsx │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── react-native-calendar/ │ │ ├── CHANGELOG.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── components/ │ │ │ │ ├── Agenda/ │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── useAgenda.ts │ │ │ │ ├── Calendar/ │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── useCalendar.tsx │ │ │ │ ├── CalendarList/ │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── useCalendarList.ts │ │ │ │ ├── Day/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── Header/ │ │ │ │ │ └── index.tsx │ │ │ │ └── Period/ │ │ │ │ ├── index.tsx │ │ │ │ └── usePeriod.tsx │ │ │ ├── constant.ts │ │ │ ├── dateUtils.ts │ │ │ ├── index.md │ │ │ ├── index.tsx │ │ │ └── type.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── react-native-echarts/ │ │ ├── CHANGELOG.md │ │ ├── package.json │ │ ├── readMe.md │ │ ├── src/ │ │ │ ├── index.md │ │ │ ├── index.tsx │ │ │ ├── tmp/ │ │ │ │ └── tpl.html │ │ │ └── utils/ │ │ │ └── builder.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── react-native-image-picker/ │ │ ├── CHANGELOG.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.md │ │ │ ├── index.tsx │ │ │ ├── type.ts │ │ │ └── useImagePicker.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── react-native-password/ │ │ ├── CHANGELOG.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── Password.tsx │ │ │ ├── PasswordModal.tsx │ │ │ ├── index.md │ │ │ ├── index.tsx │ │ │ ├── usePassword.ts │ │ │ └── usePasswordModal.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── react-native-picker/ │ │ ├── CHANGELOG.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── cascade-picker/ │ │ │ │ ├── index.tsx │ │ │ │ └── useCascader.ts │ │ │ ├── components/ │ │ │ │ ├── DatePicker/ │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── type.ts │ │ │ │ │ └── useDatePicker.ts │ │ │ │ └── WheelPicker/ │ │ │ │ ├── index.tsx │ │ │ │ └── type.ts │ │ │ ├── date-period-input/ │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ └── useDatePeriodInput.tsx │ │ │ ├── date-picker/ │ │ │ │ ├── index.md │ │ │ │ ├── index.tsx │ │ │ │ └── useDatePicker.ts │ │ │ ├── date-picker-input/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── date-picker-item/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── index.ts │ │ │ ├── normal-picker/ │ │ │ │ ├── index.tsx │ │ │ │ └── useNormalPicker.ts │ │ │ ├── picker-input/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── picker-item/ │ │ │ │ ├── index.md │ │ │ │ └── index.tsx │ │ │ ├── type.ts │ │ │ ├── useDatePicker.tsx │ │ │ ├── usePicker.tsx │ │ │ └── utils.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── react-native-rating/ │ │ ├── CHANGELOG.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── SwipeRating.tsx │ │ │ ├── TapRating.tsx │ │ │ ├── components/ │ │ │ │ ├── Star.tsx │ │ │ │ └── SwipeStar.tsx │ │ │ ├── index.md │ │ │ ├── index.tsx │ │ │ ├── type.ts │ │ │ ├── useSwipeRating.ts │ │ │ └── useTapRating.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── react-native-share/ │ │ ├── CHANGELOG.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.md │ │ │ ├── index.tsx │ │ │ └── svg/ │ │ │ ├── alipay.tsx │ │ │ ├── dingding.tsx │ │ │ ├── moments.tsx │ │ │ ├── qq.tsx │ │ │ ├── qqmail.tsx │ │ │ ├── refresh.tsx │ │ │ ├── sina.tsx │ │ │ ├── sms.tsx │ │ │ ├── wechat.tsx │ │ │ └── zhihu.tsx │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── react-native-skeleton/ │ │ ├── CHANGELOG.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── ShiverBone.tsx │ │ │ ├── StaticBone.tsx │ │ │ ├── helper.ts │ │ │ ├── index.md │ │ │ ├── index.tsx │ │ │ └── type.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── react-native-tabs/ │ │ ├── CHANGELOG.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── SceneView.tsx │ │ │ ├── ScrollBar.tsx │ │ │ ├── TabBar.tsx │ │ │ ├── TabBarIndicator.tsx │ │ │ ├── TabBarItem.tsx │ │ │ ├── index.md │ │ │ ├── index.tsx │ │ │ ├── types.tsx │ │ │ └── usePagerView.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ └── svgicon-cli/ │ ├── CHANGELOG.md │ ├── README.md │ ├── gulpfile.js │ ├── package.json │ ├── src/ │ │ ├── commands/ │ │ │ ├── createIcon.ts │ │ │ └── createJson.ts │ │ ├── libs/ │ │ │ ├── copyTemplate.ts │ │ │ ├── generateComponent.ts │ │ │ ├── getConfig.ts │ │ │ ├── getTemplate.ts │ │ │ ├── parseLocalSvg.ts │ │ │ ├── replace.ts │ │ │ ├── svgicon.json │ │ │ └── whitespace.ts │ │ └── templates/ │ │ ├── Icon.tsx.template │ │ ├── LocalSingleIcon.tsx.template │ │ └── helper.ts.template │ ├── svgicon.json │ └── tsconfig.build.json ├── pnpm-workspace.yaml ├── tsconfig.json ├── turbo.json └── typings.d.ts
SYMBOL INDEX (745 symbols across 366 files)
FILE: .umirc.ts
method chainWebpack (line 30) | chainWebpack(config) {
FILE: packages/cli/src/index.ts
function validateProjectName (line 47) | function validateProjectName(name: string) {
FILE: packages/cli/src/setupApp.ts
method init (line 13) | init(projectName: string, branch: string) {
FILE: packages/cli/src/utils/deleteOldDirs.ts
function delDir (line 3) | function delDir(path: string) {
FILE: packages/cli/src/utils/replaceProject.ts
function replaceProject (line 12) | function replaceProject(srcPath: string, destPath: string, replacements:...
FILE: packages/cli/src/utils/translateFilePath.ts
function translateFilePath (line 11) | function translateFilePath(path: string) {
FILE: packages/cli/src/utils/walk.ts
function walk (line 6) | function walk(current: string): string[] {
FILE: packages/eslint-plugin-replace-hooks/gulpfile.js
function clean (line 7) | async function clean() {
function copy (line 12) | async function copy() {
FILE: packages/eslint-plugin-replace-hooks/src/rules/no-forbidden-hooks.js
method create (line 31) | create(context) {
FILE: packages/hooks/src/createUpdateEffect/index.ts
type effectHookType (line 4) | type effectHookType = typeof useEffect | typeof useLayoutEffect;
FILE: packages/hooks/src/useAccessibilityInfo/index.ts
type AccessibilityInfoStaticInitializers (line 6) | type AccessibilityInfoStaticInitializers =
function useAccessibilityStateListener (line 14) | function useAccessibilityStateListener(
function useAccessibilityInfo (line 40) | function useAccessibilityInfo() {
FILE: packages/hooks/src/useAppState/index.ts
function useAppState (line 6) | function useAppState() {
FILE: packages/hooks/src/useAsyncEffect/demo/demo1.tsx
function mockCheck (line 5) | function mockCheck(): Promise<boolean> {
FILE: packages/hooks/src/useAsyncEffect/index.ts
function isAsyncGenerator (line 6) | function isAsyncGenerator(
function useAsyncEffect (line 12) | function useAsyncEffect(effect: () => AsyncGenerator<void, void, void> |...
FILE: packages/hooks/src/useBackHandler/index.ts
function useBackHandler (line 4) | function useBackHandler(handler: () => boolean) {
FILE: packages/hooks/src/useBoolean/index.ts
function useBoolean (line 10) | function useBoolean(defaultValue = false) {
FILE: packages/hooks/src/useControllableValue/index.test.ts
method onChange (line 29) | onChange(v: number, extra: any) {
FILE: packages/hooks/src/useControllableValue/index.ts
type Options (line 8) | interface Options<T> {
type StandardProps (line 19) | interface StandardProps<T> {
type Props (line 24) | type Props = Record<string, any>;
function useControllableValue (line 31) | function useControllableValue<T = any>(props: Props = {}, options: Optio...
FILE: packages/hooks/src/useCountdown/index.ts
function useCountDown (line 6) | function useCountDown(seconds) {
function add (line 52) | function add(date: Date, seconds: number) {
function diff (line 56) | function diff(now: Date, target: Date) {
FILE: packages/hooks/src/useCounter/index.ts
type Options (line 6) | type Options = { min?: number; max?: number };
type ValueParam (line 7) | type ValueParam = number | ((c: number) => number);
function useCounter (line 15) | function useCounter(initialValue = 0, options: Options = {}) {
function getTargetValue (line 55) | function getTargetValue(val: number, options: Options = {}) {
FILE: packages/hooks/src/useCreation/demo/demo1.tsx
class Foo (line 5) | class Foo {
method constructor (line 6) | constructor() {
FILE: packages/hooks/src/useCreation/index.test.ts
class Foo (line 12) | class Foo {
method constructor (line 15) | constructor() {
FILE: packages/hooks/src/useCreation/index.ts
function useCreation (line 12) | function useCreation<T>(factory: () => T, deps: any[]) {
FILE: packages/hooks/src/useDebounce/DebounceOptions.ts
type DebounceOptions (line 1) | interface DebounceOptions {
FILE: packages/hooks/src/useDebounce/index.ts
function useDebounce (line 12) | function useDebounce<T>(value: T, options?: DebounceOptions) {
FILE: packages/hooks/src/useDebounceEffect/index.ts
function useDebounceEffect (line 13) | function useDebounceEffect(effect: EffectCallback, deps?: DependencyList...
FILE: packages/hooks/src/useDebounceFn/index.test.ts
function debounceFn (line 18) | function debounceFn(delta: number) {
function debounceFn (line 45) | function debounceFn(delta: number) {
function debounceFn (line 77) | function debounceFn(delta: number) {
FILE: packages/hooks/src/useDebounceFn/index.ts
type noop (line 8) | type noop = (...args: any) => any;
function useDebounceFn (line 15) | function useDebounceFn<T extends noop>(fn: T, options?: DebounceOptions) {
FILE: packages/hooks/src/useDeepCompareEffect/index.ts
function useDeepCompareEffect (line 6) | function useDeepCompareEffect(effect: EffectCallback, deps: DependencyLi...
FILE: packages/hooks/src/useDeviceOrientation/index.ts
function useDeviceOrientation (line 10) | function useDeviceOrientation() {
FILE: packages/hooks/src/useDimensions/index.ts
function useDimensions (line 11) | function useDimensions() {
FILE: packages/hooks/src/useDynamicList/index.ts
function useDynamicList (line 5) | function useDynamicList<T>(initialList: T[] = []) {
FILE: packages/hooks/src/useEventEmitter/index.ts
type Subscription (line 4) | type Subscription<T> = (val: T) => void;
class EventEmitter (line 6) | class EventEmitter<T> {
method emit (line 9) | emit(val: T) {
method useSubscription (line 15) | useSubscription(cb: Subscription<T>) {
function useEventEmitter (line 37) | function useEventEmitter<T = void>() {
FILE: packages/hooks/src/useGetState/index.ts
type GetStateAction (line 7) | type GetStateAction<S> = () => S;
function useGetState (line 15) | function useGetState<S>(initialState?: S) {
FILE: packages/hooks/src/useHistoryTravel/index.ts
type HistoryData (line 7) | type HistoryData<T> = {
function useHistoryTravel (line 13) | function useHistoryTravel<T>(initialValue?: T, maxLength = 0) {
function split (line 102) | function split<T>(step: number, targetArr: T[]) {
function dumpIndex (line 111) | function dumpIndex<T>(step: number, arr: T[]) {
FILE: packages/hooks/src/useInfiniteScroll/__tests__/index.test.ts
function mockRequest (line 8) | async function mockRequest({ page, pageSize }: { page: number; pageSize:...
FILE: packages/hooks/src/useInfiniteScroll/index.ts
type PageParams (line 8) | interface PageParams {
type Page (line 13) | interface Page<T> extends PageParams {
type InfiniteScrollOptions (line 19) | interface InfiniteScrollOptions<TData> {
constant INITIAL_PAGE (line 29) | const INITIAL_PAGE = 1;
constant INITIAL_PAGE_SIZE (line 30) | const INITIAL_PAGE_SIZE = 10;
function useInfiniteScroll (line 32) | function useInfiniteScroll<T>(
FILE: packages/hooks/src/useInterval/index.ts
type Func (line 7) | type Func = (...args: any[]) => any;
function useInterval (line 9) | function useInterval(fn: Func, delay?: number, options?: { immediate: bo...
FILE: packages/hooks/src/useKeyboard/index.ts
function useKeyboard (line 19) | function useKeyboard() {
FILE: packages/hooks/src/useLatest/index.ts
function useLatest (line 8) | function useLatest<T>(value: T) {
FILE: packages/hooks/src/useLayout/index.ts
function useLayout (line 4) | function useLayout() {
FILE: packages/hooks/src/useLockFn/demo/demo1.tsx
function mockApiRequest (line 6) | function mockApiRequest() {
FILE: packages/hooks/src/useLockFn/index.ts
function useLockFn (line 7) | function useLockFn<P extends any[] = any[], U = any>(fn: (...args: P) =>...
FILE: packages/hooks/src/useMap/index.ts
function useMap (line 10) | function useMap<K = any, T = any>(initialValue?: Iterable<readonly [K, T...
FILE: packages/hooks/src/useMemoizedFn/index.ts
type noop (line 5) | type noop = (this: any, ...args: any[]) => any;
type PickFunction (line 7) | type PickFunction<T extends noop> = (this: ThisParameterType<T>, ...args...
function useMemoizedFn (line 9) | function useMemoizedFn<T extends noop>(fn: T) {
FILE: packages/hooks/src/useMount/index.ts
type Func (line 5) | type Func = (...args: any[]) => any;
function useMount (line 11) | function useMount(fn: Func) {
FILE: packages/hooks/src/usePagination/index.test.ts
type PaginationParams (line 6) | type PaginationParams = { current: number; pageSize: number };
FILE: packages/hooks/src/usePagination/types.ts
type Data (line 3) | type Data = { total: number; list: any[] };
type Params (line 5) | type Params = [{ current: number; pageSize: number; [key: string]: any }...
type Service (line 7) | type Service<TData extends Data, TParams extends Params> = (...args: TPa...
type PaginationResult (line 9) | interface PaginationResult<TData extends Data, TParams extends Params> e...
type PaginationOptions (line 21) | interface PaginationOptions<TData extends Data, TParams extends Params> ...
FILE: packages/hooks/src/usePrevious/demo/demo2.tsx
type Person (line 8) | interface Person {
FILE: packages/hooks/src/usePrevious/index.test.ts
function setup (line 10) | function setup<T>(initialValue?: T, compareFunction?: ShouldUpdateFunc<T...
type Obj (line 61) | type Obj = { label: string; value: string };
FILE: packages/hooks/src/usePrevious/index.ts
type ShouldUpdateFunc (line 3) | type ShouldUpdateFunc<T> = (prev: T | undefined, next: T) => boolean;
function usePrevious (line 12) | function usePrevious<T>(
FILE: packages/hooks/src/useRafInterval/index.test.ts
constant FRAME_TIME (line 5) | const FRAME_TIME = 16.7;
FILE: packages/hooks/src/useRafInterval/index.ts
type Handle (line 7) | interface Handle {
function cancelAnimationFrameIsNotDefined (line 34) | function cancelAnimationFrameIsNotDefined(t: any): t is NodeJS.Timer {
function useRafInterval (line 45) | function useRafInterval(
FILE: packages/hooks/src/useRafState/index.ts
function useRafState (line 10) | function useRafState<S>(initialState?: S | (() => S)) {
FILE: packages/hooks/src/useRafTimeout/index.test.ts
type ParamsObj (line 5) | interface ParamsObj {
constant FRAME_TIME (line 12) | const FRAME_TIME = 16.7;
FILE: packages/hooks/src/useRafTimeout/index.ts
type Handle (line 7) | interface Handle {
function cancelAnimationFrameIsNotDefined (line 34) | function cancelAnimationFrameIsNotDefined(t: any): t is NodeJS.Timer {
function useRafTimeout (line 45) | function useRafTimeout(fn: () => void, delay?: number) {
FILE: packages/hooks/src/useRequest/Fetch.ts
class Fetch (line 6) | class Fetch<TData, TParams extends any[]> {
method constructor (line 18) | constructor(
method setState (line 31) | setState(s: Partial<FetchState<TData, TParams>> = {}) {
method runPluginHandler (line 39) | runPluginHandler(event: keyof PluginReturn<TData, TParams>, ...rest: a...
method runAsync (line 45) | async runAsync(...params: any[]): Promise<TData> {
method run (line 119) | run(...params: any[]) {
method cancel (line 127) | cancel() {
method refresh (line 136) | refresh() {
method refreshAsync (line 140) | refreshAsync() {
method mutate (line 144) | mutate(data?: TData | ((oldData?: TData) => TData | undefined)) {
FILE: packages/hooks/src/useRequest/index.ts
function useRequest (line 14) | function useRequest<TData, TParams extends any[]>(
FILE: packages/hooks/src/useRequest/types.ts
type Subscribe (line 6) | type Subscribe = () => void;
type Service (line 7) | type Service<TData, TParams extends any[]> = (...args: TParams) => Promi...
type Options (line 8) | type Options<TData, TParams extends any[]> = Record<string, any> & {
type Plugin (line 50) | type Plugin<TData, TParams extends any[]> = {
type PluginReturn (line 55) | type PluginReturn<TData, TParams extends any[]> = {
type FetchState (line 81) | type FetchState<TData, TParams extends any[]> = {
type Result (line 88) | type Result<TData, TParams extends any[]> = {
FILE: packages/hooks/src/useRequest/useRequestImpl.ts
function useRequestImpl (line 10) | function useRequestImpl<TData, TParams extends any[]>(
FILE: packages/hooks/src/useRequest/utils/cache.ts
type Timer (line 1) | type Timer = ReturnType<typeof setTimeout>;
type CachedKey (line 2) | type CachedKey = string | number;
type CachedData (line 4) | interface CachedData<TData = any, TParams = any> {
type RecordData (line 9) | interface RecordData extends CachedData {
FILE: packages/hooks/src/useRequest/utils/cachePromise.ts
type CachedKey (line 1) | type CachedKey = string | number;
FILE: packages/hooks/src/useRequest/utils/cacheSubscribe.ts
type Listener (line 1) | type Listener = (data: any) => void;
FILE: packages/hooks/src/useResetState/demo/demo1.tsx
type State (line 5) | interface State {
FILE: packages/hooks/src/useResetState/index.ts
type ResetState (line 6) | type ResetState = () => void;
FILE: packages/hooks/src/useSafeState/index.ts
function useSafeState (line 13) | function useSafeState<S>(initialState?: S | (() => S)) {
FILE: packages/hooks/src/useSet/index.ts
function useSet (line 10) | function useSet<K>(initialValue?: Iterable<K>) {
FILE: packages/hooks/src/useSetState/index.ts
type SetState (line 6) | type SetState<S extends Record<string, unknown>> = <K extends keyof S>(
FILE: packages/hooks/src/useSms/index.ts
type Props (line 7) | interface Props {
function useSms (line 27) | function useSms({
FILE: packages/hooks/src/useThrottle/ThrottleOptions.ts
type ThrottleOptions (line 1) | interface ThrottleOptions {
FILE: packages/hooks/src/useThrottle/index.ts
function useThrottle (line 12) | function useThrottle<T>(value: T, options?: ThrottleOptions) {
FILE: packages/hooks/src/useThrottleEffect/index.ts
function useThrottleEffect (line 10) | function useThrottleEffect(effect: EffectCallback, deps?: DependencyList...
FILE: packages/hooks/src/useThrottleFn/index.ts
type noop (line 8) | type noop = (...args: any) => any;
function useThrottleFn (line 15) | function useThrottleFn<T extends noop>(fn: T, options?: ThrottleOptions) {
FILE: packages/hooks/src/useTimeout/index.ts
type Func (line 7) | type Func = (...args: any[]) => any;
function useTimeout (line 9) | function useTimeout(fn: Func, delay?: number) {
FILE: packages/hooks/src/useToggle/index.ts
type Actions (line 5) | interface Actions<T> {
function useToggle (line 22) | function useToggle<D, R>(defaultValue = false as unknown as D, reverseVa...
FILE: packages/hooks/src/useTrackedEffect/index.ts
type Effect (line 3) | type Effect<T extends DependencyList> = (changes?: number[], previousDep...
function useTrackedEffect (line 5) | function useTrackedEffect<T extends DependencyList>(effect: Effect<T>, d...
function diffTwoDeps (line 19) | function diffTwoDeps(deps1?: DependencyList, deps2?: DependencyList) {
FILE: packages/hooks/src/useUnmount/index.ts
type Func (line 5) | type Func = (...args: any[]) => any;
function useUnmount (line 7) | function useUnmount(fn: Func) {
FILE: packages/hooks/src/useUnmountedRef/index.ts
function useUnmountedRef (line 7) | function useUnmountedRef() {
FILE: packages/hooks/src/useUpdate/index.ts
function useUpdate (line 9) | function useUpdate() {
FILE: packages/hooks/src/useWhyDidYouUpdate/index.ts
type Props (line 3) | type Props = Record<string, any>;
function useWhyDidYouUpdate (line 8) | function useWhyDidYouUpdate(componentName: string, props: Props) {
FILE: packages/hooks/src/utils/index.ts
function depsAreSame (line 1) | function depsAreSame(oldDeps: any[], deps: any[]): boolean {
FILE: packages/hooks/src/utils/platform.ts
function isIOS (line 3) | function isIOS() {
FILE: packages/hooks/src/utils/testHelpers.ts
function sleep (line 6) | function sleep(time: number) {
FILE: packages/lego-map/src/basic/index.tsx
type BasicMapProps (line 12) | interface BasicMapProps {
FILE: packages/lego-map/src/drill/index.tsx
type DrillMapProps (line 13) | interface DrillMapProps {
FILE: packages/lego-map/src/simple/index.tsx
type SimpleMapProps (line 12) | interface SimpleMapProps {
FILE: packages/lego-map/src/utils/baseSeries.ts
constant INITIAL_ZOOM (line 4) | const INITIAL_ZOOM = 1.2;
FILE: packages/lego-map/src/utils/constant.ts
constant INITIAL_MAP_NAME (line 2) | const INITIAL_MAP_NAME = 'china';
constant INITIAL_ADCODE (line 5) | const INITIAL_ADCODE = '100000';
FILE: packages/lego-map/src/utils/index.ts
type AMapDistrict (line 7) | interface AMapDistrict {
type DistrictInfo (line 14) | type DistrictInfo = Pick<AMapDistrict, 'adcode' | 'level' | 'name'> & {
function formatAdcode (line 22) | function formatAdcode(districts: AMapDistrict[], adcode: string) {
function register (line 47) | async function register(mapName: string, adcode: string) {
function loadUI (line 87) | function loadUI(mapName: string, adcode: string) {
FILE: packages/lego-map/typings.d.ts
type Window (line 11) | interface Window {
FILE: packages/lego-video/src/video/index.tsx
constant DEFAULT_PLAY_BACK_RATE (line 7) | const DEFAULT_PLAY_BACK_RATE = [0.5, 0.75, 1, 1.5, 2];
constant DEFAULT_LAST_PLAY_TIME_DELAY (line 10) | const DEFAULT_LAST_PLAY_TIME_DELAY = 5;
type PlayerProps (line 12) | interface PlayerProps extends Player {
type DefinitionItemProps (line 21) | interface DefinitionItemProps {
type VideoProps (line 27) | interface VideoProps extends Omit<IPlayerOptions, 'url' | 'loop'> {
constant DEFAULT_VOLUME (line 54) | const DEFAULT_VOLUME = 0.6;
FILE: packages/lego/example/TableDemo/demo4.tsx
type ColorEnum (line 4) | enum ColorEnum {
FILE: packages/lego/src/bar-line/index.tsx
type ECOption (line 36) | type ECOption = echarts.ComposeOption<
type CuboidBarParams (line 43) | type CuboidBarParams = {
type CylinderBarParams (line 48) | type CylinderBarParams = {
type CylinderShadowBarParams (line 53) | type CylinderShadowBarParams = {
type StackBarParams (line 58) | type StackBarParams = {
type SliceBarParams (line 63) | type SliceBarParams = {
type Params (line 68) | type Params = CuboidBarParams | CylinderBarParams | CylinderShadowBarPar...
type GetValueType (line 70) | type GetValueType<TType extends Params['barType']> = Extract<Params, { b...
type BarLineProps (line 72) | interface BarLineProps<TType extends Params['barType']> {
function BarLine (line 105) | function BarLine<TType extends Params['barType']>(
FILE: packages/lego/src/base-pie/index.tsx
type ECOption (line 27) | type ECOption = echarts.ComposeOption<PieSeriesOption | TooltipComponent...
type DataType (line 32) | type DataType = {
type BasePieProps (line 39) | interface BasePieProps {
FILE: packages/lego/src/circular-solid-pie/index.tsx
type ECOption (line 24) | type ECOption = echarts.ComposeOption<PieSeriesOption | TooltipComponent...
type CircularSolidPieProps (line 28) | interface CircularSolidPieProps {
FILE: packages/lego/src/cuboid-bar/index.tsx
type ECOption (line 27) | type ECOption = echarts.ComposeOption<CustomSeriesOption | TooltipCompon...
type CuboidBarProps (line 32) | interface CuboidBarProps {
FILE: packages/lego/src/cylinder-bar/index.tsx
type ECOption (line 27) | type ECOption = echarts.ComposeOption<CustomSeriesOption | TooltipCompon...
type CylinderBarProps (line 32) | interface CylinderBarProps {
FILE: packages/lego/src/cylinder-shadow-bar/index.tsx
type ECOption (line 29) | type ECOption = echarts.ComposeOption<CustomSeriesOption | TooltipCompon...
type CylinderShadowBarProps (line 34) | interface CylinderShadowBarProps {
FILE: packages/lego/src/data-show/index.tsx
type DataShowProps (line 11) | interface DataShowProps {
FILE: packages/lego/src/flip-number/index.tsx
type FlipNumberProps (line 8) | interface FlipNumberProps {
type FlipNumberRef (line 28) | interface FlipNumberRef {
FILE: packages/lego/src/float-ball/index.tsx
constant MAX_COUNT (line 11) | const MAX_COUNT = 4;
type LabeledValue (line 13) | interface LabeledValue {
type FloatBallProps (line 19) | interface FloatBallProps {
FILE: packages/lego/src/gauge/index.tsx
type GaugeProps (line 9) | interface GaugeProps {
FILE: packages/lego/src/hooks/useBaseBarConfig.ts
function useBaseBarConfig (line 10) | function useBaseBarConfig(inModal = false) {
FILE: packages/lego/src/hooks/useBaseChartConfig.ts
type ECOption (line 14) | type ECOption = echarts.ComposeOption<
function useBaseChartConfig (line 21) | function useBaseChartConfig(inModal = false, unit?: string) {
FILE: packages/lego/src/hooks/useBaseLineConfig.ts
function useBaseLineConfig (line 10) | function useBaseLineConfig(inModal = false) {
FILE: packages/lego/src/hooks/useBasePieConfig.ts
function useBasePieConfig (line 10) | function useBasePieConfig(inModal = false) {
FILE: packages/lego/src/hooks/useChartLoop.ts
function useChartLoop (line 8) | function useChartLoop(
FILE: packages/lego/src/hooks/useEchartsRef.ts
function useEchartsRef (line 7) | function useEchartsRef(ref: ForwardedRef<ReactEcharts> | ((ref: ReactEch...
FILE: packages/lego/src/hooks/useNodeBoundingRect.ts
function useNodeBoundingRect (line 3) | function useNodeBoundingRect(target: React.RefObject<HTMLDivElement>) {
FILE: packages/lego/src/hooks/useRAF.ts
function useRAF (line 5) | function useRAF() {
FILE: packages/lego/src/hooks/useStyle.ts
function useStyle (line 3) | function useStyle(style: CSSProperties = {}) {
FILE: packages/lego/src/hooks/useTheme.ts
function useTheme (line 5) | function useTheme() {
FILE: packages/lego/src/horizontal-bar/index.tsx
type ECOption (line 27) | type ECOption = echarts.ComposeOption<
type HorizontalBarProps (line 34) | interface HorizontalBarProps {
FILE: packages/lego/src/img-line/index.tsx
type ECOption (line 19) | type ECOption = echarts.ComposeOption<LineSeriesOption | TooltipComponen...
type ImgLineProps (line 23) | interface ImgLineProps {
FILE: packages/lego/src/img-pie/index.tsx
type ECOption (line 25) | type ECOption = echarts.ComposeOption<PieSeriesOption | TooltipComponent...
type ImgPieProps (line 29) | interface ImgPieProps {
FILE: packages/lego/src/img-rose-pie/index.tsx
type ECOption (line 27) | type ECOption = echarts.ComposeOption<PieSeriesOption | TooltipComponent...
type ImgRosePieProps (line 31) | interface ImgRosePieProps {
FILE: packages/lego/src/modal/index.tsx
type ModalProps (line 8) | interface ModalProps {
FILE: packages/lego/src/multi-horizontal-bar/index.tsx
type ECOption (line 27) | type ECOption = echarts.ComposeOption<
type MultiHorizontalBarProps (line 34) | interface MultiHorizontalBarProps {
function getArrByKey (line 377) | function getArrByKey(list: { name: string; value: number | string }[], k...
FILE: packages/lego/src/multi-line/index.tsx
type ECOption (line 18) | type ECOption = echarts.ComposeOption<LineSeriesOption | TooltipComponen...
type MultiLineProps (line 22) | interface MultiLineProps {
FILE: packages/lego/src/pictorial-bar/index.tsx
type ECOption (line 26) | type ECOption = echarts.ComposeOption<PictorialBarSeriesOption | Tooltip...
type PictorialBarProps (line 31) | interface PictorialBarProps {
FILE: packages/lego/src/progress/index.tsx
type ECOption (line 27) | type ECOption = echarts.ComposeOption<
type ProgressProps (line 34) | interface ProgressProps {
FILE: packages/lego/src/radar/index.tsx
type ECOption (line 15) | type ECOption = echarts.ComposeOption<RadarSeriesOption | TooltipCompone...
type IndicatorItem (line 19) | type IndicatorItem = { name: string; unit: string; max: string };
type RadarProps (line 21) | interface RadarProps {
FILE: packages/lego/src/registerShape.ts
function registerCuboidShape (line 3) | function registerCuboidShape() {
function registerCylinderShape (line 51) | function registerCylinderShape() {
FILE: packages/lego/src/scatter/index.tsx
type ECOption (line 28) | type ECOption = echarts.ComposeOption<
type ScatterProps (line 35) | interface ScatterProps {
FILE: packages/lego/src/scroll-number/index.tsx
type ScrollNumberProps (line 5) | interface ScrollNumberProps {
FILE: packages/lego/src/slice-bar/index.tsx
type ECOption (line 27) | type ECOption = echarts.ComposeOption<PictorialBarSeriesOption | Tooltip...
type SliceBarProps (line 32) | interface SliceBarProps {
FILE: packages/lego/src/stack-bar/index.tsx
type ECOption (line 28) | type ECOption = echarts.ComposeOption<CustomSeriesOption | TooltipCompon...
type StackBarProps (line 33) | interface StackBarProps {
FILE: packages/lego/src/swiper/index.tsx
type CustomSwiperProps (line 10) | interface CustomSwiperProps {
method enable (line 36) | enable() {
method disable (line 39) | disable() {
method slideNext (line 42) | slideNext() {
method slidePrev (line 45) | slidePrev() {
method slideReset (line 48) | slideReset() {
method slideTo (line 51) | slideTo(index: number, speed?: number) {
FILE: packages/lego/src/table/index.tsx
type TextAlign (line 12) | type TextAlign = 'center' | 'left' | 'right';
type Column (line 14) | type Column<T = any> = {
type CustomTableProps (line 25) | type CustomTableProps<T> = {
function Table (line 48) | function Table<T extends Record<string, any>>({
FILE: packages/lego/src/text-scroll/index.tsx
type TextScrollProps (line 6) | interface TextScrollProps {
FILE: packages/lego/src/theme.ts
type Theme (line 103) | type Theme = typeof theme;
FILE: packages/lego/src/three-dimensional-pie/index.tsx
type ECOption (line 18) | type ECOption = echarts.ComposeOption<PieSeriesOption | TooltipComponent...
constant BAR_WIDTH_COEFFICIENT (line 22) | const BAR_WIDTH_COEFFICIENT = 0.6;
type ThreeDimensionalPieProps (line 24) | interface ThreeDimensionalPieProps {
function getParametricEquation (line 258) | function getParametricEquation(
function getPie3D (line 333) | function getPie3D(
function generate3DHeight (line 509) | function generate3DHeight(isFlat: boolean, value = 30, coefficient = 1):...
FILE: packages/lego/src/utils/RAF.ts
type RAFType (line 5) | type RAFType = 'interval' | 'timeout';
type RAFCallback (line 6) | type RAFCallback = (...args: any[]) => void;
class RAF (line 8) | class RAF {
method constructor (line 11) | constructor() {
method run (line 17) | run(type: RAFType = 'interval', cb: RAFCallback, interval = 16.7) {
method setIdMap (line 38) | setIdMap(timerSymbol: symbol, type: RAFType, loop: () => void) {
method setTimeout (line 42) | setTimeout(cb: RAFCallback, interval: number) {
method clearTimeout (line 45) | clearTimeout(timer: symbol) {
method setInterval (line 48) | setInterval(cb: RAFCallback, interval: number) {
method clearInterval (line 51) | clearInterval(timer: symbol) {
FILE: packages/lego/src/utils/createCuboidSeries.ts
function createCuboidSeries (line 8) | function createCuboidSeries(theme: Theme, seriesData: BarSeriesData, uni...
FILE: packages/lego/src/utils/createCylinderSeries.ts
function createCylinderBarSeries (line 3) | function createCylinderBarSeries(data: BarSeriesData[], colors: any[], y...
function createCylinderSeries (line 11) | function createCylinderSeries(seriesData: BarSeriesData, color: any, yAx...
FILE: packages/lego/src/utils/createCylinderShadowSeries.ts
function createCylinderShadowSeries (line 4) | function createCylinderShadowSeries(
FILE: packages/lego/src/utils/createLinearGradient.ts
function createLinearGradient (line 8) | function createLinearGradient(color: string[], vertical = true): unknown {
FILE: packages/lego/src/utils/createSliceSeries.ts
function createSliceSeries (line 4) | function createSliceSeries(theme: Theme, seriesData: BarSeriesData, max:...
FILE: packages/lego/src/utils/createStackSeries.ts
function createStackSeries (line 3) | function createStackSeries(
FILE: packages/lego/src/word-cloud/index.tsx
type WordCloudProps (line 11) | interface WordCloudProps {
FILE: packages/lego/typings.d.ts
type Window (line 7) | interface Window {
type BarSeriesData (line 16) | interface BarSeriesData {
FILE: packages/react-native-alipay/android/src/main/java/com/thundersdata/alipay/ReactNativeAlipayModule.java
class ReactNativeAlipayModule (line 16) | public class ReactNativeAlipayModule extends ReactContextBaseJavaModule {
method ReactNativeAlipayModule (line 20) | public ReactNativeAlipayModule(ReactApplicationContext reactContext) {
method getName (line 25) | @Override
method openSandBox (line 30) | @ReactMethod
method sampleMethod (line 35) | @ReactMethod
method pay (line 41) | @ReactMethod
FILE: packages/react-native-alipay/android/src/main/java/com/thundersdata/alipay/ReactNativeAlipayPackage.java
class ReactNativeAlipayPackage (line 14) | public class ReactNativeAlipayPackage implements ReactPackage {
method createNativeModules (line 15) | @Override
method createViewManagers (line 20) | @Override
FILE: packages/react-native-amap-search/android/src/main/java/com/reactnativeamapsearch/AmapSearchModule.java
class AmapSearchModule (line 30) | @ReactModule(name = AmapSearchModule.NAME)
method AmapSearchModule (line 45) | public AmapSearchModule(ReactApplicationContext context) {
method getName (line 50) | @Override
method formatData (line 58) | public WritableArray formatData(ArrayList<PoiItem> poiItems){
method onPoiSearched (line 93) | @Override
method onRoutePoiSearched (line 106) | @Override
method onPoiItemSearched (line 124) | @Override
method initSDK (line 132) | @ReactMethod
method aMapPOIAroundSearch (line 140) | @ReactMethod
method aMapPOIKeywordsSearch (line 146) | @ReactMethod
method aMapPOIPolygonSearch (line 154) | @ReactMethod
method aMapRoutePOISearch (line 159) | @ReactMethod
method doAMapPOIAroundSearch (line 168) | protected void doAMapPOIAroundSearch(double latitude , double longitud...
method doAMapPOIAroundSearch (line 191) | protected void doAMapPOIAroundSearch(String keywords ,String city,Stri...
method doAMapPOIAroundSearch (line 212) | protected void doAMapPOIAroundSearch(ReadableArray points, String keyw...
method doRoutePOISearch (line 241) | protected void doRoutePOISearch(ReadableMap origin, ReadableMap destin...
FILE: packages/react-native-amap-search/android/src/main/java/com/reactnativeamapsearch/AmapSearchPackage.java
class AmapSearchPackage (line 14) | public class AmapSearchPackage implements ReactPackage {
method createNativeModules (line 15) | @NonNull
method createViewManagers (line 23) | @NonNull
FILE: packages/react-native-amap-search/src/constant.ts
constant RADIUS (line 1) | const RADIUS = 1500;
constant SPECIAL (line 2) | const SPECIAL = true;
constant PAGE (line 3) | const PAGE = 1;
constant PAGESIZE (line 4) | const PAGESIZE = 20;
FILE: packages/react-native-amap-search/src/typing.d.ts
type AMapGeoPoint (line 1) | interface AMapGeoPoint {
type SearchPOIParams (line 7) | interface SearchPOIParams {
type KeyWordsSearchPOIParams (line 28) | interface KeyWordsSearchPOIParams {
type PolygonSearchParams (line 45) | interface PolygonSearchParams {
type RouteSearchParams (line 58) | interface RouteSearchParams {
type ResultPOI (line 71) | interface ResultPOI {
FILE: packages/react-native-amap-search/src/useSearch.tsx
function useAMapSearch (line 7) | function useAMapSearch() {
FILE: packages/react-native-calendar/src/components/Agenda/index.tsx
function Agenda (line 14) | function Agenda<ItemT extends Item>({
FILE: packages/react-native-calendar/src/components/Agenda/useAgenda.ts
function useAgenda (line 14) | function useAgenda<ItemT extends Item>({ firstDay }: Pick<AgendaProps<It...
FILE: packages/react-native-calendar/src/components/Calendar/useCalendar.tsx
function useCalendar (line 16) | function useCalendar({
FILE: packages/react-native-calendar/src/components/CalendarList/useCalendarList.ts
function useCalendarList (line 12) | function useCalendarList({
FILE: packages/react-native-calendar/src/components/Period/index.tsx
constant HEIGHT (line 12) | const HEIGHT = DAY_WIDTH + px(20);
FILE: packages/react-native-calendar/src/components/Period/usePeriod.tsx
function usePeriod (line 10) | function usePeriod({ state, date, marking, onPress }: Omit<PeriodProps, ...
FILE: packages/react-native-calendar/src/constant.ts
constant WEEK_DAY_NAMES (line 5) | const WEEK_DAY_NAMES = ['日', '一', '二', '三', '四', '五', '六'];
constant CALENDAR_HEIGHT (line 7) | const CALENDAR_HEIGHT = px(400);
constant DAY_WIDTH (line 9) | const DAY_WIDTH = px(34);
FILE: packages/react-native-calendar/src/dateUtils.ts
function sameMonth (line 12) | function sameMonth(a: Dayjs, b: Dayjs) {
function sameDate (line 17) | function sameDate(a: Dayjs, b: Dayjs) {
function isGTE (line 28) | function isGTE(a: CurDateType, b: CurDateType) {
function isLTE (line 33) | function isLTE(a: CurDateType, b: CurDateType) {
function fromTo (line 38) | function fromTo(a: Dayjs, b: Dayjs) {
function month (line 50) | function month(date: Dayjs) {
function page (line 61) | function page(date: Dayjs, firstDayOfWeek = 0, showSixWeeks = false) {
function getRows (line 97) | function getRows(date: Dayjs, firstDayOfWeek = 0) {
function dateFormat (line 116) | function dateFormat(date?: Dayjs, format = 'YYYY-MM-DD') {
function dayjsToData (line 121) | function dayjsToData(date: Dayjs) {
FILE: packages/react-native-calendar/src/type.ts
type StateType (line 7) | type StateType = 'disabled' | 'today' | 'otherMonth';
type ArrowDirection (line 9) | type ArrowDirection = 'left' | 'right' | 'down' | 'up';
type CurDateType (line 11) | type CurDateType = string | Date | Dayjs;
type MarkedDates (line 13) | type MarkedDates = { [date: string]: PeriodMarking | DotMarking };
type DateObject (line 15) | interface DateObject {
type PeriodMarking (line 23) | interface PeriodMarking {
type DotMarking (line 31) | interface DotMarking {
type RowItem (line 39) | interface RowItem {
type DayProps (line 44) | interface DayProps {
type PeriodProps (line 57) | interface PeriodProps {
type CalendarHeaderProps (line 70) | interface CalendarHeaderProps {
type CalendarHeaderControlProps (line 90) | interface CalendarHeaderControlProps {
type CalendarProps (line 101) | interface CalendarProps extends Omit<CalendarHeaderProps, 'showDown' | '...
type CalendarListProps (line 130) | interface CalendarListProps extends CalendarProps {
type Item (line 143) | interface Item {
type AgendaProps (line 149) | interface AgendaProps<ItemT> extends Omit<CalendarProps, 'showSixWeeks'> {
FILE: packages/react-native-echarts/src/index.tsx
type EchartsProps (line 9) | interface EchartsProps {
type EchartsHandler (line 22) | interface EchartsHandler {
FILE: packages/react-native-echarts/src/utils/builder.ts
type EchartsInitOptions (line 13) | type EchartsInitOptions = {
FILE: packages/react-native-image-picker/src/type.ts
type ImagePickerRef (line 7) | interface ImagePickerRef {}
type ImagePickerProps (line 9) | type ImagePickerProps = PropsWithChildren<{
type HookProps (line 44) | type HookProps = Pick<
FILE: packages/react-native-image-picker/src/useImagePicker.ts
function getSource (line 11) | function getSource(value?: string) {
function useImagePicker (line 18) | function useImagePicker({
FILE: packages/react-native-password/src/Password.tsx
type PasswordInputRef (line 20) | interface PasswordInputRef {
FILE: packages/react-native-password/src/PasswordModal.tsx
type PasswordModalProps (line 19) | interface PasswordModalProps {
FILE: packages/react-native-password/src/index.tsx
function showPasswordModal (line 8) | function showPasswordModal(props: PasswordModalProps) {
FILE: packages/react-native-password/src/usePassword.ts
type PasswordProps (line 7) | interface PasswordProps {
function usePassword (line 22) | function usePassword({
FILE: packages/react-native-password/src/usePasswordModal.ts
function usePasswordModal (line 10) | function usePasswordModal({ length = 6, onDone }: Pick<PasswordModalProp...
FILE: packages/react-native-picker/src/cascade-picker/index.tsx
function Cascader (line 14) | function Cascader(
FILE: packages/react-native-picker/src/cascade-picker/useCascader.ts
function useCascader (line 9) | function useCascader({
function generateNextValue (line 78) | function generateNextValue<T>(data: CascadePickerItemProps<T>[], value: ...
FILE: packages/react-native-picker/src/components/DatePicker/type.ts
type Event (line 6) | type Event = SyntheticEvent<
type DateMode (line 12) | type DateMode = 'datetime' | 'date' | 'time' | 'month' | 'year';
type LabelUnit (line 13) | type LabelUnit = { year: string; month: string; day: string; hour: strin...
type DateUnit (line 15) | type DateUnit = 'year' | 'month' | 'date' | 'hour' | 'minute';
type DateRef (line 16) | type DateRef = { [key in DateUnit]: string };
type DatePickerPropsBase (line 17) | interface DatePickerPropsBase extends WheelPickerPropsBase {
FILE: packages/react-native-picker/src/components/DatePicker/useDatePicker.ts
function useDatePicker (line 9) | function useDatePicker<T>({
function pad (line 264) | function pad(n: number) {
function getDaysInMonth (line 268) | function getDaysInMonth(date: Date) {
FILE: packages/react-native-picker/src/components/WheelPicker/index.tsx
function WheelPicker (line 19) | function WheelPicker<T>({
function PickerItem (line 103) | function PickerItem<T>({
FILE: packages/react-native-picker/src/components/WheelPicker/type.ts
type PickerData (line 4) | type PickerData<T> = {
type CascadePickerItemProps (line 9) | interface CascadePickerItemProps<T> extends PickerData<T> {
type WheelPickerPropsBase (line 13) | type WheelPickerPropsBase = {
type WheelPickerProps (line 22) | type WheelPickerProps<T> = ViewProps &
type WheelPickerItemProps (line 33) | type WheelPickerItemProps<T> = {
FILE: packages/react-native-picker/src/date-period-input/index.tsx
type DatePeriodInputProps (line 11) | interface DatePeriodInputProps
FILE: packages/react-native-picker/src/date-period-input/useDatePeriodInput.tsx
function useDatePeriodInput (line 9) | function useDatePeriodInput({ value, onChange }: Pick<DatePeriodInputPro...
FILE: packages/react-native-picker/src/date-picker-input/index.tsx
type DatePickerInputProps (line 11) | interface DatePickerInputProps extends DatePickerPropsBase, Omit<ModalPi...
FILE: packages/react-native-picker/src/date-picker-item/index.tsx
type PickerItemProps (line 11) | interface PickerItemProps extends DatePickerPropsBase, Omit<ModalPickerP...
FILE: packages/react-native-picker/src/date-picker/index.tsx
type DatePickerProps (line 14) | type DatePickerProps = DatePickerPropsBase & ModalPickerProps;
FILE: packages/react-native-picker/src/date-picker/useDatePicker.ts
function useDatePicker (line 9) | function useDatePicker({
FILE: packages/react-native-picker/src/normal-picker/index.tsx
function NormalPicker (line 13) | function NormalPicker(props: NormalPickerProps, ref: React.ForwardedRef<...
FILE: packages/react-native-picker/src/normal-picker/useNormalPicker.ts
function useNormalPicker (line 8) | function useNormalPicker({
FILE: packages/react-native-picker/src/picker-input/index.tsx
function PickerInput (line 13) | function PickerInput({
FILE: packages/react-native-picker/src/picker-item/index.tsx
function PickerItem (line 11) | function PickerItem({
FILE: packages/react-native-picker/src/type.ts
type PickerRef (line 5) | interface PickerRef {
type DatePickerRef (line 10) | interface DatePickerRef {
type ModalPickerProps (line 17) | interface ModalPickerProps {
type CascaderProps (line 28) | type CascaderProps = WheelPickerPropsBase & {
type NormalPickerProps (line 38) | type NormalPickerProps = WheelPickerPropsBase & {
type PickerProps (line 46) | type PickerProps = CascaderProps | NormalPickerProps;
type PickerInputProps (line 48) | type PickerInputProps = PickerProps & {
type PickerItemProps (line 74) | type PickerItemProps = PickerProps & {
FILE: packages/react-native-picker/src/useDatePicker.tsx
function getText (line 10) | function getText(value?: Date, format?: string, placeholder?: string) {
function useDatePicker (line 17) | function useDatePicker({
FILE: packages/react-native-picker/src/usePicker.tsx
function getText (line 10) | function getText<T>(
function usePicker (line 23) | function usePicker<T extends string | number>({
FILE: packages/react-native-picker/src/utils.ts
function transformValueToLabel (line 10) | function transformValueToLabel<T>(
function findByValue (line 31) | function findByValue<T>(data: CascadePickerItemProps<T>[], value: T): Ca...
FILE: packages/react-native-rating/src/TapRating.tsx
constant STAR_SIZE (line 11) | const STAR_SIZE = px(40);
FILE: packages/react-native-rating/src/components/Star.tsx
constant STAR_IMAGE (line 12) | const STAR_IMAGE =
constant STAR_SELECTED_IMAGE (line 14) | const STAR_SELECTED_IMAGE =
FILE: packages/react-native-rating/src/type.ts
type TapRatingProps (line 3) | interface TapRatingProps {
type StarProps (line 34) | type StarProps = Required<
type SwipeRatingProps (line 46) | interface SwipeRatingProps {
FILE: packages/react-native-rating/src/useSwipeRating.ts
function useSwipeRating (line 6) | function useSwipeRating({
FILE: packages/react-native-rating/src/useTapRating.ts
function useTapRating (line 7) | function useTapRating({
FILE: packages/react-native-share/src/index.tsx
type ShareItem (line 18) | interface ShareItem {
type ShareAction (line 26) | interface ShareAction {
type ShareProps (line 31) | interface ShareProps {
FILE: packages/react-native-skeleton/src/helper.ts
function calc (line 1) | function calc(val: number, percent: string) {
FILE: packages/react-native-skeleton/src/index.tsx
constant DEFAULT_BORDER_RADIUS (line 13) | const DEFAULT_BORDER_RADIUS = 4;
FILE: packages/react-native-skeleton/src/type.ts
type AnimationType (line 5) | type AnimationType = 'none' | 'shiver' | 'pulse' | undefined;
type AnimationDirection (line 6) | type AnimationDirection = 'horizontalLeft' | 'horizontalRight' | 'vertic...
type Direction (line 8) | interface Direction {
type SkeletonProps (line 13) | type SkeletonProps = PropsWithChildren<{
type ShiverBoneProps (line 34) | interface ShiverBoneProps extends Pick<SkeletonProps, 'animationDirectio...
type StaticBoneProps (line 41) | interface StaticBoneProps extends Pick<SkeletonProps, 'animationType' | ...
FILE: packages/react-native-tabs/src/SceneView.tsx
function SceneView (line 6) | function SceneView({
FILE: packages/react-native-tabs/src/ScrollBar.tsx
type ScrollBarProps (line 6) | interface ScrollBarProps {
function ScrollBar (line 11) | function ScrollBar({ height, page, children }: PropsWithChildren<ScrollB...
FILE: packages/react-native-tabs/src/TabBar.tsx
type TabBarProps (line 21) | interface TabBarProps {
function TabBar (line 38) | function TabBar({
function getIndicatorWidth (line 182) | function getIndicatorWidth(style?: StyleProp<ViewStyle>) {
FILE: packages/react-native-tabs/src/TabBarIndicator.tsx
function TabBarIndicator (line 6) | function TabBarIndicator({
FILE: packages/react-native-tabs/src/TabBarItem.tsx
type TabBarItemProps (line 6) | interface TabBarItemProps {
FILE: packages/react-native-tabs/src/index.tsx
type Tab (line 15) | type Tab = {
type TabsProps (line 20) | interface TabsProps {
function Tabs (line 46) | function Tabs({
FILE: packages/react-native-tabs/src/types.tsx
type Route (line 1) | type Route = {
type Scene (line 7) | type Scene<T extends Route> = {
type NavigationState (line 11) | type NavigationState<T extends Route> = {
FILE: packages/react-native-tabs/src/usePagerView.ts
function usePagerView (line 11) | function usePagerView(initialPage: number, page?: number, onChange?: (pa...
FILE: packages/react-native/src/accordion/type.ts
type Section (line 5) | interface Section {
type AccordionProps (line 10) | interface AccordionProps {
type SectionProps (line 27) | interface SectionProps extends Pick<AccordionProps, 'contentStyle' | 'cu...
type SectionHeaderProps (line 35) | interface SectionHeaderProps
FILE: packages/react-native/src/accordion/useAccordion.ts
function useAccordion (line 14) | function useAccordion({
FILE: packages/react-native/src/action-sheet/ActionSheetItem.tsx
type ActionSheetItemProps (line 7) | interface ActionSheetItemProps {
function ActionSheetItem (line 17) | function ActionSheetItem({
FILE: packages/react-native/src/action-sheet/index.tsx
type ActionSheetProps (line 19) | interface ActionSheetProps {
FILE: packages/react-native/src/avatar/Avatar/useAvatar.ts
function useAvatar (line 5) | function useAvatar({ size = px(46), borderRadius = 0, circular = true }:...
FILE: packages/react-native/src/avatar/type.ts
type AccessoryProps (line 4) | interface AccessoryProps {
type AvatarGroupProps (line 16) | type AvatarGroupProps = PropsWithChildren<{
type AvatarProps (line 27) | type AvatarProps = {
FILE: packages/react-native/src/badge/index.tsx
constant DOT_SIZE (line 8) | const DOT_SIZE = 8;
type BadgeProps (line 9) | interface BadgeProps {
FILE: packages/react-native/src/badge/useBadge.ts
function useBadge (line 5) | function useBadge() {
FILE: packages/react-native/src/button-group/Item.tsx
type ButtonGroupOption (line 11) | interface ButtonGroupOption {
type ItemProps (line 17) | interface ItemProps extends ButtonGroupOption {
FILE: packages/react-native/src/button-group/index.tsx
type ButtonGroupProps (line 11) | interface ButtonGroupProps {
FILE: packages/react-native/src/button/index.tsx
type ButtonProps (line 11) | type ButtonProps = PressableProps & {
FILE: packages/react-native/src/button/useButton.ts
function useButton (line 13) | function useButton(props: ButtonProps) {
FILE: packages/react-native/src/card/index.tsx
type CardProps (line 14) | interface CardProps {
FILE: packages/react-native/src/carousel/type.ts
type AlignType (line 3) | type AlignType = 'left' | 'center' | 'right';
type CarouselProps (line 4) | type CarouselProps = PropsWithChildren<{
FILE: packages/react-native/src/carousel/useCarousel.ts
function useCarousel (line 9) | function useCarousel({
FILE: packages/react-native/src/checkbox/type.ts
type CheckboxStatus (line 4) | type CheckboxStatus = 'checked' | 'unchecked' | 'halfchecked';
type CheckboxOption (line 6) | interface CheckboxOption {
type TransformedOption (line 10) | interface TransformedOption extends CheckboxOption {
type CheckboxProps (line 17) | interface CheckboxProps {
type CheckboxItemProps (line 42) | type CheckboxItemProps = Pick<CheckboxProps, 'itemStyle' | 'labelStyle' ...
FILE: packages/react-native/src/checkbox/useCheckbox.ts
function useCheckbox (line 7) | function useCheckbox(
function getCheckedAllStatus (line 97) | function getCheckedAllStatus<T extends TransformedOption>(options: T[] =...
FILE: packages/react-native/src/collapse-text/index.tsx
type CollapseTextProps (line 13) | interface CollapseTextProps {
FILE: packages/react-native/src/count-down/type.ts
type SmsProps (line 4) | interface SmsProps {
type CountDownItemProps (line 22) | interface CountDownItemProps extends InputItemProps, SmsProps {}
type CountDownProps (line 24) | interface CountDownProps extends InputProps, SmsProps {}
FILE: packages/react-native/src/divider/index.tsx
type DividerProps (line 14) | type DividerProps = {
FILE: packages/react-native/src/empty/index.tsx
type EmptyProps (line 10) | type EmptyProps = BoxProps<Theme> & {
FILE: packages/react-native/src/error-block/index.tsx
class ErrorBlock (line 8) | class ErrorBlock extends React.Component<
method getDerivedStateFromError (line 21) | static getDerivedStateFromError(error: Error) {
method componentDidCatch (line 25) | componentDidCatch(error: Error, info: ErrorInfo) {
method render (line 51) | render() {
function Fallback (line 59) | function Fallback({ onRefresh, type }: { onRefresh: () => void; type?: '...
FILE: packages/react-native/src/flex/FlexItem.tsx
type FlexItemProps (line 8) | type FlexItemProps = PropsWithChildren<Omit<BoxProps<Theme>, 'width'>>;
FILE: packages/react-native/src/float-button/index.tsx
type FloatButtonItem (line 23) | interface FloatButtonItem {
type FloatButtonProps (line 30) | interface FloatButtonProps {
function FloatButton (line 44) | function FloatButton({
FILE: packages/react-native/src/flow/index.tsx
type FlowProps (line 12) | interface FlowProps {
FILE: packages/react-native/src/flow/step.tsx
type StepProps (line 13) | interface StepProps {
FILE: packages/react-native/src/form/index.tsx
type FormProps (line 13) | type FormProps = Omit<RcFormProps, 'component'> & { formItemHeight?: num...
FILE: packages/react-native/src/form/type.ts
type RcFieldProps (line 7) | type RcFieldProps = Omit<FieldProps, 'children'>;
type FormItemProps (line 8) | interface FormItemProps extends RcFieldProps {
type FormListItemProps (line 17) | interface FormListItemProps
FILE: packages/react-native/src/helpers/index.ts
function hexToRgba (line 18) | function hexToRgba(hex: string, alpha = 1) {
FILE: packages/react-native/src/helpers/normalize.ts
constant ONE_PIXEL (line 19) | const ONE_PIXEL = StyleSheet.hairlineWidth;
FILE: packages/react-native/src/image-header/AnimateHeader.tsx
constant HEADER_HEIGHT (line 17) | const HEADER_HEIGHT = px(44);
type AnimateHeaderProps (line 18) | interface AnimateHeaderProps {
FILE: packages/react-native/src/image-header/index.tsx
type ImageHeaderProps (line 17) | type ImageHeaderProps = PropsWithChildren<{
FILE: packages/react-native/src/index.ts
type File (line 149) | type File = {
FILE: packages/react-native/src/indicator/BallIndicator.tsx
class BallIndicator (line 8) | class BallIndicator extends PureComponent<BallIndicatorProps> {
method render (line 57) | render() {
FILE: packages/react-native/src/indicator/Indicator.tsx
type IndicatorProps (line 6) | interface IndicatorProps extends BaseIndicatorProps {
type IndicatorState (line 12) | interface IndicatorState {
class Indicator (line 17) | class Indicator extends PureComponent<IndicatorProps, IndicatorState> {
method constructor (line 32) | constructor(props: IndicatorProps) {
method componentDidMount (line 57) | componentDidMount() {
method componentDidUpdate (line 65) | componentDidUpdate(prevProps: IndicatorProps) {
method startAnimation (line 84) | startAnimation() {
method stopAnimation (line 105) | stopAnimation() {
method saveAnimation (line 120) | saveAnimation(value: number) {
method resumeAnimation (line 131) | resumeAnimation() {
method renderComponent (line 157) | renderComponent(_: any, index: number) {
method render (line 168) | render() {
FILE: packages/react-native/src/indicator/MaterialIndicator.tsx
class MaterialIndicator (line 8) | class MaterialIndicator extends PureComponent<MaterialIndicatorProps> {
method render (line 106) | render() {
FILE: packages/react-native/src/indicator/UIActivityIndicator.tsx
class UIActivityIndicator (line 8) | class UIActivityIndicator extends PureComponent<UIActivityIndicatorProps> {
method render (line 52) | render() {
FILE: packages/react-native/src/indicator/type.ts
type BaseIndicatorProps (line 3) | interface BaseIndicatorProps {
type UIActivityIndicatorProps (line 41) | interface UIActivityIndicatorProps extends BaseIndicatorProps {
type BallIndicatorProps (line 56) | interface BallIndicatorProps extends BaseIndicatorProps {
type MaterialIndicatorProps (line 71) | interface MaterialIndicatorProps extends BaseIndicatorProps {
FILE: packages/react-native/src/input/InputItem.tsx
type InputItemProps (line 15) | interface InputItemProps
FILE: packages/react-native/src/input/TextArea.tsx
type TextAreaProps (line 16) | interface TextAreaProps
FILE: packages/react-native/src/input/index.tsx
type InputProps (line 16) | interface InputProps extends Omit<TextInputProps, 'placeholderTextColor'...
FILE: packages/react-native/src/input/useInput.ts
function useInput (line 7) | function useInput({
FILE: packages/react-native/src/input/useInputItem.ts
function useInputItem (line 7) | function useInputItem({
FILE: packages/react-native/src/input/useTextArea.ts
function useTextArea (line 7) | function useTextArea({ value, onChange }: Pick<TextAreaProps, 'value' | ...
FILE: packages/react-native/src/link/index.ts
type LinkType (line 3) | type LinkType = 'email' | 'phone' | 'sms' | 'url' | 'settings';
method email (line 6) | email(address: string) {
method settings (line 9) | settings() {
method url (line 12) | async url(url: string) {
method sms (line 18) | sms(phoneNumber: string) {
method call (line 21) | call(phoneNumber: string) {
FILE: packages/react-native/src/list-item/index.tsx
type ListItemProps (line 22) | type ListItemProps = {
FILE: packages/react-native/src/list/index.tsx
type ListProps (line 12) | type ListProps = {
FILE: packages/react-native/src/menu/type.ts
type StyleProps (line 4) | interface StyleProps {
type MenuProps (line 11) | type MenuProps = StyleProps & {
type MenuItemProps (line 32) | type MenuItemProps = Pick<MenuProps, 'activeOpacity' | 'itemStyle'> &
FILE: packages/react-native/src/menu/useGroup.ts
function useGroup (line 7) | function useGroup({
FILE: packages/react-native/src/menu/useMenu.ts
type FlattenedMenuItem (line 7) | type FlattenedMenuItem = MenuItemProps & { parentId?: string };
function useMenu (line 9) | function useMenu({
FILE: packages/react-native/src/modal/Modal/ModalView.tsx
function ModalView (line 12) | function ModalView(props: PropsWithChildren<ModalProps>) {
FILE: packages/react-native/src/modal/Modal/index.tsx
function Modal (line 7) | function Modal(props: PropsWithChildren<ModalProps>) {
FILE: packages/react-native/src/modal/Modal/useModal.ts
function useModal (line 28) | function useModal({
FILE: packages/react-native/src/modal/alert/index.tsx
function alert (line 7) | function alert(props: AlertProps) {
FILE: packages/react-native/src/modal/confirm/index.tsx
function confirm (line 7) | function confirm({ okText = '确定', cancelText = '取消', ...restProps }: Con...
FILE: packages/react-native/src/modal/confirm/useConfirm.ts
function useConfirm (line 5) | function useConfirm({ onOk, onCancel }: Pick<ConfirmProps, 'onOk' | 'onC...
FILE: packages/react-native/src/modal/prompt/index.tsx
function prompt (line 7) | function prompt({ okText = '确定', cancelText = '取消', ...restProps }: Prom...
FILE: packages/react-native/src/modal/prompt/usePrompt.ts
function usePrompt (line 8) | function usePrompt({ onOk, onCancel }: Pick<PromptProps, 'onOk' | 'onCan...
FILE: packages/react-native/src/modal/show/index.tsx
function show (line 9) | function show(
method closeModal (line 46) | closeModal() {
FILE: packages/react-native/src/modal/tip/index.tsx
function tip (line 7) | function tip(props: TipProps) {
FILE: packages/react-native/src/modal/type.ts
type ModalProps (line 4) | interface ModalProps {
type Action (line 27) | interface Action<T = StyleProp<TextStyle>> {
type AlertProps (line 33) | interface AlertProps {
type ConfirmProps (line 46) | interface ConfirmProps extends Omit<AlertProps, 'onPress' | 'confirmText...
type PromptProps (line 59) | interface PromptProps extends Omit<ConfirmProps, 'icon' | 'onOk'> {
type TipProps (line 65) | type TipProps = Omit<AlertProps, 'icon' | 'onPress' | 'confirmText'> & {
type ImperativeModalChildrenProps (line 74) | type ImperativeModalChildrenProps<P> = P & {
FILE: packages/react-native/src/notice-bar/index.tsx
constant DEFAULT_DURATION (line 16) | const DEFAULT_DURATION = 5000;
FILE: packages/react-native/src/notice-bar/type.ts
type NoticeBarProps (line 6) | interface NoticeBarProps {
type AnimatedNoticeProps (line 27) | type AnimatedNoticeProps = Omit<NoticeBarProps, 'mode' | 'onPress' | 'on...
FILE: packages/react-native/src/notify/constant.ts
constant SHORT (line 13) | const SHORT = 3000;
constant LONG (line 14) | const LONG = 5000;
type NotifyType (line 16) | enum NotifyType {
FILE: packages/react-native/src/notify/index.tsx
type NotifyRef (line 11) | interface NotifyRef {
type NotifyRefObj (line 15) | type NotifyRefObj = {
function Notify (line 21) | function Notify() {
FILE: packages/react-native/src/notify/type.ts
type NotifyProps (line 3) | interface NotifyProps {
FILE: packages/react-native/src/notify/useNotify.ts
function useNotify (line 26) | function useNotify() {
FILE: packages/react-native/src/number-keyboard/NumberKeyboardModal.tsx
constant SIZE (line 19) | const SIZE = px(48);
FILE: packages/react-native/src/number-keyboard/NumberKeyboardView.tsx
constant PER_WIDTH (line 18) | const PER_WIDTH = deviceWidth / 4;
FILE: packages/react-native/src/number-keyboard/type.ts
type NumberKeyBoardType (line 4) | type NumberKeyBoardType = 'number' | 'idcard' | 'integer';
type NumberKeyboardViewProps (line 6) | interface NumberKeyboardViewProps {
type NumberKeyboardItemProps (line 25) | interface NumberKeyboardItemProps
type NumberKeyboardInputProps (line 45) | interface NumberKeyboardInputProps extends NumberKeyboardItemProps {
type NumberKeyboardModalProps (line 59) | interface NumberKeyboardModalProps extends Omit<NumberKeyboardViewProps,...
type NumberKeyboardRef (line 65) | interface NumberKeyboardRef {
FILE: packages/react-native/src/number-keyboard/useNumberKeyboard.ts
function useNumberKeyboard (line 9) | function useNumberKeyboard({
FILE: packages/react-native/src/number-keyboard/useNumberKeyboardModal.ts
function useNumberKeyboardModal (line 6) | function useNumberKeyboardModal({
FILE: packages/react-native/src/number-keyboard/util.ts
function formatValue (line 10) | function formatValue(value: string, type?: NumberKeyBoardType, digit = 2) {
FILE: packages/react-native/src/pagination/index.tsx
type PaginationProps (line 8) | interface PaginationProps {
FILE: packages/react-native/src/pagination/usePagination.ts
function usePagination (line 7) | function usePagination({
FILE: packages/react-native/src/passcode/reducer.ts
type SetOtpTextForIndexPayload (line 3) | type SetOtpTextForIndexPayload = { index: number; text: string };
type SetOtpTextForIndex (line 4) | type SetOtpTextForIndex = {
type SetOtpCodePayload (line 9) | type SetOtpCodePayload = { count: number; code: string };
type SetOtpCode (line 10) | type SetOtpCode = {
type ClearOtpPayload (line 15) | type ClearOtpPayload = number;
type ClearOtp (line 16) | type ClearOtp = { type: 'clearOtp'; payload: ClearOtpPayload };
type SetHasKeySupportPayload (line 18) | type SetHasKeySupportPayload = boolean;
type SetHasKeySupport (line 19) | type SetHasKeySupport = { type: 'setHasKeySupport'; payload: SetHasKeySu...
type ReducerState (line 21) | type ReducerState = {
type ActionTypes (line 27) | type ActionTypes = {
type Actions (line 34) | type Actions = SetOtpTextForIndex | SetOtpCode | ClearOtp | SetHasKeySup...
constant ACTION_TYPES (line 36) | const ACTION_TYPES: ActionTypes = {
FILE: packages/react-native/src/passcode/type.ts
type PasscodeProps (line 11) | interface PasscodeProps extends Omit<TextInputProps, 'onChange' | 'onCha...
type PasscodeRef (line 32) | interface PasscodeRef {
type PasscodeItemProps (line 41) | interface PasscodeItemProps extends TextInputProps {
FILE: packages/react-native/src/passcode/usePasscode.ts
function usePasscode (line 10) | function usePasscode({
FILE: packages/react-native/src/portal/PortalConsumer.tsx
class PortalConsumer (line 5) | class PortalConsumer extends React.Component<PropsWithChildren<{ manager...
method componentDidMount (line 8) | componentDidMount() {
method componentDidUpdate (line 14) | componentDidUpdate() {
method componentWillUnmount (line 18) | componentWillUnmount() {
method checkManager (line 22) | private checkManager() {
method render (line 28) | render() {
FILE: packages/react-native/src/portal/PortalContext.ts
type PortalMethods (line 3) | type PortalMethods = {
FILE: packages/react-native/src/portal/PortalGuard.ts
constant ADD_TYPE (line 4) | const ADD_TYPE = 'TD_DESIGN_REACT_NATIVE_ADD_PORTAL';
constant REMOVE_TYPE (line 5) | const REMOVE_TYPE = 'TD_DESIGN_REACT_NATIVE_REMOVE_PORTAL';
class PortalGuard (line 10) | class PortalGuard {
FILE: packages/react-native/src/portal/PortalHost.tsx
type Operation (line 8) | type Operation =
class PortalHost (line 13) | class PortalHost extends React.Component<PropsWithChildren<{}>> {
method componentDidMount (line 23) | componentDidMount() {
method componentWillUnmount (line 55) | componentWillUnmount() {
method render (line 99) | render() {
FILE: packages/react-native/src/portal/PortalManager.tsx
type State (line 4) | type State = {
class PortalManager (line 11) | class PortalManager extends React.PureComponent<{}, State> {
method render (line 37) | render() {
FILE: packages/react-native/src/portal/index.tsx
class Portal (line 8) | class Portal extends React.Component<PropsWithChildren<{}>> {
method render (line 13) | render() {
FILE: packages/react-native/src/pressable/index.tsx
type Rect (line 37) | type Rect = {
type PressableProps (line 44) | interface PressableProps
function Pressable (line 61) | function Pressable(props: PressableProps) {
FILE: packages/react-native/src/progress/type.ts
type ProgressProps (line 4) | interface ProgressProps {
FILE: packages/react-native/src/progress/useCircleProgress.ts
function useCircleProgress (line 6) | function useCircleProgress({
FILE: packages/react-native/src/progress/useLineProgress.ts
function useLineProgress (line 6) | function useLineProgress({
FILE: packages/react-native/src/radio/type.ts
type RadioStatus (line 4) | type RadioStatus = 'checked' | 'unchecked';
type RadioOption (line 6) | interface RadioOption {
type TransformedOption (line 10) | interface TransformedOption extends RadioOption {
type RadioProps (line 17) | interface RadioProps {
type RadioItemProps (line 40) | type RadioItemProps = Pick<RadioProps, 'itemStyle' | 'labelStyle' | 'siz...
FILE: packages/react-native/src/radio/useRadio.ts
function useRadio (line 7) | function useRadio({
FILE: packages/react-native/src/result/index.tsx
type ActionButtonProps (line 10) | interface ActionButtonProps {
type ResultProps (line 16) | interface ResultProps {
FILE: packages/react-native/src/scroll-number/index.tsx
type ScrollNumberProps (line 16) | interface ScrollNumberProps {
type TickProps (line 88) | interface TickProps
FILE: packages/react-native/src/search-bar/index.tsx
type SearchBarProps (line 18) | type SearchBarProps = PropsWithChildren<{
FILE: packages/react-native/src/search-bar/useSearchBar.ts
function useSearchBar (line 8) | function useSearchBar({
FILE: packages/react-native/src/slider/index.tsx
type SliderProps (line 17) | interface SliderProps {
constant SLIDER_HEIGHT (line 46) | const SLIDER_HEIGHT = px(20);
FILE: packages/react-native/src/slider/useSlider.ts
function useSlider (line 13) | function useSlider({
FILE: packages/react-native/src/stepper/useStepper.ts
type StepperProps (line 8) | type StepperProps = Omit<LayoutProps<Theme>, 'width' | 'minWidth'> & {
function useStepper (line 33) | function useStepper({
FILE: packages/react-native/src/svg-icon/IconArrowdown.tsx
type SvgIconProps (line 8) | interface SvgIconProps extends GProps, ViewProps {
FILE: packages/react-native/src/svg-icon/IconBells.tsx
type SvgIconProps (line 8) | interface SvgIconProps extends GProps, ViewProps {
FILE: packages/react-native/src/svg-icon/IconCheck.tsx
type SvgIconProps (line 8) | interface SvgIconProps extends GProps, ViewProps {
FILE: packages/react-native/src/svg-icon/IconCheckboxChecked.tsx
type SvgIconProps (line 8) | interface SvgIconProps extends GProps, ViewProps {
FILE: packages/react-native/src/svg-icon/IconCheckboxHalfchecked.tsx
type SvgIconProps (line 8) | interface SvgIconProps extends GProps, ViewProps {
FILE: packages/react-native/src/svg-icon/IconCheckboxUnchecked.tsx
type SvgIconProps (line 8) | interface SvgIconProps extends GProps, ViewProps {
FILE: packages/react-native/src/svg-icon/IconCheckcircle.tsx
type SvgIconProps (line 8) | interface SvgIconProps extends GProps, ViewProps {
FILE: packages/react-native/src/svg-icon/IconCheckcircleo.tsx
type SvgIconProps (line 8) | interface SvgIconProps extends GProps, ViewProps {
FILE: packages/react-native/src/svg-icon/IconClockcircleo.tsx
type SvgIconProps (line 8) | interface SvgIconProps extends GProps, ViewProps {
FILE: packages/react-native/src/svg-icon/IconClose.tsx
type SvgIconProps (line 8) | interface SvgIconProps extends GProps, ViewProps {
FILE: packages/react-native/src/svg-icon/IconClosecircleo.tsx
type SvgIconProps (line 8) | interface SvgIconProps extends GProps, ViewProps {
FILE: packages/react-native/src/svg-icon/IconDate.tsx
type SvgIconProps (line 8) | interface SvgIconProps extends GProps, ViewProps {
FILE: packages/react-native/src/svg-icon/IconDown.tsx
type SvgIconProps (line 8) | interface SvgIconProps extends GProps, ViewProps {
FILE: packages/react-native/src/svg-icon/IconEllipsis.tsx
type SvgIconProps (line 8) | interface SvgIconProps extends GProps, ViewProps {
FILE: packages/react-native/src/svg-icon/IconEyeclose.tsx
type SvgIconProps (line 8) | interface SvgIconProps extends GProps, ViewProps {
FILE: packages/react-native/src/svg-icon/IconEyeopen.tsx
type SvgIconProps (line 8) | interface SvgIconProps extends GProps, ViewProps {
FILE: packages/react-native/src/svg-icon/IconLeft.tsx
type SvgIconProps (line 8) | interface SvgIconProps extends GProps, ViewProps {
FILE: packages/react-native/src/svg-icon/IconMinus.tsx
type SvgIconProps (line 8) | interface SvgIconProps extends GProps, ViewProps {
FILE: packages/react-native/src/svg-icon/IconPlus.tsx
type SvgIconProps (line 8) | interface SvgIconProps extends GProps, ViewProps {
FILE: packages/react-native/src/svg-icon/IconRadioChecked.tsx
type SvgIconProps (line 8) | interface SvgIconProps extends GProps, ViewProps {
FILE: packages/react-native/src/svg-icon/IconRadioUnchecked.tsx
type SvgIconProps (line 8) | interface SvgIconProps extends GProps, ViewProps {
FILE: packages/react-native/src/svg-icon/IconReload.tsx
type SvgIconProps (line 8) | interface SvgIconProps extends GProps, ViewProps {
FILE: packages/react-native/src/svg-icon/IconRight.tsx
type SvgIconProps (line 8) | interface SvgIconProps extends GProps, ViewProps {
FILE: packages/react-native/src/svg-icon/IconSearch.tsx
type SvgIconProps (line 8) | interface SvgIconProps extends GProps, ViewProps {
FILE: packages/react-native/src/svg-icon/IconUp.tsx
type SvgIconProps (line 8) | interface SvgIconProps extends GProps, ViewProps {
FILE: packages/react-native/src/svg-icon/index.tsx
type IconNames (line 34) | type IconNames =
type SvgIconProps (line 61) | interface SvgIconProps extends GProps, ViewProps {
FILE: packages/react-native/src/swipe-row/index.tsx
type SwipeAction (line 18) | interface SwipeAction {
type SwipeRowProps (line 29) | type SwipeRowProps = PropsWithChildren<{
FILE: packages/react-native/src/swipe-row/useSwipeRow.ts
function useSwipeRow (line 11) | function useSwipeRow({ anchor, onRemove }: Pick<SwipeRowProps, 'onRemove...
FILE: packages/react-native/src/switch/index.tsx
type SwitchProps (line 15) | interface SwitchProps {
constant SWITCH_WIDTH (line 34) | const SWITCH_WIDTH = px(50);
FILE: packages/react-native/src/switch/useSwitch.ts
function useSwitch (line 18) | function useSwitch({ checked, onChange }: Pick<SwitchProps, 'onChange' |...
FILE: packages/react-native/src/table/Cell.tsx
type CellProps (line 8) | interface CellProps {
FILE: packages/react-native/src/table/Head.tsx
type HeadProps (line 11) | interface HeadProps {
FILE: packages/react-native/src/table/Rows.tsx
type RowsProps (line 11) | interface RowsProps {
FILE: packages/react-native/src/table/index.tsx
function Table (line 19) | function Table<T extends Record<string, any>>(props: TableProps<T>) {
FILE: packages/react-native/src/table/type.ts
type ColumnProps (line 4) | interface ColumnProps {
type TableProps (line 23) | interface TableProps<T> {
FILE: packages/react-native/src/table/useTable.ts
function useTable (line 9) | function useTable<T extends Record<string, any>>({ columns }: Pick<Table...
FILE: packages/react-native/src/table/utils.ts
function computeWidth (line 10) | function computeWidth(cellWidth: number, width?: number) {
function computeCellWidth (line 24) | function computeCellWidth(wrapWidth: number, columns: ColumnProps[]): nu...
FILE: packages/react-native/src/tag/index.tsx
type TagSize (line 28) | type TagSize = 'large' | 'middle' | 'small';
type TagProps (line 29) | interface TagProps {
type BaseTagProps (line 51) | type BaseTagProps = BorderProps<Theme> &
function getBySize (line 215) | function getBySize(size: TagSize) {
FILE: packages/react-native/src/tag/useTag.ts
function useTag (line 5) | function useTag({
FILE: packages/react-native/src/text/ReText.tsx
type TextProps (line 13) | interface TextProps extends Omit<TextInputProps, 'value' | 'style'> {
FILE: packages/react-native/src/text/index.tsx
type Props (line 10) | type Props = TextProps<Theme> & Omit<RNTextProps, 'onLongPress' | 'onPre...
FILE: packages/react-native/src/theme/index.ts
type Theme (line 194) | type Theme = typeof lightTheme;
type Spacing (line 195) | type Spacing = keyof Theme['spacing'];
type Color (line 196) | type Color = keyof Theme['colors'];
type Variant (line 197) | type Variant = keyof Omit<Theme['textVariants'], 'defaults'>;
type BorderRadius (line 198) | type BorderRadius = keyof Theme['borderRadii'];
FILE: packages/react-native/src/timeline/type.ts
type TimelineStepProps (line 3) | interface TimelineStepProps {
type TimelineProps (line 14) | interface TimelineProps {
FILE: packages/react-native/src/toast/constant.ts
constant SHORT (line 1) | const SHORT = 1500;
constant LONG (line 2) | const LONG = 3000;
constant INFINITY (line 3) | const INFINITY = Infinity;
FILE: packages/react-native/src/toast/index.tsx
type ToastRef (line 10) | interface ToastRef {
type ToastRefObj (line 15) | type ToastRefObj = {
function Toast (line 21) | function Toast() {
FILE: packages/react-native/src/toast/type.ts
type ToastProps (line 3) | interface ToastProps {
FILE: packages/react-native/src/toast/useToast.ts
function useToast (line 9) | function useToast() {
FILE: packages/react-native/src/tooltip/Triangle.tsx
type Props (line 6) | type Props = {
FILE: packages/react-native/src/tooltip/getTooltipCoordinate.tsx
type Areas (line 44) | type Areas = {
FILE: packages/react-native/src/tooltip/index.tsx
type TooltipProps (line 13) | interface TooltipProps {
type State (line 26) | type State = {
type Action (line 34) | type Action =
FILE: packages/react-native/src/tree/TreeGroup.tsx
function TreeGroup (line 18) | function TreeGroup({
FILE: packages/react-native/src/tree/type.ts
type TreeItemProps (line 5) | interface TreeItemProps {
type FlattenTreeItem (line 24) | type FlattenTreeItem = TreeItemProps & { parentId?: string };
type TreeProps (line 26) | interface TreeProps {
FILE: packages/react-native/src/tree/useGroup.ts
function useGroup (line 11) | function useGroup({
FILE: packages/react-native/src/tree/useTree.ts
function useTree (line 8) | function useTree({
FILE: packages/react-native/src/utils/redash.ts
type AnimatedColor (line 8) | type AnimatedColor = string | number;
FILE: packages/react-native/src/utils/ref-util.ts
function addNewRef (line 1) | function addNewRef<T>(refs: any[], newRef: T) {
function removeOldRef (line 7) | function removeOldRef<T>(refs: any[], oldRef: T | null) {
function getRef (line 11) | function getRef(refs: any[]) {
FILE: packages/react-native/src/vehicle-keyboard/type.ts
type VehicleKeyboardType (line 4) | type VehicleKeyboardType = 'provinces' | 'vehicleNum';
type VehicleKeyboardViewProps (line 6) | type VehicleKeyboardViewProps = {
type VehicleKeyboardModalProps (line 19) | interface VehicleKeyboardModalProps extends Omit<VehicleKeyboardViewProp...
type VehicleKeyboardRef (line 25) | interface VehicleKeyboardRef {
type VehicleKeyboardItemProps (line 29) | interface VehicleKeyboardItemProps extends Pick<VehicleKeyboardViewProps...
type VehicleKeyboardInputProps (line 43) | interface VehicleKeyboardInputProps extends VehicleKeyboardItemProps {
FILE: packages/react-native/src/vehicle-keyboard/useVehicleKeyboard.ts
function useVehicleKeyboard (line 11) | function useVehicleKeyboard({
FILE: packages/react-native/src/vehicle-keyboard/useVehicleKeyboardModal.ts
function useVehicleKeyboardModal (line 6) | function useVehicleKeyboardModal({
FILE: packages/react-native/src/white-space/index.tsx
type WhiteSpaceProps (line 8) | interface WhiteSpaceProps {
FILE: packages/react-native/src/wing-blank/index.tsx
type WingBlankProps (line 8) | interface WingBlankProps extends BoxProps<Theme> {
FILE: packages/react-native/typings.d.ts
type ChildrenType (line 3) | type ChildrenType = JSX.Element | number | boolean | Element | ReactFrag...
type RefAttributes (line 6) | interface RefAttributes {
FILE: packages/svgicon-cli/gulpfile.js
function clean (line 9) | function clean() {
function buildCJS (line 18) | function buildCJS() {
function copyTemplate (line 29) | function copyTemplate() {
function copyJson (line 33) | function copyJson() {
FILE: packages/svgicon-cli/src/commands/createIcon.ts
type XmlData (line 6) | interface XmlData {
FILE: packages/svgicon-cli/src/libs/getConfig.ts
type Config (line 7) | interface Config {
FILE: packages/svgicon-cli/src/libs/parseLocalSvg.ts
type ILocalSvg (line 7) | interface ILocalSvg {
Condensed preview — 998 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (4,935K chars).
[
{
"path": ".changeset/README.md",
"chars": 510,
"preview": "# Changesets\n\nHello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that wo"
},
{
"path": ".changeset/config.json",
"chars": 434,
"preview": "{\n \"$schema\": \"https://unpkg.com/@changesets/config@2.2.0/schema.json\",\n \"changelog\": [\n \"@changesets/changelog-git"
},
{
"path": ".commitlintrc.js",
"chars": 83,
"preview": "module.exports = {\n extends: ['@commitlint/config-conventional'],\n rules: {},\n};\n"
},
{
"path": ".cz-config.js",
"chars": 1079,
"preview": "'use strict';\n\nmodule.exports = {\n types: [\n {\n value: 'feat',\n name: '✨ feat: 新功能',\n },\n {\n "
},
{
"path": ".editorconfig",
"chars": 245,
"preview": "# http://editorconfig.org\nroot = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_tr"
},
{
"path": ".github/workflows/changeset-version.yml",
"chars": 1442,
"preview": "name: Changesets\n\non:\n push:\n branches:\n - master\n - v4\n - v3\n\nconcurrency: ${{ github.workflow }}-${"
},
{
"path": ".github/workflows/gh-pages.yml",
"chars": 1330,
"preview": "name: Build Doc Site\n\nenv:\n NODE_OPTIONS: --max-old-space-size=6144\n\non:\n push:\n branches:\n - master\n\nconcurre"
},
{
"path": ".github/workflows/issue-close-require.yml",
"chars": 776,
"preview": "name: Issue Close Require\n\non:\n schedule:\n - cron: \"0 0 * * *\"\n\npermissions:\n contents: read\n\njobs:\n issue-close-r"
},
{
"path": ".gitignore",
"chars": 364,
"preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\npack"
},
{
"path": ".husky/.gitignore",
"chars": 2,
"preview": "_\n"
},
{
"path": ".husky/commit-msg",
"chars": 68,
"preview": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\nnpx commitlint --edit $1\n\n"
},
{
"path": ".husky/pre-commit",
"chars": 58,
"preview": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\nnpx lint-staged\n"
},
{
"path": ".npmrc",
"chars": 72,
"preview": "registry=https://registry.npmmirror.com/\nstrict-peer-dependencies=false\n"
},
{
"path": ".prettierignore",
"chars": 125,
"preview": "**/*.svg\n**/*.ejs\n**/*.html\n**/*.js\n**/*.json\n**/*.jsx\npackage.json\n*.yaml\n.umi\n.umi-production\n\nexample\nlib\n.changeset\n"
},
{
"path": ".prettierrc",
"chars": 636,
"preview": "{\n \"semi\": true,\n \"trailingComma\": \"es5\",\n \"singleQuote\": true,\n \"printWidth\": 120,\n \"tabWidth\": 2,\n \"bracketSpaci"
},
{
"path": ".umirc.ts",
"chars": 1572,
"preview": "import { defineConfig } from 'dumi';\n\nexport default defineConfig({\n title: '雷数前端',\n favicon: 'https://avatars0.github"
},
{
"path": ".vscode/settings.json",
"chars": 211,
"preview": "{\n \"editor.defaultFormatter\": \"esbenp.prettier-vscode\",\n \"editor.formatOnSave\": true,\n \"eslint.validate\": [\n \"java"
},
{
"path": "LICENSE",
"chars": 11357,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README.md",
"chars": 169,
"preview": "# dumi app\n\n## Getting Started\n\nInstall dependencies,\n\n```bash\n$ npm i\n```\n\nStart the dev server,\n\n```bash\n$ npm start\n`"
},
{
"path": "docs/development.md",
"chars": 93,
"preview": "---\nsidemenu: false\nnav:\n title: 软件开发\n path: /development\n---\n\n## 软件开发\n\n收集的一些软件开发相关的术语和概念。\n"
},
{
"path": "docs/friendlink.md",
"chars": 688,
"preview": "---\nsidemenu: false\nnav:\n title: 友情链接\n path: /friendlink\n---\n\n# 友情链接\n\n## react-native 组件库\n\n- [vant-react-native](https"
},
{
"path": "docs/index.md",
"chars": 808,
"preview": "---\ntitle: td-design 雷数科技前端团队\nhero:\n title: 雷数科技-前端文档\n desc: 本网站包含雷数科技前端团队开发的所有组件库、脚手架、模板等内容\n actions:\n - text: 开始"
},
{
"path": "docs/pattern/Strategy.md",
"chars": 2398,
"preview": "---\nnav:\n title: 策略模式\n path: /pattern\ngroup:\n title: 设计模式\n path: /\n order: 3\n---\n\n# 策略模式 - Strategy\n\n策略模式是一种行为设计模式,"
},
{
"path": "docs/pattern/index.md",
"chars": 96,
"preview": "---\nnav:\n title: 设计模式\n path: /pattern\ngroup:\n title: 设计模式\n path: /\n order: 0\n---\n\n# 设计模式介绍\n"
},
{
"path": "docs/pattern/signleton.md",
"chars": 106,
"preview": "---\nnav:\n title: 单例模式\n path: /pattern\ngroup:\n title: 设计模式\n path: /\n order: 1\n---\n\n# 单例模式 - Singleton\n"
},
{
"path": "docs/pattern/visitor.md",
"chars": 106,
"preview": "---\nnav:\n title: 访问者模式\n path: /pattern\ngroup:\n title: 设计模式\n path: /\n order: 2\n---\n\n# 访问者模式 - Visitor\n"
},
{
"path": "docs/react-faq.md",
"chars": 696,
"preview": "---\nnav:\n title: React应用开发常见问题\n path: /faq\ngroup:\n title: 开发常见问题\n path: /\n order: 1\n---\n\n# React 应用开发常见问题\n\n## 单点登录\n"
},
{
"path": "docs/react-native/index.md",
"chars": 812,
"preview": "---\ntitle: react-native组件库\ntoc: menu\norder: 1\n---\n\n# @td-design/react-native 组件库\n\n## 特性和优势\n\n- 全面兼容 react 和 react-native\n"
},
{
"path": "docs/react-native/restyle.md",
"chars": 12178,
"preview": "---\ntitle: 主题和样式\ntoc: menu\norder: 2\n---\n\n# 主题和样式\n\n先看效果图:\n\n<center>\n <figure>\n <img\n alt=\"\"\n src=\"https://u"
},
{
"path": "docs/react-native/theme.md",
"chars": 8437,
"preview": "---\ntoc: menu\norder: 3\n---\n\n# Theme 主题\n\n`@shopify/restyle`库给我们提供了完备的主题定制功能。\n\n## Spacing 间距\n\n| 名称 | 说明 | 值 |\n| ----- "
},
{
"path": "docs/react-perf.md",
"chars": 10317,
"preview": "---\nnav:\n title: React应用开发性能优化手段\n path: /faq\ngroup:\n title: 开发常见问题\n path: /\n order: 2\n---\n\n# React 应用开发常见性能优化手段\n\n**"
},
{
"path": "docs/rn-faq.md",
"chars": 17241,
"preview": "---\nnav:\n title: RN应用开发常见问题\n path: /faq\ngroup:\n title: 开发常见问题\n path: /\n order: 0\n---\n\n# RN 应用开发常见问题\n\n## 1. 自定义图标\n\n#"
},
{
"path": "docs/screen.md",
"chars": 274,
"preview": "---\nnav:\n title: 大屏素材库\n path: /screen\ngroup:\n title: 概要\n order: 1\n---\n\n# 概要\n\n大屏素材库概要,集成一些常见的大屏组件及图表组件,统一样式。\n\n## 安装方式"
},
{
"path": "docs/structure/array.md",
"chars": 2782,
"preview": "---\ntoc: menu\n---\n\n# 数组 - Array\n\n## 什么是数组\n\n数组是一种线性结构,是一个存储相同类型的变量所组成的一个有序集合,数组中每一个变量称为元素,每个元素都有一个对应的索引(索引从 0 开始)。数组还有另一个"
},
{
"path": "docs/structure/index.md",
"chars": 329,
"preview": "---\ntitle: 数据结构介绍\ntoc: menu\nnav:\n title: Structure\n---\n\n# 数据结构介绍\n\n> Data dominates. If you've chosen the right data str"
},
{
"path": "docs/structure/linkedList.md",
"chars": 41,
"preview": "---\ntoc: menu\n---\n\n# 链表 - LinkedList\n\n链表\n"
},
{
"path": "jest.config.js",
"chars": 765,
"preview": "module.exports = {\n preset: 'ts-jest/presets/js-with-ts',\n testEnvironment: 'jsdom',\n clearMocks: true,\n coverageDir"
},
{
"path": "jest.setup.js",
"chars": 828,
"preview": "jest.mock('react-native', () => {\n return {\n Platform: {\n OS: 'ios',\n Version: 123,\n isTesting: true,"
},
{
"path": "package.json",
"chars": 1913,
"preview": "{\n \"license\": \"Apache-2.0\",\n \"name\": \"thundersdata-frontend\",\n \"author\": \"陈杰 <chj_0507_dz@sina.com>\",\n \"homepage\": \""
},
{
"path": "packages/cli/README.md",
"chars": 124,
"preview": "# `@td-design/cli`\n\n> TODO: description\n\n## Usage\n\n```\nconst cli = require('@td-design/cli');\n\n// TODO: DEMONSTRATE API\n"
},
{
"path": "packages/cli/bin/index.js",
"chars": 49,
"preview": "#!/usr/bin/env node\n\nrequire('../lib/index.js');\n"
},
{
"path": "packages/cli/package.json",
"chars": 1228,
"preview": "{\n \"name\": \"@td-design/cli\",\n \"version\": \"2.6.2\",\n \"description\": \"脚手架工具,用于创建项目,创建页面等功能\",\n \"keywords\": [\n \"thunde"
},
{
"path": "packages/cli/src/index.ts",
"chars": 1536,
"preview": "import chalk from 'chalk';\nimport { program } from 'commander';\nimport fs from 'fs';\nimport inquirer from 'inquirer';\n\ni"
},
{
"path": "packages/cli/src/setupApp.ts",
"chars": 1871,
"preview": "import chalk from 'chalk';\nimport download from 'download-git-repo';\nimport fs from 'fs';\nimport ora from 'ora';\nimport "
},
{
"path": "packages/cli/src/setupSpa.ts",
"chars": 602,
"preview": "import chalk from 'chalk';\nimport download from 'download-git-repo';\nimport ora from 'ora';\n\nexport default {\n init: fu"
},
{
"path": "packages/cli/src/utils/deleteOldDirs.ts",
"chars": 402,
"preview": "import fs from 'fs';\n\nexport default function delDir(path: string) {\n let files = [];\n if (fs.existsSync(path)) {\n "
},
{
"path": "packages/cli/src/utils/replaceProject.ts",
"chars": 1108,
"preview": "import fs from 'fs';\nimport path from 'path';\n\nconst binaryExtensions = ['.png', '.jar', '.webp', '.ttf', '.svg', '.bat'"
},
{
"path": "packages/cli/src/utils/translateFilePath.ts",
"chars": 871,
"preview": "'use strict';\n\n/**\n * There are various files in the templates folder in the RN repo. We want\n * these to be ignored by "
},
{
"path": "packages/cli/src/utils/walk.ts",
"chars": 358,
"preview": "import fs from 'fs';\nimport path from 'path';\n\n('use strict');\n\nexport default function walk(current: string): string[] "
},
{
"path": "packages/cli/tsconfig.build.json",
"chars": 774,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es5\" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES"
},
{
"path": "packages/cli/types.d.ts",
"chars": 36,
"preview": "declare module 'download-git-repo';\n"
},
{
"path": "packages/eslint-plugin-replace-hooks/.eslintrc.js",
"chars": 344,
"preview": "'use strict';\n\nmodule.exports = {\n root: true,\n extends: ['eslint:recommended', 'plugin:eslint-plugin/recommended', 'p"
},
{
"path": "packages/eslint-plugin-replace-hooks/README.md",
"chars": 1076,
"preview": "# eslint-plugin-replace-hooks\n\nreplace some hooks by other hooks\n\n## Installation\n\nYou'll first need to install [ESLint]"
},
{
"path": "packages/eslint-plugin-replace-hooks/docs/rules/no-forbidden-hooks.md",
"chars": 594,
"preview": "# no forbidden hooks in project (no-forbidden-hooks)\n\n## Rule Details\n\n可以通过配置,把项目中不允许使用的 hooks 标记出来,同时提示用指定的其他 hooks 替换\n"
},
{
"path": "packages/eslint-plugin-replace-hooks/gulpfile.js",
"chars": 268,
"preview": "const gulp = require('gulp');\nconst del = require('del');\n\n/**\n * 清理构建目录\n */\nasync function clean() {\n /** 删除构建目录 */\n "
},
{
"path": "packages/eslint-plugin-replace-hooks/package.json",
"chars": 679,
"preview": "{\n \"name\": \"eslint-plugin-replace-hooks\",\n \"version\": \"1.0.3\",\n \"description\": \"replace some hooks by other hooks\",\n "
},
{
"path": "packages/eslint-plugin-replace-hooks/src/index.js",
"chars": 596,
"preview": "/**\n * @fileoverview replace some hooks by other hooks\n * @author chjdamon\n */\n'use strict';\n\n//------------------------"
},
{
"path": "packages/eslint-plugin-replace-hooks/src/rules/no-forbidden-hooks.js",
"chars": 3208,
"preview": "/**\n * @fileoverview no forbidden hooks in project\n * @author chjdamon\n */\n'use strict';\n\n//----------------------------"
},
{
"path": "packages/hooks/CHANGELOG.md",
"chars": 3471,
"preview": "# @td-design/rn-hooks\n\n## 2.8.1\n\n### Patch Changes\n\n- [#892](https://github.com/thundersdata-frontend/td-design/pull/892"
},
{
"path": "packages/hooks/package.json",
"chars": 1268,
"preview": "{\n \"name\": \"@td-design/rn-hooks\",\n \"version\": \"2.8.1\",\n \"description\": \"从ahooks里面抽取的一些在rn项目里可以使用的hooks。提高开发效率\",\n \"ke"
},
{
"path": "packages/hooks/rollup.config.js",
"chars": 1430,
"preview": "const commonjs = require('@rollup/plugin-commonjs');\nconst resolve = require('@rollup/plugin-node-resolve');\nconst types"
},
{
"path": "packages/hooks/src/createUpdateEffect/index.test.ts",
"chars": 985,
"preview": "import { useEffect, useLayoutEffect } from 'react';\n\nimport { renderHook } from '@testing-library/react-hooks';\n\nimport "
},
{
"path": "packages/hooks/src/createUpdateEffect/index.ts",
"chars": 538,
"preview": "import type { useEffect, useLayoutEffect } from 'react';\nimport { useRef } from 'react';\n\ntype effectHookType = typeof u"
},
{
"path": "packages/hooks/src/index.ts",
"chars": 3548,
"preview": "import { default as useAccessibilityInfo } from './useAccessibilityInfo';\nimport { default as useAppState } from './useA"
},
{
"path": "packages/hooks/src/useAccessibilityInfo/index.test.ts",
"chars": 8978,
"preview": "import { AccessibilityChangeEventName, AccessibilityInfo } from 'react-native';\n\nimport { act, renderHook } from '@testi"
},
{
"path": "packages/hooks/src/useAccessibilityInfo/index.ts",
"chars": 2143,
"preview": "import { useEffect } from 'react';\nimport { AccessibilityChangeEventName, AccessibilityInfo } from 'react-native';\n\nimpo"
},
{
"path": "packages/hooks/src/useAppState/index.test.ts",
"chars": 1190,
"preview": "import { AppState, AppStateStatus } from 'react-native';\n\nimport { act, renderHook } from '@testing-library/react-hooks'"
},
{
"path": "packages/hooks/src/useAppState/index.ts",
"chars": 633,
"preview": "import { useEffect } from 'react';\nimport { AppState } from 'react-native';\n\nimport useSafeState from '../useSafeState';"
},
{
"path": "packages/hooks/src/useAsyncEffect/demo/demo1.tsx",
"chars": 576,
"preview": "import React, { useState } from 'react';\n\nimport { useAsyncEffect } from '@td-design/rn-hooks';\n\nfunction mockCheck(): P"
},
{
"path": "packages/hooks/src/useAsyncEffect/index.test.ts",
"chars": 714,
"preview": "import { useState } from 'react';\n\nimport { renderHook } from '@testing-library/react-hooks';\n\nimport { sleep } from '.."
},
{
"path": "packages/hooks/src/useAsyncEffect/index.ts",
"chars": 912,
"preview": "import type { DependencyList } from 'react';\nimport { useEffect } from 'react';\n\nimport { isFunction } from '../utils';\n"
},
{
"path": "packages/hooks/src/useBackHandler/index.test.ts",
"chars": 1785,
"preview": "import { BackHandler } from 'react-native';\n\nimport { renderHook } from '@testing-library/react-hooks';\n\nimport useBackH"
},
{
"path": "packages/hooks/src/useBackHandler/index.ts",
"chars": 508,
"preview": "import { useEffect } from 'react';\nimport { BackHandler } from 'react-native';\n\nexport default function useBackHandler(h"
},
{
"path": "packages/hooks/src/useBoolean/demo/demo1.tsx",
"chars": 562,
"preview": "import React from 'react';\n\nimport { useBoolean } from '@td-design/rn-hooks';\n\nexport default () => {\n const [state, { "
},
{
"path": "packages/hooks/src/useBoolean/index.test.ts",
"chars": 1139,
"preview": "import { act, renderHook } from '@testing-library/react-hooks';\n\nimport useBoolean from './index';\n\ndescribe('useBoolean"
},
{
"path": "packages/hooks/src/useBoolean/index.ts",
"chars": 594,
"preview": "import { useMemo } from 'react';\n\nimport useToggle from '../useToggle';\n\n/**\n * 优雅的管理 boolean 状态的 Hook。\n * @param defaul"
},
{
"path": "packages/hooks/src/useControllableValue/demo/demo1.tsx",
"chars": 461,
"preview": "import React from 'react';\n\nimport { useControllableValue } from '@td-design/rn-hooks';\n\nexport default (props: any) => "
},
{
"path": "packages/hooks/src/useControllableValue/demo/demo2.tsx",
"chars": 666,
"preview": "import React, { useState } from 'react';\n\nimport { useControllableValue } from '@td-design/rn-hooks';\n\nconst Controllabl"
},
{
"path": "packages/hooks/src/useControllableValue/demo/demo3.tsx",
"chars": 610,
"preview": "import React, { useState } from 'react';\n\nimport { useControllableValue } from '@td-design/rn-hooks';\n\nconst Controllabl"
},
{
"path": "packages/hooks/src/useControllableValue/index.test.ts",
"chars": 2168,
"preview": "import { act, renderHook } from '@testing-library/react-hooks';\n\nimport useControllableValue from './index';\n\ndescribe('"
},
{
"path": "packages/hooks/src/useControllableValue/index.ts",
"chars": 1972,
"preview": "import type { SetStateAction } from 'react';\nimport { useMemo, useRef } from 'react';\n\nimport useMemoizedFn from '../use"
},
{
"path": "packages/hooks/src/useCountdown/index.ts",
"chars": 1282,
"preview": "import { useEffect, useRef, useState } from 'react';\n\nimport useAppState from '../useAppState';\nimport useMemoizedFn fro"
},
{
"path": "packages/hooks/src/useCounter/demo/demo1.tsx",
"chars": 946,
"preview": "import React from 'react';\n\nimport { useCounter } from '@td-design/rn-hooks';\n\nexport default () => {\n const [current, "
},
{
"path": "packages/hooks/src/useCounter/index.test.ts",
"chars": 1790,
"preview": "import { act, renderHook } from '@testing-library/react-hooks';\n\nimport useCounter from './index';\n\ndescribe('useCounter"
},
{
"path": "packages/hooks/src/useCounter/index.ts",
"chars": 1456,
"preview": "import { useEffect } from 'react';\n\nimport useMemoizedFn from '../useMemoizedFn';\nimport useSafeState from '../useSafeSt"
},
{
"path": "packages/hooks/src/useCreation/demo/demo1.tsx",
"chars": 485,
"preview": "import React, { useState } from 'react';\n\nimport { useCreation } from '@td-design/rn-hooks';\n\nclass Foo {\n constructor("
},
{
"path": "packages/hooks/src/useCreation/index.test.ts",
"chars": 1011,
"preview": "import { useState } from 'react';\n\nimport { act, renderHook } from '@testing-library/react-hooks';\n\nimport useCreation f"
},
{
"path": "packages/hooks/src/useCreation/index.ts",
"chars": 834,
"preview": "import { useRef } from 'react';\n\nimport { depsAreSame } from '../utils';\n\n/**\n * `useCreation` 是 `useMemo` 或 `useRef` 的替"
},
{
"path": "packages/hooks/src/useDebounce/DebounceOptions.ts",
"chars": 161,
"preview": "export interface DebounceOptions {\n /** 超时时间,单位为毫秒 */\n wait?: number;\n /** 是否在延迟开始前调用函数 */\n leading?: boolean;\n /**"
},
{
"path": "packages/hooks/src/useDebounce/demo/demo1.tsx",
"chars": 458,
"preview": "import React, { useState } from 'react';\n\nimport { useDebounce } from '@td-design/rn-hooks';\n\nexport default () => {\n c"
},
{
"path": "packages/hooks/src/useDebounce/index.test.ts",
"chars": 939,
"preview": "import { act, renderHook } from '@testing-library/react-hooks';\n\nimport { sleep } from '../utils/testHelpers';\nimport us"
},
{
"path": "packages/hooks/src/useDebounce/index.ts",
"chars": 615,
"preview": "import { useEffect, useState } from 'react';\n\nimport useDebounceFn from '../useDebounceFn';\nimport type { DebounceOption"
},
{
"path": "packages/hooks/src/useDebounceEffect/demo/demo1.tsx",
"chars": 690,
"preview": "import React, { useState } from 'react';\n\nimport { useDebounceEffect } from '@td-design/rn-hooks';\n\nexport default () =>"
},
{
"path": "packages/hooks/src/useDebounceEffect/index.test.ts",
"chars": 1588,
"preview": "import { act, renderHook } from '@testing-library/react-hooks';\n\nimport { sleep } from '../utils/testHelpers';\nimport us"
},
{
"path": "packages/hooks/src/useDebounceEffect/index.ts",
"chars": 728,
"preview": "import { DependencyList, EffectCallback, useEffect, useState } from 'react';\n\nimport type { DebounceOptions } from '../u"
},
{
"path": "packages/hooks/src/useDebounceFn/demo/demo1.tsx",
"chars": 457,
"preview": "import React, { useState } from 'react';\n\nimport { useDebounceFn } from '@td-design/rn-hooks';\n\nexport default () => {\n "
},
{
"path": "packages/hooks/src/useDebounceFn/index.test.ts",
"chars": 2448,
"preview": "import { act, renderHook } from '@testing-library/react-hooks';\n\nimport { sleep } from '../utils/testHelpers';\nimport us"
},
{
"path": "packages/hooks/src/useDebounceFn/index.ts",
"chars": 1025,
"preview": "import { debounce } from 'lodash-es';\n\nimport useCreation from '../useCreation';\nimport type { DebounceOptions } from '."
},
{
"path": "packages/hooks/src/useDeepCompareEffect/demo/demo1.tsx",
"chars": 738,
"preview": "import React, { useEffect, useRef, useState } from 'react';\n\nimport { useDeepCompareEffect } from '@td-design/rn-hooks';"
},
{
"path": "packages/hooks/src/useDeepCompareEffect/index.test.ts",
"chars": 760,
"preview": "import { useState } from 'react';\n\nimport { act, renderHook } from '@testing-library/react-hooks';\n\nimport useDeepCompar"
},
{
"path": "packages/hooks/src/useDeepCompareEffect/index.ts",
"chars": 450,
"preview": "/* eslint-disable */\nimport { DependencyList, EffectCallback, useEffect, useRef } from 'react';\n\nimport { isEqual } from"
},
{
"path": "packages/hooks/src/useDeviceOrientation/index.ts",
"chars": 1319,
"preview": "import { useEffect } from 'react';\nimport { Dimensions, ScaledSize } from 'react-native';\n\nimport useDimensions from '.."
},
{
"path": "packages/hooks/src/useDimensions/index.ts",
"chars": 966,
"preview": "import { useEffect } from 'react';\nimport { Dimensions, ScaledSize } from 'react-native';\n\nimport useSafeState from '../"
},
{
"path": "packages/hooks/src/useDynamicList/index.test.ts",
"chars": 4971,
"preview": "import { act, renderHook } from '@testing-library/react-hooks';\n\nimport useDynamicList from './index';\n\ndescribe('useDyn"
},
{
"path": "packages/hooks/src/useDynamicList/index.ts",
"chars": 4217,
"preview": "import { useCallback, useRef, useState } from 'react';\n\nimport useMemoizedFn from '../useMemoizedFn';\n\nexport default fu"
},
{
"path": "packages/hooks/src/useEventEmitter/index.ts",
"chars": 961,
"preview": "/* eslint-disable */\nimport { useEffect, useRef } from 'react';\n\ntype Subscription<T> = (val: T) => void;\n\nexport class "
},
{
"path": "packages/hooks/src/useGetState/__tests__/index.test.ts",
"chars": 944,
"preview": "import { act, renderHook } from '@testing-library/react-hooks';\n\nimport useGetState from '../index';\n\ndescribe('useGetSt"
},
{
"path": "packages/hooks/src/useGetState/demo/demo1.tsx",
"chars": 607,
"preview": "/**\n * title: Open console to view logs\n * desc: The counter prints the value every 3 seconds\n *\n * title.zh-CN: 打开控制台查看"
},
{
"path": "packages/hooks/src/useGetState/index.ts",
"chars": 749,
"preview": "import type { Dispatch, SetStateAction } from 'react';\nimport { useRef } from 'react';\n\nimport useMemoizedFn from '../us"
},
{
"path": "packages/hooks/src/useHistoryTravel/index.ts",
"chars": 2707,
"preview": "import { useRef, useState } from 'react';\n\nimport { isNumber } from 'lodash-es';\n\nimport useMemoizedFn from '../useMemoi"
},
{
"path": "packages/hooks/src/useInfiniteScroll/__tests__/index.test.ts",
"chars": 3955,
"preview": "import { useState } from 'react';\n\nimport { act, renderHook } from '@testing-library/react-hooks';\n\nimport { sleep } fro"
},
{
"path": "packages/hooks/src/useInfiniteScroll/index.ts",
"chars": 2613,
"preview": "import { DependencyList } from 'react';\n\nimport useMemoizedFn from '../useMemoizedFn';\nimport useRequest from '../useReq"
},
{
"path": "packages/hooks/src/useInterval/demo/demo1.tsx",
"chars": 258,
"preview": "import React, { useState } from 'react';\n\nimport { useInterval } from '@td-design/rn-hooks';\n\nexport default () => {\n c"
},
{
"path": "packages/hooks/src/useInterval/demo/demo2.tsx",
"chars": 851,
"preview": "import React, { useState } from 'react';\n\nimport { useInterval } from '@td-design/rn-hooks';\n\nexport default () => {\n c"
},
{
"path": "packages/hooks/src/useInterval/demo/demo3.tsx",
"chars": 1367,
"preview": "import React, { Dispatch, SetStateAction, useState } from 'react';\n\nimport { useInterval } from '@td-design/rn-hooks';\n\n"
},
{
"path": "packages/hooks/src/useInterval/index.test.ts",
"chars": 1143,
"preview": "import { renderHook } from '@testing-library/react-hooks';\n\nimport useInterval from './index';\n\ndescribe('useInterval', "
},
{
"path": "packages/hooks/src/useInterval/index.ts",
"chars": 766,
"preview": "import { useEffect, useRef } from 'react';\n\nimport { isNumber } from 'lodash-es';\n\nimport useMemoizedFn from '../useMemo"
},
{
"path": "packages/hooks/src/useKeyboard/index.test.ts",
"chars": 165,
"preview": "import useKeyboard from './index';\n\ndescribe('useKeyboard', () => {\n test('useKeyboard should be defined', () => {\n "
},
{
"path": "packages/hooks/src/useKeyboard/index.ts",
"chars": 1647,
"preview": "import { useEffect } from 'react';\nimport { Keyboard, KeyboardMetrics } from 'react-native';\n\nimport useBoolean from '.."
},
{
"path": "packages/hooks/src/useLatest/demo/demo1.tsx",
"chars": 446,
"preview": "import React, { useEffect, useState } from 'react';\n\nimport { useLatest } from '@td-design/rn-hooks';\n\nexport default ()"
},
{
"path": "packages/hooks/src/useLatest/index.test.ts",
"chars": 1116,
"preview": "import { renderHook } from '@testing-library/react-hooks';\n\nimport useLatest from './index';\n\ndescribe('useLatest', () ="
},
{
"path": "packages/hooks/src/useLatest/index.ts",
"chars": 224,
"preview": "import { useRef } from 'react';\n\n/**\n * 返回当前最新值的 Hook,可以避免闭包问题。\n * @param value 要持久化的值\n * @returns\n */\nexport default fu"
},
{
"path": "packages/hooks/src/useLayout/index.test.ts",
"chars": 821,
"preview": "import { act, renderHook } from '@testing-library/react-hooks';\n\nimport useLayout from './index';\n\ndescribe('useLayout',"
},
{
"path": "packages/hooks/src/useLayout/index.ts",
"chars": 349,
"preview": "import useMemoizedFn from '../useMemoizedFn';\nimport useSafeState from '../useSafeState';\n\nexport default function useLa"
},
{
"path": "packages/hooks/src/useLockFn/demo/demo1.tsx",
"chars": 617,
"preview": "import React, { useState } from 'react';\n\nimport { useLockFn } from '@td-design/rn-hooks';\nimport { message } from 'antd"
},
{
"path": "packages/hooks/src/useLockFn/index.test.ts",
"chars": 1723,
"preview": "import { useCallback, useRef, useState } from 'react';\n\nimport { act, renderHook } from '@testing-library/react-hooks';\n"
},
{
"path": "packages/hooks/src/useLockFn/index.ts",
"chars": 601,
"preview": "import { useCallback, useRef } from 'react';\n\n/**\n * 用于给一个异步函数增加竞态锁,防止并发执行。\n * 可以用在诸如表单提交的场景下,保证即便多次点击提交,在前一次结果未完成之前,后续操"
},
{
"path": "packages/hooks/src/useMap/demo/demo1.tsx",
"chars": 896,
"preview": "import React from 'react';\n\nimport { useMap } from '@td-design/rn-hooks';\n\nexport default () => {\n const [map, { set, s"
},
{
"path": "packages/hooks/src/useMap/index.test.ts",
"chars": 2870,
"preview": "import { act, renderHook } from '@testing-library/react-hooks';\n\nimport useMap from './index';\n\nconst setUp = (initialMa"
},
{
"path": "packages/hooks/src/useMap/index.ts",
"chars": 1062,
"preview": "import { useState } from 'react';\n\nimport useMemoizedFn from '../useMemoizedFn';\n\n/**\n * 一个可以管理 Map 类型状态的 Hook。\n * @para"
},
{
"path": "packages/hooks/src/useMemoizedFn/index.test.ts",
"chars": 956,
"preview": "import { useState } from 'react';\n\nimport { act, renderHook } from '@testing-library/react-hooks';\n\nimport useMemoizedFn"
},
{
"path": "packages/hooks/src/useMemoizedFn/index.ts",
"chars": 853,
"preview": "import { useMemo, useRef } from 'react';\n\nimport { isFunction } from '../utils';\n\ntype noop = (this: any, ...args: any[]"
},
{
"path": "packages/hooks/src/useMount/demo/demo1.tsx",
"chars": 477,
"preview": "import React from 'react';\n\nimport { useMount, useToggle } from '@td-design/rn-hooks';\nimport { message } from 'antd';\n\n"
},
{
"path": "packages/hooks/src/useMount/index.test.ts",
"chars": 667,
"preview": "import { renderHook } from '@testing-library/react-hooks';\n\nimport useMount from './index';\n\ndescribe('useMount', () => "
},
{
"path": "packages/hooks/src/useMount/index.ts",
"chars": 478,
"preview": "import { useEffect } from 'react';\n\nimport { isFunction } from '../utils';\n\ntype Func = (...args: any[]) => any;\n\n/**\n *"
},
{
"path": "packages/hooks/src/usePagination/index.test.ts",
"chars": 5251,
"preview": "import { act, renderHook, RenderHookResult } from '@testing-library/react-hooks';\n\nimport usePagination from './index';\n"
},
{
"path": "packages/hooks/src/usePagination/index.ts",
"chars": 1956,
"preview": "import { useMemo } from 'react';\n\nimport useMemoizedFn from '../useMemoizedFn';\nimport useRequest from '../useRequest';\n"
},
{
"path": "packages/hooks/src/usePagination/testingHelper.ts",
"chars": 652,
"preview": "import type { Data, Params, Service } from './types';\n\nexport const request: Service<Data, Params> = ({\n current = 1,\n "
},
{
"path": "packages/hooks/src/usePagination/types.ts",
"chars": 854,
"preview": "import type { Options, Result } from '../useRequest/types';\n\nexport type Data = { total: number; list: any[] };\n\nexport "
},
{
"path": "packages/hooks/src/usePrevious/demo/demo1.tsx",
"chars": 583,
"preview": "import React, { useState } from 'react';\n\nimport { usePrevious } from '@td-design/rn-hooks';\n\nexport default () => {\n c"
},
{
"path": "packages/hooks/src/usePrevious/demo/demo2.tsx",
"chars": 2130,
"preview": "/**\n * 只有 shouldUpdate function 返回 true 时,才会记录值的变化。\n */\nimport React, { useState } from 'react';\n\nimport { usePrevious }"
},
{
"path": "packages/hooks/src/usePrevious/index.test.ts",
"chars": 2300,
"preview": "import { renderHook } from '@testing-library/react-hooks';\n\nimport usePrevious, { ShouldUpdateFunc } from './index';\n\nde"
},
{
"path": "packages/hooks/src/usePrevious/index.ts",
"chars": 613,
"preview": "import { useRef } from 'react';\n\nexport type ShouldUpdateFunc<T> = (prev: T | undefined, next: T) => boolean;\n\nconst def"
},
{
"path": "packages/hooks/src/useRafInterval/demo/demo1.tsx",
"chars": 366,
"preview": "/**\n * title: 基础组件 usage\n * desc: Execute once per 1000ms.\n *\n * title.zh-CN: 基础用法\n * desc.zh-CN: 每1000ms,执行一次\n */\nimpor"
},
{
"path": "packages/hooks/src/useRafInterval/demo/demo2.tsx",
"chars": 993,
"preview": "/**\n * title: Advanced usage\n * desc: Modify the delay to realize the timer interval change and pause.\n *\n * title.zh-CN"
},
{
"path": "packages/hooks/src/useRafInterval/index.test.ts",
"chars": 1451,
"preview": "import { act, renderHook } from '@testing-library/react-hooks';\n\nimport useRafInterval from './index';\n\nconst FRAME_TIME"
},
{
"path": "packages/hooks/src/useRafInterval/index.ts",
"chars": 1729,
"preview": "import { useEffect, useRef } from 'react';\n\nimport { isNumber } from 'lodash-es';\n\nimport useMemoizedFn from '../useMemo"
},
{
"path": "packages/hooks/src/useRafState/demo/demo1.tsx",
"chars": 706,
"preview": "/**\n * title: Default usage\n *\n * title.zh-CN: 基础用法\n */\nimport React, { useEffect } from 'react';\n\nimport { useRafState "
},
{
"path": "packages/hooks/src/useRafState/index.test.ts",
"chars": 600,
"preview": "import { act, renderHook } from '@testing-library/react-hooks';\n\nimport useRafState from './index';\n\ndescribe('useRafSta"
},
{
"path": "packages/hooks/src/useRafState/index.ts",
"chars": 857,
"preview": "import type { Dispatch, SetStateAction } from 'react';\nimport { useRef, useState } from 'react';\n\nimport useMemoizedFn f"
},
{
"path": "packages/hooks/src/useRafTimeout/demo/demo1.tsx",
"chars": 362,
"preview": "/**\n * title: 基础组件 usage\n * desc: Execute after 2000ms.\n *\n * title.zh-CN: 基础用法\n * desc.zh-CN: 在 2000ms 后执行。\n */\nimport "
},
{
"path": "packages/hooks/src/useRafTimeout/demo/demo2.tsx",
"chars": 980,
"preview": "/**\n * title: Advanced usage\n * desc: Modify the delay to realize the timer timeout change and pause.\n *\n * title.zh-CN:"
},
{
"path": "packages/hooks/src/useRafTimeout/index.test.ts",
"chars": 1298,
"preview": "import { act, renderHook } from '@testing-library/react-hooks';\n\nimport useRafTimeout from './index';\n\ninterface ParamsO"
},
{
"path": "packages/hooks/src/useRafTimeout/index.ts",
"chars": 1619,
"preview": "import { useEffect, useRef } from 'react';\n\nimport { isNumber } from 'lodash-es';\n\nimport useMemoizedFn from '../useMemo"
},
{
"path": "packages/hooks/src/useRequest/Fetch.ts",
"chars": 3616,
"preview": "import { MutableRefObject } from 'react';\n\nimport { isFunction } from '../utils';\nimport type { FetchState, Options, Plu"
},
{
"path": "packages/hooks/src/useRequest/index.test.ts",
"chars": 4495,
"preview": "import { act, renderHook, RenderHookResult } from '@testing-library/react-hooks';\n\nimport useRequest from './index';\nimp"
},
{
"path": "packages/hooks/src/useRequest/index.ts",
"chars": 1077,
"preview": "import { useAutoRunPlugin } from './plugins/useAutoRunPlugin';\nimport { useCachePlugin } from './plugins/useCachePlugin'"
},
{
"path": "packages/hooks/src/useRequest/plugins/useAutoRunPlugin.ts",
"chars": 1032,
"preview": "import { useRef } from 'react';\n\nimport useUpdateEffect from '../../useUpdateEffect';\nimport type { Plugin } from '../ty"
},
{
"path": "packages/hooks/src/useRequest/plugins/useCachePlugin.ts",
"chars": 3743,
"preview": "import { useRef } from 'react';\n\nimport * as cache from '../utils/cache';\nimport * as cachePromise from '../utils/cacheP"
},
{
"path": "packages/hooks/src/useRequest/plugins/useDebouncePlugin.ts",
"chars": 1657,
"preview": "import { useEffect, useMemo, useRef } from 'react';\n\nimport { debounce, DebouncedFunc, DebounceSettings } from 'lodash-e"
},
{
"path": "packages/hooks/src/useRequest/plugins/useLoadingDelayPlugin.ts",
"chars": 845,
"preview": "import { useRef } from 'react';\n\nimport type { Plugin } from '../types';\n\nexport const useLoadingDelayPlugin: Plugin<any"
},
{
"path": "packages/hooks/src/useRequest/plugins/usePollingPlugin.ts",
"chars": 1385,
"preview": "import { useRef } from 'react';\n\nimport useUpdateEffect from '../../useUpdateEffect';\nimport type { Plugin } from '../ty"
},
{
"path": "packages/hooks/src/useRequest/plugins/useRetryPlugin.ts",
"chars": 1152,
"preview": "import { useRef } from 'react';\n\nimport type { Plugin } from '../types';\n\nexport const useRetryPlugin: Plugin<any, any[]"
},
{
"path": "packages/hooks/src/useRequest/plugins/useThrottlePlugin.ts",
"chars": 1479,
"preview": "import { useEffect, useMemo, useRef } from 'react';\n\nimport { DebouncedFunc, throttle, ThrottleSettings } from 'lodash-e"
},
{
"path": "packages/hooks/src/useRequest/types.ts",
"chars": 2619,
"preview": "import type { DependencyList } from 'react';\n\nimport type Fetch from './Fetch';\nimport { CachedData } from './utils/cach"
},
{
"path": "packages/hooks/src/useRequest/useRequestImpl.ts",
"chars": 2237,
"preview": "import useCreation from '../useCreation';\nimport useLatest from '../useLatest';\nimport useMemoizedFn from '../useMemoize"
},
{
"path": "packages/hooks/src/useRequest/utils/cache.ts",
"chars": 1069,
"preview": "type Timer = ReturnType<typeof setTimeout>;\ntype CachedKey = string | number;\n\nexport interface CachedData<TData = any, "
},
{
"path": "packages/hooks/src/useRequest/utils/cachePromise.ts",
"chars": 684,
"preview": "type CachedKey = string | number;\nconst cachePromise = new Map<CachedKey, Promise<any>>();\n\nconst getCachePromise = (cac"
},
{
"path": "packages/hooks/src/useRequest/utils/cacheSubscribe.ts",
"chars": 522,
"preview": "type Listener = (data: any) => void;\nconst listeners: Record<string, Listener[]> = {};\n\nconst trigger = (key: string, da"
},
{
"path": "packages/hooks/src/useRequest/utils/testingHelper.ts",
"chars": 305,
"preview": "import type { Service } from '../types';\n\nexport const request: Service<any, any[]> = (req?: number) => {\n return new P"
},
{
"path": "packages/hooks/src/useResetState/__tests__/index.test.ts",
"chars": 1170,
"preview": "import { act, renderHook } from '@testing-library/react-hooks';\n\nimport useResetState from '../index';\n\ndescribe('useRes"
},
{
"path": "packages/hooks/src/useResetState/demo/demo1.tsx",
"chars": 614,
"preview": "import React from 'react';\n\nimport { useResetState } from 'ahooks';\n\ninterface State {\n hello: string;\n count: number;"
},
{
"path": "packages/hooks/src/useResetState/index.ts",
"chars": 493,
"preview": "import type { Dispatch, SetStateAction } from 'react';\n\nimport useMemoizedFn from '../useMemoizedFn';\nimport useSafeStat"
},
{
"path": "packages/hooks/src/useSafeState/demo/demo1.tsx",
"chars": 549,
"preview": "import React, { useEffect, useState } from 'react';\n\nimport { useSafeState } from '@td-design/rn-hooks';\n\nconst Child = "
},
{
"path": "packages/hooks/src/useSafeState/index.test.ts",
"chars": 1071,
"preview": "import { act, renderHook } from '@testing-library/react-hooks';\n\nimport useSafeState from './index';\n\ndescribe('useSafeS"
},
{
"path": "packages/hooks/src/useSafeState/index.ts",
"chars": 860,
"preview": "import { Dispatch, SetStateAction, useState } from 'react';\n\nimport useMemoizedFn from '../useMemoizedFn';\nimport useUnm"
},
{
"path": "packages/hooks/src/useSet/demo/demo1.tsx",
"chars": 656,
"preview": "import React from 'react';\n\nimport { useSet } from '@td-design/rn-hooks';\n\nexport default () => {\n const [set, { add, r"
},
{
"path": "packages/hooks/src/useSet/index.test.ts",
"chars": 3195,
"preview": "import { act, renderHook } from '@testing-library/react-hooks';\n\nimport useSet from './index';\n\nconst setUp = <K>(initia"
},
{
"path": "packages/hooks/src/useSet/index.ts",
"chars": 916,
"preview": "import { useState } from 'react';\n\nimport useMemoizedFn from '../useMemoizedFn';\n\n/**\n * 一个可以管理 Set 类型状态的 Hook。\n * @para"
},
{
"path": "packages/hooks/src/useSetState/index.ts",
"chars": 750,
"preview": "import { useState } from 'react';\n\nimport useMemoizedFn from '../useMemoizedFn';\nimport { isFunction } from '../utils';\n"
},
{
"path": "packages/hooks/src/useSms/index.test.ts",
"chars": 2025,
"preview": "import { act, renderHook } from '@testing-library/react-hooks';\n\nimport useSms from './index';\n\ndescribe('useSms', () =>"
},
{
"path": "packages/hooks/src/useSms/index.ts",
"chars": 1785,
"preview": "import { ForwardedRef, MutableRefObject, useEffect, useRef, useState } from 'react';\nimport { TextInput } from 'react-na"
},
{
"path": "packages/hooks/src/useThrottle/ThrottleOptions.ts",
"chars": 161,
"preview": "export interface ThrottleOptions {\n /** 超时时间,单位为毫秒 */\n wait?: number;\n /** 是否在延迟开始前调用函数 */\n leading?: boolean;\n /**"
},
{
"path": "packages/hooks/src/useThrottle/demo/demo1.tsx",
"chars": 458,
"preview": "import React, { useState } from 'react';\n\nimport { useThrottle } from '@td-design/rn-hooks';\n\nexport default () => {\n c"
},
{
"path": "packages/hooks/src/useThrottle/index.ts",
"chars": 581,
"preview": "import { useEffect, useState } from 'react';\n\nimport useThrottleFn from '../useThrottleFn';\nimport type { ThrottleOption"
},
{
"path": "packages/hooks/src/useThrottleEffect/demo/demo1.tsx",
"chars": 690,
"preview": "import React, { useState } from 'react';\n\nimport { useThrottleEffect } from '@td-design/rn-hooks';\n\nexport default () =>"
},
{
"path": "packages/hooks/src/useThrottleEffect/index.test.ts",
"chars": 1869,
"preview": "import { act, renderHook } from '@testing-library/react-hooks';\n\nimport { sleep } from '../utils/testHelpers';\nimport us"
},
{
"path": "packages/hooks/src/useThrottleEffect/index.ts",
"chars": 600,
"preview": "import { DependencyList, EffectCallback, useEffect, useState } from 'react';\n\nimport type { ThrottleOptions } from '../u"
},
{
"path": "packages/hooks/src/useThrottleFn/demo/demo1.tsx",
"chars": 446,
"preview": "import React, { useState } from 'react';\n\nimport { useThrottleFn } from '@td-design/rn-hooks';\n\nexport default () => {\n "
},
{
"path": "packages/hooks/src/useThrottleFn/index.test.ts",
"chars": 2952,
"preview": "import { act, renderHook } from '@testing-library/react-hooks';\n\nimport { sleep } from '../utils/testHelpers';\nimport us"
},
{
"path": "packages/hooks/src/useThrottleFn/index.ts",
"chars": 1025,
"preview": "import { throttle } from 'lodash-es';\n\nimport useCreation from '../useCreation';\nimport useMemoizedFn from '../useMemoiz"
},
{
"path": "packages/hooks/src/useTimeout/demo/demo1.tsx",
"chars": 248,
"preview": "import React, { useState } from 'react';\n\nimport { useTimeout } from '@td-design/rn-hooks';\n\nexport default () => {\n co"
},
{
"path": "packages/hooks/src/useTimeout/index.test.ts",
"chars": 542,
"preview": "import { renderHook } from '@testing-library/react-hooks';\n\nimport useTimeout from './index';\n\ndescribe('useTimeout', ()"
},
{
"path": "packages/hooks/src/useTimeout/index.ts",
"chars": 702,
"preview": "import { useEffect, useRef } from 'react';\n\nimport { isNumber } from 'lodash-es';\n\nimport useMemoizedFn from '../useMemo"
},
{
"path": "packages/hooks/src/useToggle/demo/demo1.tsx",
"chars": 550,
"preview": "import React from 'react';\n\nimport { useToggle } from '@td-design/rn-hooks';\n\nexport default () => {\n const [state, { t"
},
{
"path": "packages/hooks/src/useToggle/demo/demo2.tsx",
"chars": 784,
"preview": "import React from 'react';\n\nimport { useToggle } from '@td-design/rn-hooks';\n\nexport default () => {\n const [state, { t"
},
{
"path": "packages/hooks/src/useToggle/index.test.ts",
"chars": 1355,
"preview": "import { act, renderHook } from '@testing-library/react-hooks';\n\nimport useToggle from './index';\n\ndescribe('useToggle',"
},
{
"path": "packages/hooks/src/useToggle/index.ts",
"chars": 1243,
"preview": "import { useState } from 'react';\n\nimport useMemoizedFn from '../useMemoizedFn';\n\ninterface Actions<T> {\n setLeft: () ="
},
{
"path": "packages/hooks/src/useTrackedEffect/index.ts",
"chars": 1126,
"preview": "import { DependencyList, useEffect, useRef } from 'react';\n\ntype Effect<T extends DependencyList> = (changes?: number[],"
},
{
"path": "packages/hooks/src/useUnmount/demo/demo1.tsx",
"chars": 481,
"preview": "import React from 'react';\n\nimport { useBoolean, useUnmount } from '@td-design/rn-hooks';\nimport { message } from 'antd'"
}
]
// ... and 798 more files (download for full content)
About this extraction
This page contains the full source code of the thundersdata-frontend/td-design GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 998 files (4.4 MB), approximately 1.2M tokens, and a symbol index with 745 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.