├── .gitignore ├── .storybook ├── main.js └── preview.js ├── LICENSE ├── README.md ├── docs ├── .gitignore ├── README.md ├── babel.config.js ├── blog │ ├── 2019-05-28-first-blog-post.md │ ├── 2019-05-29-long-blog-post.md │ ├── 2021-08-01-mdx-blog-post.mdx │ ├── 2021-08-26-welcome │ │ ├── docusaurus-plushie-banner.jpeg │ │ └── index.md │ └── authors.yml ├── docs │ ├── _tutorial-extras │ │ ├── _category_.json │ │ ├── manage-docs-versions.md │ │ └── translate-your-site.md │ ├── intro.md │ ├── useAccordion.mdx │ ├── useCarousel.mdx │ ├── useDropdown.mdx │ ├── usePagination.mdx │ ├── useProgressBar.mdx │ ├── useRate.mdx │ ├── useStepper.mdx │ ├── useSwitch.mdx │ ├── useTab.mdx │ └── useTooltip.mdx ├── docusaurus.config.js ├── package.json ├── sidebars.js ├── src │ ├── components │ │ ├── HomepageFeatures.js │ │ └── HomepageFeatures.module.css │ ├── css │ │ └── custom.css │ └── pages │ │ ├── index.js │ │ ├── index.module.css │ │ └── markdown-page.md ├── static │ ├── .nojekyll │ └── img │ │ ├── docusaurus.png │ │ ├── favicon.ico │ │ ├── logo.svg │ │ ├── tutorial │ │ ├── docsVersionDropdown.png │ │ └── localeDropdown.png │ │ ├── undraw_docusaurus_mountain.svg │ │ ├── undraw_docusaurus_react.svg │ │ └── undraw_docusaurus_tree.svg └── yarn.lock ├── example ├── .npmignore ├── index.html ├── index.tsx ├── package-lock.json ├── package.json └── tsconfig.json ├── package-lock.json ├── package.json ├── src ├── index.ts ├── useAccordion │ └── index.tsx ├── useCarousel │ └── index.tsx ├── useDropdown │ └── index.tsx ├── useHasClickedOutside │ └── index.ts ├── useMediaQuery │ └── index.tsx ├── usePagination │ └── index.tsx ├── useProgressBar │ └── index.tsx ├── useRate │ └── index.tsx ├── useStepper │ └── index.tsx ├── useSwitch │ └── index.tsx ├── useTab │ └── index.tsx └── useTooltip │ └── index.tsx ├── stories ├── useAccordion │ └── useAccordion.Simple.stories.tsx ├── useCarousel │ ├── styleSimpleCarousel.css │ └── useCarousel.Simple.stories.tsx ├── useDropdown │ ├── useDropdown.Simple.stories.tsx │ └── useDropdown.Styled.stories.tsx ├── useMediaQuery │ └── useMediaQuery.Simple.stories.tsx ├── usePagination │ ├── styleSimplePagination.css │ └── usePagination.Simple.stories.tsx ├── useProgressBar │ └── useProgressBar.Simple.stories.tsx ├── useRate │ └── useRate.Simple.stories.tsx ├── useStepper │ └── useStepper.Simple.stories.tsx ├── useSwitch │ └── Switch.Simple.stories.tsx ├── useTab │ ├── styleSimpleTab.css │ └── useTab.Simple.stories.tsx └── useTootip │ └── useTooltip.Simple.stories.tsx ├── test └── blah.test.tsx ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .DS_Store 3 | node_modules 4 | .cache 5 | dist 6 | -------------------------------------------------------------------------------- /.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | stories: ['../stories/**/*.stories.@(ts|tsx|js|jsx)'], 3 | addons: ['@storybook/addon-links', '@storybook/addon-essentials'], 4 | // https://storybook.js.org/docs/react/configure/typescript#mainjs-configuration 5 | typescript: { 6 | check: true, // type-check stories during Storybook build 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /.storybook/preview.js: -------------------------------------------------------------------------------- 1 | // https://storybook.js.org/docs/react/writing-stories/parameters#global-parameters 2 | export const parameters = { 3 | // https://storybook.js.org/docs/react/essentials/actions#automatically-matching-args 4 | actions: { argTypesRegex: '^on.*' }, 5 | }; 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Riccardo Tartaglia 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 all 13 | 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 THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Headless Hooks ⚛️🪝 2 | 3 | Hooks for build and design **powerful** components while retaining **100% control over markup and styles**. 4 | 5 | ### Visit https://webeetle.github.io/react-headless-hooks/ for docs, guides, API and more! 6 | 7 | ### Quick Overview 8 | - useAccordion 9 | - useCarousel 10 | - useDropdown 11 | - usePagination 12 | - useProgressBar 13 | - useStepper 14 | - useSwitch 15 | - useTab 16 | - useTooltip 17 | 18 | ## Install 19 | 20 | `npm i @webeetle/react-headless-hooks` 21 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | Using SSH: 30 | 31 | ``` 32 | $ USE_SSH=true yarn deploy 33 | ``` 34 | 35 | Not using SSH: 36 | 37 | ``` 38 | $ GIT_USER= yarn deploy 39 | ``` 40 | 41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 42 | -------------------------------------------------------------------------------- /docs/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /docs/blog/2019-05-28-first-blog-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: first-blog-post 3 | title: First Blog Post 4 | authors: 5 | name: Gao Wei 6 | title: Docusaurus Core Team 7 | url: https://github.com/wgao19 8 | image_url: https://github.com/wgao19.png 9 | tags: [hola, docusaurus] 10 | --- 11 | 12 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 13 | -------------------------------------------------------------------------------- /docs/blog/2019-05-29-long-blog-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: long-blog-post 3 | title: Long Blog Post 4 | authors: endi 5 | tags: [hello, docusaurus] 6 | --- 7 | 8 | This is the summary of a very long blog post, 9 | 10 | Use a `` comment to limit blog post size in the list view. 11 | 12 | 13 | 14 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 15 | 16 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 17 | 18 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 19 | 20 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 21 | 22 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 23 | 24 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 25 | 26 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 27 | 28 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 29 | 30 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 31 | 32 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 33 | 34 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 35 | 36 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 37 | 38 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 39 | 40 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 41 | 42 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 43 | 44 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 45 | -------------------------------------------------------------------------------- /docs/blog/2021-08-01-mdx-blog-post.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | slug: mdx-blog-post 3 | title: MDX Blog Post 4 | authors: [slorber] 5 | tags: [docusaurus] 6 | --- 7 | 8 | Blog posts support [Docusaurus Markdown features](https://docusaurus.io/docs/markdown-features), such as [MDX](https://mdxjs.com/). 9 | 10 | :::tip 11 | 12 | Use the power of React to create interactive blog posts. 13 | 14 | ```js 15 | 16 | ``` 17 | 18 | 19 | 20 | ::: 21 | -------------------------------------------------------------------------------- /docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webeetle/react-headless-hooks/7823a16067d32a24d1b6d8a3effad7e7ac57d90b/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg -------------------------------------------------------------------------------- /docs/blog/2021-08-26-welcome/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: welcome 3 | title: Welcome 4 | authors: [slorber, yangshun] 5 | tags: [facebook, hello, docusaurus] 6 | --- 7 | 8 | [Docusaurus blogging features](https://docusaurus.io/docs/blog) are powered by the [blog plugin](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog). 9 | 10 | Simply add Markdown files (or folders) to the `blog` directory. 11 | 12 | Regular blog authors can be added to `authors.yml`. 13 | 14 | The blog post date can be extracted from filenames, such as: 15 | 16 | - `2019-05-30-welcome.md` 17 | - `2019-05-30-welcome/index.md` 18 | 19 | A blog post folder can be convenient to co-locate blog post images: 20 | 21 | ![Docusaurus Plushie](./docusaurus-plushie-banner.jpeg) 22 | 23 | The blog supports tags as well! 24 | 25 | **And if you don't want a blog**: just delete this directory, and use `blog: false` in your Docusaurus config. 26 | -------------------------------------------------------------------------------- /docs/blog/authors.yml: -------------------------------------------------------------------------------- 1 | endi: 2 | name: Endilie Yacop Sucipto 3 | title: Maintainer of Docusaurus 4 | url: https://github.com/endiliey 5 | image_url: https://github.com/endiliey.png 6 | 7 | yangshun: 8 | name: Yangshun Tay 9 | title: Front End Engineer @ Facebook 10 | url: https://github.com/yangshun 11 | image_url: https://github.com/yangshun.png 12 | 13 | slorber: 14 | name: Sébastien Lorber 15 | title: Docusaurus maintainer 16 | url: https://sebastienlorber.com 17 | image_url: https://github.com/slorber.png 18 | -------------------------------------------------------------------------------- /docs/docs/_tutorial-extras/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Tutorial - Extras", 3 | "position": 3 4 | } 5 | -------------------------------------------------------------------------------- /docs/docs/_tutorial-extras/manage-docs-versions.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Manage Docs Versions 6 | 7 | Docusaurus can manage multiple versions of your docs. 8 | 9 | ## Create a docs version 10 | 11 | Release a version 1.0 of your project: 12 | 13 | ```bash 14 | npm run docusaurus docs:version 1.0 15 | ``` 16 | 17 | The `docs` folder is copied into `versioned_docs/version-1.0` and `versions.json` is created. 18 | 19 | Your docs now have 2 versions: 20 | 21 | - `1.0` at `http://localhost:3000/docs/` for the version 1.0 docs 22 | - `current` at `http://localhost:3000/docs/next/` for the **upcoming, unreleased docs** 23 | 24 | ## Add a Version Dropdown 25 | 26 | To navigate seamlessly across versions, add a version dropdown. 27 | 28 | Modify the `docusaurus.config.js` file: 29 | 30 | ```js title="docusaurus.config.js" 31 | module.exports = { 32 | themeConfig: { 33 | navbar: { 34 | items: [ 35 | // highlight-start 36 | { 37 | type: 'docsVersionDropdown', 38 | }, 39 | // highlight-end 40 | ], 41 | }, 42 | }, 43 | }; 44 | ``` 45 | 46 | The docs version dropdown appears in your navbar: 47 | 48 | ![Docs Version Dropdown](/img/tutorial/docsVersionDropdown.png) 49 | 50 | ## Update an existing version 51 | 52 | It is possible to edit versioned docs in their respective folder: 53 | 54 | - `versioned_docs/version-1.0/hello.md` updates `http://localhost:3000/docs/hello` 55 | - `docs/hello.md` updates `http://localhost:3000/docs/next/hello` 56 | -------------------------------------------------------------------------------- /docs/docs/_tutorial-extras/translate-your-site.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # Translate your site 6 | 7 | Let's translate `docs/intro.md` to French. 8 | 9 | ## Configure i18n 10 | 11 | Modify `docusaurus.config.js` to add support for the `fr` locale: 12 | 13 | ```js title="docusaurus.config.js" 14 | module.exports = { 15 | i18n: { 16 | defaultLocale: 'en', 17 | locales: ['en', 'fr'], 18 | }, 19 | }; 20 | ``` 21 | 22 | ## Translate a doc 23 | 24 | Copy the `docs/intro.md` file to the `i18n/fr` folder: 25 | 26 | ```bash 27 | mkdir -p i18n/fr/docusaurus-plugin-content-docs/current/ 28 | 29 | cp docs/intro.md i18n/fr/docusaurus-plugin-content-docs/current/intro.md 30 | ``` 31 | 32 | Translate `i18n/fr/docusaurus-plugin-content-docs/current/intro.md` in French. 33 | 34 | ## Start your localized site 35 | 36 | Start your site on the French locale: 37 | 38 | ```bash 39 | npm run start -- --locale fr 40 | ``` 41 | 42 | Your localized site is accessible at `http://localhost:3000/fr/` and the `Getting Started` page is translated. 43 | 44 | :::caution 45 | 46 | In development, you can only use one locale at a same time. 47 | 48 | ::: 49 | 50 | ## Add a Locale Dropdown 51 | 52 | To navigate seamlessly across languages, add a locale dropdown. 53 | 54 | Modify the `docusaurus.config.js` file: 55 | 56 | ```js title="docusaurus.config.js" 57 | module.exports = { 58 | themeConfig: { 59 | navbar: { 60 | items: [ 61 | // highlight-start 62 | { 63 | type: 'localeDropdown', 64 | }, 65 | // highlight-end 66 | ], 67 | }, 68 | }, 69 | }; 70 | ``` 71 | 72 | The locale dropdown now appears in your navbar: 73 | 74 | ![Locale Dropdown](/img/tutorial/localeDropdown.png) 75 | 76 | ## Build your localized site 77 | 78 | Build your site for a specific locale: 79 | 80 | ```bash 81 | npm run build -- --locale fr 82 | ``` 83 | 84 | Or build your site to include all the locales at once: 85 | 86 | ```bash 87 | npm run build 88 | ``` 89 | -------------------------------------------------------------------------------- /docs/docs/intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Getting Started 6 | 7 | Install the package... 8 | 9 | ```bash 10 | npm i @webeetle/react-headless-hooks 11 | ``` 12 | 13 | ...and you ar done! 14 | -------------------------------------------------------------------------------- /docs/docs/useAccordion.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # useAccordion 6 | 7 | **useAccordion** simulate an accordion with the given number of items. 8 | 9 | ## Basic Example 10 | 11 | 24 | 25 | ## API 26 | 27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 |
NameDescriptionDefault
maxSlideNumber of slides3
loopInfinite looptrue
activeSlideStart from a specific slide1
54 |
55 | -------------------------------------------------------------------------------- /docs/docs/useDropdown.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 4 3 | --- 4 | 5 | # useDropdown 6 | 7 | Build your own dropdown component using this hook. 8 | 9 | ## Basic Example 10 | 11 | 24 | -------------------------------------------------------------------------------- /docs/docs/useProgressBar.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 6 3 | --- 4 | 5 | # useProgressBar 6 | 7 | **useProgressBar** abstract a progress bar ux 8 | 9 | ## Basic Example 10 | 11 | 24 | -------------------------------------------------------------------------------- /docs/docs/useRate.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 6 3 | --- 4 | 5 | # useRate 6 | 7 | **useRate** abstract a rate system 8 | 9 | ## Basic Example 10 | 11 | 24 | -------------------------------------------------------------------------------- /docs/docs/useStepper.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 7 3 | --- 4 | 5 | # useStepper 6 | 7 | **useStepper** simulate a stepper motor. 8 | 9 | ## Basic Example 10 | 11 | 24 | -------------------------------------------------------------------------------- /docs/docs/useSwitch.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 8 3 | --- 4 | 5 | # useSwitch 6 | 7 | **useSwitch** simulate a switch. 8 | 9 | ## Basic Example 10 | 11 | 24 | -------------------------------------------------------------------------------- /docs/docs/useTab.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 9 3 | --- 4 | 5 | # useTab 6 | 7 | **useTab** simulate a tabbed interface. 8 | 9 | ## Basic Example 10 | 11 | 24 | -------------------------------------------------------------------------------- /docs/docs/useTooltip.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 10 3 | --- 4 | 5 | # useTooltip 6 | 7 | **useTooltip** simulate a tooltip on hover. 8 | 9 | ## Basic Example 10 | 11 | 24 | -------------------------------------------------------------------------------- /docs/docusaurus.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Note: type annotations allow type checking and IDEs autocompletion 3 | 4 | const lightCodeTheme = require('prism-react-renderer/themes/github'); 5 | const darkCodeTheme = require('prism-react-renderer/themes/dracula'); 6 | 7 | /** @type {import('@docusaurus/types').Config} */ 8 | const config = { 9 | title: 'React Headless Hooks', 10 | tagline: 11 | 'Hooks for build and design powerful components while retaining 100% control over markup and styles', 12 | url: 'https://webeetle.github.io', 13 | baseUrl: '/react-headless-hooks/', 14 | onBrokenLinks: 'throw', 15 | onBrokenMarkdownLinks: 'warn', 16 | trailingSlash: false, 17 | favicon: '🪝', 18 | organizationName: 'webeetle', // Usually your GitHub org/user name. 19 | projectName: 'react-headless-hooks', // Usually your repo name. 20 | 21 | presets: [ 22 | [ 23 | 'classic', 24 | /** @type {import('@docusaurus/preset-classic').Options} */ 25 | ({ 26 | docs: { 27 | sidebarPath: require.resolve('./sidebars.js'), 28 | // Please change this to your repo. 29 | editUrl: 'https://github.com/facebook/docusaurus/edit/main/website/', 30 | }, 31 | blog: { 32 | showReadingTime: true, 33 | // Please change this to your repo. 34 | editUrl: 35 | 'https://github.com/facebook/docusaurus/edit/main/website/blog/', 36 | }, 37 | theme: { 38 | customCss: require.resolve('./src/css/custom.css'), 39 | }, 40 | }), 41 | ], 42 | ], 43 | 44 | themeConfig: 45 | /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ 46 | ({ 47 | navbar: { 48 | title: 'React Headless Hooks', 49 | /* logo: { 50 | alt: 'React Headless Hooks Logo', 51 | src: 'img/logo.svg', 52 | }, */ 53 | items: [ 54 | { 55 | type: 'doc', 56 | docId: 'intro', 57 | position: 'left', 58 | label: 'Documentation', 59 | }, 60 | //{ to: '/blog', label: 'Blog', position: 'left' }, 61 | { 62 | href: 'https://github.com/webeetle/react-headless-hooks', 63 | label: 'GitHub', 64 | position: 'right', 65 | }, 66 | ], 67 | }, 68 | footer: { 69 | style: 'dark', 70 | links: [ 71 | /* { 72 | title: 'Docs', 73 | items: [ 74 | { 75 | label: 'Gettin Started', 76 | to: '/docs/intro', 77 | }, 78 | ], 79 | }, */ 80 | /* { 81 | title: 'Community', 82 | items: [ 83 | { 84 | label: 'Stack Overflow', 85 | href: 'https://stackoverflow.com/questions/tagged/docusaurus', 86 | }, 87 | { 88 | label: 'Discord', 89 | href: 'https://discordapp.com/invite/docusaurus', 90 | }, 91 | { 92 | label: 'Twitter', 93 | href: 'https://twitter.com/docusaurus', 94 | }, 95 | ], 96 | }, */ 97 | /* { 98 | title: 'More', 99 | items: [ 100 | { 101 | label: 'Blog', 102 | to: '/blog', 103 | }, 104 | { 105 | label: 'GitHub', 106 | href: 'https://github.com/facebook/docusaurus', 107 | }, 108 | ], 109 | }, */ 110 | ], 111 | copyright: `Powered by weBeetle.`, 112 | }, 113 | prism: { 114 | theme: lightCodeTheme, 115 | darkTheme: darkCodeTheme, 116 | }, 117 | }), 118 | }; 119 | 120 | module.exports = config; 121 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids" 15 | }, 16 | "dependencies": { 17 | "@docusaurus/core": "2.0.0-beta.13", 18 | "@docusaurus/preset-classic": "2.0.0-beta.13", 19 | "@mdx-js/react": "^1.6.21", 20 | "clsx": "^1.1.1", 21 | "prism-react-renderer": "^1.2.1", 22 | "react": "^17.0.1", 23 | "react-dom": "^17.0.1" 24 | }, 25 | "browserslist": { 26 | "production": [ 27 | ">0.5%", 28 | "not dead", 29 | "not op_mini all" 30 | ], 31 | "development": [ 32 | "last 1 chrome version", 33 | "last 1 firefox version", 34 | "last 1 safari version" 35 | ] 36 | } 37 | } -------------------------------------------------------------------------------- /docs/sidebars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creating a sidebar enables you to: 3 | - create an ordered group of docs 4 | - render a sidebar for each doc of that group 5 | - provide next/previous navigation 6 | 7 | The sidebars can be generated from the filesystem, or explicitly defined here. 8 | 9 | Create as many sidebars as you want. 10 | */ 11 | 12 | // @ts-check 13 | 14 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ 15 | const sidebars = { 16 | // By default, Docusaurus generates a sidebar from the docs folder structure 17 | tutorialSidebar: [{type: 'autogenerated', dirName: '.'}], 18 | 19 | // But you can create a sidebar manually 20 | /* 21 | tutorialSidebar: [ 22 | { 23 | type: 'category', 24 | label: 'Tutorial', 25 | items: ['hello'], 26 | }, 27 | ], 28 | */ 29 | }; 30 | 31 | module.exports = sidebars; 32 | -------------------------------------------------------------------------------- /docs/src/components/HomepageFeatures.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import styles from './HomepageFeatures.module.css'; 4 | 5 | const FeatureList = [ 6 | { 7 | title: 'Easy to Use', 8 | Svg: require('../../static/img/undraw_docusaurus_mountain.svg').default, 9 | description: ( 10 | <>There are no complicated steps to set up, just a few lines of code. 11 | ), 12 | }, 13 | { 14 | title: 'Focus on What Matters', 15 | Svg: require('../../static/img/undraw_docusaurus_tree.svg').default, 16 | description: <>Every hook abstract the complexity of your application., 17 | }, 18 | { 19 | title: 'UI Framework Agnostic', 20 | Svg: require('../../static/img/undraw_docusaurus_react.svg').default, 21 | description: ( 22 | <> 23 | You are 100% free to use any React UI framework you want: Bootstrap, 24 | MUI, Tailwind or pure HTML 25 | 26 | ), 27 | }, 28 | ]; 29 | 30 | function Feature({ Svg, title, description }) { 31 | return ( 32 |
33 | {/*
34 | 35 |
*/} 36 |
37 |

