Full Code of react-component/dropdown for AI

master 99ae51aee181 cached
43 files
54.8 KB
15.7k tokens
24 symbols
1 requests
Download .txt
Repository: react-component/dropdown
Branch: master
Commit: 99ae51aee181
Files: 43
Total size: 54.8 KB

Directory structure:
gitextract_t_l0mx02/

├── .dumirc.ts
├── .editorconfig
├── .eslintrc.js
├── .fatherrc.ts
├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       ├── ci.yml
│       └── codeql.yml
├── .gitignore
├── .husky/
│   └── pre-commit
├── .prettierrc
├── .travis.yml
├── HISTORY.md
├── LICENSE
├── README.md
├── assets/
│   └── index.less
├── docs/
│   ├── demo/
│   │   ├── arrow.md
│   │   ├── context-menu.md
│   │   ├── dropdown-menu-width.md
│   │   ├── multiple.md
│   │   ├── overlay-callback.md
│   │   └── simple.md
│   ├── examples/
│   │   ├── arrow.jsx
│   │   ├── context-menu.jsx
│   │   ├── dropdown-menu-width.jsx
│   │   ├── multiple.jsx
│   │   ├── overlay-callback.jsx
│   │   └── simple.jsx
│   └── index.md
├── index.js
├── now.json
├── package.json
├── script/
│   └── update-content.js
├── src/
│   ├── Dropdown.tsx
│   ├── Overlay.tsx
│   ├── hooks/
│   │   └── useAccessibility.ts
│   ├── index.tsx
│   └── placements.ts
├── tests/
│   ├── __mocks__/
│   │   └── @rc-component/
│   │       └── trigger.tsx
│   ├── __snapshots__/
│   │   └── basic.test.tsx.snap
│   ├── basic.test.tsx
│   ├── point.test.tsx
│   └── utils.js
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .dumirc.ts
================================================
// more config: https://d.umijs.org/config
import { defineConfig } from 'dumi';

export default defineConfig({
  favicons: ['https://avatars0.githubusercontent.com/u/9441414?s=200&v=4'],
  themeConfig: {
    name: 'rc-dropdown',
    logo: 'https://avatars0.githubusercontent.com/u/9441414?s=200&v=4',
  },
  outputPath: '.docs',
  exportStatic: {},
  styles: [
    `
      section.dumi-default-header-left  {
        width: 240px;
      }
    `,
  ],
});


================================================
FILE: .editorconfig
================================================
# 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


================================================
FILE: .eslintrc.js
================================================
const base = require('@umijs/fabric/dist/eslint');

module.exports = {
  ...base,
  rules: {
    ...base.rules,
    'no-template-curly-in-string': 0,
    'prefer-promise-reject-errors': 0,
    'react/no-array-index-key': 0,
    'react/sort-comp': 0,
    '@typescript-eslint/no-explicit-any': 0,
    'jsx-a11y/label-has-associated-control': 0,
    'jsx-a11y/label-has-for': 0,
    'no-shadow': 0
  },
};


================================================
FILE: .fatherrc.ts
================================================
import { defineConfig } from "father";

export default defineConfig({
  plugins: ["@rc-component/father-plugin"],
});


================================================
FILE: .github/dependabot.yml
================================================
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file

version: 2
updates:
  - package-ecosystem: "" # See documentation for possible values
    directory: "/" # Location of package manifests
    schedule:
      interval: "weekly"


================================================
FILE: .github/workflows/ci.yml
================================================
name: CI

on: ['push', 'pull_request']

jobs:
  CI:
    uses: react-component/rc-test/.github/workflows/test.yml@main
    secrets: inherit


================================================
FILE: .github/workflows/codeql.yml
================================================
name: "CodeQL"

on:
  push:
    branches: [ "master" ]
  pull_request:
    branches: [ "master" ]
  schedule:
    - cron: "38 3 * * 6"

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 }}"


================================================
FILE: .gitignore
================================================
*.iml
*.log
.idea/
.ipr
.iws
*~
~*
*.diff
*.patch
*.bak
.DS_Store
Thumbs.db
.project
.*proj
.svn/
*.swp
*.swo
*.pyc
*.pyo
.build
node_modules
.cache
dist
assets/**/*.css
build
lib
es
coverage
yarn.lock
package-lock.json
pnpm-lock.yaml
.vscode

# dumi
.dumi/tmp
.dumi/tmp-test
.dumi/tmp-production
.docs


================================================
FILE: .husky/pre-commit
================================================
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx lint-staged


================================================
FILE: .prettierrc
================================================
{
  "endOfLine": "lf",
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "all",
  "proseWrap": "never"
}


================================================
FILE: .travis.yml
================================================
language: node_js

node_js:
  - 10

