├── .changeset ├── README.md └── config.json ├── .codesandbox └── ci.json ├── .eslintignore ├── .eslintrc ├── .github ├── label.yml └── workflows │ ├── main.yml │ ├── release.yml │ └── size.yml ├── .gitignore ├── .husky ├── .gitignore ├── commit-msg └── pre-commit ├── .npmrc ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── commitlint.config.js ├── docs ├── .babelrc ├── .eslintrc ├── CHANGELOG.md ├── README.md ├── configs │ ├── docs-sidebar.json │ ├── search-meta.json │ └── site-config.ts ├── layouts │ ├── index.tsx │ └── mdx.tsx ├── next-env.d.ts ├── next-redirect.js ├── next.config.js ├── package.json ├── pages │ ├── 404.tsx │ ├── [[...slug]].tsx │ ├── _app.tsx │ ├── _document.tsx │ ├── getting-started.mdx │ ├── hooks │ │ ├── index.mdx │ │ ├── use-async-effect.mdx │ │ ├── use-clipboard.mdx │ │ ├── use-constant.mdx │ │ ├── use-debounce.mdx │ │ ├── use-disclosure.mdx │ │ ├── use-has-mounted.mdx │ │ ├── use-lock-body-scroll.mdx │ │ ├── use-media.mdx │ │ ├── use-merge-refs.mdx │ │ ├── use-onclick-outside.mdx │ │ ├── use-pagination.mdx │ │ ├── use-previous.mdx │ │ └── use-why-did-you-update.mdx │ ├── index.mdx │ └── utils │ │ ├── call-all-handlers.mdx │ │ ├── clean-children.mdx │ │ ├── create-context.mdx │ │ ├── index.mdx │ │ ├── is-ssr.mdx │ │ ├── noop.mdx │ │ ├── sleep.mdx │ │ └── truncate.mdx ├── public │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── fonts │ │ └── Inter.woff2 │ ├── robots.txt │ └── thumbnail.jpeg ├── src │ ├── components │ │ ├── algolia-search.tsx │ │ ├── color-palette.tsx │ │ ├── container.tsx │ │ ├── discord-strip.tsx │ │ ├── docs │ │ │ ├── discord-logo.tsx │ │ │ └── icon.tsx │ │ ├── edit-page-button.tsx │ │ ├── font-face.tsx │ │ ├── footer.tsx │ │ ├── header-nav-link.tsx │ │ ├── header.tsx │ │ ├── logo-mark.tsx │ │ ├── logo.tsx │ │ ├── mdx-components │ │ │ ├── anchor.tsx │ │ │ ├── codeblock │ │ │ │ ├── code-container.tsx │ │ │ │ ├── codeblock.tsx │ │ │ │ ├── copy-button.tsx │ │ │ │ ├── highlight.tsx │ │ │ │ ├── react-live-block.tsx │ │ │ │ ├── react-live-scope.tsx │ │ │ │ └── styles.tsx │ │ │ ├── component-links.tsx │ │ │ ├── icons-list.tsx │ │ │ ├── index.ts │ │ │ ├── inline-code.tsx │ │ │ ├── linked-heading.tsx │ │ │ ├── mdx-components.tsx │ │ │ ├── pre.tsx │ │ │ ├── table.tsx │ │ │ └── video-player.tsx │ │ ├── mobile-nav.tsx │ │ ├── omni-search.tsx │ │ ├── page-container.tsx │ │ ├── page-transition.tsx │ │ ├── pagination.tsx │ │ ├── props-table.tsx │ │ ├── resource-card.tsx │ │ ├── search.styles.tsx │ │ ├── seo.tsx │ │ ├── sidebar │ │ │ ├── sidebar-category.tsx │ │ │ ├── sidebar-link.tsx │ │ │ └── sidebar.tsx │ │ ├── storybook-icon.tsx │ │ └── table-of-content.tsx │ ├── hooks │ │ ├── use-route-changed.ts │ │ └── use-scrollspy.ts │ └── utils │ │ ├── calc-read-time.ts │ │ ├── convert-backticks-to-inline-code.tsx │ │ ├── find-route-by-path.ts │ │ ├── get-route-context.ts │ │ ├── load-mdx-dir.ts │ │ ├── load-mdx.ts │ │ ├── load-script.ts │ │ ├── mdx-utils.ts │ │ └── seo.ts ├── theme.ts └── tsconfig.json ├── example ├── .gitignore ├── CHANGELOG.md ├── index.html ├── index.tsx ├── package.json └── tsconfig.json ├── package.json ├── packages ├── eslint-config │ ├── CHANGELOG.md │ ├── README.md │ ├── index.js │ └── package.json ├── hooks │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── index.tsx │ │ ├── test │ │ │ ├── useClipboard.test.ts │ │ │ ├── useConstant.test.tsx │ │ │ ├── useDisclosure.test.ts │ │ │ ├── useHasMounted.test.tsx │ │ │ ├── usePagination.test.ts │ │ │ └── usePrevious.test.tsx │ │ ├── useAsyncEffect.ts │ │ ├── useClipboard.ts │ │ ├── useConstant.ts │ │ ├── useDebounce.ts │ │ ├── useDisclosure.ts │ │ ├── useForceUpdate.ts │ │ ├── useHasMounted.ts │ │ ├── useLockBodyScroll.ts │ │ ├── useMedia.ts │ │ ├── useMergeRefs.ts │ │ ├── useOnClickOutside.ts │ │ ├── usePagination.ts │ │ ├── usePrevious.ts │ │ └── useWhyDidYouUpdate.ts │ ├── tsconfig.build.json │ └── tsdx.config.js └── utils │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ ├── callAllHandlers.ts │ ├── cleanChildren.ts │ ├── createContext.tsx │ ├── index.ts │ ├── isSSR.ts │ ├── noop.ts │ ├── sleep.ts │ ├── test │ │ ├── callAllHandlers.test.ts │ │ ├── sleep.test.ts │ │ └── truncate.test.ts │ ├── truncate.ts │ └── types.ts │ ├── tsconfig.build.json │ └── tsdx.config.js ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── scripts └── get-search-meta.ts ├── tsconfig.build.json ├── tsconfig.json ├── tsdx.config.base.js ├── turbo.json └── types └── global.d.ts /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by 4 | `@changesets/cli`, a build tool that works with multi-package repos, or 5 | single-package repos to help you version and publish your code. You can find the 6 | full documentation for it 7 | [in our repository](https://github.com/changesets/changesets) 8 | 9 | We have a quick list of common questions to get you started engaging with this 10 | project in 11 | [our documentation](https://github.com/changesets/changesets/blob/master/docs/common-questions.md) 12 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@1.3.0/schema.json", 3 | "changelog": [ 4 | "@changesets/changelog-github", 5 | { "repo": "dwarvesf/react-toolkit" } 6 | ], 7 | "commit": false, 8 | "linked": [], 9 | "access": "public", 10 | "baseBranch": "master", 11 | "updateInternalDependencies": "patch", 12 | "ignore": [] 13 | } 14 | -------------------------------------------------------------------------------- /.codesandbox/ci.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": ["packages/hooks", "packages/utils"], 3 | "sandboxes": ["new", "vanilla"] 4 | } 5 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | 2 | dist 3 | node_modules 4 | coverage 5 | .next 6 | build 7 | docs 8 | scripts -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "extends": ["./packages/eslint-config"], 5 | "parserOptions": { 6 | "ecmaVersion": 10, 7 | "ecmaFeatures": { 8 | "jsx": true 9 | }, 10 | "project": ["tsconfig.json"] 11 | }, 12 | "env": { 13 | "es6": true, 14 | "browser": true, 15 | "node": true 16 | }, 17 | "rules": {} 18 | } 19 | -------------------------------------------------------------------------------- /.github/label.yml: -------------------------------------------------------------------------------- 1 | examples: 2 | - example/* 3 | - example/**/* 4 | 5 | hooks: 6 | - packages/hooks/* 7 | - packages/hooks/**/* 8 | 9 | utils: 10 | - packages/utils/* 11 | - packages/utils/**/* 12 | 13 | eslint: 14 | - packages/eslint-config/* 15 | 16 | repo: 17 | - ./* 18 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Unit Test 2 | on: [push] 3 | jobs: 4 | test: 5 | name: Build and test on Node ${{ matrix.node }} and ${{ matrix.os }} 6 | 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | matrix: 10 | node: ['16.x'] 11 | os: [ubuntu-latest] 12 | 13 | steps: 14 | - name: Checkout repo 15 | uses: actions/checkout@v2 16 | 17 | - name: Use Node ${{ matrix.node }} 18 | uses: actions/setup-node@v1 19 | with: 20 | node-version: ${{ matrix.node }} 21 | 22 | - name: Install deps and build (with cache) 23 | uses: pnpm/action-setup@v2 24 | with: 25 | version: 8.5.1 26 | - run: pnpm i 27 | - run: pnpm build 28 | - run: rm -rf .next 29 | 30 | - name: Test 31 | run: pnpm test:ci 32 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | branches: 5 | - master 6 | jobs: 7 | release: 8 | timeout-minutes: 20 9 | 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | with: 15 | # This makes action fetch all Git history so that Changesets can generate changelogs with the correct commits 16 | fetch-depth: 0 17 | 18 | - name: Use Node.js 16.x 19 | uses: actions/setup-node@v1 20 | with: 21 | version: 16.x 22 | 23 | - name: Install pnpm 24 | uses: pnpm/action-setup@v2 25 | with: 26 | version: 8.5.1 27 | 28 | - name: Install Dependencies 29 | run: pnpm install 30 | 31 | - name: Create Release Pull Request or Publish to npm 32 | uses: changesets/action@master 33 | with: 34 | publish: pnpm release 35 | commit: 'release: bump package(s)' 36 | env: 37 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 38 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 39 | -------------------------------------------------------------------------------- /.github/workflows/size.yml: -------------------------------------------------------------------------------- 1 | name: Compressed Size 2 | 3 | on: [pull_request] 4 | 5 | env: 6 | HUSKY: 0 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Setup Node.js 16 14 | uses: actions/setup-node@v4 15 | with: 16 | node-version: 16 17 | - uses: pnpm/action-setup@v2 18 | with: 19 | version: 8.5.1 20 | run_install: false 21 | - uses: actions/checkout@v3 22 | - uses: preactjs/compressed-size-action@2.5.0 23 | with: 24 | repo-token: '${{ secrets.GITHUB_TOKEN }}' 25 | build-script: 'build:packages' 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .DS_Store 3 | node_modules 4 | dist 5 | 6 | node_modules 7 | package-lock.json 8 | yarn.lock 9 | !/yarn.lock 10 | 11 | .yalc 12 | yalc.lock 13 | 14 | .next 15 | out -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no-install commitlint --edit "" 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | pnpm lint-staged 5 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | auto-install-peers=false 2 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v16 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | coverage 4 | .next 5 | build 6 | CHANGELOG.md 7 | out 8 | .cache -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "bracketSpacing": true, 3 | "jsxBracketSameLine": false, 4 | "jsxSingleQuote": false, 5 | "printWidth": 80, 6 | "proseWrap": "always", 7 | "semi": false, 8 | "singleQuote": true, 9 | "tabWidth": 2, 10 | "trailingComma": "all" 11 | } 12 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to make participation in our project and our 7 | community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and 9 | expression, level of experience, education, socio-economic status, nationality, 10 | personal appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | - Using welcoming and inclusive language 18 | - Being respectful of differing viewpoints and experiences 19 | - Gracefully accepting constructive criticism 20 | - Focusing on what is best for the community 21 | - Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | - The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | - Trolling, insulting/derogatory comments, and personal or political attacks 28 | - Public or private harassment 29 | - Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | - Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or reject 41 | comments, commits, code, wiki edits, issues, and other contributions that are 42 | not aligned to this Code of Conduct, or to ban temporarily or permanently any 43 | contributor for other behaviors that they deem inappropriate, threatening, 44 | offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at **team@d.foundation.com**. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an 62 | incident. Further details of specific enforcement policies may be posted 63 | separately. 64 | 65 | Project maintainers who do not follow or enforce the Code of Conduct in good 66 | faith may face temporary or permanent repercussions as determined by other 67 | members of the project's leadership. 68 | 69 | ## Attribution 70 | 71 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 72 | version 1.4, available at 73 | https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 74 | 75 | [homepage]: https://www.contributor-covenant.org 76 | 77 | For answers to common questions about this code of conduct, see 78 | https://www.contributor-covenant.org/faq 79 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 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 |

2 | React Toolkit 3 |

4 |

5 | 6 | Dwarves Foundation 7 | 8 | 9 | Dwarves Foundation Discord 10 | 11 |

12 | 13 | A library of React hooks and utilities for building robust React applications. 14 | 15 | ## Packages 16 | 17 | There are 3 packages by default: 18 | 19 | - `@dwarvesf/react-hooks` contains generic React hooks. 20 | - `@dwarvesf/react-utils` contains handy resuable functions. 21 | - `@dwarvesf/react-eslint-config` contains an ESLint config with TypeScript and 22 | Prettier support. 23 | 24 | ## Documentation 25 | 26 | For full documentation, visit 27 | [react.toolkit.d.foundation](https://react.toolkit.d.foundation/). 28 | 29 | ## Contributing 30 | 31 | Pelase follow our [contributing guidelines](/CONTRIBUTING.md). 32 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'], 3 | rules: { 4 | 'header-max-length': [2, 'always', 66], // github commit without being trucated 5 | 'type-enum': [ 6 | 2, 7 | 'always', 8 | [ 9 | 'feat', 10 | 'fix', 11 | 'release', 12 | 'docs', 13 | 'style', 14 | 'refactor', 15 | 'perf', 16 | 'test', 17 | 'chore', 18 | 'revert', 19 | 'ci', 20 | 'build', 21 | ], 22 | ], 23 | }, 24 | 'scope-enum': [2, 'always', ['utils', 'hooks', 'eslint']], 25 | 'scope-empty': [1, 'never'], 26 | 'scope-case': [2, 'always', ['lower-case']], 27 | } 28 | -------------------------------------------------------------------------------- /docs/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["next/babel"], 3 | "plugins": ["superjson-next"] 4 | } 5 | -------------------------------------------------------------------------------- /docs/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.eslintrc", 3 | "parser": "@typescript-eslint/parser", 4 | "overrides": [ 5 | { 6 | "parserOptions": { 7 | "ecmaVersion": 10, 8 | "ecmaFeatures": { 9 | "jsx": true 10 | }, 11 | "project": ["./tsconfig.json"] 12 | }, 13 | "files": ["**/*.ts?(x)", "**/*.js?(x)"], 14 | "rules": { 15 | "react/react-in-jsx-scope": "off", 16 | "no-shadow": "off" 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /docs/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @dwarvesf/react-toolkit-docs 2 | 3 | ## 0.3.1 4 | 5 | ### Patch Changes 6 | 7 | - Updated dependencies 8 | [[`01bce8c`](https://github.com/dwarvesf/react-toolkit/commit/01bce8cba4145584b5e4e78978e2b7cb2e3bdb22)]: 9 | - @dwarvesf/react-hooks@0.8.2 10 | - @dwarvesf/react-utils@0.4.2 11 | 12 | ## 0.3.0 13 | 14 | ### Minor Changes 15 | 16 | - [#56](https://github.com/dwarvesf/react-toolkit/pull/56) 17 | [`b4f230f`](https://github.com/dwarvesf/react-toolkit/commit/b4f230f017bc54be899c21165d22d22e2c87a7bb) 18 | Thanks [@zlatanpham](https://github.com/zlatanpham)! - Expose onOpenChange 19 | 20 | ### Patch Changes 21 | 22 | - Updated dependencies 23 | [[`b4f230f`](https://github.com/dwarvesf/react-toolkit/commit/b4f230f017bc54be899c21165d22d22e2c87a7bb)]: 24 | - @dwarvesf/react-hooks@0.8.0 25 | 26 | ## 0.2.0 27 | 28 | ### Minor Changes 29 | 30 | - [#39](https://github.com/dwarvesf/react-toolkit/pull/39) 31 | [`d4774a9`](https://github.com/dwarvesf/react-toolkit/commit/d4774a969704ce0b012c0c0b455d756d1de75cd7) 32 | Thanks [@tuanddd](https://github.com/tuanddd)! - Add `usePagination` hook 33 | 34 | ### Patch Changes 35 | 36 | - Updated dependencies 37 | [[`d4774a9`](https://github.com/dwarvesf/react-toolkit/commit/d4774a969704ce0b012c0c0b455d756d1de75cd7)]: 38 | - @dwarvesf/react-hooks@0.7.0 39 | 40 | ## 0.1.3 41 | 42 | ### Patch Changes 43 | 44 | - [#37](https://github.com/dwarvesf/react-toolkit/pull/37) 45 | [`abffce6`](https://github.com/dwarvesf/react-toolkit/commit/abffce61719c0d123df365b767379fd4bb09692b) 46 | Thanks [@tuanddd](https://github.com/tuanddd)! - Add missing `prepublish` 47 | script that causes no build ouput on release 48 | 49 | - Updated dependencies 50 | [[`abffce6`](https://github.com/dwarvesf/react-toolkit/commit/abffce61719c0d123df365b767379fd4bb09692b)]: 51 | - @dwarvesf/react-hooks@0.6.2 52 | - @dwarvesf/react-utils@0.4.1 53 | 54 | ## 0.1.2 55 | 56 | ### Patch Changes 57 | 58 | - Updated dependencies 59 | [[`f1516e1`](https://github.com/dwarvesf/react-toolkit/commit/f1516e15473687ab4000c3336ee1706a1ecb8c11)]: 60 | - @dwarvesf/react-utils@0.4.0 61 | - @dwarvesf/react-hooks@0.6.1 62 | 63 | ## 0.1.1 64 | 65 | ### Patch Changes 66 | 67 | - [#32](https://github.com/dwarvesf/react-toolkit/pull/32) 68 | [`56d68c3`](https://github.com/dwarvesf/react-toolkit/commit/56d68c39c57d951be7a6aa5a9e972c0f630a9199) 69 | Thanks [@trankhacvy](https://github.com/trankhacvy)! - add useHasMounted hook 70 | 71 | - Updated dependencies 72 | [[`56d68c3`](https://github.com/dwarvesf/react-toolkit/commit/56d68c39c57d951be7a6aa5a9e972c0f630a9199)]: 73 | - @dwarvesf/react-hooks@0.6.0 74 | 75 | ## 0.1.0 76 | 77 | ### Minor Changes 78 | 79 | - [#29](https://github.com/dwarvesf/react-toolkit/pull/29) 80 | [`4ceb7a6`](https://github.com/dwarvesf/react-toolkit/commit/4ceb7a6d6c26c99d003f9d7643c94811d9282c88) 81 | Thanks [@tuanddd](https://github.com/tuanddd)! - Add maskChar as option 82 | 83 | ### Patch Changes 84 | 85 | - Updated dependencies 86 | [[`4ceb7a6`](https://github.com/dwarvesf/react-toolkit/commit/4ceb7a6d6c26c99d003f9d7643c94811d9282c88)]: 87 | - @dwarvesf/react-utils@0.3.0 88 | - @dwarvesf/react-hooks@0.5.1 89 | 90 | ## 0.0.3 91 | 92 | ### Patch Changes 93 | 94 | - [#27](https://github.com/dwarvesf/react-toolkit/pull/27) 95 | [`4873f81`](https://github.com/dwarvesf/react-toolkit/commit/4873f81ed44b88aee71424f78246eb6fd57af186) 96 | Thanks [@zlatanpham](https://github.com/zlatanpham)! - Add onClickOutside hook 97 | 98 | - Updated dependencies 99 | [[`4873f81`](https://github.com/dwarvesf/react-toolkit/commit/4873f81ed44b88aee71424f78246eb6fd57af186)]: 100 | - @dwarvesf/react-hooks@0.5.0 101 | 102 | ## 0.0.2 103 | 104 | ### Patch Changes 105 | 106 | - Updated dependencies 107 | [[`e7a1787`](https://github.com/dwarvesf/react-toolkit/commit/e7a17875bbf7fbf580a13a5aff6ff742d3f3a941)]: 108 | - @dwarvesf/react-hooks@0.4.0 109 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # React Toolkit Docs 2 | -------------------------------------------------------------------------------- /docs/configs/docs-sidebar.json: -------------------------------------------------------------------------------- 1 | { 2 | "routes": [ 3 | { 4 | "title": "Getting Started", 5 | "heading": true, 6 | "routes": [ 7 | { 8 | "title": "Installation", 9 | "path": "/getting-started" 10 | }, 11 | { 12 | "title": "Hooks", 13 | "path": "/hooks" 14 | }, 15 | { 16 | "title": "Utilities", 17 | "path": "/utils" 18 | } 19 | ] 20 | }, 21 | { 22 | "title": "Hooks", 23 | "heading": true, 24 | "routes": [ 25 | { 26 | "title": "useClipboard", 27 | "path": "/hooks/use-clipboard" 28 | }, 29 | { 30 | "title": "useDisclosure", 31 | "path": "/hooks/use-disclosure" 32 | }, 33 | { 34 | "title": "useMergeRefs", 35 | "path": "/hooks/use-merge-refs" 36 | }, 37 | { 38 | "title": "useDebounce", 39 | "path": "/hooks/use-debounce" 40 | }, 41 | { 42 | "title": "useLockBodyScroll", 43 | "path": "/hooks/use-lock-body-scroll" 44 | }, 45 | { 46 | "title": "useOnClickOutside", 47 | "path": "/hooks/use-onclick-outside" 48 | }, 49 | { 50 | "title": "useMedia", 51 | "path": "/hooks/use-media" 52 | }, 53 | { 54 | "title": "useConstant", 55 | "path": "/hooks/use-constant" 56 | }, 57 | { 58 | "title": "usePrevious", 59 | "path": "/hooks/use-previous" 60 | }, 61 | { 62 | "title": "useAsyncEffect", 63 | "path": "/hooks/use-async-effect" 64 | }, 65 | { 66 | "title": "useWhyDidYouUpdate", 67 | "path": "/hooks/use-why-did-you-update" 68 | }, 69 | { 70 | "title": "useHasMounted", 71 | "path": "/hooks/use-has-mounted" 72 | }, 73 | { 74 | "title": "usePagination", 75 | "path": "/hooks/use-pagination" 76 | } 77 | ] 78 | }, 79 | { 80 | "title": "Utilities", 81 | "heading": true, 82 | "routes": [ 83 | { 84 | "title": "createContext", 85 | "path": "/utils/create-context" 86 | }, 87 | { 88 | "title": "callAllHandlers", 89 | "path": "/utils/call-all-handlers" 90 | }, 91 | { 92 | "title": "noop", 93 | "path": "/utils/noop" 94 | }, 95 | { 96 | "title": "isSSR", 97 | "path": "/utils/is-ssr" 98 | }, 99 | { 100 | "title": "truncate", 101 | "path": "/utils/truncate" 102 | }, 103 | { 104 | "title": "cleanChildren", 105 | "path": "/utils/clean-children" 106 | }, 107 | { 108 | "title": "sleep", 109 | "path": "/utils/sleep" 110 | } 111 | ] 112 | } 113 | ] 114 | } 115 | -------------------------------------------------------------------------------- /docs/configs/site-config.ts: -------------------------------------------------------------------------------- 1 | const baseUrl = 'https://github.com/dwarvesf/react-toolkit' 2 | 3 | const siteConfig = { 4 | copyright: `Copyright © ${new Date().getFullYear()} Dwarvesf. All Rights Reserved.`, 5 | author: { 6 | site: 'https://dwarves.foundation/', 7 | github: 'https://github.com/dwarvesf', 8 | twitter: 'https://twitter.com/dwarvesf', 9 | linkedin: 'https://www.linkedin.com/company/dwarvesf/', 10 | discord: 'https://discord.com/invite/Y2vvH9rQE4', 11 | }, 12 | repo: { 13 | url: baseUrl, 14 | editUrl: `${baseUrl}/edit/main/website/pages`, 15 | blobUrl: `${baseUrl}/blob/main`, 16 | }, 17 | discord: { 18 | url: 'https://discord.com/invite/Y2vvH9rQE4', 19 | }, 20 | seo: { 21 | title: 'React Toolkit', 22 | titleTemplate: '%s - React Toolkit', 23 | description: 24 | 'Simple, Modular React Hooks and Utilities to build robust React applications.', 25 | siteUrl: '', 26 | twitter: { 27 | handle: '@dwarvesf', 28 | site: '@dwarvesf', 29 | cardType: 'summary_large_image', 30 | }, 31 | openGraph: { 32 | type: 'website', 33 | locale: 'en_US', 34 | url: '', 35 | title: 'React Toolkit', 36 | description: 37 | 'Simple, Modular React Hooks and Utilities to build robust React applications.', 38 | site_name: 39 | 'React Toolkit: Simple, Modular React Hooks and Utilities to build robust React applications.', 40 | images: [ 41 | { 42 | url: '/thumbnail.jpeg', 43 | width: 1240, 44 | height: 480, 45 | alt: 'React Toolkit: Simple, Modular React Hooks and Utilities to build robust React applications.', 46 | }, 47 | ], 48 | }, 49 | }, 50 | } 51 | 52 | export default siteConfig 53 | -------------------------------------------------------------------------------- /docs/layouts/index.tsx: -------------------------------------------------------------------------------- 1 | import PageContainer from 'components/page-container' 2 | import dynamic from 'next/dynamic' 3 | 4 | const MDXLayout = dynamic(() => import('layouts/mdx')) 5 | 6 | export default function DefaultLayout({ children, frontMatter }) { 7 | const { slug } = frontMatter 8 | 9 | const layoutMap = { 10 | '': {children}, 11 | default: ( 12 | {children} 13 | ), 14 | } 15 | 16 | const layout = Object.entries(layoutMap).find(([path]) => 17 | String(slug).startsWith(`/${path}`), 18 | ) 19 | 20 | if (!layout) return layoutMap.default 21 | 22 | return layout[1] 23 | } 24 | -------------------------------------------------------------------------------- /docs/layouts/mdx.tsx: -------------------------------------------------------------------------------- 1 | import * as chakraComponents from '@chakra-ui/react' 2 | import { MDXProvider } from '@mdx-js/react' 3 | import { MDXComponents } from 'components/mdx-components' 4 | import PageContainer from 'components/page-container' 5 | import Pagination from 'components/pagination' 6 | import Sidebar from 'components/sidebar/sidebar' 7 | import docsSidebar from 'configs/docs-sidebar.json' 8 | import * as React from 'react' 9 | import { findRouteByPath, removeFromLast } from 'utils/find-route-by-path' 10 | import { getRouteContext } from 'utils/get-route-context' 11 | 12 | export function getRoutes(slug: string) { 13 | // for home page, use docs sidebat 14 | if (slug === '/') return docsSidebar.routes 15 | 16 | const configMap = { 17 | '': docsSidebar, 18 | } 19 | 20 | const [, sidebar] = 21 | Object.entries(configMap).find(([path]) => slug.startsWith(path)) ?? [] 22 | 23 | return sidebar?.routes ?? [] 24 | } 25 | 26 | export function MDXLayoutProvider({ children }) { 27 | return ( 28 | 29 | {children} 30 | 31 | ) 32 | } 33 | 34 | interface MDXLayoutProps { 35 | frontmatter: any 36 | children: React.ReactNode 37 | } 38 | 39 | export default function MDXLayout(props: MDXLayoutProps) { 40 | const { frontmatter, children } = props 41 | const routes = getRoutes(frontmatter.slug) 42 | 43 | const route = findRouteByPath(removeFromLast(frontmatter.slug, '#'), routes) 44 | const routeContext = getRouteContext(route, routes) 45 | 46 | return ( 47 | 48 | } 51 | pagination={ 52 | 56 | } 57 | > 58 | {children} 59 | 60 | 61 | ) 62 | } 63 | -------------------------------------------------------------------------------- /docs/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | 5 | // NOTE: This file should not be edited 6 | // see https://nextjs.org/docs/basic-features/typescript for more information. 7 | -------------------------------------------------------------------------------- /docs/next-redirect.js: -------------------------------------------------------------------------------- 1 | async function redirect() { 2 | return [] 3 | } 4 | 5 | module.exports = redirect 6 | -------------------------------------------------------------------------------- /docs/next.config.js: -------------------------------------------------------------------------------- 1 | const withPlugins = require('next-compose-plugins') 2 | const withBundleAnalyzer = require('@next/bundle-analyzer')({ 3 | enabled: process.env.ANALYZE === 'true', 4 | }) 5 | 6 | const defaultConfig = { 7 | target: 'serverless', 8 | webpack: (config) => ({ 9 | ...config, 10 | externals: [...config.externals, 'sharp'], 11 | }), 12 | experimental: { 13 | optimizeFonts: true, 14 | modern: true, 15 | }, 16 | redirects: require('./next-redirect'), 17 | eslint: { 18 | // TODO: consider removing the setting once fixing all the ESLint warning 19 | // Warning: This allows production builds to successfully complete even if 20 | // your project has ESLint errors. 21 | ignoreDuringBuilds: true, 22 | }, 23 | } 24 | 25 | module.exports = withPlugins([withBundleAnalyzer], defaultConfig) 26 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dwarvesf/react-toolkit-docs", 3 | "version": "0.3.1", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next", 7 | "build": "next build && next export", 8 | "start": "next start", 9 | "clean": "rimraf .next .mdx-data", 10 | "analyze": "ANALYZE=true next build" 11 | }, 12 | "dependencies": { 13 | "@chakra-ui/cli": "1.5.0", 14 | "@chakra-ui/icons": "^1.0.15", 15 | "@chakra-ui/props-docs": "1.0.32", 16 | "@chakra-ui/react": "1.6.7", 17 | "@chakra-ui/skip-nav": "^1.1.11", 18 | "@chakra-ui/system": "1.6.7", 19 | "@chakra-ui/theme-tools": "1.2.0", 20 | "@chakra-ui/utils": "1.8.2", 21 | "@docsearch/react": "^3.0.0", 22 | "@docusaurus/utils": "^2.0.0-alpha.69", 23 | "@dwarvesf/react-hooks": "workspace:*", 24 | "@dwarvesf/react-utils": "workspace:*", 25 | "@emotion/react": "^11.4.1", 26 | "@emotion/styled": "^11.3.0", 27 | "@mdx-js/react": "^1.6.22", 28 | "@octokit/rest": "^18.0.12", 29 | "date-fns": "^2.16.1", 30 | "docsearch.js": "^2.6.3", 31 | "execa": "^5.0.0", 32 | "focus-visible": "5.2.0", 33 | "formik": "^2.2.5", 34 | "framer-motion": "^4.1.17", 35 | "github-slugger": "^1.3.0", 36 | "gray-matter": "^2.1.0", 37 | "highlight-words-core": "^1.2.2", 38 | "lodash": "^4.17.21", 39 | "match-sorter": "^6.1.0", 40 | "next": "11.1.2", 41 | "next-mdx-enhanced": "^5.0.0", 42 | "next-mdx-remote": "^3.0.4", 43 | "next-seo": "^4.17.0", 44 | "prism-react-renderer": "^1.1.1", 45 | "react": "^17.0.2", 46 | "react-dom": "^17.0.2", 47 | "react-focus-lock": "2.5.0", 48 | "react-icons": "^4.1.0", 49 | "react-live": "^3.1.1", 50 | "react-lorem-component": "latest", 51 | "react-multi-ref": "1.0.0", 52 | "react-player": "^2.9.0", 53 | "react-remove-scroll": "2.4.1", 54 | "react-spinners": "latest", 55 | "react-table": "latest", 56 | "remark": "^13.0.0", 57 | "remark-autolink-headings": "^6.0.1", 58 | "remark-emoji": "^2.1.0", 59 | "remark-images": "^2.0.0", 60 | "remark-mdx": "^1.6.22", 61 | "remark-slug": "^6.0.0", 62 | "remark-toc": "^7.0.0", 63 | "remark-unwrap-images": "^2.0.0", 64 | "scroll-into-view-if-needed": "^2.2.26", 65 | "superjson": "^1.7.4" 66 | }, 67 | "devDependencies": { 68 | "@next/bundle-analyzer": "^10.0.5", 69 | "@types/github-slugger": "^1.3.0", 70 | "@types/node": "^16.7.10", 71 | "@types/sharp": "^0.26.1", 72 | "babel-plugin-superjson-next": "^0.3.0", 73 | "eslint-config-next": "^11.1.2", 74 | "next-compose-plugins": "^2.2.1" 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /docs/pages/404.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Heading, Text, VStack } from '@chakra-ui/react' 2 | import { FaHome } from 'react-icons/fa' 3 | import Header from 'components/header' 4 | import SEO from 'components/seo' 5 | import * as React from 'react' 6 | import NextLink from 'next/link' 7 | 8 | const NotFoundPage = () => { 9 | return ( 10 | <> 11 | 12 |
13 | 20 | 404 | Page Not Found 21 | 22 | You just hit a route that doesn't exist... the sadness.😢 23 | 24 | 25 | 34 | 35 | 36 | 37 | ) 38 | } 39 | 40 | export default NotFoundPage 41 | -------------------------------------------------------------------------------- /docs/pages/[[...slug]].tsx: -------------------------------------------------------------------------------- 1 | import { InferGetStaticPropsType } from 'next' 2 | import { MDXRemote } from 'next-mdx-remote' 3 | import loadMDXFromPages from 'utils/load-mdx-dir' 4 | import { MDXComponents } from 'components/mdx-components' 5 | import Layout from 'layouts' 6 | 7 | const CONTENT_PATH = '' 8 | 9 | export default function Page({ 10 | mdxSource, 11 | frontMatter, 12 | }: InferGetStaticPropsType) { 13 | return ( 14 | 15 | 16 | 17 | ) 18 | } 19 | 20 | export async function getStaticPaths() { 21 | const pages = await loadMDXFromPages(CONTENT_PATH) 22 | const paths = pages.map(({ slug }) => { 23 | return { 24 | params: { 25 | slug: slug 26 | .slice(1) // remove the first `/` 27 | .split('/') // split to get an array 28 | .filter((item) => item !== CONTENT_PATH), // remove the CONTENT_PATH since this isnt needed in static paths 29 | }, 30 | } 31 | }) 32 | 33 | return { 34 | paths: [ 35 | ...paths, 36 | { params: { slug: [''] } }, 37 | { params: { slug: ['hooks'] } }, 38 | { params: { slug: ['utils'] } }, 39 | ], 40 | fallback: false, 41 | } 42 | } 43 | 44 | export async function getStaticProps({ params }) { 45 | const { slug } = params 46 | const combinedPageSlug = Array.isArray(slug) 47 | ? `${[CONTENT_PATH, ...slug].join('/')}` 48 | : CONTENT_PATH 49 | const pages = await loadMDXFromPages(CONTENT_PATH) 50 | 51 | const page = pages.find((page) => { 52 | return ( 53 | combinedPageSlug === page.slug || 54 | (combinedPageSlug === CONTENT_PATH && page.slug === '/index') || 55 | (combinedPageSlug === '/hooks' && page.slug === '/hooks/index') || 56 | (combinedPageSlug === '/utils' && page.slug === '/utils/index') 57 | ) 58 | }) 59 | 60 | if (!page) { 61 | throw new Error(`No content found for slug "${slug.join('/')}"`) 62 | } 63 | 64 | const { mdxSource, ...frontMatter } = page 65 | 66 | return { 67 | props: { 68 | mdxSource, 69 | frontMatter, 70 | }, 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /docs/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import { ChakraProvider } from '@chakra-ui/react' 2 | import FontFace from 'components/font-face' 3 | import { DefaultSeo } from 'next-seo' 4 | import Head from 'next/head' 5 | import React from 'react' 6 | import theme from 'theme' 7 | import { getSeo } from 'utils/seo' 8 | 9 | const App = ({ Component, pageProps }) => { 10 | const seo = getSeo() 11 | return ( 12 | <> 13 | 14 | 15 | 16 | 17 | 23 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | ) 40 | } 41 | 42 | export default App 43 | -------------------------------------------------------------------------------- /docs/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import { ColorModeScript } from '@chakra-ui/react' 2 | import NextDocument, { 3 | DocumentContext, 4 | Head, 5 | Html, 6 | Main, 7 | NextScript, 8 | } from 'next/document' 9 | import React from 'react' 10 | 11 | class Document extends NextDocument { 12 | static getInitialProps(ctx: DocumentContext) { 13 | return NextDocument.getInitialProps(ctx) 14 | } 15 | 16 | render() { 17 | return ( 18 | 19 | 20 | 27 | 28 | 29 | 30 |
31 | 32 | 33 | 34 | ) 35 | } 36 | } 37 | 38 | export default Document 39 | -------------------------------------------------------------------------------- /docs/pages/getting-started.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started 3 | description: How to install and set up Dwarvesf React Toolkit in your project 4 | --- 5 | 6 | ## Installation 7 | 8 | Inside your React project directory, install React Toolkit packages by either 9 | `npm` or `yarn`: 10 | 11 | ### React Hooks 12 | 13 | ```bash 14 | npm i @dwarvesf/react-hooks 15 | ``` 16 | 17 | ```bash 18 | yarn add @dwarvesf/react-hooks 19 | ``` 20 | 21 | ### React Utilities 22 | 23 | ```bash 24 | npm i @dwarvesf/react-utils 25 | ``` 26 | 27 | ```bash 28 | yarn add @dwarvesf/react-utils 29 | ``` 30 | -------------------------------------------------------------------------------- /docs/pages/hooks/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: React Hooks 3 | description: 4 | 'Hooks that allow you to build an interface using any component library.' 5 | --- 6 | 7 | Hooks allow you to build an interface using any component library. 8 | 9 | ## Installation 10 | 11 | ```bash 12 | npm i @dwarvesf/react-hooks 13 | ``` 14 | 15 | ```bash 16 | yarn add @dwarvesf/react-hooks 17 | ``` 18 | 19 | ## Import 20 | 21 | Pick and choose a hook you'd like to use and import them. 22 | 23 | ```js 24 | import { useConstant, useDebounce } from '@dwarvesf/react-hooks' 25 | ``` 26 | -------------------------------------------------------------------------------- /docs/pages/hooks/use-async-effect.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: useAsyncEffect 3 | package: '@dwarvesf/react-hooks' 4 | description: 'A React hook handles asynchronous side effects.' 5 | --- 6 | 7 | `useAsyncEffect` is an custom hook for handling asynchronous side effects. 8 | 9 | ## Import 10 | 11 | ```js 12 | import { useAsyncEffect } from '@dwarvesf/react-hooks' 13 | ``` 14 | 15 | ## Usage 16 | 17 | Basic mount/unmount 18 | 19 | ```js 20 | useAsyncEffect( 21 | async () => console.log('mount'), 22 | () => console.log('unmount'), 23 | [], 24 | ) 25 | ``` 26 | 27 | Omitting destroy 28 | 29 | ```js 30 | useAsyncEffect(async () => console.log('mount'), []) 31 | ``` 32 | 33 | Handle effect result in destroy 34 | 35 | ```js 36 | useAsyncEffect( 37 | () => fetch('url'), 38 | (result) => console.log(result), 39 | ) 40 | ``` 41 | 42 | Making sure it's still mounted before updating component state 43 | 44 | ```js 45 | useAsyncEffect( 46 | async (isMounted) => { 47 | const data = await fetch(`/users/${id}`).then((res) => res.json()) 48 | if (!isMounted()) return 49 | setUser(data) 50 | }, 51 | [id], 52 | ) 53 | ``` 54 | 55 | ## Parameters 56 | 57 | The API is the same as React's `useEffect`, except for some notable difference: 58 | 59 | ```js 60 | useAsyncEffect(callback, dependencies?) 61 | useAsyncEffect(callback, onDestroy, dependencies?) 62 | ``` 63 | 64 | | Name | Type | Default | Description | 65 | | -------------- | ----------------------------------------------------- | ------- | ---------------------------------------------------------------------------------------------------- | 66 | | `callback` | `(isMounted?:() => boolean) => Data \| Promise` | `_` | The async callback that receieves a single function to check whether the component is still mounted. | 67 | | `onDestroy` | `(result?: Data) => void` | `_` | The callback is called when the component is unmounted. | 68 | | `dependencies` | `any[]` | `_` | The dependencies array that will re-run the effect when the values within the array change. | 69 | -------------------------------------------------------------------------------- /docs/pages/hooks/use-clipboard.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: useClipboard 3 | package: '@dwarvesf/react-hooks' 4 | description: 'A React hooks hanldes copying content to clipboard.' 5 | --- 6 | 7 | `useClipboard` is a custom hook that handles copying content to clipboard. 8 | 9 | ## Import 10 | 11 | ```js 12 | import { useClipboard } from '@dwarvesf/react-hooks' 13 | ``` 14 | 15 | ## Return value 16 | 17 | The `useClipboard` hook returns an object with the following fields: 18 | 19 | | Name | Type | Default | Description | 20 | | ----------- | ---------- | ------- | ---------------------------------------- | 21 | | `value` | `string` | `_` | The copied value. | 22 | | `onCopy` | `function` | `_` | Callback function to copy content. | 23 | | `hasCopied` | `boolean` | `false` | If `true`, the content has been copied . | 24 | 25 | ## Usage 26 | 27 | ```jsx 28 | function Example() { 29 | const [value, setValue] = React.useState('Hello world') 30 | const { hasCopied, onCopy } = useClipboard(value) 31 | 32 | return ( 33 | <> 34 | 35 | 36 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | ) 46 | } 47 | ``` 48 | 49 | ## Parameters 50 | 51 | The `useClipboard` hook accepts a single value param: 52 | 53 | | Name | Type | Default | Description | 54 | | ------- | -------- | ------- | ----------------- | 55 | | `value` | `string` | `_` | The copied value. | 56 | -------------------------------------------------------------------------------- /docs/pages/hooks/use-constant.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: useConstant 3 | package: '@dwarvesf/react-hooks' 4 | description: 'A React hook that creates a constant value over the lifecycle 5 | of a component.' 6 | --- 7 | 8 | `useConstant` is a custom hook that creates a constant value over the lifecycle 9 | of a component. Even if `useMemo` is provided an empty array as its final 10 | argument, it doesn't offer a guarantee that it won't re-run for performance 11 | reasons later on. By using `useConstant` you can ensure that initialisers don't 12 | execute twice or more. 13 | 14 | ## Import 15 | 16 | ```js 17 | import { useConstant } from '@dwarvesf/react-hooks' 18 | ``` 19 | 20 | ## Return value 21 | 22 | The `useConstant` hook returns a function that receives the element and assign 23 | the value to the given React refs. 24 | 25 | ## Usage 26 | 27 | ```jsx 28 | function Example() { 29 | const value = useConstant(() => { 30 | // heavy computation 31 | return 0 32 | }) 33 | 34 | return

