@eduvidu/react-autoscale 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,29 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2026-02-23
9
+
10
+ ### Added
11
+
12
+ - `useAutoScale()` hook — zero-config, container-aware scaling with ResizeObserver
13
+ - `<AutoScale>` component — declarative wrapper with full prop forwarding
14
+ - `ScaleProvider` / `useScaleContext()` — nested scale context for deep trees
15
+ - Scale modes: `width`, `height`, `contain`, `cover`, custom calculator
16
+ - Measurement strategies: `scrollSize`, `boundingRect`, custom measurer
17
+ - `clampScale()` — safe clamping with edge-case handling
18
+ - `calculateScale()` — pure, framework-agnostic scale calculation
19
+ - `createScheduler()` — rAF-batched scheduling with throttle/debounce
20
+ - `isBrowser()` / `canUseDom()` — SSR environment guards
21
+ - Compensation modes: `none`, `width`, `height`, `both`
22
+ - `onScaleChange` callback with full dimension details
23
+ - `disabled` / `debug` options
24
+ - Dual ESM + CJS builds with TypeScript declarations
25
+ - Full sourcemaps
26
+ - 69 unit tests with 94%+ coverage
27
+ - Zero runtime dependencies
28
+ - Tree-shakeable exports
29
+ - ~3KB gzipped ESM bundle
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 eduvidu
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.
package/README.md ADDED
@@ -0,0 +1,436 @@
1
+ # @eduvidu/react-autoscale
2
+
3
+ A production-ready, enterprise-grade universal scaling engine for React.
4
+
5
+ **Zero config • SSR-safe • Tree-shakeable • ~3KB gzipped**
6
+
7
+ [![npm](https://img.shields.io/npm/v/@eduvidu/react-autoscale)](https://www.npmjs.com/package/@eduvidu/react-autoscale)
8
+ [![bundle size](https://img.shields.io/bundlephobia/minzip/@eduvidu/react-autoscale)](https://bundlephobia.com/package/@eduvidu/react-autoscale)
9
+ [![license](https://img.shields.io/npm/l/@eduvidu/react-autoscale)](LICENSE)
10
+
11
+ ---
12
+
13
+ ## Features
14
+
15
+ - 🪶 **~3KB gzipped** — zero runtime dependencies
16
+ - ⚡ **ResizeObserver + rAF batching** — no layout thrashing
17
+ - 🔒 **SSR-safe** — works with Next.js (App Router & Pages Router)
18
+ - 🌳 **Tree-shakeable** — import only what you use
19
+ - 🎯 **Zero config** — works with zero props out of the box
20
+ - 📐 **5 scale modes** — `width`, `height`, `contain`, `cover`, custom
21
+ - 🪆 **Nested scaling** — context-aware deep nesting support
22
+ - 🔌 **Plugin-style** — custom calculators, custom measurers
23
+ - 📦 **Dual ESM + CJS** — with full TypeScript declarations
24
+
25
+ ---
26
+
27
+ ## Installation
28
+
29
+ ```bash
30
+ npm install @eduvidu/react-autoscale
31
+ # or
32
+ yarn add @eduvidu/react-autoscale
33
+ # or
34
+ pnpm add @eduvidu/react-autoscale
35
+ ```
36
+
37
+ **Peer dependencies:** `react >= 16.8.0`
38
+
39
+ ---
40
+
41
+ ## Compatibility
42
+
43
+ | React Version | Status | Notes |
44
+ | ------------- | ------------ | ------------------------------------------ |
45
+ | 16.8+ | ✅ Supported | Hooks baseline |
46
+ | 17 | ✅ Tested | Full support |
47
+ | 18 | ✅ Tested | StrictMode safe, concurrent rendering safe |
48
+ | 19 | ✅ Tested | Full support |
49
+
50
+ **StrictMode:** The library is fully compatible with React 18+ StrictMode. In development mode, React double-invokes effects to catch bugs — `useAutoScale` correctly creates and tears down `ResizeObserver` and the rAF scheduler in each mount/unmount cycle with no duplicate observers or memory leaks.
51
+
52
+ **Concurrent rendering:** The library uses only stable React public APIs (`useState`, `useRef`, `useCallback`, `useEffect`, `useMemo`, `useContext`, `forwardRef`, `useImperativeHandle`, `createContext`). No internal or private React APIs are used. No assumptions that break under concurrent rendering.
53
+
54
+ **SSR:** All browser APIs (`window`, `document`, `ResizeObserver`, `requestAnimationFrame`) are guarded behind `isBrowser()` / `isResizeObserverSupported()` checks. The library renders safely in Next.js App Router, Pages Router, and any Node.js SSR environment — returning `scale: 1` and `isReady: false` until the client hydrates.
55
+
56
+ ---
57
+
58
+ ## Quick Start
59
+
60
+ ### Component API
61
+
62
+ ```tsx
63
+ import { AutoScale } from '@eduvidu/react-autoscale';
64
+
65
+ function Dashboard() {
66
+ return (
67
+ <div style={{ width: '100vw', height: '100vh' }}>
68
+ <AutoScale>
69
+ <div style={{ width: 1920, height: 1080 }}>
70
+ {/* Your fixed-size content scales to fit */}
71
+ <DashboardGrid />
72
+ </div>
73
+ </AutoScale>
74
+ </div>
75
+ );
76
+ }
77
+ ```
78
+
79
+ ### Hook API
80
+
81
+ ```tsx
82
+ import { useAutoScale } from '@eduvidu/react-autoscale';
83
+
84
+ function ScalableTable() {
85
+ const { containerRef, contentRef, scale, isReady } = useAutoScale({
86
+ mode: 'width',
87
+ });
88
+
89
+ return (
90
+ <div ref={containerRef} style={{ width: '100%', height: '100%' }}>
91
+ <div
92
+ ref={contentRef}
93
+ style={{
94
+ transform: `scale(${scale})`,
95
+ transformOrigin: 'top left',
96
+ opacity: isReady ? 1 : 0,
97
+ }}
98
+ >
99
+ <LargeTable />
100
+ </div>
101
+ </div>
102
+ );
103
+ }
104
+ ```
105
+
106
+ ---
107
+
108
+ ## API Reference
109
+
110
+ ### `<AutoScale>`
111
+
112
+ Declarative scaling wrapper. All props optional.
113
+
114
+ | Prop | Type | Default | Description |
115
+ | --------------------- | --------------------------------------------- | -------------- | ------------------------------------------- |
116
+ | `mode` | `'width' \| 'height' \| 'contain' \| 'cover'` | `'contain'` | Scale strategy |
117
+ | `minScale` | `number` | — | Minimum scale factor |
118
+ | `maxScale` | `number` | — | Maximum scale factor |
119
+ | `customCalculator` | `(container, content) => number` | — | Custom scale function (overrides mode) |
120
+ | `observeParent` | `boolean` | `false` | Observe parent element instead of container |
121
+ | `transformOrigin` | `string` | `'top left'` | CSS transform-origin |
122
+ | `throttle` | `number` | — | Min interval (ms) between recalculations |
123
+ | `debounce` | `number` | — | Debounce delay (ms) |
124
+ | `disabled` | `boolean` | `false` | Disable scaling entirely |
125
+ | `debug` | `boolean` | `false` | Log measurements to console |
126
+ | `compensationMode` | `'none' \| 'width' \| 'height' \| 'both'` | `'none'` | Adjust container size after scaling |
127
+ | `onScaleChange` | `(scale, details) => void` | — | Called when scale changes |
128
+ | `measurementStrategy` | `'scrollSize' \| 'boundingRect' \| 'custom'` | `'scrollSize'` | How to measure content size |
129
+ | `customMeasurer` | `(element) => { width, height }` | — | Custom measurement function |
130
+ | `contentClassName` | `string` | — | Class for inner content wrapper |
131
+ | `contentStyle` | `CSSProperties` | — | Style for inner content wrapper |
132
+
133
+ All standard `<div>` attributes (className, id, data-\*, onClick, etc.) are forwarded to the outer container.
134
+
135
+ ### `useAutoScale(options?)`
136
+
137
+ Returns:
138
+
139
+ | Property | Type | Description |
140
+ | --------------------- | --------------------------- | ------------------------------------- |
141
+ | `containerRef` | `RefObject<HTMLDivElement>` | Attach to container element |
142
+ | `contentRef` | `RefObject<HTMLDivElement>` | Attach to content element |
143
+ | `scale` | `number` | Current scale factor (default `1`) |
144
+ | `dimensions` | `{ width, height }` | Scaled content dimensions |
145
+ | `containerDimensions` | `{ width, height }` | Container dimensions |
146
+ | `contentDimensions` | `{ width, height }` | Natural (unscaled) content dimensions |
147
+ | `isReady` | `boolean` | `true` after first measurement |
148
+
149
+ ### `ScaleProvider` / `useScaleContext()`
150
+
151
+ For nested scaling awareness:
152
+
153
+ ```tsx
154
+ import { useScaleContext } from '@eduvidu/react-autoscale';
155
+
156
+ function NestedWidget() {
157
+ const { scale, parentScale, depth } = useScaleContext();
158
+ // scale = this level's scale
159
+ // parentScale = accumulated scale from all ancestors
160
+ // depth = nesting level (0 = no AutoScale ancestor)
161
+ }
162
+ ```
163
+
164
+ ### Core Utilities
165
+
166
+ These are framework-agnostic and can be used independently:
167
+
168
+ ```ts
169
+ import {
170
+ calculateScale,
171
+ clampScale,
172
+ measureElement,
173
+ measureContainer,
174
+ createScheduler,
175
+ isBrowser,
176
+ canUseDom,
177
+ } from '@eduvidu/react-autoscale';
178
+ ```
179
+
180
+ ---
181
+
182
+ ## Examples
183
+
184
+ ### Nested Scaling
185
+
186
+ ```tsx
187
+ <AutoScale mode="contain">
188
+ <div style={{ width: 1920, height: 1080 }}>
189
+ <Header />
190
+ <AutoScale mode="width">
191
+ <DataTable columns={50} />
192
+ </AutoScale>
193
+ <Footer />
194
+ </div>
195
+ </AutoScale>
196
+ ```
197
+
198
+ ### Custom Scale Calculator
199
+
200
+ ```tsx
201
+ <AutoScale
202
+ customCalculator={(container, content) => {
203
+ // Scale to 80% of container width
204
+ return (container.width * 0.8) / content.width;
205
+ }}
206
+ >
207
+ <WidgetPanel />
208
+ </AutoScale>
209
+ ```
210
+
211
+ ### Scale Change Tracking
212
+
213
+ ```tsx
214
+ <AutoScale
215
+ onScaleChange={(scale, { previousScale, containerDimensions }) => {
216
+ analytics.track('scale_changed', { scale, previousScale });
217
+ }}
218
+ >
219
+ <Dashboard />
220
+ </AutoScale>
221
+ ```
222
+
223
+ ### Compensation Mode
224
+
225
+ When you need surrounding content to flow around the scaled content:
226
+
227
+ ```tsx
228
+ <AutoScale compensationMode="height" mode="width">
229
+ {/* Container height adjusts to match scaled content height */}
230
+ <FixedWidthContent />
231
+ </AutoScale>
232
+ ```
233
+
234
+ ### With Clamping
235
+
236
+ ```tsx
237
+ <AutoScale minScale={0.5} maxScale={2}>
238
+ <ResponsivePanel />
239
+ </AutoScale>
240
+ ```
241
+
242
+ ### Debounced Scaling
243
+
244
+ ```tsx
245
+ <AutoScale debounce={150}>
246
+ <ExpensiveChart />
247
+ </AutoScale>
248
+ ```
249
+
250
+ ---
251
+
252
+ ## Next.js Usage
253
+
254
+ Works out of the box with both App Router and Pages Router:
255
+
256
+ ```tsx
257
+ // app/dashboard/page.tsx (App Router)
258
+ 'use client';
259
+
260
+ import { AutoScale } from '@eduvidu/react-autoscale';
261
+
262
+ export default function DashboardPage() {
263
+ return (
264
+ <div style={{ width: '100vw', height: '100vh' }}>
265
+ <AutoScale>
266
+ <div style={{ width: 1920, height: 1080 }}>
267
+ <DashboardContent />
268
+ </div>
269
+ </AutoScale>
270
+ </div>
271
+ );
272
+ }
273
+ ```
274
+
275
+ > **Note:** Since `AutoScale` uses `useRef` and `useEffect`, it must be a client component. Add `'use client'` at the top of the file when using App Router.
276
+
277
+ ---
278
+
279
+ ## Vite Usage
280
+
281
+ ```tsx
282
+ // src/App.tsx
283
+ import { AutoScale } from '@eduvidu/react-autoscale';
284
+
285
+ function App() {
286
+ return (
287
+ <AutoScale style={{ width: '100vw', height: '100vh' }}>
288
+ <div style={{ width: 1440, height: 900 }}>
289
+ <MyApp />
290
+ </div>
291
+ </AutoScale>
292
+ );
293
+ }
294
+ ```
295
+
296
+ ---
297
+
298
+ ## Performance Notes
299
+
300
+ ### How it works
301
+
302
+ 1. **ResizeObserver** watches the container for size changes
303
+ 2. **rAF batching** coalesces rapid resize events into a single frame
304
+ 3. **Epsilon comparison** (`1e-4`) skips React re-renders when scale hasn't meaningfully changed
305
+ 4. **Stable refs** prevent unnecessary observer re-creation
306
+ 5. **transform: scale()** is GPU-accelerated — no reflow triggered
307
+
308
+ ### What we avoid
309
+
310
+ - ❌ `zoom` property (inconsistent cross-browser)
311
+ - ❌ Measuring scaled dimensions (leads to feedback loops)
312
+ - ❌ Layout thrashing (no interleaved reads/writes)
313
+ - ❌ ResizeObserver loop errors (rAF wrapping)
314
+ - ❌ Memory leaks (observer disconnect + scheduler destroy on unmount)
315
+
316
+ ---
317
+
318
+ ## Anti-Patterns
319
+
320
+ ```tsx
321
+ // ❌ BAD: Don't set explicit width/height on the AutoScale container
322
+ // (it needs to fill available space to calculate scale)
323
+ <AutoScale style={{ width: 500, height: 300 }}>...</AutoScale>
324
+
325
+ // ✅ GOOD: Let it fill its parent
326
+ <div style={{ width: 500, height: 300 }}>
327
+ <AutoScale>...</AutoScale>
328
+ </div>
329
+ ```
330
+
331
+ ```tsx
332
+ // ❌ BAD: Don't apply transform to children manually
333
+ <AutoScale>
334
+ <div style={{ transform: 'scale(2)' }}>...</div>
335
+ </AutoScale>
336
+
337
+ // ✅ GOOD: Let AutoScale handle all transforms
338
+ <AutoScale>
339
+ <div>...</div>
340
+ </AutoScale>
341
+ ```
342
+
343
+ ---
344
+
345
+ ## Competitor Comparison
346
+
347
+ | Feature | @eduvidu/react-autoscale | react-fit | react-zoom-pan-pinch |
348
+ | ------------------ | ----------------------------------------- | --------- | -------------------- |
349
+ | Bundle (gzip) | **~3KB** | ~8KB | ~45KB |
350
+ | Dependencies | **0** | Multiple | Multiple |
351
+ | Scale modes | **5** (width/height/contain/cover/custom) | 1 | Pan/zoom only |
352
+ | SSR safe | **✅** | ❌ | ❌ |
353
+ | Tree-shakeable | **✅** | ❌ | ❌ |
354
+ | rAF batching | **✅** | ❌ | Partial |
355
+ | Nested scaling | **✅** | ❌ | ❌ |
356
+ | Zero config | **✅** | ❌ | ❌ |
357
+ | Plugin calculators | **✅** | ❌ | ❌ |
358
+ | Compensation modes | **✅** | ❌ | ❌ |
359
+
360
+ ---
361
+
362
+ ## Known Limitations
363
+
364
+ 1. **Content must have intrinsic size** — the content children should have explicit or natural dimensions for measurement to work. Flexbox/grid children that stretch may report incorrect sizes.
365
+
366
+ 2. **CSS transitions on scaled content** — adding `transition: transform` to the content div may cause visual jitter during rapid resizing.
367
+
368
+ 3. **Hidden containers** — if the container is `display: none` at mount time, the initial measurement will be `0`. Scale will recalculate when the container becomes visible.
369
+
370
+ 4. **iframe content** — content inside iframes cannot be measured by the parent's ResizeObserver.
371
+
372
+ ---
373
+
374
+ ## Publishing
375
+
376
+ ```bash
377
+ # 1. Build
378
+ npm run build
379
+
380
+ # 2. Test
381
+ npm test
382
+
383
+ # 3. Type-check
384
+ npm run typecheck
385
+
386
+ # 4. Dry run
387
+ npm pack --dry-run
388
+
389
+ # 5. Publish
390
+ npm publish --access public
391
+ ```
392
+
393
+ ---
394
+
395
+ ## Development
396
+
397
+ ```bash
398
+ # Install
399
+ npm install
400
+
401
+ # Dev mode (watch)
402
+ npm run dev
403
+
404
+ # Test (watch)
405
+ npm run test:watch
406
+
407
+ # Test with coverage
408
+ npm run test:coverage
409
+
410
+ # Lint
411
+ npm run lint
412
+
413
+ # Format
414
+ npm run format
415
+
416
+ # Build
417
+ npm run build
418
+ ```
419
+
420
+ ---
421
+
422
+ ## Future Roadmap
423
+
424
+ - [ ] `useAutoScaleTransition` — animated scale transitions
425
+ - [ ] `direction: 'rtl'` support for transform-origin
426
+ - [ ] Resize breakpoint callbacks
427
+ - [ ] Performance observer integration
428
+ - [ ] React Native support (experimental)
429
+ - [ ] Storybook examples
430
+ - [ ] Playground website
431
+
432
+ ---
433
+
434
+ ## License
435
+
436
+ [MIT](LICENSE) © eduvidu