├── .github ├── card.png └── demo.gif ├── .gitignore ├── LICENSE.md ├── README.md ├── package.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── src ├── index.tsx └── utils.tsx ├── test └── benchmark │ ├── gen.js │ ├── ssr-balancer.html │ └── ssr.html ├── tsconfig.json ├── tsup.config.ts ├── turbo.json └── website ├── app ├── layout.tsx ├── page.tsx └── style.css ├── css.d.ts ├── next-env.d.ts ├── next.config.js ├── package.json ├── public ├── android-icon-144x144.png ├── android-icon-192x192.png ├── android-icon-36x36.png ├── android-icon-48x48.png ├── android-icon-72x72.png ├── android-icon-96x96.png ├── apple-icon-114x114.png ├── apple-icon-120x120.png ├── apple-icon-144x144.png ├── apple-icon-152x152.png ├── apple-icon-180x180.png ├── apple-icon-57x57.png ├── apple-icon-60x60.png ├── apple-icon-72x72.png ├── apple-icon-76x76.png ├── apple-icon-precomposed.png ├── apple-icon.png ├── bench.svg ├── dark-android-icon-144x144.png ├── dark-android-icon-192x192.png ├── dark-android-icon-36x36.png ├── dark-android-icon-48x48.png ├── dark-android-icon-72x72.png ├── dark-android-icon-96x96.png ├── dark-apple-icon-114x114.png ├── dark-apple-icon-120x120.png ├── dark-apple-icon-144x144.png ├── dark-apple-icon-152x152.png ├── dark-apple-icon-180x180.png ├── dark-apple-icon-57x57.png ├── dark-apple-icon-60x60.png ├── dark-apple-icon-72x72.png ├── dark-apple-icon-76x76.png ├── dark-apple-icon-precomposed.png ├── dark-apple-icon.png ├── dark-favicon-16x16.png ├── dark-favicon-32x32.png ├── dark-favicon-96x96.png ├── dark-favicon.ico ├── dark-ms-icon-144x144.png ├── dark-ms-icon-150x150.png ├── dark-ms-icon-310x310.png ├── dark-ms-icon-70x70.png ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon-96x96.png ├── favicon.ico ├── ms-icon-144x144.png ├── ms-icon-150x150.png ├── ms-icon-310x310.png ├── ms-icon-70x70.png └── og.png ├── src ├── components │ ├── BlankLink.tsx │ ├── Comparison.tsx │ ├── Content.tsx │ └── index.ts ├── icons │ ├── Copy.tsx │ ├── GitHub.tsx │ ├── TooltipArrow.tsx │ ├── TooltipTrigger.tsx │ ├── Vercel.tsx │ └── index.ts └── sections │ ├── About.tsx │ ├── CustomBalanceRatio.tsx │ ├── Footer.tsx │ ├── GettingStarted.tsx │ ├── Header.tsx │ ├── Hero.tsx │ ├── HowItWorks.tsx │ ├── Performance.tsx │ ├── UseCases.tsx │ └── index.ts └── tsconfig.json /.github/card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/.github/card.png -------------------------------------------------------------------------------- /.github/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/.github/demo.gif -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .DS_Store 4 | .next 5 | .turbo 6 | .vscode -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Shu Ding 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![React Wrap Balancer - Simple React Component That Makes Titles More Readable](.github/card.png)](https://react-wrap-balancer.vercel.app) 2 | 3 | ## Introduction 4 | 5 | [**React Wrap Balancer**](https://react-wrap-balancer.vercel.app) is a simple React Component that makes your titles more readable in different viewport sizes. It improves the wrapping to avoid situations like single word in the last line, makes the content more “balanced”: 6 | 7 | ![](.github/demo.gif) 8 | 9 | ## Usage 10 | 11 | To start using the library, install it to your project: 12 | 13 | ```bash 14 | npm i react-wrap-balancer 15 | ``` 16 | 17 | And wrap text content with it: 18 | 19 | ```jsx 20 | import Balancer from 'react-wrap-balancer' 21 | 22 | // ... 23 | 24 | function Title() { 25 | return ( 26 |

27 | My Awesome Title 28 |

29 | ) 30 | } 31 | ``` 32 | 33 | ### `` 34 | 35 | `` is the main component of the library. It will automatically balance the text content inside it. It accepts the following props: 36 | 37 | - **`as`** (_optional_): The HTML tag to be used to wrap the text content. Default to `span`. 38 | - **`ratio`** (_optional_): The ratio of “balance-ness”, 0 <= ratio <= 1. Default to `1`. 39 | - **`preferNative`** (_optional_): An option to skip the re-balance logic and use the native CSS text-balancing if supported. Default to `true`. 40 | - **`nonce`** (_optional_): The [nonce](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) attribute to allowlist inline script injection by the component. 41 | 42 | ### `` 43 | 44 | If you have multiple `` components used, it’s recommended (but optional) to also use 45 | `` to wrap the entire app. This will make them share the re-balance logic and reduce the HTML size: 46 | 47 | ```jsx 48 | import { Provider } from 'react-wrap-balancer' 49 | 50 | // ... 51 | 52 | function App() { 53 | return ( 54 | 55 | 56 | 57 | ) 58 | } 59 | ``` 60 | 61 | For full documentation and use cases, please visit [**react-wrap-balancer.vercel.app**](https://react-wrap-balancer.vercel.app). 62 | 63 | ## Browser Support Information 64 | Desktop: 65 | 66 | | Browser | Min Version | 67 | |:-------:|:-----------:| 68 | | Chrome | 64 | 69 | | Edge | 79 | 70 | | Safari | 13.1 | 71 | | FireFox | 69 | 72 | | Opera | 51 | 73 | | IE | No Support | 74 | 75 | Mobile: 76 | 77 | | Browser | Min Version | 78 | |:---------------:|:-----------:| 79 | | Chrome | 64 | 80 | | Safari | 13.4 | 81 | | Firefox | 69 | 82 | | Opera | 47 | 83 | | WebView Android | 64 | 84 | 85 | Cross-browser compatibility issues are mainly due to the fact that lib uses the ResizeObserver API. More information about this API can be found at this [link](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver). 86 | 87 | If you are using a browser which version is lower than the versions in the table, please consider adding polyfill for this API or upgrade your browser. 88 | 89 | ## About 90 | 91 | This project was inspired by Adobe’s [balance-text](https://github.com/adobe/balance-text) project, NYT’s [text-balancer](https://github.com/nytimes/text-balancer) project, and Daniel Aleksandersen’s [Improving the New York Times’ line wrap balancer](https://www.ctrl.blog/entry/text-wrap-balance.html). If you want to learn more, you can also take a look at the [text-wrap: balance](https://drafts.csswg.org/css-text-4/#text-wrap) proposal. 92 | 93 | Special thanks to [Emil Kowalski](https://twitter.com/emilkowalski_) for testing and feedback. 94 | 95 | Created by [Shu Ding](https://twitter.com/shuding_) in 2022, released under the MIT license. 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-wrap-balancer", 3 | "version": "1.1.1", 4 | "description": "Simple React component that makes titles more readable.", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/shuding/react-wrap-balancer.git" 8 | }, 9 | "homepage": "https://react-wrap-balancer.vercel.app", 10 | "bugs": "https://github.com/shuding/react-wrap-balancer/issues", 11 | "main": "dist/index.js", 12 | "module": "dist/index.mjs", 13 | "types": "dist/index.d.ts", 14 | "files": ["dist"], 15 | "scripts": { 16 | "build": "tsup src/index.tsx", 17 | "dev": "tsup src/index.tsx --watch", 18 | "dev:website": "turbo run dev --filter=website..." 19 | }, 20 | "keywords": ["react", "wrapping", "text", "typography"], 21 | "author": "Shu Ding ", 22 | "license": "MIT", 23 | "devDependencies": { 24 | "@types/node": "^18.11.13", 25 | "@types/react": "^18.0.26", 26 | "react": "^18.2.0", 27 | "tsup": "^6.4.0", 28 | "turbo": "^1.6.3", 29 | "typescript": "^4.8.4" 30 | }, 31 | "peerDependencies": { 32 | "react": ">=16.8.0 || ^17.0.0 || ^18" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | devDependencies: 11 | '@types/node': 12 | specifier: ^18.11.13 13 | version: 18.16.1 14 | '@types/react': 15 | specifier: ^18.0.26 16 | version: 18.2.0 17 | react: 18 | specifier: ^18.2.0 19 | version: 18.2.0 20 | tsup: 21 | specifier: ^6.4.0 22 | version: 6.7.0(typescript@4.9.5) 23 | turbo: 24 | specifier: ^1.6.3 25 | version: 1.9.3 26 | typescript: 27 | specifier: ^4.8.4 28 | version: 4.9.5 29 | 30 | website: 31 | dependencies: 32 | '@react-spring/web': 33 | specifier: ^9.5.5 34 | version: 9.7.2(react-dom@18.2.0)(react@18.2.0) 35 | '@vercel/analytics': 36 | specifier: ^1.0.0 37 | version: 1.0.0(react@18.2.0) 38 | clsx: 39 | specifier: ^1.2.1 40 | version: 1.2.1 41 | copy-to-clipboard: 42 | specifier: ^3.3.3 43 | version: 3.3.3 44 | next: 45 | specifier: 13.3.1 46 | version: 13.3.1(react-dom@18.2.0)(react@18.2.0) 47 | react: 48 | specifier: ^18.2.0 49 | version: 18.2.0 50 | react-dom: 51 | specifier: ^18.2.0 52 | version: 18.2.0(react@18.2.0) 53 | react-wrap-balancer: 54 | specifier: workspace:* 55 | version: link:.. 56 | devDependencies: 57 | '@types/react': 58 | specifier: ^18.2.0 59 | version: 18.2.0 60 | '@types/react-dom': 61 | specifier: ^18.2.1 62 | version: 18.2.1 63 | csstype: 64 | specifier: ^3.1.2 65 | version: 3.1.2 66 | typescript: 67 | specifier: ^4.8.4 68 | version: 4.9.5 69 | 70 | packages: 71 | 72 | '@esbuild/android-arm64@0.17.18': 73 | resolution: {integrity: sha512-/iq0aK0eeHgSC3z55ucMAHO05OIqmQehiGay8eP5l/5l+iEr4EIbh4/MI8xD9qRFjqzgkc0JkX0LculNC9mXBw==} 74 | engines: {node: '>=12'} 75 | cpu: [arm64] 76 | os: [android] 77 | 78 | '@esbuild/android-arm@0.17.18': 79 | resolution: {integrity: sha512-EmwL+vUBZJ7mhFCs5lA4ZimpUH3WMAoqvOIYhVQwdIgSpHC8ImHdsRyhHAVxpDYUSm0lWvd63z0XH1IlImS2Qw==} 80 | engines: {node: '>=12'} 81 | cpu: [arm] 82 | os: [android] 83 | 84 | '@esbuild/android-x64@0.17.18': 85 | resolution: {integrity: sha512-x+0efYNBF3NPW2Xc5bFOSFW7tTXdAcpfEg2nXmxegm4mJuVeS+i109m/7HMiOQ6M12aVGGFlqJX3RhNdYM2lWg==} 86 | engines: {node: '>=12'} 87 | cpu: [x64] 88 | os: [android] 89 | 90 | '@esbuild/darwin-arm64@0.17.18': 91 | resolution: {integrity: sha512-6tY+djEAdF48M1ONWnQb1C+6LiXrKjmqjzPNPWXhu/GzOHTHX2nh8Mo2ZAmBFg0kIodHhciEgUBtcYCAIjGbjQ==} 92 | engines: {node: '>=12'} 93 | cpu: [arm64] 94 | os: [darwin] 95 | 96 | '@esbuild/darwin-x64@0.17.18': 97 | resolution: {integrity: sha512-Qq84ykvLvya3dO49wVC9FFCNUfSrQJLbxhoQk/TE1r6MjHo3sFF2tlJCwMjhkBVq3/ahUisj7+EpRSz0/+8+9A==} 98 | engines: {node: '>=12'} 99 | cpu: [x64] 100 | os: [darwin] 101 | 102 | '@esbuild/freebsd-arm64@0.17.18': 103 | resolution: {integrity: sha512-fw/ZfxfAzuHfaQeMDhbzxp9mc+mHn1Y94VDHFHjGvt2Uxl10mT4CDavHm+/L9KG441t1QdABqkVYwakMUeyLRA==} 104 | engines: {node: '>=12'} 105 | cpu: [arm64] 106 | os: [freebsd] 107 | 108 | '@esbuild/freebsd-x64@0.17.18': 109 | resolution: {integrity: sha512-FQFbRtTaEi8ZBi/A6kxOC0V0E9B/97vPdYjY9NdawyLd4Qk5VD5g2pbWN2VR1c0xhzcJm74HWpObPszWC+qTew==} 110 | engines: {node: '>=12'} 111 | cpu: [x64] 112 | os: [freebsd] 113 | 114 | '@esbuild/linux-arm64@0.17.18': 115 | resolution: {integrity: sha512-R7pZvQZFOY2sxUG8P6A21eq6q+eBv7JPQYIybHVf1XkQYC+lT7nDBdC7wWKTrbvMXKRaGudp/dzZCwL/863mZQ==} 116 | engines: {node: '>=12'} 117 | cpu: [arm64] 118 | os: [linux] 119 | 120 | '@esbuild/linux-arm@0.17.18': 121 | resolution: {integrity: sha512-jW+UCM40LzHcouIaqv3e/oRs0JM76JfhHjCavPxMUti7VAPh8CaGSlS7cmyrdpzSk7A+8f0hiedHqr/LMnfijg==} 122 | engines: {node: '>=12'} 123 | cpu: [arm] 124 | os: [linux] 125 | 126 | '@esbuild/linux-ia32@0.17.18': 127 | resolution: {integrity: sha512-ygIMc3I7wxgXIxk6j3V00VlABIjq260i967Cp9BNAk5pOOpIXmd1RFQJQX9Io7KRsthDrQYrtcx7QCof4o3ZoQ==} 128 | engines: {node: '>=12'} 129 | cpu: [ia32] 130 | os: [linux] 131 | 132 | '@esbuild/linux-loong64@0.17.18': 133 | resolution: {integrity: sha512-bvPG+MyFs5ZlwYclCG1D744oHk1Pv7j8psF5TfYx7otCVmcJsEXgFEhQkbhNW8otDHL1a2KDINW20cfCgnzgMQ==} 134 | engines: {node: '>=12'} 135 | cpu: [loong64] 136 | os: [linux] 137 | 138 | '@esbuild/linux-mips64el@0.17.18': 139 | resolution: {integrity: sha512-oVqckATOAGuiUOa6wr8TXaVPSa+6IwVJrGidmNZS1cZVx0HqkTMkqFGD2HIx9H1RvOwFeWYdaYbdY6B89KUMxA==} 140 | engines: {node: '>=12'} 141 | cpu: [mips64el] 142 | os: [linux] 143 | 144 | '@esbuild/linux-ppc64@0.17.18': 145 | resolution: {integrity: sha512-3dLlQO+b/LnQNxgH4l9rqa2/IwRJVN9u/bK63FhOPB4xqiRqlQAU0qDU3JJuf0BmaH0yytTBdoSBHrb2jqc5qQ==} 146 | engines: {node: '>=12'} 147 | cpu: [ppc64] 148 | os: [linux] 149 | 150 | '@esbuild/linux-riscv64@0.17.18': 151 | resolution: {integrity: sha512-/x7leOyDPjZV3TcsdfrSI107zItVnsX1q2nho7hbbQoKnmoeUWjs+08rKKt4AUXju7+3aRZSsKrJtaRmsdL1xA==} 152 | engines: {node: '>=12'} 153 | cpu: [riscv64] 154 | os: [linux] 155 | 156 | '@esbuild/linux-s390x@0.17.18': 157 | resolution: {integrity: sha512-cX0I8Q9xQkL/6F5zWdYmVf5JSQt+ZfZD2bJudZrWD+4mnUvoZ3TDDXtDX2mUaq6upMFv9FlfIh4Gfun0tbGzuw==} 158 | engines: {node: '>=12'} 159 | cpu: [s390x] 160 | os: [linux] 161 | 162 | '@esbuild/linux-x64@0.17.18': 163 | resolution: {integrity: sha512-66RmRsPlYy4jFl0vG80GcNRdirx4nVWAzJmXkevgphP1qf4dsLQCpSKGM3DUQCojwU1hnepI63gNZdrr02wHUA==} 164 | engines: {node: '>=12'} 165 | cpu: [x64] 166 | os: [linux] 167 | 168 | '@esbuild/netbsd-x64@0.17.18': 169 | resolution: {integrity: sha512-95IRY7mI2yrkLlTLb1gpDxdC5WLC5mZDi+kA9dmM5XAGxCME0F8i4bYH4jZreaJ6lIZ0B8hTrweqG1fUyW7jbg==} 170 | engines: {node: '>=12'} 171 | cpu: [x64] 172 | os: [netbsd] 173 | 174 | '@esbuild/openbsd-x64@0.17.18': 175 | resolution: {integrity: sha512-WevVOgcng+8hSZ4Q3BKL3n1xTv5H6Nb53cBrtzzEjDbbnOmucEVcZeGCsCOi9bAOcDYEeBZbD2SJNBxlfP3qiA==} 176 | engines: {node: '>=12'} 177 | cpu: [x64] 178 | os: [openbsd] 179 | 180 | '@esbuild/sunos-x64@0.17.18': 181 | resolution: {integrity: sha512-Rzf4QfQagnwhQXVBS3BYUlxmEbcV7MY+BH5vfDZekU5eYpcffHSyjU8T0xucKVuOcdCsMo+Ur5wmgQJH2GfNrg==} 182 | engines: {node: '>=12'} 183 | cpu: [x64] 184 | os: [sunos] 185 | 186 | '@esbuild/win32-arm64@0.17.18': 187 | resolution: {integrity: sha512-Kb3Ko/KKaWhjeAm2YoT/cNZaHaD1Yk/pa3FTsmqo9uFh1D1Rfco7BBLIPdDOozrObj2sahslFuAQGvWbgWldAg==} 188 | engines: {node: '>=12'} 189 | cpu: [arm64] 190 | os: [win32] 191 | 192 | '@esbuild/win32-ia32@0.17.18': 193 | resolution: {integrity: sha512-0/xUMIdkVHwkvxfbd5+lfG7mHOf2FRrxNbPiKWg9C4fFrB8H0guClmaM3BFiRUYrznVoyxTIyC/Ou2B7QQSwmw==} 194 | engines: {node: '>=12'} 195 | cpu: [ia32] 196 | os: [win32] 197 | 198 | '@esbuild/win32-x64@0.17.18': 199 | resolution: {integrity: sha512-qU25Ma1I3NqTSHJUOKi9sAH1/Mzuvlke0ioMJRthLXKm7JiSKVwFghlGbDLOO2sARECGhja4xYfRAZNPAkooYg==} 200 | engines: {node: '>=12'} 201 | cpu: [x64] 202 | os: [win32] 203 | 204 | '@jridgewell/gen-mapping@0.3.3': 205 | resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} 206 | engines: {node: '>=6.0.0'} 207 | 208 | '@jridgewell/resolve-uri@3.1.0': 209 | resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} 210 | engines: {node: '>=6.0.0'} 211 | 212 | '@jridgewell/set-array@1.1.2': 213 | resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} 214 | engines: {node: '>=6.0.0'} 215 | 216 | '@jridgewell/sourcemap-codec@1.4.14': 217 | resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} 218 | 219 | '@jridgewell/sourcemap-codec@1.4.15': 220 | resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} 221 | 222 | '@jridgewell/trace-mapping@0.3.18': 223 | resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==} 224 | 225 | '@next/env@13.3.1': 226 | resolution: {integrity: sha512-EDtCoedIZC7JlUQ3uaQpSc4aVmyhbLHmQVALg7pFfQgOTjgSnn7mKtA0DiCMkYvvsx6aFb5octGMtWrOtGXW9A==} 227 | 228 | '@next/swc-darwin-arm64@13.3.1': 229 | resolution: {integrity: sha512-UXPtriEc/pBP8luSLSCZBcbzPeVv+SSjs9cH/KygTbhmACye8/OOXRZO13Z2Wq1G0gLmEAIHQAOuF+vafPd2lw==} 230 | engines: {node: '>= 10'} 231 | cpu: [arm64] 232 | os: [darwin] 233 | 234 | '@next/swc-darwin-x64@13.3.1': 235 | resolution: {integrity: sha512-lT36yYxosCfLtplFzJWgo0hrPu6/do8+msgM7oQkPeohDNdhjtjFUgOOwdSnPublLR6Mo2Ym4P/wl5OANuD2bw==} 236 | engines: {node: '>= 10'} 237 | cpu: [x64] 238 | os: [darwin] 239 | 240 | '@next/swc-linux-arm64-gnu@13.3.1': 241 | resolution: {integrity: sha512-wRb76nLWJhonH8s3kxC/1tFguEkeOPayIwe9mkaz1G/yeS3OrjeyKMJsb4+Kdg0zbTo53bNCOl59NNtDM7yyyw==} 242 | engines: {node: '>= 10'} 243 | cpu: [arm64] 244 | os: [linux] 245 | 246 | '@next/swc-linux-arm64-musl@13.3.1': 247 | resolution: {integrity: sha512-qz3BzjJRZ16Iq/jrp+pjiYOc0jTjHlfmxQmZk9x/+5uhRP6/eWQSTAPVJ33BMo6oK5O5N4644OgTAbzXzorecg==} 248 | engines: {node: '>= 10'} 249 | cpu: [arm64] 250 | os: [linux] 251 | 252 | '@next/swc-linux-x64-gnu@13.3.1': 253 | resolution: {integrity: sha512-6mgkLmwlyWlomQmpl21I3hxgqE5INoW4owTlcLpNsd1V4wP+J46BlI/5zV5KWWbzjfncIqzXoeGs5Eg+1GHODA==} 254 | engines: {node: '>= 10'} 255 | cpu: [x64] 256 | os: [linux] 257 | 258 | '@next/swc-linux-x64-musl@13.3.1': 259 | resolution: {integrity: sha512-uqm5sielhQmKJM+qayIhgZv1KlS5pqTdQ99b+Z7hMWryXS96qE0DftTmMZowBcUL6x7s2vSXyH5wPtO1ON7LBg==} 260 | engines: {node: '>= 10'} 261 | cpu: [x64] 262 | os: [linux] 263 | 264 | '@next/swc-win32-arm64-msvc@13.3.1': 265 | resolution: {integrity: sha512-WomIiTj/v3LevltlibNQKmvrOymNRYL+a0dp5R73IwPWN5FvXWwSELN/kiNALig/+T3luc4qHNTyvMCp9L6U5Q==} 266 | engines: {node: '>= 10'} 267 | cpu: [arm64] 268 | os: [win32] 269 | 270 | '@next/swc-win32-ia32-msvc@13.3.1': 271 | resolution: {integrity: sha512-M+PoH+0+q658wRUbs285RIaSTYnGBSTdweH/0CdzDgA6Q4rBM0sQs4DHmO3BPP0ltCO/vViIoyG7ks66XmCA5g==} 272 | engines: {node: '>= 10'} 273 | cpu: [ia32] 274 | os: [win32] 275 | 276 | '@next/swc-win32-x64-msvc@13.3.1': 277 | resolution: {integrity: sha512-Sl1F4Vp5Z1rNXWZYqJwMuWRRol4bqOB6+/d7KqkgQ4AcafKPN1PZmpkCoxv4UFHtFNIB7EotnuIhtXu3zScicQ==} 278 | engines: {node: '>= 10'} 279 | cpu: [x64] 280 | os: [win32] 281 | 282 | '@nodelib/fs.scandir@2.1.5': 283 | resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} 284 | engines: {node: '>= 8'} 285 | 286 | '@nodelib/fs.stat@2.0.5': 287 | resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} 288 | engines: {node: '>= 8'} 289 | 290 | '@nodelib/fs.walk@1.2.8': 291 | resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 292 | engines: {node: '>= 8'} 293 | 294 | '@react-spring/animated@9.7.2': 295 | resolution: {integrity: sha512-ipvleJ99ipqlnHkz5qhSsgf/ny5aW0ZG8Q+/2Oj9cI7LCc7COdnrSO6V/v8MAX3JOoQNzfz6dye2s5Pt5jGaIA==} 296 | peerDependencies: 297 | react: ^16.8.0 || ^17.0.0 || ^18.0.0 298 | 299 | '@react-spring/core@9.7.2': 300 | resolution: {integrity: sha512-fF512edZT/gKVCA90ZRxfw1DmELeVwiL4OC2J6bMUlNr707C0h4QRoec6DjzG27uLX2MvS1CEatf9KRjwZR9/w==} 301 | peerDependencies: 302 | react: ^16.8.0 || ^17.0.0 || ^18.0.0 303 | 304 | '@react-spring/rafz@9.7.2': 305 | resolution: {integrity: sha512-kDWMYDQto3+flkrX3vy6DU/l9pxQ4TVW91DglQEc11iDc7shF4+WVDRJvOVLX+xoMP7zyag1dMvlIgvQ+dvA/A==} 306 | 307 | '@react-spring/shared@9.7.2': 308 | resolution: {integrity: sha512-6U9qkno+9DxlH5nSltnPs+kU6tYKf0bPLURX2te13aGel8YqgcpFYp5Av8DcN2x3sukinAsmzHUS/FRsdZMMBA==} 309 | peerDependencies: 310 | react: ^16.8.0 || ^17.0.0 || ^18.0.0 311 | 312 | '@react-spring/types@9.7.2': 313 | resolution: {integrity: sha512-GEflx2Ex/TKVMHq5g5MxQDNNPNhqg+4Db9m7+vGTm8ttZiyga7YQUF24shgRNebKIjahqCuei16SZga8h1pe4g==} 314 | 315 | '@react-spring/web@9.7.2': 316 | resolution: {integrity: sha512-7qNc7/5KShu2D05x7o2Ols2nUE7mCKfKLaY2Ix70xPMfTle1sZisoQMBFgV9w/fSLZlHZHV9P0uWJqEXQnbV4Q==} 317 | peerDependencies: 318 | react: ^16.8.0 || ^17.0.0 || ^18.0.0 319 | react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 320 | 321 | '@swc/helpers@0.5.0': 322 | resolution: {integrity: sha512-SjY/p4MmECVVEWspzSRpQEM3sjR17sP8PbGxELWrT+YZMBfiUyt1MRUNjMV23zohwlG2HYtCQOsCwsTHguXkyg==} 323 | 324 | '@types/node@18.16.1': 325 | resolution: {integrity: sha512-DZxSZWXxFfOlx7k7Rv4LAyiMroaxa3Ly/7OOzZO8cBNho0YzAi4qlbrx8W27JGqG57IgR/6J7r+nOJWw6kcvZA==} 326 | 327 | '@types/prop-types@15.7.5': 328 | resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} 329 | 330 | '@types/react-dom@18.2.1': 331 | resolution: {integrity: sha512-8QZEV9+Kwy7tXFmjJrp3XUKQSs9LTnE0KnoUb0YCguWBiNW0Yfb2iBMYZ08WPg35IR6P3Z0s00B15SwZnO26+w==} 332 | 333 | '@types/react@18.2.0': 334 | resolution: {integrity: sha512-0FLj93y5USLHdnhIhABk83rm8XEGA7kH3cr+YUlvxoUGp1xNt/DINUMvqPxLyOQMzLmZe8i4RTHbvb8MC7NmrA==} 335 | 336 | '@types/scheduler@0.16.3': 337 | resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==} 338 | 339 | '@vercel/analytics@1.0.0': 340 | resolution: {integrity: sha512-RQmj7pv82JwGDHrnKeRc6TtSw2U7rWNubc2IH0ernTzWTj02yr9zvIYiYJeztsBzrJtWv7m8Nz6vxxb+cdEtJw==} 341 | peerDependencies: 342 | react: ^16.8||^17||^18 343 | 344 | any-promise@1.3.0: 345 | resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} 346 | 347 | anymatch@3.1.3: 348 | resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} 349 | engines: {node: '>= 8'} 350 | 351 | array-union@2.1.0: 352 | resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} 353 | engines: {node: '>=8'} 354 | 355 | balanced-match@1.0.2: 356 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 357 | 358 | binary-extensions@2.2.0: 359 | resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} 360 | engines: {node: '>=8'} 361 | 362 | brace-expansion@1.1.11: 363 | resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} 364 | 365 | braces@3.0.2: 366 | resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} 367 | engines: {node: '>=8'} 368 | 369 | bundle-require@4.0.1: 370 | resolution: {integrity: sha512-9NQkRHlNdNpDBGmLpngF3EFDcwodhMUuLz9PaWYciVcQF9SE4LFjM2DB/xV1Li5JiuDMv7ZUWuC3rGbqR0MAXQ==} 371 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 372 | peerDependencies: 373 | esbuild: '>=0.17' 374 | 375 | busboy@1.6.0: 376 | resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} 377 | engines: {node: '>=10.16.0'} 378 | 379 | cac@6.7.14: 380 | resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} 381 | engines: {node: '>=8'} 382 | 383 | caniuse-lite@1.0.30001481: 384 | resolution: {integrity: sha512-KCqHwRnaa1InZBtqXzP98LPg0ajCVujMKjqKDhZEthIpAsJl/YEIa3YvXjGXPVqzZVguccuu7ga9KOE1J9rKPQ==} 385 | 386 | chokidar@3.5.3: 387 | resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} 388 | engines: {node: '>= 8.10.0'} 389 | 390 | client-only@0.0.1: 391 | resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} 392 | 393 | clsx@1.2.1: 394 | resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} 395 | engines: {node: '>=6'} 396 | 397 | commander@4.1.1: 398 | resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} 399 | engines: {node: '>= 6'} 400 | 401 | concat-map@0.0.1: 402 | resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} 403 | 404 | copy-to-clipboard@3.3.3: 405 | resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} 406 | 407 | cross-spawn@7.0.3: 408 | resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} 409 | engines: {node: '>= 8'} 410 | 411 | csstype@3.1.2: 412 | resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} 413 | 414 | debug@4.3.4: 415 | resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} 416 | engines: {node: '>=6.0'} 417 | peerDependencies: 418 | supports-color: '*' 419 | peerDependenciesMeta: 420 | supports-color: 421 | optional: true 422 | 423 | dir-glob@3.0.1: 424 | resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} 425 | engines: {node: '>=8'} 426 | 427 | esbuild@0.17.18: 428 | resolution: {integrity: sha512-z1lix43jBs6UKjcZVKOw2xx69ffE2aG0PygLL5qJ9OS/gy0Ewd1gW/PUQIOIQGXBHWNywSc0floSKoMFF8aK2w==} 429 | engines: {node: '>=12'} 430 | hasBin: true 431 | 432 | execa@5.1.1: 433 | resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} 434 | engines: {node: '>=10'} 435 | 436 | fast-glob@3.2.12: 437 | resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} 438 | engines: {node: '>=8.6.0'} 439 | 440 | fastq@1.15.0: 441 | resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} 442 | 443 | fill-range@7.0.1: 444 | resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} 445 | engines: {node: '>=8'} 446 | 447 | fs.realpath@1.0.0: 448 | resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} 449 | 450 | fsevents@2.3.2: 451 | resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} 452 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 453 | os: [darwin] 454 | 455 | get-stream@6.0.1: 456 | resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} 457 | engines: {node: '>=10'} 458 | 459 | glob-parent@5.1.2: 460 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 461 | engines: {node: '>= 6'} 462 | 463 | glob@7.1.6: 464 | resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} 465 | 466 | globby@11.1.0: 467 | resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} 468 | engines: {node: '>=10'} 469 | 470 | human-signals@2.1.0: 471 | resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} 472 | engines: {node: '>=10.17.0'} 473 | 474 | ignore@5.2.4: 475 | resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} 476 | engines: {node: '>= 4'} 477 | 478 | inflight@1.0.6: 479 | resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} 480 | 481 | inherits@2.0.4: 482 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 483 | 484 | is-binary-path@2.1.0: 485 | resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} 486 | engines: {node: '>=8'} 487 | 488 | is-extglob@2.1.1: 489 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 490 | engines: {node: '>=0.10.0'} 491 | 492 | is-glob@4.0.3: 493 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 494 | engines: {node: '>=0.10.0'} 495 | 496 | is-number@7.0.0: 497 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 498 | engines: {node: '>=0.12.0'} 499 | 500 | is-stream@2.0.1: 501 | resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} 502 | engines: {node: '>=8'} 503 | 504 | isexe@2.0.0: 505 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 506 | 507 | joycon@3.1.1: 508 | resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} 509 | engines: {node: '>=10'} 510 | 511 | js-tokens@4.0.0: 512 | resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} 513 | 514 | lilconfig@2.1.0: 515 | resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} 516 | engines: {node: '>=10'} 517 | 518 | lines-and-columns@1.2.4: 519 | resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} 520 | 521 | load-tsconfig@0.2.5: 522 | resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} 523 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 524 | 525 | lodash.sortby@4.7.0: 526 | resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} 527 | 528 | loose-envify@1.4.0: 529 | resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} 530 | hasBin: true 531 | 532 | merge-stream@2.0.0: 533 | resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} 534 | 535 | merge2@1.4.1: 536 | resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} 537 | engines: {node: '>= 8'} 538 | 539 | micromatch@4.0.5: 540 | resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} 541 | engines: {node: '>=8.6'} 542 | 543 | mimic-fn@2.1.0: 544 | resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} 545 | engines: {node: '>=6'} 546 | 547 | minimatch@3.1.2: 548 | resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} 549 | 550 | ms@2.1.2: 551 | resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} 552 | 553 | mz@2.7.0: 554 | resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} 555 | 556 | nanoid@3.3.6: 557 | resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} 558 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 559 | hasBin: true 560 | 561 | next@13.3.1: 562 | resolution: {integrity: sha512-eByWRxPzKHs2oQz1yE41LX35umhz86ZSZ+mYyXBqn2IBi2hyUqxBA88avywdr4uyH+hCJczegGsDGWbzQA5Rqw==} 563 | engines: {node: '>=14.18.0'} 564 | hasBin: true 565 | peerDependencies: 566 | '@opentelemetry/api': ^1.1.0 567 | fibers: '>= 3.1.0' 568 | node-sass: ^6.0.0 || ^7.0.0 569 | react: ^18.2.0 570 | react-dom: ^18.2.0 571 | sass: ^1.3.0 572 | peerDependenciesMeta: 573 | '@opentelemetry/api': 574 | optional: true 575 | fibers: 576 | optional: true 577 | node-sass: 578 | optional: true 579 | sass: 580 | optional: true 581 | 582 | normalize-path@3.0.0: 583 | resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} 584 | engines: {node: '>=0.10.0'} 585 | 586 | npm-run-path@4.0.1: 587 | resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} 588 | engines: {node: '>=8'} 589 | 590 | object-assign@4.1.1: 591 | resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} 592 | engines: {node: '>=0.10.0'} 593 | 594 | once@1.4.0: 595 | resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} 596 | 597 | onetime@5.1.2: 598 | resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} 599 | engines: {node: '>=6'} 600 | 601 | path-is-absolute@1.0.1: 602 | resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} 603 | engines: {node: '>=0.10.0'} 604 | 605 | path-key@3.1.1: 606 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 607 | engines: {node: '>=8'} 608 | 609 | path-type@4.0.0: 610 | resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} 611 | engines: {node: '>=8'} 612 | 613 | picocolors@1.0.0: 614 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} 615 | 616 | picomatch@2.3.1: 617 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 618 | engines: {node: '>=8.6'} 619 | 620 | pirates@4.0.5: 621 | resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==} 622 | engines: {node: '>= 6'} 623 | 624 | postcss-load-config@3.1.4: 625 | resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} 626 | engines: {node: '>= 10'} 627 | peerDependencies: 628 | postcss: '>=8.0.9' 629 | ts-node: '>=9.0.0' 630 | peerDependenciesMeta: 631 | postcss: 632 | optional: true 633 | ts-node: 634 | optional: true 635 | 636 | postcss@8.4.14: 637 | resolution: {integrity: sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==} 638 | engines: {node: ^10 || ^12 || >=14} 639 | 640 | punycode@2.3.0: 641 | resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} 642 | engines: {node: '>=6'} 643 | 644 | queue-microtask@1.2.3: 645 | resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} 646 | 647 | react-dom@18.2.0: 648 | resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} 649 | peerDependencies: 650 | react: ^18.2.0 651 | 652 | react@18.2.0: 653 | resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} 654 | engines: {node: '>=0.10.0'} 655 | 656 | readdirp@3.6.0: 657 | resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} 658 | engines: {node: '>=8.10.0'} 659 | 660 | resolve-from@5.0.0: 661 | resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} 662 | engines: {node: '>=8'} 663 | 664 | reusify@1.0.4: 665 | resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} 666 | engines: {iojs: '>=1.0.0', node: '>=0.10.0'} 667 | 668 | rollup@3.21.0: 669 | resolution: {integrity: sha512-ANPhVcyeHvYdQMUyCbczy33nbLzI7RzrBje4uvNiTDJGIMtlKoOStmympwr9OtS1LZxiDmE2wvxHyVhoLtf1KQ==} 670 | engines: {node: '>=14.18.0', npm: '>=8.0.0'} 671 | hasBin: true 672 | 673 | run-parallel@1.2.0: 674 | resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} 675 | 676 | scheduler@0.23.0: 677 | resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} 678 | 679 | shebang-command@2.0.0: 680 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 681 | engines: {node: '>=8'} 682 | 683 | shebang-regex@3.0.0: 684 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 685 | engines: {node: '>=8'} 686 | 687 | signal-exit@3.0.7: 688 | resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} 689 | 690 | slash@3.0.0: 691 | resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} 692 | engines: {node: '>=8'} 693 | 694 | source-map-js@1.0.2: 695 | resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} 696 | engines: {node: '>=0.10.0'} 697 | 698 | source-map@0.8.0-beta.0: 699 | resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} 700 | engines: {node: '>= 8'} 701 | 702 | streamsearch@1.1.0: 703 | resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} 704 | engines: {node: '>=10.0.0'} 705 | 706 | strip-final-newline@2.0.0: 707 | resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} 708 | engines: {node: '>=6'} 709 | 710 | styled-jsx@5.1.1: 711 | resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} 712 | engines: {node: '>= 12.0.0'} 713 | peerDependencies: 714 | '@babel/core': '*' 715 | babel-plugin-macros: '*' 716 | react: '>= 16.8.0 || 17.x.x || ^18.0.0-0' 717 | peerDependenciesMeta: 718 | '@babel/core': 719 | optional: true 720 | babel-plugin-macros: 721 | optional: true 722 | 723 | sucrase@3.32.0: 724 | resolution: {integrity: sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==} 725 | engines: {node: '>=8'} 726 | hasBin: true 727 | 728 | thenify-all@1.6.0: 729 | resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} 730 | engines: {node: '>=0.8'} 731 | 732 | thenify@3.3.1: 733 | resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} 734 | 735 | to-regex-range@5.0.1: 736 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 737 | engines: {node: '>=8.0'} 738 | 739 | toggle-selection@1.0.6: 740 | resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} 741 | 742 | tr46@1.0.1: 743 | resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} 744 | 745 | tree-kill@1.2.2: 746 | resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} 747 | hasBin: true 748 | 749 | ts-interface-checker@0.1.13: 750 | resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} 751 | 752 | tslib@2.5.0: 753 | resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} 754 | 755 | tsup@6.7.0: 756 | resolution: {integrity: sha512-L3o8hGkaHnu5TdJns+mCqFsDBo83bJ44rlK7e6VdanIvpea4ArPcU3swWGsLVbXak1PqQx/V+SSmFPujBK+zEQ==} 757 | engines: {node: '>=14.18'} 758 | hasBin: true 759 | peerDependencies: 760 | '@swc/core': ^1 761 | postcss: ^8.4.12 762 | typescript: '>=4.1.0' 763 | peerDependenciesMeta: 764 | '@swc/core': 765 | optional: true 766 | postcss: 767 | optional: true 768 | typescript: 769 | optional: true 770 | 771 | turbo-darwin-64@1.9.3: 772 | resolution: {integrity: sha512-0dFc2cWXl82kRE4Z+QqPHhbEFEpUZho1msHXHWbz5+PqLxn8FY0lEVOHkq5tgKNNEd5KnGyj33gC/bHhpZOk5g==} 773 | cpu: [x64] 774 | os: [darwin] 775 | 776 | turbo-darwin-arm64@1.9.3: 777 | resolution: {integrity: sha512-1cYbjqLBA2zYE1nbf/qVnEkrHa4PkJJbLo7hnuMuGM0bPzh4+AnTNe98gELhqI1mkTWBu/XAEeF5u6dgz0jLNA==} 778 | cpu: [arm64] 779 | os: [darwin] 780 | 781 | turbo-linux-64@1.9.3: 782 | resolution: {integrity: sha512-UuBPFefawEwpuxh5pM9Jqq3q4C8M0vYxVYlB3qea/nHQ80pxYq7ZcaLGEpb10SGnr3oMUUs1zZvkXWDNKCJb8Q==} 783 | cpu: [x64] 784 | os: [linux] 785 | 786 | turbo-linux-arm64@1.9.3: 787 | resolution: {integrity: sha512-vUrNGa3hyDtRh9W0MkO+l1dzP8Co2gKnOVmlJQW0hdpOlWlIh22nHNGGlICg+xFa2f9j4PbQlWTsc22c019s8Q==} 788 | cpu: [arm64] 789 | os: [linux] 790 | 791 | turbo-windows-64@1.9.3: 792 | resolution: {integrity: sha512-0BZ7YaHs6r+K4ksqWus1GKK3W45DuDqlmfjm/yuUbTEVc8szmMCs12vugU2Zi5GdrdJSYfoKfEJ/PeegSLIQGQ==} 793 | cpu: [x64] 794 | os: [win32] 795 | 796 | turbo-windows-arm64@1.9.3: 797 | resolution: {integrity: sha512-QJUYLSsxdXOsR1TquiOmLdAgtYcQ/RuSRpScGvnZb1hY0oLc7JWU0llkYB81wVtWs469y8H9O0cxbKwCZGR4RQ==} 798 | cpu: [arm64] 799 | os: [win32] 800 | 801 | turbo@1.9.3: 802 | resolution: {integrity: sha512-ID7mxmaLUPKG/hVkp+h0VuucB1U99RPCJD9cEuSEOdIPoSIuomcIClEJtKamUsdPLhLCud+BvapBNnhgh58Nzw==} 803 | hasBin: true 804 | 805 | typescript@4.9.5: 806 | resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} 807 | engines: {node: '>=4.2.0'} 808 | hasBin: true 809 | 810 | webidl-conversions@4.0.2: 811 | resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} 812 | 813 | whatwg-url@7.1.0: 814 | resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} 815 | 816 | which@2.0.2: 817 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 818 | engines: {node: '>= 8'} 819 | hasBin: true 820 | 821 | wrappy@1.0.2: 822 | resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} 823 | 824 | yaml@1.10.2: 825 | resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} 826 | engines: {node: '>= 6'} 827 | 828 | snapshots: 829 | 830 | '@esbuild/android-arm64@0.17.18': 831 | optional: true 832 | 833 | '@esbuild/android-arm@0.17.18': 834 | optional: true 835 | 836 | '@esbuild/android-x64@0.17.18': 837 | optional: true 838 | 839 | '@esbuild/darwin-arm64@0.17.18': 840 | optional: true 841 | 842 | '@esbuild/darwin-x64@0.17.18': 843 | optional: true 844 | 845 | '@esbuild/freebsd-arm64@0.17.18': 846 | optional: true 847 | 848 | '@esbuild/freebsd-x64@0.17.18': 849 | optional: true 850 | 851 | '@esbuild/linux-arm64@0.17.18': 852 | optional: true 853 | 854 | '@esbuild/linux-arm@0.17.18': 855 | optional: true 856 | 857 | '@esbuild/linux-ia32@0.17.18': 858 | optional: true 859 | 860 | '@esbuild/linux-loong64@0.17.18': 861 | optional: true 862 | 863 | '@esbuild/linux-mips64el@0.17.18': 864 | optional: true 865 | 866 | '@esbuild/linux-ppc64@0.17.18': 867 | optional: true 868 | 869 | '@esbuild/linux-riscv64@0.17.18': 870 | optional: true 871 | 872 | '@esbuild/linux-s390x@0.17.18': 873 | optional: true 874 | 875 | '@esbuild/linux-x64@0.17.18': 876 | optional: true 877 | 878 | '@esbuild/netbsd-x64@0.17.18': 879 | optional: true 880 | 881 | '@esbuild/openbsd-x64@0.17.18': 882 | optional: true 883 | 884 | '@esbuild/sunos-x64@0.17.18': 885 | optional: true 886 | 887 | '@esbuild/win32-arm64@0.17.18': 888 | optional: true 889 | 890 | '@esbuild/win32-ia32@0.17.18': 891 | optional: true 892 | 893 | '@esbuild/win32-x64@0.17.18': 894 | optional: true 895 | 896 | '@jridgewell/gen-mapping@0.3.3': 897 | dependencies: 898 | '@jridgewell/set-array': 1.1.2 899 | '@jridgewell/sourcemap-codec': 1.4.15 900 | '@jridgewell/trace-mapping': 0.3.18 901 | 902 | '@jridgewell/resolve-uri@3.1.0': {} 903 | 904 | '@jridgewell/set-array@1.1.2': {} 905 | 906 | '@jridgewell/sourcemap-codec@1.4.14': {} 907 | 908 | '@jridgewell/sourcemap-codec@1.4.15': {} 909 | 910 | '@jridgewell/trace-mapping@0.3.18': 911 | dependencies: 912 | '@jridgewell/resolve-uri': 3.1.0 913 | '@jridgewell/sourcemap-codec': 1.4.14 914 | 915 | '@next/env@13.3.1': {} 916 | 917 | '@next/swc-darwin-arm64@13.3.1': 918 | optional: true 919 | 920 | '@next/swc-darwin-x64@13.3.1': 921 | optional: true 922 | 923 | '@next/swc-linux-arm64-gnu@13.3.1': 924 | optional: true 925 | 926 | '@next/swc-linux-arm64-musl@13.3.1': 927 | optional: true 928 | 929 | '@next/swc-linux-x64-gnu@13.3.1': 930 | optional: true 931 | 932 | '@next/swc-linux-x64-musl@13.3.1': 933 | optional: true 934 | 935 | '@next/swc-win32-arm64-msvc@13.3.1': 936 | optional: true 937 | 938 | '@next/swc-win32-ia32-msvc@13.3.1': 939 | optional: true 940 | 941 | '@next/swc-win32-x64-msvc@13.3.1': 942 | optional: true 943 | 944 | '@nodelib/fs.scandir@2.1.5': 945 | dependencies: 946 | '@nodelib/fs.stat': 2.0.5 947 | run-parallel: 1.2.0 948 | 949 | '@nodelib/fs.stat@2.0.5': {} 950 | 951 | '@nodelib/fs.walk@1.2.8': 952 | dependencies: 953 | '@nodelib/fs.scandir': 2.1.5 954 | fastq: 1.15.0 955 | 956 | '@react-spring/animated@9.7.2(react@18.2.0)': 957 | dependencies: 958 | '@react-spring/shared': 9.7.2(react@18.2.0) 959 | '@react-spring/types': 9.7.2 960 | react: 18.2.0 961 | 962 | '@react-spring/core@9.7.2(react@18.2.0)': 963 | dependencies: 964 | '@react-spring/animated': 9.7.2(react@18.2.0) 965 | '@react-spring/rafz': 9.7.2 966 | '@react-spring/shared': 9.7.2(react@18.2.0) 967 | '@react-spring/types': 9.7.2 968 | react: 18.2.0 969 | 970 | '@react-spring/rafz@9.7.2': {} 971 | 972 | '@react-spring/shared@9.7.2(react@18.2.0)': 973 | dependencies: 974 | '@react-spring/rafz': 9.7.2 975 | '@react-spring/types': 9.7.2 976 | react: 18.2.0 977 | 978 | '@react-spring/types@9.7.2': {} 979 | 980 | '@react-spring/web@9.7.2(react-dom@18.2.0)(react@18.2.0)': 981 | dependencies: 982 | '@react-spring/animated': 9.7.2(react@18.2.0) 983 | '@react-spring/core': 9.7.2(react@18.2.0) 984 | '@react-spring/shared': 9.7.2(react@18.2.0) 985 | '@react-spring/types': 9.7.2 986 | react: 18.2.0 987 | react-dom: 18.2.0(react@18.2.0) 988 | 989 | '@swc/helpers@0.5.0': 990 | dependencies: 991 | tslib: 2.5.0 992 | 993 | '@types/node@18.16.1': {} 994 | 995 | '@types/prop-types@15.7.5': {} 996 | 997 | '@types/react-dom@18.2.1': 998 | dependencies: 999 | '@types/react': 18.2.0 1000 | 1001 | '@types/react@18.2.0': 1002 | dependencies: 1003 | '@types/prop-types': 15.7.5 1004 | '@types/scheduler': 0.16.3 1005 | csstype: 3.1.2 1006 | 1007 | '@types/scheduler@0.16.3': {} 1008 | 1009 | '@vercel/analytics@1.0.0(react@18.2.0)': 1010 | dependencies: 1011 | react: 18.2.0 1012 | 1013 | any-promise@1.3.0: {} 1014 | 1015 | anymatch@3.1.3: 1016 | dependencies: 1017 | normalize-path: 3.0.0 1018 | picomatch: 2.3.1 1019 | 1020 | array-union@2.1.0: {} 1021 | 1022 | balanced-match@1.0.2: {} 1023 | 1024 | binary-extensions@2.2.0: {} 1025 | 1026 | brace-expansion@1.1.11: 1027 | dependencies: 1028 | balanced-match: 1.0.2 1029 | concat-map: 0.0.1 1030 | 1031 | braces@3.0.2: 1032 | dependencies: 1033 | fill-range: 7.0.1 1034 | 1035 | bundle-require@4.0.1(esbuild@0.17.18): 1036 | dependencies: 1037 | esbuild: 0.17.18 1038 | load-tsconfig: 0.2.5 1039 | 1040 | busboy@1.6.0: 1041 | dependencies: 1042 | streamsearch: 1.1.0 1043 | 1044 | cac@6.7.14: {} 1045 | 1046 | caniuse-lite@1.0.30001481: {} 1047 | 1048 | chokidar@3.5.3: 1049 | dependencies: 1050 | anymatch: 3.1.3 1051 | braces: 3.0.2 1052 | glob-parent: 5.1.2 1053 | is-binary-path: 2.1.0 1054 | is-glob: 4.0.3 1055 | normalize-path: 3.0.0 1056 | readdirp: 3.6.0 1057 | optionalDependencies: 1058 | fsevents: 2.3.2 1059 | 1060 | client-only@0.0.1: {} 1061 | 1062 | clsx@1.2.1: {} 1063 | 1064 | commander@4.1.1: {} 1065 | 1066 | concat-map@0.0.1: {} 1067 | 1068 | copy-to-clipboard@3.3.3: 1069 | dependencies: 1070 | toggle-selection: 1.0.6 1071 | 1072 | cross-spawn@7.0.3: 1073 | dependencies: 1074 | path-key: 3.1.1 1075 | shebang-command: 2.0.0 1076 | which: 2.0.2 1077 | 1078 | csstype@3.1.2: {} 1079 | 1080 | debug@4.3.4: 1081 | dependencies: 1082 | ms: 2.1.2 1083 | 1084 | dir-glob@3.0.1: 1085 | dependencies: 1086 | path-type: 4.0.0 1087 | 1088 | esbuild@0.17.18: 1089 | optionalDependencies: 1090 | '@esbuild/android-arm': 0.17.18 1091 | '@esbuild/android-arm64': 0.17.18 1092 | '@esbuild/android-x64': 0.17.18 1093 | '@esbuild/darwin-arm64': 0.17.18 1094 | '@esbuild/darwin-x64': 0.17.18 1095 | '@esbuild/freebsd-arm64': 0.17.18 1096 | '@esbuild/freebsd-x64': 0.17.18 1097 | '@esbuild/linux-arm': 0.17.18 1098 | '@esbuild/linux-arm64': 0.17.18 1099 | '@esbuild/linux-ia32': 0.17.18 1100 | '@esbuild/linux-loong64': 0.17.18 1101 | '@esbuild/linux-mips64el': 0.17.18 1102 | '@esbuild/linux-ppc64': 0.17.18 1103 | '@esbuild/linux-riscv64': 0.17.18 1104 | '@esbuild/linux-s390x': 0.17.18 1105 | '@esbuild/linux-x64': 0.17.18 1106 | '@esbuild/netbsd-x64': 0.17.18 1107 | '@esbuild/openbsd-x64': 0.17.18 1108 | '@esbuild/sunos-x64': 0.17.18 1109 | '@esbuild/win32-arm64': 0.17.18 1110 | '@esbuild/win32-ia32': 0.17.18 1111 | '@esbuild/win32-x64': 0.17.18 1112 | 1113 | execa@5.1.1: 1114 | dependencies: 1115 | cross-spawn: 7.0.3 1116 | get-stream: 6.0.1 1117 | human-signals: 2.1.0 1118 | is-stream: 2.0.1 1119 | merge-stream: 2.0.0 1120 | npm-run-path: 4.0.1 1121 | onetime: 5.1.2 1122 | signal-exit: 3.0.7 1123 | strip-final-newline: 2.0.0 1124 | 1125 | fast-glob@3.2.12: 1126 | dependencies: 1127 | '@nodelib/fs.stat': 2.0.5 1128 | '@nodelib/fs.walk': 1.2.8 1129 | glob-parent: 5.1.2 1130 | merge2: 1.4.1 1131 | micromatch: 4.0.5 1132 | 1133 | fastq@1.15.0: 1134 | dependencies: 1135 | reusify: 1.0.4 1136 | 1137 | fill-range@7.0.1: 1138 | dependencies: 1139 | to-regex-range: 5.0.1 1140 | 1141 | fs.realpath@1.0.0: {} 1142 | 1143 | fsevents@2.3.2: 1144 | optional: true 1145 | 1146 | get-stream@6.0.1: {} 1147 | 1148 | glob-parent@5.1.2: 1149 | dependencies: 1150 | is-glob: 4.0.3 1151 | 1152 | glob@7.1.6: 1153 | dependencies: 1154 | fs.realpath: 1.0.0 1155 | inflight: 1.0.6 1156 | inherits: 2.0.4 1157 | minimatch: 3.1.2 1158 | once: 1.4.0 1159 | path-is-absolute: 1.0.1 1160 | 1161 | globby@11.1.0: 1162 | dependencies: 1163 | array-union: 2.1.0 1164 | dir-glob: 3.0.1 1165 | fast-glob: 3.2.12 1166 | ignore: 5.2.4 1167 | merge2: 1.4.1 1168 | slash: 3.0.0 1169 | 1170 | human-signals@2.1.0: {} 1171 | 1172 | ignore@5.2.4: {} 1173 | 1174 | inflight@1.0.6: 1175 | dependencies: 1176 | once: 1.4.0 1177 | wrappy: 1.0.2 1178 | 1179 | inherits@2.0.4: {} 1180 | 1181 | is-binary-path@2.1.0: 1182 | dependencies: 1183 | binary-extensions: 2.2.0 1184 | 1185 | is-extglob@2.1.1: {} 1186 | 1187 | is-glob@4.0.3: 1188 | dependencies: 1189 | is-extglob: 2.1.1 1190 | 1191 | is-number@7.0.0: {} 1192 | 1193 | is-stream@2.0.1: {} 1194 | 1195 | isexe@2.0.0: {} 1196 | 1197 | joycon@3.1.1: {} 1198 | 1199 | js-tokens@4.0.0: {} 1200 | 1201 | lilconfig@2.1.0: {} 1202 | 1203 | lines-and-columns@1.2.4: {} 1204 | 1205 | load-tsconfig@0.2.5: {} 1206 | 1207 | lodash.sortby@4.7.0: {} 1208 | 1209 | loose-envify@1.4.0: 1210 | dependencies: 1211 | js-tokens: 4.0.0 1212 | 1213 | merge-stream@2.0.0: {} 1214 | 1215 | merge2@1.4.1: {} 1216 | 1217 | micromatch@4.0.5: 1218 | dependencies: 1219 | braces: 3.0.2 1220 | picomatch: 2.3.1 1221 | 1222 | mimic-fn@2.1.0: {} 1223 | 1224 | minimatch@3.1.2: 1225 | dependencies: 1226 | brace-expansion: 1.1.11 1227 | 1228 | ms@2.1.2: {} 1229 | 1230 | mz@2.7.0: 1231 | dependencies: 1232 | any-promise: 1.3.0 1233 | object-assign: 4.1.1 1234 | thenify-all: 1.6.0 1235 | 1236 | nanoid@3.3.6: {} 1237 | 1238 | next@13.3.1(react-dom@18.2.0)(react@18.2.0): 1239 | dependencies: 1240 | '@next/env': 13.3.1 1241 | '@swc/helpers': 0.5.0 1242 | busboy: 1.6.0 1243 | caniuse-lite: 1.0.30001481 1244 | postcss: 8.4.14 1245 | react: 18.2.0 1246 | react-dom: 18.2.0(react@18.2.0) 1247 | styled-jsx: 5.1.1(react@18.2.0) 1248 | optionalDependencies: 1249 | '@next/swc-darwin-arm64': 13.3.1 1250 | '@next/swc-darwin-x64': 13.3.1 1251 | '@next/swc-linux-arm64-gnu': 13.3.1 1252 | '@next/swc-linux-arm64-musl': 13.3.1 1253 | '@next/swc-linux-x64-gnu': 13.3.1 1254 | '@next/swc-linux-x64-musl': 13.3.1 1255 | '@next/swc-win32-arm64-msvc': 13.3.1 1256 | '@next/swc-win32-ia32-msvc': 13.3.1 1257 | '@next/swc-win32-x64-msvc': 13.3.1 1258 | transitivePeerDependencies: 1259 | - '@babel/core' 1260 | - babel-plugin-macros 1261 | 1262 | normalize-path@3.0.0: {} 1263 | 1264 | npm-run-path@4.0.1: 1265 | dependencies: 1266 | path-key: 3.1.1 1267 | 1268 | object-assign@4.1.1: {} 1269 | 1270 | once@1.4.0: 1271 | dependencies: 1272 | wrappy: 1.0.2 1273 | 1274 | onetime@5.1.2: 1275 | dependencies: 1276 | mimic-fn: 2.1.0 1277 | 1278 | path-is-absolute@1.0.1: {} 1279 | 1280 | path-key@3.1.1: {} 1281 | 1282 | path-type@4.0.0: {} 1283 | 1284 | picocolors@1.0.0: {} 1285 | 1286 | picomatch@2.3.1: {} 1287 | 1288 | pirates@4.0.5: {} 1289 | 1290 | postcss-load-config@3.1.4: 1291 | dependencies: 1292 | lilconfig: 2.1.0 1293 | yaml: 1.10.2 1294 | 1295 | postcss@8.4.14: 1296 | dependencies: 1297 | nanoid: 3.3.6 1298 | picocolors: 1.0.0 1299 | source-map-js: 1.0.2 1300 | 1301 | punycode@2.3.0: {} 1302 | 1303 | queue-microtask@1.2.3: {} 1304 | 1305 | react-dom@18.2.0(react@18.2.0): 1306 | dependencies: 1307 | loose-envify: 1.4.0 1308 | react: 18.2.0 1309 | scheduler: 0.23.0 1310 | 1311 | react@18.2.0: 1312 | dependencies: 1313 | loose-envify: 1.4.0 1314 | 1315 | readdirp@3.6.0: 1316 | dependencies: 1317 | picomatch: 2.3.1 1318 | 1319 | resolve-from@5.0.0: {} 1320 | 1321 | reusify@1.0.4: {} 1322 | 1323 | rollup@3.21.0: 1324 | optionalDependencies: 1325 | fsevents: 2.3.2 1326 | 1327 | run-parallel@1.2.0: 1328 | dependencies: 1329 | queue-microtask: 1.2.3 1330 | 1331 | scheduler@0.23.0: 1332 | dependencies: 1333 | loose-envify: 1.4.0 1334 | 1335 | shebang-command@2.0.0: 1336 | dependencies: 1337 | shebang-regex: 3.0.0 1338 | 1339 | shebang-regex@3.0.0: {} 1340 | 1341 | signal-exit@3.0.7: {} 1342 | 1343 | slash@3.0.0: {} 1344 | 1345 | source-map-js@1.0.2: {} 1346 | 1347 | source-map@0.8.0-beta.0: 1348 | dependencies: 1349 | whatwg-url: 7.1.0 1350 | 1351 | streamsearch@1.1.0: {} 1352 | 1353 | strip-final-newline@2.0.0: {} 1354 | 1355 | styled-jsx@5.1.1(react@18.2.0): 1356 | dependencies: 1357 | client-only: 0.0.1 1358 | react: 18.2.0 1359 | 1360 | sucrase@3.32.0: 1361 | dependencies: 1362 | '@jridgewell/gen-mapping': 0.3.3 1363 | commander: 4.1.1 1364 | glob: 7.1.6 1365 | lines-and-columns: 1.2.4 1366 | mz: 2.7.0 1367 | pirates: 4.0.5 1368 | ts-interface-checker: 0.1.13 1369 | 1370 | thenify-all@1.6.0: 1371 | dependencies: 1372 | thenify: 3.3.1 1373 | 1374 | thenify@3.3.1: 1375 | dependencies: 1376 | any-promise: 1.3.0 1377 | 1378 | to-regex-range@5.0.1: 1379 | dependencies: 1380 | is-number: 7.0.0 1381 | 1382 | toggle-selection@1.0.6: {} 1383 | 1384 | tr46@1.0.1: 1385 | dependencies: 1386 | punycode: 2.3.0 1387 | 1388 | tree-kill@1.2.2: {} 1389 | 1390 | ts-interface-checker@0.1.13: {} 1391 | 1392 | tslib@2.5.0: {} 1393 | 1394 | tsup@6.7.0(typescript@4.9.5): 1395 | dependencies: 1396 | bundle-require: 4.0.1(esbuild@0.17.18) 1397 | cac: 6.7.14 1398 | chokidar: 3.5.3 1399 | debug: 4.3.4 1400 | esbuild: 0.17.18 1401 | execa: 5.1.1 1402 | globby: 11.1.0 1403 | joycon: 3.1.1 1404 | postcss-load-config: 3.1.4 1405 | resolve-from: 5.0.0 1406 | rollup: 3.21.0 1407 | source-map: 0.8.0-beta.0 1408 | sucrase: 3.32.0 1409 | tree-kill: 1.2.2 1410 | typescript: 4.9.5 1411 | transitivePeerDependencies: 1412 | - supports-color 1413 | - ts-node 1414 | 1415 | turbo-darwin-64@1.9.3: 1416 | optional: true 1417 | 1418 | turbo-darwin-arm64@1.9.3: 1419 | optional: true 1420 | 1421 | turbo-linux-64@1.9.3: 1422 | optional: true 1423 | 1424 | turbo-linux-arm64@1.9.3: 1425 | optional: true 1426 | 1427 | turbo-windows-64@1.9.3: 1428 | optional: true 1429 | 1430 | turbo-windows-arm64@1.9.3: 1431 | optional: true 1432 | 1433 | turbo@1.9.3: 1434 | optionalDependencies: 1435 | turbo-darwin-64: 1.9.3 1436 | turbo-darwin-arm64: 1.9.3 1437 | turbo-linux-64: 1.9.3 1438 | turbo-linux-arm64: 1.9.3 1439 | turbo-windows-64: 1.9.3 1440 | turbo-windows-arm64: 1.9.3 1441 | 1442 | typescript@4.9.5: {} 1443 | 1444 | webidl-conversions@4.0.2: {} 1445 | 1446 | whatwg-url@7.1.0: 1447 | dependencies: 1448 | lodash.sortby: 4.7.0 1449 | tr46: 1.0.1 1450 | webidl-conversions: 4.0.2 1451 | 1452 | which@2.0.2: 1453 | dependencies: 1454 | isexe: 2.0.0 1455 | 1456 | wrappy@1.0.2: {} 1457 | 1458 | yaml@1.10.2: {} 1459 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'website' 3 | - '.' 4 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import React from 'react' 4 | import { useId, IS_SERVER, useIsomorphicLayoutEffect } from './utils' 5 | 6 | const SYMBOL_KEY = '__wrap_b' 7 | const SYMBOL_NATIVE_KEY = '__wrap_n' 8 | const SYMBOL_OBSERVER_KEY = '__wrap_o' 9 | 10 | interface WrapperElement extends HTMLElement { 11 | [SYMBOL_OBSERVER_KEY]?: ResizeObserver | undefined 12 | } 13 | 14 | type RelayoutFn = ( 15 | id: string | number, 16 | ratio: number, 17 | wrapper?: WrapperElement 18 | ) => void 19 | 20 | declare global { 21 | interface Window { 22 | [SYMBOL_KEY]: RelayoutFn 23 | // A flag to indicate whether the browser supports text-balancing natively. 24 | // undefined: not injected 25 | // 1: injected and supported 26 | // 2: injected but not supported 27 | [SYMBOL_NATIVE_KEY]?: number 28 | } 29 | } 30 | 31 | const relayout: RelayoutFn = (id, ratio, wrapper) => { 32 | wrapper = 33 | wrapper || document.querySelector(`[data-br="${id}"]`) 34 | const container = wrapper?.parentElement 35 | 36 | if (!container) { return; } 37 | 38 | const update = (width: number) => (wrapper.style.maxWidth = width + 'px') 39 | 40 | // Reset wrapper width 41 | wrapper.style.maxWidth = '' 42 | 43 | // Get the initial container size 44 | const width = container.clientWidth 45 | const height = container.clientHeight 46 | 47 | // Synchronously do binary search and calculate the layout 48 | let lower: number = width / 2 - 0.25 49 | let upper: number = width + 0.5 50 | let middle: number 51 | 52 | if (width) { 53 | // Ensure we don't search widths lower than when the text overflows 54 | update(lower) 55 | lower = Math.max(wrapper.scrollWidth, lower) 56 | 57 | while (lower + 1 < upper) { 58 | middle = Math.round((lower + upper) / 2) 59 | update(middle) 60 | if (container.clientHeight === height) { 61 | upper = middle 62 | } else { 63 | lower = middle 64 | } 65 | } 66 | 67 | // Update the wrapper width 68 | update(upper * ratio + width * (1 - ratio)) 69 | } 70 | 71 | // Create a new observer if we don't have one. 72 | // Note that we must inline the key here as we use `toString()` to serialize 73 | // the function. 74 | if (!wrapper['__wrap_o']) { 75 | if (typeof ResizeObserver !== 'undefined') { 76 | ;(wrapper['__wrap_o'] = new ResizeObserver(() => { 77 | self.__wrap_b(0, +wrapper.dataset.brr, wrapper) 78 | })).observe(container) 79 | } else { 80 | // Silently ignore ResizeObserver for production builds 81 | if (process.env.NODE_ENV === 'development') { 82 | console.warn( 83 | 'The browser you are using does not support the ResizeObserver API. ' + 84 | 'Please consider add polyfill for this API to avoid potential layout shifts or upgrade your browser. ' + 85 | 'Read more: https://github.com/shuding/react-wrap-balancer#browser-support-information' 86 | ) 87 | } 88 | } 89 | } 90 | } 91 | 92 | const RELAYOUT_STR = relayout.toString() 93 | 94 | const isTextWrapBalanceSupported = `(self.CSS&&CSS.supports("text-wrap","balance")?1:2)` 95 | 96 | const createScriptElement = ( 97 | injected: boolean, 98 | nonce?: string, 99 | suffix: string = '' 100 | ) => { 101 | if (suffix) { 102 | suffix = `self.${SYMBOL_NATIVE_KEY}!=1&&${suffix}` 103 | } 104 | return ( 105 | ` 43 | 44 | withoutBalancer += `

