@reykjavik/hanna-react 0.10.103 → 0.10.104

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.
Files changed (136) hide show
  1. package/AccordionList.js +2 -2
  2. package/Alert.d.ts +2 -2
  3. package/Alert.js +1 -0
  4. package/AutosuggestSearch.d.ts +40 -0
  5. package/AutosuggestSearch.js +70 -0
  6. package/BasicTable.d.ts +24 -4
  7. package/BasicTable.js +20 -19
  8. package/BreadCrumbs.d.ts +2 -2
  9. package/CHANGELOG.md +34 -0
  10. package/ContactBubble.d.ts +4 -3
  11. package/ContactBubble.js +7 -4
  12. package/Datepicker.d.ts +8 -3
  13. package/Datepicker.js +36 -14
  14. package/FileInput.d.ts +2 -1
  15. package/FileInput.js +2 -2
  16. package/FormField.js +2 -2
  17. package/Gallery.d.ts +2 -1
  18. package/Gallery.js +5 -0
  19. package/Layout.d.ts +4 -3
  20. package/Layout.js +0 -3
  21. package/MainMenu/_PrimaryPanel.d.ts +3 -4
  22. package/MainMenu/_PrimaryPanel.js +1 -1
  23. package/MainMenu.d.ts +6 -4
  24. package/MainMenu.js +8 -16
  25. package/Multiselect.d.ts +2 -1
  26. package/Multiselect.js +4 -3
  27. package/NameCard.d.ts +2 -1
  28. package/NameCard.js +7 -0
  29. package/Pagination.d.ts +2 -2
  30. package/ReadSpeakerPlayer.js +13 -5
  31. package/SearchInput.d.ts +24 -2
  32. package/SearchInput.js +13 -3
  33. package/SearchResults.d.ts +2 -1
  34. package/SearchResults.js +10 -2
  35. package/ShareButtons.d.ts +3 -2
  36. package/SiteSearchAutocomplete.d.ts +11 -11
  37. package/SiteSearchAutocomplete.js +21 -7
  38. package/SiteSearchCurtain.js +2 -2
  39. package/SiteSearchInput.d.ts +19 -10
  40. package/SiteSearchInput.js +9 -6
  41. package/Tooltip.js +4 -3
  42. package/VerticalTabsTOC.js +2 -2
  43. package/_abstract/_ScrollWrapper.d.ts +10 -0
  44. package/_abstract/_ScrollWrapper.js +21 -0
  45. package/_abstract/_Table.d.ts +71 -0
  46. package/_abstract/_Table.js +55 -0
  47. package/_abstract/_TogglerGroup.js +2 -2
  48. package/_abstract/_TogglerInput.js +2 -2
  49. package/_mixed_export_resolution_/ReactDatepicker.d.ts +3 -0
  50. package/esm/AccordionList.js +1 -1
  51. package/esm/Alert.d.ts +2 -2
  52. package/esm/Alert.js +1 -0
  53. package/esm/AutosuggestSearch.d.ts +40 -0
  54. package/esm/AutosuggestSearch.js +66 -0
  55. package/esm/BasicTable.d.ts +24 -4
  56. package/esm/BasicTable.js +19 -18
  57. package/esm/BreadCrumbs.d.ts +2 -2
  58. package/esm/ContactBubble.d.ts +4 -3
  59. package/esm/ContactBubble.js +6 -3
  60. package/esm/Datepicker.d.ts +8 -3
  61. package/esm/Datepicker.js +35 -13
  62. package/esm/FileInput.d.ts +2 -1
  63. package/esm/FileInput.js +1 -1
  64. package/esm/FormField.js +1 -1
  65. package/esm/Gallery.d.ts +2 -1
  66. package/esm/Gallery.js +5 -0
  67. package/esm/Layout.d.ts +4 -3
  68. package/esm/Layout.js +0 -3
  69. package/esm/MainMenu/_PrimaryPanel.d.ts +3 -4
  70. package/esm/MainMenu/_PrimaryPanel.js +1 -1
  71. package/esm/MainMenu.d.ts +6 -4
  72. package/esm/MainMenu.js +8 -16
  73. package/esm/Multiselect.d.ts +2 -1
  74. package/esm/Multiselect.js +2 -1
  75. package/esm/NameCard.d.ts +2 -1
  76. package/esm/NameCard.js +7 -0
  77. package/esm/Pagination.d.ts +2 -2
  78. package/esm/ReadSpeakerPlayer.js +13 -5
  79. package/esm/SearchInput.d.ts +24 -2
  80. package/esm/SearchInput.js +13 -3
  81. package/esm/SearchResults.d.ts +2 -1
  82. package/esm/SearchResults.js +9 -1
  83. package/esm/ShareButtons.d.ts +3 -2
  84. package/esm/SiteSearchAutocomplete.d.ts +11 -11
  85. package/esm/SiteSearchAutocomplete.js +21 -7
  86. package/esm/SiteSearchCurtain.js +1 -1
  87. package/esm/SiteSearchInput.d.ts +19 -10
  88. package/esm/SiteSearchInput.js +8 -6
  89. package/esm/Tooltip.js +2 -1
  90. package/esm/VerticalTabsTOC.js +1 -1
  91. package/esm/_abstract/_ScrollWrapper.d.ts +10 -0
  92. package/esm/_abstract/_ScrollWrapper.js +16 -0
  93. package/esm/_abstract/_Table.d.ts +71 -0
  94. package/esm/_abstract/_Table.js +51 -0
  95. package/esm/_abstract/_TogglerGroup.js +1 -1
  96. package/esm/_abstract/_TogglerInput.js +1 -1
  97. package/esm/_mixed_export_resolution_/ReactDatepicker.d.ts +3 -0
  98. package/esm/index.d.ts +1 -0
  99. package/esm/utils/browserSide.d.ts +119 -1
  100. package/esm/utils/browserSide.js +152 -1
  101. package/esm/utils/config.d.ts +1 -14
  102. package/esm/utils/config.js +0 -2
  103. package/esm/utils/useCallbackOnEsc.d.ts +6 -0
  104. package/esm/utils/useCallbackOnEsc.js +25 -0
  105. package/esm/utils/useDomid.d.ts +8 -0
  106. package/esm/utils/useDomid.js +17 -0
  107. package/esm/utils/useLaggedState.d.ts +18 -0
  108. package/esm/utils/useLaggedState.js +84 -0
  109. package/esm/utils/useOnClickOutside.d.ts +9 -0
  110. package/esm/utils/useOnClickOutside.js +32 -0
  111. package/esm/utils/useScrollEdgeDetect.d.ts +15 -0
  112. package/esm/utils/useScrollEdgeDetect.js +45 -0
  113. package/esm/utils/useShortState.d.ts +2 -0
  114. package/esm/utils/useShortState.js +34 -0
  115. package/esm/utils.d.ts +1 -0
  116. package/esm/utils.js +1 -0
  117. package/index.d.ts +1 -0
  118. package/package.json +6 -2
  119. package/utils/browserSide.d.ts +119 -1
  120. package/utils/browserSide.js +156 -4
  121. package/utils/config.d.ts +1 -14
  122. package/utils/config.js +1 -4
  123. package/utils/useCallbackOnEsc.d.ts +6 -0
  124. package/utils/useCallbackOnEsc.js +29 -0
  125. package/utils/useDomid.d.ts +8 -0
  126. package/utils/useDomid.js +21 -0
  127. package/utils/useLaggedState.d.ts +18 -0
  128. package/utils/useLaggedState.js +88 -0
  129. package/utils/useOnClickOutside.d.ts +9 -0
  130. package/utils/useOnClickOutside.js +35 -0
  131. package/utils/useScrollEdgeDetect.d.ts +15 -0
  132. package/utils/useScrollEdgeDetect.js +50 -0
  133. package/utils/useShortState.d.ts +2 -0
  134. package/utils/useShortState.js +38 -0
  135. package/utils.d.ts +1 -0
  136. package/utils.js +1 -0
