├── .editorconfig ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── actions │ ├── functional │ │ └── action.yml │ ├── setup │ │ └── action.yml │ ├── type-check │ │ └── action.yml │ └── unit │ │ └── action.yml └── workflows │ ├── ci.yml │ └── update-snapshots.yml ├── .gitignore ├── .vscode └── extensions.json ├── LICENSE.md ├── README.md ├── eslint.config.js ├── examples ├── example-react-ts │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── src │ │ ├── App.tsx │ │ ├── components │ │ │ ├── Block.tsx │ │ │ ├── HighOrderFloat.tsx │ │ │ └── HoverMenu.tsx │ │ ├── main.tsx │ │ ├── pages │ │ │ ├── adaptive-width.tsx │ │ │ ├── arrow.tsx │ │ │ ├── combobox.tsx │ │ │ ├── dialog.tsx │ │ │ ├── floatingui-options.tsx │ │ │ ├── high-order-component.tsx │ │ │ ├── listbox.tsx │ │ │ ├── menu.tsx │ │ │ ├── popover.tsx │ │ │ ├── portal.tsx │ │ │ ├── static.tsx │ │ │ ├── transition.tsx │ │ │ └── virtual-element.tsx │ │ ├── router.ts │ │ └── style.css │ ├── tailwind.config.ts │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── example-react │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── jsconfig.json │ ├── package.json │ ├── postcss.config.js │ ├── src │ │ ├── App.jsx │ │ ├── components │ │ │ ├── Block.jsx │ │ │ ├── HighOrderFloat.jsx │ │ │ └── HoverMenu.jsx │ │ ├── main.jsx │ │ ├── pages │ │ │ ├── adaptive-width.jsx │ │ │ ├── arrow.jsx │ │ │ ├── combobox.jsx │ │ │ ├── dialog.jsx │ │ │ ├── floatingui-options.jsx │ │ │ ├── high-order-component.jsx │ │ │ ├── listbox.jsx │ │ │ ├── menu.jsx │ │ │ ├── popover.jsx │ │ │ ├── portal.jsx │ │ │ ├── static.jsx │ │ │ ├── transition.jsx │ │ │ └── virtual-element.jsx │ │ ├── router.js │ │ └── style.css │ ├── tailwind.config.js │ └── vite.config.js ├── example-vue-ts │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── src │ │ ├── App.vue │ │ ├── components │ │ │ ├── Block.vue │ │ │ ├── HighOrderFloat.ts │ │ │ └── HoverMenu.vue │ │ ├── main.ts │ │ ├── pages │ │ │ ├── adaptive-width.vue │ │ │ ├── arrow.vue │ │ │ ├── combobox.vue │ │ │ ├── dialog.vue │ │ │ ├── floatingui-options.vue │ │ │ ├── high-order-component.vue │ │ │ ├── listbox.vue │ │ │ ├── menu.vue │ │ │ ├── popover.vue │ │ │ ├── portal.vue │ │ │ ├── static.vue │ │ │ ├── transition.vue │ │ │ └── virtual-element.vue │ │ ├── router.ts │ │ └── style.css │ ├── tailwind.config.ts │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts └── example-vue │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── jsconfig.json │ ├── package.json │ ├── postcss.config.js │ ├── src │ ├── App.vue │ ├── components │ │ ├── Block.vue │ │ ├── HighOrderFloat.vue │ │ └── HoverMenu.vue │ ├── main.js │ ├── pages │ │ ├── adaptive-width.vue │ │ ├── arrow.vue │ │ ├── combobox.vue │ │ ├── dialog.vue │ │ ├── floatingui-options.vue │ │ ├── high-order-component.vue │ │ ├── listbox.vue │ │ ├── menu.vue │ │ ├── popover.vue │ │ ├── portal.vue │ │ ├── static.vue │ │ ├── transition.vue │ │ └── virtual-element.vue │ ├── router.js │ └── style.css │ ├── tailwind.config.js │ └── vite.config.js ├── package.json ├── packages ├── nuxt │ ├── .gitignore │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── playground │ │ ├── app.vue │ │ ├── components │ │ │ ├── Block.vue │ │ │ └── HighOrderFloat.ts │ │ ├── nuxt.config.ts │ │ ├── pages │ │ │ ├── arrow.vue │ │ │ ├── combobox.vue │ │ │ ├── floatingui-options.vue │ │ │ ├── high-order-component.vue │ │ │ ├── listbox.vue │ │ │ ├── menu.vue │ │ │ ├── popover.vue │ │ │ ├── portal.vue │ │ │ ├── transition.vue │ │ │ └── virtual-element.vue │ │ ├── routes.ts │ │ ├── tailwind.config.ts │ │ └── tsconfig.json │ ├── src │ │ └── module.ts │ └── tsconfig.json ├── react │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── playwright.config.ts │ ├── scripts │ │ └── build.sh │ ├── src │ │ ├── class-resolvers │ │ │ ├── index.ts │ │ │ ├── tailwindcss-rtl.ts │ │ │ ├── tailwindcss.ts │ │ │ └── type.ts │ │ ├── float.tsx │ │ ├── hooks │ │ │ ├── use-document-event.ts │ │ │ ├── use-floating-middleware-from-props.ts │ │ │ ├── use-id.ts │ │ │ ├── use-iso-morphic-effect.ts │ │ │ ├── use-latest-value.ts │ │ │ ├── use-origin-class.ts │ │ │ ├── use-outside-click.ts │ │ │ ├── use-reference-el-resize-observer.ts │ │ │ └── use-server-handoff-complete.ts │ │ ├── index.ts │ │ └── utils │ │ │ ├── disposables.ts │ │ │ ├── dpr.ts │ │ │ ├── env.ts │ │ │ ├── focus-management.ts │ │ │ ├── match.ts │ │ │ ├── micro-task.ts │ │ │ └── owner.ts │ ├── test │ │ ├── functional │ │ │ ├── components.test.ts │ │ │ ├── components.test.ts-snapshots │ │ │ │ ├── combobox-linux.png │ │ │ │ ├── combobox-win32.png │ │ │ │ ├── dialog-close-linux.png │ │ │ │ ├── dialog-close-win32.png │ │ │ │ ├── dialog-open-linux.png │ │ │ │ ├── dialog-open-win32.png │ │ │ │ ├── listbox-linux.png │ │ │ │ ├── listbox-win32.png │ │ │ │ ├── menu-linux.png │ │ │ │ ├── menu-win32.png │ │ │ │ ├── popover-linux.png │ │ │ │ └── popover-win32.png │ │ │ ├── floatingui-options.test.ts │ │ │ ├── floatingui-options.test.ts-snapshots │ │ │ │ ├── flip-scrollto-160-linux.png │ │ │ │ ├── flip-scrollto-160-win32.png │ │ │ │ ├── flip-scrollto-250-linux.png │ │ │ │ ├── flip-scrollto-250-win32.png │ │ │ │ ├── flip-scrollto-482-linux.png │ │ │ │ ├── flip-scrollto-482-win32.png │ │ │ │ ├── hide-scrollto-0-linux.png │ │ │ │ ├── hide-scrollto-0-win32.png │ │ │ │ ├── hide-scrollto-220-linux.png │ │ │ │ ├── hide-scrollto-220-win32.png │ │ │ │ ├── hide-scrollto-400-linux.png │ │ │ │ ├── hide-scrollto-400-win32.png │ │ │ │ ├── hide-scrollto-50-linux.png │ │ │ │ ├── hide-scrollto-50-win32.png │ │ │ │ ├── offset-0-linux.png │ │ │ │ ├── offset-0-win32.png │ │ │ │ ├── offset-100-linux.png │ │ │ │ ├── offset-100-win32.png │ │ │ │ ├── offset-4-linux.png │ │ │ │ ├── offset-4-win32.png │ │ │ │ ├── placement-bottom-end-linux.png │ │ │ │ ├── placement-bottom-end-win32.png │ │ │ │ ├── placement-bottom-linux.png │ │ │ │ ├── placement-bottom-start-linux.png │ │ │ │ ├── placement-bottom-start-win32.png │ │ │ │ ├── placement-bottom-win32.png │ │ │ │ ├── placement-left-end-linux.png │ │ │ │ ├── placement-left-end-win32.png │ │ │ │ ├── placement-left-linux.png │ │ │ │ ├── placement-left-start-linux.png │ │ │ │ ├── placement-left-start-win32.png │ │ │ │ ├── placement-left-win32.png │ │ │ │ ├── placement-right-end-linux.png │ │ │ │ ├── placement-right-end-win32.png │ │ │ │ ├── placement-right-linux.png │ │ │ │ ├── placement-right-start-linux.png │ │ │ │ ├── placement-right-start-win32.png │ │ │ │ ├── placement-right-win32.png │ │ │ │ ├── placement-top-end-linux.png │ │ │ │ ├── placement-top-end-win32.png │ │ │ │ ├── placement-top-linux.png │ │ │ │ ├── placement-top-start-linux.png │ │ │ │ ├── placement-top-start-win32.png │ │ │ │ ├── placement-top-win32.png │ │ │ │ ├── shift-scrollto-240-linux.png │ │ │ │ ├── shift-scrollto-240-win32.png │ │ │ │ ├── shift-scrollto-482-linux.png │ │ │ │ ├── shift-scrollto-482-win32.png │ │ │ │ ├── shift-scrollto-82-linux.png │ │ │ │ └── shift-scrollto-82-win32.png │ │ │ └── utils │ │ │ │ └── wait.ts │ │ └── unit │ │ │ ├── components.test.tsx │ │ │ ├── events.test.tsx │ │ │ ├── portal.test.tsx │ │ │ ├── render-as.test.tsx │ │ │ ├── setup.ts │ │ │ ├── ssr.test.tsx │ │ │ ├── transition.test.tsx │ │ │ ├── utils │ │ │ ├── ssr.tsx │ │ │ └── testing-library.ts │ │ │ └── virtual-element.test.tsx │ ├── tsconfig.app.json │ ├── tsconfig.build.json │ ├── tsconfig.json │ ├── tsconfig.node.json │ ├── tsconfig.playwright.json │ ├── tsconfig.vitest.json │ ├── vite.config.ts │ └── vitest.config.ts └── vue │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── playwright.config.ts │ ├── scripts │ └── build.sh │ ├── src │ ├── class-resolvers │ │ ├── index.ts │ │ ├── tailwindcss-rtl.ts │ │ ├── tailwindcss.ts │ │ └── type.ts │ ├── createHighOrderFloatComponent.ts │ ├── float.ts │ ├── hooks │ │ ├── use-document-event.ts │ │ ├── use-floating-middleware-from-props.ts │ │ ├── use-outside-click.ts │ │ ├── use-reference-el-resize-observer.ts │ │ └── use-transition-and-origin-class.ts │ ├── index.ts │ ├── resolver.ts │ ├── types.ts │ └── utils │ │ ├── dom.ts │ │ ├── dpr.ts │ │ ├── env.ts │ │ ├── focus-management.ts │ │ ├── match.ts │ │ ├── owner.ts │ │ ├── render.ts │ │ └── warn.ts │ ├── test │ ├── functional │ │ ├── components.test.ts │ │ ├── components.test.ts-snapshots │ │ │ ├── combobox-linux.png │ │ │ ├── combobox-win32.png │ │ │ ├── dialog-close-linux.png │ │ │ ├── dialog-close-win32.png │ │ │ ├── dialog-open-linux.png │ │ │ ├── dialog-open-win32.png │ │ │ ├── listbox-linux.png │ │ │ ├── listbox-win32.png │ │ │ ├── menu-linux.png │ │ │ ├── menu-win32.png │ │ │ ├── popover-linux.png │ │ │ └── popover-win32.png │ │ ├── floatingui-options.test.ts │ │ ├── floatingui-options.test.ts-snapshots │ │ │ ├── flip-scrollto-160-linux.png │ │ │ ├── flip-scrollto-160-win32.png │ │ │ ├── flip-scrollto-250-linux.png │ │ │ ├── flip-scrollto-250-win32.png │ │ │ ├── flip-scrollto-482-linux.png │ │ │ ├── flip-scrollto-482-win32.png │ │ │ ├── hide-scrollto-0-linux.png │ │ │ ├── hide-scrollto-0-win32.png │ │ │ ├── hide-scrollto-220-linux.png │ │ │ ├── hide-scrollto-220-win32.png │ │ │ ├── hide-scrollto-400-linux.png │ │ │ ├── hide-scrollto-400-win32.png │ │ │ ├── hide-scrollto-50-linux.png │ │ │ ├── hide-scrollto-50-win32.png │ │ │ ├── offset-0-linux.png │ │ │ ├── offset-0-win32.png │ │ │ ├── offset-100-linux.png │ │ │ ├── offset-100-win32.png │ │ │ ├── offset-4-linux.png │ │ │ ├── offset-4-win32.png │ │ │ ├── placement-bottom-end-linux.png │ │ │ ├── placement-bottom-end-win32.png │ │ │ ├── placement-bottom-linux.png │ │ │ ├── placement-bottom-start-linux.png │ │ │ ├── placement-bottom-start-win32.png │ │ │ ├── placement-bottom-win32.png │ │ │ ├── placement-left-end-linux.png │ │ │ ├── placement-left-end-win32.png │ │ │ ├── placement-left-linux.png │ │ │ ├── placement-left-start-linux.png │ │ │ ├── placement-left-start-win32.png │ │ │ ├── placement-left-win32.png │ │ │ ├── placement-right-end-linux.png │ │ │ ├── placement-right-end-win32.png │ │ │ ├── placement-right-linux.png │ │ │ ├── placement-right-start-linux.png │ │ │ ├── placement-right-start-win32.png │ │ │ ├── placement-right-win32.png │ │ │ ├── placement-top-end-linux.png │ │ │ ├── placement-top-end-win32.png │ │ │ ├── placement-top-linux.png │ │ │ ├── placement-top-start-linux.png │ │ │ ├── placement-top-start-win32.png │ │ │ ├── placement-top-win32.png │ │ │ ├── shift-scrollto-240-linux.png │ │ │ ├── shift-scrollto-240-win32.png │ │ │ ├── shift-scrollto-482-linux.png │ │ │ ├── shift-scrollto-482-win32.png │ │ │ ├── shift-scrollto-82-linux.png │ │ │ └── shift-scrollto-82-win32.png │ │ └── utils │ │ │ └── wait.ts │ └── unit │ │ ├── components.test.ts │ │ ├── events.test.ts │ │ ├── portal.test.ts │ │ ├── render-as.test.ts │ │ ├── setup.ts │ │ ├── ssr.test.ts │ │ ├── transition.test.ts │ │ ├── utils │ │ ├── html.ts │ │ ├── ssr.ts │ │ └── testing-library.ts │ │ └── virtual-element.test.ts │ ├── tsconfig.app.json │ ├── tsconfig.build.json │ ├── tsconfig.json │ ├── tsconfig.node.json │ ├── tsconfig.playwright.json │ ├── tsconfig.vitest.json │ ├── vite.config.ts │ └── vitest.config.ts └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 2 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | patreon: ycs77 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | 17 | 18 | **Use Version** 19 | Use version when bugs appear: 20 | - Headless UI: v1.7.5 21 | - Headless UI Float: v0.15.0 22 | - Framework: [e.g. vue v3.3.0, react v18.0] 23 | - `@floating-ui/core`: v1.0.0 24 | - `@floating-ui/dom`: v1.0.0 25 | - Browser [e.g. chrome, safari] 26 | 27 | **Describe the bug** 28 | A clear and concise description of what the bug is. 29 | 30 | **To Reproduce** 31 | 32 | Please provide a minimal reproducible example (like github repo, codesandbox, stackblitz...), and steps to reproduce the behavior, it is recommended to fork the Headless UI Flaot's [React Demo](https://headlessui-float.vercel.app/react/quick-start.html#online-demo) or [Vue Demo](https://headlessui-float.vercel.app/vue/quick-start.html#online-demo) to reproduce: 33 | 1. Go to '...' 34 | 2. Click on '....' 35 | 3. Scroll down to '....' 36 | 4. See error 37 | 38 | **Screenshots** 39 | If applicable, add screenshots to help explain your problem. 40 | 41 | **Expected behavior** 42 | A clear and concise description of what you expected to happen. 43 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | -------------------------------------------------------------------------------- /.github/actions/functional/action.yml: -------------------------------------------------------------------------------- 1 | name: Functional 2 | description: Performs functional tests checks 3 | runs: 4 | using: composite 5 | steps: 6 | - run: npx playwright install --with-deps chromium 7 | shell: sh 8 | - run: yarn build 9 | shell: sh 10 | - run: yarn react test:functional 11 | shell: sh 12 | - run: yarn vue test:functional 13 | shell: sh 14 | - uses: actions/upload-artifact@v4 15 | if: always() 16 | with: 17 | name: visual-snapshots-diff 18 | path: test-results 19 | -------------------------------------------------------------------------------- /.github/actions/setup/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup 2 | description: Setup node and install dependencies 3 | runs: 4 | using: composite 5 | steps: 6 | - name: Checkout 7 | uses: actions/setup-node@v4 8 | with: 9 | node-version: 22 10 | 11 | - name: Install dependencies 12 | uses: bahmutov/npm-install@v1 13 | -------------------------------------------------------------------------------- /.github/actions/type-check/action.yml: -------------------------------------------------------------------------------- 1 | name: Type check 2 | description: Performs typescript checks 3 | runs: 4 | using: composite 5 | steps: 6 | - run: yarn lint 7 | shell: sh 8 | - run: yarn react type-check 9 | shell: sh 10 | - run: yarn vue type-check 11 | shell: sh 12 | -------------------------------------------------------------------------------- /.github/actions/unit/action.yml: -------------------------------------------------------------------------------- 1 | name: Unit 2 | description: Performs unit tests checks 3 | runs: 4 | using: composite 5 | steps: 6 | - run: yarn build 7 | shell: sh 8 | - run: yarn react test:unit 9 | shell: sh 10 | - run: yarn vue test:unit 11 | shell: sh 12 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | checks: 13 | name: Linting and type checking 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: ./.github/actions/setup 18 | - uses: ./.github/actions/type-check 19 | 20 | unit-tests: 21 | name: Unit tests 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v4 25 | - uses: ./.github/actions/setup 26 | - uses: ./.github/actions/unit 27 | 28 | functional-tests: 29 | name: Functional tests 30 | runs-on: ubuntu-latest 31 | steps: 32 | - uses: actions/checkout@v4 33 | - uses: ./.github/actions/setup 34 | - uses: ./.github/actions/functional 35 | -------------------------------------------------------------------------------- /.github/workflows/update-snapshots.yml: -------------------------------------------------------------------------------- 1 | name: Update Visual Snapshots 2 | 3 | on: [workflow_dispatch] 4 | 5 | env: 6 | PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 # Skip downloading during yarn install 7 | PLAYWRIGHT_BROWSERS_PATH: 0 # Places binaries to node_modules/@playwright/test 8 | 9 | jobs: 10 | functional-tests: 11 | name: Functional 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: ./.github/actions/setup 16 | - run: npx playwright install --with-deps chromium 17 | - run: yarn build 18 | - run: yarn react test:functional:update 19 | shell: sh 20 | - run: yarn vue test:functional:update 21 | shell: sh 22 | - uses: EndBug/add-and-commit@v9 23 | with: 24 | add: . 25 | message: '(test): Update visual snapshots' 26 | author_name: GitHub Actions 27 | author_email: github-actions@github.com 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .DS_Store 12 | dist 13 | dist-ssr 14 | coverage 15 | *.local 16 | *.tsbuildinfo 17 | vite.config.ts.timestamp-*.mjs 18 | 19 | /cypress/videos/ 20 | /cypress/screenshots/ 21 | test-results 22 | 23 | # Editor directories and files 24 | .vscode/* 25 | !.vscode/extensions.json 26 | .idea 27 | *.suo 28 | *.ntvs* 29 | *.njsproj 30 | *.sln 31 | *.sw? 32 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "vue.volar", 4 | "vue.vscode-typescript-vue-plugin", 5 | "bradlc.vscode-tailwindcss" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2022-present Lucas Yang 4 | 5 | > Permission is hereby granted, free of charge, to any person obtaining a copy 6 | > of this software and associated documentation files (the "Software"), to deal 7 | > in the Software without restriction, including without limitation the rights 8 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | > copies of the Software, and to permit persons to whom the Software is 10 | > furnished to do so, subject to the following conditions: 11 | > 12 | > The above copyright notice and this permission notice shall be included in 13 | > all copies or substantial portions of the Software. 14 | > 15 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | > THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import ycs77, { GLOB_JSX, GLOB_TS, GLOB_TSX, GLOB_VUE } from '@ycs77/eslint-config' 2 | 3 | export default ycs77({ 4 | vue: true, 5 | typescript: true, 6 | }) 7 | .append({ 8 | rules: { 9 | 'test/prefer-lowercase-title': 'off', 10 | }, 11 | }) 12 | .append({ 13 | files: [GLOB_TS, GLOB_TSX, GLOB_VUE], 14 | rules: { 15 | 'style/indent': 'off', 16 | 17 | 'ts/prefer-literal-enum-member': 'off', 18 | 19 | 'antfu/top-level-function': 'off', 20 | 'antfu/consistent-list-newline': 'off', 21 | }, 22 | }) 23 | .append({ 24 | files: [GLOB_JSX, GLOB_TSX], 25 | rules: { 26 | 'antfu/top-level-function': 'off', 27 | 'antfu/consistent-list-newline': 'off', 28 | 29 | 'style/indent': 'off', 30 | 'style/jsx-closing-bracket-location': 'off', 31 | 'style/jsx-curly-newline': 'off', 32 | 'style/jsx-first-prop-new-line': 'off', 33 | 'style/jsx-indent': 'off', 34 | 'style/jsx-max-props-per-line': 'off', 35 | 'style/jsx-one-expression-per-line': 'off', 36 | 'style/quote-props': 'off', 37 | 38 | 'no-unused-vars': 'off', 39 | 'unused-imports/no-unused-vars': 'off', 40 | 'unused-imports/no-unused-imports': 'off', 41 | }, 42 | }) 43 | .append({ 44 | files: ['packages/*/src/utils/*.ts'], 45 | rules: { 46 | 'style/comma-dangle': 'off', 47 | 'ts/comma-dangle': 'off', 48 | 'ts/no-unsafe-function-type': 'off', 49 | }, 50 | }) 51 | -------------------------------------------------------------------------------- /examples/example-react-ts/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /examples/example-react-ts/README.md: -------------------------------------------------------------------------------- 1 | # Headless UI Float - React x TypeScript Example 2 | 3 | Start example: 4 | 5 | ```bash 6 | yarn 7 | yarn dev 8 | ``` 9 | -------------------------------------------------------------------------------- /examples/example-react-ts/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Headless UI Float - React Example 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/example-react-ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "headlessui-float-example-react-ts", 3 | "type": "module", 4 | "version": "0.0.0", 5 | "private": true, 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc --build --force && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@headlessui-float/react": "*", 13 | "@headlessui/react": "^1.7.18", 14 | "react": "^18.0.0", 15 | "react-dom": "^18.0.0", 16 | "react-router-dom": "^6.8.0", 17 | "tailwindcss": "^3.4.0" 18 | }, 19 | "devDependencies": { 20 | "@iconify-json/heroicons": "^1.1.10", 21 | "@svgr/core": "^6.2.1", 22 | "@types/node": "^22.0.0", 23 | "@types/react": "^18.0.0", 24 | "@types/react-dom": "^18.0.0", 25 | "@vitejs/plugin-react": "^4.2.0", 26 | "autoprefixer": "^10.4.19", 27 | "postcss": "^8.4.31", 28 | "typescript": "~5.4.0", 29 | "unplugin-icons": "^0.18.0", 30 | "vite": "^5.4.12" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/example-react-ts/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/example-react-ts/src/components/Block.tsx: -------------------------------------------------------------------------------- 1 | import type { ReactElement } from 'react' 2 | 3 | interface BlockProps { 4 | title: string 5 | contentClass: string 6 | children: ReactElement | ReactElement[] 7 | form?: ReactElement 8 | 'data-testid'?: string 9 | } 10 | 11 | export default function Block(props: BlockProps) { 12 | return ( 13 |
14 |

{props.title}

15 |
16 | {props.children} 17 |
18 | {props.form} 19 |
20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /examples/example-react-ts/src/components/HighOrderFloat.tsx: -------------------------------------------------------------------------------- 1 | import { Float, type FloatProps } from '@headlessui-float/react' 2 | 3 | export default function HighOrderFloat(props: FloatProps) { 4 | return ( 5 | 19 | {props.children} 20 | 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /examples/example-react-ts/src/main.tsx: -------------------------------------------------------------------------------- 1 | import { StrictMode } from 'react' 2 | import { createRoot } from 'react-dom/client' 3 | import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom' 4 | import { routes } from './router' 5 | import App from './App' 6 | import './style.css' 7 | 8 | createRoot(document.getElementById('root')!).render( 9 | 10 | 11 | 12 | }> 13 | {routes.map(({ path, component: Component, redirect }) => ( 14 | (redirect && ( 15 | } /> 16 | )) || 17 | (path && Component && ( 18 | } /> 19 | )) 20 | ))} 21 | 22 | 23 | 24 | 25 | ) 26 | -------------------------------------------------------------------------------- /examples/example-react-ts/src/pages/menu.tsx: -------------------------------------------------------------------------------- 1 | import { Menu } from '@headlessui/react' 2 | import { Float } from '@headlessui-float/react' 3 | import Block from '@/components/Block' 4 | 5 | export default function ExampleMenu() { 6 | return ( 7 | 8 | 9 | 10 | 11 | Options 12 | 13 | 14 | 15 | 16 | {({ active }) => ( 17 | 22 | )} 23 | 24 | 25 | {({ active }) => ( 26 | 31 | )} 32 | 33 | 34 | 35 | Invite a friend (coming soon!) 36 | 37 | 38 | 39 | 40 | 41 | 42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /examples/example-react-ts/src/pages/popover.tsx: -------------------------------------------------------------------------------- 1 | import { Popover } from '@headlessui/react' 2 | import { Float } from '@headlessui-float/react' 3 | import Block from '@/components/Block' 4 | import HeroiconsBars3BottomLeft20Solid from '~icons/heroicons/bars-3-bottom-left-20-solid' 5 | 6 | export default function ExamplePopover() { 7 | return ( 8 | 9 | 10 | 24 | 25 | 27 | 28 | 29 | 30 |
31 | Popover & arrow, content... 32 |
33 |
34 |
35 |
36 |
37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /examples/example-react-ts/src/pages/portal.tsx: -------------------------------------------------------------------------------- 1 | import { Menu } from '@headlessui/react' 2 | import { Float } from '@headlessui-float/react' 3 | import Block from '@/components/Block' 4 | 5 | export default function ExamplePortal() { 6 | return ( 7 | 8 | 9 | 10 | 11 | Options 12 | 13 | 14 | 15 | 16 | {({ active }) => ( 17 | 22 | )} 23 | 24 | 25 | {({ active }) => ( 26 | 31 | )} 32 | 33 | 34 | 35 | Invite a friend (coming soon!) 36 | 37 | 38 | 39 | 40 | 41 | 42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /examples/example-react-ts/src/pages/transition.tsx: -------------------------------------------------------------------------------- 1 | import { Menu } from '@headlessui/react' 2 | import { Float } from '@headlessui-float/react' 3 | import Block from '@/components/Block' 4 | 5 | export default function ExampleTransition() { 6 | return ( 7 | 8 | 9 | 20 | 21 | Options 22 | 23 | 24 | 25 | 26 | {({ active }) => ( 27 | 32 | )} 33 | 34 | 35 | {({ active }) => ( 36 | 41 | )} 42 | 43 | 44 | 45 | Invite a friend (coming soon!) 46 | 47 | 48 | 49 | 50 | 51 | 52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /examples/example-react-ts/src/router.ts: -------------------------------------------------------------------------------- 1 | import type { ElementType } from 'react' 2 | import FloatinguiOptions from '@/pages/floatingui-options' 3 | import Transition from '@/pages/transition' 4 | import Arrow from '@/pages/arrow' 5 | import AdaptiveWidth from '@/pages/adaptive-width' 6 | import Portal from '@/pages/portal' 7 | import Static from '@/pages/static' 8 | import HighOrderComponent from '@/pages/high-order-component' 9 | import Menu from '@/pages/menu' 10 | import Listbox from '@/pages/listbox' 11 | import Combobox from '@/pages/combobox' 12 | import Dialog from '@/pages/dialog' 13 | import Popover from '@/pages/popover' 14 | import VirtualElement from '@/pages/virtual-element' 15 | 16 | export interface RouteRecord { 17 | path: string 18 | component?: ElementType | null 19 | redirect?: string 20 | title?: string 21 | group?: string 22 | soon?: boolean 23 | } 24 | 25 | export const featuresRoutes = [ 26 | { path: '/floatingui-options', component: FloatinguiOptions, title: 'Floating UI options' }, 27 | { path: '/transition', component: Transition }, 28 | { path: '/arrow', component: Arrow }, 29 | { path: '/adaptive-width', component: AdaptiveWidth }, 30 | { path: '/portal', component: Portal }, 31 | { path: '/static', component: Static }, 32 | { path: '/high-order-component', component: HighOrderComponent, title: 'High-order component' }, 33 | ] 34 | 35 | export const componentsRoutes = [ 36 | { path: '/menu', component: Menu }, 37 | { path: '/listbox', component: Listbox }, 38 | { path: '/combobox', component: Combobox }, 39 | { path: '/dialog', component: Dialog }, 40 | { path: '/popover', component: Popover }, 41 | { path: '/virtual-element', component: VirtualElement }, 42 | ] 43 | 44 | export const routes = [ 45 | { path: '/', redirect: '/floatingui-options' }, 46 | ...featuresRoutes.map(route => ({ ...route, group: 'features' })), 47 | ...componentsRoutes.map(route => ({ ...route, group: 'components' })), 48 | ] 49 | -------------------------------------------------------------------------------- /examples/example-react-ts/src/style.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | button { 7 | @apply focus:outline-none; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/example-react-ts/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss' 2 | import { tailwindcssOriginSafelist } from '@headlessui-float/react' 3 | 4 | export default { 5 | content: [ 6 | './index.html', 7 | './src/**/*.{vue,js,ts,jsx,tsx}', 8 | ], 9 | safelist: [...tailwindcssOriginSafelist], 10 | theme: { 11 | extend: {}, 12 | }, 13 | plugins: [], 14 | } satisfies Config 15 | -------------------------------------------------------------------------------- /examples/example-react-ts/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 5 | "target": "ES2020", 6 | "jsx": "react-jsx", 7 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 8 | "useDefineForClassFields": true, 9 | "module": "ESNext", 10 | "moduleResolution": "Bundler", 11 | "paths": { 12 | "@/*": ["./src/*"] 13 | }, 14 | "types": ["vite/client", "unplugin-icons/types/react"], 15 | "strict": true, 16 | "noImplicitThis": true, 17 | "noEmit": true, 18 | "verbatimModuleSyntax": true, 19 | "skipLibCheck": true 20 | }, 21 | "include": ["src/**/*"] 22 | } 23 | -------------------------------------------------------------------------------- /examples/example-react-ts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "references": [ 3 | { "path": "./tsconfig.app.json" }, 4 | { "path": "./tsconfig.node.json" } 5 | ], 6 | "files": [] 7 | } 8 | -------------------------------------------------------------------------------- /examples/example-react-ts/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", 5 | "target": "ES2022", 6 | "lib": ["ES2023"], 7 | "moduleDetection": "force", 8 | "module": "ESNext", 9 | "moduleResolution": "Bundler", 10 | "types": ["node"], 11 | "strict": true, 12 | "noEmit": true, 13 | "esModuleInterop": true, 14 | "skipLibCheck": true 15 | }, 16 | "include": ["vite.config.ts"] 17 | } 18 | -------------------------------------------------------------------------------- /examples/example-react-ts/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { URL, fileURLToPath } from 'node:url' 2 | import { defineConfig } from 'vite' 3 | import React from '@vitejs/plugin-react' 4 | import Icons from 'unplugin-icons/vite' 5 | 6 | export default defineConfig({ 7 | plugins: [ 8 | React(), 9 | Icons({ 10 | compiler: 'jsx', 11 | jsx: 'react', 12 | }), 13 | ], 14 | resolve: { 15 | alias: { 16 | '@': fileURLToPath(new URL('./src', import.meta.url)), 17 | }, 18 | }, 19 | }) 20 | -------------------------------------------------------------------------------- /examples/example-react/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /examples/example-react/README.md: -------------------------------------------------------------------------------- 1 | # Headless UI Float - React Example 2 | 3 | Start example: 4 | 5 | ```bash 6 | yarn 7 | yarn dev 8 | ``` 9 | -------------------------------------------------------------------------------- /examples/example-react/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Headless UI Float - React Example 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/example-react/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "@/*": ["./src/*"] 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/example-react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "headlessui-float-example-react", 3 | "type": "module", 4 | "version": "0.0.0", 5 | "private": true, 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@headlessui-float/react": "*", 13 | "@headlessui/react": "^1.7.18", 14 | "react": "^18.0.0", 15 | "react-dom": "^18.0.0", 16 | "react-router-dom": "^6.8.0", 17 | "tailwindcss": "^3.4.0" 18 | }, 19 | "devDependencies": { 20 | "@iconify-json/heroicons": "^1.1.10", 21 | "@svgr/core": "^6.2.1", 22 | "@vitejs/plugin-react": "^4.2.0", 23 | "autoprefixer": "^10.4.19", 24 | "postcss": "^8.4.31", 25 | "unplugin-icons": "^0.18.0", 26 | "vite": "^5.4.12" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/example-react/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/example-react/src/components/Block.jsx: -------------------------------------------------------------------------------- 1 | export default function Block(props) { 2 | return ( 3 |
4 |