${title}

` 45 | } 46 | 47 | withBalancer += close 48 | withoutBalancer += close 49 | 50 | await fs.writeFile('ssr-balancer.html', withBalancer) 51 | await fs.writeFile('ssr.html', withoutBalancer) 52 | } 53 | 54 | generateSSRTest() 55 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "jsx": "react", 4 | "moduleResolution": "node", 5 | "esModuleInterop": true, 6 | "lib": ["es2015", "dom"] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup' 2 | 3 | export default defineConfig({ 4 | minify: true, 5 | target: 'es2018', 6 | external: ['react'], 7 | sourcemap: true, 8 | dts: true, 9 | format: ['esm', 'cjs'], 10 | esbuildOptions(options) { 11 | options.banner = { 12 | js: '"use client"', 13 | } 14 | }, 15 | }) 16 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turbo.build/schema.json", 3 | "extends": ["//"], 4 | "pipeline": { 5 | "build": { 6 | "dependsOn": ["^build"], 7 | "outputs": [".next/**", "dist"] 8 | }, 9 | "dev": { 10 | "cache": false 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /website/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from 'next' 2 | import { Analytics } from '@vercel/analytics/react' 3 | import { Inter, Alice } from 'next/font/google' 4 | import React from 'react' 5 | import clsx from 'clsx' 6 | import './style.css' 7 | 8 | const inter = Inter({ 9 | weight: 'variable', 10 | subsets: ['latin'], 11 | display: 'block', 12 | }) 13 | 14 | const alice = Alice({ 15 | weight: '400', 16 | subsets: ['latin'], 17 | variable: '--font-alice', 18 | display: 'block', 19 | }) 20 | 21 | const title = 'React Wrap Balancer' 22 | const description = 'Simple React Component That Makes Titles More Readable' 23 | const url = 'https://react-wrap-balancer.vercel.app/' 24 | const ogImage = url + 'og.png' 25 | 26 | const iconSizes = ['32x32', '96x96', '16x16'] 27 | const andriodIconSizes = [ 28 | '36x36', 29 | '48x48', 30 | '72x72', 31 | '96x96', 32 | '144x144', 33 | '192x192', 34 | ] 35 | const appleIconSizes = [ 36 | '57x57', 37 | '60x60', 38 | '72x72', 39 | '76x76', 40 | '114x114', 41 | '120x120', 42 | '144x144', 43 | '152x152', 44 | '180x180', 45 | ] 46 | 47 | export const metadata: Metadata = { 48 | title, 49 | description, 50 | keywords: 'react, wrapping, typography, balance, web', 51 | authors: { name: 'Shu Ding' }, 52 | viewport: { 53 | width: 'device-width', 54 | initialScale: 1, 55 | }, 56 | openGraph: { 57 | title, 58 | description, 59 | siteName: title, 60 | url, 61 | images: [ogImage], 62 | }, 63 | twitter: { 64 | card: 'summary_large_image', 65 | site: '@shuding_', 66 | title, 67 | description, 68 | images: [ogImage], 69 | }, 70 | icons: { 71 | shortcut: [ 72 | { url: '/favicon.ico', media: '(prefers-color-scheme: light)' }, 73 | { url: '/dark-favicon.ico', media: '(prefers-color-scheme: dark)' }, 74 | ], 75 | icon: [ 76 | ...andriodIconSizes.map((sizes) => ({ 77 | type: 'image/png', 78 | sizes, 79 | url: `/android-icon-${sizes}.png`, 80 | media: '(prefers-color-scheme: light)', 81 | })), 82 | ...iconSizes.map((sizes) => ({ 83 | type: 'image/png', 84 | sizes, 85 | url: `/favicon-${sizes}.png`, 86 | media: '(prefers-color-scheme: light)', 87 | })), 88 | ...andriodIconSizes.map((sizes) => ({ 89 | type: 'image/png', 90 | sizes, 91 | url: `/dark-android-icon-${sizes}.png`, 92 | media: '(prefers-color-scheme: dark)', 93 | })), 94 | ...iconSizes.map((sizes) => ({ 95 | type: 'image/png', 96 | sizes, 97 | url: `/dark-favicon-${sizes}.png`, 98 | media: '(prefers-color-scheme: dark)', 99 | })), 100 | ], 101 | apple: [ 102 | ...appleIconSizes.map((sizes) => ({ 103 | type: 'image/png', 104 | sizes, 105 | url: `/apple-icon-${sizes}.png`, 106 | media: '(prefers-color-scheme: light)', 107 | })), 108 | ...appleIconSizes.map((sizes) => ({ 109 | type: 'image/png', 110 | sizes, 111 | url: `/dark-apple-icon-${sizes}.png`, 112 | media: '(prefers-color-scheme: dark)', 113 | })), 114 | ], 115 | }, 116 | } 117 | 118 | interface LayoutProps { 119 | children: React.ReactNode 120 | params?: Record 121 | } 122 | 123 | export default function RootLayout({ children }: LayoutProps): JSX.Element { 124 | const bodyClassName = clsx(inter.className, alice.variable) 125 | 126 | return ( 127 | 128 | 129 | 130 | {children} 131 | 132 | 133 | 134 | ) 135 | } 136 | -------------------------------------------------------------------------------- /website/app/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { Provider } from 'react-wrap-balancer' 4 | 5 | import Section from '../src/sections' 6 | 7 | export default function HomePage() { 8 | return ( 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |
22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /website/app/style.css: -------------------------------------------------------------------------------- 1 | *, 2 | *::before, 3 | *::after { 4 | box-sizing: border-box; 5 | } 6 | 7 | html, 8 | body { 9 | height: 100%; 10 | } 11 | 12 | p, 13 | h1, 14 | h2 { 15 | word-break: break-word; 16 | } 17 | 18 | body { 19 | margin: 0; 20 | -webkit-font-smoothing: subpixel-antialiased; 21 | color: #222; 22 | background-color: #f5f5f5; 23 | font-feature-settings: 'ss04'; 24 | line-height: 1.5; 25 | --w0: 320px; 26 | --w1: min(27.5vw, 480px); 27 | } 28 | 29 | .logo-container { 30 | display: flex; 31 | justify-content: center; 32 | margin-top: 40px; 33 | } 34 | 35 | .logo { 36 | display: inline-block; 37 | border: 5px solid currentColor; 38 | padding: 0 6px 0 4px; 39 | font-weight: 700; 40 | font-size: 30px; 41 | letter-spacing: -0.021em; 42 | line-height: 34px; 43 | margin: 0; 44 | user-select: none; 45 | border-radius: 7px; 46 | align-self: center; 47 | text-decoration: none; 48 | mask-image: linear-gradient( 49 | 45deg, 50 | black 25%, 51 | rgba(0, 0, 0, 0.2) 50%, 52 | black 75% 53 | ); 54 | mask-size: 800%; 55 | mask-position: 0%; 56 | } 57 | 58 | .headline { 59 | text-align: center; 60 | font-family: var(--font-alice); 61 | margin: 80px 0 50px; 62 | font-size: 26px; 63 | letter-spacing: -0.04rem; 64 | line-height: 1.45; 65 | } 66 | 67 | .github-link { 68 | display: inline-flex; 69 | align-items: center; 70 | margin: 0.8em 0; 71 | font-size: 14px; 72 | gap: 0.3em; 73 | user-select: none; 74 | } 75 | .github-link span { 76 | display: inline-flex; 77 | align-items: center; 78 | gap: 0.2em; 79 | white-space: nowrap; 80 | } 81 | 82 | .installation { 83 | cursor: copy; 84 | transition: all 0.2s ease; 85 | } 86 | 87 | .installation:hover { 88 | background-color: #ffffffaa; 89 | } 90 | .installation:active .copy { 91 | opacity: 0.2; 92 | } 93 | 94 | .copy { 95 | position: absolute; 96 | right: 8.6px; 97 | width: 2em; 98 | height: 2em; 99 | border-radius: 1em; 100 | background-color: #f5f5f5; 101 | display: flex; 102 | align-items: center; 103 | justify-content: center; 104 | top: 0; 105 | bottom: 0; 106 | margin: auto; 107 | transition: all 0.1s ease; 108 | } 109 | 110 | .copy svg { 111 | animation: fadein 0.5s ease forwards; 112 | } 113 | 114 | @keyframes fadein { 115 | 0% { 116 | opacity: 0; 117 | } 118 | 100% { 119 | opacity: 1; 120 | } 121 | } 122 | 123 | h3 { 124 | text-align: center; 125 | font-family: var(--font-alice); 126 | margin: 30px 0 80px; 127 | color: #888; 128 | font-weight: 400; 129 | } 130 | 131 | h3 > span::before { 132 | content: ''; 133 | width: 0; 134 | height: 0; 135 | border-left: 0.35em solid transparent; 136 | border-right: 0.35em solid transparent; 137 | border-bottom: 0.6em solid currentColor; 138 | display: inline-block; 139 | margin-right: 0.4em; 140 | } 141 | 142 | h3 + .headline { 143 | margin-top: 10px; 144 | } 145 | 146 | a { 147 | color: inherit; 148 | text-decoration: underline; 149 | text-decoration-thickness: from-font; 150 | text-underline-position: from-font; 151 | transition: opacity 0.2s ease; 152 | } 153 | 154 | a:hover { 155 | opacity: 0.6; 156 | } 157 | a.logo:hover { 158 | transition: mask-position 2s ease, -webkit-mask-position 2s ease; 159 | mask-position: 120%; 160 | opacity: 1; 161 | } 162 | 163 | main { 164 | display: flex; 165 | flex-direction: column; 166 | align-items: center; 167 | padding: 40px 20px; 168 | margin: 0 auto; 169 | max-width: 1800px; 170 | } 171 | 172 | main > * { 173 | width: 100%; 174 | max-width: 600px; 175 | } 176 | 177 | label { 178 | font-weight: 500; 179 | text-transform: uppercase; 180 | font-size: 12px; 181 | display: block; 182 | letter-spacing: 0.04rem; 183 | margin: 1.5em 0 8px; 184 | color: #888; 185 | user-select: none; 186 | } 187 | 188 | code, 189 | .code { 190 | font-family: 'SF Mono', SFMono-Regular, ui-monospace, Menlo, Consolas, 191 | monospace; 192 | } 193 | code { 194 | position: relative; 195 | background: #fff; 196 | display: block; 197 | border-radius: 6px; 198 | padding: 10px 12px; 199 | font-size: 14px; 200 | line-height: 1.75; 201 | margin: 8px 0 1.5em; 202 | white-space: pre-wrap; 203 | letter-spacing: 0; 204 | } 205 | code:last-child { 206 | margin-bottom: 0; 207 | } 208 | .code { 209 | font-size: 0.92em; 210 | letter-spacing: 0; 211 | } 212 | code .hl-fade { 213 | color: #888; 214 | } 215 | code .hl-highlighted { 216 | --color: rgba(0, 0, 0, 0.04); 217 | background: var(--color); 218 | box-shadow: 0 0 0 3px var(--color); 219 | border-radius: 2px; 220 | } 221 | 222 | p, 223 | .p { 224 | font-size: 15px; 225 | line-height: 1.8; 226 | margin: 0 0 1.5em; 227 | letter-spacing: -0.01em; 228 | } 229 | 230 | .controller { 231 | position: relative; 232 | align-self: center; 233 | display: flex; 234 | align-items: center; 235 | width: auto; 236 | height: 32px; 237 | padding: 5px 10px; 238 | font-size: 15px; 239 | background-color: #fff; 240 | border-top-left-radius: 10px; 241 | border-top-right-radius: 10px; 242 | user-select: none; 243 | } 244 | 245 | .controller::before { 246 | content: ''; 247 | display: block; 248 | width: 20px; 249 | height: 20px; 250 | position: absolute; 251 | background-color: transparent; 252 | border-bottom: 10px solid white; 253 | border-right: 10px solid white; 254 | border-bottom-right-radius: 20px; 255 | left: -10px; 256 | bottom: -10px; 257 | clip-path: inset(0 10px 10px 0); 258 | } 259 | 260 | .controller::after { 261 | content: ''; 262 | display: block; 263 | width: 20px; 264 | height: 20px; 265 | position: absolute; 266 | background-color: transparent; 267 | border-bottom: 10px solid white; 268 | border-left: 10px solid white; 269 | border-bottom-left-radius: 20px; 270 | right: -10px; 271 | bottom: -10px; 272 | clip-path: inset(0 0 10px 10px); 273 | } 274 | 275 | .controller input { 276 | appearance: none; 277 | background: #dedede; 278 | height: 3px; 279 | border-radius: 3px; 280 | } 281 | 282 | .demo-container { 283 | max-width: unset; 284 | display: flex; 285 | flex-direction: column; 286 | align-items: center; 287 | } 288 | 289 | .demo { 290 | display: flex; 291 | gap: 20px; 292 | padding: 20px; 293 | background-color: #fff; 294 | border-radius: 10px; 295 | box-shadow: 0 20px 25px -5px rgb(0 0 0 / 8%), 0 3px 10px -6px rgb(0 0 0 / 10%); 296 | } 297 | 298 | .demo > div { 299 | flex: 1; 300 | } 301 | 302 | .demo .item { 303 | border: 1px dashed transparent; 304 | border-radius: 2px; 305 | transition: border-color 0.5s ease; 306 | } 307 | 308 | .demo legend { 309 | font-size: 13px; 310 | margin-bottom: 5px; 311 | padding: 0; 312 | text-align: left; 313 | color: #888; 314 | user-select: none; 315 | } 316 | 317 | h2 { 318 | margin: 10px 0 0; 319 | font-size: 22px; 320 | } 321 | 322 | .skeleton { 323 | display: inline-block; 324 | width: var(--w); 325 | height: 24px; 326 | margin-top: 24px; 327 | border-radius: 4px; 328 | background-color: #f5f5f5; 329 | } 330 | .skeleton + .skeleton { 331 | margin-top: 12px; 332 | } 333 | .skeleton:last-child { 334 | margin-bottom: 10px; 335 | } 336 | 337 | .tooltip-container { 338 | position: relative; 339 | } 340 | .TooltipContent { 341 | position: absolute; 342 | left: 50%; 343 | transform: translate3d(-50%, -10px, 0); 344 | margin: auto; 345 | bottom: 100%; 346 | border-radius: 9px; 347 | padding: 10px; 348 | line-height: 1; 349 | background-color: white; 350 | box-shadow: 0 1px 5px #0000000f, 0 5px 20px #00000017; 351 | font-size: 14px; 352 | user-select: none; 353 | } 354 | .TooltipArrow { 355 | fill: white; 356 | position: absolute; 357 | left: 0; 358 | right: 0; 359 | top: 100%; 360 | margin: auto; 361 | } 362 | 363 | .tooltip-trigger { 364 | margin-top: 120px; 365 | margin-bottom: 10px; 366 | padding: 4px; 367 | text-align: center; 368 | font-size: 14px; 369 | } 370 | .tooltip { 371 | line-height: 1.5; 372 | text-align: center; 373 | } 374 | 375 | .controller:active + .demo .item { 376 | border-color: #229dff; 377 | } 378 | 379 | blockquote::before { 380 | content: '“'; 381 | display: block; 382 | font-size: 80px; 383 | text-align: center; 384 | color: #ddd; 385 | margin-bottom: -40px; 386 | } 387 | 388 | blockquote { 389 | line-height: 1.65; 390 | display: flex; 391 | flex-direction: column; 392 | align-items: center; 393 | font-family: var(--font-alice); 394 | margin: 0 0 20px; 395 | font-size: 15px; 396 | } 397 | 398 | ul { 399 | margin: 0; 400 | padding-left: 18px; 401 | } 402 | 403 | h2.ratio-title { 404 | position: relative; 405 | margin-bottom: 1em; 406 | background: #fff2e0; 407 | border-right: 1px dashed #c59759; 408 | border-left: 1px dashed #c59759; 409 | } 410 | h2.ratio-title span { 411 | background: #cdebff; 412 | } 413 | h2.ratio-title:after, 414 | h2.ratio-ruler span:after { 415 | content: '0'; 416 | position: absolute; 417 | top: 100%; 418 | left: 0; 419 | font-size: 11px; 420 | transform: translateX(-50%); 421 | margin-top: 2px; 422 | margin-left: -1px; 423 | font-weight: 400; 424 | } 425 | h2.ratio-ruler { 426 | margin-top: 0; 427 | position: absolute; 428 | width: 100%; 429 | user-select: none; 430 | pointer-events: none; 431 | z-index: 1; 432 | } 433 | h2.ratio-ruler > span { 434 | position: relative; 435 | border-right: 1px dashed #2d76a7; 436 | border-left: 1px dashed #2d76a7; 437 | } 438 | h2.ratio-ruler span span { 439 | color: transparent; 440 | } 441 | h2.ratio-ruler span:after { 442 | content: '1'; 443 | } 444 | 445 | .benchmark { 446 | display: flex; 447 | padding: 20px 16px; 448 | box-sizing: border-box; 449 | width: calc(100vw - 32px); 450 | max-width: 700px; 451 | background-color: white; 452 | border-radius: 6px; 453 | margin: 0 0 1.5em 0; 454 | } 455 | 456 | .benchmark img { 457 | width: 100%; 458 | } 459 | 460 | /* Mobile */ 461 | @media screen and (max-width: 640px) { 462 | .demo { 463 | flex-direction: column; 464 | } 465 | body { 466 | --w0: 200px; 467 | --w1: 80vw; 468 | } 469 | .demo-container { 470 | flex-direction: column-reverse; 471 | } 472 | .controller { 473 | position: sticky; 474 | z-index: 1; 475 | bottom: 25px; 476 | margin-top: 15px; 477 | border: 1px solid #e5e5e5; 478 | border-radius: 20px; 479 | box-shadow: 0 5px 10px rgb(0 0 0 / 4%); 480 | backdrop-filter: blur(10px); 481 | background: rgb(255 255 255 / 83%); 482 | } 483 | .controller::before, 484 | .controller::after { 485 | display: none; 486 | } 487 | blockquote { 488 | font-size: 15px; 489 | } 490 | h2 { 491 | font-size: 20px; 492 | } 493 | h3 { 494 | font-size: 16px; 495 | } 496 | .headline { 497 | font-size: 22px; 498 | } 499 | .TooltipContent { 500 | font-size: 10px; 501 | } 502 | h2.ratio-ruler, 503 | h2.ratio-title { 504 | font-size: 22px; 505 | } 506 | } 507 | 508 | @media screen and (max-width: 450px) { 509 | h2.ratio-ruler, 510 | h2.ratio-title { 511 | font-size: 18px; 512 | } 513 | } 514 | 515 | @media screen and (max-width: 390px) { 516 | h2.ratio-ruler, 517 | h2.ratio-title { 518 | font-size: 16px; 519 | } 520 | } 521 | -------------------------------------------------------------------------------- /website/css.d.ts: -------------------------------------------------------------------------------- 1 | import * as CSS from 'csstype' 2 | 3 | declare module 'csstype' { 4 | interface Properties { 5 | '--w'?: CSS.Property.Width 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /website/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /website/next.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | experimental: { 3 | appDir: true, 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "website", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "next dev", 8 | "build": "next build", 9 | "start": "next start" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "@react-spring/web": "^9.5.5", 16 | "@vercel/analytics": "^1.0.0", 17 | "clsx": "^1.2.1", 18 | "copy-to-clipboard": "^3.3.3", 19 | "next": "13.3.1", 20 | "react": "^18.2.0", 21 | "react-dom": "^18.2.0", 22 | "react-wrap-balancer": "workspace:*" 23 | }, 24 | "devDependencies": { 25 | "@types/react": "^18.2.0", 26 | "@types/react-dom": "^18.2.1", 27 | "csstype": "^3.1.2", 28 | "typescript": "^4.8.4" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /website/public/android-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/android-icon-144x144.png -------------------------------------------------------------------------------- /website/public/android-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/android-icon-192x192.png -------------------------------------------------------------------------------- /website/public/android-icon-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/android-icon-36x36.png -------------------------------------------------------------------------------- /website/public/android-icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/android-icon-48x48.png -------------------------------------------------------------------------------- /website/public/android-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/android-icon-72x72.png -------------------------------------------------------------------------------- /website/public/android-icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/android-icon-96x96.png -------------------------------------------------------------------------------- /website/public/apple-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/apple-icon-114x114.png -------------------------------------------------------------------------------- /website/public/apple-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/apple-icon-120x120.png -------------------------------------------------------------------------------- /website/public/apple-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/apple-icon-144x144.png -------------------------------------------------------------------------------- /website/public/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/apple-icon-152x152.png -------------------------------------------------------------------------------- /website/public/apple-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/apple-icon-180x180.png -------------------------------------------------------------------------------- /website/public/apple-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/apple-icon-57x57.png -------------------------------------------------------------------------------- /website/public/apple-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/apple-icon-60x60.png -------------------------------------------------------------------------------- /website/public/apple-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/apple-icon-72x72.png -------------------------------------------------------------------------------- /website/public/apple-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/apple-icon-76x76.png -------------------------------------------------------------------------------- /website/public/apple-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/apple-icon-precomposed.png -------------------------------------------------------------------------------- /website/public/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/apple-icon.png -------------------------------------------------------------------------------- /website/public/bench.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /website/public/dark-android-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/dark-android-icon-144x144.png -------------------------------------------------------------------------------- /website/public/dark-android-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/dark-android-icon-192x192.png -------------------------------------------------------------------------------- /website/public/dark-android-icon-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/dark-android-icon-36x36.png -------------------------------------------------------------------------------- /website/public/dark-android-icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/dark-android-icon-48x48.png -------------------------------------------------------------------------------- /website/public/dark-android-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/dark-android-icon-72x72.png -------------------------------------------------------------------------------- /website/public/dark-android-icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/dark-android-icon-96x96.png -------------------------------------------------------------------------------- /website/public/dark-apple-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/dark-apple-icon-114x114.png -------------------------------------------------------------------------------- /website/public/dark-apple-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/dark-apple-icon-120x120.png -------------------------------------------------------------------------------- /website/public/dark-apple-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/dark-apple-icon-144x144.png -------------------------------------------------------------------------------- /website/public/dark-apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/dark-apple-icon-152x152.png -------------------------------------------------------------------------------- /website/public/dark-apple-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/dark-apple-icon-180x180.png -------------------------------------------------------------------------------- /website/public/dark-apple-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/dark-apple-icon-57x57.png -------------------------------------------------------------------------------- /website/public/dark-apple-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/dark-apple-icon-60x60.png -------------------------------------------------------------------------------- /website/public/dark-apple-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/dark-apple-icon-72x72.png -------------------------------------------------------------------------------- /website/public/dark-apple-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/dark-apple-icon-76x76.png -------------------------------------------------------------------------------- /website/public/dark-apple-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/dark-apple-icon-precomposed.png -------------------------------------------------------------------------------- /website/public/dark-apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/dark-apple-icon.png -------------------------------------------------------------------------------- /website/public/dark-favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/dark-favicon-16x16.png -------------------------------------------------------------------------------- /website/public/dark-favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/dark-favicon-32x32.png -------------------------------------------------------------------------------- /website/public/dark-favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/dark-favicon-96x96.png -------------------------------------------------------------------------------- /website/public/dark-favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/dark-favicon.ico -------------------------------------------------------------------------------- /website/public/dark-ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/dark-ms-icon-144x144.png -------------------------------------------------------------------------------- /website/public/dark-ms-icon-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/dark-ms-icon-150x150.png -------------------------------------------------------------------------------- /website/public/dark-ms-icon-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/dark-ms-icon-310x310.png -------------------------------------------------------------------------------- /website/public/dark-ms-icon-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/dark-ms-icon-70x70.png -------------------------------------------------------------------------------- /website/public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/favicon-16x16.png -------------------------------------------------------------------------------- /website/public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/favicon-32x32.png -------------------------------------------------------------------------------- /website/public/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/favicon-96x96.png -------------------------------------------------------------------------------- /website/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/favicon.ico -------------------------------------------------------------------------------- /website/public/ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/ms-icon-144x144.png -------------------------------------------------------------------------------- /website/public/ms-icon-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/ms-icon-150x150.png -------------------------------------------------------------------------------- /website/public/ms-icon-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/ms-icon-310x310.png -------------------------------------------------------------------------------- /website/public/ms-icon-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/ms-icon-70x70.png -------------------------------------------------------------------------------- /website/public/og.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/website/public/og.png -------------------------------------------------------------------------------- /website/src/components/BlankLink.tsx: -------------------------------------------------------------------------------- 1 | export default function BlankLink(props: JSX.IntrinsicElements['a']) { 2 | return 3 | } 4 | -------------------------------------------------------------------------------- /website/src/components/Comparison.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { 4 | animated, 5 | Interpolation, 6 | SpringValue, 7 | useSpring, 8 | } from '@react-spring/web' 9 | 10 | // TODO: Better typing for "a" and "b" props 11 | interface ComparisonProps { 12 | a: any 13 | b: any 14 | align?: React.CSSProperties['textAlign'] 15 | } 16 | 17 | export default function Comparison({ a, b, align = 'left' }: ComparisonProps) { 18 | const [{ width }, api] = useSpring(() => ({ width: 0.55 })) 19 | 20 | const handleRange: React.ChangeEventHandler = (event) => { 21 | api.start({ width: +event.target.value / 100 }) 22 | } 23 | 24 | return ( 25 |
26 |
27 | 28 |
29 | 30 | 31 |
32 | ) 33 | } 34 | 35 | function ConditionalRender({ 36 | a, 37 | b, 38 | align, 39 | width, 40 | }: ComparisonProps & { 41 | width: SpringValue 42 | }) { 43 | if (typeof a !== typeof b) return null 44 | 45 | let interpolation: Interpolation 46 | 47 | if (typeof a === 'function') { 48 | interpolation = width.to( 49 | (v) => `calc(${v} * var(--w1) + ${150 * (1 - v) - 31 * v}px)` 50 | ) 51 | 52 | return ( 53 |
57 | {renderAB(a(interpolation), b(interpolation))} 58 |
59 | ) 60 | } 61 | 62 | interpolation = width.to((v) => `calc(${v * 100}% + ${1 - v} * var(--w0))`) 63 | 64 | return ( 65 | 69 | {renderAB(a, b)} 70 | 71 | ) 72 | } 73 | 74 | const renderAB = (a: JSX.Element, b: JSX.Element) => ( 75 | <> 76 |
77 | Default 78 | {a} 79 |
80 |
81 | With Balancer 82 | {b} 83 |
84 | 85 | ) 86 | -------------------------------------------------------------------------------- /website/src/components/Content.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const content = ( 4 | <> 5 |
6 |
7 |
8 | 9 | ) 10 | 11 | export type ContentProps = 12 | (T extends keyof JSX.IntrinsicElements ? JSX.IntrinsicElements[T] : never) & { 13 | as?: T 14 | children?: React.ReactNode 15 | } 16 | 17 | export default function Content({ 18 | as, 19 | children, 20 | ...props 21 | }: ContentProps) { 22 | if (as) { 23 | return React.createElement(as, props, children, content) 24 | } 25 | 26 | return ( 27 | 28 | {children} 29 | {content} 30 | 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /website/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as BlankLink } from './BlankLink' 2 | export { default as Comparison } from './Comparison' 3 | export { default as Content } from './Content' 4 | -------------------------------------------------------------------------------- /website/src/icons/Copy.tsx: -------------------------------------------------------------------------------- 1 | export default function Copy({ 2 | copying = false, 3 | ...svgProps 4 | }: React.SVGProps & { 5 | copying?: boolean 6 | }) { 7 | const props: React.SVGProps = { 8 | width: 15, 9 | height: 15, 10 | viewBox: '0 0 15 15', 11 | fill: 'none', 12 | xmlns: 'http://www.w3.org/2000/svg', 13 | ...svgProps, 14 | } 15 | 16 | if (copying) { 17 | return ( 18 | 19 | 25 | 26 | ) 27 | } 28 | 29 | return ( 30 | 31 | 39 | 40 | ) 41 | } 42 | -------------------------------------------------------------------------------- /website/src/icons/GitHub.tsx: -------------------------------------------------------------------------------- 1 | export default function GitHub(svgProps: React.SVGProps) { 2 | const props: React.SVGProps = { 3 | viewBox: '0 0 15 15', 4 | fill: 'none', 5 | xmlns: 'http://www.w3.org/2000/svg', 6 | ...svgProps, 7 | } 8 | 9 | return ( 10 | 11 | 17 | 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /website/src/icons/TooltipArrow.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx' 2 | 3 | export default function TooltipArrow(svgProps: React.SVGProps) { 4 | const props: React.SVGProps = { 5 | width: 10, 6 | height: 5, 7 | preserveAspectRatio: 'none', 8 | viewBox: '0 0 30 10', 9 | ...svgProps, 10 | className: clsx('TooltipArrow', svgProps.className), 11 | } 12 | 13 | return ( 14 | 15 | 16 | 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /website/src/icons/TooltipTrigger.tsx: -------------------------------------------------------------------------------- 1 | export default function TooltipTrigger( 2 | svgProps: React.SVGProps 3 | ) { 4 | const props: React.SVGProps = { 5 | width: 16, 6 | fill: 'currentColor', 7 | viewBox: '0 0 20 20', 8 | xmlns: 'http://www.w3.org/2000/svg', 9 | ...svgProps, 10 | } 11 | 12 | return ( 13 | 14 | 19 | 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /website/src/icons/Vercel.tsx: -------------------------------------------------------------------------------- 1 | export default function Vercel(props: React.SVGProps) { 2 | return ( 3 | 4 | 5 | 6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /website/src/icons/index.ts: -------------------------------------------------------------------------------- 1 | export { default as CopyIcon } from './Copy' 2 | export { default as GitHubIcon } from './GitHub' 3 | export { default as TooltipArrowIcon } from './TooltipArrow' 4 | export { default as TooltipTriggerIcon } from './TooltipTrigger' 5 | export { default as VercelIcon } from './Vercel' 6 | -------------------------------------------------------------------------------- /website/src/sections/About.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import Balancer from 'react-wrap-balancer' 4 | 5 | import BlankLink from '../components/BlankLink' 6 | 7 | export default function About() { 8 | return ( 9 | <> 10 |

11 | About React Wrap Balancer 12 |

13 |

21 | 22 | This project was inspired by Adobe’s{' '} 23 | 24 | balance-text 25 | {' '} 26 | project, NYT’s{' '} 27 | 28 | text-balancer 29 | {' '} 30 | project, and Daniel Aleksandersen’s{' '} 31 | 32 | Improving the New York Times’ line wrap balancer 33 | 34 | . If you want to learn more, you can also take a look at the{' '} 35 | 36 | 37 | text-wrap: balance 38 | 39 | {' '} 40 | proposal. 41 | 42 |

43 |

44 | 45 | Special thanks to{' '} 46 | 47 | Emil Kowalski 48 | {' '} 49 | for testing and feedback. Created by{' '} 50 | Shu Ding in 51 | 2022, released under the MIT license. 52 | 53 |

54 | 55 | ) 56 | } 57 | -------------------------------------------------------------------------------- /website/src/sections/CustomBalanceRatio.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { useSpring } from '@react-spring/web' 4 | import { useState } from 'react' 5 | import Balancer from 'react-wrap-balancer' 6 | import { BlankLink } from '../components' 7 | 8 | export default function CustomBalanceRatio() { 9 | const [ratio, setRatio] = useState(0.65) 10 | const [currentRatio, setCurrentRatio] = useState(0.65) 11 | 12 | const handleRange: React.ChangeEventHandler = (event) => { 13 | setRatio(+event.target.value / 100) 14 | } 15 | 16 | useSpring({ 17 | from: { r: 0.65 }, 18 | to: { r: ratio }, 19 | onChange(prop) { 20 | setCurrentRatio(prop.value.r) 21 | }, 22 | }) 23 | 24 | return ( 25 | <> 26 |

27 | Custom Balance Ratio 28 |

29 |
30 |
31 | 32 |
33 |
34 |
40 |
41 |

42 | 43 | The quick brown fox jumps over the lazy dog 44 | 45 |

46 |

47 | 48 | The quick brown fox jumps over the lazy dog 49 | 50 |

51 | {``} 54 |
55 |
56 |
57 |
58 |

59 | 60 | Adjust the balance ratio to a custom value between{' '} 61 | 0 (loose) and{' '} 62 | 1 (compact, the default) 63 | 64 |

65 |

70 | 82 | 87 | 88 | Note that if the native CSS balance ( 89 | 90 | text-wrap: balance 91 | 92 | ) is available, React Wrap Balancer will use it instead. And the `ratio` 93 | option will be ignored in that case. You can provide the `preferNative` 94 | option to opt out. 95 |

96 | 97 | ) 98 | } 99 | -------------------------------------------------------------------------------- /website/src/sections/Footer.tsx: -------------------------------------------------------------------------------- 1 | import BlankLink from '../components/BlankLink' 2 | import VercelIcon from '../icons/Vercel' 3 | 4 | export default function Footer() { 5 | return ( 6 |
13 | 24 | Deployed on 25 | 26 | 27 |
28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /website/src/sections/GettingStarted.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { useRef, useState } from 'react' 4 | import Balancer from 'react-wrap-balancer' 5 | import copy from 'copy-to-clipboard' 6 | 7 | import BlankLink from '../components/BlankLink' 8 | import CopyIcon from '../icons/Copy' 9 | import GitHubIcon from '../icons/GitHub' 10 | 11 | // Quick and dirty monochrome code highlighter via string templates, don't use this. 12 | function highlightedCode( 13 | fades: TemplateStringsArray, 14 | ...highlighted: string[] 15 | ) { 16 | const elements: JSX.Element[] = [] 17 | let i = 0 18 | 19 | for (const fade of fades) { 20 | elements.push( 21 | 22 | {fade} 23 | 24 | ) 25 | if (highlighted.length) { 26 | elements.push( 27 | 28 | {highlighted.shift()} 29 | 30 | ) 31 | } 32 | } 33 | 34 | return elements 35 | } 36 | 37 | export default function GettingStarted() { 38 | const [copying, setCopying] = useState(0) 39 | const pointerPos = useRef>({ x: -1, y: -1 }) 40 | 41 | const handlePointerMove: React.MouseEventHandler = (event) => { 42 | pointerPos.current.x = event.clientX 43 | pointerPos.current.y = event.clientY 44 | } 45 | 46 | const handleCopy: React.MouseEventHandler = (event) => { 47 | let text = 'npm install react-wrap-balancer' 48 | 49 | // Only copy the selected text if the pointer is moved 50 | if ( 51 | Math.abs(event.clientX - pointerPos.current.x) > 5 || 52 | Math.abs(event.clientY - pointerPos.current.y) > 5 53 | ) { 54 | text = window.getSelection().toString() 55 | if (!text) return 56 | } 57 | 58 | copy(text) 59 | setCopying((c) => c + 1) 60 | setTimeout(() => { 61 | setCopying((c) => c - 1) 62 | }, 2000) 63 | } 64 | 65 | return ( 66 | <> 67 |

68 | Getting Started 69 |

70 |

71 | 72 | 77 | npm install react-wrap-balancer 78 | 79 | 0} /> 80 | 81 | 82 |

83 |

84 | 85 | 86 | The simplest way is to wrap the text content with{' '} 87 | {``}: 88 | 89 | 90 | {highlightedCode`import ${'Balancer'} from ${"'react-wrap-balancer'"}\n\n// ...\n\n