@@ -1,6 +1,158 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useIsServerSide = exports.useIsBrowserSide = void 0;
4
- var hooks_1 = require("@hugsmidjan/react/hooks");
5
- Object.defineProperty(exports, "useIsBrowserSide", { enumerable: true, get: function () { return hooks_1.useIsBrowserSide; } });
6
- Object.defineProperty(exports, "useIsServerSide", { enumerable: true, get: function () { return hooks_1.useIsServerSide; } });
3
+ exports.setDefaultSSR = exports.useIsBrowserSide = exports.useIsServerSide = void 0;
4
+ const react_1 = require("react");
5
+ let alreadyBrowserSide = false;
6
+ const defaultSSRSupport = true;
7
+ /**
8
+ * The default value use for the optional `ssrSupport` parameter
9
+ * on the `useIsBRowserSide` and `useIsServerSide` hooks.`
10
+ */
11
+ let DEFAULT_SSR_SUPPORT = defaultSSRSupport;
12
+ /**
13
+ * Low-level useState wrapper that initializes the state to one value
14
+ * during initial render and then updates it to another value
15
+ * once the component has been mounted.
16
+ *
17
+ * After that it's just a normal [value, setValue] pair.
18
+ *
19
+ * NOTE: The optional `ssrSupport` parameter is ignored after the initial render
20
+ */
21
+ const useClientState = (serverState, clientState,
22
+ /**
23
+ * Indicates whether server-side rendering is supported or not.
24
+ *
25
+ * The `ssr-only` value is useful for cases where you need
26
+ * to demo the server-rendered version in a browser.
27
+ */
28
+ ssrSupport = DEFAULT_SSR_SUPPORT) => {
29
+ const stateTuple = (0, react_1.useState)(() => (ssrSupport === 'ssr-only'
30
+ ? serverState
31
+ : ssrSupport && !alreadyBrowserSide
32
+ ? serverState
33
+ : clientState) // TODO: Remove this type assertion once @types/react and typescript have been updated
34
+ );
35
+ (0, react_1.useEffect)(() => {
36
+ alreadyBrowserSide = true;
37
+ if (ssrSupport !== 'ssr-only') {
38
+ stateTuple[1](clientState);
39
+ }
40
+ }, []);
41
+ return stateTuple;
42
+ };
43
+ /**
44
+ * Returns `true` if `useEffect` has not executed yet.
45
+ *
46
+ * This signals that we're in "server-side rendering" mode
47
+ * and it's not yet appropriate to do JS-driven UI enhancements.
48
+ *
49
+ * ```js
50
+ * const Knob = (props) => {
51
+ * const [visible, setVisible] = useState(false);
52
+ * const isServer = useIsServerSide();
53
+ * const handleClick = () => {
54
+ * setVisible(!visible);
55
+ * props.onClick && props.onClick(!visible);
56
+ * };
57
+ *
58
+ * if (isServer) {
59
+ * return <span className="Knob">{props.label}</span>
60
+ * }
61
+ * return (
62
+ * <button className="Knob" aria-pressed={visible} onClick={handleClick}>
63
+ * {props.label}
64
+ * </button>
65
+ * );
66
+ * }
67
+ * ```
68
+ *
69
+ * SSR support mode can optionally be set to:
70
+ *
71
+ * - `true` (the default) enables the serve-side phase (returns `true` then `undefined`).
72
+ * - `false` disables (skips) the serve-side phase (always returns `undefined`).
73
+ * - `"ssr-only"` disables (skips) the browser-side phase (always returns `true`).
74
+ *
75
+ * NOTE: The `ssrSupport` parameter is ignored after the initial render.
76
+ */
77
+ const useIsServerSide = (ssrSupport) =>
78
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
79
+ useClientState(true, false, ssrSupport)[0] || undefined;
80
+ exports.useIsServerSide = useIsServerSide;
81
+ /**
82
+ * Returns `true` when `useEffect` has executed.
83
+ *
84
+ * This signals the time to apply Progressive Enhancement.
85
+ *
86
+ * ```js
87
+ * const Knob = (props) => {
88
+ * const [visible, setVisible] = useState(false);
89
+ * const isBrowser = useIsBrowserSide();
90
+ * const handleClick = () => {
91
+ * setVisible(!visible);
92
+ * props.onClick && props.onClick(!visible);
93
+ * };
94
+ *
95
+ * if (isBrowser) {
96
+ * return (
97
+ * <button className="Knob" aria-pressed={visible} onClick={handleClick}>
98
+ * {props.label}
99
+ * </button>
100
+ * );
101
+ * }
102
+ * return <span className="Knob">{props.label}</span>
103
+ * }
104
+ * ```
105
+ *
106
+ * SSR support mode can optionally be set to:
107
+ *
108
+ * - `true` (the default) enables the serve-side phase (returns `true` then `undefined`).
109
+ * - `false` disables (skips) the serve-side phase (always returns `true`).
110
+ * - `"ssr-only"` disables (skips) the browser-side phase (always returns `undefined`).
111
+ *
112
+ * NOTE: The `ssrSupport` parameter is ignored after the initial render.
113
+ */
114
+ const useIsBrowserSide = (ssrSupport) =>
115
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
116
+ useClientState(false, true, ssrSupport)[0] || undefined;
117
+ exports.useIsBrowserSide = useIsBrowserSide;
118
+ // ---------------------------------------------------------------------------
119
+ const _history = [];
120
+ /**
121
+ * Allows you to set a the default SSRSupport value for the `useIsBRowserSide`
122
+ * and `useIsServerSide` hooks.
123
+ *
124
+ * Example use:
125
+ *
126
+ * ```js
127
+ * setDefaultSSR(false);
128
+ * ```
129
+ *
130
+ * The values are pushed to a simple stack, and if you want to revert
131
+ * a temporarily set value, use the `setDefaultSSR.pop()` method
132
+ * to go back to the previous value. Example:
133
+ *
134
+ * ```js
135
+ * setDefaultSSR('ssr-only');
136
+ * // ...render some components...
137
+ * setDefaultSSR.pop(); // go back to the previous state
138
+ * ```
139
+ *
140
+ * You explicitly switch to using the library's default by passing `undefined`
141
+ * as an argument — like so:
142
+ *
143
+ * ```js
144
+ * setDefaultSSR(undefined);
145
+ * ```
146
+ */
147
+ const setDefaultSSR = (ssrSupport) => {
148
+ DEFAULT_SSR_SUPPORT = ssrSupport != null ? ssrSupport : defaultSSRSupport;
149
+ _history.unshift(DEFAULT_SSR_SUPPORT);
150
+ };
151
+ exports.setDefaultSSR = setDefaultSSR;
152
+ /**
153
+ * Unsets the last pushed defaultSSR value
154
+ */
155
+ exports.setDefaultSSR.pop = () => {
156
+ _history.shift();
157
+ DEFAULT_SSR_SUPPORT = _history[0] != null ? _history[0] : defaultSSRSupport;
158
+ };
package/utils/config.d.ts CHANGED
@@ -1,14 +1 @@
1
- import { SSRSupport } from '@hugsmidjan/react/hooks';
2
- export type { LinkRenderer } from '../_abstract/_Link.js';
3
- export { setLinkRenderer } from '../_abstract/_Link.js';
4
- export { setDefaultSSR } from '@hugsmidjan/react/hooks';
5
- export type SSRSupportProps = {
6
- /**
7
- * Indicates whether server-side rendering is supported or not.
8
- *
9
- * The `ssr-only` value is useful for cases where you need
10
- * to demo the server-rendered version in a browser.
11
- */
12
- ssr?: SSRSupport;
13
- };
14
- export type { SSRSupport };
1
+ export { type LinkRenderer, setLinkRenderer } from '../_abstract/_Link.js';
package/utils/config.js CHANGED
@@ -1,8 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.setDefaultSSR = exports.setLinkRenderer = void 0;
3
+ exports.setLinkRenderer = void 0;
4
4
  var _Link_js_1 = require("../_abstract/_Link.js");