script:
  - |
    if [ "$TEST_TYPE" = test ]; then
      npm run coverage && \
      bash <(curl -s https://codecov.io/bash)
    else
      npm run $TEST_TYPE
    fi
env:
  matrix:
    - TEST_TYPE=lint
    - TEST_TYPE=test


================================================
FILE: HISTORY.md
================================================
# History
----

## 2.4.0 / 2018-12-28

- `overlay` support function render

## 2.3.0 / 2018-12-21

- add `openClassName`

## 2.2.0 / 2018-06-06

- add `alignPoint` to support mosue point align

## 1.5.0 / 2016-07-27

- Add `onOverlayClick`.

-

## 1.4.5 / 2016-03-02

- if exists getPopupContainer it will be passed to Trigger component

## 1.4.0 / 2015-10-26

- update for react 0.14

## 1.2.0 / 2015-06-07

- remove closeOnSelect, use visible prop to control

## 0.8.0 / 2015-06-07

Already available


================================================
FILE: LICENSE
================================================
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.


================================================
FILE: README.md
================================================
# @rc-component/dropdown

react dropdown component.

[![NPM version][npm-image]][npm-url] [![npm download][download-image]][download-url] [![build status][github-actions-image]][github-actions-url] [![Codecov][codecov-image]][codecov-url] [![bundle size][bundlephobia-image]][bundlephobia-url] [![dumi][dumi-image]][dumi-url]

[npm-image]: https://img.shields.io/npm/v/@rc-component/dropdown.svg?style=flat-square
[npm-url]: https://npmjs.org/package/@rc-component/dropdown
[travis-image]: https://img.shields.io/travis/react-component/dropdown/master?style=flat-square
[travis-url]: https://travis-ci.com/react-component/dropdown
[github-actions-image]: https://github.com/react-component/dropdown/actions/workflows/ci.yml/badge.svg
[github-actions-url]: https://github.com/react-component/dropdown/actions/workflows/ci.yml
[codecov-image]: https://img.shields.io/codecov/c/github/react-component/dropdown/master.svg?style=flat-square
[codecov-url]: https://app.codecov.io/gh/react-component/dropdown
[david-url]: https://david-dm.org/react-component/dropdown
[david-image]: https://david-dm.org/react-component/dropdown/status.svg?style=flat-square
[david-dev-url]: https://david-dm.org/react-component/dropdown?type=dev
[david-dev-image]: https://david-dm.org/react-component/dropdown/dev-status.svg?style=flat-square
[download-image]: https://img.shields.io/npm/dm/@rc-component/dropdown.svg?style=flat-square
[download-url]: https://npmjs.org/package/@rc-component/dropdown
[bundlephobia-url]: https://bundlephobia.com/package/@rc-component/dropdown
[bundlephobia-image]: https://badgen.net/bundlephobia/minzip/@rc-component/dropdown
[dumi-url]: https://github.com/umijs/dumi
[dumi-image]: https://img.shields.io/badge/docs%20by-dumi-blue?style=flat-square

## Screenshot

![](https://t.alipayobjects.com/images/rmsweb/T1bWpgXgBaXXXXXXXX.png)

## Example

online example: http://react-component.github.io/dropdown/examples/

## install

[![@rc-component/dropdown](https://nodei.co/npm/@rc-component/dropdown.png)](https://npmjs.org/package/@rc-component/dropdown)

## Usage

```js
var Dropdown = require('@rc-component/dropdown');
// use dropdown
```

## API

### props

<table class="table table-bordered table-striped">
    <thead>
    <tr>
        <th style="width: 100px;">name</th>
        <th style="width: 50px;">type</th>
        <th style="width: 50px;">default</th>
        <th>description</th>
    </tr>
    </thead>
    <tbody>
        <tr>
          <td>overlayClassName</td>
          <td>String</td>
          <td></td>
          <td>additional css class of root dom node</td>
        </tr>
        <tr>
          <td>openClassName</td>
          <td>String</td>
          <td>`${prefixCls}-open`</td>
          <td>className of trigger when dropdown is opened</td>
        </tr>
        <tr>
          <td>prefixCls</td>
          <td>String</td>
          <td>rc-dropdown</td>
          <td>prefix class name</td>
        </tr>
        <tr>
          <td>transitionName</td>
          <td>String</td>
          <td></td>
          <td>dropdown menu's animation css class name</td>
        </tr>
        <tr>
          <td>animation</td>
          <td>String</td>
          <td></td>
          <td>part of dropdown menu's animation css class name</td>
        </tr>
        <tr>
          <td>placement</td>
          <td>String</td>
          <td>bottomLeft</td>
          <td>Position of menu item. There are: top, topCenter, topRight, bottomLeft, bottom, bottomRight </td>
        </tr>
        <tr>
          <td>onVisibleChange</td>
          <td>Function</td>
          <td></td>
          <td>call when visible is changed</td>
        </tr>
        <tr>
          <td>visible</td>
          <td>boolean</td>
          <td></td>
          <td>whether tooltip is visible</td>
        </tr>
        <tr>
          <td>defaultVisible</td>
          <td>boolean</td>
          <td></td>
          <td>whether tooltip is visible initially</td>
        </tr>
        <tr>
          <td>overlay</td>
          <td>rc-menu</td>
          <td></td>
          <td><a href="https://github.com/react-component/menu">rc-menu</a> element</td>
        </tr>
        <tr>
          <td>onOverlayClick</td>
          <td>function(e)</td>
          <td></td>
          <td>call when overlay is clicked</td>
        </tr>
        <tr>
          <td>minOverlayWidthMatchTrigger</td>
          <td>boolean</td>
          <td>true (false when set alignPoint)</td>
          <td>whether overlay's width must not be less than trigger's </td>
        </tr>
        <tr>
          <td>getPopupContainer</td>
          <td>Function(menuDOMNode): HTMLElement</td>
          <th>() => document.body</th>
          <td>Where to render the DOM node of dropdown</td>
        </tr>
    </tbody>
</table>

Note: Additional props are passed into the underlying [rc-trigger](https://github.com/react-component/trigger) component. This can be useful for example, to display the dropdown in a separate [portal](https://reactjs.org/docs/portals.html)-driven window via the `getDocument()` rc-trigger prop.

## Development

```bash
npm install
npm start
```

## Test Case

```bash
npm test
npm run chrome-test
```

## Coverage

```bash
npm run coverage
```

open coverage/ dir

## License

@rc-component/dropdown is released under the MIT license.


================================================
FILE: assets/index.less
================================================
@dropdownPrefixCls: rc-dropdown;

@dropdown-arrow-width: 8px;
@dropdown-distance: @dropdown-arrow-width + 4;
@dropdown-arrow-color: #373737;
@dropdown-overlay-shadow: 0 1px 5px #ccc;

@font-face {
  font-family: 'anticon';
  src: url('//at.alicdn.com/t/font_1434092639_4910953.eot');
  /* IE9*/
  src: url('//at.alicdn.com/t/font_1434092639_4910953.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('//at.alicdn.com/t/font_1434092639_4910953.woff') format('woff'), /* chrome、firefox */ url('//at.alicdn.com/t/font_1434092639_4910953.ttf') format('truetype'), /* chrome、firefox、opera、Safari, Android, iOS 4.2+*/ url('//at.alicdn.com/t/font_1434092639_4910953.svg#iconfont') format('svg');
  /* iOS 4.1- */
}

.@{dropdownPrefixCls} {
  position: absolute;
  left: -9999px;
  top: -9999px;
  z-index: 1070;
  display: block;
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-size: 12px;
  font-weight: normal;
  line-height: 1.5;

  &-hidden {
    display: none;
  }

  .rc-menu {
    outline: none;
    position: relative;
    list-style-type: none;
    padding: 0;
    margin: 2px 0 2px;
    text-align: left;
    background-color: #fff;
    border-radius: 3px;
    box-shadow: @dropdown-overlay-shadow;
    background-clip: padding-box;
    border: 1px solid #ccc;

    > li {
      margin: 0;
      padding: 0;
    }

    &:before {
      content: "";
      position: absolute;
      top: -4px;
      left: 0;
      width: 100%;
      height: 4px;
      background: rgb(255, 255, 255);
      background: rgba(255, 255, 255, 0.01);
    }

    & > &-item {
      position: relative;
      display: block;
      padding: 7px 10px;
      clear: both;
      font-size: 12px;
      font-weight: normal;
      color: #666666;
      white-space: nowrap;

      &:hover, &-active, &-selected {
        background-color: #ebfaff;
      }

      &-selected {
        position: relative;
        &:after {
          content: '\e613';
          font-family: 'anticon';
          font-weight: bold;
          position: absolute;
          top: 6px;
          right: 16px;
          color: #3CB8F0;
        }
      }

      &-disabled {
        color: #ccc;
        cursor: not-allowed;
        pointer-events: none;

        &:hover {
          color: #ccc;
          background-color: #fff;
          cursor: not-allowed;
        }
      }

      &:last-child {
        border-bottom-left-radius: 3px;
        border-bottom-right-radius: 3px;
      }

      &:first-child {
        border-top-left-radius: 3px;
        border-top-right-radius: 3px;
      }

      &-divider {
        height: 1px;
        margin: 1px 0;
        overflow: hidden;
        background-color: #e5e5e5;
        line-height: 0;
      }
    }
  }

  .effect() {
    animation-duration: 0.3s;
    animation-fill-mode: both;
    transform-origin: 0 0;
    display: block !important;
  }

  &-slide-up-enter,&-slide-up-appear {
    .effect();
    opacity: 0;
    animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1);
    animation-play-state: paused;
  }

  &-slide-up-leave {
    .effect();
    opacity: 1;
    animation-timing-function: cubic-bezier(0.6, 0.04, 0.98, 0.34);
    animation-play-state: paused;
  }

  &-slide-up-enter&-slide-up-enter-active&-placement-bottomLeft,
  &-slide-up-appear&-slide-up-appear-active&-placement-bottomLeft,
  &-slide-up-enter&-slide-up-enter-active&-placement-bottomCenter,
  &-slide-up-appear&-slide-up-appear-active&-placement-bottomCenter,
  &-slide-up-enter&-slide-up-enter-active&-placement-bottomRight,
  &-slide-up-appear&-slide-up-appear-active&-placement-bottomRight {
    animation-name: rcDropdownSlideUpIn;
    animation-play-state: running;
  }

  &-slide-up-enter&-slide-up-enter-active&-placement-topLeft,
  &-slide-up-appear&-slide-up-appear-active&-placement-topLeft,
  &-slide-up-enter&-slide-up-enter-active&-placement-topCenter,
  &-slide-up-appear&-slide-up-appear-active&-placement-topCenter,
  &-slide-up-enter&-slide-up-enter-active&-placement-topRight,
  &-slide-up-appear&-slide-up-appear-active&-placement-topRight {
    animation-name: rcDropdownSlideDownIn;
    animation-play-state: running;
  }

  &-slide-up-leave&-slide-up-leave-active&-placement-bottomLeft,
  &-slide-up-leave&-slide-up-leave-active&-placement-bottomCenter,
  &-slide-up-leave&-slide-up-leave-active&-placement-bottomRight {
    animation-name: rcDropdownSlideUpOut;
    animation-play-state: running;
  }

  &-slide-up-leave&-slide-up-leave-active&-placement-topLeft,
  &-slide-up-leave&-slide-up-leave-active&-placement-topCenter,
  &-slide-up-leave&-slide-up-leave-active&-placement-topRight {
    animation-name: rcDropdownSlideDownOut;
    animation-play-state: running;
  }

  @keyframes rcDropdownSlideUpIn {
    0% {
      opacity: 0;
      transform-origin: 0% 0%;
      transform: scaleY(0);
    }
    100% {
      opacity: 1;
      transform-origin: 0% 0%;
      transform: scaleY(1);
    }
  }
  @keyframes rcDropdownSlideUpOut {
    0% {
      opacity: 1;
      transform-origin: 0% 0%;
      transform: scaleY(1);
    }
    100% {
      opacity: 0;
      transform-origin: 0% 0%;
      transform: scaleY(0);
    }
  }

  @keyframes rcDropdownSlideDownIn {
    0% {
      opacity: 0;
      transform-origin: 0% 100%;
      transform: scaleY(0);
    }
    100% {
      opacity: 1;
      transform-origin: 0% 100%;
      transform: scaleY(1);
    }
  }
  @keyframes rcDropdownSlideDownOut {
    0% {
      opacity: 1;
      transform-origin: 0% 100%;
      transform: scaleY(1);
    }
    100% {
      opacity: 0;
      transform-origin: 0% 100%;
      transform: scaleY(0);
    }
  }
}

// arrows
.@{dropdownPrefixCls}-arrow {
  position: absolute;
  border-width: @dropdown-arrow-width / 2;
  border-color: transparent;
  box-shadow: @dropdown-overlay-shadow;
  border-style: solid;
  transform: rotate(45deg);
}

.@{dropdownPrefixCls} {
  // adjust padding
  &-show-arrow&-placement-top,
  &-show-arrow&-placement-topLeft,
  &-show-arrow&-placement-topRight {
    padding-bottom: 6px;
  }

  &-show-arrow&-placement-bottom,
  &-show-arrow&-placement-bottomLeft,
  &-show-arrow&-placement-bottomRight {
    padding-top: 6px;
  }

  // top-*
  &-placement-top &-arrow,
  &-placement-topLeft &-arrow,
  &-placement-topRight &-arrow {
    bottom: @dropdown-distance - @dropdown-arrow-width;
    border-top-color: white;
  }

  &-placement-top &-arrow {
    left: 50%;
  }

  &-placement-topLeft &-arrow {
    left: 15%;
  }

  &-placement-topRight &-arrow {
    right: 15%;
  }

  // bottom-*
  &-placement-bottom &-arrow,
  &-placement-bottomLeft &-arrow,
  &-placement-bottomRight &-arrow {
    top: @dropdown-distance - @dropdown-arrow-width;
    border-bottom-color: white;
  }

  &-placement-bottom &-arrow {
    left: 50%;
  }

  &-placement-bottomLeft &-arrow {
    left: 15%;
  }

  &-placement-bottomRight &-arrow {
    right: 15%;
  }
}


================================================
FILE: docs/demo/arrow.md
================================================
---
title: arrow
nav:
  title: Demo
  path: /demo
---

<code src="../examples/arrow.jsx"></code>


================================================
FILE: docs/demo/context-menu.md
================================================
---
title: context-menu
nav:
  title: Demo
  path: /demo
---

<code src="../examples/context-menu.jsx"></code>


================================================
FILE: docs/demo/dropdown-menu-width.md
================================================
---
title: dropdown-menu-width
nav:
  title: Demo
  path: /demo
---

<code src="../examples/dropdown-menu-width.jsx"></code>


================================================
FILE: docs/demo/multiple.md
================================================
---
title: multiple
nav:
  title: Demo
  path: /demo
---

<code src="../examples/multiple.jsx"></code>


================================================
FILE: docs/demo/overlay-callback.md
================================================
---
title: overlay-callback
nav:
  title: Demo
  path: /demo
---

<code src="../examples/overlay-callback.jsx"></code>


================================================
FILE: docs/demo/simple.md
================================================
---
title: simple
nav:
  title: Demo
  path: /demo
---

<code src="../examples/simple.jsx"></code>


================================================
FILE: docs/examples/arrow.jsx
================================================
import Dropdown from '@rc-component/dropdown';
import Menu, { Divider, Item as MenuItem } from '@rc-component/menu';
import React from 'react';
import '../../assets/index.less';

function onSelect({ key }) {
  console.log(`${key} selected`);
}

function onVisibleChange(visible) {
  console.log(visible);
}

const menu = (
  <Menu onSelect={onSelect}>
    <MenuItem disabled>disabled</MenuItem>
    <MenuItem key="1">one</MenuItem>
    <Divider />
    <MenuItem key="2">two</MenuItem>
  </Menu>
);

export default function Arrow() {
  return (
    <div style={{ margin: 20 }}>
      <div style={{ height: 100 }} />
      <div>
        <Dropdown
          arrow
          trigger={['click']}
          overlay={menu}
          animation="slide-up"
          onVisibleChange={onVisibleChange}
        >
          <button style={{ width: 100 }}>open</button>
        </Dropdown>
      </div>
      <div>
        <Dropdown
          placement="topLeft"
          arrow
          trigger={['click']}
          overlay={menu}
          animation="slide-up"
          onVisibleChange={onVisibleChange}
        >
          <button style={{ width: 100 }}>open</button>
        </Dropdown>
      </div>
    </div>
  );
}


================================================
FILE: docs/examples/context-menu.jsx
================================================
import Dropdown from '@rc-component/dropdown';
import Menu, { Item as MenuItem } from '@rc-component/menu';
import React from 'react';
import '../../assets/index.less';

function ContextMenu() {
  const menu = (
    <Menu style={{ width: 140 }}>
      <MenuItem key="1">one</MenuItem>
      <MenuItem key="2">two</MenuItem>
    </Menu>
  );

  return (
    <Dropdown
      trigger={['contextMenu']}
      overlay={menu}
      animation="slide-up"
      alignPoint
    >
      <div
        role="button"
        style={{
          border: '1px solid #000',
          padding: '100px 0',
          textAlign: 'center',
        }}
      >
        Right click me!
      </div>
    </Dropdown>
  );
}

export default ContextMenu;


================================================
FILE: docs/examples/dropdown-menu-width.jsx
================================================
import Dropdown from '@rc-component/dropdown';
import Menu, { Item as MenuItem } from '@rc-component/menu';
import React, { PureComponent } from 'react';
import '../../assets/index.less';

class Example extends PureComponent {
  state = { longList: false };

  short = () => {
    this.setState({ longList: false });
  };

  long = () => {
    this.setState({ longList: true });
  };

  render() {
    const menuItems = [
      <MenuItem key="1">1st item</MenuItem>,
      <MenuItem key="2">2nd item</MenuItem>,
    ];

    if (this.state.longList) {
      menuItems.push(<MenuItem key="3">3rd LONG SUPER LONG item</MenuItem>);
    }
    const menu = <Menu>{menuItems}</Menu>;
    return (
      <div>
        <Dropdown overlay={menu}>
          <button>Actions</button>
        </Dropdown>
        <button onClick={this.long}>Long List</button>
        <button onClick={this.short}>Short List</button>
      </div>
    );
  }
}

export default Example;


================================================
FILE: docs/examples/multiple.jsx
================================================
import Dropdown from '@rc-component/dropdown';
import Menu, { Divider, Item as MenuItem } from '@rc-component/menu';
import React, { Component } from 'react';
import '../../assets/index.less';

class Test extends Component {
  state = {
    visible: false,
  };

  onVisibleChange = (visible) => {
    console.log('visible', visible);
    this.setState({
      visible,
    });
  };

  selected = [];

  saveSelected = ({ selectedKeys }) => {
    this.selected = selectedKeys;
  };

  confirm = () => {
    console.log(this.selected);
    this.setState({
      visible: false,
    });
  };

  render() {
    const menu = (
      <Menu
        style={{ width: 140 }}
        multiple
        onSelect={this.saveSelected}
        onDeselect={this.saveSelected}
      >
        <MenuItem key="1">one</MenuItem>
        <MenuItem key="2">two</MenuItem>
        <Divider />
        <MenuItem disabled>
          <button
            style={{
              cursor: 'pointer',
              color: '#000',
              pointerEvents: 'visible',
            }}
            onClick={this.confirm}
          >
            确定
          </button>
        </MenuItem>
      </Menu>
    );

    return (
      <Dropdown
        trigger={['click']}
        onVisibleChange={this.onVisibleChange}
        visible={this.state.visible}
        closeOnSelect={false}
        overlay={menu}
        animation="slide-up"
      >
        <button>open</button>
      </Dropdown>
    );
  }
}

export default Test;


================================================
FILE: docs/examples/overlay-callback.jsx
================================================
import Dropdown from '@rc-component/dropdown';
import Menu, { Divider, Item as MenuItem } from '@rc-component/menu';
import React from 'react';
import '../../assets/index.less';

function onSelect({ key }) {
  console.log(`${key} selected`);
}

function onVisibleChange(visible) {
  console.log(visible);
}

const menuCallback = () => (
  <Menu onSelect={onSelect}>
    <MenuItem disabled>disabled</MenuItem>
    <MenuItem key="1">one</MenuItem>
    <Divider />
    <MenuItem key="2">two</MenuItem>
  </Menu>
);

export default function OverlayCallback() {
  return (
    <div style={{ margin: 20 }}>
      <div style={{ height: 100 }} />
      <div>
        <Dropdown
          trigger={['click']}
          overlay={menuCallback}
          animation="slide-up"
          onVisibleChange={onVisibleChange}
        >
          <button style={{ width: 100 }}>open</button>
        </Dropdown>
      </div>
    </div>
  );
}


================================================
FILE: docs/examples/simple.jsx
================================================
/* eslint-disable no-console,react/button-has-type */
import Dropdown from '@rc-component/dropdown';
import Menu, { Divider, Item as MenuItem } from '@rc-component/menu';
import React from 'react';
import '../../assets/index.less';

function onSelect({ key }) {
  console.log(`${key} selected`);
}

function onVisibleChange(visible) {
  console.log(visible);
}

const menu = (
  <Menu onSelect={onSelect}>
    <MenuItem disabled>disabled</MenuItem>
    <MenuItem key="1">one</MenuItem>
    <Divider />
    <MenuItem key="2">two</MenuItem>
  </Menu>
);

export default function Simple() {
  return (
    <div style={{ margin: 20 }}>
      <div style={{ height: 100 }} />
      <div>
        <Dropdown
          autoFocus
          trigger={['click']}
          overlay={menu}
          animation="slide-up"
          onVisibleChange={onVisibleChange}
        >
          <button style={{ width: 100 }}>open</button>
        </Dropdown>
      </div>
    </div>
  );
}


================================================
FILE: docs/index.md
================================================
---
hero:
  title: rc-dropdown
  description: React Dropdown Component
---

<embed src="../README.md"></embed>


================================================
FILE: index.js
================================================
'use strict';

module.exports = require('./src');


================================================
FILE: now.json
================================================
{
  "version": 2,
  "name": "rc-dropdown",
  "builds": [
    {
      "src": "package.json",
      "use": "@now/static-build",
      "config": { "distDir": ".docs" }
    }
  ]
}


================================================
FILE: package.json
================================================
{
  "name": "@rc-component/dropdown",
  "version": "1.0.2",
  "description": "dropdown ui component for react",
  "keywords": [
    "react",
    "react-dropdown"
  ],
  "homepage": "http://github.com/react-component/dropdown",
  "bugs": {
    "url": "http://github.com/react-component/dropdown/issues"
  },
  "repository": {
    "type": "git",
    "url": "git@github.com:react-component/dropdown.git"
  },
  "license": "MIT",
  "maintainers": [
    "yiminghe@gmail.com",
    "hualei5280@gmail.com"
  ],
  "main": "lib/index",
  "module": "./es/index",
  "files": [
    "lib",
    "es",
    "assets/*.css"
  ],
  "scripts": {
    "build": "dumi build",
    "compile": "father build && lessc assets/index.less assets/index.css",
    "coverage": "rc-test --coverage",
    "lint": "eslint src/ docs/examples/ --ext .tsx,.ts,.jsx,.js",
    "now-build": "npm run build",
    "prepare": "husky install && dumi setup",
    "prepublishOnly": "npm run compile && rc-np",
    "start": "dumi dev",
    "test": "rc-test"
  },
  "lint-staged": {
    "**/*.{js,jsx,tsx,ts,md,json}": [
      "prettier --write",
      "git add"
    ]
  },
  "dependencies": {
    "@rc-component/trigger": "^3.0.0",
    "@rc-component/util": "^1.2.1",
    "clsx": "^2.1.1"
  },
  "devDependencies": {
    "@rc-component/father-plugin": "^2.0.2",
    "@rc-component/menu": "^1.0.0",
    "@rc-component/np": "^1.0.3",
    "@rc-component/resize-observer": "^1.0.0",
    "@testing-library/jest-dom": "^5.16.5",
    "@testing-library/react": "^14.0.0",
    "@types/jest": "^29.0.0",
    "@types/node": "^24.5.2",
    "@types/react": "^18.0.0",
    "@types/react-dom": "^18.0.0",
    "@types/warning": "^3.0.0",
    "@umijs/fabric": "^3.0.0",
    "dumi": "^2.0.0",
    "eslint": "^7.18.0",
    "father": "^4.0.0",
    "glob": "^10.0.0",
    "husky": "^8.0.3",
    "jest-environment-jsdom": "^29.5.0",
    "less": "^4.1.1",
    "lint-staged": "^13.2.1",
    "prettier": "^2.8.7",
    "rc-test": "^7.0.14",
    "react": "^18.0.0",
    "react-dom": "^18.0.0",
    "typescript": "^5.0.0"
  },
  "peerDependencies": {
    "react": ">=16.11.0",
    "react-dom": ">=16.11.0"
  }
}


================================================
FILE: script/update-content.js
================================================
/*
  用于 dumi 改造使用,
  可用于将 examples 的文件批量修改为 demo 引入形式,
  其他项目根据具体情况使用。
*/

const fs = require('fs');
const glob = require('glob');

const paths = glob.sync('./docs/examples/*.jsx');

paths.forEach((path) => {
  const name = path.split('/').pop().split('.')[0];
  fs.writeFile(
    `./docs/demo/${name}.md`,
    `---
title: ${name}
nav:
  title: Demo
  path: /demo
---

<code src="../examples/${name}.jsx"></code>
`,
    'utf8',
    function (error) {
      if (error) {
        console.log(error);
        return false;
      }
      console.log(`${name} 更新成功~`);
    },
  );
});


================================================
FILE: src/Dropdown.tsx
================================================
import type { TriggerProps, TriggerRef } from '@rc-component/trigger';
import Trigger from '@rc-component/trigger';
import type {
  ActionType,
  AlignType,
  AnimationType,
  BuildInPlacements,
} from '@rc-component/trigger/lib/interface';
import { composeRef, getNodeRef, supportRef } from '@rc-component/util/lib/ref';
import { clsx } from 'clsx';
import React from 'react';
import useAccessibility from './hooks/useAccessibility';
import Overlay from './Overlay';
import Placements from './placements';

export interface DropdownProps
  extends Pick<
    TriggerProps,
    | 'getPopupContainer'
    | 'children'
    | 'mouseEnterDelay'
    | 'mouseLeaveDelay'
    | 'onPopupAlign'
    | 'builtinPlacements'
    | 'autoDestroy'
  > {
  minOverlayWidthMatchTrigger?: boolean;
  arrow?: boolean;
  onVisibleChange?: (visible: boolean) => void;
  onOverlayClick?: (e: Event) => void;
  prefixCls?: string;
  transitionName?: string;
  overlayClassName?: string;
  openClassName?: string;
  animation?: AnimationType;
  align?: AlignType;
  overlayStyle?: React.CSSProperties;
  placement?: keyof typeof Placements;
  placements?: BuildInPlacements;
  overlay?: (() => React.ReactElement) | React.ReactElement;
  trigger?: ActionType | ActionType[];
  alignPoint?: boolean;
  showAction?: ActionType[];
  hideAction?: ActionType[];
  visible?: boolean;
  autoFocus?: boolean;
}

const Dropdown = React.forwardRef<TriggerRef, DropdownProps>((props, ref) => {
  const {
    arrow = false,
    prefixCls = 'rc-dropdown',
    transitionName,
    animation,
    align,
    placement = 'bottomLeft',
    placements = Placements,
    getPopupContainer,
    showAction,
    hideAction,
    overlayClassName,
    overlayStyle,
    visible,
    trigger = ['hover'],
    autoFocus,
    overlay,
    children,
    onVisibleChange,
    ...otherProps
  } = props;

  const [triggerVisible, setTriggerVisible] = React.useState<boolean>();
  const mergedVisible = 'visible' in props ? visible : triggerVisible;
  const mergedMotionName = animation
    ? `${prefixCls}-${animation}`
    : transitionName;

  const triggerRef = React.useRef(null);
  const overlayRef = React.useRef(null);
  const childRef = React.useRef(null);
  React.useImperativeHandle(ref, () => triggerRef.current);

  const handleVisibleChange = (newVisible: boolean) => {
    setTriggerVisible(newVisible);
    onVisibleChange?.(newVisible);
  };

  useAccessibility({
    visible: mergedVisible,
    triggerRef: childRef,
    onVisibleChange: handleVisibleChange,
    autoFocus,
    overlayRef,
  });

  const onClick = (e) => {
    const { onOverlayClick } = props;
    setTriggerVisible(false);

    if (onOverlayClick) {
      onOverlayClick(e);
    }
  };

  const getMenuElement = () => (
    <Overlay
      ref={overlayRef}
      overlay={overlay}
      prefixCls={prefixCls}
      arrow={arrow}
    />
  );

  const getMenuElementOrLambda = () => {
    if (typeof overlay === 'function') {
      return getMenuElement;
    }
    return getMenuElement();
  };

  const getMinOverlayWidthMatchTrigger = () => {
    const { minOverlayWidthMatchTrigger, alignPoint } = props;
    if ('minOverlayWidthMatchTrigger' in props) {
      return minOverlayWidthMatchTrigger;
    }

    return !alignPoint;
  };

  const getOpenClassName = () => {
    const { openClassName } = props;
    if (openClassName !== undefined) {
      return openClassName;
    }
    return `${prefixCls}-open`;
  };

  const childrenNode = React.cloneElement(children as React.ReactElement, {
    className: clsx(
      (children as React.ReactElement).props?.className,
      mergedVisible && getOpenClassName(),
    ),
    ref: supportRef(children)
      ? composeRef(childRef, getNodeRef(children as React.ReactElement))
      : undefined,
  });

  let triggerHideAction = hideAction;
  if (!triggerHideAction && trigger.indexOf('contextMenu') !== -1) {
    triggerHideAction = ['click'];
  }

  return (
    <Trigger
      builtinPlacements={placements}
      {...otherProps}
      prefixCls={prefixCls}
      ref={triggerRef}
      popupClassName={clsx(overlayClassName, {
        [`${prefixCls}-show-arrow`]: arrow,
      })}
      popupStyle={overlayStyle}
      action={trigger}
      showAction={showAction}
      hideAction={triggerHideAction}
      popupPlacement={placement}
      popupAlign={align}
      popupMotion={{ motionName: mergedMotionName }}
      popupVisible={mergedVisible}
      stretch={getMinOverlayWidthMatchTrigger() ? 'minWidth' : ''}
      popup={getMenuElementOrLambda()}
      onOpenChange={handleVisibleChange}
      onPopupClick={onClick}
      getPopupContainer={getPopupContainer}
    >
      {childrenNode}
    </Trigger>
  );
});

export default Dropdown;


================================================
FILE: src/Overlay.tsx
================================================
import { composeRef, getNodeRef, supportRef } from '@rc-component/util/lib/ref';
import React, { forwardRef, useMemo } from 'react';
import type { DropdownProps } from './Dropdown';

export type OverlayProps = Pick<
  DropdownProps,
  'overlay' | 'arrow' | 'prefixCls'
>;

const Overlay = forwardRef<HTMLElement, OverlayProps>((props, ref) => {
  const { overlay, arrow, prefixCls } = props;

  const overlayNode = useMemo(() => {
    let overlayElement: React.ReactElement;
    if (typeof overlay === 'function') {
      overlayElement = overlay();
    } else {
      overlayElement = overlay;
    }
    return overlayElement;
  }, [overlay]);

  const composedRef = composeRef(ref, getNodeRef(overlayNode));

  return (
    <>
      {arrow && <div className={`${prefixCls}-arrow`} />}
      {React.cloneElement(overlayNode, {
        ref: supportRef(overlayNode) ? composedRef : undefined,
      })}
    </>
  );
});

export default Overlay;


================================================
FILE: src/hooks/useAccessibility.ts
================================================
import KeyCode from '@rc-component/util/lib/KeyCode';
import raf from '@rc-component/util/lib/raf';
import * as React from 'react';

const { ESC, TAB } = KeyCode;

interface UseAccessibilityProps {
  visible: boolean;
  triggerRef: React.RefObject<any>;
  onVisibleChange?: (visible: boolean) => void;
  autoFocus?: boolean;
  overlayRef?: React.RefObject<any>;
}

export default function useAccessibility({
  visible,
  triggerRef,
  onVisibleChange,
  autoFocus,
  overlayRef,
}: UseAccessibilityProps) {
  const focusMenuRef = React.useRef<boolean>(false);

  const handleCloseMenuAndReturnFocus = () => {
    if (visible) {
      triggerRef.current?.focus?.();
      onVisibleChange?.(false);
    }
  };

  const focusMenu = () => {
    if (overlayRef.current?.focus) {
      overlayRef.current.focus();
      focusMenuRef.current = true;
      return true;
    }
    return false;
  };

  const handleKeyDown = (event) => {
    switch (event.keyCode) {
      case ESC:
        handleCloseMenuAndReturnFocus();
        break;
      case TAB: {
        let focusResult: boolean = false;
        if (!focusMenuRef.current) {
          focusResult = focusMenu();
        }

        if (focusResult) {
          event.preventDefault();
        } else {
          handleCloseMenuAndReturnFocus();
        }
        break;
      }
    }
  };

  React.useEffect(() => {
    if (visible) {
      window.addEventListener('keydown', handleKeyDown);
      if (autoFocus) {
        // FIXME: hack with raf
        raf(focusMenu, 3);
      }
      return () => {
        window.removeEventListener('keydown', handleKeyDown);
        focusMenuRef.current = false;
      };
    }
    return () => {
      focusMenuRef.current = false;
    };
  }, [visible]); // eslint-disable-line react-hooks/exhaustive-deps
}


================================================
FILE: src/index.tsx
================================================
export type { TriggerProps } from '@rc-component/trigger';
export type { DropdownProps } from './Dropdown';
export type { OverlayProps } from './Overlay';

import Dropdown from './Dropdown';

export default Dropdown;


================================================
FILE: src/placements.ts
================================================
const autoAdjustOverflow = {
  adjustX: 1,
  adjustY: 1,
};

const targetOffset = [0, 0];

const placements = {
  topLeft: {
    points: ['bl', 'tl'],
    overflow: autoAdjustOverflow,
    offset: [0, -4],
    targetOffset,
  },
  top: {
    points: ['bc', 'tc'],
    overflow: autoAdjustOverflow,
    offset: [0, -4],
    targetOffset,
  },
  topRight: {
    points: ['br', 'tr'],
    overflow: autoAdjustOverflow,
    offset: [0, -4],
    targetOffset,
  },
  bottomLeft: {
    points: ['tl', 'bl'],
    overflow: autoAdjustOverflow,
    offset: [0, 4],
    targetOffset,
  },
  bottom: {
    points: ['tc', 'bc'],
    overflow: autoAdjustOverflow,
    offset: [0, 4],
    targetOffset,
  },
  bottomRight: {
    points: ['tr', 'br'],
    overflow: autoAdjustOverflow,
    offset: [0, 4],
    targetOffset,
  },
};

export default placements;


================================================
FILE: tests/__mocks__/@rc-component/trigger.tsx
================================================
import Trigger from '@rc-component/trigger/lib/mock';

export default Trigger;


================================================
FILE: tests/__snapshots__/basic.test.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`dropdown simply works 1`] = `
<div>
  <button
    class="my-button rc-dropdown-open"
  >
    open
  </button>
  <div
    class="rc-dropdown rc-dropdown-placement-bottomLeft"
    style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box; min-width: 100px;"
  >
    <ul
      class="rc-menu rc-menu-root rc-menu-vertical"
      data-menu-list="true"
      role="menu"
      style="width: 140px;"
      tabindex="0"
    >
      <li
        class="rc-menu-item"
        data-menu-id="rc-menu-uuid-1"
        role="menuitem"
        tabindex="-1"
      >
        <span
          class="my-menuitem"
        >
          one
        </span>
      </li>
      <li
        class="rc-menu-item-divider"
        role="separator"
      />
      <li
        class="rc-menu-item"
        data-menu-id="rc-menu-uuid-2"
        role="menuitem"
        tabindex="-1"
      >
        two
      </li>
    </ul>
    <div
      aria-hidden="true"
      style="display: none;"
    />
  </div>
</div>
`;


================================================
FILE: tests/basic.test.tsx
================================================
/* eslint-disable react/button-has-type,react/no-find-dom-node,react/no-render-return-value,object-shorthand,func-names,max-len */
import type { MenuRef } from '@rc-component/menu';
import Menu, { Divider, Item as MenuItem } from '@rc-component/menu';
import { _rs } from '@rc-component/resize-observer';
import { spyElementPrototypes } from '@rc-component/util/lib/test/domHook';
import { act, fireEvent } from '@testing-library/react';
import type { HTMLAttributes } from 'react';
import * as React from 'react';
import { createRef, forwardRef, useImperativeHandle } from 'react';
import Dropdown from '../src';
import { render, sleep } from './utils';

async function waitForTime() {
  for (let i = 0; i < 10; i += 1) {
    await act(async () => {
      jest.runAllTimers();
    });
  }
}

async function triggerResize(target: Element) {
  act(() => {
    _rs([{ target } as ResizeObserverEntry]);
  });

  await waitForTime();
}

spyElementPrototypes(HTMLElement, {
  offsetParent: {
    get: () => document.body,
  },
  offsetLeft: {
    get: function () {
      return parseFloat(window.getComputedStyle(this).marginLeft) || 0;
    },
  },
  offsetTop: {
    get: function () {
      return parseFloat(window.getComputedStyle(this).marginTop) || 0;
    },
  },
  offsetHeight: {
    get: function () {
      return parseFloat(window.getComputedStyle(this).height) || 0;
    },
  },
  offsetWidth: {
    get: function () {
      return parseFloat(window.getComputedStyle(this).width) || 0;
    },
  },

  getBoundingClientRect: () => ({
    width: 100,
    height: 100,
  }),
});

describe('dropdown', () => {
  beforeEach(() => {
    jest.clearAllTimers();
  });

  it('default visible', () => {
    const { container } = render(
      <Dropdown overlay={<div className="check-for-visible">Test</div>} visible>
        <button className="my-button">open</button>
      </Dropdown>,
    );
    expect(container instanceof HTMLDivElement).toBeTruthy();
    expect(
      container
        .querySelector('.my-button')
        ?.classList.contains('rc-dropdown-open'),
    ).toBeTruthy();
  });

  it('supports controlled visible prop', () => {
    const onVisibleChange = jest.fn();
    const { container } = render(
      <Dropdown
        overlay={<div className="check-for-visible">Test</div>}
        visible
        trigger={['click']}
        onVisibleChange={onVisibleChange}
      >
        <button className="my-button">open</button>
      </Dropdown>,
    );
    expect(container instanceof HTMLDivElement).toBeTruthy();
    expect(
      container
        .querySelector('.my-button')
        ?.classList.contains('rc-dropdown-open'),
    ).toBeTruthy();

    fireEvent.click(container.querySelector('.my-button'));
    expect(onVisibleChange).toHaveBeenCalledWith(false);
  });

  it('simply works', async () => {
    let clicked;

    function onClick({ key }) {
      clicked = key;
    }

    const onOverlayClick = jest.fn();

    const menu = (
      <Menu style={{ width: 140 }} onClick={onClick}>
        <MenuItem key="1">
          <span className="my-menuitem">one</span>
        </MenuItem>
        <Divider />
        <MenuItem key="2">two</MenuItem>
      </Menu>
    );
    const { container, baseElement } = render(
      <Dropdown
        trigger={['click']}
        overlay={menu}
        onOverlayClick={onOverlayClick}
      >
        <button className="my-button">open</button>
      </Dropdown>,
    );
    expect(container.querySelector('.my-button')).toBeTruthy();
    // should not display until be triggered
    expect(baseElement.querySelector('.rc-dropdown')).toBeFalsy();

    fireEvent.click(container.querySelector('.my-button'));
    expect(clicked).toBeUndefined();
    expect(
      baseElement
        .querySelector('.rc-dropdown')
        .classList.contains('rc-dropdown-hidden'),
    ).toBeFalsy();
    expect(container).toMatchSnapshot();

    fireEvent.click(baseElement.querySelector('.my-menuitem'));
    expect(clicked).toBe('1');
    expect(onOverlayClick).toHaveBeenCalled();
    expect(
      baseElement
        .querySelector('.rc-dropdown')
        .classList.contains('rc-dropdown-hidden'),
    ).toBeTruthy();
  });

  it('re-align works', async () => {
    jest.useFakeTimers();

    const onPopupAlign = jest.fn();

    const buttonStyle = { width: 600, height: 20, marginLeft: 100 };
    const menu = (
      <Menu>
        <MenuItem key="1">one</MenuItem>
      </Menu>
    );
    const { container } = render(
      <Dropdown
        trigger={['click']}
        placement="bottomRight"
        overlay={menu}
        onPopupAlign={onPopupAlign}
      >
        <button className="my-btn" style={buttonStyle}>
          open
        </button>
      </Dropdown>,
    );

    expect(onPopupAlign).not.toHaveBeenCalled();

    fireEvent.click(container.querySelector('.my-btn'));
    await waitForTime();

    expect(onPopupAlign).toHaveBeenCalled();

    jest.useRealTimers();
  });

  it('Test default minOverlayWidthMatchTrigger', async () => {
    jest.useFakeTimers();

    const overlayWidth = 50;
    const overlay = <div style={{ width: overlayWidth }}>Test</div>;

    const { container, baseElement } = render(
      <Dropdown trigger={['click']} overlay={overlay} visible>
        <button style={{ width: 100 }} className="my-button">
          open
        </button>
      </Dropdown>,
    );

    await triggerResize(container.querySelector('button'));

    expect(baseElement.querySelector('.rc-dropdown')).toHaveStyle({
      minWidth: '100px',
    });

    jest.useRealTimers();
  });

  it('user pass minOverlayWidthMatchTrigger', async () => {
    jest.useFakeTimers();

    const overlayWidth = 50;
    const overlay = <div style={{ width: overlayWidth }}>Test</div>;

    const { container, baseElement } = render(
      <Dropdown
        trigger={['click']}
        overlay={overlay}
        minOverlayWidthMatchTrigger={false}
        visible
      >
        <button style={{ width: 100 }} className="my-button">
          open
        </button>
      </Dropdown>,
    );

    await triggerResize(container.querySelector('button'));

    expect(baseElement.querySelector('.rc-dropdown')).not.toHaveStyle({
      minWidth: '100px',
    });

    jest.useRealTimers();
  });

  it('should support default openClassName', () => {
    const overlay = <div style={{ width: 50 }}>Test</div>;
    const { container } = render(
      <Dropdown
        trigger={['click']}
        overlay={overlay}
        minOverlayWidthMatchTrigger={false}
      >
        <button style={{ width: 100 }} className="my-button">
          open
        </button>
      </Dropdown>,
    );
    fireEvent.click(container.querySelector('.my-button'));
    expect(
      container
        .querySelector('.my-button')
        .classList.contains('rc-dropdown-open'),
    ).toBeTruthy();
    fireEvent.click(container.querySelector('.my-button'));
    expect(
      container
        .querySelector('.my-button')
        .classList.contains('rc-dropdown-open'),
    ).toBeFalsy();
  });

  it('should support custom openClassName', async () => {
    const overlay = <div style={{ width: 50 }}>Test</div>;
    const { container } = render(
      <Dropdown
        trigger={['click']}
        overlay={overlay}
        minOverlayWidthMatchTrigger={false}
        openClassName="opened"
      >
        <button style={{ width: 100 }} className="my-button">
          open
        </button>
      </Dropdown>,
    );

    fireEvent.click(container.querySelector('.my-button'));
    expect(
      container.querySelector('.my-button').classList.contains('opened'),
    ).toBeTruthy();
    fireEvent.click(container.querySelector('.my-button'));
    expect(
      container.querySelector('.my-button').classList.contains('opened'),
    ).toBeFalsy();
  });

  it('overlay callback', async () => {
    const overlay = <div style={{ width: 50 }}>Test</div>;
    const { container, baseElement } = render(
      <Dropdown trigger={['click']} overlay={() => overlay}>
        <button className="my-button">open</button>
      </Dropdown>,
    );

    fireEvent.click(container.querySelector('.my-button'));
    expect(
      baseElement
        .querySelector('.rc-dropdown')
        .classList.contains('rc-dropdown-hidden'),
    ).toBeFalsy();
  });

  it('should support arrow', async () => {
    const overlay = <div style={{ width: 50 }}>Test</div>;
    const { container, baseElement } = render(
      <Dropdown arrow overlay={overlay} trigger={['click']}>
        <button style={{ width: 100 }} className="my-button">
          open
        </button>
      </Dropdown>,
    );

    fireEvent.click(container.querySelector('.my-button'));
    await sleep(500);
    expect(
      baseElement
        .querySelector('.rc-dropdown')
        .classList.contains('rc-dropdown-show-arrow'),
    ).toBeTruthy();
    expect(
      baseElement
        .querySelector('.rc-dropdown')
        .firstElementChild.classList.contains('rc-dropdown-arrow'),
    ).toBeTruthy();
  });

  it('Keyboard navigation works', async () => {
    jest.useFakeTimers();

    const overlay = (
      <Menu>
        <MenuItem key="1">
          <span className="my-menuitem">one</span>
        </MenuItem>
        <MenuItem key="2">two</MenuItem>
      </Menu>
    );
    const { container, baseElement } = render(
      <Dropdown trigger={['click']} overlay={overlay}>
        <button className="my-button">open</button>
      </Dropdown>,
    );
    const trigger = container.querySelector('.my-button');

    // Open menu;
    fireEvent.click(trigger);
    await waitForTime();
    expect(
      baseElement
        .querySelector('.rc-dropdown')
        .classList.contains('rc-dropdown-hidden'),
    ).toBeFalsy();

    // Close menu with Esc
    fireEvent.keyDown(window, { key: 'Esc', keyCode: 27 });
    await waitForTime();
    expect(document.activeElement.className).toContain('my-button');

    // Open menu
    fireEvent.click(trigger);
    await waitForTime();
    expect(
      baseElement
        .querySelector('.rc-dropdown')
        .classList.contains('rc-dropdown-hidden'),
    ).toBeFalsy();

    // Focus menu with Tab
    window.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 9 })); // Tab
    expect(document.activeElement.className).toContain('menu');

    // Close menu with Tab
    window.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 9 })); // Tab
    await waitForTime();
    expect(document.activeElement.className).toContain('my-button');

    jest.useRealTimers();
  });

  it('Tab should close menu if overlay cannot be focused', async () => {
    jest.useFakeTimers();

    const Overlay = () => <div>test</div>;
    const { container, baseElement } = render(
      <Dropdown trigger={['click']} overlay={<Overlay />}>
        <button className="my-button">open</button>
      </Dropdown>,
    );
    const trigger = container.querySelector('.my-button');

    // Open menu;
    fireEvent.click(trigger);
    await waitForTime();
    expect(
      baseElement
        .querySelector('.rc-dropdown')
        .classList.contains('rc-dropdown-hidden'),
    ).toBeFalsy();

    // Close menu with Esc
    fireEvent.keyDown(window, { key: 'Esc', keyCode: 27 });
    await waitForTime();
    expect(document.activeElement.className).toContain('my-button');

    // Open menu
    fireEvent.click(trigger);
    await waitForTime();
    expect(
      baseElement
        .querySelector('.rc-dropdown')
        .classList.contains('rc-dropdown-hidden'),
    ).toBeFalsy();

    // Close menu with Tab
    window.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 9 })); // Tab
    await waitForTime();
    expect(document.activeElement.className).toContain('my-button');

    jest.useRealTimers();
  });

  it('keyboard should work if menu is wrapped', async () => {
    const overlay = (
      <div>
        <Menu>
          <MenuItem key="1">
            <span className="my-menuitem">one</span>
          </MenuItem>
          <MenuItem key="2">two</MenuItem>
        </Menu>
      </div>
    );
    const { container, baseElement } = render(
      <Dropdown trigger={['click']} overlay={overlay}>
        <button className="my-button">open</button>
      </Dropdown>,
    );
    const trigger = container.querySelector('.my-button');

    // Open menu
    fireEvent.click(trigger);
    await sleep(200);
    expect(
      baseElement
        .querySelector('.rc-dropdown')
        .classList.contains('rc-dropdown-hidden'),
    ).toBeFalsy();

    // Close menu with Esc
    window.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 27 })); // Esc
    await sleep(200);
    expect(document.activeElement.className).toContain('my-button');

    // Open menu
    fireEvent.click(trigger);
    await sleep(200);
    expect(
      baseElement
        .querySelector('.rc-dropdown')
        .classList.contains('rc-dropdown-hidden'),
    ).toBeFalsy();

    // Focus menu with Tab
    window.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 9 })); // Tab

    // Close menu with Tab
    window.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 9 })); // Tab
    await sleep(200);
    expect(document.activeElement.className).toContain('my-button');
  });

  it('support Menu expandIcon', async () => {
    const props = {
      overlay: (
        <Menu expandIcon={<span id="customExpandIcon" />}>
          <Menu.Item key="1">foo</Menu.Item>
          <Menu.SubMenu title="SubMenu">
            <Menu.Item key="1">foo</Menu.Item>
          </Menu.SubMenu>
        </Menu>
      ),
      visible: true,
      getPopupContainer: (node) => node,
    };

    const { container } = render(
      <Dropdown {...props}>
        <button type="button">button</button>
      </Dropdown>,
    );
    await sleep(500);
    expect(container.querySelector('#customExpandIcon')).toBeTruthy();
  });

  it('should support customized menuRef', async () => {
    const menuRef = createRef<MenuRef>();
    const props = {
      overlay: (
        <Menu ref={menuRef}>
          <Menu.Item key="1">foo</Menu.Item>
        </Menu>
      ),
      visible: true,
    };

    render(
      <Dropdown {...props}>
        <button type="button">button</button>
      </Dropdown>,
    );

    await sleep(500);
    expect(menuRef.current).toBeTruthy();
  });

  it('should support trigger when child provide nativeElement', async () => {
    jest.useFakeTimers();
    const Button = forwardRef<any, HTMLAttributes<HTMLButtonElement>>(
      (props, ref) => {
        const btnRef = createRef<HTMLButtonElement>();
        useImperativeHandle(ref, () => ({
          foo: () => {},
          nativeElement: btnRef.current,
        }));
        return (
          <button
            ref={btnRef}
            onClick={(e) => {
              props?.onClick?.(e);
            }}
          >
            trigger
          </button>
        );
      },
    );
    const { container, baseElement } = render(
      <Dropdown
        trigger={['click']}
        getPopupContainer={(node) => node}
        overlay={
          <Menu>
            <Menu.Item key="1">foo</Menu.Item>
          </Menu>
        }
      >
        <Button />
      </Dropdown>,
    );
    fireEvent.click(container.querySelector('button'));
    fireEvent.click(baseElement.querySelectorAll('li')[0]);

    jest.runAllTimers();
    jest.useRealTimers();
  });

  it('should support autoFocus', async () => {
    jest.useFakeTimers();

    const overlay = (
      <Menu>
        <MenuItem key="1">
          <span className="my-menuitem">one</span>
        </MenuItem>
        <MenuItem key="2">two</MenuItem>
      </Menu>
    );
    const { container } = render(
      <Dropdown autoFocus trigger={['click']} overlay={overlay}>
        <button className="my-button">open</button>
      </Dropdown>,
    );
    const trigger = container.querySelector('.my-button');

    // Open menu
    fireEvent.click(trigger);

    await waitForTime();

    expect(
      container
        .querySelector('.rc-dropdown')
        .classList.contains('rc-dropdown-hidden'),
    ).toBeFalsy();
    expect(document.activeElement.className).toContain('menu');

    // Close menu with Tab
    window.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 9 })); // Tab

    await waitForTime();

    expect(document.activeElement.className).toContain('my-button');

    jest.useRealTimers();
  });

  it('children cannot be given ref should not throw', () => {
    const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
    const Component = () => <div>test</div>;

    render(
      <Dropdown overlay={<div>test</div>}>
        <Component />
      </Dropdown>,
    );
    expect(errorSpy).not.toHaveBeenCalledWith(
      expect.stringContaining(
        'Warning: Function components cannot be given refs',
      ),
      expect.anything(),
      expect.anything(),
    );
  });
});