{title}

38 |

{description}

39 |
40 |
41 | ); 42 | } 43 | 44 | export default function HomepageFeatures() { 45 | return ( 46 |
47 |
48 |
49 | {FeatureList.map((props, idx) => ( 50 | 51 | ))} 52 |
53 |
54 |
55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /docs/src/components/HomepageFeatures.module.css: -------------------------------------------------------------------------------- 1 | .features { 2 | display: flex; 3 | align-items: center; 4 | padding: 10rem 0 2rem 0; 5 | width: 100%; 6 | } 7 | 8 | @media screen and (max-width: 966px) { 9 | .features { 10 | padding: 2rem 0 2rem 0; 11 | } 12 | } 13 | 14 | .featureSvg { 15 | height: 200px; 16 | width: 200px; 17 | } 18 | -------------------------------------------------------------------------------- /docs/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. The classic template 3 | * bundles Infima by default. Infima is a CSS framework designed to 4 | * work well for content-centric websites. 5 | */ 6 | 7 | /* You can override the default Infima variables here. */ 8 | :root { 9 | --ifm-color-primary: #10bf8d; 10 | --ifm-color-primary-dark: rgb(33, 175, 144); 11 | --ifm-color-primary-darker: rgb(31, 165, 136); 12 | --ifm-color-primary-darkest: rgb(26, 136, 112); 13 | --ifm-color-primary-light: rgb(70, 203, 174); 14 | --ifm-color-primary-lighter: rgb(102, 212, 189); 15 | --ifm-color-primary-lightest: rgb(146, 224, 208); 16 | --ifm-code-font-size: 95%; 17 | } 18 | 19 | .docusaurus-highlight-code-line { 20 | background-color: rgba(0, 0, 0, 0.1); 21 | display: block; 22 | margin: 0 calc(-1 * var(--ifm-pre-padding)); 23 | padding: 0 var(--ifm-pre-padding); 24 | } 25 | 26 | html[data-theme='dark'] .docusaurus-highlight-code-line { 27 | background-color: rgba(0, 0, 0, 0.3); 28 | } 29 | -------------------------------------------------------------------------------- /docs/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import Layout from '@theme/Layout'; 4 | import Link from '@docusaurus/Link'; 5 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; 6 | import styles from './index.module.css'; 7 | import HomepageFeatures from '../components/HomepageFeatures'; 8 | 9 | function HomepageHeader() { 10 | const { siteConfig } = useDocusaurusContext(); 11 | return ( 12 |
13 |
14 |

{siteConfig.title} ⚛️🪝

15 |

{siteConfig.tagline}

16 |
17 | 21 | Start in 2min ⏱️ 22 | 23 |
24 |
25 |
26 | ); 27 | } 28 | 29 | export default function Home() { 30 | const { siteConfig } = useDocusaurusContext(); 31 | return ( 32 | 36 | 37 |
38 | 39 |
40 |
41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /docs/src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 4rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | } 12 | 13 | @media screen and (max-width: 966px) { 14 | .heroBanner { 15 | padding: 2rem; 16 | } 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | } 24 | -------------------------------------------------------------------------------- /docs/src/pages/markdown-page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Markdown page example 3 | --- 4 | 5 | # Markdown page example 6 | 7 | You don't need React to write simple standalone pages. 8 | -------------------------------------------------------------------------------- /docs/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webeetle/react-headless-hooks/7823a16067d32a24d1b6d8a3effad7e7ac57d90b/docs/static/.nojekyll -------------------------------------------------------------------------------- /docs/static/img/docusaurus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webeetle/react-headless-hooks/7823a16067d32a24d1b6d8a3effad7e7ac57d90b/docs/static/img/docusaurus.png -------------------------------------------------------------------------------- /docs/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webeetle/react-headless-hooks/7823a16067d32a24d1b6d8a3effad7e7ac57d90b/docs/static/img/favicon.ico -------------------------------------------------------------------------------- /docs/static/img/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/static/img/tutorial/docsVersionDropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webeetle/react-headless-hooks/7823a16067d32a24d1b6d8a3effad7e7ac57d90b/docs/static/img/tutorial/docsVersionDropdown.png -------------------------------------------------------------------------------- /docs/static/img/tutorial/localeDropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webeetle/react-headless-hooks/7823a16067d32a24d1b6d8a3effad7e7ac57d90b/docs/static/img/tutorial/localeDropdown.png -------------------------------------------------------------------------------- /docs/static/img/undraw_docusaurus_mountain.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /docs/static/img/undraw_docusaurus_react.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /docs/static/img/undraw_docusaurus_tree.svg: -------------------------------------------------------------------------------- 1 | docu_tree -------------------------------------------------------------------------------- /example/.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .cache 3 | dist -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Playground 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /example/index.tsx: -------------------------------------------------------------------------------- 1 | import 'react-app-polyfill/ie11'; 2 | import * as React from 'react'; 3 | import * as ReactDOM from 'react-dom'; 4 | import { useDropdown } from '../.'; 5 | 6 | const App = () => { 7 | const { trigger, list, isListOpen } = useDropdown(); 8 | 9 | return ( 10 | <> 11 |
12 | 13 |
14 | {isListOpen && ( 15 | 21 | )} 22 | 23 | ); 24 | }; 25 | 26 | ReactDOM.render(, document.getElementById('root')); 27 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "scripts": { 7 | "start": "parcel index.html", 8 | "build": "parcel build index.html" 9 | }, 10 | "dependencies": { 11 | "react-app-polyfill": "^1.0.0" 12 | }, 13 | "alias": { 14 | "react": "../node_modules/react", 15 | "react-dom": "../node_modules/react-dom/profiling", 16 | "scheduler/tracing": "../node_modules/scheduler/tracing-profiling" 17 | }, 18 | "devDependencies": { 19 | "@types/react": "^16.9.11", 20 | "@types/react-dom": "^16.8.4", 21 | "parcel": "1.12.3", 22 | "typescript": "^3.4.5" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": false, 4 | "target": "es5", 5 | "module": "commonjs", 6 | "jsx": "react", 7 | "moduleResolution": "node", 8 | "noImplicitAny": false, 9 | "noUnusedLocals": false, 10 | "noUnusedParameters": false, 11 | "removeComments": true, 12 | "strictNullChecks": true, 13 | "preserveConstEnums": true, 14 | "sourceMap": true, 15 | "lib": ["es2015", "es2016", "dom"], 16 | "types": ["node"] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@webeetle/react-headless-hooks", 3 | "version": "0.2.0", 4 | "license": "MIT", 5 | "main": "dist/index.js", 6 | "typings": "dist/index.d.ts", 7 | "files": [ 8 | "dist", 9 | "src" 10 | ], 11 | "engines": { 12 | "node": ">=10" 13 | }, 14 | "scripts": { 15 | "start": "tsdx watch", 16 | "build": "tsdx build", 17 | "test": "tsdx test --passWithNoTests", 18 | "lint": "tsdx lint", 19 | "prepare": "tsdx build", 20 | "size": "size-limit", 21 | "analyze": "size-limit --why", 22 | "storybook": "start-storybook -p 6006", 23 | "build-storybook": "build-storybook" 24 | }, 25 | "peerDependencies": { 26 | "react": ">=16" 27 | }, 28 | "husky": { 29 | "hooks": { 30 | "pre-commit": "tsdx lint" 31 | } 32 | }, 33 | "prettier": { 34 | "printWidth": 80, 35 | "semi": true, 36 | "singleQuote": true, 37 | "trailingComma": "es5" 38 | }, 39 | "author": "weBeetle", 40 | "module": "dist/react-headless-hooks.esm.js", 41 | "size-limit": [ 42 | { 43 | "path": "dist/react-headless-hooks.cjs.production.min.js", 44 | "limit": "10 KB" 45 | }, 46 | { 47 | "path": "dist/react-headless-hooks.esm.js", 48 | "limit": "10 KB" 49 | } 50 | ], 51 | "devDependencies": { 52 | "@babel/core": "^7.16.0", 53 | "@size-limit/preset-small-lib": "^7.0.3", 54 | "@storybook/addon-essentials": "^6.4.8", 55 | "@storybook/addon-info": "^5.3.21", 56 | "@storybook/addon-links": "^6.4.8", 57 | "@storybook/addons": "^6.4.8", 58 | "@storybook/react": "^6.4.8", 59 | "@types/react": "^17.0.37", 60 | "@types/react-dom": "^17.0.11", 61 | "babel-loader": "^8.2.3", 62 | "husky": "^7.0.4", 63 | "react": "^17.0.2", 64 | "react-dom": "^17.0.2", 65 | "react-is": "^17.0.2", 66 | "size-limit": "^7.0.4", 67 | "tsdx": "^0.14.1", 68 | "tslib": "^2.3.1", 69 | "typescript": "^4.5.2" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import useCarousel from './useCarousel'; 2 | import useDropdown from './useDropdown'; 3 | import useStepper from './useStepper'; 4 | import useTooltip from './useTooltip'; 5 | import useSwitch from './useSwitch'; 6 | import useProgressBar from './useProgressBar'; 7 | import useRate from './useRate'; 8 | import useHasClickedOutside from './useHasClickedOutside'; 9 | import useAccordion from './useAccordion'; 10 | import useTab from './useTab'; 11 | import usePagination from './usePagination'; 12 | import useMediaQuery from './useMediaQuery'; 13 | 14 | export { 15 | useCarousel, 16 | useDropdown, 17 | useHasClickedOutside, 18 | useProgressBar, 19 | useRate, 20 | useStepper, 21 | useSwitch, 22 | useTooltip, 23 | useAccordion, 24 | useTab, 25 | usePagination, 26 | useMediaQuery, 27 | }; 28 | -------------------------------------------------------------------------------- /src/useAccordion/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, ReactNode } from 'react'; 2 | 3 | export interface AccordionData { 4 | title: ReactNode; 5 | content: ReactNode; 6 | } 7 | export interface AccordionProps { 8 | /** Indexes of Accordion Items */ 9 | data?: Array; 10 | /** Trigger on Item status change */ 11 | onChange?: Function; 12 | /** Set Default All Panels State */ 13 | expandAll?: boolean; 14 | /** Set Specific Default Panels State By Indexes */ 15 | expandByIndexes?: Array; 16 | } 17 | 18 | export interface AccordionPropsReturn { 19 | accordion?: Array; 20 | currentOpenPanels: Array; 21 | } 22 | 23 | const useAccordion = (props: AccordionProps = {}) => { 24 | const { 25 | data: defaultData = [], 26 | onChange: onChangePanelStatus = null, 27 | expandAll = false, 28 | expandByIndexes = [], 29 | } = props; 30 | 31 | const isExpandedByDefault = ( 32 | indexesArray: Array, 33 | currentIndex: number 34 | ): Boolean => { 35 | if (indexesArray.includes(currentIndex)) return true; 36 | return false; 37 | }; 38 | 39 | const onChangePanelStatusHandler = (indexElement: number): void => { 40 | const arrayCopy = [...accordion]; 41 | const ps = arrayCopy.findIndex((el: any) => el.index === indexElement); 42 | arrayCopy[ps]['isExpanded'] = !arrayCopy[ps]?.isExpanded; 43 | return setAccordion(arrayCopy); 44 | }; 45 | 46 | const getCurrentOpenPanels = (accordionState: any[]): any[] => { 47 | return [...accordionState].filter((el: any) => el.isExpanded); 48 | }; 49 | 50 | const [currentOpenPanels, setCurrentOpenPanels] = useState>([]); 51 | const [accordion, setAccordion] = useState>(() => 52 | defaultData 53 | ? defaultData.map((el: Object, index: number) => ({ 54 | ...el, 55 | index, 56 | isExpanded: expandAll 57 | ? expandAll 58 | : isExpandedByDefault(expandByIndexes, index) 59 | ? true 60 | : false, 61 | trigger: (indexElement: number) => { 62 | return { 63 | onClick: () => onChangePanelStatusHandler(indexElement), 64 | }; 65 | }, 66 | })) 67 | : [] 68 | ); 69 | 70 | useEffect(() => { 71 | if (onChangePanelStatus) { 72 | onChangePanelStatus(accordion); 73 | } 74 | if (accordion.length > 0) { 75 | setCurrentOpenPanels(getCurrentOpenPanels(accordion)); 76 | } 77 | }, [accordion]); 78 | 79 | return { accordion, currentOpenPanels }; 80 | }; 81 | 82 | export default useAccordion; 83 | -------------------------------------------------------------------------------- /src/useCarousel/index.tsx: -------------------------------------------------------------------------------- 1 | import useStepper, { TriggerProps } from "../useStepper"; 2 | 3 | export interface CarouselProps { 4 | /** Number of slides */ 5 | maxSlide: number; 6 | /** infinite loop */ 7 | loop: boolean; 8 | /** start from a specific slide */ 9 | activeSlide?: number; 10 | /** overwrite the trigger logic go to the previous slide */ 11 | triggerGoToPrevSlide?: TriggerProps; 12 | /** overwrite the trigger logic go to the next slide */ 13 | triggerGoToNextSlide?: TriggerProps; 14 | } 15 | 16 | export interface CarouselReturn { 17 | /** Current slide */ 18 | currentSlide: number; 19 | /** Go to the slide */ 20 | goToSlide: Function; 21 | /** Go to the previous slide */ 22 | goToPrevSlide: Function; 23 | /** Go to the next slide */ 24 | goToNextSlide: Function; 25 | /** Is the current step the first slide? */ 26 | isFirstSlide: boolean; 27 | /** Is the current step the last slide? */ 28 | isLastSlide: boolean; 29 | /** Can you go to the previous slide? */ 30 | canGoToPrevSlide: boolean; 31 | /** Can you go to the next slide? */ 32 | canGoToNextSlide: boolean; 33 | /** trigger prev slide attrs */ 34 | triggerGoToPrevSlide: TriggerProps; 35 | /** trigger next slide attrs */ 36 | triggerGoToNextSlide: TriggerProps; 37 | } 38 | 39 | const useCarousel = (props: CarouselProps = { 40 | maxSlide: 1, 41 | loop: true 42 | }): CarouselReturn => { 43 | const { 44 | maxSlide, 45 | loop, 46 | activeSlide = 1, 47 | triggerGoToPrevSlide = {}, 48 | triggerGoToNextSlide = {} 49 | } = props; 50 | const { 51 | currentStep, 52 | goToStep, 53 | goToPrevStep, 54 | goToNextStep, 55 | isFirstStep, 56 | isLastStep, 57 | canGoToPrevStep, 58 | canGoToNextStep, 59 | triggerGoToPrevStep, 60 | triggerGoToNextStep 61 | } = useStepper({ 62 | maxStep: maxSlide, 63 | loop, 64 | activeStep: activeSlide, 65 | triggerGoToPrevStep: triggerGoToPrevSlide, 66 | triggerGoToNextStep: triggerGoToNextSlide 67 | }) 68 | 69 | return { 70 | currentSlide: currentStep, 71 | goToSlide: goToStep, 72 | goToPrevSlide: goToPrevStep, 73 | goToNextSlide: goToNextStep, 74 | isFirstSlide: isFirstStep, 75 | isLastSlide: isLastStep, 76 | canGoToPrevSlide: canGoToPrevStep, 77 | canGoToNextSlide: canGoToNextStep, 78 | triggerGoToPrevSlide: triggerGoToPrevStep, 79 | triggerGoToNextSlide: triggerGoToNextStep 80 | } 81 | }; 82 | 83 | export default useCarousel; -------------------------------------------------------------------------------- /src/useDropdown/index.tsx: -------------------------------------------------------------------------------- 1 | // Please do not use types off of a default export module or else Storybook Docs will suffer. 2 | // see: https://github.com/storybookjs/storybook/issues/9556\ 3 | 4 | import useSwitch, { TriggerProps, TargetProps } from '../useSwitch'; 5 | 6 | export interface DropdownProps { 7 | /** Is list open by default */ 8 | isOpen?: boolean; 9 | /** Open List on trigger Mouse Enter */ 10 | openListOnTriggerHover?: boolean; 11 | /** Open List on trigger Click */ 12 | disableTriggerClick?: boolean; 13 | /** Close list on list Mouse Leave */ 14 | closeListOnListLeave?: boolean; 15 | /** Close list on click outside */ 16 | closeListOnClickOutside?: boolean; 17 | } 18 | 19 | export interface DropdownReturn { 20 | /** trigger attrs */ 21 | trigger: TriggerProps; 22 | /** list attrs */ 23 | list: TargetProps; 24 | /** toggle list open/close */ 25 | toggleList: Function; 26 | /** open list */ 27 | openList: Function; 28 | /** close list */ 29 | closeList: Function; 30 | /** is list open */ 31 | isListOpen: boolean; 32 | } 33 | 34 | /** 35 | * useDropdown() - Build your dropdown component that can be used to display a list of items. 36 | */ 37 | const useDropdown = (props: DropdownProps = {}): DropdownReturn => { 38 | const { 39 | isOpen = false, 40 | disableTriggerClick = false, 41 | openListOnTriggerHover = false, 42 | closeListOnListLeave = false, 43 | closeListOnClickOutside = false, 44 | } = props; 45 | const { trigger, target, toggle, turnOn, turnOff, isOn } = useSwitch({ 46 | isOn: isOpen, 47 | disableTriggerClick, 48 | turnOnOnTriggerHover: openListOnTriggerHover, 49 | turnOffOnTargetLeave: closeListOnListLeave, 50 | turnOffOnClickOutside: closeListOnClickOutside, 51 | }); 52 | 53 | return { 54 | trigger, 55 | list: target, 56 | toggleList: toggle, 57 | openList: turnOn, 58 | closeList: turnOff, 59 | isListOpen: isOn, 60 | }; 61 | }; 62 | 63 | export default useDropdown; 64 | -------------------------------------------------------------------------------- /src/useHasClickedOutside/index.ts: -------------------------------------------------------------------------------- 1 | import { RefObject, useEffect, useState } from 'react'; 2 | /** 3 | * useHasClickedOutside() - Returns true if the user has clicked outside of the given ref. 4 | */ 5 | function useHasClickedOutside(ref: RefObject): boolean { 6 | const [hasClickedOutside, setHasClickedOutside] = useState(false); 7 | useEffect(() => { 8 | /** 9 | * Alert if clicked on outside of element 10 | */ 11 | function handleClickOutside(event: Event) { 12 | if (ref.current && !ref.current.contains(event.target)) { 13 | setHasClickedOutside(true); 14 | } 15 | setHasClickedOutside(false); 16 | } 17 | 18 | // Bind the event listener 19 | document.addEventListener('mousedown', handleClickOutside); 20 | return () => { 21 | // Unbind the event listener on clean up 22 | document.removeEventListener('mousedown', handleClickOutside); 23 | }; 24 | }, [ref]); 25 | 26 | return hasClickedOutside; 27 | } 28 | export default useHasClickedOutside; 29 | -------------------------------------------------------------------------------- /src/useMediaQuery/index.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | 3 | export interface MediaQueryProps { 4 | /** Breakpoints data */ 5 | breakpoints?: Array; 6 | } 7 | 8 | export interface AccordionPropsReturn { 9 | /** Breakpoints data given by prop*/ 10 | breakpointsDefault: Array; 11 | /** Breakpoints Object with Media Listner Attached*/ 12 | media: IMediaQuery; 13 | } 14 | 15 | interface IBreakPoints { 16 | xs?: String; 17 | sm?: String; 18 | md?: String; 19 | lg?: String; 20 | xl?: String; 21 | triggerFlow: String; 22 | } 23 | 24 | interface IMediaQuery { 25 | xs?: Boolean; 26 | sm?: Boolean; 27 | md?: Boolean; 28 | lg?: Boolean; 29 | xl?: Boolean; 30 | } 31 | 32 | const useMediaQuery = (props: MediaQueryProps = {}) => { 33 | const { breakpoints: breakpointsDefault = [] } = props; 34 | 35 | const getBreakPoint = (obj: any, getValue = true) => { 36 | const allBreaks = ['xs', 'sm', 'md', 'lg', 'xl']; 37 | let returned; 38 | for (const key in obj) { 39 | if (allBreaks.includes(key) && getValue) { 40 | returned = obj[key]; 41 | } else { 42 | returned = key; 43 | } 44 | break; 45 | } 46 | return returned; 47 | }; 48 | 49 | const buildMediaQueryForEachObject = () => { 50 | const obj: any = {}; 51 | const min = 'min'; 52 | const max = 'max'; 53 | breakpointsDefault.forEach((el: IBreakPoints) => { 54 | const { triggerFlow, ...rest } = el; 55 | const triggerMedia = triggerFlow === 'up' ? min : max; 56 | obj[`${getBreakPoint(rest, false)}`] = window.matchMedia( 57 | `(${triggerMedia}-width: ${getBreakPoint(rest)})` 58 | ).matches; 59 | }); 60 | return obj; 61 | }; 62 | 63 | const [media] = useState(() => buildMediaQueryForEachObject()); 64 | 65 | useEffect(() => {}, [media['xl']]); 66 | 67 | return { breakpointsDefault, media }; 68 | }; 69 | 70 | export default useMediaQuery; 71 | -------------------------------------------------------------------------------- /src/usePagination/index.tsx: -------------------------------------------------------------------------------- 1 | import useStepper, { TriggerProps } from "../useStepper"; 2 | import {useRef} from "react"; 3 | 4 | export interface PaginationProps { 5 | /** Number of pages */ 6 | numPages: number; 7 | /** start from a specific page */ 8 | activePage?: number; 9 | /** overwrite the trigger logic go to the previous page */ 10 | triggerGoToPrevPage?: TriggerProps; 11 | /** overwrite the trigger logic go to the next page */ 12 | triggerGoToNextPage?: TriggerProps; 13 | /** overwrite the trigger logic go to the first page */ 14 | triggerGoToFirstPage?: TriggerProps; 15 | /** overwrite the trigger logic go to the last page */ 16 | triggerGoToLastPage?: TriggerProps; 17 | } 18 | 19 | export interface PaginationReturn { 20 | /** Current page */ 21 | currentPage: number; 22 | /** Go to the page */ 23 | goToPage: Function; 24 | /** Go to the previous page */ 25 | goToPrevPage: Function; 26 | /** Go to the next page */ 27 | goToNextPage: Function; 28 | /** Is the current step the first page? */ 29 | isFirstPage: boolean; 30 | /** Is the current step the last page? */ 31 | isLastPage: boolean; 32 | /** Can you go to the previous page? */ 33 | canGoToPrevPage: boolean; 34 | /** Can you go to the next page? */ 35 | canGoToNextPage: boolean; 36 | /** trigger prev page attrs */ 37 | triggerGoToPrevPage: TriggerProps; 38 | /** trigger next page attrs */ 39 | triggerGoToNextPage: TriggerProps; 40 | /** trigger first page attrs */ 41 | triggerGoToFirstPage: TriggerProps; 42 | /** trigger last page attrs */ 43 | triggerGoToLastPage: TriggerProps; 44 | } 45 | 46 | const usePagination = (props: PaginationProps = { 47 | numPages: 3 48 | }): PaginationReturn => { 49 | const { 50 | numPages, 51 | activePage = 1, 52 | triggerGoToPrevPage = {}, 53 | triggerGoToNextPage = {}, 54 | triggerGoToFirstPage: triggerDefaultGoToFirstPage = {}, 55 | triggerGoToLastPage: triggerDefaultGoToLastPage = {}, 56 | } = props; 57 | 58 | const { 59 | currentStep, 60 | goToStep, 61 | goToPrevStep, 62 | goToNextStep, 63 | isFirstStep, 64 | isLastStep, 65 | canGoToPrevStep, 66 | canGoToNextStep, 67 | triggerGoToPrevStep, 68 | triggerGoToNextStep 69 | } = useStepper({ 70 | maxStep: numPages, 71 | activeStep: activePage, 72 | triggerGoToPrevStep: triggerGoToPrevPage, 73 | triggerGoToNextStep: triggerGoToNextPage 74 | }); 75 | 76 | const triggerGoToFirstPageRef = useRef(null); 77 | const triggerGoToLastPageRef = useRef(null); 78 | 79 | const triggerGoToFirstPage: TriggerProps = { 80 | onClick: () => { 81 | goToStep(1) 82 | }, 83 | ...triggerDefaultGoToFirstPage, 84 | ref: triggerGoToFirstPageRef 85 | } 86 | 87 | const triggerGoToLastPage: TriggerProps = { 88 | onClick: () => { 89 | goToStep(numPages) 90 | }, 91 | ...triggerDefaultGoToLastPage, 92 | ref: triggerGoToLastPageRef 93 | } 94 | 95 | return { 96 | currentPage: currentStep, 97 | goToPage: goToStep, 98 | goToPrevPage: goToPrevStep, 99 | goToNextPage: goToNextStep, 100 | isFirstPage: isFirstStep, 101 | isLastPage: isLastStep, 102 | canGoToPrevPage: canGoToPrevStep, 103 | canGoToNextPage: canGoToNextStep, 104 | triggerGoToPrevPage: triggerGoToPrevStep, 105 | triggerGoToNextPage: triggerGoToNextStep, 106 | triggerGoToFirstPage, 107 | triggerGoToLastPage 108 | } 109 | }; 110 | 111 | export default usePagination; -------------------------------------------------------------------------------- /src/useProgressBar/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | export interface ProgressBarProps { 4 | /** override the bar percentuage */ 5 | percentuage?: number; 6 | /** Trigger on percentuage change */ 7 | onChange?: Function; 8 | onComplete?: Function; 9 | } 10 | 11 | export interface ProgressBarReturn { 12 | percentuage: number; 13 | isEmpty: Boolean; 14 | isCompleted: Boolean; 15 | increment: Function; 16 | decrement: Function; 17 | reset: Function; 18 | complete: Function; 19 | empty: Function; 20 | } 21 | 22 | const useProgressBar = (props: ProgressBarProps = {}) => { 23 | const { 24 | percentuage: defaultPercentuage = 0, 25 | onChange = null, 26 | onComplete = null, 27 | } = props; 28 | const [percentuage, setPercentuage] = useState(defaultPercentuage); 29 | const [isCompleted, setIsCompleted] = useState(false); 30 | const [isEmpty, setIsEmpty] = useState(false); 31 | 32 | useEffect(() => { 33 | if (onChange) { 34 | onChange(percentuage); 35 | } 36 | 37 | if (percentuage === 100 && onComplete) { 38 | onComplete(percentuage); 39 | } 40 | 41 | if (percentuage === 100) { 42 | setIsCompleted(true); 43 | } else { 44 | setIsCompleted(false); 45 | } 46 | 47 | if (percentuage === 0) { 48 | setIsEmpty(true); 49 | } else { 50 | setIsEmpty(false); 51 | } 52 | }, [percentuage]); 53 | 54 | const reset = (): void => { 55 | setPercentuage(defaultPercentuage); 56 | }; 57 | const empty = (): void => { 58 | setPercentuage(0); 59 | }; 60 | const complete = (): void => { 61 | setPercentuage(100); 62 | }; 63 | const increment = (value: number): void => { 64 | setPercentuage((prev) => (prev + value <= 100 ? prev + value : 100)); 65 | }; 66 | 67 | const decrement = (value: number): void => { 68 | setPercentuage((prev) => (prev - value >= 0 ? prev - value : 0)); 69 | }; 70 | 71 | return { 72 | percentuage, 73 | increment, 74 | decrement, 75 | reset, 76 | complete, 77 | empty, 78 | isCompleted, 79 | isEmpty, 80 | }; 81 | }; 82 | 83 | export default useProgressBar; 84 | -------------------------------------------------------------------------------- /src/useRate/index.tsx: -------------------------------------------------------------------------------- 1 | import { MouseEventHandler, RefObject, useCallback, useEffect, useRef, useState} from "react"; 2 | 3 | export interface RateProps { 4 | /* number of rates */ 5 | numRate: number; 6 | /* start from a specific rate */ 7 | activeRate?: number; 8 | /* reset when click again */ 9 | allowClear?: boolean; 10 | /** overwrite the trigger logic clear rate */ 11 | triggerClearRate?: TriggerProps; 12 | } 13 | 14 | export interface RateReturn { 15 | /** Current rate */ 16 | currentRate: number; 17 | /** change the rate */ 18 | onChangeRate: Function; 19 | /** Is the current rate the first rate? */ 20 | isFirstRate: boolean; 21 | /** Is the current rate the last rate? */ 22 | isLastRate: boolean; 23 | /** Is the current rate the reset rate? */ 24 | isReset: boolean; 25 | /** Reset when click again */ 26 | clearRate: Function; 27 | /** trigger clear rate */ 28 | triggerClearRate: TriggerProps; 29 | } 30 | 31 | export interface TriggerProps { 32 | ref?: RefObject; 33 | onClick?: MouseEventHandler; 34 | } 35 | 36 | /** 37 | * useRate() - Build your rate component. 38 | */ 39 | const useRate = (props: RateProps = { 40 | numRate: 5 41 | }): RateReturn => { 42 | const { 43 | numRate, 44 | activeRate = 0, 45 | allowClear = true, 46 | triggerClearRate: triggerDefaultClearRate = {} 47 | } = props; 48 | 49 | const [currentRate, setCurrentRate] = useState(activeRate) 50 | const [isFirstRate, setIsFirstRate] = useState(true); 51 | const [isLastRate, setIsLastRate] = useState(false) 52 | const [isReset, setIsReset] = useState(false) 53 | const triggerClearRateRef = useRef(null) 54 | 55 | const onChangeRate = useCallback((value: number) => { 56 | if (value >=1 && value <= numRate) { 57 | if (allowClear && value === currentRate) { 58 | setCurrentRate(0) 59 | } 60 | else { 61 | setCurrentRate(value) 62 | } 63 | } 64 | }, [numRate, currentRate, allowClear]) 65 | 66 | const clearRate = useCallback(() => { 67 | if (allowClear) { 68 | setCurrentRate(0) 69 | } 70 | else { 71 | setCurrentRate(1) 72 | } 73 | }, [currentRate, allowClear]) 74 | 75 | useEffect(() => { 76 | setIsFirstRate(false) 77 | setIsLastRate(false) 78 | setIsReset(false) 79 | if (currentRate === 1) { 80 | setIsFirstRate(true) 81 | } 82 | if (currentRate === numRate) { 83 | setIsLastRate(true) 84 | } 85 | if (currentRate === 0) { 86 | setIsReset(true) 87 | } 88 | 89 | return () => { 90 | setIsFirstRate(true) 91 | setIsLastRate(false) 92 | setIsReset(false) 93 | } 94 | }, [currentRate]) 95 | 96 | const triggerClearRate: TriggerProps = { 97 | onClick: () => { 98 | clearRate() 99 | }, 100 | ...triggerDefaultClearRate, 101 | ref: triggerClearRateRef 102 | } 103 | 104 | return { 105 | currentRate, 106 | onChangeRate, 107 | isFirstRate, 108 | isLastRate, 109 | isReset, 110 | clearRate, 111 | triggerClearRate 112 | } 113 | } 114 | 115 | export default useRate; -------------------------------------------------------------------------------- /src/useStepper/index.tsx: -------------------------------------------------------------------------------- 1 | import {MouseEventHandler, RefObject, useCallback, useEffect, useMemo, useRef, useState} from "react"; 2 | 3 | export interface StepperProps { 4 | /** Number of steps */ 5 | maxStep: number; 6 | /** start from a specific step */ 7 | activeStep?: number; 8 | /** loop */ 9 | loop?: boolean; 10 | /** overwrite the trigger logic go to the previous step */ 11 | triggerGoToPrevStep?: TriggerProps; 12 | /** overwrite the trigger logic go to the next step */ 13 | triggerGoToNextStep?: TriggerProps; 14 | /** overwrite the trigger logic reset step */ 15 | triggerResetStep?: TriggerProps; 16 | } 17 | 18 | export interface StepperReturn { 19 | /** Current step */ 20 | currentStep: number; 21 | /** Go to the step */ 22 | goToStep: Function; 23 | /** Go to the previous step */ 24 | goToPrevStep: Function; 25 | /** Go to the next step */ 26 | goToNextStep: Function; 27 | /** Is the current step the first step? */ 28 | isFirstStep: boolean; 29 | /** Is the current step the last step? */ 30 | isLastStep: boolean; 31 | /** Go to the first step */ 32 | reset: Function; 33 | /** Can you go to the previous step? */ 34 | canGoToPrevStep: boolean; 35 | /** Can you go to the next step? */ 36 | canGoToNextStep: boolean; 37 | /** trigger prev step attrs */ 38 | triggerGoToPrevStep: TriggerProps; 39 | /** trigger next step attrs */ 40 | triggerGoToNextStep: TriggerProps; 41 | /** trigger reset step attrs */ 42 | triggerResetStep: TriggerProps; 43 | } 44 | 45 | export interface TriggerProps { 46 | ref?: RefObject; 47 | onClick?: MouseEventHandler; 48 | } 49 | 50 | /** 51 | * useStepper() - Build your stepper component. 52 | */ 53 | const useStepper = (props: StepperProps = { 54 | maxStep: 1 55 | }): StepperReturn => { 56 | const { 57 | maxStep, 58 | activeStep = 1, 59 | loop = false, 60 | triggerGoToPrevStep: triggerDefaulGoToPrevStep = {}, 61 | triggerGoToNextStep: triggerDefaulGoToNextStep = {}, 62 | triggerResetStep: triggerDefaulResetStep = {} 63 | } = props 64 | const [currentStep, setCurrentStep] = useState(activeStep) 65 | const [isFirstStep, setIsFirstStep] = useState(true) 66 | const [isLastStep, setIsLastStep] = useState(false) 67 | const triggerGoToPrevStepRef = useRef(null) 68 | const triggerGoToNextStepRef = useRef(null) 69 | const triggerResetStepRef = useRef(null) 70 | 71 | const canGoToPrevStep = useMemo(() => currentStep - 1 >= 1, [currentStep]); 72 | const canGoToNextStep = useMemo(() => currentStep + 1 <= maxStep, [currentStep]); 73 | 74 | const goToPrevStep = useCallback(() => { 75 | if (canGoToPrevStep) { 76 | setCurrentStep(step => step - 1) 77 | } 78 | else { 79 | if (loop) { 80 | setCurrentStep(maxStep) 81 | } 82 | } 83 | }, [currentStep, maxStep]); 84 | 85 | const goToNextStep = useCallback(() => { 86 | if (canGoToNextStep) { 87 | setCurrentStep(step => step + 1) 88 | } 89 | else { 90 | if (loop) { 91 | setCurrentStep(1) 92 | } 93 | } 94 | }, [currentStep]); 95 | 96 | const reset = useCallback(() => { 97 | setCurrentStep(1) 98 | }, []); 99 | 100 | const goToStep = useCallback((step: number) => { 101 | if (step >= 1 && step <= maxStep) { 102 | setCurrentStep(step) 103 | } 104 | }, [maxStep]); 105 | 106 | useEffect(() => { 107 | setIsFirstStep(false) 108 | setIsLastStep(false) 109 | if (currentStep === 1) { 110 | setIsFirstStep(true) 111 | } 112 | if (currentStep === maxStep) { 113 | setIsLastStep(true) 114 | } 115 | 116 | return () => { 117 | setIsFirstStep(true) 118 | setIsLastStep(false) 119 | } 120 | }, [currentStep]) 121 | 122 | const triggerGoToPrevStep: TriggerProps = { 123 | onClick: () => { 124 | goToPrevStep() 125 | }, 126 | ...triggerDefaulGoToPrevStep, 127 | ref: triggerGoToPrevStepRef 128 | } 129 | 130 | const triggerGoToNextStep: TriggerProps = { 131 | onClick: () => { 132 | goToNextStep() 133 | }, 134 | ...triggerDefaulGoToNextStep, 135 | ref: triggerGoToNextStepRef 136 | } 137 | 138 | const triggerResetStep: TriggerProps = { 139 | onClick: () => { 140 | reset() 141 | }, 142 | ...triggerDefaulResetStep, 143 | ref: triggerResetStepRef 144 | } 145 | 146 | 147 | return { 148 | currentStep, 149 | goToStep, 150 | goToPrevStep, 151 | goToNextStep, 152 | isFirstStep, 153 | isLastStep, 154 | reset, 155 | canGoToPrevStep, 156 | canGoToNextStep, 157 | triggerGoToPrevStep, 158 | triggerGoToNextStep, 159 | triggerResetStep 160 | } 161 | } 162 | 163 | export default useStepper; -------------------------------------------------------------------------------- /src/useSwitch/index.tsx: -------------------------------------------------------------------------------- 1 | // Please do not use types off of a default export module or else Storybook Docs will suffer. 2 | // see: https://github.com/storybookjs/storybook/issues/9556\ 3 | 4 | import { 5 | useState, 6 | useRef, 7 | useEffect, 8 | MouseEventHandler, 9 | RefObject, 10 | useCallback, 11 | } from 'react'; 12 | import useHasClickedOutside from '../useHasClickedOutside'; 13 | 14 | export interface SwitchProps { 15 | /** Is switch on? By default false */ 16 | isOn?: boolean; 17 | /** Turn on if the mouse enter in the trigger */ 18 | turnOnOnTriggerHover?: boolean; 19 | /** Turn off if the mouse leave the trigger */ 20 | turnOffOnTriggerLeave?: boolean; 21 | /** disable the trigger click */ 22 | disableTriggerClick?: boolean; 23 | /** Disable target on target Mouse Leave */ 24 | turnOffOnTargetLeave?: boolean; 25 | /** Disable target on click outside */ 26 | turnOffOnClickOutside?: boolean; 27 | } 28 | 29 | export interface SwitchReturn { 30 | /** trigger attrs */ 31 | trigger: TriggerProps; 32 | /** target attrs */ 33 | target: TargetProps; 34 | /** toggle switch on/off */ 35 | toggle: Function; 36 | /** turn off switch */ 37 | turnOn: Function; 38 | /** turn off the switch */ 39 | turnOff: Function; 40 | /** is the switch on */ 41 | isOn: boolean; 42 | } 43 | 44 | export interface TriggerProps { 45 | ref?: RefObject; 46 | onClick?: MouseEventHandler; 47 | onMouseEnter?: MouseEventHandler; 48 | onMouseLeave?: MouseEventHandler; 49 | } 50 | 51 | export interface TargetProps { 52 | ref?: RefObject; 53 | onMouseLeave?: MouseEventHandler; 54 | } 55 | 56 | /** 57 | * useSwitch() - simulate a switch on/off logic. 58 | */ 59 | const useSwitch = (props: SwitchProps = {}): SwitchReturn => { 60 | const { 61 | isOn: isOnDefault = false, 62 | disableTriggerClick = false, 63 | turnOnOnTriggerHover = false, 64 | turnOffOnTriggerLeave = false, 65 | turnOffOnTargetLeave = false, 66 | turnOffOnClickOutside = false, 67 | } = props; 68 | const [isOn, setIsOn] = useState(isOnDefault); 69 | const targetRef = useRef(null); 70 | const triggerRef = useRef(null); 71 | const hasClickedOutsideTarget = useHasClickedOutside(targetRef); 72 | 73 | const toggle = useCallback(() => { 74 | setIsOn((old) => !old); 75 | }, []); 76 | const turnOn = useCallback(() => { 77 | setIsOn(true); 78 | }, []); 79 | const turnOff = useCallback(() => { 80 | setIsOn(false); 81 | }, []); 82 | 83 | useEffect(() => { 84 | if (turnOffOnClickOutside && hasClickedOutsideTarget) { 85 | turnOff(); 86 | } 87 | }, [hasClickedOutsideTarget]); 88 | 89 | const trigger: TriggerProps = { 90 | onClick: useCallback(() => { 91 | !disableTriggerClick ? toggle() : null; 92 | }, [disableTriggerClick]), 93 | onMouseEnter: useCallback(() => { 94 | turnOnOnTriggerHover ? turnOn() : null; 95 | }, [turnOnOnTriggerHover]), 96 | onMouseLeave: useCallback(() => { 97 | turnOffOnTriggerLeave ? turnOff() : null; 98 | }, [turnOffOnTriggerLeave]), 99 | ref: triggerRef, 100 | }; 101 | 102 | const target: TargetProps = { 103 | onMouseLeave: useCallback(() => { 104 | turnOffOnTargetLeave ? turnOff() : null; 105 | }, [turnOffOnTargetLeave]), 106 | ref: targetRef, 107 | }; 108 | return { 109 | trigger, 110 | target, 111 | toggle, 112 | turnOn, 113 | turnOff, 114 | isOn, 115 | }; 116 | }; 117 | 118 | export default useSwitch; 119 | -------------------------------------------------------------------------------- /src/useTab/index.tsx: -------------------------------------------------------------------------------- 1 | import useStepper, { TriggerProps } from "../useStepper"; 2 | 3 | export interface TabProps { 4 | /** Number of tabs */ 5 | numTabs: number; 6 | /** start from a specific tab */ 7 | activeTab?: number; 8 | /** overwrite the trigger logic go to the previous tab */ 9 | triggerGoToPrevTab?: TriggerProps; 10 | /** overwrite the trigger logic go to the next tab */ 11 | triggerGoToNextTab?: TriggerProps; 12 | } 13 | 14 | export interface TabReturn { 15 | /** Current tab */ 16 | currentTab: number; 17 | /** Go to the tab */ 18 | goToTab: Function; 19 | /** Go to the previous tab */ 20 | goToPrevTab: Function; 21 | /** Go to the next tab */ 22 | goToNextTab: Function; 23 | /** Is the current step the first tab? */ 24 | isFirstTab: boolean; 25 | /** Is the current step the last tab? */ 26 | isLastTab: boolean; 27 | /** Can you go to the previous tab? */ 28 | canGoToPrevTab: boolean; 29 | /** Can you go to the next tab? */ 30 | canGoToNextTab: boolean; 31 | /** trigger prev tab attrs */ 32 | triggerGoToPrevTab: TriggerProps; 33 | /** trigger next tab attrs */ 34 | triggerGoToNextTab: TriggerProps; 35 | } 36 | 37 | const useTab = (props: TabProps = { 38 | numTabs: 3 39 | }): TabReturn => { 40 | const { 41 | numTabs, 42 | activeTab = 1, 43 | triggerGoToPrevTab = {}, 44 | triggerGoToNextTab = {} 45 | } = props; 46 | const { 47 | currentStep, 48 | goToStep, 49 | goToPrevStep, 50 | goToNextStep, 51 | isFirstStep, 52 | isLastStep, 53 | canGoToPrevStep, 54 | canGoToNextStep, 55 | triggerGoToPrevStep, 56 | triggerGoToNextStep 57 | } = useStepper({ 58 | maxStep: numTabs, 59 | activeStep: activeTab, 60 | triggerGoToPrevStep: triggerGoToPrevTab, 61 | triggerGoToNextStep: triggerGoToNextTab, 62 | }) 63 | 64 | return { 65 | currentTab: currentStep, 66 | goToTab: goToStep, 67 | goToPrevTab: goToPrevStep, 68 | goToNextTab: goToNextStep, 69 | isFirstTab: isFirstStep, 70 | isLastTab: isLastStep, 71 | canGoToPrevTab: canGoToPrevStep, 72 | canGoToNextTab: canGoToNextStep, 73 | triggerGoToPrevTab: triggerGoToPrevStep, 74 | triggerGoToNextTab: triggerGoToNextStep 75 | } 76 | }; 77 | 78 | export default useTab; -------------------------------------------------------------------------------- /src/useTooltip/index.tsx: -------------------------------------------------------------------------------- 1 | // Please do not use types off of a default export module or else Storybook Docs will suffer. 2 | // see: https://github.com/storybookjs/storybook/issues/9556\ 3 | 4 | import useSwitch, { TriggerProps, TargetProps } from '../useSwitch'; 5 | 6 | export interface TooltipProps { 7 | /** Is tooltip visible by default? */ 8 | isVisible?: boolean; 9 | /** Show tooltip on click and not hover */ 10 | showOnClick?: boolean; 11 | /** Close tooltip on trigger Mouse Leave */ 12 | hiddenTooltipOnTriggerLeave?: boolean; 13 | /** Close tooltip on tooltip click outside */ 14 | closeTooltipOnClickOutside?: boolean; 15 | } 16 | 17 | export interface TooltipReturn { 18 | /** trigger attrs */ 19 | trigger: TriggerProps; 20 | /** list attrs */ 21 | tooltip: TargetProps; 22 | /** toggle list open/close */ 23 | toggleTooltip: Function; 24 | /** open list */ 25 | showTooltip: Function; 26 | /** close list */ 27 | hiddenTooltip: Function; 28 | /** is list open */ 29 | isVisible: boolean; 30 | } 31 | 32 | /** 33 | * useDropdown() - Build your dropdown component that can be used to display a list of items. 34 | */ 35 | const useDropdown = (props: TooltipProps = {}): TooltipReturn => { 36 | const { 37 | isVisible = false, 38 | showOnClick = false, 39 | hiddenTooltipOnTriggerLeave = true, 40 | closeTooltipOnClickOutside = false, 41 | } = props; 42 | const { trigger, target, toggle, turnOn, turnOff, isOn } = useSwitch({ 43 | isOn: isVisible, 44 | disableTriggerClick: !showOnClick, 45 | turnOnOnTriggerHover: !showOnClick, 46 | turnOffOnTriggerLeave: hiddenTooltipOnTriggerLeave, 47 | turnOffOnClickOutside: closeTooltipOnClickOutside, 48 | }); 49 | 50 | return { 51 | trigger, 52 | tooltip: target, 53 | toggleTooltip: toggle, 54 | showTooltip: turnOn, 55 | hiddenTooltip: turnOff, 56 | isVisible: isOn, 57 | }; 58 | }; 59 | 60 | export default useDropdown; 61 | -------------------------------------------------------------------------------- /stories/useAccordion/useAccordion.Simple.stories.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import useAccordion, { AccordionProps } from '../../src/useAccordion'; 3 | import { Meta } from '@storybook/react'; 4 | 5 | const fakeData = [ 6 | { title: <>Title 1, content: <>content 1 }, 7 | { title: 'Title 2', content: 'content 2' }, 8 | { title: 'Title 3', content: 'content 3' }, 9 | ]; 10 | 11 | export const Simple: FC = ({ 12 | expandAll = true, 13 | expandByIndexes = [0, 2], 14 | }) => { 15 | const { accordion, currentOpenPanels } = useAccordion({ 16 | data: fakeData, 17 | onChange: (state) => null, 18 | expandAll, 19 | expandByIndexes, 20 | }); 21 | 22 | return ( 23 |
32 |
35 | {accordion && 36 | accordion.map(({ title, content, trigger, isExpanded }, index) => ( 37 |
41 |
49 |

{title}

50 | 53 |
54 | {isExpanded && ( 55 |
56 | {content} 57 |
58 | )} 59 |
60 | ))} 61 |
62 |
63 | ); 64 | }; 65 | 66 | const meta: Meta = { 67 | title: 'useAccordion', 68 | component: Simple, 69 | argTypes: {}, 70 | parameters: { 71 | controls: { expanded: true }, 72 | }, 73 | }; 74 | 75 | export default meta; 76 | -------------------------------------------------------------------------------- /stories/useCarousel/styleSimpleCarousel.css: -------------------------------------------------------------------------------- 1 | .carousel-stepper { 2 | position: relative; 3 | width: 750px; 4 | } 5 | .carousel-indicators { 6 | position: absolute; 7 | right: 0; 8 | bottom: 10px; 9 | left: 0; 10 | z-index: 2; 11 | display: flex; 12 | justify-content: center; 13 | padding: 0; 14 | margin-right: 15%; 15 | margin-bottom: 1rem; 16 | margin-left: 15%; 17 | list-style: none; 18 | } 19 | .carousel-indicators button { 20 | box-sizing: content-box; 21 | flex: 0 1 auto; 22 | width: 30px; 23 | height: 3px; 24 | padding: 0; 25 | margin-right: 3px; 26 | margin-left: 3px; 27 | text-indent: -999px; 28 | cursor: pointer; 29 | background-color: #fff; 30 | background-clip: padding-box; 31 | border: 0; 32 | border-top: 10px solid transparent; 33 | border-bottom: 10px solid transparent; 34 | opacity: .5; 35 | transition: opacity .6s ease; 36 | } 37 | .carousel-indicators .active { 38 | opacity: 1; 39 | } 40 | .carousel-inner { 41 | position: relative; 42 | width: 100%; 43 | overflow: hidden; 44 | display: flex; 45 | justify-content: center; 46 | } 47 | .carousel-item { 48 | position: relative; 49 | display: none; 50 | } 51 | .carousel-item.active { 52 | display: block; 53 | } 54 | .carousel-control-prev, 55 | .carousel-control-next { 56 | position: absolute; 57 | top: 0; 58 | bottom: 0; 59 | z-index: 1; 60 | display: flex; 61 | align-items: center; 62 | justify-content: center; 63 | width: 10%; 64 | padding: 0; 65 | cursor: pointer; 66 | color: #ffffff; 67 | text-align: center; 68 | font-weight: bold; 69 | font-size: 24px; 70 | background: 0 0; 71 | border: 0; 72 | opacity: .5; 73 | transition: opacity .15s ease; 74 | } 75 | .carousel-control-prev { 76 | left: 0; 77 | } 78 | .carousel-control-next { 79 | right: 0; 80 | } 81 | .carousel-control-next:hover, 82 | .carousel-control-prev:hover { 83 | color: #ffffff; 84 | text-decoration: none; 85 | outline: 0; 86 | opacity: .9; 87 | } -------------------------------------------------------------------------------- /stories/useCarousel/useCarousel.Simple.stories.tsx: -------------------------------------------------------------------------------- 1 | import React, {FC} from "react"; 2 | import useCarousel, {CarouselProps} from "../../src/useCarousel"; 3 | import './styleSimpleCarousel.css' 4 | import { Meta } from "@storybook/react"; 5 | 6 | export const Simple: FC = ({maxSlide = 3, loop = true, activeSlide = 1}) => { 7 | const { 8 | currentSlide, 9 | goToSlide, 10 | triggerGoToPrevSlide, 11 | triggerGoToNextSlide 12 | } = useCarousel({maxSlide, loop, activeSlide}) 13 | 14 | const btnItems = [] 15 | for (let i=1; i<=maxSlide; i++) { 16 | btnItems.push() 17 | } 18 | 19 | const imgItems = [] 20 | for (let i=1; i<=maxSlide; i++) { 21 | imgItems.push(
22 | {`img 23 |
) 24 | } 25 | 26 | return ( 27 |
28 |
29 | {btnItems} 30 |
31 |
32 | {imgItems} 33 |
34 | 35 | 36 |
37 | ); 38 | 39 | }; 40 | 41 | const meta: Meta = { 42 | title: 'useCarousel', 43 | component: Simple, 44 | argTypes: {}, 45 | parameters: { 46 | controls: { expanded: true }, 47 | }, 48 | } 49 | 50 | export default meta; -------------------------------------------------------------------------------- /stories/useDropdown/useDropdown.Simple.stories.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import useDropdown, { DropdownProps } from '../../src/useDropdown'; 3 | import { Meta } from '@storybook/react'; 4 | 5 | export const Simple: FC = ({ 6 | isOpen = false, 7 | disableTriggerClick = false, 8 | openListOnTriggerHover = false, 9 | closeListOnListLeave = false, 10 | closeListOnClickOutside = false, 11 | }) => { 12 | const { trigger, list, isListOpen } = useDropdown({ 13 | isOpen, 14 | disableTriggerClick, 15 | openListOnTriggerHover, 16 | closeListOnListLeave, 17 | closeListOnClickOutside, 18 | }); 19 | 20 | return ( 21 | <> 22 |
23 | 24 |
25 | {isListOpen && ( 26 |
    27 |
  • Item 1
  • 28 |
  • Item 2
  • 29 |
  • Item 3
  • 30 |
  • Item 4
  • 31 |
32 | )} 33 | 34 | ); 35 | }; 36 | 37 | const meta: Meta = { 38 | title: 'useDropdown', 39 | component: Simple, 40 | argTypes: {}, 41 | parameters: { 42 | controls: { expanded: true }, 43 | }, 44 | }; 45 | 46 | export default meta; 47 | -------------------------------------------------------------------------------- /stories/useDropdown/useDropdown.Styled.stories.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import useDropdown, { DropdownProps } from '../../src/useDropdown'; 3 | import { Meta } from '@storybook/react'; 4 | 5 | export const Styled: FC = ({ 6 | isOpen = false, 7 | disableTriggerClick = false, 8 | openListOnTriggerHover = false, 9 | closeListOnListLeave = false, 10 | closeListOnClickOutside = false, 11 | }) => { 12 | const { trigger, list, isListOpen } = useDropdown({ 13 | isOpen, 14 | disableTriggerClick, 15 | openListOnTriggerHover, 16 | closeListOnListLeave, 17 | closeListOnClickOutside, 18 | }); 19 | 20 | return ( 21 |
22 |
23 | 35 |
36 | {isListOpen && ( 37 | 58 | )} 59 |

lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do

60 |
61 | ); 62 | }; 63 | 64 | const meta: Meta = { 65 | title: 'useDropdown', 66 | component: Styled, 67 | argTypes: {}, 68 | parameters: { 69 | controls: { expanded: true }, 70 | }, 71 | }; 72 | 73 | export default meta; 74 | -------------------------------------------------------------------------------- /stories/useMediaQuery/useMediaQuery.Simple.stories.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import useMediaQuery, { MediaQueryProps } from '../../src/useMediaQuery'; 3 | import { Meta } from '@storybook/react'; 4 | 5 | const fakeBreakpoints = [ 6 | { sm: '300px ', triggerFlow: 'up' }, 7 | { lg: '700px ', triggerFlow: 'down' }, 8 | { md: '600px ', triggerFlow: 'up' }, 9 | { xl: '900px ', triggerFlow: 'down' }, 10 | ]; 11 | 12 | export const Simple: FC = ({}) => { 13 | const { media } = useMediaQuery({ 14 | breakpoints: fakeBreakpoints, 15 | }); 16 | 17 | console.log(media, 'media'); 18 | return ( 19 |
28 |
{JSON.stringify(media, null)}
29 |
30 | ); 31 | }; 32 | 33 | const meta: Meta = { 34 | title: 'useMediaQuery', 35 | component: Simple, 36 | argTypes: {}, 37 | parameters: { 38 | controls: { expanded: true }, 39 | }, 40 | }; 41 | 42 | export default meta; 43 | -------------------------------------------------------------------------------- /stories/usePagination/styleSimplePagination.css: -------------------------------------------------------------------------------- 1 | .pagination-stepper { 2 | position: relative; 3 | display: flex; 4 | justify-content: center; 5 | } 6 | .pagination { 7 | display: flex; 8 | padding-left: 0; 9 | margin-top: 0.5rem; 10 | margin-bottom: 0.5rem; 11 | list-style: none; 12 | } 13 | .page-link { 14 | position: relative; 15 | display: block; 16 | padding: .375rem .75rem; 17 | color: #0d6efd; 18 | text-decoration: none; 19 | background-color: #ffffff; 20 | border: 1px solid #dee2e6; 21 | cursor: pointer; 22 | transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out; 23 | } 24 | .page-item:not(:first-child) .page-link { 25 | margin-left: -1px; 26 | } 27 | .page-item:first-child .page-link { 28 | border-top-left-radius: 0.25rem; 29 | border-bottom-right-radius: 0.25rem; 30 | } 31 | .page-item.disabled .page-link { 32 | color: #6c757d; 33 | pointer-events: none; 34 | background-color: #ffffff; 35 | border-color: #dee2e6; 36 | } 37 | .page-item.active .page-link { 38 | z-index: 3; 39 | color: #ffffff; 40 | background-color: #0d6efd; 41 | border-color: #0d6efd; 42 | } -------------------------------------------------------------------------------- /stories/usePagination/usePagination.Simple.stories.tsx: -------------------------------------------------------------------------------- 1 | import React, {FC} from "react"; 2 | import usePagination, {PaginationProps} from "../../src/usePagination"; 3 | import './styleSimplePagination.css' 4 | import {Meta} from "@storybook/react"; 5 | 6 | export const Simple: FC = ({numPages = 3, activePage = 1}) => { 7 | const { 8 | currentPage, 9 | goToPage, 10 | isFirstPage, 11 | isLastPage, 12 | canGoToPrevPage, 13 | canGoToNextPage, 14 | triggerGoToPrevPage, 15 | triggerGoToNextPage, 16 | triggerGoToFirstPage, 17 | triggerGoToLastPage 18 | } = usePagination({numPages, activePage}) 19 | 20 | const pageItems = [] 21 | pageItems.push(
  • 22 | 23 |
  • ) 24 | pageItems.push(
  • 25 | 26 |
  • ) 27 | for (let i=1; i<=numPages; i++) { 28 | pageItems.push(
  • 29 | 30 |
  • ) 31 | } 32 | pageItems.push(
  • 33 | 34 |
  • ) 35 | pageItems.push(
  • 36 | 37 |
  • ) 38 | 39 | return ( 40 |
    41 |
      42 | {pageItems} 43 |
    44 |
    45 | ); 46 | }; 47 | 48 | const meta: Meta = { 49 | title: 'usePagination', 50 | component: Simple, 51 | argTypes: {}, 52 | parameters: { 53 | controls: { expanded: true }, 54 | }, 55 | } 56 | 57 | export default meta; -------------------------------------------------------------------------------- /stories/useProgressBar/useProgressBar.Simple.stories.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import useProgressBar, { ProgressBarProps } from '../../src/useProgressBar'; 3 | import { Meta } from '@storybook/react'; 4 | 5 | export const Simple = ({ 6 | percentuage: percentuageDefault, 7 | }: ProgressBarProps) => { 8 | const { 9 | increment, 10 | decrement, 11 | percentuage, 12 | reset, 13 | complete, 14 | empty, 15 | isCompleted, 16 | isEmpty, 17 | } = useProgressBar({ 18 | percentuage: percentuageDefault, 19 | onChange: () => null, 20 | onComplete: () => null, 21 | }); 22 | 23 | return ( 24 |
    34 |
    35 | 36 | 37 | 38 | 39 | 40 |
    41 |
    42 |
    49 |
    50 |
    51 | ); 52 | }; 53 | 54 | const meta: Meta = { 55 | title: 'useProgressBar', 56 | component: Simple, 57 | argTypes: {}, 58 | parameters: { 59 | controls: { expanded: true }, 60 | }, 61 | }; 62 | 63 | export default meta; 64 | -------------------------------------------------------------------------------- /stories/useRate/useRate.Simple.stories.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import useRate, { RateProps } from '../../src/useRate'; 3 | import { Meta } from '@storybook/react'; 4 | 5 | export const Simple: FC = ({ numRate = 5 }) => { 6 | const { 7 | currentRate, 8 | onChangeRate, 9 | isFirstRate, 10 | isLastRate, 11 | isReset, 12 | clearRate, 13 | triggerClearRate, 14 | } = useRate({ numRate }); 15 | 16 | const stars = []; 17 | for (let i = 1; i <= numRate; i++) { 18 | stars.push( 19 | 22 | ); 23 | } 24 | 25 | return ( 26 | <> 27 |
    28 | rate: {currentRate} 29 |
    30 |
    31 | {stars} 32 | 33 | 34 |
    35 | {isFirstRate &&
    first rate
    } 36 | {isLastRate &&
    last rate
    } 37 | {isReset &&
    reset rate
    } 38 | 39 | ); 40 | }; 41 | 42 | const meta: Meta = { 43 | title: 'useRate', 44 | component: Simple, 45 | argTypes: {}, 46 | parameters: { 47 | controls: { expanded: true }, 48 | }, 49 | }; 50 | 51 | export default meta; 52 | -------------------------------------------------------------------------------- /stories/useStepper/useStepper.Simple.stories.tsx: -------------------------------------------------------------------------------- 1 | import React, {FC} from "react"; 2 | import useStepper, {StepperProps} from "../../src/useStepper"; 3 | import { Meta } from "@storybook/react"; 4 | 5 | export const Simple: FC = ({maxStep= 10, activeStep = 3}) => { 6 | const { 7 | currentStep, 8 | goToStep, 9 | goToPrevStep, 10 | goToNextStep, 11 | isFirstStep, 12 | isLastStep, 13 | reset, 14 | canGoToPrevStep, 15 | canGoToNextStep, 16 | triggerGoToPrevStep, 17 | triggerGoToNextStep, 18 | triggerResetStep 19 | } = useStepper({maxStep, activeStep}) 20 | 21 | return ( 22 | <> 23 |
    24 |
    25 | step: {currentStep} 26 |
    27 |
    28 | 29 | 30 | 31 |
    32 |
    33 | 41 | 49 | 54 |
    55 |
    56 | 57 | 58 |
    59 | {isFirstStep &&
    first step
    } 60 | {isLastStep &&
    last step
    } 61 |
    62 | 63 | ); 64 | }; 65 | 66 | const meta: Meta = { 67 | title: 'useStepper', 68 | component: Simple, 69 | argTypes: {}, 70 | parameters: { 71 | controls: { expanded: true }, 72 | }, 73 | } 74 | 75 | export default meta; -------------------------------------------------------------------------------- /stories/useSwitch/Switch.Simple.stories.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import useSwitch, { SwitchProps } from '../../src/useSwitch'; 3 | import { Meta } from '@storybook/react'; 4 | 5 | export const Simple: FC = ({ 6 | isOn: isOnDefault = false, 7 | disableTriggerClick = false, 8 | turnOnOnTriggerHover = false, 9 | turnOffOnTriggerLeave = false, 10 | turnOffOnTargetLeave = false, 11 | turnOffOnClickOutside = false, 12 | }) => { 13 | const { trigger, target, isOn } = useSwitch({ 14 | isOn: isOnDefault, 15 | disableTriggerClick, 16 | turnOnOnTriggerHover, 17 | turnOffOnTriggerLeave, 18 | turnOffOnTargetLeave, 19 | turnOffOnClickOutside, 20 | }); 21 | 22 | return ( 23 | <> 24 |
    25 | 26 |
    27 |
    The switch is {isOn ? 'On' : 'Off'}
    28 | 29 | {isOn &&
    Target visible only if the switch is On
    } 30 | 31 | ); 32 | }; 33 | 34 | const meta: Meta = { 35 | title: 'useSwitch', 36 | component: Simple, 37 | argTypes: {}, 38 | parameters: { 39 | controls: { expanded: true }, 40 | }, 41 | }; 42 | 43 | export default meta; 44 | -------------------------------------------------------------------------------- /stories/useTab/styleSimpleTab.css: -------------------------------------------------------------------------------- 1 | .tabs-stepper { 2 | position: relative; 3 | width: 750px; 4 | } 5 | .fade { 6 | transition: opacity .15s linear; 7 | } 8 | .nav { 9 | display: flex; 10 | flex-wrap: wrap; 11 | padding-left: 0; 12 | margin-top: 0; 13 | list-style: none; 14 | } 15 | .nav-tabs { 16 | border-bottom: 1px solid #dee2e6; 17 | } 18 | .nav-link { 19 | display: block; 20 | padding: 0.5rem 1rem; 21 | text-decoration: none; 22 | transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out; 23 | } 24 | .nav-tabs .nav-link { 25 | margin-bottom: -1px; 26 | background: 0 0; 27 | border: 1px solid transparent; 28 | border-top-left-radius: 0.25rem; 29 | border-top-right-radius: 0.25rem; 30 | cursor: pointer; 31 | } 32 | .nav-tabs .nav-item.show .nav-link, 33 | .nav-tabs .nav-link.active { 34 | color: #495057; 35 | background-color: #ffffff; 36 | border-color: #dee2e6 #dee2e6 #fff; 37 | } 38 | .tab-content > .tab-pane { 39 | display: none; 40 | padding: 1rem 1rem 0.5rem; 41 | border-width: 1px; 42 | border-style: solid; 43 | border-color: #fff #dee2e6 #dee2e6; 44 | } 45 | .tab-content > .active { 46 | display: block; 47 | } 48 | .tab-content .tab-pane p { 49 | margin-top: 0; 50 | margin-bottom: 1rem; 51 | } -------------------------------------------------------------------------------- /stories/useTab/useTab.Simple.stories.tsx: -------------------------------------------------------------------------------- 1 | import React, {FC} from "react"; 2 | import useTab, {TabProps} from "../../src/useTab"; 3 | import './styleSimpleTab.css' 4 | import { Meta } from "@storybook/react"; 5 | 6 | export const Simple: FC = ({numTabs = 3, activeTab = 1}) => { 7 | const { 8 | currentTab, 9 | goToTab, 10 | triggerGoToPrevTab, 11 | triggerGoToNextTab 12 | } = useTab({numTabs, activeTab}) 13 | 14 | const navTabItems = [] 15 | navTabItems.push() 16 | for (let i=1; i<=numTabs; i++) { 17 | navTabItems.push() 18 | } 19 | navTabItems.push() 20 | 21 | const tabPaneItems = [] 22 | for (let i=1; i<=numTabs; i++) { 23 | tabPaneItems.push(
    24 |

    Content Tab {i}
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. 25 | Mauris tempor efficitur enim et euismod. Etiam quam orci, 26 | interdum nec tristique nec, hendrerit nec turpis. Vestibulum 27 | finibus auctor nibh id mattis. Curabitur facilisis eros est, 28 | non pharetra tellus semper et. Fusce et tincidunt justo. 29 | Morbi porta urna turpis, ac tristique mauris auctor nec. 30 | Donec eu tristique purus. Ut lobortis lacinia ante, 31 | aliquet placerat elit lobortis quis. Sed finibus, lectus id congue 32 | euismod, dolor mauris feugiat nulla, ac pellentesque augue leo 33 | vitae sem. Class aptent taciti sociosqu ad litora torquent per 34 | conubia nostra, per inceptos himenaeos. Aenean sollicitudin 35 | imperdiet aliquet.

    36 |
    ) 37 | } 38 | 39 | return ( 40 |
    41 | 46 |
    47 | {tabPaneItems} 48 |
    49 |
    50 | ); 51 | }; 52 | 53 | const meta: Meta = { 54 | title: 'useTab', 55 | component: Simple, 56 | argTypes: {}, 57 | parameters: { 58 | controls: { expanded: true }, 59 | }, 60 | } 61 | 62 | export default meta; -------------------------------------------------------------------------------- /stories/useTootip/useTooltip.Simple.stories.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import useTooltip, { TooltipProps } from '../../src/useTooltip'; 3 | import { Meta } from '@storybook/react'; 4 | 5 | export const Simple: FC = ({ 6 | isVisible: isVisibleDefault = false, 7 | showOnClick = false, 8 | closeTooltipOnClickOutside = false, 9 | hiddenTooltipOnTriggerLeave = true, 10 | }) => { 11 | const { trigger, tooltip, isVisible } = useTooltip({ 12 | isVisible: isVisibleDefault, 13 | showOnClick, 14 | closeTooltipOnClickOutside, 15 | hiddenTooltipOnTriggerLeave, 16 | }); 17 | 18 | return ( 19 |
    20 |

    21 | To open a tooltip{' '} 22 | 26 | hover here 27 | {isVisible && ( 28 |

    36 |

    Lorem ipusm dolor sit amet

    37 |
    38 | )} 39 | 40 |

    41 |
    42 | ); 43 | }; 44 | 45 | const meta: Meta = { 46 | title: 'useTooltip', 47 | component: Simple, 48 | argTypes: {}, 49 | parameters: { 50 | controls: { expanded: true }, 51 | }, 52 | }; 53 | 54 | export default meta; 55 | -------------------------------------------------------------------------------- /test/blah.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | 4 | describe('Thing', () => { 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(
    , div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // see https://www.typescriptlang.org/tsconfig to better understand tsconfigs 3 | "include": ["src", "types"], 4 | "compilerOptions": { 5 | "module": "esnext", 6 | "lib": ["dom", "esnext"], 7 | "importHelpers": true, 8 | // output .d.ts declaration files for consumers 9 | "declaration": true, 10 | // output .js.map sourcemap files for consumers 11 | "sourceMap": true, 12 | // match output dir to input dir. e.g. dist/index instead of dist/src/index 13 | "rootDir": "./src", 14 | // stricter type-checking for stronger correctness. Recommended by TS 15 | "strict": true, 16 | // linter checks for common issues 17 | "noImplicitReturns": true, 18 | "noFallthroughCasesInSwitch": true, 19 | // noUnused* overlap with @typescript-eslint/no-unused-vars, can disable if duplicative 20 | "noUnusedLocals": true, 21 | "noUnusedParameters": true, 22 | // use Node's module resolution algorithm, instead of the legacy TS one 23 | "moduleResolution": "node", 24 | // transpile JSX to React.createElement 25 | "jsx": "react", 26 | // interop between ESM and CJS modules. Recommended by TS 27 | "esModuleInterop": true, 28 | // significant perf increase by skipping checking .d.ts files, particularly those in node_modules. Recommended by TS 29 | "skipLibCheck": true, 30 | // error out if import and file system have a casing mismatch. Recommended by TS 31 | "forceConsistentCasingInFileNames": true, 32 | // `tsdx build` ignores this option, but it is commonly used when type-checking separately with `tsc` 33 | "noEmit": true, 34 | } 35 | } 36 | --------------------------------------------------------------------------------