5
5
  Object.defineProperty(exports, "setLinkRenderer", { enumerable: true, get: function () { return _Link_js_1.setLinkRenderer; } });
6
- // ---------------------------------------------------------------------------
7
- var hooks_1 = require("@hugsmidjan/react/hooks");
8
- Object.defineProperty(exports, "setDefaultSSR", { enumerable: true, get: function () { return hooks_1.setDefaultSSR; } });
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Performs a callback whenever the user hits the ESC key.
3
+ *
4
+ * Pass `undefined` to remove the event listener
5
+ */
6
+ export declare const useCallbackOnEsc: (callback: (() => void) | undefined) => void;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useCallbackOnEsc = void 0;
4
+ const react_1 = require("react");
5
+ /**
6
+ * Performs a callback whenever the user hits the ESC key.
7
+ *
8
+ * Pass `undefined` to remove the event listener
9
+ */
10
+ const useCallbackOnEsc = (callback) => {
11
+ const cb = (0, react_1.useRef)(callback);
12
+ const active = !!callback;
13
+ cb.current = callback;
14
+ (0, react_1.useEffect)(() => {
15
+ if (!active) {
16
+ return;
17
+ }
18
+ const callbackOnEsc = (e) => {
19
+ if (e.key === 'Escape') {
20
+ cb.current && cb.current();
21
+ }
22
+ };
23
+ document.addEventListener('keydown', callbackOnEsc);
24
+ return () => {
25
+ document.removeEventListener('keydown', callbackOnEsc);
26
+ };
27
+ }, [active]);
28
+ };
29
+ exports.useCallbackOnEsc = useCallbackOnEsc;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Returns a stable, unique ID string.
3
+ *
4
+ * Uses useId from React@18 when available, but falls back on a custom id
5
+ * generator. (The custom generator causes angry hydration warnings in dev
6
+ * mode).
7
+ */
8
+ export declare const useDomid: (staticId?: string) => string;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useDomid = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const React = tslib_1.__importStar(require("react"));
6
+ const domid_1 = tslib_1.__importDefault(require("@hugsmidjan/qj/domid"));
7
+ // @ts-expect-error (transparently feature-detect useId hook, which is introduced in React@18)
8
+ const useId = React.useId;
9
+ /**
10
+ * Returns a stable, unique ID string.
11
+ *
12
+ * Uses useId from React@18 when available, but falls back on a custom id
13
+ * generator. (The custom generator causes angry hydration warnings in dev
14
+ * mode).
15
+ */
16
+ exports.useDomid = useId
17
+ ? (staticId) => {
18
+ const id = useId();
19
+ return staticId || id;
20
+ }
21
+ : (staticId) => React.useRef(staticId || (0, domid_1.default)()).current;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * A `useState` alternative with in-built support for delayed (debounced) effect.
3
+ *
4
+ * This is especially useful when emulating "focusin"/"focusout" events,
5
+ * and a less jittery 'onMouseEnter'/'onMouseLeave' behavior.
6
+ *
7
+ * The returned `setState` function accepts an optional `customDelay` parameter.
8
+ *
9
+ * An explicitly falsy delay (`0` | `false` | `null`) results in a
10
+ * immediate (normal) `setState` invocation, whereas any other value uses
11
+ * `setTimeout` to delay the update.
12
+ *
13
+ * Each `setState` call automatically cancels any pending scheduled update.
14
+ *
15
+ * You can also use the `setState.cancel()` helper to to cancel the last
16
+ * scheduled update, without explicitly setting a new value.
17
+ */
18
+ export declare const useLaggedState: <S>(initialState: S | (() => S), defaultDelay?: number, thenState?: S | (() => S) | undefined) => [currentState: S, setState: (newState: S | ((prevState: S, upcomingState: S) => S), customDelay?: number | false) => void, nextState: S, isTransitioning: true | undefined];
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useLaggedState = void 0;
4
+ const react_1 = require("react");
5
+ // ---------------------------------------------------------------------------
6
+ /**
7
+ * A `useState` alternative with in-built support for delayed (debounced) effect.
8
+ *
9
+ * This is especially useful when emulating "focusin"/"focusout" events,
10
+ * and a less jittery 'onMouseEnter'/'onMouseLeave' behavior.
11
+ *
12
+ * The returned `setState` function accepts an optional `customDelay` parameter.
13
+ *
14
+ * An explicitly falsy delay (`0` | `false` | `null`) results in a
15
+ * immediate (normal) `setState` invocation, whereas any other value uses
16
+ * `setTimeout` to delay the update.
17
+ *
18
+ * Each `setState` call automatically cancels any pending scheduled update.
19
+ *
20
+ * You can also use the `setState.cancel()` helper to to cancel the last
21
+ * scheduled update, without explicitly setting a new value.
22
+ */
23
+ const useLaggedState = (initialState,
24
+ /**
25
+ * Default delay in milliseconds. A value of `0` results in a
26
+ * immediate (normal) setState invocation.
27
+ *
28
+ * NOTE: The `defaultDelay` parameter is ignored after the initial render.
29
+ */
30
+ defaultDelay = 0,
31
+ /**
32
+ * A state that should be automatically transitioned to after the initial mounting.
33
+ *
34
+ * Syntatic sugar equivalent to the following:
35
+ *
36
+ * ```js
37
+ * const [value, _, setValue] = useLaggedState(initialState, defaultDelay);
38
+ * useEffect(() => {
39
+ * setValue(thenState, defaultDelay);
40
+ * }, [])
41
+ * ```
42
+ */
43
+ thenState) => {
44
+ const timeout = (0, react_1.useRef)();
45
+ const [[currentState, nextState], setLocalState] = (0, react_1.useState)(() => {
46
+ const initial = typeof initialState === 'function' ? initialState() : initialState;
47
+ return [initial, initial];
48
+ });
49
+ const setState = (0, react_1.useMemo)(() => {
50
+ const _setter = (newState, customDelay) => {
51
+ timeout.current && clearTimeout(timeout.current);
52
+ const delay = customDelay !== undefined ? customDelay : defaultDelay;
53
+ setLocalState((prevState) => {
54
+ const [current, upcoming] = prevState;
55
+ const newValue = typeof newState === 'function'
56
+ ? newState(current, upcoming)
57
+ : newState;
58
+ if (!delay || newValue === current) {
59
+ // Instant update!
60
+ return newValue === current && newValue === upcoming
61
+ ? prevState
62
+ : [newValue, newValue];
63
+ }
64
+ // Debounced update!
65
+ timeout.current = setTimeout(() => setLocalState([newValue, newValue]), delay);
66
+ return [current, newValue];
67
+ });
68
+ };
69
+ _setter.cancel = () => {
70
+ _setter((prevState) => prevState, null);
71
+ };
72
+ return _setter;
73
+ },
74
+ // eslint-disable-next-line react-hooks/exhaustive-deps
75
+ [
76
+ // defaultDelay,
77
+ ]);
78
+ (0, react_1.useEffect)(() => {
79
+ thenState !== undefined && setState(thenState);
80
+ return () => {
81
+ timeout.current && clearTimeout(timeout.current);
82
+ };
83
+ }, [] // eslint-disable-line react-hooks/exhaustive-deps
84
+ );
85
+ const isTransitioning = currentState !== nextState || undefined;
86
+ return [currentState, setState, nextState, isTransitioning];
87
+ };
88
+ exports.useLaggedState = useLaggedState;
@@ -0,0 +1,9 @@
1
+ import { MutableRefObject, RefObject } from 'react';
2
+ type Ref<E extends HTMLElement> = MutableRefObject<E> | RefObject<E>;
3
+ /**
4
+ *
5
+ * @param ref single or array of refs to check for click outside
6
+ * @param handler callback to run when clicked outside of the ref
7
+ */
8
+ declare const useOnClickOutside: <E extends HTMLElement>(ref: Ref<E> | Ref<E>[], handler: (event: globalThis.MouseEvent | globalThis.TouchEvent) => void) => void;
9
+ export { useOnClickOutside };
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useOnClickOutside = void 0;
4
+ const react_1 = require("react");
5
+ /**
6
+ *
7
+ * @param ref single or array of refs to check for click outside
8
+ * @param handler callback to run when clicked outside of the ref
9
+ */
10
+ const useOnClickOutside = (ref, handler) => {
11
+ const refs = Array.isArray(ref) ? ref : [ref];
12
+ // eslint-disable-next-line react-hooks/exhaustive-deps
13
+ const stableRefs = (0, react_1.useMemo)(() => refs, refs);
14
+ (0, react_1.useEffect)(() => {
15
+ const listener = (event) => {
16
+ const shouldTrigger = !stableRefs.some((r) => {
17
+ const node = r.current;
18
+ if (!node) {
19
+ return false;
20
+ }
21
+ return node.contains(event.target);
22
+ });
23
+ if (shouldTrigger) {
24
+ handler(event);
25
+ }
26
+ };
27
+ document.addEventListener('mousedown', listener);
28
+ document.addEventListener('touchstart', listener);
29
+ return () => {
30
+ document.removeEventListener('mousedown', listener);
31
+ document.removeEventListener('touchstart', listener);
32
+ };
33
+ }, [handler, stableRefs]);
34
+ };
35
+ exports.useOnClickOutside = useOnClickOutside;
@@ -0,0 +1,15 @@
1
+ import { RefObject } from 'react';
2
+ type ScrollAxis = 'horizontal' | 'vertical';
3
+ type AtState = {
4
+ start: boolean;
5
+ end: boolean;
6
+ };
7
+ export type ScrollEdgeDetectOptions<RefElm extends HTMLElement = HTMLElement> = {
8
+ axis: ScrollAxis;
9
+ /** **NOTE:** Make sure this function is stable to avoid unnecessary re-runs */
10
+ getElm?: (elm: RefElm | null | undefined) => HTMLElement | undefined | null | false;
11
+ /** Initial `at` status */
12
+ startAt?: AtState;
13
+ };
14
+ export declare const useScrollEdgeDetect: <RefElm extends HTMLElement = HTMLElement>(options: ScrollAxis | ScrollEdgeDetectOptions<RefElm>, scrollerRef?: RefObject<RefElm> | undefined) => [scrollElmRef: RefObject<RefElm>, at: AtState];
15
+ export {};
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useScrollEdgeDetect = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const react_1 = require("react");
6
+ const throttle_1 = tslib_1.__importDefault(require("@hugsmidjan/qj/throttle"));
7
+ const tolerance = 8; // px
8
+ const throttleMs = 100;
9
+ const useScrollEdgeDetect = (options, scrollerRef) => {
10
+ const opts = typeof options === 'string' ? { axis: options } : options;
11
+ const _scrollerRef = (0, react_1.useRef)(null);
12
+ scrollerRef = scrollerRef || _scrollerRef;
13
+ const [at, setAt] = (0, react_1.useState)(opts.startAt || { start: true, end: true });
14
+ const scrollerRefElm = scrollerRef.current;
15
+ const { getElm, axis } = opts;
16
+ (0, react_1.useEffect)(() => {
17
+ const scrollerElm = scrollerRefElm && getElm ? getElm(scrollerRefElm) : scrollerRefElm;
18
+ if (!(scrollerElm instanceof HTMLElement)) {
19
+ return;
20
+ }
21
+ const checkScroll = (0, throttle_1.default)(() => setAt((at) => {
22
+ let scroll, offsetSize, totalSize;
23
+ if (axis === 'horizontal') {
24
+ scroll = scrollerElm.scrollLeft;
25
+ offsetSize = scrollerElm.offsetWidth;
26
+ totalSize = scrollerElm.scrollWidth;
27
+ }
28
+ else {
29
+ scroll = scrollerElm.scrollTop;
30
+ offsetSize = scrollerElm.offsetHeight;
31
+ totalSize = scrollerElm.scrollHeight;
32
+ }
33
+ const start = scroll < tolerance;
34
+ const end = totalSize - (offsetSize + scroll) < tolerance;
35
+ if (at.start === start && at.end === end) {
36
+ return at;
37
+ }
38
+ return { start, end };
39
+ }), throttleMs);
40
+ scrollerElm.addEventListener('scroll', checkScroll);
41
+ window.addEventListener('resize', checkScroll);
42
+ checkScroll();
43
+ return () => {
44
+ scrollerElm.removeEventListener('scroll', checkScroll);
45
+ window.removeEventListener('resize', checkScroll);
46
+ };
47
+ }, [scrollerRefElm, getElm, axis]);
48
+ return [scrollerRef, at];
49
+ };
50
+ exports.useScrollEdgeDetect = useScrollEdgeDetect;
@@ -0,0 +1,2 @@
1
+ /** State variable that always snaps back to `undefined` after `duration` milliseconds. */
2
+ export declare const useShortState: <S>(initialState?: S | (() => S) | undefined, defaultDuration?: number) => readonly [S | undefined, (newState: S | (() => S), duration?: number) => void];
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useShortState = void 0;
4
+ const react_1 = require("react");
5
+ const DEFAULT_DURATION = 0;
6
+ // TODO: Add function signtures allowing either zero args, or 2.
7
+ /** State variable that always snaps back to `undefined` after `duration` milliseconds. */
8
+ const useShortState = (
9
+ /** Initial temporary state that then gets reverted back
10
+ * to `undefined` after `duration` milliseconds
11
+ * */
12
+ initialState,
13
+ /** Default duration, can be overridden on a case-by-case basis
14
+ * by passing a custom duration to the `setState` function
15
+ * */
16
+ defaultDuration = DEFAULT_DURATION) => {
17
+ const [state, _setState] = (0, react_1.useState)(initialState);
18
+ const timeout = (0, react_1.useRef)();
19
+ const cancelTimeout = () => {
20
+ timeout.current && clearTimeout(timeout.current);
21
+ };
22
+ const setState = (0, react_1.useRef)((newState, duration = defaultDuration) => {
23
+ _setState(newState);
24
+ cancelTimeout();
25
+ timeout.current = setTimeout(() => {
26
+ timeout.current = null;
27
+ _setState(undefined);
28
+ }, duration);
29
+ }).current;
30
+ (0, react_1.useEffect)(() => {
31
+ if (initialState !== undefined) {
32
+ setState(initialState, defaultDuration);
33
+ }
34
+ return cancelTimeout;
35
+ }, []);
36
+ return [state, setState];
37
+ };
38
+ exports.useShortState = useShortState;
package/utils.d.ts CHANGED
@@ -3,6 +3,7 @@ export * from './utils/browserSide.js';
3
3
  export * from './utils/config.js';