================================================
FILE: tests/point.test.tsx
================================================
/* eslint-disable react/button-has-type,react/no-render-return-value */
import { act, fireEvent } from '@testing-library/react';
import * as React from 'react';
import Dropdown from '../src';
import { render } from './utils';

// Fix prettier rm this
console.log(!!React);

async function waitForTime() {
  for (let i = 0; i < 10; i += 1) {
    await act(async () => {
      jest.runAllTimers();
    });
  }
}

describe('point', () => {
  beforeEach(() => {
    jest.useFakeTimers();
  });

  afterEach(() => {
    jest.clearAllTimers();
    jest.useRealTimers();
  });

  it('click show', async () => {
    const overlay = (
      <div
        className="check-for-visible"
        style={{
          width: 10,
        }}
      >
        Test
      </div>
    );

    const onPopupAlign = jest.fn();

    const { container } = render(
      <Dropdown
        onPopupAlign={onPopupAlign}
        trigger={['contextMenu']}
        overlay={overlay}
        alignPoint
        align={{
          points: ['tl'],
          overflow: {},
        }}
      >
        <button className="my-button">open</button>
      </Dropdown>,
    );

    fireEvent.contextMenu(container.querySelector('.my-button'));
    await waitForTime();

    expect(container.querySelector('.rc-dropdown')).toBeTruthy();
  });
});


