diff --git a/.dumirc.ts b/.dumirc.ts new file mode 100644 index 0000000..bd80941 --- /dev/null +++ b/.dumirc.ts @@ -0,0 +1,23 @@ +import { defineConfig } from 'dumi'; +import path from 'path'; + +export default defineConfig({ + alias: { + 'rc-trigger$': path.resolve('src'), + 'rc-trigger/es': path.resolve('src'), + }, + mfsu: false, + favicons: ['https://avatars0.githubusercontent.com/u/9441414?s=200&v=4'], + themeConfig: { + name: 'Trigger', + logo: 'https://avatars0.githubusercontent.com/u/9441414?s=200&v=4', + }, + styles: [ + ` + .dumi-default-previewer-demo { + position: relative; + min-height: 300px; + } + `, + ] +}); diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..604c94e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*.{js,css}] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..7e19ab3 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,14 @@ +module.exports = { + extends: [require.resolve('@umijs/fabric/dist/eslint')], + rules: { + 'default-case': 0, + 'import/no-extraneous-dependencies': 0, + 'react-hooks/exhaustive-deps': 0, + 'react/no-find-dom-node': 0, + 'react/no-did-update-set-state': 0, + 'react/no-unused-state': 1, + 'react/sort-comp': 0, + 'jsx-a11y/label-has-for': 0, + 'jsx-a11y/label-has-associated-control': 0, + }, +}; diff --git a/.fatherrc.js b/.fatherrc.js new file mode 100644 index 0000000..4ddbafd --- /dev/null +++ b/.fatherrc.js @@ -0,0 +1,5 @@ +import { defineConfig } from 'father'; + +export default defineConfig({ + plugins: ['@rc-component/father-plugin'], +}); \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..23a5fb8 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,30 @@ +version: 2 +updates: +- package-ecosystem: npm + directory: "/" + schedule: + interval: daily + time: "21:00" + open-pull-requests-limit: 10 + ignore: + - dependency-name: np + versions: + - 7.2.0 + - 7.3.0 + - 7.4.0 + - dependency-name: "@types/react-dom" + versions: + - 17.0.0 + - 17.0.1 + - 17.0.2 + - dependency-name: "@types/react" + versions: + - 17.0.0 + - 17.0.1 + - 17.0.2 + - 17.0.3 + - dependency-name: typescript + versions: + - 4.1.3 + - 4.1.4 + - 4.1.5 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..67f7884 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,41 @@ +name: "CodeQL" + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + schedule: + - cron: "40 12 * * 0" + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ javascript ] + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + queries: +security-and-quality + + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..a23ad09 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,114 @@ +name: CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + setup: + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@master + + - uses: actions/setup-node@v1 + with: + node-version: '16' + + - name: cache package-lock.json + uses: actions/cache@v2 + with: + path: package-temp-dir + key: lock-${{ github.sha }} + + - name: create package-lock.json + run: npm i --package-lock-only + + - name: hack for singe file + run: | + if [ ! -d "package-temp-dir" ]; then + mkdir package-temp-dir + fi + cp package-lock.json package-temp-dir + + - name: cache node_modules + id: node_modules_cache_id + uses: actions/cache@v2 + with: + path: node_modules + key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }} + + - name: install + if: steps.node_modules_cache_id.outputs.cache-hit != 'true' + run: npm i + + lint: + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@master + + - name: restore cache from package-lock.json + uses: actions/cache@v2 + with: + path: package-temp-dir + key: lock-${{ github.sha }} + + - name: restore cache from node_modules + uses: actions/cache@v2 + with: + path: node_modules + key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }} + + - name: lint + run: npm run lint + + needs: setup + + compile: + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@master + + - name: restore cache from package-lock.json + uses: actions/cache@v2 + with: + path: package-temp-dir + key: lock-${{ github.sha }} + + - name: restore cache from node_modules + uses: actions/cache@v2 + with: + path: node_modules + key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }} + + - name: compile + run: npm run compile + + needs: setup + + coverage: + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@master + + - name: restore cache from package-lock.json + uses: actions/cache@v2 + with: + path: package-temp-dir + key: lock-${{ github.sha }} + + - name: restore cache from node_modules + uses: actions/cache@v2 + with: + path: node_modules + key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }} + + - name: coverage + run: npm run coverage && bash <(curl -s https://codecov.io/bash) + + needs: setup \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..65899cf --- /dev/null +++ b/.gitignore @@ -0,0 +1,40 @@ +.storybook +*.iml +*.log +.idea +.ipr +.iws +*~ +~* +*.diff +*.patch +*.bak +.DS_Store +Thumbs.db +.project +.*proj +.svn +*.swp +*.swo +*.pyc +*.pyo +node_modules +.cache +*.css +build +lib +es +coverage +yarn.lock +package-lock.json + +# dumi +.umi +.umi-production +.umi-test +.docs + + +# dumi +.dumi/tmp +.dumi/tmp-production \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..fb49bf5 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,8 @@ +{ + "endOfLine": "lf", + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "all", + "jsxSingleQuote": false +} diff --git a/HISTORY.md b/HISTORY.md new file mode 100644 index 0000000..7ee63c8 --- /dev/null +++ b/HISTORY.md @@ -0,0 +1,58 @@ +# History +---- + +## 4.1.0 / 2020-05-08 + +- upgrade rc-animate to `3.x` + +## 2.5.0 / 2018-06-05 + +- support `alignPoint` + +## 2.1.0 / 2017-10-16 + +- add action `contextMenu` + +## 2.0.0 / 2017-09-25 + +- support React 16 + +## 1.11.0 / 2017-06-07 + +- add es + +## 1.9.0 / 2017-02-27 + +- add getDocument prop + +## 1.8.2 / 2017-02-24 + +- change default container to absolute to fix scrollbar change problem + +## 1.7.0 / 2016-07-18 + +- use getContainerRenderMixin from 'rc-util' + +## 1.6.0 / 2016-05-26 + +- support popup as function + +## 1.5.0 / 2016-05-26 + +- add forcePopupAlign method + +## 1.4.0 / 2016-04-06 + +- support onPopupAlign + +## 1.3.0 / 2016-03-25 + +- support mask/maskTransitionName/zIndex + +## 1.2.0 / 2016-03-01 + +- add showAction/hideAction + +## 1.1.0 / 2016-01-06 + +- add root trigger node as parameter of getPopupContainer diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fbf368a --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) +Copyright (c) 2015-present Alipay.com, https://www.alipay.com/ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e479e56 --- /dev/null +++ b/README.md @@ -0,0 +1,258 @@ +# @rc-component/trigger + +React Trigger Component + +[![NPM version][npm-image]][npm-url] +[![npm download][download-image]][download-url] +[![build status][github-actions-image]][github-actions-url] +[![Test coverage][codecov-image]][codecov-url] +[![bundle size][bundlephobia-image]][bundlephobia-url] +[![dumi][dumi-image]][dumi-url] + +[npm-image]: http://img.shields.io/npm/v/rc-checkbox.svg?style=flat-square +[npm-url]: http://npmjs.org/package/rc-checkbox +[github-actions-image]: https://github.com/react-component/checkbox/workflows/CI/badge.svg +[github-actions-url]: https://github.com/react-component/checkbox/actions +[codecov-image]: https://img.shields.io/codecov/c/github/react-component/checkbox/master.svg?style=flat-square +[codecov-url]: https://codecov.io/gh/react-component/checkbox/branch/master +[david-url]: https://david-dm.org/react-component/checkbox +[david-image]: https://david-dm.org/react-component/checkbox/status.svg?style=flat-square +[david-dev-url]: https://david-dm.org/react-component/checkbox?type=dev +[david-dev-image]: https://david-dm.org/react-component/checkbox/dev-status.svg?style=flat-square +[download-image]: https://img.shields.io/npm/dm/rc-checkbox.svg?style=flat-square +[download-url]: https://npmjs.org/package/rc-checkbox +[bundlephobia-url]: https://bundlephobia.com/result?p=rc-checkbox +[bundlephobia-image]: https://badgen.net/bundlephobia/minzip/rc-checkbox +[dumi-image]: https://img.shields.io/badge/docs%20by-dumi-blue?style=flat-square +[dumi-url]: https://github.com/umijs/dumi + +## Install + +[![@rc-component/trigger](https://nodei.co/npm/@rc-component/trigger.png)](https://npmjs.org/package/@rc-component/trigger) + +## Usage + +Include the default [styling](https://github.com/react-component/trigger/blob/master/assets/index.less#L4:L11) and then: + +```js +import React from 'react'; +import ReactDOM from 'react-dom'; +import Trigger from '@rc-component/trigger'; + +ReactDOM.render(( + popup} + popupAlign={{ + points: ['tl', 'bl'], + offset: [0, 3] + }} + > + hover + +), container); +``` + +## Compatibility + +| [IE / Edge](http://godban.github.io/browsers-support-badges/)
IE / Edge | [Firefox](http://godban.github.io/browsers-support-badges/)
Firefox | [Chrome](http://godban.github.io/browsers-support-badges/)
Chrome | [Safari](http://godban.github.io/browsers-support-badges/)
Safari | [Electron](http://godban.github.io/browsers-support-badges/)
Electron | +| --- | --- | --- | --- | --- | +| IE11, Edge | last 2 versions | last 2 versions | last 2 versions | last 2 versions | + +## Example + +http://localhost:9001 + +## Development + +``` +npm install +npm start +``` + +## API + +### props + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
nametypedefaultdescription
alignPointboolfalsePopup will align with mouse position (support action of 'click', 'hover' and 'contextMenu')
popupClassNamestringadditional className added to popup
forceRenderbooleanfalsewhether render popup before first show
destroyPopupOnHidebooleanfalsewhether destroy popup when hide
getPopupClassNameFromAligngetPopupClassNameFromAlign(align: Object):Stringadditional className added to popup according to align
actionstring[]['hover']which actions cause popup shown. enum of 'hover','click','focus','contextMenu'
mouseEnterDelaynumber0delay time to show when mouse enter. unit: s.
mouseLeaveDelaynumber0.1delay time to hide when mouse leave. unit: s.
popupStyleObjectadditional style of popup
prefixClsStringrc-trigger-popupprefix class name
popupTransitionNameString|Objecthttps://github.com/react-component/animate
maskTransitionNameString|Objecthttps://github.com/react-component/animate
onPopupVisibleChangeFunctioncall when popup visible is changed
maskbooleanfalsewhether to support mask
maskClosablebooleantruewhether to support click mask to hide
popupVisiblebooleanwhether popup is visible
zIndexnumberpopup's zIndex
defaultPopupVisiblebooleanwhether popup is visible initially
popupAlignObject: alignConfig of [dom-align](https://github.com/yiminghe/dom-align)popup 's align config
onPopupAlignfunction(popupDomNode, align)callback when popup node is aligned
popupReact.Element | function() => React.Elementpopup content
getPopupContainergetPopupContainer(): HTMLElementfunction returning html node which will act as popup container
getDocumentgetDocument(): HTMLElementfunction returning document node which will be attached click event to close trigger
popupPlacementstringuse preset popup align config from builtinPlacements, can be merged by popupAlign prop
builtinPlacementsobjectbuiltin placement align map. used by placement prop
stretchstringLet popup div stretch with trigger element. enums of 'width', 'minWidth', 'height', 'minHeight'. (You can also mixed with 'height minWidth')
+ + +## Test Case + +``` +npm test +npm run coverage +``` + +open coverage/ dir + +## License + +rc-trigger is released under the MIT license. diff --git a/assets/index.less b/assets/index.less new file mode 100644 index 0000000..e13a125 --- /dev/null +++ b/assets/index.less @@ -0,0 +1,79 @@ +@triggerPrefixCls: rc-trigger-popup; + +.@{triggerPrefixCls} { + position: absolute; + top: -9999px; + left: -9999px; + z-index: 1050; + + &-hidden { + display: none; + } + + .effect() { + animation-duration: 0.3s; + animation-fill-mode: both; + } + + &-zoom-enter, + &-zoom-appear { + opacity: 0; + animation-play-state: paused; + animation-timing-function: cubic-bezier(0.18, 0.89, 0.32, 1.28); + .effect(); + } + + &-zoom-leave { + .effect(); + animation-play-state: paused; + animation-timing-function: cubic-bezier(0.6, -0.3, 0.74, 0.05); + } + + &-zoom-enter&-zoom-enter-active, + &-zoom-appear&-zoom-appear-active { + animation-name: rcTriggerZoomIn; + animation-play-state: running; + } + + &-zoom-leave&-zoom-leave-active { + animation-name: rcTriggerZoomOut; + animation-play-state: running; + } + + &-arrow { + z-index: 1; + width: 0px; + height: 0px; + background: #000; + border-radius: 100vw; + box-shadow: 0 0 0 3px black; + } + + @keyframes rcTriggerZoomIn { + 0% { + transform: scale(0, 0); + transform-origin: var(--arrow-x, 50%) var(--arrow-y, 50%); + opacity: 0; + } + 100% { + transform: scale(1, 1); + transform-origin: var(--arrow-x, 50%) var(--arrow-y, 50%); + opacity: 1; + } + } + @keyframes rcTriggerZoomOut { + 0% { + transform: scale(1, 1); + transform-origin: var(--arrow-x, 50%) var(--arrow-y, 50%); + opacity: 1; + } + 100% { + transform: scale(0, 0); + transform-origin: var(--arrow-x, 50%) var(--arrow-y, 50%); + opacity: 0; + } + } +} + +@import './index/Mask'; +@import './index/Mobile'; diff --git a/assets/index/Mask.less b/assets/index/Mask.less new file mode 100644 index 0000000..81692e6 --- /dev/null +++ b/assets/index/Mask.less @@ -0,0 +1,63 @@ +.@{triggerPrefixCls} { + &-mask { + position: fixed; + top: 0; + right: 0; + left: 0; + bottom: 0; + background-color: rgb(55, 55, 55); + background-color: rgba(55, 55, 55, 0.6); + height: 100%; + filter: alpha(opacity=50); + z-index: 1050; + + &-hidden { + display: none; + } + } + + .fade-effect() { + animation-duration: 0.3s; + animation-fill-mode: both; + animation-timing-function: cubic-bezier(0.55, 0, 0.55, 0.2); + } + + &-fade-enter,&-fade-appear { + opacity: 0; + .fade-effect(); + animation-play-state: paused; + } + + &-fade-leave { + .fade-effect(); + animation-play-state: paused; + } + + &-fade-enter&-fade-enter-active,&-fade-appear&-fade-appear-active { + animation-name: rcTriggerMaskFadeIn; + animation-play-state: running; + } + + &-fade-leave&-fade-leave-active { + animation-name: rcDialogFadeOut; + animation-play-state: running; + } + + @keyframes rcTriggerMaskFadeIn { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } + } + + @keyframes rcDialogFadeOut { + 0% { + opacity: 1; + } + 100% { + opacity: 0; + } + } +} diff --git a/assets/index/Mobile.less b/assets/index/Mobile.less new file mode 100644 index 0000000..3c302ca --- /dev/null +++ b/assets/index/Mobile.less @@ -0,0 +1,25 @@ +.@{triggerPrefixCls} { + &-mobile { + transition: all 0.3s; + position: fixed; + left: 0; + right: 0; + bottom: 0; + top: auto; + + &-fade { + &-appear, + &-enter { + &-start { + transform: translateY(100%); + } + } + + &-leave { + &-active { + transform: translateY(100%); + } + } + } + } +} diff --git a/docs/demos/body-overflow.md b/docs/demos/body-overflow.md new file mode 100644 index 0000000..6f6f9c4 --- /dev/null +++ b/docs/demos/body-overflow.md @@ -0,0 +1,8 @@ +--- +title: Body Overflow +nav: + title: Demo + path: /demo +--- + + \ No newline at end of file diff --git a/docs/demos/case.md b/docs/demos/case.md new file mode 100644 index 0000000..053a1bc --- /dev/null +++ b/docs/demos/case.md @@ -0,0 +1,8 @@ +--- +title: Case +nav: + title: Demo + path: /demo +--- + + \ No newline at end of file diff --git a/docs/demos/click-nested.md b/docs/demos/click-nested.md new file mode 100644 index 0000000..c67236d --- /dev/null +++ b/docs/demos/click-nested.md @@ -0,0 +1,8 @@ +--- +title: Click Nested +nav: + title: Demo + path: /demo +--- + + \ No newline at end of file diff --git a/docs/demos/clip.md b/docs/demos/clip.md new file mode 100644 index 0000000..e2798ee --- /dev/null +++ b/docs/demos/clip.md @@ -0,0 +1,8 @@ +--- +title: Clip +nav: + title: Demo + path: /demo +--- + + \ No newline at end of file diff --git a/docs/demos/container.md b/docs/demos/container.md new file mode 100644 index 0000000..a4860cb --- /dev/null +++ b/docs/demos/container.md @@ -0,0 +1,8 @@ +--- +title: Container +nav: + title: Demo + path: /demo +--- + + \ No newline at end of file diff --git a/docs/demos/inside.md b/docs/demos/inside.md new file mode 100644 index 0000000..a9853c3 --- /dev/null +++ b/docs/demos/inside.md @@ -0,0 +1,8 @@ +--- +title: Inside +nav: + title: Demo + path: /demo +--- + + \ No newline at end of file diff --git a/docs/demos/large-popup.md b/docs/demos/large-popup.md new file mode 100644 index 0000000..048c77a --- /dev/null +++ b/docs/demos/large-popup.md @@ -0,0 +1,8 @@ +--- +title: Large Popup +nav: + title: Demo + path: /demo +--- + + \ No newline at end of file diff --git a/docs/demos/nested.md b/docs/demos/nested.md new file mode 100644 index 0000000..5daf43b --- /dev/null +++ b/docs/demos/nested.md @@ -0,0 +1,8 @@ +--- +title: Nested +nav: + title: Demo + path: /demo +--- + + \ No newline at end of file diff --git a/docs/demos/point.md b/docs/demos/point.md new file mode 100644 index 0000000..073a89b --- /dev/null +++ b/docs/demos/point.md @@ -0,0 +1,8 @@ +--- +title: Point +nav: + title: Demo + path: /demo +--- + + \ No newline at end of file diff --git a/docs/demos/shadow.md b/docs/demos/shadow.md new file mode 100644 index 0000000..379812b --- /dev/null +++ b/docs/demos/shadow.md @@ -0,0 +1,8 @@ +--- +title: Shadow +nav: + title: Demo + path: /demo +--- + + \ No newline at end of file diff --git a/docs/demos/simple.md b/docs/demos/simple.md new file mode 100644 index 0000000..dbb7c35 --- /dev/null +++ b/docs/demos/simple.md @@ -0,0 +1,8 @@ +--- +title: Simple +nav: + title: Demo + path: /demo +--- + + \ No newline at end of file diff --git a/docs/demos/static-scroll.md b/docs/demos/static-scroll.md new file mode 100644 index 0000000..14a1846 --- /dev/null +++ b/docs/demos/static-scroll.md @@ -0,0 +1,8 @@ +--- +title: Static Scroll +nav: + title: Demo + path: /demo +--- + + diff --git a/docs/demos/visible-fallback.md b/docs/demos/visible-fallback.md new file mode 100644 index 0000000..cb5a8f2 --- /dev/null +++ b/docs/demos/visible-fallback.md @@ -0,0 +1,8 @@ +--- +title: Visible Fallback +nav: + title: Demo + path: /demo +--- + + \ No newline at end of file diff --git a/docs/examples/body-overflow.tsx b/docs/examples/body-overflow.tsx new file mode 100644 index 0000000..744e86a --- /dev/null +++ b/docs/examples/body-overflow.tsx @@ -0,0 +1,248 @@ +/* eslint no-console:0 */ +import Trigger from 'rc-trigger'; +import React from 'react'; +import { createPortal } from 'react-dom'; +import '../../assets/index.less'; + +const PortalDemo = () => { + return createPortal( +
+ PortalNode +
, + document.body, + ); +}; + +export default () => { + const [open, setOpen] = React.useState(false); + const [open1, setOpen1] = React.useState(false); + const [open2, setOpen2] = React.useState(false); + const [open3, setOpen3] = React.useState(false); + return ( + +