{value}

35 | } 36 | ``` 37 | 38 | ## Parameters 39 | 40 | The `useConstant` hook accepts a single value param: 41 | 42 | | Name | Type | Default | Description | 43 | | ------- | ------------------- | ------- | ------------------- | 44 | | `value` | `any` \| `Function` | `_` | The constant value. | 45 | -------------------------------------------------------------------------------- /docs/pages/hooks/use-debounce.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: useDebounce 3 | package: '@dwarvesf/react-hooks' 4 | description: 'A React hook that allows you to debounce any fast changing 5 | value.' 6 | --- 7 | 8 | `useDebounce` is a custom hook that allows you to debounce any fast changing 9 | value. 10 | 11 | ## Import 12 | 13 | ```js 14 | import { useDebounce } from '@dwarvesf/react-hooks' 15 | ``` 16 | 17 | ## Return value 18 | 19 | The `useDebounce` hook returns the result of the last func invocation. Note that 20 | it will return `undefined` if there are no previous invocations. 21 | 22 | ## Usage 23 | 24 | ```jsx 25 | function Example() { 26 | // Usage 27 | function App() { 28 | // State and setters for ... 29 | // Search term 30 | const [searchTerm, setSearchTerm] = useState('') 31 | // API search results 32 | const [results, setResults] = useState([]) 33 | // Searching status (whether there is pending API request) 34 | const [isSearching, setIsSearching] = useState(false) 35 | // Debounce search term so that it only gives us latest value ... 36 | // ... if searchTerm has not been updated within last 500ms. 37 | // The goal is to only have the API call fire when user stops typing ... 38 | // ... so that we aren't hitting our API rapidly. 39 | const debouncedSearchTerm = useDebounce(searchTerm, 500) 40 | // Effect for API call 41 | useEffect( 42 | () => { 43 | if (debouncedSearchTerm) { 44 | setIsSearching(true) 45 | searchCharacters(debouncedSearchTerm).then((results) => { 46 | setIsSearching(false) 47 | setResults(results) 48 | }) 49 | } else { 50 | setResults([]) 51 | setIsSearching(false) 52 | } 53 | }, 54 | [debouncedSearchTerm], // Only call effect if debounced search term changes 55 | ) 56 | return ( 57 | 58 | setSearchTerm(e.target.value)} 61 | /> 62 | {isSearching && } 63 | {results.length > 0 && ( 64 | 65 | 66 | {results.map((result) => ( 67 | {result.name} 68 | ))} 69 | 70 | 71 | )} 72 | 73 | ) 74 | } 75 | // API search function 76 | function searchCharacters(search) { 77 | const apiKey = 'f9dfb1e8d466d36c27850bedd2047687' 78 | return fetch( 79 | `https://api.github.com/search/repositories?per_page=5&q=${search}`, 80 | { 81 | method: 'GET', 82 | }, 83 | ) 84 | .then((r) => r.json()) 85 | .then((r) => r.items) 86 | .catch((error) => { 87 | console.error(error) 88 | return [] 89 | }) 90 | } 91 | 92 | return 93 | } 94 | ``` 95 | 96 | ## Parameters 97 | 98 | The `useDebounce` hook accepts following params: 99 | 100 | | Name | Type | Default | Description | 101 | | ------- | -------- | ------- | ------------------------------- | 102 | | `value` | `any` | `_` | | 103 | | `delay` | `number` | `_` | The delay time in milliseconds. | 104 | -------------------------------------------------------------------------------- /docs/pages/hooks/use-disclosure.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: useDisclosure 3 | package: '@dwarvesf/react-hooks' 4 | description: 5 | 'React hook to handle common open and close scenarios in UI components.' 6 | --- 7 | 8 | `useDisclosure` is a custom hook used to help handle common open, close, or 9 | toggle scenarios. It can be used to control feedback component such as Modal, 10 | AlertDialog, Drawer. 11 | 12 | ## Import 13 | 14 | ```js 15 | import { useDisclosure } from '@dwarvesf/react-hooks' 16 | ``` 17 | 18 | ## Return value 19 | 20 | The `useDisclosure` hook returns an object with the following fields: 21 | 22 | | Name | Type | Default | Description | 23 | | -------------- | --------------------------- | ------- | ------------------------------------------------------------------- | 24 | | `isOpen` | `boolean` | `false` | If `true`, it sets the controlled component to its visible state. | 25 | | `onClose` | `() => void` | `_` | Callback function to set a falsy value for the `isOpen` parameter. | 26 | | `onOpen` | `() => void` | `_` | Callback function to set a truthy value for the `isOpen` parameter. | 27 | | `onToggle` | `() => void` | `_` | Callback function to toggle the value of the `isOpen` parameter. | 28 | | `onOpenChange` | `(isOpen: boolean) => void` | `_` | Callback function to set the value for the `isOpen` parameter. | 29 | 30 | ## Usage 31 | 32 | ```jsx 33 | function Example() { 34 | const { isOpen, onOpen, onClose } = useDisclosure() 35 | 36 | return ( 37 | <> 38 | 39 | 40 | 41 | 42 | Basic Drawer 43 | 44 |

Some contents...

45 |

Some contents...

46 |

Some contents...

47 |
48 |
49 |
50 | 51 | ) 52 | } 53 | ``` 54 | 55 | ## Parameters 56 | 57 | The `useDisclosure` hook accepts an optional object with the following 58 | properties: 59 | 60 | | Name | Type | Default | Description | 61 | | --------------- | ------------ | ------- | ------------------------------------------------- | 62 | | `defaultIsOpen` | `boolean` | `false` | The default value. | 63 | | `onClose` | `() => void` | `_` | The callback when the `isOpen` is set to `false`. | 64 | | `onOpen` | `() => void` | `_` | The callback when the `isOpen` is set to `true`. | 65 | -------------------------------------------------------------------------------- /docs/pages/hooks/use-has-mounted.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: useHasMounted 3 | package: '@dwarvesf/react-hooks' 4 | description: 'Lifecycle hook checking if a component has mounted yet' 5 | --- 6 | 7 | Lifecycle hook providing ability to check component's mount state. Returns 8 | `true` if component mounted and `false` otherwise. 9 | 10 | ## Import 11 | 12 | ```js 13 | import { useHasMounted } from '@dwarvesf/react-hooks' 14 | ``` 15 | 16 | ## Return value 17 | 18 | Returns `true` if component mounted and `false` otherwise. 19 | 20 | ## Usage 21 | 22 | ```jsx 23 | function Example() { 24 | const hasMounted = useHasMounted() 25 | 26 | if (!hasMounted) { 27 | return null 28 | } 29 | return

My component

30 | } 31 | ``` 32 | -------------------------------------------------------------------------------- /docs/pages/hooks/use-lock-body-scroll.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: useLockBodyScroll 3 | package: '@dwarvesf/react-hooks' 4 | description: 'A React hooks prevents your users from being able to 5 | scroll the body of your page.' 6 | --- 7 | 8 | `useLockBodyScroll` is a custom hook that prevents your users from being able to 9 | scroll the body of your page while a particular component is absolutely 10 | positioned over your page (think modal or full-screen mobile menu). 11 | 12 | ## Import 13 | 14 | ```js 15 | import { useLockBodyScroll } from '@dwarvesf/react-hooks' 16 | ``` 17 | 18 | ## Usage 19 | 20 | ```jsx 21 | function Example() { 22 | function Modal({ title, content, onClose }) { 23 | // Call hook to lock body scroll 24 | useLockBodyScroll() 25 | return ( 26 |
41 |
42 |

{title}

43 |

{content}

44 |
45 |
46 | ) 47 | } 48 | 49 | function App() { 50 | // State for our modal 51 | const [modalOpen, setModalOpen] = useState(false) 52 | return ( 53 |
54 | 55 | {modalOpen && ( 56 | setModalOpen(false)} 60 | /> 61 | )} 62 |
63 | ) 64 | } 65 | 66 | return 67 | } 68 | ``` 69 | -------------------------------------------------------------------------------- /docs/pages/hooks/use-media.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: useMedia 3 | package: '@dwarvesf/react-hooks' 4 | description: 5 | 'A React hook makes it easy to utilize media queries in your component logic.' 6 | --- 7 | 8 | `useMedia` is an custom hook for utilizing media queries in your component 9 | logic. 10 | 11 | ## Import 12 | 13 | ```js 14 | import { useMedia } from '@dwarvesf/react-hooks' 15 | ``` 16 | 17 | ## Usage 18 | 19 | ```jsx 20 | function Example() { 21 | const columnCount = useMedia( 22 | ['(min-width: 1500px)', '(min-width: 1000px)', '(min-width: 600px)'], 23 | [5, 4, 3], 24 | 2, 25 | ) 26 | return
Resize your browser window: {columnCount}
27 | } 28 | ``` 29 | 30 | ```jsx 31 | function Example() { 32 | const isMobile = useMedia(['(max-width: 512px)'], [true], false) 33 | return
{isMobile ? 'Mobile view' : 'Desktop view'}
34 | } 35 | ``` 36 | 37 | ## Parameters 38 | 39 | The `useDisclosure` hook accepts an list of following paramesters: 40 | 41 | ```js 42 | useMedia(queries, values, defaultValue) 43 | ``` 44 | 45 | | Name | Type | Default | Description | 46 | | -------------- | ---------- | ------- | ------------------------------------------------------------------ | 47 | | `queries` | `string[]` | `_` | The list of media queries. | 48 | | `values` | `any[]` | `_` | The values relate to the list of media queries by array index. | 49 | | `defaultValue` | `any` | `_` | The default value if none of the values matches the media queries. | 50 | -------------------------------------------------------------------------------- /docs/pages/hooks/use-merge-refs.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: useMergeRefs 3 | package: '@dwarvesf/react-hooks' 4 | description: 5 | 'React hook that merges react refs into a single memoized function.' 6 | --- 7 | 8 | `useMergeRefs` is a custom hook used to merge several react refs into a single 9 | one. 10 | 11 | ## Import 12 | 13 | ```js 14 | import { useMergeRefs } from '@dwarvesf/react-hooks' 15 | ``` 16 | 17 | ## Return value 18 | 19 | The `useMergeRefs` hook returns a function that receives the element and assign 20 | the value to the given React refs. 21 | 22 | ## Usage 23 | 24 | ```jsx 25 | function Example({ ref, ...props }) { 26 | const internalRef = React.useRef() 27 | const refs = useMergeRefs(internalRef, ref) 28 | 29 | return ( 30 |
31 | A div with multiple refs. 32 |
33 | ) 34 | } 35 | ``` 36 | -------------------------------------------------------------------------------- /docs/pages/hooks/use-onclick-outside.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: useOnClickOutside 3 | package: '@dwarvesf/react-hooks' 4 | description: 5 | 'A React hook allows you to detect clicks outside of a specified element.' 6 | --- 7 | 8 | `useOnClickOutside` is a custom hook that allows you to detect clicks outside of 9 | a specified element. In the example below we use it to close a modal when any 10 | element outside of the modal is clicked. By abstracting this logic out into a 11 | hook we can easily use it across all of our components that need this kind of 12 | functionality (dropdown menus, tooltips, etc) 13 | 14 | ## Import 15 | 16 | ```js 17 | import { useOnClickOutside } from '@dwarvesf/react-hooks' 18 | ``` 19 | 20 | ## Usage 21 | 22 | ```jsx 23 | function Example() { 24 | function Modal({ title, content, onClose }) { 25 | const ref = React.useRef(null) 26 | // Call hook to lock body scroll 27 | useOnClickOutside(ref, onClose) 28 | return ( 29 |
43 |
44 |

{title}

45 |

{content}

46 |
47 |
48 | ) 49 | } 50 | 51 | function App() { 52 | // State for our modal 53 | const [modalOpen, setModalOpen] = useState(false) 54 | return ( 55 |
56 | 57 | {modalOpen && ( 58 | setModalOpen(false)} 62 | /> 63 | )} 64 |
65 | ) 66 | } 67 | 68 | return 69 | } 70 | ``` 71 | 72 | ## Parameters 73 | 74 | The `useOnClickOutside` hook accepts an optional object with the following 75 | properties: 76 | 77 | | Name | Type | Default | Description | 78 | | ---------- | ----------------- | ------- | ----------------------------------------------- | 79 | | `ref` | `React.RefObject` | `_` | The ref of the element. | 80 | | `callback` | `(event) => void` | `_` | The callback when clicking outside the element. | 81 | -------------------------------------------------------------------------------- /docs/pages/hooks/use-pagination.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: usePagination 3 | package: '@dwarvesf/react-hooks' 4 | description: 'A React hook that manages pagination state' 5 | --- 6 | 7 | `usePagination` is a custom hook that provides methods for managing pagination state. 8 | 9 | ## Import 10 | 11 | ```js 12 | import { usePagination } from "@dwarvesf/react-hooks"; 13 | ``` 14 | 15 | ## Return value 16 | 17 | The `usePagination` hook returns the result in the shape of below: 18 | 19 | ```ts 20 | { 21 | totalPage: number 22 | pageSize: number 23 | // initial value, but changing it will update the hook's `currentPage` return value 24 | page: number 25 | // go to next page, optionally taking a boolean param to decide whether to go the last page immediately 26 | next: (toLast?: boolean) => void 27 | // back to previous page, optionally taking a boolean param to decide whether to go back to page 1 immediately 28 | back: (toFirst?: boolean) => void 29 | // jump to a specific page 30 | go: (page: number) => void 31 | hasNextPage: boolean 32 | hasPreviousPage: boolean 33 | } 34 | ``` 35 | 36 | ## Usage 37 | 38 | ```jsx 39 | function Example() { 40 | const { currentPage, totalPage, next, back, go, hasNextPage, hasPreviousPage } = usePagination({ 41 | pageSize: 2, 42 | page: 1, 43 | totalPage: 6 44 | }); 45 | 46 | return ( 47 |
    48 |
  • Page: {currentPage}
  • 49 |
  • Total page: {totalPage}
  • 50 |
  • Has next page: {String(hasNextPage)}
  • 51 |
  • Has previous page: {String(hasPreviousPage)}
  • 52 |
  • 53 | 54 |
  • 55 |
  • 56 | 57 |
  • 58 |
  • 59 | 60 |
  • 61 |
  • 62 | 63 |
  • 64 |
65 | ); 66 | } 67 | ``` 68 | 69 | ## Parameters 70 | 71 | The `usePagination` hook accepts a param of below shape: 72 | 73 | | Name | Type | Default | Description | 74 | | ----------- | ------------ | ------- | ------------------------------ | 75 | | `pageSize` | `number` | `_` | | 76 | | `totalPage` | `number` | | | 77 | | `currentPage` | `number` | | | 78 | -------------------------------------------------------------------------------- /docs/pages/hooks/use-previous.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: usePrevious 3 | package: '@dwarvesf/react-hooks' 4 | description: 'A React hook stores and return the previous value from a state.' 5 | --- 6 | 7 | `usePrevious` stores and return the previous value from a state. 8 | 9 | ## Import 10 | 11 | ```js 12 | import { usePrevious } from '@dwarvesf/react-hooks' 13 | ``` 14 | 15 | ## Return value 16 | 17 | The `usePrevious` hook returns the previous value. 18 | 19 | ## Usage 20 | 21 | ```jsx 22 | function Exapmle() { 23 | // State value and setter for our example 24 | const [count, setCount] = useState(0) 25 | // Get the previous value (was passed into hook on last render) 26 | const prevCount = usePrevious(count) 27 | // Display both current and previous count value 28 | return ( 29 |
30 |

31 | Now: {count}, before: {prevCount} 32 |

33 | 34 |
35 | ) 36 | } 37 | ``` 38 | 39 | ## Parameters 40 | 41 | The `usePrevious` hook accepts a single value param: 42 | 43 | | Name | Type | Default | Description | 44 | | ------- | ----- | ------- | ---------------------------------------------------- | 45 | | `value` | `any` | `_` | The state that you want to store its previous value. | 46 | -------------------------------------------------------------------------------- /docs/pages/hooks/use-why-did-you-update.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: useWhyDidYouUpdate 3 | package: '@dwarvesf/react-hooks' 4 | description: 'A React hooks that makes it easy to see which prop changes are causing a 5 | component to re-render.' 6 | --- 7 | 8 | `useWhyDidYouUpdate` makes it easy to see which prop changes are causing a 9 | component to re-render. If a function is particularly expensive to run and you 10 | know it renders the same results given the same props you can use the 11 | `React.memo` higher order component. 12 | 13 | ## Import 14 | 15 | ```js 16 | import { useWhyDidYouUpdate } from '@dwarvesf/react-hooks' 17 | ``` 18 | 19 | ## Return value 20 | 21 | The `useWhyDidYouUpdate` hook returns a function that receives the element and 22 | assign the value to the given React refs. 23 | 24 | ## Usage 25 | 26 | ```jsx 27 | function Example() { 28 | const Counter = React.memo((props) => { 29 | useWhyDidYouUpdate('Counter', props) 30 | return
{props.count}
31 | }) 32 | 33 | function App() { 34 | const [count, setCount] = useState(0) 35 | const [userId, setUserId] = useState(0) 36 | // Our console output tells use that the style prop for ... 37 | // ... changes on every render, even when we only change userId state by ... 38 | // ... clicking the "switch user" button. Oh of course! That's because the 39 | // ... counterStyle object is being re-created on every render. 40 | // Thanks to our hook we figured this out and realized we should probably ... 41 | // ... move this object outside of the component body. 42 | const counterStyle = { 43 | fontSize: '3rem', 44 | color: 'red', 45 | } 46 | return ( 47 |
48 |
49 | 50 | 51 |
52 |
53 | 54 | 55 |
56 |
57 | ) 58 | } 59 | 60 | return 61 | } 62 | ``` 63 | 64 | ## Parameters 65 | 66 | The `useWhyDidYouUpdate` hook accepts following params: 67 | 68 | | Name | Type | Default | Description | 69 | | ------- | --------------------- | ------- | ----------- | 70 | | `name` | `string` | `_` | | 71 | | `props` | `Record` | `_` | | 72 | -------------------------------------------------------------------------------- /docs/pages/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: React Toolkit 3 | --- 4 | 5 | The Dwarvesf React Toolkit is a library of React Hooks and Utilities to help 6 | build robust React applications. 7 | 8 | - `@dwarvesf/react-hooks`: React hooks that allow you to build an interface 9 | using any component library. 10 | 11 | - `@dwarvesf/react-utils`: a set of utility functions for common tasks to build 12 | React applications at ease. 13 | -------------------------------------------------------------------------------- /docs/pages/utils/call-all-handlers.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: callAllHandlers 3 | package: '@dwarvesf/react-utils' 4 | description: 'A helper combines multiple functions and call them at once.' 5 | --- 6 | 7 | `callAllHandlers` is a utility that combines multiple functions and call them at 8 | once. 9 | 10 | ## Import 11 | 12 | ```js 13 | import { callAllHandlers } from '@dwarvesf/react-utils' 14 | ``` 15 | 16 | ## Return value 17 | 18 | The `callAllHandlers` triggers all functions passed in and return `undefined`: 19 | 20 | ## Usage 21 | 22 | ```jsx 23 | function Example() { 24 | function CustomButton(props) { 25 | return ( 26 | 29 | ) 30 | } 31 | 32 | return alert(1)} /> 33 | } 34 | ``` 35 | -------------------------------------------------------------------------------- /docs/pages/utils/clean-children.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: cleanChildren 3 | package: '@dwarvesf/react-utils' 4 | description: 5 | 'A React utility to return a list of all valid React child elements.' 6 | --- 7 | 8 | `cleanChildren` is a utility to get a list of all valid React child elements. 9 | 10 | ## Import 11 | 12 | ```js 13 | import { cleanChildren } from '@dwarvesf/react-utils' 14 | ``` 15 | 16 | ## Return value 17 | 18 | The `cleanChildren` hook returns a list of all valid React child elements. 19 | 20 | ## Usage 21 | 22 | ```jsx 23 | function Example() { 24 | function App({ children }) { 25 | return
{cleanChildren(children)}
26 | } 27 | 28 | return ( 29 | 30 | abc 31 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

32 | 9999 33 |
34 | ) 35 | } 36 | ``` 37 | -------------------------------------------------------------------------------- /docs/pages/utils/create-context.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: createContext 3 | package: '@dwarvesf/react-utils' 4 | description: 5 | 'A React utility helps to create React context without providing a default 6 | value.' 7 | --- 8 | 9 | `createContext` is a helper function that guards against accessing a `Context` 10 | whose value wasn't provided. By doing this, API instead, we never have to 11 | provide a default and nver have to check for `undefined`. 12 | 13 | ## Import 14 | 15 | ```js 16 | import { createContext } from '@dwarvesf/react-utils' 17 | ``` 18 | 19 | ## Usage 20 | 21 | ```js 22 | const [CurrentUserProvider, useCurrentUserName] = createContext({}); 23 | 24 | function EnthusasticGreeting() { 25 | const currentUser = useCurrentUserName(); 26 | return
HELLO {currentUser.toUpperCase()}!
; 27 | } 28 | 29 | function App() { 30 | return ( 31 | 32 | 33 | 34 | ); 35 | } 36 | ``` 37 | -------------------------------------------------------------------------------- /docs/pages/utils/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: React Utils 3 | description: 4 | --- 5 | 6 | A library provides utility functions for common tasks to build React 7 | applications at ease. 8 | 9 | ## Installation 10 | 11 | ```bash 12 | npm i @dwarvesf/react-utils 13 | ``` 14 | 15 | ```bash 16 | yarn add @dwarvesf/react-utils 17 | ``` 18 | 19 | ## Import 20 | 21 | Pick and choose a utility you'd like to use and import them. 22 | 23 | ```js 24 | import { createContext } from '@dwarvesf/react-utils' 25 | ``` 26 | -------------------------------------------------------------------------------- /docs/pages/utils/is-ssr.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: isSSR 3 | package: '@dwarvesf/react-utils' 4 | description: 'A helper is used to distingush between server environment and client 5 | environment.' 6 | --- 7 | 8 | `isSSR` is a utility to distingush between server environment and client 9 | environment. 10 | 11 | ## Import 12 | 13 | ```js 14 | import { isSSR } from '@dwarvesf/react-utils' 15 | ``` 16 | 17 | ## Return value 18 | 19 | This method returns `true` if the function is invoked in a browser and returns 20 | `false` if it is invoked in a server. 21 | 22 | ## Usage 23 | 24 | ```js 25 | if (isSSR()) { 26 | /// do something 27 | } 28 | ``` 29 | -------------------------------------------------------------------------------- /docs/pages/utils/noop.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: noop 3 | package: '@dwarvesf/react-utils' 4 | description: 'An empty function when you wish to pass around a function that will do 5 | nothing.' 6 | --- 7 | 8 | `noop` is an empty function when you wish to pass around a function that will do 9 | nothing. 10 | 11 | ## Import 12 | 13 | ```js 14 | import { noop } from '@dwarvesf/react-utils' 15 | ``` 16 | 17 | ## Return value 18 | 19 | This method returns `undefined`. 20 | 21 | ## Usage 22 | 23 | ```js 24 | noop() 25 | ``` 26 | -------------------------------------------------------------------------------- /docs/pages/utils/sleep.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: sleep 3 | package: '@dwarvesf/react-utils' 4 | description: 'A helper to delay execution for a specific duration.' 5 | --- 6 | 7 | `sleep` is a utility to delay execution for a specific duration. 8 | 9 | ## Import 10 | 11 | ```js 12 | import { sleep } from '@dwarvesf/react-utils' 13 | ``` 14 | 15 | ## Return value 16 | 17 | This method returns a promise that will resolve after X ms has passed. 18 | 19 | ## Usage 20 | 21 | ```js 22 | function Example() { 23 | const [state, setState] = useState( 24 | "Click me and I'll change after 5 seconds!!", 25 | ) 26 | 27 | const onClick = async () => { 28 | await sleep(5000) 29 | setState('Tada!') 30 | } 31 | 32 | return ( 33 | 34 | 35 | 36 | ) 37 | } 38 | ``` 39 | 40 | ## Parameters 41 | 42 | The `sleep` method accepts a time parameter in miliseconds: 43 | 44 | ```js 45 | sleep(time) 46 | ``` 47 | 48 | | Name | Type | Default | Description | 49 | | ------ | -------- | ------- | ----------------------------------------------- | 50 | | `time` | `number` | `_` | The time to wait for before continue execution. | 51 | -------------------------------------------------------------------------------- /docs/pages/utils/truncate.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: truncate 3 | package: '@dwarvesf/react-utils' 4 | description: 'A helper is used to truncate a long string.' 5 | --- 6 | 7 | `truncate` is a utility to truncate a long string. 8 | 9 | ## Import 10 | 11 | ```js 12 | import { truncate } from '@dwarvesf/react-utils' 13 | ``` 14 | 15 | ## Return value 16 | 17 | This method returns a truncated string with a `...` middle or ending. 18 | 19 | ## Usage 20 | 21 | ```jsx 22 | function Example() { 23 | return ( 24 | 25 | {truncate('Lorem ipsum dolor sit amet', 20)} 26 | {truncate('Lorem ipsum dolor sit amet', 10, true)} 27 | 28 | ) 29 | } 30 | ``` 31 | 32 | ## Masking character 33 | 34 | Alternatively, you can use other character instead of `"."`, for example `"*"`: 35 | 36 | ```jsx 37 | function Example() { 38 | return ( 39 | 40 | {truncate('Lorem ipsum dolor sit amet', 10, true, "*")} 41 | 42 | ) 43 | } 44 | ``` 45 | 46 | ## Parameters 47 | 48 | The `truncate` method accepts an list of following paramesters: 49 | 50 | ```js 51 | truncate(str, max, middle) 52 | ``` 53 | 54 | | Name | Type | Default | Description | 55 | | ---------- | --------- | ------- | ------------------------------------------------------------------------- | 56 | | `str` | `string` | `_` | The string to truncate. | 57 | | `max` | `number` | `_` | The maximum string length. | 58 | | `middle` | `boolean` | `false` | Whether to put `...` in the middle or at the end of the truncated string. | 59 | | `maskChar` | `string` | `"."` | What to use as the masking character when truncating. | 60 | -------------------------------------------------------------------------------- /docs/public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwarvesf/react-toolkit/94ac657db6d61ae8e9b178907a7423fdd0760d75/docs/public/favicon-16x16.png -------------------------------------------------------------------------------- /docs/public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwarvesf/react-toolkit/94ac657db6d61ae8e9b178907a7423fdd0760d75/docs/public/favicon-32x32.png -------------------------------------------------------------------------------- /docs/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwarvesf/react-toolkit/94ac657db6d61ae8e9b178907a7423fdd0760d75/docs/public/favicon.ico -------------------------------------------------------------------------------- /docs/public/fonts/Inter.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwarvesf/react-toolkit/94ac657db6d61ae8e9b178907a7423fdd0760d75/docs/public/fonts/Inter.woff2 -------------------------------------------------------------------------------- /docs/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /docs/public/thumbnail.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwarvesf/react-toolkit/94ac657db6d61ae8e9b178907a7423fdd0760d75/docs/public/thumbnail.jpeg -------------------------------------------------------------------------------- /docs/src/components/algolia-search.tsx: -------------------------------------------------------------------------------- 1 | import { SearchIcon } from '@chakra-ui/icons' 2 | import { 3 | chakra, 4 | HStack, 5 | HTMLChakraProps, 6 | Kbd, 7 | Text, 8 | useColorModeValue, 9 | VisuallyHidden, 10 | } from '@chakra-ui/react' 11 | import * as React from 'react' 12 | 13 | const ACTION_KEY_DEFAULT = ['Ctrl', 'Control'] 14 | const ACTION_KEY_APPLE = ['⌘', 'Command'] 15 | 16 | export const SearchButton = React.forwardRef(function SearchButton( 17 | props: HTMLChakraProps<'button'>, 18 | ref: React.Ref, 19 | ) { 20 | const [actionKey, setActionKey] = React.useState(ACTION_KEY_APPLE) 21 | React.useEffect(() => { 22 | if (typeof navigator === 'undefined') return 23 | const isMac = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform) 24 | if (!isMac) { 25 | setActionKey(ACTION_KEY_DEFAULT) 26 | } 27 | }, []) 28 | 29 | return ( 30 | 53 | 54 | 55 | 56 | Search the docs 57 | 58 | 59 | Press 60 | 61 | 66 | {actionKey[0]} 67 | 68 | 69 | and 70 | 71 | K 72 | 73 | to search 74 | 75 | 76 | 77 | ) 78 | }) 79 | -------------------------------------------------------------------------------- /docs/src/components/color-palette.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Box, 3 | Flex, 4 | FlexProps, 5 | Grid, 6 | GridProps, 7 | useTheme, 8 | } from '@chakra-ui/react' 9 | import React from 'react' 10 | 11 | type ColorPaletteProps = FlexProps & { color?: string; name?: string } 12 | 13 | export const ColorPalette = (props: ColorPaletteProps) => { 14 | const { color, name, ...rest } = props 15 | 16 | const theme = useTheme() 17 | let colorCode = color 18 | const [shade, hue] = color.split('.') 19 | 20 | if (shade && hue) { 21 | colorCode = theme.colors[shade][hue] 22 | } 23 | 24 | if (color in theme.colors && typeof theme.colors[color] === 'string') { 25 | colorCode = theme.colors[color] 26 | } 27 | 28 | return ( 29 | 30 | 37 | 38 | 39 | {name} 40 | 41 | {colorCode} 42 | 43 | 44 | ) 45 | } 46 | 47 | export const ColorPalettes = (props: { color: string }) => { 48 | const { color } = props 49 | const theme = useTheme() 50 | const keys = Object.keys(theme.colors[color]) 51 | 52 | return keys.map((item) => ( 53 | 58 | )) 59 | } 60 | 61 | export const ColorWrapper: React.FC = (props) => ( 62 | 68 | ) 69 | -------------------------------------------------------------------------------- /docs/src/components/container.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Box, BoxProps } from '@chakra-ui/react' 3 | 4 | export const Container = (props: BoxProps) => ( 5 | 14 | ) 15 | 16 | export default Container 17 | -------------------------------------------------------------------------------- /docs/src/components/discord-strip.tsx: -------------------------------------------------------------------------------- 1 | import { Box, BoxProps, chakra, Flex, Heading, Text } from '@chakra-ui/react' 2 | import * as React from 'react' 3 | import DiscordIcon from './docs/discord-logo' 4 | import Container from './container' 5 | 6 | function DiscordStrip(props: BoxProps) { 7 | return ( 8 | 9 | 10 | 15 | 16 | 22 | 23 | 24 | 25 | 26 | Connect with the community 27 | 28 | 29 | Feel free to ask questions, report issues, and meet new people. 30 | 31 | 32 | 33 | 52 | Join the #Chakra Discord! 53 | 54 | 55 | 56 | 57 | ) 58 | } 59 | 60 | export default DiscordStrip 61 | -------------------------------------------------------------------------------- /docs/src/components/docs/discord-logo.tsx: -------------------------------------------------------------------------------- 1 | import { Icon, IconProps } from '@chakra-ui/react' 2 | 3 | const DiscordIcon = (props: IconProps) => ( 4 | 5 | 6 | 15 | 16 | 17 | ) 18 | 19 | export default DiscordIcon 20 | -------------------------------------------------------------------------------- /docs/src/components/docs/icon.tsx: -------------------------------------------------------------------------------- 1 | import { Icon, IconProps } from '@chakra-ui/react' 2 | 3 | /** This component is used in the `icon.mdx` page. */ 4 | const CircleIcon = (props: IconProps) => ( 5 | 6 | 10 | 11 | ) 12 | 13 | export default CircleIcon 14 | -------------------------------------------------------------------------------- /docs/src/components/edit-page-button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { chakra, Icon, Stack, Link } from '@chakra-ui/react' 3 | import { MdEdit } from 'react-icons/md' 4 | 5 | const EditPageLink: React.FC<{ href?: string }> = ({ href }) => { 6 | return ( 7 | 8 | 15 | 16 | Edit this page 17 | 18 | 19 | ) 20 | } 21 | 22 | export default EditPageLink 23 | -------------------------------------------------------------------------------- /docs/src/components/font-face.tsx: -------------------------------------------------------------------------------- 1 | const FontFace = () => ( 2 | 77 | ) 78 | 79 | export default FontFace 80 | -------------------------------------------------------------------------------- /docs/src/components/footer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Box, Icon, Text, Stack, Link } from '@chakra-ui/react' 3 | import { IoLogoTwitter, IoLogoLinkedin } from 'react-icons/io' 4 | import { FaDiscord } from 'react-icons/fa' 5 | import { DiGithubBadge } from 'react-icons/di' 6 | import siteConfig from 'configs/site-config' 7 | 8 | type FooterLinkProps = { 9 | icon?: React.ElementType 10 | href?: string 11 | label?: string 12 | } 13 | 14 | const FooterLink: React.FC = ({ icon, href, label }) => ( 15 | 16 | 17 | 18 | ) 19 | 20 | const links = [ 21 | { 22 | icon: DiGithubBadge, 23 | label: 'GitHub', 24 | href: siteConfig.author.github, 25 | }, 26 | { 27 | icon: IoLogoTwitter, 28 | label: 'Twitter', 29 | href: siteConfig.author.twitter, 30 | }, 31 | { 32 | icon: IoLogoLinkedin, 33 | label: 'LinkedIn', 34 | href: siteConfig.author.linkedin, 35 | }, 36 | { 37 | icon: FaDiscord, 38 | label: 'Discord', 39 | href: siteConfig.author.discord, 40 | }, 41 | ] 42 | 43 | export const Footer = () => ( 44 | 45 | 46 | Made with ❤️ by{' '} 47 | 48 | Dwarves Foundation 49 | 50 | 51 | 52 | {links.map((link) => ( 53 | 54 | ))} 55 | 56 | 57 | ) 58 | 59 | export default Footer 60 | -------------------------------------------------------------------------------- /docs/src/components/header-nav-link.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | chakra, 3 | HTMLChakraProps, 4 | useColorModeValue as getColorModeValue, 5 | } from '@chakra-ui/react' 6 | import NextLink from 'next/link' 7 | import { useRouter } from 'next/router' 8 | import React from 'react' 9 | 10 | function NavLink(props: HTMLChakraProps<'a'>) { 11 | const { href, ...rest } = props 12 | const { pathname } = useRouter() 13 | 14 | const [, group] = href.split('/') 15 | const isActive = pathname.includes(group) 16 | 17 | return ( 18 | 19 | 35 | 36 | ) 37 | } 38 | 39 | export default NavLink 40 | -------------------------------------------------------------------------------- /docs/src/components/logo-mark.tsx: -------------------------------------------------------------------------------- 1 | import { chakra } from '@chakra-ui/react' 2 | import * as React from 'react' 3 | 4 | function LogoMark(props) { 5 | return ( 6 | 7 | 11 | 12 | ) 13 | } 14 | 15 | export default LogoMark 16 | -------------------------------------------------------------------------------- /docs/src/components/logo.tsx: -------------------------------------------------------------------------------- 1 | import { chakra, HTMLChakraProps } from '@chakra-ui/react' 2 | import React from 'react' 3 | 4 | export const Logo = (props: HTMLChakraProps<'svg'>) => ( 5 | 13 | 14 | 18 | 22 | 23 | 24 | ) 25 | 26 | export default Logo 27 | -------------------------------------------------------------------------------- /docs/src/components/mdx-components/anchor.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { chakra } from '@chakra-ui/react' 3 | 4 | export const Anchor = React.forwardRef((props: any, ref: any) => ( 5 | 6 | )) 7 | -------------------------------------------------------------------------------- /docs/src/components/mdx-components/codeblock/code-container.tsx: -------------------------------------------------------------------------------- 1 | import { Box, BoxProps } from '@chakra-ui/react' 2 | import React from 'react' 3 | 4 | function CodeContainer(props: BoxProps) { 5 | return 6 | } 7 | 8 | export default CodeContainer 9 | -------------------------------------------------------------------------------- /docs/src/components/mdx-components/codeblock/codeblock.tsx: -------------------------------------------------------------------------------- 1 | import { Box, useBoolean } from '@chakra-ui/react' 2 | import dynamic from 'next/dynamic' 3 | import theme from 'prism-react-renderer/themes/oceanicNext' 4 | import React, { useEffect } from 'react' 5 | import CodeContainer from './code-container' 6 | import CopyButton from './copy-button' 7 | import Highlight from './highlight' 8 | 9 | const ReactLiveBlock = dynamic(() => import('./react-live-block')) 10 | 11 | function CodeBlock(props) { 12 | const [isMounted, { on }] = useBoolean() 13 | useEffect( 14 | /** 15 | * Lazily-load to save bundle size. 16 | */ 17 | on, 18 | [], 19 | ) 20 | const { 21 | className, 22 | live = true, 23 | manual, 24 | render, 25 | children, 26 | viewlines, 27 | ln, 28 | mountStylesheet = false, 29 | ...rest 30 | } = props 31 | 32 | const language = className?.replace(/language-/, '') 33 | const rawCode = children.trim() 34 | const reactLiveBlockProps = { 35 | rawCode, 36 | language, 37 | theme, 38 | noInline: manual, 39 | mountStylesheet, 40 | ...rest, 41 | } 42 | 43 | if (isMounted && language === 'jsx' && live === true) { 44 | return 45 | } 46 | 47 | if (isMounted && render) { 48 | /** 49 | * @TODO Not sure if this is even used? 50 | */ 51 | return ( 52 |
53 | 54 |
55 | ) 56 | } 57 | 58 | return ( 59 | 60 | 61 | 68 | 69 | 70 | 71 | ) 72 | } 73 | 74 | export default CodeBlock 75 | -------------------------------------------------------------------------------- /docs/src/components/mdx-components/codeblock/copy-button.tsx: -------------------------------------------------------------------------------- 1 | import { Button, ButtonProps, useClipboard } from '@chakra-ui/react' 2 | import React from 'react' 3 | 4 | interface CopyButtonProps extends ButtonProps { 5 | code: string 6 | } 7 | 8 | function CopyButton({ code, ...props }: CopyButtonProps) { 9 | const { hasCopied, onCopy } = useClipboard(code) 10 | 11 | return ( 12 | 33 | ) 34 | } 35 | 36 | export default CopyButton 37 | -------------------------------------------------------------------------------- /docs/src/components/mdx-components/codeblock/highlight.tsx: -------------------------------------------------------------------------------- 1 | import { chakra } from '@chakra-ui/react' 2 | import BaseHighlight, { 3 | defaultProps, 4 | Language, 5 | PrismTheme, 6 | } from 'prism-react-renderer' 7 | import React from 'react' 8 | import { liveEditorStyle } from './styles' 9 | 10 | const RE = /{([\d,-]+)}/ 11 | 12 | const calculateLinesToHighlight = (meta: string) => { 13 | if (!RE.test(meta)) { 14 | return () => false 15 | } 16 | const lineNumbers = RE.exec(meta)[1] 17 | .split(`,`) 18 | .map((v) => v.split(`-`).map((x) => parseInt(x, 10))) 19 | 20 | return (index: number) => { 21 | const lineNumber = index + 1 22 | const inRange = lineNumbers.some(([start, end]) => 23 | end ? lineNumber >= start && lineNumber <= end : lineNumber === start, 24 | ) 25 | return inRange 26 | } 27 | } 28 | 29 | interface HighlightProps { 30 | codeString: string 31 | language: Language 32 | theme: PrismTheme 33 | metastring?: string 34 | showLines?: boolean 35 | } 36 | 37 | function Highlight({ 38 | codeString, 39 | language, 40 | metastring, 41 | showLines, 42 | ...props 43 | }: HighlightProps) { 44 | const shouldHighlightLine = calculateLinesToHighlight(metastring) 45 | 46 | return ( 47 | 53 | {({ className, style, tokens, getLineProps, getTokenProps }) => ( 54 |
55 |
56 |             {tokens.map((line, i) => {
57 |               const lineProps = getLineProps({ line, key: i })
58 |               return (
59 |                 
64 |                   {showLines && (
65 |                     
66 |                       {i + 1}
67 |                     
68 |                   )}
69 |                   {line.map((token, key) => (
70 |                     
71 |                   ))}
72 |                 
73 |               )
74 |             })}
75 |           
76 |
77 | )} 78 |
79 | ) 80 | } 81 | 82 | export default Highlight 83 | -------------------------------------------------------------------------------- /docs/src/components/mdx-components/codeblock/react-live-block.tsx: -------------------------------------------------------------------------------- 1 | import { Box, BoxProps, chakra } from '@chakra-ui/react' 2 | import React, { useState } from 'react' 3 | import { LiveEditor, LiveError, LivePreview, LiveProvider } from 'react-live' 4 | import CodeContainer from './code-container' 5 | import CopyButton from './copy-button' 6 | import scope from './react-live-scope' 7 | import { liveEditorStyle, liveErrorStyle } from './styles' 8 | 9 | const LiveCodePreview = chakra(LivePreview, { 10 | baseStyle: { 11 | fontFamily: 'body', 12 | mt: 5, 13 | p: 3, 14 | borderWidth: 1, 15 | borderRadius: '12px', 16 | }, 17 | }) 18 | 19 | const EditableNotice = (props: BoxProps) => { 20 | return ( 21 | 38 | Editable Example 39 | 40 | ) 41 | } 42 | 43 | function ReactLiveBlock({ editable, rawCode, ...rest }) { 44 | const [editorCode, setEditorCode] = useState(rawCode.trim()) 45 | const onChange = (newCode) => setEditorCode(newCode.trim()) 46 | const liveProviderProps = { 47 | code: editorCode, 48 | scope, 49 | ...rest, 50 | } 51 | return ( 52 | 53 | 54 | 55 | {editable && ( 56 | 57 | 58 | 59 | )} 60 | 61 | {editable && } 62 | 63 | {editable && } 64 | 65 | ) 66 | } 67 | 68 | export default ReactLiveBlock 69 | -------------------------------------------------------------------------------- /docs/src/components/mdx-components/codeblock/react-live-scope.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import * as Chakra from '@chakra-ui/react' 3 | import * as reactHooks from '@dwarvesf/react-hooks' 4 | import * as reactUtils from '@dwarvesf/react-utils' 5 | import { chakra } from '@chakra-ui/react' 6 | import * as Icons from '@chakra-ui/icons' 7 | import * as Formik from 'formik' 8 | import * as ReactTable from 'react-table' 9 | import FocusLock from 'react-focus-lock' 10 | import { 11 | MdSettings, 12 | MdReceipt, 13 | MdGroupWork, 14 | MdCheckCircle, 15 | MdGraphicEq, 16 | MdBuild, 17 | MdCall, 18 | MdPhone, 19 | MdArrowDropDown, 20 | } from 'react-icons/md' 21 | import { AiOutlineUser } from 'react-icons/ai' 22 | import { FaFacebook, FaTwitter } from 'react-icons/fa' 23 | import Lorem from 'react-lorem-component' 24 | import * as Loaders from 'react-spinners' 25 | import CircleIcon from '../../docs/icon' 26 | 27 | const reactIcons = { 28 | MdSettings, 29 | MdReceipt, 30 | MdGroupWork, 31 | MdCheckCircle, 32 | MdGraphicEq, 33 | MdBuild, 34 | MdCall, 35 | MdPhone, 36 | MdArrowDropDown, 37 | AiOutlineUser, 38 | FaFacebook, 39 | FaTwitter, 40 | } 41 | 42 | const StarIcon = (props) => ( 43 | 44 | 45 | 46 | ) 47 | 48 | const ReactLiveScope = { 49 | React, 50 | ...React, 51 | ...Chakra, 52 | ...Formik, 53 | ...ReactTable, 54 | ...Icons, 55 | ...Loaders, 56 | ...reactIcons, 57 | ...reactHooks, 58 | ...reactUtils, 59 | StarIcon, 60 | FocusLock, 61 | Lorem, 62 | CircleIcon, 63 | } 64 | 65 | export default ReactLiveScope 66 | -------------------------------------------------------------------------------- /docs/src/components/mdx-components/codeblock/styles.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export const liveEditorStyle: React.CSSProperties = { 4 | fontSize: 14, 5 | overflowX: 'auto', 6 | fontFamily: 'SF Mono, Menlo, monospace', 7 | } 8 | 9 | export const liveErrorStyle: React.CSSProperties = { 10 | fontFamily: 'SF Mono, Menlo, monospace', 11 | fontSize: 14, 12 | padding: '1em', 13 | overflowX: 'auto', 14 | color: 'white', 15 | backgroundColor: '#FF5555', 16 | } 17 | -------------------------------------------------------------------------------- /docs/src/components/mdx-components/component-links.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { 3 | Icon, 4 | Text, 5 | HStack, 6 | Wrap, 7 | Link, 8 | useColorModeValue, 9 | LinkProps, 10 | WrapItem, 11 | } from '@chakra-ui/react' 12 | import { FaNpm, FaGithub } from 'react-icons/fa' 13 | import StorybookIcon from '../storybook-icon' 14 | 15 | type ComponentLinkProps = LinkProps & { 16 | icon: React.ElementType 17 | url: string 18 | iconSize?: string 19 | iconColor?: string 20 | } 21 | 22 | function ComponentLink(props: ComponentLinkProps) { 23 | const { icon, url, children, iconSize, iconColor, ...rest } = props 24 | return ( 25 | 42 | 43 | 44 | 45 | {children} 46 | 47 | 48 | 49 | ) 50 | } 51 | 52 | export type ComponentLinksProps = { 53 | theme?: { componentName: string } 54 | github?: { url?: string; package?: string } 55 | npm?: { package: string } 56 | storybook?: { url: string } 57 | } 58 | function ComponentLinks(props: ComponentLinksProps) { 59 | const { theme, github, npm, storybook, ...rest } = props 60 | 61 | const githubRepoUrl = 'https://github.com/dwarvesf/react-toolkit' 62 | 63 | const githubLink = (github?.url || github?.package) && ( 64 | 65 | 73 | View source 74 | 75 | 76 | ) 77 | 78 | const npmLink = npm?.package && ( 79 | 80 | 86 | {npm.package} 87 | 88 | 89 | ) 90 | 91 | const storybookLink = storybook?.url && ( 92 | 93 | 99 | View storybook 100 | 101 | 102 | ) 103 | 104 | const themeComponentLink = theme && ( 105 | 106 | 112 | View theme source 113 | 114 | 115 | ) 116 | 117 | return ( 118 | 119 | {githubLink} 120 | {themeComponentLink} 121 | {npmLink} 122 | {storybookLink} 123 | 124 | ) 125 | } 126 | 127 | export default ComponentLinks 128 | -------------------------------------------------------------------------------- /docs/src/components/mdx-components/icons-list.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Grid, Button, Text, useClipboard, useToast } from '@chakra-ui/react' 3 | import * as icons from '@chakra-ui/icons' 4 | 5 | const iconList = { 6 | AddIcon: icons.AddIcon, 7 | ArrowBackIcon: icons.ArrowBackIcon, 8 | ArrowDownIcon: icons.ArrowDownIcon, 9 | ArrowForwardIcon: icons.ArrowForwardIcon, 10 | ArrowLeftIcon: icons.ArrowLeftIcon, 11 | ArrowRightIcon: icons.ArrowRightIcon, 12 | ArrowUpIcon: icons.ArrowUpIcon, 13 | ArrowUpDownIcon: icons.ArrowUpDownIcon, 14 | AtSignIcon: icons.AtSignIcon, 15 | AttachmentIcon: icons.AttachmentIcon, 16 | BellIcon: icons.BellIcon, 17 | CalendarIcon: icons.CalendarIcon, 18 | ChatIcon: icons.ChatIcon, 19 | CheckIcon: icons.CheckIcon, 20 | CheckCircleIcon: icons.CheckCircleIcon, 21 | ChevronDownIcon: icons.ChevronDownIcon, 22 | ChevronLeftIcon: icons.ChevronLeftIcon, 23 | ChevronRightIcon: icons.ChevronRightIcon, 24 | ChevronUpIcon: icons.ChevronUpIcon, 25 | CloseIcon: icons.CloseIcon, 26 | CopyIcon: icons.CopyIcon, 27 | DeleteIcon: icons.DeleteIcon, 28 | DownloadIcon: icons.DownloadIcon, 29 | DragHandleIcon: icons.DragHandleIcon, 30 | EditIcon: icons.EditIcon, 31 | EmailIcon: icons.EmailIcon, 32 | ExternalLinkIcon: icons.ExternalLinkIcon, 33 | HamburgerIcon: icons.HamburgerIcon, 34 | InfoIcon: icons.InfoIcon, 35 | InfoOutlineIcon: icons.InfoOutlineIcon, 36 | LinkIcon: icons.LinkIcon, 37 | LockIcon: icons.LockIcon, 38 | MinusIcon: icons.MinusIcon, 39 | MoonIcon: icons.MoonIcon, 40 | NotAllowedIcon: icons.NotAllowedIcon, 41 | PhoneIcon: icons.PhoneIcon, 42 | PlusSquareIcon: icons.PlusSquareIcon, 43 | QuestionIcon: icons.QuestionIcon, 44 | QuestionOutlineIcon: icons.QuestionOutlineIcon, 45 | RepeatIcon: icons.RepeatIcon, 46 | RepeatClockIcon: icons.RepeatClockIcon, 47 | SearchIcon: icons.SearchIcon, 48 | Search2Icon: icons.Search2Icon, 49 | SettingsIcon: icons.SettingsIcon, 50 | SmallAddIcon: icons.SmallAddIcon, 51 | SmallCloseIcon: icons.SmallCloseIcon, 52 | SpinnerIcon: icons.SpinnerIcon, 53 | StarIcon: icons.StarIcon, 54 | SunIcon: icons.SunIcon, 55 | TimeIcon: icons.TimeIcon, 56 | TriangleDownIcon: icons.TriangleDownIcon, 57 | TriangleUpIcon: icons.TriangleUpIcon, 58 | UnlockIcon: icons.UnlockIcon, 59 | UpDownIcon: icons.UpDownIcon, 60 | ViewIcon: icons.ViewIcon, 61 | ViewOffIcon: icons.ViewOffIcon, 62 | WarningIcon: icons.WarningIcon, 63 | WarningTwoIcon: icons.WarningTwoIcon, 64 | } 65 | 66 | const IconsList = () => { 67 | const toast = useToast() 68 | 69 | return ( 70 | 75 | {Object.keys(iconList).map((key, i) => { 76 | const Icon = iconList[key] 77 | const { onCopy } = useClipboard(key) 78 | 79 | const onCopyIcon = () => { 80 | onCopy() 81 | 82 | toast({ 83 | title: `'${key}' copied to clipboard`, 84 | status: 'success', 85 | duration: 2000, 86 | isClosable: false, 87 | }) 88 | } 89 | 90 | return ( 91 | 112 | ) 113 | })} 114 | 115 | ) 116 | } 117 | 118 | export default IconsList 119 | -------------------------------------------------------------------------------- /docs/src/components/mdx-components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './mdx-components' 2 | -------------------------------------------------------------------------------- /docs/src/components/mdx-components/inline-code.tsx: -------------------------------------------------------------------------------- 1 | import { chakra, useColorModeValue } from '@chakra-ui/react' 2 | import React from 'react' 3 | 4 | export const InlineCode = (props: any) => ( 5 | 6 | ) 7 | -------------------------------------------------------------------------------- /docs/src/components/mdx-components/linked-heading.tsx: -------------------------------------------------------------------------------- 1 | import { chakra, HTMLChakraProps } from '@chakra-ui/react' 2 | import React from 'react' 3 | 4 | export const LinkedHeading = (props: HTMLChakraProps<'h2'>) => ( 5 | 6 | {props.children} 7 | {props.id && ( 8 | 19 | # 20 | 21 | )} 22 | 23 | ) 24 | -------------------------------------------------------------------------------- /docs/src/components/mdx-components/mdx-components.tsx: -------------------------------------------------------------------------------- 1 | import * as Chakra from '@chakra-ui/react' 2 | import { 3 | ColorPalette, 4 | ColorPalettes, 5 | ColorWrapper, 6 | } from 'components/color-palette' 7 | import { Anchor } from 'components/mdx-components/anchor' 8 | import { InlineCode } from 'components/mdx-components/inline-code' 9 | import { LinkedHeading } from 'components/mdx-components/linked-heading' 10 | import { Pre } from 'components/mdx-components/pre' 11 | import { Table, TData, THead } from 'components/mdx-components/table' 12 | import { VideoPlayer } from 'components/mdx-components/video-player' 13 | import * as React from 'react' 14 | import PropsTable from '../props-table' 15 | import CodeBlock from './codeblock/codeblock' 16 | import ComponentLinks from './component-links' 17 | import IconsList from './icons-list' 18 | 19 | const { Alert, AspectRatio, Box, chakra, Kbd } = Chakra 20 | 21 | export const MDXComponents = { 22 | ...Chakra, 23 | h1: (props) => , 24 | h2: (props) => , 25 | h3: (props) => , 26 | h4: (props) => , 27 | hr: (props) => , 28 | strong: (props) => , 29 | inlineCode: InlineCode, 30 | code: CodeBlock, 31 | pre: Pre, 32 | kbd: Kbd, 33 | br: ({ reset, ...props }) => ( 34 | 39 | ), 40 | table: Table, 41 | th: THead, 42 | td: TData, 43 | a: Anchor, 44 | p: (props) => , 45 | ul: (props) => , 46 | ol: (props) => , 47 | li: (props) => , 48 | blockquote: (props) => ( 49 | 59 | ), 60 | ComponentLinks, 61 | IconsList, 62 | PropsTable, 63 | VideoPlayer, 64 | AspectRatio, 65 | ColorPalette, 66 | ColorPalettes, 67 | ColorWrapper, 68 | } 69 | -------------------------------------------------------------------------------- /docs/src/components/mdx-components/pre.tsx: -------------------------------------------------------------------------------- 1 | import { chakra } from '@chakra-ui/react' 2 | import * as React from 'react' 3 | 4 | export const Pre = (props) => ( 5 | 6 | ) 7 | -------------------------------------------------------------------------------- /docs/src/components/mdx-components/table.tsx: -------------------------------------------------------------------------------- 1 | import { chakra, useColorModeValue } from '@chakra-ui/react' 2 | import * as React from 'react' 3 | 4 | export const Table = (props) => ( 5 | 6 | 7 | 8 | ) 9 | 10 | export const THead = (props) => ( 11 | 18 | ) 19 | 20 | export const TData = (props) => ( 21 | 29 | ) 30 | -------------------------------------------------------------------------------- /docs/src/components/mdx-components/video-player.tsx: -------------------------------------------------------------------------------- 1 | import ReactPlayer, { ReactPlayerProps } from 'react-player' 2 | import React from 'react' 3 | 4 | export const VideoPlayer = (props: ReactPlayerProps) => ( 5 | 6 | ) 7 | -------------------------------------------------------------------------------- /docs/src/components/mobile-nav.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Box, 3 | BoxProps, 4 | Center, 5 | CloseButton, 6 | Flex, 7 | HStack, 8 | IconButton, 9 | IconButtonProps, 10 | useBreakpointValue, 11 | useColorModeValue, 12 | useUpdateEffect, 13 | } from '@chakra-ui/react' 14 | import { AnimatePresence, motion, useElementScroll } from 'framer-motion' 15 | import useRouteChanged from 'hooks/use-route-changed' 16 | import { getRoutes } from 'layouts/mdx' 17 | import { useRouter } from 'next/router' 18 | import * as React from 'react' 19 | import { AiOutlineMenu } from 'react-icons/ai' 20 | import { RemoveScroll } from 'react-remove-scroll' 21 | import Logo from './logo' 22 | import { SidebarContent } from './sidebar/sidebar' 23 | 24 | interface MobileNavContentProps { 25 | isOpen?: boolean 26 | onClose?: () => void 27 | } 28 | 29 | export function MobileNavContent(props: MobileNavContentProps) { 30 | const { isOpen, onClose } = props 31 | const closeBtnRef = React.useRef() 32 | const { pathname } = useRouter() 33 | 34 | useRouteChanged(onClose) 35 | 36 | /** 37 | * Scenario: Menu is open on mobile, and user resizes to desktop/tablet viewport. 38 | * Result: We'll close the menu 39 | */ 40 | const showOnBreakpoint = useBreakpointValue({ base: true, lg: false }) 41 | 42 | React.useEffect(() => { 43 | if (showOnBreakpoint == false) { 44 | onClose() 45 | } 46 | }, [showOnBreakpoint]) 47 | 48 | useUpdateEffect(() => { 49 | if (isOpen) { 50 | requestAnimationFrame(() => { 51 | closeBtnRef.current?.focus() 52 | }) 53 | } 54 | }, [isOpen]) 55 | 56 | const [shadow, setShadow] = React.useState() 57 | 58 | return ( 59 | 60 | {isOpen && ( 61 | 62 | 68 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | { 91 | setShadow(scrolled ? 'md' : undefined) 92 | }} 93 | > 94 | 98 | 99 | 100 | 101 | 102 | )} 103 | 104 | ) 105 | } 106 | 107 | const ScrollView = (props: BoxProps & { onScroll?: any }) => { 108 | const { onScroll, ...rest } = props 109 | const [y, setY] = React.useState(0) 110 | const elRef = React.useRef() 111 | const { scrollY } = useElementScroll(elRef) 112 | React.useEffect(() => { 113 | return scrollY.onChange(() => setY(scrollY.get())) 114 | }, [scrollY]) 115 | 116 | useUpdateEffect(() => { 117 | onScroll?.(y > 5 ? true : false) 118 | }, [y]) 119 | 120 | return ( 121 | 130 | ) 131 | } 132 | 133 | export const MobileNavButton = React.forwardRef( 134 | (props: IconButtonProps, ref: React.Ref) => { 135 | return ( 136 | } 144 | {...props} 145 | /> 146 | ) 147 | }, 148 | ) 149 | -------------------------------------------------------------------------------- /docs/src/components/page-container.tsx: -------------------------------------------------------------------------------- 1 | import { useRouter } from 'next/router' 2 | import * as React from 'react' 3 | import { Badge, Box, chakra, Flex } from '@chakra-ui/react' 4 | import { SkipNavContent, SkipNavLink } from '@chakra-ui/skip-nav' 5 | import EditPageLink from 'components/edit-page-button' 6 | import Footer from 'components/footer' 7 | import Header from 'components/header' 8 | import SEO from 'components/seo' 9 | import TableOfContent from 'components/table-of-content' 10 | import { convertBackticksToInlineCode } from 'utils/convert-backticks-to-inline-code' 11 | 12 | function useHeadingFocusOnRouteChange() { 13 | const router = useRouter() 14 | 15 | React.useEffect(() => { 16 | const onRouteChange = () => { 17 | const [heading] = Array.from(document.getElementsByTagName('h1')) 18 | heading?.focus() 19 | } 20 | router.events.on('routeChangeComplete', onRouteChange) 21 | return () => { 22 | router.events.off('routeChangeComplete', onRouteChange) 23 | } 24 | }, [router.events]) 25 | } 26 | 27 | export interface Heading { 28 | level: 'h2' | 'h3' 29 | text: string 30 | id: string 31 | } 32 | 33 | interface PageContainerProps { 34 | frontmatter: { 35 | slug?: string 36 | title: string 37 | description?: string 38 | editUrl?: string 39 | version?: string 40 | headings?: Heading[] 41 | } 42 | children: React.ReactNode 43 | sidebar?: any 44 | pagination?: any 45 | } 46 | 47 | function PageContainer(props: PageContainerProps) { 48 | const { frontmatter, children, sidebar, pagination } = props 49 | useHeadingFocusOnRouteChange() 50 | 51 | const { title, description, editUrl, version, headings = [] } = frontmatter 52 | 53 | return ( 54 | <> 55 | 56 | Skip to Content 57 |
58 | 59 | 60 | {sidebar || null} 61 | 62 | 63 | 64 | 65 | 71 | 72 | 73 | {convertBackticksToInlineCode(title)} 74 | 75 | {version && ( 76 | 77 | v{version} 78 | 79 | )} 80 | {children} 81 | 82 | {editUrl && } 83 | {pagination || null} 84 | 85 | 86 |