@rethunk/react-load-hooks 0.3.1 → 0.4.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/README.md CHANGED
@@ -32,7 +32,7 @@ A worked example and the contracts: [HUMANS.md](HUMANS.md).
32
32
  - `useLoaded` and `useDebouncedLoad` fetch on dependency changes and drop superseded answers.
33
33
  - `loader` is the same logic without React, for a store or a class.
34
34
  - `useStoredState` persists UI state in `localStorage`.
35
- - `useWidth` tracks an element's width before paint; `useDebouncedValue` settles a fast-changing value.
35
+ - `useSize` tracks an element's width and height before paint; `useWidth` is the width only. `useDebouncedValue` settles a fast-changing value.
36
36
  - `sameValue`, `keepUnchanged` and `keepFields` keep the stored value when a fresh backend answer is equal, so subscribers do not re-render.
37
37
  - `clockWhen`, `absoluteWhen` and `relativeWhen` label a time through `Intl`: "3 minutes ago" within a week, a date after.
38
38
  - `useNowValue` and `useSecondValue` share one timer per period and re-render only on a changed answer.
package/dist/index.d.ts CHANGED
@@ -5,6 +5,7 @@ export { useDebouncedLoad } from './useDebouncedLoad.ts';
5
5
  export { useDebouncedValue } from './useDebouncedValue.ts';
6
6
  export { type LoadedOptions, useLoaded } from './useLoaded.ts';
7
7
  export { useNowValue, useSecondValue } from './useNow.ts';
8
+ export { useSize } from './useSize.ts';
8
9
  export { readStored, useStoredState, writeStored } from './useStoredState.ts';
9
10
  export { useWidth } from './useWidth.ts';
10
11
  export { absoluteWhen, clockWhen, relativeWhen, type WhenOptions } from './when.ts';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK,QAAQ,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAC9D,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC1D,OAAO,EAAE,KAAK,aAAa,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC9D,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AACzD,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAC7E,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK,QAAQ,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAC9D,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC1D,OAAO,EAAE,KAAK,aAAa,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC9D,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AACzD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAC7E,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAA"}
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ export { useDebouncedLoad } from './useDebouncedLoad.js';
5
5
  export { useDebouncedValue } from './useDebouncedValue.js';
6
6
  export { useLoaded } from './useLoaded.js';
7
7
  export { useNowValue, useSecondValue } from './useNow.js';
8
+ export { useSize } from './useSize.js';
8
9
  export { readStored, useStoredState, writeStored } from './useStoredState.js';
9
10
  export { useWidth } from './useWidth.js';
10
11
  export { absoluteWhen, clockWhen, relativeWhen } from './when.js';
@@ -0,0 +1,8 @@
1
+ import { type RefObject } from 'react';
2
+ /** The client size of the element the ref points at, kept current as it resizes; 0 until measured. */
3
+ declare function useSize<T extends HTMLElement>(ref: RefObject<T | null>): {
4
+ width: number;
5
+ height: number;
6
+ };
7
+ export { useSize };
8
+ //# sourceMappingURL=useSize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSize.d.ts","sourceRoot":"","sources":["../src/useSize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAA6B,MAAM,OAAO,CAAA;AAGjE,sGAAsG;AACtG,iBAAS,OAAO,CAAC,CAAC,SAAS,WAAW,EACpC,GAAG,EAAE,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,GACvB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAsBnC;AAED,OAAO,EAAE,OAAO,EAAE,CAAA"}
@@ -0,0 +1,24 @@
1
+ import { useLayoutEffect, useState } from 'react';
2
+ import { flushSync } from 'react-dom';
3
+ /** The client size of the element the ref points at, kept current as it resizes; 0 until measured. */
4
+ function useSize(ref) {
5
+ const [size, setSize] = useState({ width: 0, height: 0 });
6
+ // Measured before paint, so the first frame is laid out for the real size rather than 0.
7
+ useLayoutEffect(() => {
8
+ const el = ref.current;
9
+ if (!el) {
10
+ return;
11
+ }
12
+ const apply = () => setSize((prev) => prev.width === el.clientWidth && prev.height === el.clientHeight
13
+ ? prev
14
+ : { width: el.clientWidth, height: el.clientHeight });
15
+ // Rendered before the browser paints the resize: a default-priority update would paint one frame laid out for the
16
+ // old size, with whatever columns or scrollbar that size implies.
17
+ const ro = new ResizeObserver(() => flushSync(apply));
18
+ ro.observe(el);
19
+ apply();
20
+ return () => ro.disconnect();
21
+ }, [ref]);
22
+ return size;
23
+ }
24
+ export { useSize };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rethunk/react-load-hooks",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "React hooks for async loads that never land out of order, plus the small state hooks an app builds screens from.",
5
5
  "license": "MIT",
6
6
  "author": "Rethunk.Tech, LLC",
package/src/index.ts CHANGED
@@ -5,6 +5,7 @@ export { useDebouncedLoad } from './useDebouncedLoad.ts'
5
5
  export { useDebouncedValue } from './useDebouncedValue.ts'
6
6
  export { type LoadedOptions, useLoaded } from './useLoaded.ts'
7
7
  export { useNowValue, useSecondValue } from './useNow.ts'
8
+ export { useSize } from './useSize.ts'
8
9
  export { readStored, useStoredState, writeStored } from './useStoredState.ts'
9
10
  export { useWidth } from './useWidth.ts'
10
11
  export { absoluteWhen, clockWhen, relativeWhen, type WhenOptions } from './when.ts'
package/src/useSize.ts ADDED
@@ -0,0 +1,31 @@
1
+ import { type RefObject, useLayoutEffect, useState } from 'react'
2
+ import { flushSync } from 'react-dom'
3
+
4
+ /** The client size of the element the ref points at, kept current as it resizes; 0 until measured. */
5
+ function useSize<T extends HTMLElement>(
6
+ ref: RefObject<T | null>,
7
+ ): { width: number; height: number } {
8
+ const [size, setSize] = useState({ width: 0, height: 0 })
9
+ // Measured before paint, so the first frame is laid out for the real size rather than 0.
10
+ useLayoutEffect(() => {
11
+ const el = ref.current
12
+ if (!el) {
13
+ return
14
+ }
15
+ const apply = () =>
16
+ setSize((prev) =>
17
+ prev.width === el.clientWidth && prev.height === el.clientHeight
18
+ ? prev
19
+ : { width: el.clientWidth, height: el.clientHeight },
20
+ )
21
+ // Rendered before the browser paints the resize: a default-priority update would paint one frame laid out for the
22
+ // old size, with whatever columns or scrollbar that size implies.
23
+ const ro = new ResizeObserver(() => flushSync(apply))
24
+ ro.observe(el)
25
+ apply()
26
+ return () => ro.disconnect()
27
+ }, [ref])
28
+ return size
29
+ }
30
+
31
+ export { useSize }