{props.title}

5 |
6 | {props.children} 7 |
8 | {props.form} 9 |
10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /examples/example-react/src/components/HighOrderFloat.jsx: -------------------------------------------------------------------------------- 1 | import { Float } from '@headlessui-float/react' 2 | 3 | export default function HighOrderFloat(props) { 4 | return ( 5 | 19 | {props.children} 20 | 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /examples/example-react/src/main.jsx: -------------------------------------------------------------------------------- 1 | import { StrictMode } from 'react' 2 | import { createRoot } from 'react-dom/client' 3 | import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom' 4 | import { routes } from './router' 5 | import App from './App' 6 | import './style.css' 7 | 8 | createRoot(document.getElementById('root')).render( 9 | 10 | 11 | 12 | }> 13 | {routes.map(({ path, component: Component, redirect }) => ( 14 | (redirect && ( 15 | } /> 16 | )) || 17 | (path && Component && ( 18 | } /> 19 | )) 20 | ))} 21 | 22 | 23 | 24 | 25 | ) 26 | -------------------------------------------------------------------------------- /examples/example-react/src/pages/menu.jsx: -------------------------------------------------------------------------------- 1 | import { Menu } from '@headlessui/react' 2 | import { Float } from '@headlessui-float/react' 3 | import Block from '@/components/Block' 4 | 5 | export default function ExampleMenu() { 6 | return ( 7 | 8 | 9 | 10 | 11 | Options 12 | 13 | 14 | 15 | 16 | {({ active }) => ( 17 | 22 | )} 23 | 24 | 25 | {({ active }) => ( 26 | 31 | )} 32 | 33 | 34 | 35 | Invite a friend (coming soon!) 36 | 37 | 38 | 39 | 40 | 41 | 42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /examples/example-react/src/pages/popover.jsx: -------------------------------------------------------------------------------- 1 | import { Popover } from '@headlessui/react' 2 | import { Float } from '@headlessui-float/react' 3 | import Block from '@/components/Block' 4 | import HeroiconsBars3BottomLeft20Solid from '~icons/heroicons/bars-3-bottom-left-20-solid' 5 | 6 | export default function ExamplePopover() { 7 | return ( 8 | 9 | 10 | 24 | 25 | 27 | 28 | 29 | 30 |
31 | Popover & arrow, content... 32 |
33 |
34 |
35 |
36 |
37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /examples/example-react/src/pages/portal.jsx: -------------------------------------------------------------------------------- 1 | import { Menu } from '@headlessui/react' 2 | import { Float } from '@headlessui-float/react' 3 | import Block from '@/components/Block' 4 | 5 | export default function ExamplePortal() { 6 | return ( 7 | 8 | 9 | 10 | 11 | Options 12 | 13 | 14 | 15 | 16 | {({ active }) => ( 17 | 22 | )} 23 | 24 | 25 | {({ active }) => ( 26 | 31 | )} 32 | 33 | 34 | 35 | Invite a friend (coming soon!) 36 | 37 | 38 | 39 | 40 | 41 | 42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /examples/example-react/src/pages/transition.jsx: -------------------------------------------------------------------------------- 1 | import { Menu } from '@headlessui/react' 2 | import { Float } from '@headlessui-float/react' 3 | import Block from '@/components/Block' 4 | 5 | export default function ExampleTransition() { 6 | return ( 7 | 8 | 9 | 20 | 21 | Options 22 | 23 | 24 | 25 | 26 | {({ active }) => ( 27 | 32 | )} 33 | 34 | 35 | {({ active }) => ( 36 | 41 | )} 42 | 43 | 44 | 45 | Invite a friend (coming soon!) 46 | 47 | 48 | 49 | 50 | 51 | 52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /examples/example-react/src/router.js: -------------------------------------------------------------------------------- 1 | import FloatinguiOptions from '@/pages/floatingui-options' 2 | import Transition from '@/pages/transition' 3 | import Arrow from '@/pages/arrow' 4 | import AdaptiveWidth from '@/pages/adaptive-width' 5 | import Portal from '@/pages/portal' 6 | import Static from '@/pages/static' 7 | import HighOrderComponent from '@/pages/high-order-component' 8 | import Menu from '@/pages/menu' 9 | import Listbox from '@/pages/listbox' 10 | import Combobox from '@/pages/combobox' 11 | import Dialog from '@/pages/dialog' 12 | import Popover from '@/pages/popover' 13 | import VirtualElement from '@/pages/virtual-element' 14 | 15 | export const featuresRoutes = [ 16 | { path: '/floatingui-options', component: FloatinguiOptions, title: 'Floating UI options' }, 17 | { path: '/transition', component: Transition }, 18 | { path: '/arrow', component: Arrow }, 19 | { path: '/adaptive-width', component: AdaptiveWidth }, 20 | { path: '/portal', component: Portal }, 21 | { path: '/static', component: Static }, 22 | { path: '/high-order-component', component: HighOrderComponent, title: 'High-order component' }, 23 | ] 24 | 25 | export const componentsRoutes = [ 26 | { path: '/menu', component: Menu }, 27 | { path: '/listbox', component: Listbox }, 28 | { path: '/combobox', component: Combobox }, 29 | { path: '/dialog', component: Dialog }, 30 | { path: '/popover', component: Popover }, 31 | { path: '/virtual-element', component: VirtualElement }, 32 | ] 33 | 34 | export const routes = [ 35 | { path: '/', redirect: '/floatingui-options' }, 36 | ...featuresRoutes.map(route => ({ ...route, group: 'features' })), 37 | ...componentsRoutes.map(route => ({ ...route, group: 'components' })), 38 | ] 39 | -------------------------------------------------------------------------------- /examples/example-react/src/style.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | button { 7 | @apply focus:outline-none; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/example-react/tailwind.config.js: -------------------------------------------------------------------------------- 1 | import { tailwindcssOriginSafelist } from '@headlessui-float/react' 2 | 3 | /** @type {import('tailwindcss').Config} */ 4 | export default { 5 | content: [ 6 | './index.html', 7 | './src/**/*.{vue,js,ts,jsx,tsx}', 8 | ], 9 | safelist: [...tailwindcssOriginSafelist], 10 | theme: { 11 | extend: {}, 12 | }, 13 | plugins: [], 14 | } 15 | -------------------------------------------------------------------------------- /examples/example-react/vite.config.js: -------------------------------------------------------------------------------- 1 | import { URL, fileURLToPath } from 'node:url' 2 | import { defineConfig } from 'vite' 3 | import React from '@vitejs/plugin-react' 4 | import Icons from 'unplugin-icons/vite' 5 | 6 | export default defineConfig({ 7 | plugins: [ 8 | React(), 9 | Icons({ 10 | compiler: 'jsx', 11 | jsx: 'react', 12 | }), 13 | ], 14 | resolve: { 15 | alias: { 16 | '@': fileURLToPath(new URL('./src', import.meta.url)), 17 | }, 18 | }, 19 | }) 20 | -------------------------------------------------------------------------------- /examples/example-vue-ts/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /examples/example-vue-ts/README.md: -------------------------------------------------------------------------------- 1 | # Headless UI Float - Vue x TypeScript Example 2 | 3 | Start example: 4 | 5 | ```bash 6 | yarn 7 | yarn dev 8 | ``` 9 | -------------------------------------------------------------------------------- /examples/example-vue-ts/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Headless UI Float - Vue Example 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/example-vue-ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "headlessui-float-example-vue-ts", 3 | "type": "module", 4 | "version": "0.0.0", 5 | "private": true, 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vue-tsc --build --force && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@headlessui-float/vue": "*", 13 | "@headlessui/vue": "^1.7.22", 14 | "tailwindcss": "^3.4.0", 15 | "vue": "^3.5.0", 16 | "vue-router": "^4.3.0" 17 | }, 18 | "devDependencies": { 19 | "@iconify-json/heroicons": "^1.1.10", 20 | "@types/node": "^22.0.0", 21 | "@vitejs/plugin-vue": "^5.0.0", 22 | "autoprefixer": "^10.4.19", 23 | "npm-run-all2": "^6.1.2", 24 | "postcss": "^8.4.31", 25 | "typescript": "~5.4.0", 26 | "unplugin-icons": "^0.18.0", 27 | "vite": "^5.4.12", 28 | "vue-tsc": "^2.2.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/example-vue-ts/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/example-vue-ts/src/components/Block.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 18 | -------------------------------------------------------------------------------- /examples/example-vue-ts/src/components/HighOrderFloat.ts: -------------------------------------------------------------------------------- 1 | import { type Float, createHighOrderFloatComponent } from '@headlessui-float/vue' 2 | 3 | const HighOrderFloat: typeof Float = createHighOrderFloatComponent({ 4 | offset: 8, 5 | flip: true, 6 | shift: 6, 7 | portal: true, 8 | enter: 'transition duration-200 ease-out', 9 | enterFrom: 'scale-95 opacity-0', 10 | enterTo: 'scale-100 opacity-100', 11 | leave: 'transition duration-150 ease-in', 12 | leaveFrom: 'scale-100 opacity-100', 13 | leaveTo: 'scale-95 opacity-0', 14 | tailwindcssOriginClass: true, 15 | }) 16 | 17 | export default HighOrderFloat 18 | -------------------------------------------------------------------------------- /examples/example-vue-ts/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import { router } from './router' 3 | import App from './App.vue' 4 | import './style.css' 5 | 6 | createApp(App) 7 | .use(router) 8 | .mount('#app') 9 | -------------------------------------------------------------------------------- /examples/example-vue-ts/src/pages/menu.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 36 | -------------------------------------------------------------------------------- /examples/example-vue-ts/src/pages/popover.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 39 | -------------------------------------------------------------------------------- /examples/example-vue-ts/src/pages/portal.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 36 | -------------------------------------------------------------------------------- /examples/example-vue-ts/src/pages/transition.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 46 | -------------------------------------------------------------------------------- /examples/example-vue-ts/src/pages/virtual-element.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 52 | -------------------------------------------------------------------------------- /examples/example-vue-ts/src/router.ts: -------------------------------------------------------------------------------- 1 | import { type RouteRecordRaw, createRouter, createWebHistory } from 'vue-router' 2 | import FloatinguiOptions from '@/pages/floatingui-options.vue' 3 | import Transition from '@/pages/transition.vue' 4 | import Arrow from '@/pages/arrow.vue' 5 | import AdaptiveWidth from '@/pages/adaptive-width.vue' 6 | import Portal from '@/pages/portal.vue' 7 | import Static from '@/pages/static.vue' 8 | import HighOrderComponent from '@/pages/high-order-component.vue' 9 | import Menu from '@/pages/menu.vue' 10 | import Listbox from '@/pages/listbox.vue' 11 | import Combobox from '@/pages/combobox.vue' 12 | import Dialog from '@/pages/dialog.vue' 13 | import Popover from '@/pages/popover.vue' 14 | import VirtualElement from '@/pages/virtual-element.vue' 15 | 16 | export type RouteRecord = RouteRecordRaw & { 17 | title?: string 18 | group?: string 19 | soon?: boolean 20 | } 21 | 22 | export const featuresRoutes = [ 23 | { path: '/floatingui-options', component: FloatinguiOptions, title: 'Floating UI options' }, 24 | { path: '/transition', component: Transition }, 25 | { path: '/arrow', component: Arrow }, 26 | { path: '/adaptive-width', component: AdaptiveWidth }, 27 | { path: '/portal', component: Portal }, 28 | { path: '/static', component: Static }, 29 | { path: '/high-order-component', component: HighOrderComponent, title: 'High-order component' }, 30 | ] 31 | 32 | export const componentsRoutes = [ 33 | { path: '/menu', component: Menu }, 34 | { path: '/listbox', component: Listbox }, 35 | { path: '/combobox', component: Combobox }, 36 | { path: '/dialog', component: Dialog }, 37 | { path: '/popover', component: Popover }, 38 | { path: '/virtual-element', component: VirtualElement }, 39 | ] 40 | 41 | export const routes = [ 42 | { path: '/', redirect: '/floatingui-options' }, 43 | ...featuresRoutes.map(route => ({ ...route, group: 'features' })), 44 | ...componentsRoutes.map(route => ({ ...route, group: 'components' })), 45 | ] 46 | 47 | export const router = createRouter({ 48 | history: createWebHistory(), 49 | routes, 50 | }) 51 | -------------------------------------------------------------------------------- /examples/example-vue-ts/src/style.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | button { 7 | @apply focus:outline-none; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/example-vue-ts/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss' 2 | import { tailwindcssOriginSafelist } from '@headlessui-float/vue' 3 | 4 | export default { 5 | content: [ 6 | './index.html', 7 | './src/**/*.{vue,js,ts,jsx,tsx}', 8 | ], 9 | safelist: [...tailwindcssOriginSafelist], 10 | theme: { 11 | extend: {}, 12 | }, 13 | plugins: [], 14 | } satisfies Config 15 | -------------------------------------------------------------------------------- /examples/example-vue-ts/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 5 | "target": "ES2020", 6 | "jsx": "preserve", 7 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 8 | "useDefineForClassFields": true, 9 | "module": "ESNext", 10 | "moduleResolution": "Bundler", 11 | "paths": { 12 | "@/*": ["./src/*"] 13 | }, 14 | "types": ["vite/client", "unplugin-icons/types/vue3"], 15 | "strict": true, 16 | "noImplicitThis": true, 17 | "noEmit": true, 18 | "verbatimModuleSyntax": true, 19 | "skipLibCheck": true 20 | }, 21 | "include": ["src/**/*", "src/**/*.vue"] 22 | } 23 | -------------------------------------------------------------------------------- /examples/example-vue-ts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "references": [ 3 | { "path": "./tsconfig.app.json" }, 4 | { "path": "./tsconfig.node.json" } 5 | ], 6 | "files": [] 7 | } 8 | -------------------------------------------------------------------------------- /examples/example-vue-ts/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", 5 | "target": "ES2022", 6 | "lib": ["ES2023"], 7 | "moduleDetection": "force", 8 | "module": "ESNext", 9 | "moduleResolution": "Bundler", 10 | "types": ["node"], 11 | "strict": true, 12 | "noEmit": true, 13 | "esModuleInterop": true, 14 | "skipLibCheck": true 15 | }, 16 | "include": ["vite.config.ts"] 17 | } 18 | -------------------------------------------------------------------------------- /examples/example-vue-ts/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { URL, fileURLToPath } from 'node:url' 2 | import { defineConfig } from 'vite' 3 | import Vue from '@vitejs/plugin-vue' 4 | import Icons from 'unplugin-icons/vite' 5 | 6 | export default defineConfig({ 7 | plugins: [ 8 | Vue(), 9 | Icons({ compiler: 'vue3' }), 10 | ], 11 | resolve: { 12 | alias: { 13 | '@': fileURLToPath(new URL('./src', import.meta.url)), 14 | }, 15 | }, 16 | }) 17 | -------------------------------------------------------------------------------- /examples/example-vue/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /examples/example-vue/README.md: -------------------------------------------------------------------------------- 1 | # Headless UI Float - Vue Example 2 | 3 | Start example: 4 | 5 | ```bash 6 | yarn 7 | yarn dev 8 | ``` 9 | -------------------------------------------------------------------------------- /examples/example-vue/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Headless UI Float - Vue Example 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/example-vue/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "@/*": ["./src/*"] 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/example-vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "headlessui-float-example-vue", 3 | "type": "module", 4 | "version": "0.0.0", 5 | "private": true, 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@headlessui-float/vue": "*", 13 | "@headlessui/vue": "^1.7.22", 14 | "tailwindcss": "^3.4.0", 15 | "vue": "^3.5.0", 16 | "vue-router": "^4.3.0" 17 | }, 18 | "devDependencies": { 19 | "@iconify-json/heroicons": "^1.1.10", 20 | "@vitejs/plugin-vue": "^5.0.0", 21 | "autoprefixer": "^10.4.19", 22 | "postcss": "^8.4.31", 23 | "unplugin-icons": "^0.18.0", 24 | "vite": "^5.4.12" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/example-vue/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/example-vue/src/components/Block.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 18 | -------------------------------------------------------------------------------- /examples/example-vue/src/components/HighOrderFloat.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 23 | -------------------------------------------------------------------------------- /examples/example-vue/src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import { router } from './router' 3 | import App from './App.vue' 4 | import './style.css' 5 | 6 | createApp(App) 7 | .use(router) 8 | .mount('#app') 9 | -------------------------------------------------------------------------------- /examples/example-vue/src/pages/menu.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 36 | -------------------------------------------------------------------------------- /examples/example-vue/src/pages/popover.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 39 | -------------------------------------------------------------------------------- /examples/example-vue/src/pages/portal.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 36 | -------------------------------------------------------------------------------- /examples/example-vue/src/pages/transition.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 46 | -------------------------------------------------------------------------------- /examples/example-vue/src/pages/virtual-element.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 52 | -------------------------------------------------------------------------------- /examples/example-vue/src/router.js: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory } from 'vue-router' 2 | import FloatinguiOptions from '@/pages/floatingui-options.vue' 3 | import Transition from '@/pages/transition.vue' 4 | import Arrow from '@/pages/arrow.vue' 5 | import AdaptiveWidth from '@/pages/adaptive-width.vue' 6 | import Portal from '@/pages/portal.vue' 7 | import Static from '@/pages/static.vue' 8 | import HighOrderComponent from '@/pages/high-order-component.vue' 9 | import Menu from '@/pages/menu.vue' 10 | import Listbox from '@/pages/listbox.vue' 11 | import Combobox from '@/pages/combobox.vue' 12 | import Dialog from '@/pages/dialog.vue' 13 | import Popover from '@/pages/popover.vue' 14 | import VirtualElement from '@/pages/virtual-element.vue' 15 | 16 | export const featuresRoutes = [ 17 | { path: '/floatingui-options', component: FloatinguiOptions, title: 'Floating UI options' }, 18 | { path: '/transition', component: Transition }, 19 | { path: '/arrow', component: Arrow }, 20 | { path: '/adaptive-width', component: AdaptiveWidth }, 21 | { path: '/portal', component: Portal }, 22 | { path: '/static', component: Static }, 23 | { path: '/high-order-component', component: HighOrderComponent, title: 'High-order component' }, 24 | ] 25 | 26 | export const componentsRoutes = [ 27 | { path: '/menu', component: Menu }, 28 | { path: '/listbox', component: Listbox }, 29 | { path: '/combobox', component: Combobox }, 30 | { path: '/dialog', component: Dialog }, 31 | { path: '/popover', component: Popover }, 32 | { path: '/virtual-element', component: VirtualElement }, 33 | ] 34 | 35 | export const routes = [ 36 | { path: '/', redirect: '/floatingui-options' }, 37 | ...featuresRoutes.map(route => ({ ...route, group: 'features' })), 38 | ...componentsRoutes.map(route => ({ ...route, group: 'components' })), 39 | ] 40 | 41 | export const router = createRouter({ 42 | history: createWebHistory(), 43 | routes, 44 | }) 45 | -------------------------------------------------------------------------------- /examples/example-vue/src/style.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | button { 7 | @apply focus:outline-none; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/example-vue/tailwind.config.js: -------------------------------------------------------------------------------- 1 | import { tailwindcssOriginSafelist } from '@headlessui-float/vue' 2 | 3 | /** @type {import('tailwindcss').Config} */ 4 | export default { 5 | content: [ 6 | './index.html', 7 | './src/**/*.{vue,js,ts,jsx,tsx}', 8 | ], 9 | safelist: [...tailwindcssOriginSafelist], 10 | theme: { 11 | extend: {}, 12 | }, 13 | plugins: [], 14 | } 15 | -------------------------------------------------------------------------------- /examples/example-vue/vite.config.js: -------------------------------------------------------------------------------- 1 | import { URL, fileURLToPath } from 'node:url' 2 | import { defineConfig } from 'vite' 3 | import Vue from '@vitejs/plugin-vue' 4 | import Icons from 'unplugin-icons/vite' 5 | 6 | export default defineConfig({ 7 | plugins: [ 8 | Vue(), 9 | Icons({ compiler: 'vue3' }), 10 | ], 11 | resolve: { 12 | alias: { 13 | '@': fileURLToPath(new URL('./src', import.meta.url)), 14 | }, 15 | }, 16 | }) 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "headlessui-float", 3 | "type": "module", 4 | "version": "0.0.0", 5 | "private": true, 6 | "description": "Easily use Headless UI components with Floating UI (Popper.js)", 7 | "license": "MIT", 8 | "workspaces": [ 9 | "packages/*", 10 | "examples/*" 11 | ], 12 | "scripts": { 13 | "build": "run-s build:*", 14 | "build:react": "yarn react build", 15 | "build:vue": "yarn vue build", 16 | "build:nuxt": "yarn nuxt build", 17 | "watch": "run-p watch:*", 18 | "watch:react": "yarn react watch", 19 | "watch:vue": "yarn vue watch", 20 | "watch:example": "run-p watch:example:*", 21 | "watch:example:react": "yarn react:example dev --port=3011", 22 | "watch:example:react-ts": "yarn react-ts:example dev --port=3012", 23 | "watch:example:vue": "yarn vue:example dev --port=3013", 24 | "watch:example:vue-ts": "yarn vue-ts:example dev --port=3014", 25 | "test": "run-s test:unit test:functional", 26 | "test:unit": "run-s test:unit:*", 27 | "test:unit:react": "yarn react test:unit --run", 28 | "test:unit:vue": "yarn vue test:unit --run", 29 | "test:functional": "run-s test:functional:*", 30 | "test:functional:react": "yarn react test:functional", 31 | "test:functional:vue": "yarn vue test:functional", 32 | "react": "yarn workspace @headlessui-float/react", 33 | "react:example": "yarn workspace headlessui-float-example-react", 34 | "react-ts:example": "yarn workspace headlessui-float-example-react-ts", 35 | "vue": "yarn workspace @headlessui-float/vue", 36 | "vue:example": "yarn workspace headlessui-float-example-vue", 37 | "vue-ts:example": "yarn workspace headlessui-float-example-vue-ts", 38 | "nuxt": "yarn workspace @headlessui-float/nuxt", 39 | "lint": "eslint ." 40 | }, 41 | "devDependencies": { 42 | "@ycs77/eslint-config": "^3.1.2", 43 | "eslint": "^9.15.0", 44 | "npm-run-all2": "^7.0.2" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packages/nuxt/.gitignore: -------------------------------------------------------------------------------- 1 | .nuxt 2 | .output 3 | .data 4 | .vercel_build_output 5 | .build-* 6 | .netlify 7 | -------------------------------------------------------------------------------- /packages/nuxt/LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2022-present Lucas Yang 4 | 5 | > Permission is hereby granted, free of charge, to any person obtaining a copy 6 | > of this software and associated documentation files (the "Software"), to deal 7 | > in the Software without restriction, including without limitation the rights 8 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | > copies of the Software, and to permit persons to whom the Software is 10 | > furnished to do so, subject to the following conditions: 11 | > 12 | > The above copyright notice and this permission notice shall be included in 13 | > all copies or substantial portions of the Software. 14 | > 15 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | > THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/nuxt/README.md: -------------------------------------------------------------------------------- 1 |

Headless UI Float Nuxt

2 | 3 |

4 | Easily use Headless UI for Nuxt 3 with Floating UI (New version Popper.js) to position floating elements. 5 |

6 | 7 |

8 | NPM Version 9 | Software License 10 | NPM Downloads 11 |

12 | 13 |
14 | 15 | > [!NOTE] 16 | > [Headless UI](https://headlessui.com/) has [released version 2.0](https://tailwindcss.com/blog/headless-ui-v2#built-in-anchor-positioning) (currently for React only), which builds in anchor positioning with Floating UI. Therefore, this package will be unnecessary when Headless UI v2 supports Vue. 17 | 18 | ## Features 19 | 20 | * 💙 Easily use Headless UI & Tailwind CSS 21 | * 💬 Floating UI (New version Popper.js) position floating elements 22 | * 🔔 Auto-update floating elements 23 | * ♾️ Support Transition 24 | * 🚪 Support Portal (Teleport) 25 | * ➡️ Support Arrow 26 | 27 | ## Documentation 28 | 29 | [Documentation](https://headlessui-float.vercel.app/) | [繁體中文文檔](https://headlessui-float.vercel.app/zh-tw/) 30 | 31 | ## Sponsor 32 | 33 | If you think this package has helped you, please consider [Becoming a sponsor](https://www.patreon.com/ycs77) to support my work~ and your avatar will be visible on my major projects. 34 | 35 |

36 | 37 | 38 | 39 |

40 | 41 | 42 | Become a Patron 43 | 44 | 45 | ## Credits 46 | 47 | * [Headless UI](https://headlessui.com/) 48 | * [Floating UI](https://floating-ui.com/) 49 | * This package is inspired by the [headlessui#154 example](https://github.com/tailwindlabs/headlessui/issues/154) 50 | 51 | ## License 52 | 53 | Under the [MIT LICENSE](LICENSE.md) 54 | -------------------------------------------------------------------------------- /packages/nuxt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@headlessui-float/nuxt", 3 | "type": "module", 4 | "version": "0.15.0", 5 | "description": "Easily use Headless UI for Nuxt 3 with Floating UI (Popper.js)", 6 | "license": "MIT", 7 | "homepage": "https://headlessui-float.vercel.app", 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/ycs77/headlessui-float.git", 11 | "directory": "packages/nuxt" 12 | }, 13 | "keywords": [ 14 | "floating", 15 | "popper", 16 | "popover", 17 | "tooltip", 18 | "dropdown", 19 | "headless", 20 | "headlessui", 21 | "vue", 22 | "nuxt" 23 | ], 24 | "exports": { 25 | ".": { 26 | "types": "./dist/types.d.ts", 27 | "import": "./dist/module.mjs", 28 | "require": "./dist/module.cjs" 29 | } 30 | }, 31 | "main": "./dist/module.cjs", 32 | "types": "./dist/types.d.ts", 33 | "files": [ 34 | "dist" 35 | ], 36 | "publishConfig": { 37 | "access": "public" 38 | }, 39 | "scripts": { 40 | "build": "npm run build:prepare && nuxt-module-build build", 41 | "build:prepare": "cross-env NUXT_TELEMETRY_DISABLED=1 nuxt-module-build prepare", 42 | "dev": "nuxi dev playground", 43 | "dev:build": "nuxi build playground", 44 | "dev:prepare": "nuxt-module-build build --stub && npm run build:prepare && nuxi prepare playground", 45 | "prepack": "npm run build" 46 | }, 47 | "dependencies": { 48 | "@headlessui-float/vue": "^0.15.0", 49 | "@headlessui/vue": "^1.7.0", 50 | "@nuxt/kit": "^3.13.0" 51 | }, 52 | "devDependencies": { 53 | "@nuxt/module-builder": "^0.8.4", 54 | "@nuxt/schema": "~3.13.0", 55 | "@nuxtjs/tailwindcss": "~6.12.2", 56 | "@types/node": "^22.0.0", 57 | "cross-env": "^7.0.3", 58 | "nuxt": "~3.13.0" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /packages/nuxt/playground/components/Block.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 18 | -------------------------------------------------------------------------------- /packages/nuxt/playground/components/HighOrderFloat.ts: -------------------------------------------------------------------------------- 1 | import { type Float, createHighOrderFloatComponent } from '@headlessui-float/vue' 2 | 3 | const HighOrderFloat: typeof Float = createHighOrderFloatComponent({ 4 | offset: 8, 5 | flip: true, 6 | shift: 6, 7 | portal: true, 8 | enter: 'transition duration-200 ease-out', 9 | enterFrom: 'scale-95 opacity-0', 10 | enterTo: 'scale-100 opacity-100', 11 | leave: 'transition duration-150 ease-in', 12 | leaveFrom: 'scale-100 opacity-100', 13 | leaveTo: 'scale-95 opacity-0', 14 | tailwindcssOriginClass: true, 15 | }) 16 | 17 | export default HighOrderFloat 18 | -------------------------------------------------------------------------------- /packages/nuxt/playground/nuxt.config.ts: -------------------------------------------------------------------------------- 1 | export default defineNuxtConfig({ 2 | modules: [ 3 | '@nuxtjs/tailwindcss', 4 | 'unplugin-icons/nuxt', 5 | '../src/module', 6 | ], 7 | headlessuiFloat: {}, 8 | routeRules: { 9 | '/': { redirect: '/floatingui-options' }, 10 | }, 11 | telemetry: false, 12 | }) 13 | -------------------------------------------------------------------------------- /packages/nuxt/playground/pages/menu.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 34 | -------------------------------------------------------------------------------- /packages/nuxt/playground/pages/popover.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 37 | -------------------------------------------------------------------------------- /packages/nuxt/playground/pages/portal.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 34 | -------------------------------------------------------------------------------- /packages/nuxt/playground/pages/transition.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 44 | -------------------------------------------------------------------------------- /packages/nuxt/playground/pages/virtual-element.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 51 | -------------------------------------------------------------------------------- /packages/nuxt/playground/routes.ts: -------------------------------------------------------------------------------- 1 | import type { RouteRecordRaw } from 'vue-router' 2 | 3 | export type RouteRecord = RouteRecordRaw & { 4 | title?: string 5 | group?: string 6 | soon?: boolean 7 | } 8 | 9 | export const featuresRoutes = [ 10 | { path: '/floatingui-options', title: 'Floating UI options' }, 11 | { path: '/transition' }, 12 | { path: '/arrow' }, 13 | { path: '/portal' }, 14 | { path: '/high-order-component', title: 'High-order component' }, 15 | ] 16 | 17 | export const componentsRoutes = [ 18 | { path: '/menu' }, 19 | { path: '/listbox' }, 20 | { path: '/combobox' }, 21 | { path: '/popover' }, 22 | { path: '/virtual-element' }, 23 | ] 24 | 25 | export const routes = [ 26 | ...featuresRoutes.map(route => ({ ...route, group: 'features' })), 27 | ...componentsRoutes.map(route => ({ ...route, group: 'components' })), 28 | ] 29 | -------------------------------------------------------------------------------- /packages/nuxt/playground/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss' 2 | import { tailwindcssOriginSafelist } from '@headlessui-float/vue' 3 | 4 | export default { 5 | safelist: [...tailwindcssOriginSafelist], 6 | theme: { 7 | extend: {}, 8 | }, 9 | } satisfies Partial 10 | -------------------------------------------------------------------------------- /packages/nuxt/playground/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.nuxt/tsconfig.json", 3 | "compilerOptions": { 4 | "types": ["unplugin-icons/types/vue3"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/nuxt/src/module.ts: -------------------------------------------------------------------------------- 1 | import { addComponent, defineNuxtModule } from '@nuxt/kit' 2 | 3 | export interface ModuleOptions { 4 | prefix?: string 5 | } 6 | 7 | const components = [ 8 | 'Float', 9 | 'FloatArrow', 10 | 'FloatContent', 11 | 'FloatReference', 12 | ] 13 | 14 | export default defineNuxtModule({ 15 | meta: { 16 | name: 'headlessui-float', 17 | configKey: 'headlessuiFloat', 18 | compatibility: { 19 | nuxt: '^3.0.0', 20 | }, 21 | }, 22 | defaults: { 23 | prefix: '', 24 | }, 25 | setup(options) { 26 | components.forEach(componentName => { 27 | addComponent({ 28 | name: `${options.prefix}${componentName}`, 29 | export: componentName, 30 | filePath: '@headlessui-float/vue', 31 | }) 32 | }) 33 | }, 34 | }) 35 | -------------------------------------------------------------------------------- /packages/nuxt/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.nuxt/tsconfig.json", 3 | "exclude": [ 4 | "dist", 5 | "node_modules", 6 | "playground" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /packages/react/LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2022-present Lucas Yang 4 | 5 | > Permission is hereby granted, free of charge, to any person obtaining a copy 6 | > of this software and associated documentation files (the "Software"), to deal 7 | > in the Software without restriction, including without limitation the rights 8 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | > copies of the Software, and to permit persons to whom the Software is 10 | > furnished to do so, subject to the following conditions: 11 | > 12 | > The above copyright notice and this permission notice shall be included in 13 | > all copies or substantial portions of the Software. 14 | > 15 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | > THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@headlessui-float/react", 3 | "type": "module", 4 | "version": "0.15.0", 5 | "description": "Easily use Headless UI for React with Floating UI (Popper.js)", 6 | "license": "MIT", 7 | "homepage": "https://headlessui-float.vercel.app", 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/ycs77/headlessui-float.git", 11 | "directory": "packages/react" 12 | }, 13 | "keywords": [ 14 | "floating", 15 | "popper", 16 | "popover", 17 | "tooltip", 18 | "dropdown", 19 | "headless", 20 | "headlessui", 21 | "react" 22 | ], 23 | "sideEffects": false, 24 | "exports": { 25 | ".": { 26 | "types": "./dist/index.d.ts", 27 | "import": "./dist/headlessui-float.mjs", 28 | "require": "./dist/headlessui-float.cjs" 29 | } 30 | }, 31 | "main": "./dist/headlessui-float.cjs", 32 | "module": "./dist/headlessui-float.mjs", 33 | "types": "./dist/index.d.ts", 34 | "files": [ 35 | "dist" 36 | ], 37 | "publishConfig": { 38 | "access": "public" 39 | }, 40 | "scripts": { 41 | "build": "sh scripts/build.sh", 42 | "watch": "vite build --watch", 43 | "type-check": "tsc --noEmit", 44 | "test:unit": "vitest --root test/unit/", 45 | "test:functional": "playwright test test/functional", 46 | "test:functional:update": "yarn test:functional -u", 47 | "prepack": "npm run build" 48 | }, 49 | "peerDependencies": { 50 | "@headlessui/react": "^1.0.0", 51 | "react": "^18", 52 | "react-dom": "^18" 53 | }, 54 | "dependencies": { 55 | "@floating-ui/core": "^1.5.3", 56 | "@floating-ui/dom": "^1.5.4", 57 | "@floating-ui/react": "^0.26.5" 58 | }, 59 | "devDependencies": { 60 | "@headlessui/react": "^1.7.18", 61 | "@playwright/test": "^1.30.0", 62 | "@testing-library/jest-dom": "^6.2.0", 63 | "@testing-library/react": "^14.1.0", 64 | "@testing-library/user-event": "^14.5.2", 65 | "@types/jsdom": "^21.1.7", 66 | "@types/node": "^22.0.0", 67 | "@types/react": "^18.0.0", 68 | "@types/react-dom": "^18.0.0", 69 | "@vitejs/plugin-react": "^4.2.0", 70 | "jsdom": "^23.2.0", 71 | "react": "^18.0.0", 72 | "react-dom": "^18.0.0", 73 | "typescript": "~5.4.0", 74 | "vite": "^5.4.12", 75 | "vitest": "^2.0.0" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /packages/react/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import type { PlaywrightTestConfig } from '@playwright/test' 2 | 3 | export default { 4 | use: { 5 | launchOptions: { 6 | ignoreDefaultArgs: ['--hide-scrollbars'], 7 | }, 8 | }, 9 | webServer: { 10 | command: 'yarn workspace headlessui-float-example-react-ts dev --port=3031', 11 | port: 3031, 12 | timeout: 120 * 1000, 13 | reuseExistingServer: !process.env.CI, 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /packages/react/scripts/build.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | 3 | rm dist -rf 4 | 5 | tsc --noEmit && \ 6 | vite build && \ 7 | tsc --declaration --emitDeclarationOnly -p tsconfig.build.json 8 | -------------------------------------------------------------------------------- /packages/react/src/class-resolvers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './tailwindcss' 2 | export * from './tailwindcss-rtl' 3 | export * from './type' 4 | -------------------------------------------------------------------------------- /packages/react/src/class-resolvers/tailwindcss-rtl.ts: -------------------------------------------------------------------------------- 1 | import type { ClassResolver } from './type' 2 | 3 | export const tailwindcssRtlOriginSafelist = [ 4 | 'origin-bottom', 5 | 'origin-top', 6 | 'ltr:origin-right rtl:origin-left', 7 | 'ltr:origin-left rtl:origin-right', 8 | 'ltr:origin-bottom-left rtl:origin-bottom-right', 9 | 'ltr:origin-bottom-right rtl:origin-bottom-left', 10 | 'ltr:origin-top-left rtl:origin-top-right', 11 | 'ltr:origin-top-right rtl:origin-top-left', 12 | ] 13 | 14 | export const tailwindcssRtlOriginClassResolver: ClassResolver = placement => { 15 | switch (placement) { 16 | case 'top': 17 | return 'origin-bottom' 18 | case 'bottom': 19 | return 'origin-top' 20 | case 'left': 21 | return 'ltr:origin-right rtl:origin-left' 22 | case 'right': 23 | return 'ltr:origin-left rtl:origin-right' 24 | case 'top-start': 25 | case 'right-end': 26 | return 'ltr:origin-bottom-left rtl:origin-bottom-right' 27 | case 'top-end': 28 | case 'left-end': 29 | return 'ltr:origin-bottom-right rtl:origin-bottom-left' 30 | case 'right-start': 31 | case 'bottom-start': 32 | return 'ltr:origin-top-left rtl:origin-top-right' 33 | case 'left-start': 34 | case 'bottom-end': 35 | return 'ltr:origin-top-right rtl:origin-top-left' 36 | default: 37 | return 'origin-center' 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/react/src/class-resolvers/tailwindcss.ts: -------------------------------------------------------------------------------- 1 | import type { ClassResolver } from './type' 2 | 3 | export const tailwindcssOriginSafelist = [ 4 | 'origin-bottom', 5 | 'origin-top', 6 | 'origin-right', 7 | 'origin-left', 8 | 'origin-bottom-left', 9 | 'origin-bottom-right', 10 | 'origin-top-left', 11 | 'origin-top-right', 12 | ] 13 | 14 | export const tailwindcssOriginClassResolver: ClassResolver = placement => { 15 | switch (placement) { 16 | case 'top': 17 | return 'origin-bottom' 18 | case 'bottom': 19 | return 'origin-top' 20 | case 'left': 21 | return 'origin-right' 22 | case 'right': 23 | return 'origin-left' 24 | case 'top-start': 25 | case 'right-end': 26 | return 'origin-bottom-left' 27 | case 'top-end': 28 | case 'left-end': 29 | return 'origin-bottom-right' 30 | case 'right-start': 31 | case 'bottom-start': 32 | return 'origin-top-left' 33 | case 'left-start': 34 | case 'bottom-end': 35 | return 'origin-top-right' 36 | default: 37 | return 'origin-center' 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/react/src/class-resolvers/type.ts: -------------------------------------------------------------------------------- 1 | import type { Placement } from '@floating-ui/dom' 2 | 3 | export type ClassResolver = (placement: Placement) => string 4 | 5 | /** @deprecated Replace to using `ClassResolver` */ 6 | export type OriginClassResolver = ClassResolver 7 | -------------------------------------------------------------------------------- /packages/react/src/hooks/use-document-event.ts: -------------------------------------------------------------------------------- 1 | // Reference: https://github.com/tailwindlabs/headlessui/blob/7794d563e181787e995db1f877cd26c460e385ee/packages/%40headlessui-react/src/hooks/use-document-event.ts 2 | 3 | import { useEffect } from 'react' 4 | import { useLatestValue } from './use-latest-value' 5 | 6 | export function useDocumentEvent( 7 | type: TType, 8 | listener: (ev: DocumentEventMap[TType]) => any, 9 | options?: boolean | AddEventListenerOptions 10 | ) { 11 | const listenerRef = useLatestValue(listener) 12 | 13 | useEffect(() => { 14 | function handler(event: DocumentEventMap[TType]) { 15 | listenerRef.current(event) 16 | } 17 | 18 | document.addEventListener(type, handler, options) 19 | return () => document.removeEventListener(type, handler, options) 20 | }, [type, options]) 21 | } 22 | -------------------------------------------------------------------------------- /packages/react/src/hooks/use-id.ts: -------------------------------------------------------------------------------- 1 | // Reference: https://github.com/tailwindlabs/headlessui/blob/7794d563e181787e995db1f877cd26c460e385ee/packages/%40headlessui-react/src/hooks/use-id.ts 2 | 3 | import React from 'react' 4 | import { env } from '../utils/env' 5 | import { useIsoMorphicEffect } from './use-iso-morphic-effect' 6 | import { useServerHandoffComplete } from './use-server-handoff-complete' 7 | 8 | // We used a "simple" approach first which worked for SSR and rehydration on the client. However we 9 | // didn't take care of the Suspense case. To fix this we used the approach the @reach-ui/auto-id 10 | // uses. 11 | // 12 | // Credits: https://github.com/reach/reach-ui/blob/develop/packages/auto-id/src/index.tsx 13 | 14 | // let id = 0 15 | // function generateId() { 16 | // return ++id 17 | // } 18 | 19 | export const useId = 20 | // Prefer React's `useId` if it's available. 21 | React.useId ?? 22 | function useId() { 23 | const ready = useServerHandoffComplete() 24 | const [id, setId] = React.useState(ready ? () => env.nextId() : null) 25 | 26 | useIsoMorphicEffect(() => { 27 | if (id === null) setId(env.nextId()) 28 | }, [id]) 29 | 30 | return id != null ? `${id}` : undefined 31 | } 32 | -------------------------------------------------------------------------------- /packages/react/src/hooks/use-iso-morphic-effect.ts: -------------------------------------------------------------------------------- 1 | // Reference: https://github.com/tailwindlabs/headlessui/blob/7794d563e181787e995db1f877cd26c460e385ee/packages/%40headlessui-react/src/hooks/use-iso-morphic-effect.ts 2 | 3 | import { type DependencyList, type EffectCallback, useEffect, useLayoutEffect } from 'react' 4 | import { env } from '../utils/env' 5 | 6 | export const useIsoMorphicEffect = (effect: EffectCallback, deps?: DependencyList | undefined) => { 7 | if (env.isServer) { 8 | useEffect(effect, deps) 9 | } else { 10 | useLayoutEffect(effect, deps) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/react/src/hooks/use-latest-value.ts: -------------------------------------------------------------------------------- 1 | // Reference: https://github.com/tailwindlabs/headlessui/blob/7794d563e181787e995db1f877cd26c460e385ee/packages/%40headlessui-react/src/hooks/use-latest-value.ts 2 | 3 | import { useRef } from 'react' 4 | import { useIsoMorphicEffect } from './use-iso-morphic-effect' 5 | 6 | export function useLatestValue(value: T) { 7 | const cache = useRef(value) 8 | 9 | useIsoMorphicEffect(() => { 10 | cache.current = value 11 | }, [value]) 12 | 13 | return cache 14 | } 15 | -------------------------------------------------------------------------------- /packages/react/src/hooks/use-origin-class.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react' 2 | import type { Placement } from '@floating-ui/core' 3 | import { type ClassResolver, tailwindcssOriginClassResolver } from '../class-resolvers' 4 | 5 | export function useOriginClass(props: { 6 | originClass?: string | ClassResolver 7 | tailwindcssOriginClass?: boolean 8 | }, placement: Placement) { 9 | return useMemo(() => { 10 | if (typeof props.originClass === 'function') { 11 | return props.originClass(placement) 12 | } else if (typeof props.originClass === 'string') { 13 | return props.originClass 14 | } else if (props.tailwindcssOriginClass) { 15 | return tailwindcssOriginClassResolver(placement) 16 | } 17 | return '' 18 | }, [placement, props.originClass, props.tailwindcssOriginClass]) 19 | } 20 | -------------------------------------------------------------------------------- /packages/react/src/hooks/use-reference-el-resize-observer.ts: -------------------------------------------------------------------------------- 1 | import { type MutableRefObject, useEffect } from 'react' 2 | import type { ReferenceType } from '@floating-ui/react' 3 | import { env } from '../utils/env' 4 | 5 | export function useReferenceElResizeObserver( 6 | enabled: boolean | undefined, 7 | reference: MutableRefObject, 8 | setReferenceElWidth: (width: number | null) => void, 9 | ) { 10 | useEffect(() => { 11 | if (enabled && 12 | env.isClient && 13 | typeof ResizeObserver !== 'undefined' && 14 | reference.current && 15 | reference.current instanceof Element 16 | ) { 17 | const observer = new ResizeObserver(([entry]) => { 18 | const width = entry.borderBoxSize.reduce((acc, { inlineSize }) => acc + inlineSize, 0) 19 | setReferenceElWidth(width) 20 | }) 21 | observer.observe(reference.current) 22 | 23 | return () => { 24 | observer.disconnect() 25 | setReferenceElWidth(null) 26 | } 27 | } 28 | }, []) 29 | } 30 | -------------------------------------------------------------------------------- /packages/react/src/hooks/use-server-handoff-complete.ts: -------------------------------------------------------------------------------- 1 | // Reference: https://github.com/tailwindlabs/headlessui/blob/0162c57d88cfdc74209d6bdcac94d54078f97675/packages/%40headlessui-react/src/hooks/use-server-handoff-complete.ts 2 | 3 | import { useEffect, useState } from 'react' 4 | 5 | const state = { serverHandoffComplete: false } 6 | 7 | export function useServerHandoffComplete() { 8 | const [serverHandoffComplete, setServerHandoffComplete] = useState(state.serverHandoffComplete) 9 | 10 | useEffect(() => { 11 | if (serverHandoffComplete === true) return 12 | 13 | setServerHandoffComplete(true) 14 | }, [serverHandoffComplete]) 15 | 16 | useEffect(() => { 17 | if (state.serverHandoffComplete === false) state.serverHandoffComplete = true 18 | }, []) 19 | 20 | return serverHandoffComplete 21 | } 22 | -------------------------------------------------------------------------------- /packages/react/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './float' 2 | export * from './class-resolvers' 3 | export { useOutsideClick } from './hooks/use-outside-click' 4 | -------------------------------------------------------------------------------- /packages/react/src/utils/dpr.ts: -------------------------------------------------------------------------------- 1 | export function getDPR(element: Element) { 2 | if (typeof window === 'undefined') { 3 | return 1 4 | } 5 | const win = element.ownerDocument.defaultView || window 6 | return win.devicePixelRatio || 1 7 | } 8 | 9 | export function roundByDPR(element: Element, value: number) { 10 | const dpr = getDPR(element) 11 | return Math.round(value * dpr) / dpr 12 | } 13 | -------------------------------------------------------------------------------- /packages/react/src/utils/env.ts: -------------------------------------------------------------------------------- 1 | // Reference: https://github.com/tailwindlabs/headlessui/blob/7794d563e181787e995db1f877cd26c460e385ee/packages/%40headlessui-react/src/utils/env.ts 2 | 3 | export type RenderEnv = 'client' | 'server' 4 | 5 | class Env { 6 | current: RenderEnv = this.detect() 7 | currentId = 0 8 | 9 | set(env: RenderEnv): void { 10 | if (this.current === env) return 11 | 12 | this.currentId = 0 13 | this.current = env 14 | } 15 | 16 | reset(): void { 17 | this.set(this.detect()) 18 | } 19 | 20 | nextId() { 21 | return ++this.currentId 22 | } 23 | 24 | get isServer(): boolean { 25 | return this.current === 'server' 26 | } 27 | 28 | get isClient(): boolean { 29 | return this.current === 'client' 30 | } 31 | 32 | private detect(): RenderEnv { 33 | if (typeof window === 'undefined' || typeof document === 'undefined') { 34 | return 'server' 35 | } 36 | 37 | return 'client' 38 | } 39 | } 40 | 41 | export const env = new Env() 42 | -------------------------------------------------------------------------------- /packages/react/src/utils/match.ts: -------------------------------------------------------------------------------- 1 | // Reference: https://github.com/tailwindlabs/headlessui/blob/7794d563e181787e995db1f877cd26c460e385ee/packages/%40headlessui-react/src/utils/match.ts 2 | 3 | export function match( 4 | value: TValue, 5 | lookup: Record TReturnValue)>, 6 | ...args: any[] 7 | ): TReturnValue { 8 | if (value in lookup) { 9 | const returnValue = lookup[value] 10 | return typeof returnValue === 'function' ? returnValue(...args) : returnValue 11 | } 12 | 13 | const error = new Error( 14 | `Tried to handle "${value}" but there is no handler defined. Only defined handlers are: ${Object.keys( 15 | lookup 16 | ) 17 | .map(key => `"${key}"`) 18 | .join(', ')}.` 19 | ) 20 | if (Error.captureStackTrace) Error.captureStackTrace(error, match) 21 | throw error 22 | } 23 | -------------------------------------------------------------------------------- /packages/react/src/utils/micro-task.ts: -------------------------------------------------------------------------------- 1 | // Reference: https://github.com/tailwindlabs/headlessui/blob/7794d563e181787e995db1f877cd26c460e385ee/packages/%40headlessui-react/src/utils/micro-task.ts 2 | 3 | // Polyfill 4 | export function microTask(cb: () => void) { 5 | if (typeof queueMicrotask === 'function') { 6 | queueMicrotask(cb) 7 | } else { 8 | Promise.resolve() 9 | .then(cb) 10 | .catch(e => 11 | setTimeout(() => { 12 | throw e 13 | }) 14 | ) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/react/src/utils/owner.ts: -------------------------------------------------------------------------------- 1 | // Reference: https://github.com/tailwindlabs/headlessui/blob/226042231d7529d530be7c65790fbb681b5adb63/packages/%40headlessui-react/src/utils/owner.ts 2 | 3 | import type { MutableRefObject } from 'react' 4 | import { env } from './env' 5 | 6 | export function getOwnerDocument>( 7 | element: T | null | undefined 8 | ) { 9 | if (env.isServer) return null 10 | if (element instanceof Node) return element.ownerDocument 11 | if (element && Object.prototype.hasOwnProperty.call(element, 'current')) { 12 | if (element.current instanceof Node) return element.current.ownerDocument 13 | } 14 | 15 | return document 16 | } 17 | -------------------------------------------------------------------------------- /packages/react/test/functional/components.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from '@playwright/test' 2 | import { wait } from './utils/wait.js' 3 | 4 | test('render floating menu', async ({ page }) => { 5 | await page.goto('http://localhost:3031/menu') 6 | await page.click('[data-testid=block-menu] button') 7 | await wait(10) 8 | expect(await page.getByTestId('block-menu').screenshot()).toMatchSnapshot('menu.png') 9 | }) 10 | 11 | test('render floating listbox', async ({ page }) => { 12 | await page.goto('http://localhost:3031/listbox') 13 | await page.click('[data-testid=block-listbox] button') 14 | await wait(10) 15 | expect(await page.getByTestId('block-listbox').screenshot()).toMatchSnapshot('listbox.png') 16 | }) 17 | 18 | test('render floating combobox', async ({ page }) => { 19 | await page.goto('http://localhost:3031/combobox') 20 | await page.click('[data-testid=block-combobox] button') 21 | await wait(10) 22 | expect(await page.getByTestId('block-combobox').screenshot()).toMatchSnapshot('combobox.png') 23 | }) 24 | 25 | test('render floating dialog', async ({ page }) => { 26 | await page.goto('http://localhost:3031/dialog') 27 | await page.click('[data-testid=open-dialog-button]') 28 | await wait(310) 29 | expect(await page.getByTestId('block-dialog').screenshot()).toMatchSnapshot('dialog-open.png') 30 | await page.click('[data-testid=dialog-overlay]') 31 | await wait(210) 32 | expect(await page.getByTestId('block-dialog').screenshot()).toMatchSnapshot('dialog-close.png') 33 | }) 34 | 35 | test('render floating popover', async ({ page }) => { 36 | await page.goto('http://localhost:3031/popover') 37 | await page.click('[data-testid=block-popover] button') 38 | await wait(210) 39 | expect(await page.getByTestId('block-popover').screenshot()).toMatchSnapshot('popover.png') 40 | }) 41 | -------------------------------------------------------------------------------- /packages/react/test/functional/components.test.ts-snapshots/combobox-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/components.test.ts-snapshots/combobox-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/components.test.ts-snapshots/combobox-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/components.test.ts-snapshots/combobox-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/components.test.ts-snapshots/dialog-close-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/components.test.ts-snapshots/dialog-close-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/components.test.ts-snapshots/dialog-close-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/components.test.ts-snapshots/dialog-close-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/components.test.ts-snapshots/dialog-open-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/components.test.ts-snapshots/dialog-open-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/components.test.ts-snapshots/dialog-open-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/components.test.ts-snapshots/dialog-open-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/components.test.ts-snapshots/listbox-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/components.test.ts-snapshots/listbox-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/components.test.ts-snapshots/listbox-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/components.test.ts-snapshots/listbox-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/components.test.ts-snapshots/menu-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/components.test.ts-snapshots/menu-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/components.test.ts-snapshots/menu-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/components.test.ts-snapshots/menu-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/components.test.ts-snapshots/popover-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/components.test.ts-snapshots/popover-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/components.test.ts-snapshots/popover-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/components.test.ts-snapshots/popover-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/flip-scrollto-160-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/flip-scrollto-160-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/flip-scrollto-160-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/flip-scrollto-160-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/flip-scrollto-250-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/flip-scrollto-250-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/flip-scrollto-250-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/flip-scrollto-250-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/flip-scrollto-482-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/flip-scrollto-482-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/flip-scrollto-482-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/flip-scrollto-482-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-0-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-0-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-0-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-0-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-220-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-220-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-220-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-220-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-400-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-400-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-400-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-400-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-50-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-50-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-50-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-50-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/offset-0-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/offset-0-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/offset-0-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/offset-0-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/offset-100-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/offset-100-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/offset-100-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/offset-100-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/offset-4-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/offset-4-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/offset-4-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/offset-4-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-bottom-end-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-bottom-end-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-bottom-end-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-bottom-end-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-bottom-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-bottom-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-bottom-start-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-bottom-start-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-bottom-start-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-bottom-start-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-bottom-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-bottom-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-left-end-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-left-end-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-left-end-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-left-end-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-left-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-left-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-left-start-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-left-start-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-left-start-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-left-start-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-left-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-left-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-right-end-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-right-end-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-right-end-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-right-end-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-right-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-right-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-right-start-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-right-start-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-right-start-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-right-start-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-right-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-right-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-top-end-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-top-end-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-top-end-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-top-end-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-top-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-top-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-top-start-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-top-start-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-top-start-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-top-start-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-top-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/placement-top-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/shift-scrollto-240-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/shift-scrollto-240-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/shift-scrollto-240-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/shift-scrollto-240-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/shift-scrollto-482-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/shift-scrollto-482-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/shift-scrollto-482-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/shift-scrollto-482-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/shift-scrollto-82-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/shift-scrollto-82-linux.png -------------------------------------------------------------------------------- /packages/react/test/functional/floatingui-options.test.ts-snapshots/shift-scrollto-82-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/react/test/functional/floatingui-options.test.ts-snapshots/shift-scrollto-82-win32.png -------------------------------------------------------------------------------- /packages/react/test/functional/utils/wait.ts: -------------------------------------------------------------------------------- 1 | export function nextFrame() { 2 | return new Promise(resolve => 3 | setImmediate(() => 4 | setImmediate(resolve) 5 | ) 6 | ) 7 | } 8 | 9 | export function wait(ms: number) { 10 | return new Promise(resolve => setTimeout(resolve, ms)) 11 | } 12 | -------------------------------------------------------------------------------- /packages/react/test/unit/components.test.tsx: -------------------------------------------------------------------------------- 1 | import { Menu } from '@headlessui/react' 2 | import { Float } from '../../src/float' 3 | import { render, screen, userEvent, waitFor } from './utils/testing-library' 4 | 5 | describe('Render components', () => { 6 | it('should to render with ', async () => { 7 | render( 8 | 9 | 10 | Options 11 | 12 | 13 | 14 | Invite a friend (coming soon!) 15 | 16 | 17 | 18 | ) 19 | 20 | await waitFor() 21 | 22 | const button = screen.getByText('Options') 23 | await waitFor() 24 | expect(button).toBeInTheDocument() 25 | expect(screen.queryByRole('menu')).toBeNull() 26 | 27 | await userEvent.click(button) 28 | await waitFor() 29 | 30 | expect(screen.queryByRole('menu')).toHaveAttribute('data-headlessui-state', 'open') 31 | const menuItems = screen.queryAllByRole('menuitem') 32 | expect(menuItems).toHaveLength(3) 33 | 34 | await userEvent.click(button) 35 | await waitFor() 36 | 37 | expect(screen.queryByRole('menu')).toBeNull() 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /packages/react/test/unit/portal.test.tsx: -------------------------------------------------------------------------------- 1 | import { Menu } from '@headlessui/react' 2 | import { Float } from '../../src/float' 3 | import { render, screen, userEvent, waitFor } from './utils/testing-library' 4 | 5 | describe('Portal elements', () => { 6 | it('should to render element to portal root', async () => { 7 | render( 8 | 9 | 10 | Options 11 | 12 | 13 | 14 | 15 | 16 | 17 | ) 18 | 19 | await waitFor() 20 | 21 | const button = screen.getByText('Options') 22 | await waitFor() 23 | 24 | await userEvent.click(button) 25 | await waitFor() 26 | 27 | const menu = screen.queryByRole('menu') 28 | expect(menu).toBeInTheDocument() 29 | expect(menu?.closest('#headlessui-portal-root')).toBeTruthy() 30 | expect(menu?.innerHTML).toContain('Account settings') 31 | expect(menu?.innerHTML).toContain('Documentation') 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /packages/react/test/unit/setup.ts: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom' 2 | 3 | const ResizeObserverMock = vi.fn(() => ({ 4 | observe: vi.fn(), 5 | unobserve: vi.fn(), 6 | disconnect: vi.fn(), 7 | })) 8 | vi.stubGlobal('ResizeObserver', ResizeObserverMock) 9 | -------------------------------------------------------------------------------- /packages/react/test/unit/transition.test.tsx: -------------------------------------------------------------------------------- 1 | import { Menu } from '@headlessui/react' 2 | import { Float } from '../../src/float' 3 | import { render, screen, userEvent, waitFor } from './utils/testing-library' 4 | 5 | describe('Transition', () => { 6 | it('should to render of Headless UI', async () => { 7 | render( 8 | 9 | 10 | button 11 | content 12 | 13 | 14 | ) 15 | 16 | await waitFor() 17 | 18 | const button = screen.getByText('button') 19 | await waitFor() 20 | expect(button).toBeInTheDocument() 21 | expect(screen.queryByRole('menu')).toBeNull() 22 | 23 | await userEvent.click(button) 24 | await waitFor() 25 | 26 | expect(screen.queryByRole('menu')).toHaveAttribute('data-headlessui-state', 'open') 27 | 28 | await userEvent.click(button) 29 | await waitFor() 30 | 31 | expect(screen.queryByRole('menu')).toBeNull() 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /packages/react/test/unit/utils/testing-library.ts: -------------------------------------------------------------------------------- 1 | import type { ReactElement } from 'react' 2 | import { cleanup, render, waitFor } from '@testing-library/react' 3 | import type { Queries } from '@testing-library/dom' 4 | import { afterEach } from 'vitest' 5 | 6 | afterEach(() => { 7 | cleanup() 8 | }) 9 | 10 | const customRender = (ui: ReactElement, options = {}) => 11 | render(ui, { 12 | // wrap provider(s) here if needed 13 | wrapper: ({ children }) => children, 14 | ...options, 15 | }) 16 | 17 | const promisedWaitFor = () => new Promise(resolve => waitFor(resolve)) 18 | const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)) 19 | 20 | export * from '@testing-library/react' 21 | export { default as userEvent } from '@testing-library/user-event' 22 | export { 23 | customRender as render, 24 | promisedWaitFor as waitFor, 25 | wait, 26 | } 27 | -------------------------------------------------------------------------------- /packages/react/test/unit/virtual-element.test.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | import { Float, type FloatVirtualInitialProps } from '../../src/float' 3 | import { render, screen, waitFor } from './utils/testing-library' 4 | 5 | describe('Render virtual elements', () => { 6 | it('should to render ', async () => { 7 | function onInitial({ refs }: FloatVirtualInitialProps) { 8 | useEffect(() => { 9 | refs.setPositionReference({ 10 | getBoundingClientRect() { 11 | return { 12 | width: 0, 13 | height: 0, 14 | x: 300, 15 | y: 120, 16 | top: 120, 17 | left: 300, 18 | right: 300, 19 | bottom: 120, 20 | } 21 | }, 22 | }) 23 | }, []) 24 | } 25 | 26 | render( 27 | 28 |
content
29 |
30 | ) 31 | 32 | await waitFor() 33 | 34 | expect(screen.getByTestId('content').innerHTML).toBe('content') 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /packages/react/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 5 | "target": "ESNext", 6 | "jsx": "react-jsx", 7 | "lib": ["ESNext", "DOM", "DOM.Iterable"], 8 | "useDefineForClassFields": true, 9 | "module": "ESNext", 10 | "moduleResolution": "Node", 11 | "types": ["node", "vite/client"], 12 | "strict": true, 13 | "noImplicitThis": true, 14 | "allowSyntheticDefaultImports": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "verbatimModuleSyntax": true, 17 | "skipLibCheck": true 18 | }, 19 | "include": ["src/**/*"] 20 | } 21 | -------------------------------------------------------------------------------- /packages/react/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "jsx": "react-jsx", 5 | "lib": ["ESNext", "DOM", "DOM.Iterable"], 6 | "useDefineForClassFields": true, 7 | "module": "ESNext", 8 | "moduleResolution": "Node", 9 | "types": ["node", "vite/client"], 10 | "strict": true, 11 | "noImplicitThis": true, 12 | "outDir": "dist", 13 | "allowSyntheticDefaultImports": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "verbatimModuleSyntax": true, 16 | "skipLibCheck": true 17 | }, 18 | "include": ["src/**/*"] 19 | } 20 | -------------------------------------------------------------------------------- /packages/react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "references": [ 3 | { "path": "./tsconfig.app.json" }, 4 | { "path": "./tsconfig.node.json" }, 5 | { "path": "./tsconfig.build.json" }, 6 | { "path": "./tsconfig.vitest.json" }, 7 | { "path": "./tsconfig.playwright.json" } 8 | ], 9 | "files": [] 10 | } 11 | -------------------------------------------------------------------------------- /packages/react/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", 5 | "target": "ES2022", 6 | "lib": ["ES2023"], 7 | "moduleDetection": "force", 8 | "module": "ESNext", 9 | "moduleResolution": "Bundler", 10 | "types": ["node"], 11 | "strict": true, 12 | "noEmit": true, 13 | "esModuleInterop": true, 14 | "skipLibCheck": true 15 | }, 16 | "include": [ 17 | "vite.config.ts", 18 | "vitest.config.ts", 19 | "playwright.config.ts" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /packages/react/tsconfig.playwright.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.node.json", 3 | "compilerOptions": { 4 | "composite": true, 5 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.playwright.tsbuildinfo", 6 | "lib": [], 7 | "types": ["node"] 8 | }, 9 | "include": ["test/functional/**/*"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/react/tsconfig.vitest.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.app.json", 3 | "compilerOptions": { 4 | "composite": true, 5 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.vitest.tsbuildinfo", 6 | "lib": [], 7 | "types": ["node", "jsdom", "vitest/globals"] 8 | }, 9 | "include": ["src/**/*", "test/unit/**/*"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/react/vite.config.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path' 2 | import { defineConfig } from 'vite' 3 | import React from '@vitejs/plugin-react' 4 | 5 | export default defineConfig({ 6 | plugins: [ 7 | React(), 8 | ], 9 | build: { 10 | lib: { 11 | entry: path.resolve(__dirname, 'src/index.ts'), 12 | formats: ['es', 'cjs'], 13 | fileName: format => format === 'cjs' ? 'headlessui-float.cjs' : 'headlessui-float.mjs', 14 | }, 15 | rollupOptions: { 16 | external: [ 17 | 'react', 18 | 'react-dom', 19 | '@headlessui/react', 20 | '@floating-ui/core', 21 | '@floating-ui/dom', 22 | '@floating-ui/react', 23 | ], 24 | }, 25 | }, 26 | }) 27 | -------------------------------------------------------------------------------- /packages/react/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { mergeConfig } from 'vite' 2 | import { defineConfig } from 'vitest/config' 3 | import viteConfig from './vite.config' 4 | 5 | export default mergeConfig( 6 | viteConfig, 7 | defineConfig({ 8 | test: { 9 | globals: true, 10 | environment: 'jsdom', 11 | setupFiles: './setup.ts', 12 | }, 13 | }), 14 | ) 15 | -------------------------------------------------------------------------------- /packages/vue/LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2022-present Lucas Yang 4 | 5 | > Permission is hereby granted, free of charge, to any person obtaining a copy 6 | > of this software and associated documentation files (the "Software"), to deal 7 | > in the Software without restriction, including without limitation the rights 8 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | > copies of the Software, and to permit persons to whom the Software is 10 | > furnished to do so, subject to the following conditions: 11 | > 12 | > The above copyright notice and this permission notice shall be included in 13 | > all copies or substantial portions of the Software. 14 | > 15 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | > THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@headlessui-float/vue", 3 | "type": "module", 4 | "version": "0.15.0", 5 | "description": "Easily use Headless UI for Vue 3 with Floating UI (Popper.js)", 6 | "license": "MIT", 7 | "homepage": "https://headlessui-float.vercel.app", 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/ycs77/headlessui-float.git", 11 | "directory": "packages/vue" 12 | }, 13 | "keywords": [ 14 | "floating", 15 | "popper", 16 | "popover", 17 | "tooltip", 18 | "dropdown", 19 | "headless", 20 | "headlessui", 21 | "vue" 22 | ], 23 | "sideEffects": false, 24 | "exports": { 25 | ".": { 26 | "types": "./dist/index.d.ts", 27 | "import": "./dist/headlessui-float.mjs", 28 | "require": "./dist/headlessui-float.cjs" 29 | } 30 | }, 31 | "main": "./dist/headlessui-float.cjs", 32 | "module": "./dist/headlessui-float.mjs", 33 | "types": "./dist/index.d.ts", 34 | "files": [ 35 | "dist" 36 | ], 37 | "publishConfig": { 38 | "access": "public" 39 | }, 40 | "scripts": { 41 | "build": "sh scripts/build.sh", 42 | "watch": "vite build --watch", 43 | "type-check": "vue-tsc --noEmit", 44 | "test:unit": "vitest --root test/unit/", 45 | "test:functional": "playwright test test/functional", 46 | "test:functional:update": "yarn test:functional -u", 47 | "prepack": "npm run build" 48 | }, 49 | "peerDependencies": { 50 | "@headlessui/vue": "^1.0.0", 51 | "vue": "^3.0.0" 52 | }, 53 | "dependencies": { 54 | "@floating-ui/core": "^1.5.3", 55 | "@floating-ui/dom": "^1.5.4", 56 | "@floating-ui/vue": "^1.0.3" 57 | }, 58 | "devDependencies": { 59 | "@headlessui/vue": "^1.7.22", 60 | "@playwright/test": "^1.30.0", 61 | "@testing-library/jest-dom": "^6.2.0", 62 | "@testing-library/user-event": "^14.5.2", 63 | "@testing-library/vue": "^8.0.0", 64 | "@types/jsdom": "^21.1.7", 65 | "@types/node": "^22.0.0", 66 | "@vitejs/plugin-vue": "^5.0.0", 67 | "jsdom": "^23.2.0", 68 | "typescript": "~5.4.0", 69 | "unplugin-vue-components": "^0.26.0", 70 | "vite": "^5.4.12", 71 | "vitest": "^2.0.0", 72 | "vue": "^3.5.0", 73 | "vue-tsc": "^2.2.0" 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /packages/vue/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import type { PlaywrightTestConfig } from '@playwright/test' 2 | 3 | export default { 4 | use: { 5 | launchOptions: { 6 | ignoreDefaultArgs: ['--hide-scrollbars'], 7 | }, 8 | }, 9 | webServer: { 10 | command: 'yarn workspace headlessui-float-example-vue-ts dev --port=3032', 11 | port: 3032, 12 | timeout: 120 * 1000, 13 | reuseExistingServer: !process.env.CI, 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /packages/vue/scripts/build.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | 3 | rm dist -rf 4 | 5 | vue-tsc --noEmit && \ 6 | vite build && \ 7 | vue-tsc --declaration --emitDeclarationOnly -p tsconfig.build.json 8 | -------------------------------------------------------------------------------- /packages/vue/src/class-resolvers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './tailwindcss' 2 | export * from './tailwindcss-rtl' 3 | export * from './type' 4 | -------------------------------------------------------------------------------- /packages/vue/src/class-resolvers/tailwindcss-rtl.ts: -------------------------------------------------------------------------------- 1 | import type { ClassResolver } from './type' 2 | 3 | export const tailwindcssRtlOriginSafelist = [ 4 | 'origin-bottom', 5 | 'origin-top', 6 | 'ltr:origin-right rtl:origin-left', 7 | 'ltr:origin-left rtl:origin-right', 8 | 'ltr:origin-bottom-left rtl:origin-bottom-right', 9 | 'ltr:origin-bottom-right rtl:origin-bottom-left', 10 | 'ltr:origin-top-left rtl:origin-top-right', 11 | 'ltr:origin-top-right rtl:origin-top-left', 12 | ] 13 | 14 | export const tailwindcssRtlOriginClassResolver: ClassResolver = placement => { 15 | switch (placement) { 16 | case 'top': 17 | return 'origin-bottom' 18 | case 'bottom': 19 | return 'origin-top' 20 | case 'left': 21 | return 'ltr:origin-right rtl:origin-left' 22 | case 'right': 23 | return 'ltr:origin-left rtl:origin-right' 24 | case 'top-start': 25 | case 'right-end': 26 | return 'ltr:origin-bottom-left rtl:origin-bottom-right' 27 | case 'top-end': 28 | case 'left-end': 29 | return 'ltr:origin-bottom-right rtl:origin-bottom-left' 30 | case 'right-start': 31 | case 'bottom-start': 32 | return 'ltr:origin-top-left rtl:origin-top-right' 33 | case 'left-start': 34 | case 'bottom-end': 35 | return 'ltr:origin-top-right rtl:origin-top-left' 36 | default: 37 | return 'origin-center' 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/vue/src/class-resolvers/tailwindcss.ts: -------------------------------------------------------------------------------- 1 | import type { ClassResolver } from './type' 2 | 3 | export const tailwindcssOriginSafelist = [ 4 | 'origin-bottom', 5 | 'origin-top', 6 | 'origin-right', 7 | 'origin-left', 8 | 'origin-bottom-left', 9 | 'origin-bottom-right', 10 | 'origin-top-left', 11 | 'origin-top-right', 12 | ] 13 | 14 | export const tailwindcssOriginClassResolver: ClassResolver = placement => { 15 | switch (placement) { 16 | case 'top': 17 | return 'origin-bottom' 18 | case 'bottom': 19 | return 'origin-top' 20 | case 'left': 21 | return 'origin-right' 22 | case 'right': 23 | return 'origin-left' 24 | case 'top-start': 25 | case 'right-end': 26 | return 'origin-bottom-left' 27 | case 'top-end': 28 | case 'left-end': 29 | return 'origin-bottom-right' 30 | case 'right-start': 31 | case 'bottom-start': 32 | return 'origin-top-left' 33 | case 'left-start': 34 | case 'bottom-end': 35 | return 'origin-top-right' 36 | default: 37 | return 'origin-center' 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/vue/src/class-resolvers/type.ts: -------------------------------------------------------------------------------- 1 | import type { Placement } from '@floating-ui/dom' 2 | 3 | export type ClassResolver = (placement: Placement) => string 4 | 5 | /** @deprecated Replace to using `ClassResolver` */ 6 | export type OriginClassResolver = ClassResolver 7 | -------------------------------------------------------------------------------- /packages/vue/src/createHighOrderFloatComponent.ts: -------------------------------------------------------------------------------- 1 | import { defineComponent, h, mergeProps } from 'vue' 2 | import { Float } from './float' 3 | import type { FloatProps } from './float' 4 | 5 | export function createHighOrderFloatComponent(baseProps: FloatProps) { 6 | const HighOrderFloat = defineComponent({ 7 | name: 'HighOrderFloat', 8 | setup(overrideProps, { slots }) { 9 | return () => h(Float, mergeProps( 10 | baseProps as Record, 11 | overrideProps as Record 12 | ), slots) 13 | }, 14 | }) 15 | 16 | return HighOrderFloat as unknown as typeof Float 17 | } 18 | -------------------------------------------------------------------------------- /packages/vue/src/hooks/use-document-event.ts: -------------------------------------------------------------------------------- 1 | // Reference: https://github.com/tailwindlabs/headlessui/blob/7794d563e181787e995db1f877cd26c460e385ee/packages/%40headlessui-vue/src/hooks/use-document-event.ts 2 | 3 | import { watchEffect } from 'vue' 4 | import { env } from '../utils/env' 5 | 6 | export function useDocumentEvent( 7 | type: TType, 8 | listener: (this: Document, ev: DocumentEventMap[TType]) => any, 9 | options?: boolean | AddEventListenerOptions 10 | ) { 11 | if (env.isServer) return 12 | 13 | watchEffect(onInvalidate => { 14 | document.addEventListener(type, listener, options) 15 | onInvalidate(() => document.removeEventListener(type, listener, options)) 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /packages/vue/src/hooks/use-reference-el-resize-observer.ts: -------------------------------------------------------------------------------- 1 | import { type ComputedRef, type Ref, onBeforeUnmount, onMounted } from 'vue' 2 | import type { ReferenceElement } from '@floating-ui/dom' 3 | import { env } from '../utils/env' 4 | 5 | export function useReferenceElResizeObserver( 6 | enabled: boolean | undefined, 7 | referenceEl: ComputedRef, 8 | referenceElWidth: Ref, 9 | ) { 10 | let cleanupResizeObserver: () => void = () => {} 11 | 12 | onMounted(() => { 13 | if (enabled && 14 | env.isClient && 15 | typeof ResizeObserver !== 'undefined' && 16 | referenceEl.value && 17 | referenceEl.value instanceof Element 18 | ) { 19 | const observer = new ResizeObserver(([entry]) => { 20 | referenceElWidth.value = entry.borderBoxSize.reduce((acc, { inlineSize }) => acc + inlineSize, 0) 21 | }) 22 | observer.observe(referenceEl.value) 23 | 24 | cleanupResizeObserver = () => { 25 | observer.disconnect() 26 | referenceElWidth.value = null 27 | } 28 | } 29 | }) 30 | 31 | onBeforeUnmount(() => { 32 | cleanupResizeObserver() 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /packages/vue/src/hooks/use-transition-and-origin-class.ts: -------------------------------------------------------------------------------- 1 | import { type Ref, computed } from 'vue' 2 | import type { Placement } from '@floating-ui/core' 3 | import { type ClassResolver, tailwindcssOriginClassResolver } from '../class-resolvers' 4 | 5 | export function useTransitionAndOriginClass(props: { 6 | enter?: string 7 | leave?: string 8 | originClass?: string | ClassResolver 9 | tailwindcssOriginClass?: boolean 10 | }, placement: Ref) { 11 | const originClassRef = computed(() => { 12 | if (typeof props.originClass === 'function') { 13 | return props.originClass(placement.value) 14 | } else if (typeof props.originClass === 'string') { 15 | return props.originClass 16 | } else if (props.tailwindcssOriginClass) { 17 | return tailwindcssOriginClassResolver(placement.value) 18 | } 19 | return undefined 20 | }) 21 | 22 | const enterActiveClassRef = computed(() => 23 | props.enter || originClassRef.value 24 | ? `${props.enter || ''} ${originClassRef.value || ''}` 25 | : undefined 26 | ) 27 | 28 | const leaveActiveClassRef = computed(() => 29 | props.leave || originClassRef.value 30 | ? `${props.leave || ''} ${originClassRef.value || ''}` 31 | : undefined 32 | ) 33 | 34 | return { originClassRef, enterActiveClassRef, leaveActiveClassRef } 35 | } 36 | -------------------------------------------------------------------------------- /packages/vue/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './float' 2 | export * from './class-resolvers' 3 | export { useOutsideClick } from './hooks/use-outside-click' 4 | export { createHighOrderFloatComponent } from './createHighOrderFloatComponent' 5 | export * from './resolver' 6 | -------------------------------------------------------------------------------- /packages/vue/src/resolver.ts: -------------------------------------------------------------------------------- 1 | /** @ts-ignore */ 2 | import type { ComponentResolver } from 'unplugin-vue-components' 3 | 4 | const components = [ 5 | 'Float', 6 | 'FloatArrow', 7 | 'FloatContent', 8 | 'FloatReference', 9 | ] 10 | 11 | export interface HeadlessUiFloatResolverOptions { 12 | prefix?: string 13 | } 14 | 15 | export function HeadlessUiFloatResolver(options: HeadlessUiFloatResolverOptions = {}): ComponentResolver { 16 | const { prefix = '' } = options 17 | return { 18 | type: 'component', 19 | resolve: (name: string) => { 20 | if (name.startsWith(prefix)) { 21 | const componentName = name.substring(prefix.length) 22 | if (components.includes(componentName)) { 23 | return { 24 | name: componentName, 25 | from: '@headlessui-float/vue', 26 | } 27 | } 28 | } 29 | }, 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/vue/src/types.ts: -------------------------------------------------------------------------------- 1 | import type { VirtualElement } from '@floating-ui/dom' 2 | 3 | export type ReferenceElement = HTMLElement | VirtualElement 4 | 5 | export type FloatingElement = HTMLElement 6 | 7 | // Using the `__VLS_WithTemplateSlots` type to provide vue component slots type. 8 | // Copied from code generated by Volar. 9 | export type __VLS_WithTemplateSlots = T & { 10 | new (): { 11 | $slots: S 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/vue/src/utils/dom.ts: -------------------------------------------------------------------------------- 1 | // Reference: https://github.com/tailwindlabs/headlessui/blob/d4a94cb5647d9e11ffc72d92033d43cbc3361da7/packages/%40headlessui-vue/src/utils/dom.ts 2 | 3 | import type { VirtualElement } from '@floating-ui/dom' 4 | import { ref as createRef } from 'vue' 5 | import type { ComponentPublicInstance, Ref } from 'vue' 6 | 7 | type AsElement = 8 | | (T extends HTMLElement ? T : HTMLElement) 9 | | null 10 | 11 | export function dom( 12 | ref?: Ref | null 13 | ): AsElement | null { 14 | if (ref == null) return null 15 | if (ref.value == null) return null 16 | 17 | // In this case we check for `Node` because returning `null` from a 18 | // component renders a `Comment` which is a `Node` but not `Element` 19 | // The types don't encode this possibility but we handle it here at runtime 20 | if (ref.value instanceof Node) { 21 | return ref.value as AsElement 22 | } 23 | 24 | // Recursion call dom to get element in nested component 25 | if ('$el' in ref.value && ref.value.$el) { 26 | return dom(createRef(ref.value.$el)) 27 | } 28 | 29 | // Pass `VirtualElement` for Floating UI 30 | if ('getBoundingClientRect' in ref.value) { 31 | return ref.value as AsElement 32 | } 33 | 34 | return null 35 | } 36 | -------------------------------------------------------------------------------- /packages/vue/src/utils/dpr.ts: -------------------------------------------------------------------------------- 1 | export function getDPR(element: Element) { 2 | if (typeof window === 'undefined') { 3 | return 1 4 | } 5 | const win = element.ownerDocument.defaultView || window 6 | return win.devicePixelRatio || 1 7 | } 8 | 9 | export function roundByDPR(element: Element, value: number) { 10 | const dpr = getDPR(element) 11 | return Math.round(value * dpr) / dpr 12 | } 13 | -------------------------------------------------------------------------------- /packages/vue/src/utils/env.ts: -------------------------------------------------------------------------------- 1 | // Reference: https://github.com/tailwindlabs/headlessui/blob/c7f6bc60ed2ab6c84fb080b0f419ed16824c880d/packages/%40headlessui-vue/src/utils/env.ts 2 | 3 | type RenderEnv = 'client' | 'server' 4 | 5 | class Env { 6 | current: RenderEnv = this.detect() 7 | 8 | set(env: RenderEnv): void { 9 | if (this.current === env) return 10 | 11 | this.current = env 12 | } 13 | 14 | reset(): void { 15 | this.set(this.detect()) 16 | } 17 | 18 | get isServer(): boolean { 19 | return this.current === 'server' 20 | } 21 | 22 | get isClient(): boolean { 23 | return this.current === 'client' 24 | } 25 | 26 | private detect(): RenderEnv { 27 | if (typeof window === 'undefined' || typeof document === 'undefined') { 28 | return 'server' 29 | } 30 | 31 | return 'client' 32 | } 33 | } 34 | 35 | export const env = new Env() 36 | -------------------------------------------------------------------------------- /packages/vue/src/utils/match.ts: -------------------------------------------------------------------------------- 1 | // Reference: https://github.com/tailwindlabs/headlessui/blob/7794d563e181787e995db1f877cd26c460e385ee/packages/%40headlessui-vue/src/utils/match.ts 2 | 3 | export function match( 4 | value: TValue, 5 | lookup: Record TReturnValue)>, 6 | ...args: any[] 7 | ): TReturnValue { 8 | if (value in lookup) { 9 | const returnValue = lookup[value] 10 | return typeof returnValue === 'function' ? returnValue(...args) : returnValue 11 | } 12 | 13 | const error = new Error( 14 | `Tried to handle "${value}" but there is no handler defined. Only defined handlers are: ${Object.keys( 15 | lookup 16 | ) 17 | .map(key => `"${key}"`) 18 | .join(', ')}.` 19 | ) 20 | if (Error.captureStackTrace) Error.captureStackTrace(error, match) 21 | throw error 22 | } 23 | -------------------------------------------------------------------------------- /packages/vue/src/utils/owner.ts: -------------------------------------------------------------------------------- 1 | // Reference: https://github.com/tailwindlabs/headlessui/blob/226042231d7529d530be7c65790fbb681b5adb63/packages/%40headlessui-vue/src/utils/owner.ts 2 | 3 | import type { Ref } from 'vue' 4 | import { dom } from './dom' 5 | import { env } from './env' 6 | 7 | export function getOwnerDocument>( 8 | element: T | null | undefined 9 | ) { 10 | if (env.isServer) return null 11 | if (element instanceof Node) return element.ownerDocument 12 | if (element && Object.prototype.hasOwnProperty.call(element, 'value')) { 13 | const domElement = dom(element as any) 14 | if (domElement) return domElement.ownerDocument 15 | } 16 | 17 | return document 18 | } 19 | -------------------------------------------------------------------------------- /packages/vue/src/utils/render.ts: -------------------------------------------------------------------------------- 1 | import { Fragment, type VNode, unref } from 'vue' 2 | 3 | export function flattenFragment(nodes: VNode[]): VNode[] { 4 | return nodes.reduce((carry, node) => { 5 | if (node.type === Fragment) { 6 | return carry.concat(flattenFragment(node.children as VNode[])) 7 | } 8 | return carry.concat(node) 9 | }, []) 10 | } 11 | 12 | // Reference: https://github.com/tailwindlabs/headlessui/blob/d8424fe311923f6858f6e3d55083df957bca824d/packages/%40headlessui-vue/src/utils/render.ts#L139-L145 13 | export function isValidElement(input: any): boolean { 14 | if (input == null) return false // No children 15 | if (typeof input.type === 'string') return true // 'div', 'span', ... 16 | if (typeof input.type === 'object') return true // Other components 17 | if (typeof input.type === 'function') return true // Built-ins like Transition 18 | return false // Comments, strings, ... 19 | } 20 | 21 | export function isVisibleDOMElement(input: any): boolean { 22 | input = unref(input) 23 | return input && input?.nodeType !== Node.COMMENT_NODE 24 | } 25 | -------------------------------------------------------------------------------- /packages/vue/src/utils/warn.ts: -------------------------------------------------------------------------------- 1 | export function showVueTransitionWarn(component: string, props: { 2 | vueTransition?: boolean 3 | transitionName?: string 4 | transitionType?: 'transition' | 'animation' 5 | } & Record) { 6 | if (!props.vueTransition && (props.transitionName || props.transitionType)) { 7 | console.warn(`[headlessui-float]: <${component} /> pass "transition-name" or "transition-type" prop, must be set "vue-transition" prop.`) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/vue/test/functional/components.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from '@playwright/test' 2 | import { wait } from './utils/wait.js' 3 | 4 | test('render floating menu', async ({ page }) => { 5 | await page.goto('http://localhost:3032/menu') 6 | await page.click('[data-testid=block-menu] button') 7 | await wait(10) 8 | expect(await page.getByTestId('block-menu').screenshot()).toMatchSnapshot('menu.png') 9 | }) 10 | 11 | test('render floating listbox', async ({ page }) => { 12 | await page.goto('http://localhost:3032/listbox') 13 | await page.click('[data-testid=block-listbox] button') 14 | await wait(10) 15 | expect(await page.getByTestId('block-listbox').screenshot()).toMatchSnapshot('listbox.png') 16 | }) 17 | 18 | test('render floating combobox', async ({ page }) => { 19 | await page.goto('http://localhost:3032/combobox') 20 | await page.click('[data-testid=block-combobox] button') 21 | await wait(10) 22 | expect(await page.getByTestId('block-combobox').screenshot()).toMatchSnapshot('combobox.png') 23 | }) 24 | 25 | test('render floating dialog', async ({ page }) => { 26 | await page.goto('http://localhost:3032/dialog') 27 | await page.click('[data-testid=open-dialog-button]') 28 | await wait(310) 29 | expect(await page.getByTestId('block-dialog').screenshot()).toMatchSnapshot('dialog-open.png') 30 | await page.click('[data-testid=dialog-overlay]') 31 | await wait(210) 32 | expect(await page.getByTestId('block-dialog').screenshot()).toMatchSnapshot('dialog-close.png') 33 | }) 34 | 35 | test('render floating popover', async ({ page }) => { 36 | await page.goto('http://localhost:3032/popover') 37 | await page.click('[data-testid=block-popover] button') 38 | await wait(210) 39 | expect(await page.getByTestId('block-popover').screenshot()).toMatchSnapshot('popover.png') 40 | }) 41 | -------------------------------------------------------------------------------- /packages/vue/test/functional/components.test.ts-snapshots/combobox-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/components.test.ts-snapshots/combobox-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/components.test.ts-snapshots/combobox-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/components.test.ts-snapshots/combobox-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/components.test.ts-snapshots/dialog-close-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/components.test.ts-snapshots/dialog-close-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/components.test.ts-snapshots/dialog-close-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/components.test.ts-snapshots/dialog-close-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/components.test.ts-snapshots/dialog-open-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/components.test.ts-snapshots/dialog-open-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/components.test.ts-snapshots/dialog-open-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/components.test.ts-snapshots/dialog-open-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/components.test.ts-snapshots/listbox-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/components.test.ts-snapshots/listbox-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/components.test.ts-snapshots/listbox-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/components.test.ts-snapshots/listbox-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/components.test.ts-snapshots/menu-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/components.test.ts-snapshots/menu-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/components.test.ts-snapshots/menu-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/components.test.ts-snapshots/menu-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/components.test.ts-snapshots/popover-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/components.test.ts-snapshots/popover-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/components.test.ts-snapshots/popover-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/components.test.ts-snapshots/popover-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/flip-scrollto-160-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/flip-scrollto-160-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/flip-scrollto-160-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/flip-scrollto-160-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/flip-scrollto-250-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/flip-scrollto-250-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/flip-scrollto-250-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/flip-scrollto-250-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/flip-scrollto-482-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/flip-scrollto-482-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/flip-scrollto-482-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/flip-scrollto-482-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-0-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-0-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-0-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-0-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-220-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-220-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-220-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-220-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-400-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-400-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-400-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-400-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-50-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-50-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-50-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/hide-scrollto-50-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/offset-0-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/offset-0-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/offset-0-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/offset-0-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/offset-100-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/offset-100-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/offset-100-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/offset-100-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/offset-4-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/offset-4-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/offset-4-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/offset-4-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-bottom-end-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-bottom-end-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-bottom-end-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-bottom-end-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-bottom-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-bottom-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-bottom-start-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-bottom-start-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-bottom-start-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-bottom-start-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-bottom-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-bottom-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-left-end-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-left-end-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-left-end-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-left-end-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-left-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-left-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-left-start-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-left-start-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-left-start-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-left-start-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-left-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-left-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-right-end-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-right-end-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-right-end-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-right-end-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-right-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-right-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-right-start-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-right-start-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-right-start-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-right-start-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-right-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-right-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-top-end-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-top-end-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-top-end-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-top-end-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-top-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-top-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-top-start-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-top-start-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-top-start-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-top-start-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-top-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/placement-top-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/shift-scrollto-240-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/shift-scrollto-240-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/shift-scrollto-240-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/shift-scrollto-240-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/shift-scrollto-482-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/shift-scrollto-482-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/shift-scrollto-482-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/shift-scrollto-482-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/shift-scrollto-82-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/shift-scrollto-82-linux.png -------------------------------------------------------------------------------- /packages/vue/test/functional/floatingui-options.test.ts-snapshots/shift-scrollto-82-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycs77/headlessui-float/4c3a682e1b5e14f09926c4f72035c06185b927a4/packages/vue/test/functional/floatingui-options.test.ts-snapshots/shift-scrollto-82-win32.png -------------------------------------------------------------------------------- /packages/vue/test/functional/utils/wait.ts: -------------------------------------------------------------------------------- 1 | export function nextFrame() { 2 | return new Promise(resolve => 3 | setImmediate(() => 4 | setImmediate(resolve) 5 | ) 6 | ) 7 | } 8 | 9 | export function wait(ms: number) { 10 | return new Promise(resolve => setTimeout(resolve, ms)) 11 | } 12 | -------------------------------------------------------------------------------- /packages/vue/test/unit/components.test.ts: -------------------------------------------------------------------------------- 1 | import { defineComponent } from 'vue' 2 | import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/vue' 3 | import { Float } from '../../src/float' 4 | import { render, screen, userEvent, wait } from './utils/testing-library' 5 | import { html } from './utils/html' 6 | 7 | describe('Render components', () => { 8 | it('should to render with ', async () => { 9 | render(defineComponent({ 10 | components: { Menu, MenuButton, MenuItem, MenuItems, Float }, 11 | template: html` 12 | 13 | 14 | Options 15 | 16 | 17 | 18 | Invite a friend (coming soon!) 19 | 20 | 21 | 22 | `, 23 | })) 24 | 25 | const button = screen.getByText('Options') 26 | expect(button).toBeInTheDocument() 27 | expect(screen.queryByRole('menu')).toBeNull() 28 | 29 | await userEvent.click(button) 30 | await wait(50) 31 | 32 | expect(screen.queryByRole('menu')).toHaveAttribute('data-headlessui-state', 'open') 33 | const menuItems = screen.queryAllByRole('menuitem') 34 | expect(menuItems).toHaveLength(3) 35 | 36 | await userEvent.click(button) 37 | await wait(50) 38 | 39 | expect(screen.queryByRole('menu')).toBeNull() 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /packages/vue/test/unit/portal.test.ts: -------------------------------------------------------------------------------- 1 | import { defineComponent } from 'vue' 2 | import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/vue' 3 | import { Float } from '../../src/float' 4 | import { render, screen, userEvent, wait } from './utils/testing-library' 5 | import { html } from './utils/html' 6 | 7 | describe('Portal elements', () => { 8 | it('should to render element to portal root', async () => { 9 | render(defineComponent({ 10 | components: { Menu, MenuButton, MenuItem, MenuItems, Float }, 11 | template: html` 12 | 13 | 14 | Options 15 | 16 | 17 | 18 | 19 | 20 | 21 | `, 22 | })) 23 | 24 | const button = screen.getByText('Options') 25 | await userEvent.click(button) 26 | await wait(50) 27 | 28 | const menu = screen.queryByRole('menu') 29 | expect(menu).toBeInTheDocument() 30 | expect(menu?.closest('#headlessui-portal-root')).toBeTruthy() 31 | expect(menu?.innerHTML).toContain('Account settings') 32 | expect(menu?.innerHTML).toContain('Documentation') 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /packages/vue/test/unit/setup.ts: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom' 2 | 3 | const ResizeObserverMock = vi.fn(() => ({ 4 | observe: vi.fn(), 5 | unobserve: vi.fn(), 6 | disconnect: vi.fn(), 7 | })) 8 | vi.stubGlobal('ResizeObserver', ResizeObserverMock) 9 | -------------------------------------------------------------------------------- /packages/vue/test/unit/ssr.test.ts: -------------------------------------------------------------------------------- 1 | import { defineComponent } from 'vue' 2 | import { Float } from '../../src/float' 3 | import { renderHydrate, renderSSR } from './utils/ssr' 4 | import { html } from './utils/html' 5 | 6 | const Example = defineComponent({ 7 | components: { Float }, 8 | template: html` 9 | 10 | 11 |
content
12 |
13 | `, 14 | }) 15 | 16 | describe('SSR', () => { 17 | it('should to render on SSR', async () => { 18 | const { contents } = await renderSSR(Example) 19 | 20 | expect(contents).toContain('button') 21 | expect(contents).not.toContain('content') 22 | expect(contents).toContain('
') 23 | }) 24 | 25 | it('should to hydrate with show content', async () => { 26 | const { contents } = await renderHydrate(Example, { show: true }) 27 | 28 | expect(contents).toContain('button') 29 | expect(contents).toContain('content') 30 | expect(contents).toContain('
') 31 | }) 32 | 33 | it('should to hydrate with not show content', async () => { 34 | const { contents } = await renderHydrate(Example, { show: false }) 35 | 36 | expect(contents).toContain('button') 37 | expect(contents).not.toContain('content') 38 | expect(contents).toContain('
') 39 | }) 40 | }) 41 | -------------------------------------------------------------------------------- /packages/vue/test/unit/transition.test.ts: -------------------------------------------------------------------------------- 1 | import { defineComponent } from 'vue' 2 | import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/vue' 3 | import { Float } from '../../src/float' 4 | import { render, screen, userEvent, wait } from './utils/testing-library' 5 | import { html } from './utils/html' 6 | 7 | describe('Transition', () => { 8 | it('should to render of Headless UI', async () => { 9 | render(defineComponent({ 10 | components: { Menu, MenuButton, MenuItem, MenuItems, Float }, 11 | template: html` 12 | 13 | 14 | button 15 | content 16 | 17 | 18 | `, 19 | })) 20 | 21 | const button = screen.getByText('button') 22 | expect(button).toBeInTheDocument() 23 | expect(screen.queryByRole('menu')).toBeNull() 24 | 25 | await userEvent.click(button) 26 | await wait(50) 27 | 28 | expect(screen.queryByRole('menu')).toHaveAttribute('data-headlessui-state', 'open') 29 | 30 | await userEvent.click(button) 31 | await wait(50) 32 | 33 | expect(screen.queryByRole('menu')).toBeNull() 34 | }) 35 | 36 | it('should to render of Vue', async () => { 37 | render(defineComponent({ 38 | components: { Menu, MenuButton, MenuItem, MenuItems, Float }, 39 | template: html` 40 | 41 | 42 | button 43 | content 44 | 45 | 46 | `, 47 | })) 48 | 49 | const button = screen.getByText('button') 50 | expect(button).toBeInTheDocument() 51 | expect(screen.queryByRole('menu')).toBeNull() 52 | 53 | await userEvent.click(button) 54 | await wait(50) 55 | 56 | expect(screen.queryByRole('menu')).toHaveAttribute('data-headlessui-state', 'open') 57 | 58 | await userEvent.click(button) 59 | await wait(50) 60 | 61 | expect(screen.queryByRole('menu')).toBeNull() 62 | }) 63 | }) 64 | -------------------------------------------------------------------------------- /packages/vue/test/unit/utils/html.ts: -------------------------------------------------------------------------------- 1 | export function jsx(templates: TemplateStringsArray) { 2 | return templates.join('') 3 | } 4 | 5 | export function html(templates: TemplateStringsArray) { 6 | return templates.join('') 7 | } 8 | -------------------------------------------------------------------------------- /packages/vue/test/unit/utils/ssr.ts: -------------------------------------------------------------------------------- 1 | // Reference: https://github.com/tailwindlabs/headlessui/blob/c7f6bc60ed2ab6c84fb080b0f419ed16824c880d/packages/%40headlessui-vue/src/test-utils/ssr.ts 2 | 3 | import { createApp, createSSRApp, nextTick } from 'vue' 4 | import { renderToString } from 'vue/server-renderer' 5 | import { env } from '../../../src/utils/env' 6 | 7 | export async function renderSSR(component: any, props: Record = {}) { 8 | const container = document.createElement('div') 9 | document.body.appendChild(container) 10 | 11 | env.set('server') 12 | const app = createSSRApp(component, props) 13 | const contents = await renderToString(app) 14 | container.innerHTML = contents 15 | 16 | return { 17 | contents, 18 | async hydrate() { 19 | env.set('client') 20 | const app = createApp(component, props) 21 | app.mount(container) 22 | 23 | await nextTick() 24 | 25 | return { 26 | contents: container.innerHTML, 27 | } 28 | }, 29 | } 30 | } 31 | 32 | export async function renderHydrate(component: any, props: any = {}) { 33 | const { hydrate } = await renderSSR(component, props) 34 | return hydrate() 35 | } 36 | -------------------------------------------------------------------------------- /packages/vue/test/unit/utils/testing-library.ts: -------------------------------------------------------------------------------- 1 | import { type RenderOptions, cleanup, render, waitFor } from '@testing-library/vue' 2 | import { afterEach } from 'vitest' 3 | 4 | afterEach(() => { 5 | cleanup() 6 | }) 7 | 8 | const customRender = (TestComponent: any, options?: RenderOptions) => 9 | render(TestComponent, { 10 | global: { 11 | stubs: { 12 | transition: false, 13 | }, 14 | }, 15 | ...options, 16 | }) 17 | 18 | const promisedWaitFor = () => new Promise(resolve => waitFor(resolve)) 19 | const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)) 20 | 21 | export * from '@testing-library/vue' 22 | export { default as userEvent } from '@testing-library/user-event' 23 | export { 24 | customRender as render, 25 | promisedWaitFor as waitFor, 26 | wait, 27 | } 28 | -------------------------------------------------------------------------------- /packages/vue/test/unit/virtual-element.test.ts: -------------------------------------------------------------------------------- 1 | import { defineComponent } from 'vue' 2 | import { FloatVirtual, type FloatVirtualInitialProps } from '../../src/float' 3 | import { render, screen, wait } from './utils/testing-library' 4 | import { html } from './utils/html' 5 | 6 | describe('Render virtual elements', () => { 7 | it('should to render ', async () => { 8 | render(defineComponent({ 9 | components: { FloatVirtual }, 10 | methods: { 11 | onInitial({ reference }: FloatVirtualInitialProps) { 12 | reference.value = { 13 | getBoundingClientRect() { 14 | return { 15 | width: 0, 16 | height: 0, 17 | x: 300, 18 | y: 120, 19 | top: 120, 20 | left: 300, 21 | right: 300, 22 | bottom: 120, 23 | } 24 | }, 25 | } 26 | }, 27 | }, 28 | template: html` 29 | 30 |
content
31 |
32 | `, 33 | })) 34 | 35 | await wait(50) 36 | 37 | expect(screen.getByTestId('content').innerHTML).toBe('content') 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /packages/vue/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 5 | "target": "ESNext", 6 | "jsx": "preserve", 7 | "lib": ["ESNext", "DOM", "DOM.Iterable"], 8 | "useDefineForClassFields": true, 9 | "module": "ESNext", 10 | "moduleResolution": "Node", 11 | "types": ["node", "vite/client"], 12 | "strict": true, 13 | "noImplicitThis": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "verbatimModuleSyntax": true, 16 | "skipLibCheck": true 17 | }, 18 | "include": ["src/**/*"] 19 | } 20 | -------------------------------------------------------------------------------- /packages/vue/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "jsx": "preserve", 5 | "lib": ["ESNext", "DOM", "DOM.Iterable"], 6 | "useDefineForClassFields": true, 7 | "module": "ESNext", 8 | "moduleResolution": "Node", 9 | "types": ["node", "vite/client"], 10 | "strict": true, 11 | "noImplicitThis": true, 12 | "outDir": "dist", 13 | "forceConsistentCasingInFileNames": true, 14 | "verbatimModuleSyntax": true, 15 | "skipLibCheck": true 16 | }, 17 | "include": ["src/**/*"] 18 | } 19 | -------------------------------------------------------------------------------- /packages/vue/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "references": [ 3 | { "path": "./tsconfig.app.json" }, 4 | { "path": "./tsconfig.node.json" }, 5 | { "path": "./tsconfig.build.json" }, 6 | { "path": "./tsconfig.vitest.json" }, 7 | { "path": "./tsconfig.playwright.json" } 8 | ], 9 | "files": [] 10 | } 11 | -------------------------------------------------------------------------------- /packages/vue/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", 5 | "target": "ES2022", 6 | "lib": ["ES2023"], 7 | "moduleDetection": "force", 8 | "module": "ESNext", 9 | "moduleResolution": "Bundler", 10 | "types": ["node"], 11 | "strict": true, 12 | "noEmit": true, 13 | "esModuleInterop": true, 14 | "skipLibCheck": true 15 | }, 16 | "include": [ 17 | "vite.config.ts", 18 | "vitest.config.ts", 19 | "playwright.config.ts" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /packages/vue/tsconfig.playwright.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.node.json", 3 | "compilerOptions": { 4 | "composite": true, 5 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.playwright.tsbuildinfo", 6 | "lib": [], 7 | "types": ["node"] 8 | }, 9 | "include": ["test/functional/**/*"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/vue/tsconfig.vitest.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.app.json", 3 | "compilerOptions": { 4 | "composite": true, 5 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.vitest.tsbuildinfo", 6 | "lib": [], 7 | "types": ["node", "jsdom", "vitest/globals"] 8 | }, 9 | "include": ["src/**/*", "test/unit/**/*"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/vue/vite.config.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path' 2 | import { defineConfig } from 'vite' 3 | import Vue from '@vitejs/plugin-vue' 4 | 5 | export default defineConfig({ 6 | plugins: [ 7 | Vue(), 8 | ], 9 | build: { 10 | lib: { 11 | entry: path.resolve(__dirname, 'src/index.ts'), 12 | formats: ['es', 'cjs'], 13 | fileName: format => format === 'cjs' ? 'headlessui-float.cjs' : 'headlessui-float.mjs', 14 | }, 15 | rollupOptions: { 16 | external: [ 17 | 'vue', 18 | '@headlessui/vue', 19 | '@floating-ui/core', 20 | '@floating-ui/dom', 21 | '@floating-ui/vue', 22 | ], 23 | }, 24 | }, 25 | }) 26 | -------------------------------------------------------------------------------- /packages/vue/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { mergeConfig } from 'vite' 2 | import { defineConfig } from 'vitest/config' 3 | import viteConfig from './vite.config' 4 | 5 | export default mergeConfig( 6 | viteConfig, 7 | defineConfig({ 8 | test: { 9 | globals: true, 10 | environment: 'jsdom', 11 | setupFiles: './setup.ts', 12 | server: { 13 | deps: { 14 | inline: ['vue'], 15 | }, 16 | }, 17 | }, 18 | }), 19 | ) 20 | --------------------------------------------------------------------------------