\n ${'My Title'}\n

`} 91 | 92 | 93 | If you have multiple {``}{' '} 94 | components used, it’s recommended (but optional) to use{' '} 95 | {``} to wrap the entire app. 96 | This will make them share the re-balance logic and reduce the HTML 97 | size: 98 | 99 | 100 | {highlightedCode`import { ${'Provider'} } from ${"'react-wrap-balancer'"}\n\n// ...\n\n${''}\n \n${''}`} 101 | 102 |

103 |
104 | 105 |
    106 |
  • 1 kB Gzipped
  • 107 |
  • Fast O(log n) algorithm
  • 108 |
  • 109 | Doesn’t cause{' '} 110 | layout shifts 111 |
  • 112 |
  • Works perfectly with web fonts
  • 113 |
  • 114 | SSR and{' '} 115 | 116 | streaming SSR 117 | {' '} 118 | supported 119 |
  • 120 |
  • 121 | Switches to native CSS{' '} 122 | 123 | text-wrap: balance 124 | {' '} 125 | if available 126 |
  • 127 |
  • 128 | 129 | Next.js 13 app directory and React Server Components 130 | {' '} 131 | compatible 132 |
  • 133 |
134 |
135 |

136 | 137 | This library requires React ≥ 16.8.0, and IE 11 is not supported. 138 |