4
4
  export { HannaUIState, useHannaUIState } from './utils/HannaUIState.js';
5
5
  export * from './utils/useDidChange.js';
6
+ export * from './utils/useDomid.js';
6
7
  export * from './utils/useFormatMonitor.js';
7
8
  export * from './utils/useGetSVGtext.js';
8
9
  export * from './utils/useMenuToggling.js';
package/utils.js CHANGED
@@ -8,6 +8,7 @@ var HannaUIState_js_1 = require("./utils/HannaUIState.js");
8
8
  Object.defineProperty(exports, "HannaUIState", { enumerable: true, get: function () { return HannaUIState_js_1.HannaUIState; } });
9
9
  Object.defineProperty(exports, "useHannaUIState", { enumerable: true, get: function () { return HannaUIState_js_1.useHannaUIState; } });
10
10
  tslib_1.__exportStar(require("./utils/useDidChange.js"), exports);
11
+ tslib_1.__exportStar(require("./utils/useDomid.js"), exports);
11
12
  tslib_1.__exportStar(require("./utils/useFormatMonitor.js"), exports);
12
13
  tslib_1.__exportStar(require("./utils/useGetSVGtext.js"), exports);
13
14
  tslib_1.__exportStar(require("./utils/useMenuToggling.js"), exports);