================================================
FILE: tests/utils.js
================================================
import { StrictMode } from 'react';
import { render, act } from '@testing-library/react';

const globalTimeout = global.setTimeout;

export async function sleep(timeout = 0) {
  await act(async () => {
    await new Promise((resolve) => {
      globalTimeout(resolve, timeout);
    });
  });
}

function customRender(ui, options) {
  return render(ui, { wrapper: StrictMode, ...options });
}

export { customRender as render };


================================================
FILE: tsconfig.json
================================================
{
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "baseUrl": "./",
    "declaration": true,
    "module": "esnext",
    "target": "esnext",
    "moduleResolution": "node",
    "jsx": "react",
    "skipLibCheck": true,
    "paths": {
      "@@/*": [".dumi/tmp/*"]
    }
  },
  "include": ["./src", "./tests", "./typings/"],
  "typings": "./typings/index.d.ts",
  "exclude": [
    "node_modules",
    "build",
    "scripts",
    "acceptance-tests",
    "webpack",
    "jest",
    "src/setupTests.ts",
    "tslint:latest",
    "tslint-config-prettier"
  ]
}
Download .txt
gitextract_t_l0mx02/

├── .dumirc.ts
├── .editorconfig
├── .eslintrc.js
├── .fatherrc.ts
├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       ├── ci.yml
│       └── codeql.yml
├── .gitignore
├── .husky/
│   └── pre-commit
├── .prettierrc
├── .travis.yml
├── HISTORY.md
├── LICENSE
├── README.md
├── assets/
│   └── index.less
├── docs/
│   ├── demo/
│   │   ├── arrow.md
│   │   ├── context-menu.md
│   │   ├── dropdown-menu-width.md
│   │   ├── multiple.md
│   │   ├── overlay-callback.md
│   │   └── simple.md
│   ├── examples/
│   │   ├── arrow.jsx
│   │   ├── context-menu.jsx
│   │   ├── dropdown-menu-width.jsx
│   │   ├── multiple.jsx
│   │   ├── overlay-callback.jsx
│   │   └── simple.jsx
│   └── index.md
├── index.js
├── now.json
├── package.json
├── script/
│   └── update-content.js
├── src/
│   ├── Dropdown.tsx
│   ├── Overlay.tsx
│   ├── hooks/
│   │   └── useAccessibility.ts
│   ├── index.tsx
│   └── placements.ts
├── tests/
│   ├── __mocks__/
│   │   └── @rc-component/
│   │       └── trigger.tsx
│   ├── __snapshots__/
│   │   └── basic.test.tsx.snap
│   ├── basic.test.tsx
│   ├── point.test.tsx
│   └── utils.js
└── tsconfig.json
Download .txt
SYMBOL INDEX (24 symbols across 12 files)