139 |

140 | 144 | View project on 145 | 146 | 147 | GitHub 148 | 149 | 150 |

151 | 152 | ) 153 | } 154 | -------------------------------------------------------------------------------- /website/src/sections/Header.tsx: -------------------------------------------------------------------------------- 1 | import BlankLink from '../components/BlankLink' 2 | 3 | export default function Header() { 4 | return ( 5 |
6 | 10 | React 11 |
12 | Wrap 13 |
14 | Balancer 15 |
16 |
17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /website/src/sections/Hero.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import Balancer from 'react-wrap-balancer' 4 | 5 | import Comparison from '../components/Comparison' 6 | import Content from '../components/Content' 7 | 8 | export default function HeroSection() { 9 | return ( 10 | <> 11 |

12 | 13 | Simple React Component That Makes Titles More Readable 14 | 15 |

16 | 20 |

React: A JavaScript library for building user interfaces

21 | 22 | } 23 | b={ 24 | 25 |

26 | 27 | React: A JavaScript library for building user interfaces 28 | 29 |

30 |
31 | } 32 | /> 33 |

34 | 35 | React Wrap Balancer avoids single hanging word on the last line 36 | 37 |

38 | 39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /website/src/sections/HowItWorks.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import Balancer from 'react-wrap-balancer' 4 | 5 | import BlankLink from '../components/BlankLink' 6 | 7 | export default function HowItWorks() { 8 | return ( 9 | <> 10 |

11 | How Does It Work? 12 |

13 |

14 | React Wrap Balancer reduces the width of the content wrapper as much as 15 | it could, before causing an extra line break. When reaching the minimum 16 | width, each line will approximately have the same width, and look more 17 | compact and balanced. 18 |

19 |

20 | Check out the{' '} 21 | 22 | GitHub Repository 23 | {' '} 24 | to learn more. 25 |

26 | 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /website/src/sections/Performance.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import Balancer from 'react-wrap-balancer' 4 | 5 | import BlankLink from '../components/BlankLink' 6 | 7 | export default function Performance() { 8 | return ( 9 | <> 10 |

11 | Performance Impact 12 |

13 |

19 | It is worth to mention that this project is a workaround for the lack of 20 | native support for balanced text wrapping in CSS. It is not perfect as 21 | it adds some performance overhead. However, the performance impact is 22 | usually very trivial and can be ignored in most cases. 23 |

24 |

30 | The following benchmark ( 31 | 32 | source 33 | 34 | ) is done by measuring the script execution time of X balanced titles 35 | when loading the webpage ( 36 | 37 | raw data 38 | 39 | ): 40 |

41 |
42 | Benchmark result 43 | 44 |

50 | It shows that when there are less than 100 elements with React Wrap 51 | Balancer in the initial HTML, the per-element impact to the page load 52 | time is less than 0.25 ms. When there are 1,000 elements, that number 53 | increases to ~1 ms. When there are 5,000 elements, the per-element 54 | script execution time becomes ~7 ms. 55 |

56 |

62 | These numbers don’t scale linearly because re-layouts usually have 63 | an impact to other elements on the page. Hence the best practice is to 64 | only use this library for title elements when necessary, or use it for 65 | content that is behind user interactions (e.g. tooltips), to avoid 66 | negative impacts to the page performance. 67 |

68 | 69 | ) 70 | } 71 | -------------------------------------------------------------------------------- /website/src/sections/UseCases.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { animated } from '@react-spring/web' 4 | import Balancer from 'react-wrap-balancer' 5 | 6 | import Comparison from '../components/Comparison' 7 | import Content from '../components/Content' 8 | import TooltipArrowIcon from '../icons/TooltipArrow' 9 | import TooltipTriggerIcon from '../icons/TooltipTrigger' 10 | 11 | export default function UseCases() { 12 | return ( 13 | <> 14 |

15 | Use Cases 16 |

17 | ( 19 |
20 |
21 | 22 |
23 | This deployment is currently in progress. Read more. 24 |
25 |
26 | 27 |
28 |
29 | 30 |
31 |
32 | )} 33 | b={(width) => ( 34 |
35 |
36 | 37 |
38 | 39 | This deployment is currently in progress. Read more. 40 | 41 |
42 |
43 | 44 |
45 |
46 | 47 |
48 |
49 | )} 50 | /> 51 |

52 | Useful in tooltips and other UI components 53 |

54 | 57 |

58 | 第六個沉思:論物質性東西的存在;論人的靈魂和肉體之間的實在區別 59 |

60 | 61 | } 62 | b={ 63 | 64 |

65 | 66 | 第六個沉思:論物質性東西的存在;論人的靈魂和肉體之間的實在區別 67 | 68 |

69 |
70 | } 71 | /> 72 |

73 | Left aligned, non-latin content 74 |

75 | 78 | 79 | You have wakened not out of sleep, but into a prior dream, and 80 | that dream lies within another, and so on, to infinity, which is 81 | the number of grains of sand. The path that you are to take is 82 | endless, and you will die before you have truly awakened. 83 | 84 |
- Jorge Luis Borges 85 | 86 | } 87 | b={ 88 |
89 | 90 | You have wakened not out of sleep, but into a prior dream, and 91 | that dream lies within another, and so on, to infinity, which is 92 | the number of grains of sand. The path that you are to take is 93 | endless, and you will die before you have truly awakened. 94 | 95 |
- Jorge Luis Borges 96 |
97 | } 98 | /> 99 |

100 | 101 | Makes multi-line content more compact with fewer visual changes when 102 | resizing 103 | 104 |

105 | 106 | ) 107 | } 108 | -------------------------------------------------------------------------------- /website/src/sections/index.ts: -------------------------------------------------------------------------------- 1 | import { default as About } from './About' 2 | import { default as CustomBalanceRatio } from './CustomBalanceRatio' 3 | import { default as GettingStarted } from './GettingStarted' 4 | import { default as Header } from './Header' 5 | import { default as Footer } from './Footer' 6 | import { default as Hero } from './Hero' 7 | import { default as HowItWorks } from './HowItWorks' 8 | import { default as Performance } from './Performance' 9 | import { default as UseCases } from './UseCases' 10 | export { 11 | About, 12 | CustomBalanceRatio, 13 | GettingStarted, 14 | Header, 15 | Footer, 16 | Hero, 17 | HowItWorks, 18 | Performance, 19 | UseCases, 20 | } 21 | const Sections = { 22 | About, 23 | CustomBalanceRatio, 24 | GettingStarted, 25 | Header, 26 | Footer, 27 | Hero, 28 | HowItWorks, 29 | Performance, 30 | UseCases, 31 | } as const 32 | 33 | export default Sections 34 | -------------------------------------------------------------------------------- /website/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": false, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "incremental": true, 11 | "esModuleInterop": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "jsx": "preserve", 17 | "plugins": [ 18 | { 19 | "name": "next" 20 | } 21 | ] 22 | }, 23 | "include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"], 24 | "exclude": ["node_modules"] 25 | } 26 | --------------------------------------------------------------------------------