├── .babelrc
├── .editorconfig
├── .github
├── dependabot.yml
└── workflows
│ └── main.yml
├── .gitignore
├── .npmrc
├── .storybook
├── main.js
└── manager.js
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── package.json
├── rollup.config.js
├── src
└── index.ts
├── stories
└── index.js
└── tsconfig.json
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "production": {
4 | "plugins": [
5 | "@babel/plugin-transform-react-constant-elements",
6 | "@babel/plugin-transform-runtime"
7 | ]
8 | }
9 | },
10 | "presets": [
11 | ["@babel/preset-env", {
12 | "targets": { "browsers": [
13 | ">0.25%",
14 | "not ie 11",
15 | "not op_mini all"
16 | ] }
17 | }],
18 | "@babel/preset-react"
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | indent_style = space
7 | indent_size = 2
8 | end_of_line = lf
9 | charset = utf-8
10 | trim_trailing_whitespace = true
11 | insert_final_newline = true
12 | max_line_length = 100
13 | indent_brace_style = 1TBS
14 | spaces_around_operators = true
15 | quote_type = auto
16 |
17 | [package.json]
18 | indent_style = space
19 | indent_size = 2
20 |
21 | [*.md]
22 | trim_trailing_whitespace = false
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: npm
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | - package-ecosystem: "github-actions"
8 | directory: "/"
9 | schedule:
10 | # Check for updates to GitHub Actions every weekday
11 | interval: "daily"
12 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: test
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | branches:
9 | - master
10 |
11 | jobs:
12 | test:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Checkout
16 | uses: actions/checkout@v4
17 | with:
18 | token: ${{ secrets.GITHUB_TOKEN }}
19 | - name: Setup Node.js
20 | uses: actions/setup-node@v4
21 | with:
22 | node-version: lts/*
23 | - name: Setup PNPM
24 | uses: pnpm/action-setup@v4
25 | with:
26 | version: latest
27 | run_install: true
28 | - name: Test
29 | run: npm test
30 | - name: Release
31 | if: ${{ github.ref == 'refs/heads/master' && !startsWith(github.event.head_commit.message, 'chore(release):') && !startsWith(github.event.head_commit.message, 'docs:') }}
32 | env:
33 | GH_TOKEN: ${{ secrets.GH_TOKEN }}
34 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
35 | run: |
36 | git config --global user.email ${{ secrets.GIT_EMAIL }}
37 | git config --global user.name ${{ secrets.GIT_USERNAME }}
38 | npm run release
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | yarn.lock
4 | dist
5 | .envrc
6 | stats.html
7 | storybook-static
8 | .vercel
9 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | unsafe-perm=true
2 | save-prefix=~
3 | save=false
4 |
--------------------------------------------------------------------------------
/.storybook/main.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | stories: ['../stories/*.js'],
3 | core: { builder: 'webpack5' }
4 | }
5 |
--------------------------------------------------------------------------------
/.storybook/manager.js:
--------------------------------------------------------------------------------
1 | import { addons } from '@storybook/addons'
2 | import { create } from '@storybook/theming'
3 |
4 | const theme = create({
5 | base: 'light',
6 | brandTitle: 'nanoclamp',
7 | brandUrl: 'https://github.com/microlinkhq/nanoclamp',
8 | appBg: 'white',
9 | appContentBg: 'white',
10 | appBorderColor: 'white'
11 | })
12 |
13 | addons.setConfig({ theme })
14 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4 |
5 | ### 2.0.14 (2025-03-24)
6 |
7 | ### 2.0.13 (2025-03-24)
8 |
9 | ### 2.0.12 (2024-12-07)
10 |
11 | ### 2.0.11 (2024-12-06)
12 |
13 | ### 2.0.10 (2024-05-09)
14 |
15 | ### 2.0.9 (2024-03-26)
16 |
17 | ### 2.0.8 (2024-03-25)
18 |
19 | ### 2.0.7 (2023-10-24)
20 |
21 | ### 2.0.6 (2023-09-07)
22 |
23 | ### 2.0.5 (2023-05-13)
24 |
25 | ### [2.0.4](https://github.com/microlinkhq/nanoclamp/compare/v2.0.3...v2.0.4) (2022-11-13)
26 |
27 |
28 | ### Bug Fixes
29 |
30 | * cleanup dist before build ([a43e475](https://github.com/microlinkhq/nanoclamp/commit/a43e4753a863087a6d4a9bfd8cd0306753886b92))
31 |
32 | ### [2.0.3](https://github.com/microlinkhq/nanoclamp/compare/v2.0.2...v2.0.3) (2022-11-13)
33 |
34 | ### 2.0.2 (2022-11-13)
35 |
36 |
37 | ### Bug Fixes
38 |
39 | * avoid run contributors on CI ([4c591b7](https://github.com/microlinkhq/nanoclamp/commit/4c591b72b3bc2a1c795d40790cf6d8f1507190c0))
40 |
41 | ### 2.0.1 (2022-11-13)
42 |
43 | ## [2.0.0](https://github.com/microlinkhq/nanoclamp/compare/v1.2.3...v2.0.0) (2022-11-12)
44 |
45 |
46 | ### Features
47 |
48 | * typescript ([620950e](https://github.com/microlinkhq/nanoclamp/commit/620950ef3c29f65fd0766240c187415b4673edcb))
49 |
50 |
51 | ### Bug Fixes
52 |
53 | * add missing dependency ([1fca48b](https://github.com/microlinkhq/nanoclamp/commit/1fca48b8e7b843c55454e7d23d85f81d52cfb333))
54 | * add missing node types ([c4715d5](https://github.com/microlinkhq/nanoclamp/commit/c4715d5755caf24c83d2b75c63fd0e405e36da9f))
55 | * add types into the npm bundle ([870d35d](https://github.com/microlinkhq/nanoclamp/commit/870d35d5f36944ba51a039925f7157d5f09ab82e))
56 | * linter ([2b7294f](https://github.com/microlinkhq/nanoclamp/commit/2b7294fac26962016299bf82b6d47b49e7f5339f))
57 | * malformed yaml ([d0a3826](https://github.com/microlinkhq/nanoclamp/commit/d0a382625ca286753e92adbdd77bec0bdf610600))
58 | * typo ([1f4b1ca](https://github.com/microlinkhq/nanoclamp/commit/1f4b1ca2cac83ba5302f17e52b0826fd353c2d5a))
59 |
60 | ## 1.4.0 (2020-11-20)
61 |
62 | * build: update dependencies ([7c75175](https://github.com/microlinkhq/nanoclamp/commit/7c75175))
63 |
64 |
65 |
66 | ## 1.3.4 (2020-07-02)
67 |
68 | * fix: add types into the npm bundle ([870d35d](https://github.com/microlinkhq/nanoclamp/commit/870d35d))
69 |
70 |
71 |
72 | ## 1.3.3 (2020-02-12)
73 |
74 | * build: remove babel plugin ([1403676](https://github.com/microlinkhq/nanoclamp/commit/1403676))
75 | * build: tweaks ([df9509f](https://github.com/microlinkhq/nanoclamp/commit/df9509f))
76 |
77 |
78 |
79 | ## 1.3.2 (2020-02-09)
80 |
81 | * build: ensure to don't add babel helpers ([8af3a39](https://github.com/microlinkhq/nanoclamp/commit/8af3a39))
82 |
83 |
84 |
85 | ## 1.3.1 (2020-02-09)
86 |
87 | * fix: typo ([1f4b1ca](https://github.com/microlinkhq/nanoclamp/commit/1f4b1ca))
88 |
89 |
90 |
91 | ## 1.3.0 (2020-02-09)
92 |
93 | * build: ignore common files ([be1692a](https://github.com/microlinkhq/nanoclamp/commit/be1692a))
94 | * build: use rollup instead of microbundle ([5f9ae77](https://github.com/microlinkhq/nanoclamp/commit/5f9ae77))
95 |
96 |
97 |
98 | ## 1.2.12 (2020-02-09)
99 |
100 | * build: update build system ([0350d9e](https://github.com/microlinkhq/nanoclamp/commit/0350d9e))
101 | * Update LICENSE ([97d91a9](https://github.com/microlinkhq/nanoclamp/commit/97d91a9))
102 | * Update README.md ([1b4790e](https://github.com/microlinkhq/nanoclamp/commit/1b4790e))
103 | * Update README.md ([4a95931](https://github.com/microlinkhq/nanoclamp/commit/4a95931))
104 |
105 |
106 |
107 |
108 | ## 1.2.11 (2019-04-25)
109 |
110 | * build: fix meta ([64b2c45](https://github.com/microlinkhq/nanoclamp/commit/64b2c45))
111 |
112 |
113 |
114 |
115 | ## 1.2.10 (2019-04-25)
116 |
117 | * build: update workflow ([2a48887](https://github.com/microlinkhq/nanoclamp/commit/2a48887))
118 |
119 |
120 |
121 |
122 | ## 1.2.9 (2019-03-07)
123 |
124 | * Add Typescript types ([4da4c75](https://github.com/microlinkhq/nanoclamp/commit/4da4c75))
125 | * migrate hooks ([85c6659](https://github.com/microlinkhq/nanoclamp/commit/85c6659))
126 | * Release 1.2.8 ([e684ded](https://github.com/microlinkhq/nanoclamp/commit/e684ded))
127 |
128 |
129 |
130 |
131 | ## 1.2.8 (2019-03-07)
132 |
133 | * Add Typescript types ([4da4c75](https://github.com/microlinkhq/nanoclamp/commit/4da4c75))
134 | * migrate hooks ([85c6659](https://github.com/microlinkhq/nanoclamp/commit/85c6659))
135 |
136 |
137 |
138 |
139 | ## 1.2.7 (2018-04-26)
140 |
141 | * prepublish → prepublishOnly ([6ed7e7d](https://github.com/microlinkhq/nanoclamp/commit/6ed7e7d))
142 | * Remove unnecessary build step ([6deff03](https://github.com/microlinkhq/nanoclamp/commit/6deff03))
143 | * Update browser targets ([aefb9e8](https://github.com/microlinkhq/nanoclamp/commit/aefb9e8))
144 | * Update README.md ([112fcf8](https://github.com/microlinkhq/nanoclamp/commit/112fcf8))
145 |
146 |
147 |
148 |
149 | ## 1.2.6 (2018-03-18)
150 |
151 | * Add prepublish script ([85aa87f](https://github.com/microlinkhq/nanoclamp/commit/85aa87f))
152 | * Check for `this.original` before clamping lines ([e415f02](https://github.com/microlinkhq/nanoclamp/commit/e415f02))
153 |
154 |
155 |
156 |
157 | ## 1.2.5 (2018-03-09)
158 |
159 | * Fix `accessibility` prop’s `propType` ([f1c799b](https://github.com/microlinkhq/nanoclamp/commit/f1c799b))
160 | * Fix README license ([df06bd2](https://github.com/microlinkhq/nanoclamp/commit/df06bd2))
161 |
162 |
163 |
164 |
165 | ## 1.2.4 (2018-02-24)
166 |
167 | * Add `accessibility` prop ([04d9ecd](https://github.com/microlinkhq/nanoclamp/commit/04d9ecd))
168 | * Update README ([db5ad1c](https://github.com/microlinkhq/nanoclamp/commit/db5ad1c))
169 |
170 |
171 |
172 |
173 | ## 1.2.1 (2018-02-23)
174 |
175 | * Fix unnecessary clamping ([af92865](https://github.com/microlinkhq/nanoclamp/commit/af92865))
176 |
177 |
178 |
179 |
180 | # 1.2.0 (2018-02-23)
181 |
182 | * Avoid pass component props to children props ([f571af0](https://github.com/microlinkhq/nanoclamp/commit/f571af0))
183 | * Build lib ([562070d](https://github.com/microlinkhq/nanoclamp/commit/562070d))
184 | * Improve ellipsis size calculation ([67529c4](https://github.com/microlinkhq/nanoclamp/commit/67529c4))
185 | * Increase default debounce value ([271aa82](https://github.com/microlinkhq/nanoclamp/commit/271aa82))
186 | * Prettier storybook ([e3de8f5](https://github.com/microlinkhq/nanoclamp/commit/e3de8f5))
187 | * Refactor ([836e238](https://github.com/microlinkhq/nanoclamp/commit/836e238))
188 | * Refactor ([5a06ebd](https://github.com/microlinkhq/nanoclamp/commit/5a06ebd))
189 | * Update README ([5d83b67](https://github.com/microlinkhq/nanoclamp/commit/5d83b67))
190 | * Update storybook ([c6557b6](https://github.com/microlinkhq/nanoclamp/commit/c6557b6))
191 | * Update storybook styles ([bc58b06](https://github.com/microlinkhq/nanoclamp/commit/bc58b06))
192 |
193 |
194 |
195 |
196 | ## 1.1.4 (2018-02-22)
197 |
198 | * Avoid umd bundle ([2ee8675](https://github.com/microlinkhq/nanoclamp/commit/2ee8675))
199 | * Update README.md ([afa2348](https://github.com/microlinkhq/nanoclamp/commit/afa2348))
200 | * Update README.md ([b7f4670](https://github.com/microlinkhq/nanoclamp/commit/b7f4670))
201 |
202 |
203 |
204 |
205 | ## 1.1.3 (2018-02-22)
206 |
207 | * Fix release message ([669777e](https://github.com/microlinkhq/nanoclamp/commit/669777e))
208 | * Release react-microlink@1.1.2 ([2745a17](https://github.com/microlinkhq/nanoclamp/commit/2745a17))
209 | * Update README.md ([784ab03](https://github.com/microlinkhq/nanoclamp/commit/784ab03))
210 | * Update README.md ([d5a8450](https://github.com/microlinkhq/nanoclamp/commit/d5a8450))
211 | * Update README.md ([99aec90](https://github.com/microlinkhq/nanoclamp/commit/99aec90))
212 |
213 |
214 |
215 |
216 | ## 1.1.2 (2018-02-22)
217 |
218 | * Update README.md ([784ab03](https://github.com/microlinkhq/nanoclamp/commit/784ab03))
219 | * Update README.md ([d5a8450](https://github.com/microlinkhq/nanoclamp/commit/d5a8450))
220 | * Update README.md ([99aec90](https://github.com/microlinkhq/nanoclamp/commit/99aec90))
221 |
222 |
223 |
224 |
225 | ## 1.1.1 (2018-02-22)
226 |
227 | * 1.1.1 ([805e27c](https://github.com/microlinkhq/nanoclamp/commit/805e27c))
228 | * Add meta files ([ac0896c](https://github.com/microlinkhq/nanoclamp/commit/ac0896c))
229 | * Add missing dependencies ([9ef26d9](https://github.com/microlinkhq/nanoclamp/commit/9ef26d9))
230 | * Add netlify link to README ([32cceb1](https://github.com/microlinkhq/nanoclamp/commit/32cceb1))
231 | * Improve build process ([031aecf](https://github.com/microlinkhq/nanoclamp/commit/031aecf))
232 | * Update README.md ([654dd5d](https://github.com/microlinkhq/nanoclamp/commit/654dd5d))
233 | * Update README.md ([741983b](https://github.com/microlinkhq/nanoclamp/commit/741983b))
234 | * Update unnecessary script ([d5d95a7](https://github.com/microlinkhq/nanoclamp/commit/d5d95a7))
235 |
236 |
237 |
238 |
239 | # 1.1.0 (2018-02-22)
240 |
241 | * 1.1.0 ([30d432e](https://github.com/microlinkhq/nanoclamp/commit/30d432e))
242 | * Add `is` prop ([3f8f798](https://github.com/microlinkhq/nanoclamp/commit/3f8f798))
243 | * Add `key` prop to story components ([c532404](https://github.com/microlinkhq/nanoclamp/commit/c532404))
244 | * Add package.json homepage ([b556e88](https://github.com/microlinkhq/nanoclamp/commit/b556e88))
245 | * Add storybook ([44f569a](https://github.com/microlinkhq/nanoclamp/commit/44f569a))
246 | * Build lib ([0e84ccd](https://github.com/microlinkhq/nanoclamp/commit/0e84ccd))
247 | * Refactoring ([72a3d7c](https://github.com/microlinkhq/nanoclamp/commit/72a3d7c))
248 | * Update .gitignore ([2ec96ef](https://github.com/microlinkhq/nanoclamp/commit/2ec96ef))
249 | * Update storybook ([bcfb5c1](https://github.com/microlinkhq/nanoclamp/commit/bcfb5c1))
250 |
251 |
252 |
253 |
254 | ## 1.0.1 (2018-02-22)
255 |
256 | * 0.1.1 ([c56b2ed](https://github.com/microlinkhq/nanoclamp/commit/c56b2ed))
257 | * 0.1.2 ([7fb6791](https://github.com/microlinkhq/nanoclamp/commit/7fb6791))
258 | * 1.0.1 ([499bd1f](https://github.com/microlinkhq/nanoclamp/commit/499bd1f))
259 | * Add CI yml ([062d601](https://github.com/microlinkhq/nanoclamp/commit/062d601))
260 | * Bug #3 fixed - DOM manipulation was firing even with empty string ([38aa956](https://github.com/microlinkhq/nanoclamp/commit/38aa956))
261 | * demo page added ([f16215a](https://github.com/microlinkhq/nanoclamp/commit/f16215a))
262 | * demo url added ([3df9721](https://github.com/microlinkhq/nanoclamp/commit/3df9721))
263 | * file structure fixed ([6b4b596](https://github.com/microlinkhq/nanoclamp/commit/6b4b596))
264 | * image updated ([45bce17](https://github.com/microlinkhq/nanoclamp/commit/45bce17))
265 | * image url updated ([8873519](https://github.com/microlinkhq/nanoclamp/commit/8873519))
266 | * index.js location fixed ([e637c06](https://github.com/microlinkhq/nanoclamp/commit/e637c06))
267 | * Initial commit ([a9b4391](https://github.com/microlinkhq/nanoclamp/commit/a9b4391))
268 | * Initial commit ([b85412a](https://github.com/microlinkhq/nanoclamp/commit/b85412a))
269 | * Initial refactoring ([dee3893](https://github.com/microlinkhq/nanoclamp/commit/dee3893))
270 | * latest react v16.2.0 and import bug fixed ([d22f1bf](https://github.com/microlinkhq/nanoclamp/commit/d22f1bf))
271 | * npm code ([95a3fd2](https://github.com/microlinkhq/nanoclamp/commit/95a3fd2))
272 | * README nobr added for props table's values ([0a80bd2](https://github.com/microlinkhq/nanoclamp/commit/0a80bd2))
273 | * README updated ([2de05c7](https://github.com/microlinkhq/nanoclamp/commit/2de05c7))
274 | * README updated ([94d2c6b](https://github.com/microlinkhq/nanoclamp/commit/94d2c6b))
275 | * Rename clamp component ([6533606](https://github.com/microlinkhq/nanoclamp/commit/6533606))
276 | * reverted the version number ([def527b](https://github.com/microlinkhq/nanoclamp/commit/def527b))
277 | * simplified the docs content ([d17b161](https://github.com/microlinkhq/nanoclamp/commit/d17b161))
278 | * table column title updated ([53580a6](https://github.com/microlinkhq/nanoclamp/commit/53580a6))
279 | * typo fixed ([6781a85](https://github.com/microlinkhq/nanoclamp/commit/6781a85))
280 | * Update license 👉 MIT ([82fbaa3](https://github.com/microlinkhq/nanoclamp/commit/82fbaa3))
281 | * Update package.json ([f3c6751](https://github.com/microlinkhq/nanoclamp/commit/f3c6751))
282 | * Update README ([347c756](https://github.com/microlinkhq/nanoclamp/commit/347c756))
283 | * Update README ([5258cd9](https://github.com/microlinkhq/nanoclamp/commit/5258cd9))
284 | * Updated demo link in README ([890b76a](https://github.com/microlinkhq/nanoclamp/commit/890b76a))
285 | * url fixed for css and js ([b95b75d](https://github.com/microlinkhq/nanoclamp/commit/b95b75d))
286 | * Version bumped up in package.json ([1bd3539](https://github.com/microlinkhq/nanoclamp/commit/1bd3539))
287 | * version number updated to 1.0.0 ([419370e](https://github.com/microlinkhq/nanoclamp/commit/419370e))
288 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright © 2019 Microlink (microlink.io)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |

3 |

4 |
5 |
6 |
7 |
8 | [](https://github.com/microlinkhq/nanoclamp/releases)
9 | [](https://www.npmjs.org/package/nanoclamp)
10 |
11 | ###### [Storybook](https://nanoclamp.netlify.com/)
12 |
13 | > Responsive text clamping component. Inspired by [react-clamp-lines](https://github.com/zoltantothcom/react-clamp-lines), but smaller (~1KB).
14 |
15 | ## Install
16 |
17 | ```
18 | npm install nanoclamp --save
19 | ```
20 |
21 | ## Usage
22 |
23 | ```jsx
24 | import NanoClamp from 'nanoclamp';
25 |
26 |
32 | ```
33 |
34 | ## API
35 |
36 | | prop | type | default | description |
37 | |---------------|-----------|---------|-------------------------------------------------------------------------|
38 | | accessibility | `boolean` | `true` | Pass the full _unclamped_ string to the DOM element's `title` attribute |
39 | | className | `string` | | CSS classname(s) added to the string |
40 | | debounce | `number` | `300` | Time in milliseconds used for debounce |
41 | | ellipsis | `string` | `'…'` | String displayed after the clamped `text` |
42 | | is | `string` | `'div'` | DOM selector used to render the string |
43 | | lines | `number` | `3` | Number of visible lines |
44 | | text | `string` | | Text you wish to clamp |
45 |
46 | ## License
47 |
48 | **nanoclamp** © [Microlink](https://microlink.io), released under the [MIT](https://github.com/microlinkhq/nanoclamp/blob/master/LICENSE.md) License.
49 | Adapted from [`react-clamp-lines`](https://github.com/zoltantothcom/react-clamp-lines) by Brad Adams with help from [contributors](https://github.com/microlinkhq/nanoclamp/contributors).
50 |
51 | > [microlink.io](https://microlink.io) · GitHub [microlinkhq](https://github.com/microlinkhq) · X [@microlinkhq](https://x.com/microlinkhq)
52 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nanoclamp",
3 | "description": "Responsive text clamping component for React",
4 | "homepage": "https://github.com/microlinkhq/nanoclamp",
5 | "version": "2.0.14",
6 | "types": "dist/index.d.ts",
7 | "main": "dist/nanoclamp.js",
8 | "module": "dist/nanoclamp.mjs",
9 | "author": {
10 | "name": "Brad Adams"
11 | },
12 | "contributors": [
13 | {
14 | "name": "Kiko Beats",
15 | "email": "josefrancisco.verdu@gmail.com"
16 | },
17 | {
18 | "name": "Brad Adams",
19 | "email": "hi@breadadams.com"
20 | },
21 | {
22 | "name": "Zoltan Toth",
23 | "email": "ztoth@indigo.ca"
24 | },
25 | {
26 | "name": "Axel Hernández Ferrera",
27 | "email": "axelhzf@gmail.com"
28 | }
29 | ],
30 | "repository": {
31 | "type": "git",
32 | "url": "git+https://github.com/microlinkhq/nanoclamp.git"
33 | },
34 | "bugs": {
35 | "url": "https://github.com/microlinkhq/nanoclamp/issues"
36 | },
37 | "keywords": [
38 | "clamp",
39 | "clamping",
40 | "components",
41 | "ellipsis",
42 | "lines",
43 | "react"
44 | ],
45 | "devDependencies": {
46 | "@babel/eslint-parser": "latest",
47 | "@babel/plugin-transform-react-constant-elements": "latest",
48 | "@babel/plugin-transform-runtime": "latest",
49 | "@babel/preset-env": "latest",
50 | "@babel/preset-react": "latest",
51 | "@babel/runtime": "latest",
52 | "@commitlint/cli": "latest",
53 | "@commitlint/config-conventional": "latest",
54 | "@ksmithut/prettier-standard": "latest",
55 | "@rollup/plugin-babel": "latest",
56 | "@rollup/plugin-terser": "latest",
57 | "@rollup/plugin-typescript": "latest",
58 | "@storybook/addons": "latest",
59 | "@storybook/builder-webpack5": "latest",
60 | "@storybook/manager-webpack5": "latest",
61 | "@storybook/react": "latest",
62 | "@storybook/theming": "latest",
63 | "@types/react": "^19",
64 | "babel-loader": "latest",
65 | "ci-publish": "latest",
66 | "finepack": "latest",
67 | "git-authors-cli": "latest",
68 | "github-generate-release": "latest",
69 | "nano-staged": "latest",
70 | "prop-types": "latest",
71 | "react": "^19",
72 | "react-dom": "^19",
73 | "rollup": "latest",
74 | "rollup-plugin-filesize": "latest",
75 | "rollup-plugin-peer-deps-external": "latest",
76 | "rollup-plugin-visualizer": "latest",
77 | "simple-git-hooks": "latest",
78 | "standard-markdown": "latest",
79 | "standard-version": "latest",
80 | "ts-standard": "latest",
81 | "typescript": "latest"
82 | },
83 | "engines": {
84 | "node": ">= 8"
85 | },
86 | "files": [
87 | "dist",
88 | "typings"
89 | ],
90 | "scripts": {
91 | "build": "NODE_ENV=production rollup -c rollup.config.js --bundleConfigAsCjs",
92 | "build-storybook": "NODE_ENV=production build-storybook",
93 | "contributors": "(npx git-authors-cli && npx finepack && git add package.json && git commit -m 'build: contributors' --no-verify) || true",
94 | "dev": "start-storybook -p 6006",
95 | "lint": "standard-markdown && ts-standard src && ts-standard stories",
96 | "postrelease": "npm run release:tags && npm run release:github && (ci-publish || npm publish --access=public)",
97 | "prebuild": "rm -rf dist",
98 | "prepare": "npx simple-git-hooks",
99 | "prepublishOnly": "npm run build",
100 | "prerelease": "npm run update:check && [[ -z \"${CI}\" ]] && npm run contributors || exit 0",
101 | "pretest": "npm run lint",
102 | "release": "standard-version -a",
103 | "release:github": "github-generate-release",
104 | "release:tags": "git push --follow-tags origin HEAD:master",
105 | "test": "exit 0"
106 | },
107 | "license": "MIT",
108 | "commitlint": {
109 | "extends": [
110 | "@commitlint/config-conventional"
111 | ],
112 | "rules": {
113 | "body-max-line-length": [
114 | 0
115 | ]
116 | }
117 | },
118 | "nano-staged": {
119 | "*.js": [
120 | "prettier-standard",
121 | "standard --fix"
122 | ],
123 | "*.md": [
124 | "standard-markdown"
125 | ],
126 | "package.json": [
127 | "finepack"
128 | ]
129 | },
130 | "peerDependencies": {
131 | "react": "^19"
132 | },
133 | "simple-git-hooks": {
134 | "commit-msg": "npx commitlint --edit",
135 | "pre-commit": "npx nano-staged"
136 | },
137 | "ts-standard": {
138 | "globals": [
139 | "React"
140 | ],
141 | "ignore": [
142 | "/dist/"
143 | ],
144 | "parser": "@babel/eslint-parser"
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import external from 'rollup-plugin-peer-deps-external'
2 | import { visualizer } from 'rollup-plugin-visualizer'
3 | import typescript from '@rollup/plugin-typescript'
4 | import filesize from 'rollup-plugin-filesize'
5 | import terser from '@rollup/plugin-terser'
6 | import babel from '@rollup/plugin-babel'
7 | import fs from 'fs'
8 |
9 | import pkg from './package.json'
10 |
11 | const babelRc = JSON.parse(fs.readFileSync('./.babelrc'))
12 |
13 | export default {
14 | input: 'src/index.ts',
15 | external: [/@babel\/runtime/],
16 | output: [
17 | {
18 | file: pkg.main,
19 | format: 'cjs',
20 | sourcemap: true
21 | },
22 | {
23 | file: pkg.module,
24 | format: 'es',
25 | sourcemap: true
26 | }
27 | ],
28 | plugins: [
29 | external({
30 | includeDependencies: true
31 | }),
32 | babel({
33 | babelrc: false,
34 | babelHelpers: 'runtime',
35 | ...babelRc
36 | }),
37 | typescript(),
38 | terser(),
39 | filesize(),
40 | visualizer({ template: 'treemap' })
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { createElement, useCallback, useLayoutEffect, useMemo, useRef } from 'react'
2 |
3 | interface Props extends React.HTMLAttributes {
4 | accessibility?: boolean
5 | debounce?: number
6 | ellipsis?: string
7 | is?: string
8 | lines?: number
9 | text: string
10 | }
11 |
12 | const DEFAULT_ELLIPSIS = '…'
13 | const DEFAULT_TEXT = '.'
14 |
15 | const debounceFn = (func: () => void, timeoutMs: number): () => void => {
16 | let timeout: ReturnType | undefined
17 |
18 | const later = (): void => {
19 | timeout = undefined
20 | func()
21 | }
22 |
23 | return () => {
24 | const callNow = timeout == null
25 | clearTimeout(timeout)
26 | timeout = setTimeout(later, timeoutMs)
27 | if (callNow) func()
28 | }
29 | }
30 |
31 | const NanoClamp = ({
32 | accessibility = true,
33 | debounce = 300,
34 | ellipsis = DEFAULT_ELLIPSIS,
35 | is = 'div',
36 | lines = 3,
37 | text,
38 | ...props
39 | }: Props): React.ReactElement | null => {
40 | const elementRef = useRef(null)
41 | const textRef = useRef(DEFAULT_TEXT)
42 |
43 | const clampProps = {
44 | ref: elementRef,
45 | ...(accessibility ? { title: text } : {}),
46 | ...props
47 | }
48 |
49 | const hasText = useMemo(() => typeof text === 'string' && text.length > 0, [text])
50 |
51 | const clampLines = useCallback(() => {
52 | if (!hasText) return
53 |
54 | const updateTextRefs = (newText: string): void => {
55 | textRef.current = newText
56 |
57 | if (elementRef.current != null) {
58 | elementRef.current.textContent = newText
59 | }
60 | }
61 |
62 | const elementHeight = (): number => elementRef.current?.clientHeight ?? 0
63 |
64 | updateTextRefs(DEFAULT_TEXT)
65 |
66 | const lineHeight = elementHeight() + 1
67 | const maxHeight = lineHeight * lines + 1
68 |
69 | updateTextRefs(text)
70 | if (elementHeight() <= maxHeight) {
71 | return
72 | }
73 |
74 | let start = 0
75 | let middle = 0
76 | let end = text.length
77 |
78 | while (start <= end) {
79 | middle = Math.floor((start + end) / 2)
80 |
81 | const slicedText = text.slice(0, middle).trim() + ellipsis
82 | updateTextRefs(slicedText)
83 |
84 | if (elementHeight() <= maxHeight) {
85 | start = middle + 1
86 | } else {
87 | end = middle - 1
88 | }
89 | }
90 |
91 | const textPlusEllipsis = text.slice(0, middle - 1).trim() + ellipsis
92 |
93 | updateTextRefs(textPlusEllipsis)
94 | }, [ellipsis, hasText, lines, text])
95 |
96 | useLayoutEffect(() => {
97 | clampLines()
98 |
99 | if (elementRef.current == null) return
100 |
101 | const observer = new ResizeObserver(debounceFn(clampLines, debounce))
102 | observer.observe(elementRef.current)
103 | return () => observer.disconnect()
104 | }, [clampLines, debounce])
105 |
106 | return hasText ? createElement(is, clampProps, textRef.current) : null
107 | }
108 |
109 | export default NanoClamp
110 |
--------------------------------------------------------------------------------
/stories/index.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import NanoClamp from '../src/index'
4 |
5 | const string =
6 | 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Incidunt, dignissimos totam nam cumque ipsum autem placeat. Doloribus nesciunt id saepe quasi, quod, quis voluptatibus rerum at perspiciatis impedit ab nostrum.'
7 |
8 | const NanoClampWrap = ({ background, width, ...props }) => (
9 |
23 |
24 |
25 | )
26 |
27 | const examples = [
28 | {
29 | background: '#0984e3',
30 | key: 1,
31 | lines: 1,
32 | width: 500
33 | },
34 | {
35 | background: '#6c5ce7',
36 | key: 2,
37 | lines: 2,
38 | width: 370
39 | },
40 | {
41 | background: '#e84393',
42 | key: 3,
43 | lines: 3,
44 | width: 270
45 | }
46 | ]
47 |
48 | storiesOf('NanoClamp', module)
49 | .add('default', () => (
50 | <>
51 | {examples.map(({ ...props }) => (
52 |
53 | ))}
54 | >
55 | ))
56 | .add('with ellipsis prop', () => (
57 | <>
58 | {examples.map(({ ...props }) => (
59 |
64 | ))}
65 | >
66 | ))
67 | .add('with is prop', () => (
68 | <>
69 | {examples.map(({ ...props }) => (
70 |
71 | ))}
72 | >
73 | ))
74 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "moduleResolution": "node",
4 | "removeComments": false,
5 | "sourceMap": true,
6 | "target": "es2018",
7 | "outDir": "dist",
8 | "declaration": true,
9 | "strict": true,
10 | "module": "esnext",
11 | "noEmitOnError": true,
12 | "lib": ["dom", "es2017"],
13 | "esModuleInterop": false
14 | },
15 | "include": ["src"]
16 | }
17 |
--------------------------------------------------------------------------------