FILE: docs/examples/arrow.jsx
  function onSelect (line 6) | function onSelect({ key }) {
  function onVisibleChange (line 10) | function onVisibleChange(visible) {
  function Arrow (line 23) | function Arrow() {

FILE: docs/examples/context-menu.jsx
  function ContextMenu (line 6) | function ContextMenu() {

FILE: docs/examples/dropdown-menu-width.jsx
  class Example (line 6) | class Example extends PureComponent {
    method render (line 17) | render() {

FILE: docs/examples/multiple.jsx
  class Test (line 6) | class Test extends Component {
    method render (line 31) | render() {

FILE: docs/examples/overlay-callback.jsx
  function onSelect (line 6) | function onSelect({ key }) {
  function onVisibleChange (line 10) | function onVisibleChange(visible) {
  function OverlayCallback (line 23) | function OverlayCallback() {

FILE: docs/examples/simple.jsx
  function onSelect (line 7) | function onSelect({ key }) {
  function onVisibleChange (line 11) | function onVisibleChange(visible) {
  function Simple (line 24) | function Simple() {

FILE: src/Dropdown.tsx
  type DropdownProps (line 16) | interface DropdownProps

FILE: src/Overlay.tsx
  type OverlayProps (line 5) | type OverlayProps = Pick<

FILE: src/hooks/useAccessibility.ts
  type UseAccessibilityProps (line 7) | interface UseAccessibilityProps {
  function useAccessibility (line 15) | function useAccessibility({

FILE: tests/basic.test.tsx
  function waitForTime (line 13) | async function waitForTime() {
  function triggerResize (line 21) | async function triggerResize(target: Element) {
  function onClick (line 105) | function onClick({ key }) {

FILE: tests/point.test.tsx
  function waitForTime (line 10) | async function waitForTime() {

FILE: tests/utils.js
  function sleep (line 6) | async function sleep(timeout = 0) {
  function customRender (line 14) | function customRender(ui, options) {
Condensed preview — 43 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (61K chars).
[
  {
    "path": ".dumirc.ts",
    "chars": 455,
    "preview": "// more config: https://d.umijs.org/config\nimport { defineConfig } from 'dumi';\n\nexport default defineConfig({\n  favicon"
  },
  {
    "path": ".editorconfig",
    "chars": 192,
    "preview": "# top-most EditorConfig file\nroot = true\n\n# Unix-style newlines with a newline ending every file\n[*.{js,css}]\nend_of_lin"
  },
  {
    "path": ".eslintrc.js",
    "chars": 403,
    "preview": "const base = require('@umijs/fabric/dist/eslint');\n\nmodule.exports = {\n  ...base,\n  rules: {\n    ...base.rules,\n    'no-"
  },
  {
    "path": ".fatherrc.ts",
    "chars": 118,
    "preview": "import { defineConfig } from \"father\";\n\nexport default defineConfig({\n  plugins: [\"@rc-component/father-plugin\"],\n});\n"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 522,
    "preview": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where "
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 139,
    "preview": "name: CI\n\non: ['push', 'pull_request']\n\njobs:\n  CI:\n    uses: react-component/rc-test/.github/workflows/test.yml@main\n  "
  },
  {
    "path": ".github/workflows/codeql.yml",
    "chars": 841,
    "preview": "name: \"CodeQL\"\n\non:\n  push:\n    branches: [ \"master\" ]\n  pull_request:\n    branches: [ \"master\" ]\n  schedule:\n    - cron"
  },
  {
    "path": ".gitignore",
    "chars": 303,
    "preview": "*.iml\n*.log\n.idea/\n.ipr\n.iws\n*~\n~*\n*.diff\n*.patch\n*.bak\n.DS_Store\nThumbs.db\n.project\n.*proj\n.svn/\n*.swp\n*.swo\n*.pyc\n*.py"
  },
  {
    "path": ".husky/pre-commit",
    "chars": 69,
    "preview": "#!/usr/bin/env sh\n. \"$(dirname -- \"$0\")/_/husky.sh\"\n\nnpx lint-staged\n"
  },
  {
    "path": ".prettierrc",
    "chars": 130,
    "preview": "{\n  \"endOfLine\": \"lf\",\n  \"semi\": true,\n  \"singleQuote\": true,\n  \"tabWidth\": 2,\n  \"trailingComma\": \"all\",\n  \"proseWrap\": "
  },
  {
    "path": ".travis.yml",
    "chars": 259,
    "preview": "language: node_js\n\nnode_js:\n  - 10\n\nscript:\n  - |\n    if [ \"$TEST_TYPE\" = test ]; then\n      npm run coverage && \\\n     "
  },
  {
    "path": "HISTORY.md",
    "chars": 503,
    "preview": "# History\n----\n\n## 2.4.0 / 2018-12-28\n\n- `overlay` support function render\n\n## 2.3.0 / 2018-12-21\n\n- add `openClassName`"
  },
  {
    "path": "LICENSE",
    "chars": 1115,
    "preview": "The MIT License (MIT)\nCopyright (c) 2015-present Alipay.com, https://www.alipay.com/\n\nPermission is hereby granted, free"
  },
  {
    "path": "README.md",
    "chars": 5335,
    "preview": "# @rc-component/dropdown\n\nreact dropdown component.\n\n[![NPM version][npm-image]][npm-url] [![npm download][download-imag"
  },
  {
    "path": "assets/index.less",
    "chars": 6897,
    "preview": "@dropdownPrefixCls: rc-dropdown;\n\n@dropdown-arrow-width: 8px;\n@dropdown-distance: @dropdown-arrow-width + 4;\n@dropdown-a"
  },
  {
    "path": "docs/demo/arrow.md",
    "chars": 97,
    "preview": "---\ntitle: arrow\nnav:\n  title: Demo\n  path: /demo\n---\n\n<code src=\"../examples/arrow.jsx\"></code>\n"
  },
  {
    "path": "docs/demo/context-menu.md",
    "chars": 111,
    "preview": "---\ntitle: context-menu\nnav:\n  title: Demo\n  path: /demo\n---\n\n<code src=\"../examples/context-menu.jsx\"></code>\n"
  },
  {
    "path": "docs/demo/dropdown-menu-width.md",
    "chars": 125,
    "preview": "---\ntitle: dropdown-menu-width\nnav:\n  title: Demo\n  path: /demo\n---\n\n<code src=\"../examples/dropdown-menu-width.jsx\"></c"
  },
  {
    "path": "docs/demo/multiple.md",
    "chars": 103,
    "preview": "---\ntitle: multiple\nnav:\n  title: Demo\n  path: /demo\n---\n\n<code src=\"../examples/multiple.jsx\"></code>\n"
  },
  {
    "path": "docs/demo/overlay-callback.md",
    "chars": 119,
    "preview": "---\ntitle: overlay-callback\nnav:\n  title: Demo\n  path: /demo\n---\n\n<code src=\"../examples/overlay-callback.jsx\"></code>\n"
  },
  {
    "path": "docs/demo/simple.md",
    "chars": 99,
    "preview": "---\ntitle: simple\nnav:\n  title: Demo\n  path: /demo\n---\n\n<code src=\"../examples/simple.jsx\"></code>\n"
  },
  {
    "path": "docs/examples/arrow.jsx",
    "chars": 1211,
    "preview": "import Dropdown from '@rc-component/dropdown';\nimport Menu, { Divider, Item as MenuItem } from '@rc-component/menu';\nimp"
  },
  {
    "path": "docs/examples/context-menu.jsx",
    "chars": 725,
    "preview": "import Dropdown from '@rc-component/dropdown';\nimport Menu, { Item as MenuItem } from '@rc-component/menu';\nimport React"
  },
  {
    "path": "docs/examples/dropdown-menu-width.jsx",
    "chars": 954,
    "preview": "import Dropdown from '@rc-component/dropdown';\nimport Menu, { Item as MenuItem } from '@rc-component/menu';\nimport React"
  },
  {
    "path": "docs/examples/multiple.jsx",
    "chars": 1491,
    "preview": "import Dropdown from '@rc-component/dropdown';\nimport Menu, { Divider, Item as MenuItem } from '@rc-component/menu';\nimp"
  },
  {
    "path": "docs/examples/overlay-callback.jsx",
    "chars": 923,
    "preview": "import Dropdown from '@rc-component/dropdown';\nimport Menu, { Divider, Item as MenuItem } from '@rc-component/menu';\nimp"
  },
  {
    "path": "docs/examples/simple.jsx",
    "chars": 966,
    "preview": "/* eslint-disable no-console,react/button-has-type */\nimport Dropdown from '@rc-component/dropdown';\nimport Menu, { Divi"
  },
  {
    "path": "docs/index.md",
    "chars": 111,
    "preview": "---\nhero:\n  title: rc-dropdown\n  description: React Dropdown Component\n---\n\n<embed src=\"../README.md\"></embed>\n"
  },
  {
    "path": "index.js",
    "chars": 50,
    "preview": "'use strict';\n\nmodule.exports = require('./src');\n"
  },
  {
    "path": "now.json",
    "chars": 177,
    "preview": "{\n  \"version\": 2,\n  \"name\": \"rc-dropdown\",\n  \"builds\": [\n    {\n      \"src\": \"package.json\",\n      \"use\": \"@now/static-bu"
  },
  {
    "path": "package.json",
    "chars": 2134,
    "preview": "{\n  \"name\": \"@rc-component/dropdown\",\n  \"version\": \"1.0.2\",\n  \"description\": \"dropdown ui component for react\",\n  \"keywo"
  },
  {
    "path": "script/update-content.js",
    "chars": 580,
    "preview": "/*\n  用于 dumi 改造使用,\n  可用于将 examples 的文件批量修改为 demo 引入形式,\n  其他项目根据具体情况使用。\n*/\n\nconst fs = require('fs');\nconst glob = requir"
  },
  {
    "path": "src/Dropdown.tsx",
    "chars": 4727,
    "preview": "import type { TriggerProps, TriggerRef } from '@rc-component/trigger';\nimport Trigger from '@rc-component/trigger';\nimpo"
  },
  {
    "path": "src/Overlay.tsx",
    "chars": 944,
    "preview": "import { composeRef, getNodeRef, supportRef } from '@rc-component/util/lib/ref';\nimport React, { forwardRef, useMemo } f"
  },
  {
    "path": "src/hooks/useAccessibility.ts",
    "chars": 1801,
    "preview": "import KeyCode from '@rc-component/util/lib/KeyCode';\nimport raf from '@rc-component/util/lib/raf';\nimport * as React fr"
  },
  {
    "path": "src/index.tsx",
    "chars": 217,
    "preview": "export type { TriggerProps } from '@rc-component/trigger';\nexport type { DropdownProps } from './Dropdown';\nexport type "
  },
  {
    "path": "src/placements.ts",
    "chars": 845,
    "preview": "const autoAdjustOverflow = {\n  adjustX: 1,\n  adjustY: 1,\n};\n\nconst targetOffset = [0, 0];\n\nconst placements = {\n  topLef"
  },
  {
    "path": "tests/__mocks__/@rc-component/trigger.tsx",
    "chars": 79,
    "preview": "import Trigger from '@rc-component/trigger/lib/mock';\n\nexport default Trigger;\n"
  },
  {
    "path": "tests/__snapshots__/basic.test.tsx.snap",
    "chars": 1067,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`dropdown simply works 1`] = `\n<div>\n  <button\n    class=\"my-button "
  },
  {
    "path": "tests/basic.test.tsx",
    "chars": 16902,
    "preview": "/* eslint-disable react/button-has-type,react/no-find-dom-node,react/no-render-return-value,object-shorthand,func-names,"
  },
  {
    "path": "tests/point.test.tsx",
    "chars": 1301,
    "preview": "/* eslint-disable react/button-has-type,react/no-render-return-value */\nimport { act, fireEvent } from '@testing-library"
  },
  {
    "path": "tests/utils.js",
    "chars": 428,
    "preview": "import { StrictMode } from 'react';\nimport { render, act } from '@testing-library/react';\n\nconst globalTimeout = global."
  },
  {
    "path": "tsconfig.json",
    "chars": 576,
    "preview": "{\n  \"compilerOptions\": {\n    \"allowSyntheticDefaultImports\": true,\n    \"baseUrl\": \"./\",\n    \"declaration\": true,\n    \"mo"
  }
]

About this extraction

This page contains the full source code of the react-component/dropdown GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 43 files (54.8 KB), approximately 15.7k tokens, and a symbol index with 24 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.

Copied to clipboard!