@cloud-ru/ds-utils 1.0.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.
Files changed (216) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/LICENSE +201 -0
  3. package/README.md +222 -0
  4. package/dist/cjs/hooks/index.d.ts +13 -0
  5. package/dist/cjs/hooks/index.js +29 -0
  6. package/dist/cjs/hooks/useCopyToClipboard.d.ts +9 -0
  7. package/dist/cjs/hooks/useCopyToClipboard.js +27 -0
  8. package/dist/cjs/hooks/useDataPersist/index.d.ts +2 -0
  9. package/dist/cjs/hooks/useDataPersist/index.js +18 -0
  10. package/dist/cjs/hooks/useDataPersist/sources/baseSource.d.ts +11 -0
  11. package/dist/cjs/hooks/useDataPersist/sources/baseSource.js +35 -0
  12. package/dist/cjs/hooks/useDataPersist/sources/index.d.ts +3 -0
  13. package/dist/cjs/hooks/useDataPersist/sources/index.js +19 -0
  14. package/dist/cjs/hooks/useDataPersist/sources/localStorageSource.d.ts +5 -0
  15. package/dist/cjs/hooks/useDataPersist/sources/localStorageSource.js +19 -0
  16. package/dist/cjs/hooks/useDataPersist/sources/queryParamSource.d.ts +5 -0
  17. package/dist/cjs/hooks/useDataPersist/sources/queryParamSource.js +21 -0
  18. package/dist/cjs/hooks/useDataPersist/useDataPersist.d.ts +16 -0
  19. package/dist/cjs/hooks/useDataPersist/useDataPersist.js +26 -0
  20. package/dist/cjs/hooks/useDataPersist/useSource.d.ts +8 -0
  21. package/dist/cjs/hooks/useDataPersist/useSource.js +15 -0
  22. package/dist/cjs/hooks/useDebounce.d.ts +6 -0
  23. package/dist/cjs/hooks/useDebounce.js +25 -0
  24. package/dist/cjs/hooks/useDynamicList.d.ts +18 -0
  25. package/dist/cjs/hooks/useDynamicList.js +99 -0
  26. package/dist/cjs/hooks/useEventHandler.d.ts +6 -0
  27. package/dist/cjs/hooks/useEventHandler.js +23 -0
  28. package/dist/cjs/hooks/useHierarchicalSelection.d.ts +12 -0
  29. package/dist/cjs/hooks/useHierarchicalSelection.js +11 -0
  30. package/dist/cjs/hooks/useIsomorphicLayoutEffect.d.ts +7 -0
  31. package/dist/cjs/hooks/useIsomorphicLayoutEffect.js +12 -0
  32. package/dist/cjs/hooks/useLocalStorage.d.ts +1 -0
  33. package/dist/cjs/hooks/useLocalStorage.js +15 -0
  34. package/dist/cjs/hooks/useModalOpenState.d.ts +18 -0
  35. package/dist/cjs/hooks/useModalOpenState.js +27 -0
  36. package/dist/cjs/hooks/usePopstateSubscription.d.ts +1 -0
  37. package/dist/cjs/hooks/usePopstateSubscription.js +14 -0
  38. package/dist/cjs/hooks/useSwipeable.d.ts +21 -0
  39. package/dist/cjs/hooks/useSwipeable.js +50 -0
  40. package/dist/cjs/hooks/useUncontrolledProp.d.ts +21 -0
  41. package/dist/cjs/hooks/useUncontrolledProp.js +23 -0
  42. package/dist/cjs/hooks/useValueControl.d.ts +20 -0
  43. package/dist/cjs/hooks/useValueControl.js +20 -0
  44. package/dist/cjs/index.d.ts +3 -0
  45. package/dist/cjs/index.js +19 -0
  46. package/dist/cjs/package.json +3 -0
  47. package/dist/cjs/types/index.d.ts +2 -0
  48. package/dist/cjs/types/index.js +18 -0
  49. package/dist/cjs/types/polymorphic.d.ts +3 -0
  50. package/dist/cjs/types/polymorphic.js +2 -0
  51. package/dist/cjs/types/valueOf.d.ts +5 -0
  52. package/dist/cjs/types/valueOf.js +2 -0
  53. package/dist/cjs/utils/componentPropsProcessors.d.ts +19 -0
  54. package/dist/cjs/utils/componentPropsProcessors.js +43 -0
  55. package/dist/cjs/utils/copyToClipboard.d.ts +12 -0
  56. package/dist/cjs/utils/copyToClipboard.js +47 -0
  57. package/dist/cjs/utils/debounce.d.ts +11 -0
  58. package/dist/cjs/utils/debounce.js +24 -0
  59. package/dist/cjs/utils/hierarchicalSelection/applySelectionDiff.d.ts +4 -0
  60. package/dist/cjs/utils/hierarchicalSelection/applySelectionDiff.js +16 -0
  61. package/dist/cjs/utils/hierarchicalSelection/checkGroupSelection.d.ts +5 -0
  62. package/dist/cjs/utils/hierarchicalSelection/checkGroupSelection.js +18 -0
  63. package/dist/cjs/utils/hierarchicalSelection/createHierarchicalSelectionHandlers.d.ts +19 -0
  64. package/dist/cjs/utils/hierarchicalSelection/createHierarchicalSelectionHandlers.js +20 -0
  65. package/dist/cjs/utils/hierarchicalSelection/getNodeSelectionState.d.ts +8 -0
  66. package/dist/cjs/utils/hierarchicalSelection/getNodeSelectionState.js +22 -0
  67. package/dist/cjs/utils/hierarchicalSelection/index.d.ts +6 -0
  68. package/dist/cjs/utils/hierarchicalSelection/index.js +22 -0
  69. package/dist/cjs/utils/hierarchicalSelection/toggleHierarchicalSelection.d.ts +5 -0
  70. package/dist/cjs/utils/hierarchicalSelection/toggleHierarchicalSelection.js +69 -0
  71. package/dist/cjs/utils/hierarchicalSelection/types.d.ts +23 -0
  72. package/dist/cjs/utils/hierarchicalSelection/types.js +2 -0
  73. package/dist/cjs/utils/index.d.ts +10 -0
  74. package/dist/cjs/utils/index.js +26 -0
  75. package/dist/cjs/utils/innerRef.d.ts +26 -0
  76. package/dist/cjs/utils/innerRef.js +37 -0
  77. package/dist/cjs/utils/isBrowser.d.ts +6 -0
  78. package/dist/cjs/utils/isBrowser.js +12 -0
  79. package/dist/cjs/utils/keyboard.d.ts +41 -0
  80. package/dist/cjs/utils/keyboard.js +66 -0
  81. package/dist/cjs/utils/preventEvent.d.ts +4 -0
  82. package/dist/cjs/utils/preventEvent.js +15 -0
  83. package/dist/cjs/utils/stopEventPropagation.d.ts +2 -0
  84. package/dist/cjs/utils/stopEventPropagation.js +6 -0
  85. package/dist/cjs/utils/throttle.d.ts +11 -0
  86. package/dist/cjs/utils/throttle.js +43 -0
  87. package/dist/cjs/utils/typeGuards.d.ts +5 -0
  88. package/dist/cjs/utils/typeGuards.js +13 -0
  89. package/dist/esm/hooks/index.d.ts +13 -0
  90. package/dist/esm/hooks/index.js +13 -0
  91. package/dist/esm/hooks/useCopyToClipboard.d.ts +9 -0
  92. package/dist/esm/hooks/useCopyToClipboard.js +24 -0
  93. package/dist/esm/hooks/useDataPersist/index.d.ts +2 -0
  94. package/dist/esm/hooks/useDataPersist/index.js +2 -0
  95. package/dist/esm/hooks/useDataPersist/sources/baseSource.d.ts +11 -0
  96. package/dist/esm/hooks/useDataPersist/sources/baseSource.js +31 -0
  97. package/dist/esm/hooks/useDataPersist/sources/index.d.ts +3 -0
  98. package/dist/esm/hooks/useDataPersist/sources/index.js +3 -0
  99. package/dist/esm/hooks/useDataPersist/sources/localStorageSource.d.ts +5 -0
  100. package/dist/esm/hooks/useDataPersist/sources/localStorageSource.js +15 -0
  101. package/dist/esm/hooks/useDataPersist/sources/queryParamSource.d.ts +5 -0
  102. package/dist/esm/hooks/useDataPersist/sources/queryParamSource.js +17 -0
  103. package/dist/esm/hooks/useDataPersist/useDataPersist.d.ts +16 -0
  104. package/dist/esm/hooks/useDataPersist/useDataPersist.js +22 -0
  105. package/dist/esm/hooks/useDataPersist/useSource.d.ts +8 -0
  106. package/dist/esm/hooks/useDataPersist/useSource.js +11 -0
  107. package/dist/esm/hooks/useDebounce.d.ts +6 -0
  108. package/dist/esm/hooks/useDebounce.js +22 -0
  109. package/dist/esm/hooks/useDynamicList.d.ts +18 -0
  110. package/dist/esm/hooks/useDynamicList.js +96 -0
  111. package/dist/esm/hooks/useEventHandler.d.ts +6 -0
  112. package/dist/esm/hooks/useEventHandler.js +20 -0
  113. package/dist/esm/hooks/useHierarchicalSelection.d.ts +12 -0
  114. package/dist/esm/hooks/useHierarchicalSelection.js +8 -0
  115. package/dist/esm/hooks/useIsomorphicLayoutEffect.d.ts +7 -0
  116. package/dist/esm/hooks/useIsomorphicLayoutEffect.js +9 -0
  117. package/dist/esm/hooks/useLocalStorage.d.ts +1 -0
  118. package/dist/esm/hooks/useLocalStorage.js +12 -0
  119. package/dist/esm/hooks/useModalOpenState.d.ts +18 -0
  120. package/dist/esm/hooks/useModalOpenState.js +24 -0
  121. package/dist/esm/hooks/usePopstateSubscription.d.ts +1 -0
  122. package/dist/esm/hooks/usePopstateSubscription.js +11 -0
  123. package/dist/esm/hooks/useSwipeable.d.ts +21 -0
  124. package/dist/esm/hooks/useSwipeable.js +46 -0
  125. package/dist/esm/hooks/useUncontrolledProp.d.ts +21 -0
  126. package/dist/esm/hooks/useUncontrolledProp.js +20 -0
  127. package/dist/esm/hooks/useValueControl.d.ts +20 -0
  128. package/dist/esm/hooks/useValueControl.js +17 -0
  129. package/dist/esm/index.d.ts +3 -0
  130. package/dist/esm/index.js +3 -0
  131. package/dist/esm/types/index.d.ts +2 -0
  132. package/dist/esm/types/index.js +2 -0
  133. package/dist/esm/types/polymorphic.d.ts +3 -0
  134. package/dist/esm/types/polymorphic.js +1 -0
  135. package/dist/esm/types/valueOf.d.ts +5 -0
  136. package/dist/esm/types/valueOf.js +1 -0
  137. package/dist/esm/utils/componentPropsProcessors.d.ts +19 -0
  138. package/dist/esm/utils/componentPropsProcessors.js +38 -0
  139. package/dist/esm/utils/copyToClipboard.d.ts +12 -0
  140. package/dist/esm/utils/copyToClipboard.js +44 -0
  141. package/dist/esm/utils/debounce.d.ts +11 -0
  142. package/dist/esm/utils/debounce.js +21 -0
  143. package/dist/esm/utils/hierarchicalSelection/applySelectionDiff.d.ts +4 -0
  144. package/dist/esm/utils/hierarchicalSelection/applySelectionDiff.js +13 -0
  145. package/dist/esm/utils/hierarchicalSelection/checkGroupSelection.d.ts +5 -0
  146. package/dist/esm/utils/hierarchicalSelection/checkGroupSelection.js +15 -0
  147. package/dist/esm/utils/hierarchicalSelection/createHierarchicalSelectionHandlers.d.ts +19 -0
  148. package/dist/esm/utils/hierarchicalSelection/createHierarchicalSelectionHandlers.js +17 -0
  149. package/dist/esm/utils/hierarchicalSelection/getNodeSelectionState.d.ts +8 -0
  150. package/dist/esm/utils/hierarchicalSelection/getNodeSelectionState.js +19 -0
  151. package/dist/esm/utils/hierarchicalSelection/index.d.ts +6 -0
  152. package/dist/esm/utils/hierarchicalSelection/index.js +6 -0
  153. package/dist/esm/utils/hierarchicalSelection/toggleHierarchicalSelection.d.ts +5 -0
  154. package/dist/esm/utils/hierarchicalSelection/toggleHierarchicalSelection.js +66 -0
  155. package/dist/esm/utils/hierarchicalSelection/types.d.ts +23 -0
  156. package/dist/esm/utils/hierarchicalSelection/types.js +1 -0
  157. package/dist/esm/utils/index.d.ts +10 -0
  158. package/dist/esm/utils/index.js +10 -0
  159. package/dist/esm/utils/innerRef.d.ts +26 -0
  160. package/dist/esm/utils/innerRef.js +32 -0
  161. package/dist/esm/utils/isBrowser.d.ts +6 -0
  162. package/dist/esm/utils/isBrowser.js +9 -0
  163. package/dist/esm/utils/keyboard.d.ts +41 -0
  164. package/dist/esm/utils/keyboard.js +58 -0
  165. package/dist/esm/utils/preventEvent.d.ts +4 -0
  166. package/dist/esm/utils/preventEvent.js +10 -0
  167. package/dist/esm/utils/stopEventPropagation.d.ts +2 -0
  168. package/dist/esm/utils/stopEventPropagation.js +3 -0
  169. package/dist/esm/utils/throttle.d.ts +11 -0
  170. package/dist/esm/utils/throttle.js +40 -0
  171. package/dist/esm/utils/typeGuards.d.ts +5 -0
  172. package/dist/esm/utils/typeGuards.js +5 -0
  173. package/dist/tsconfig.cjs.tsbuildinfo +1 -0
  174. package/dist/tsconfig.esm.tsbuildinfo +1 -0
  175. package/package.json +38 -0
  176. package/src/hooks/index.ts +13 -0
  177. package/src/hooks/useCopyToClipboard.ts +38 -0
  178. package/src/hooks/useDataPersist/index.ts +2 -0
  179. package/src/hooks/useDataPersist/sources/baseSource.ts +36 -0
  180. package/src/hooks/useDataPersist/sources/index.ts +3 -0
  181. package/src/hooks/useDataPersist/sources/localStorageSource.ts +17 -0
  182. package/src/hooks/useDataPersist/sources/queryParamSource.ts +19 -0
  183. package/src/hooks/useDataPersist/useDataPersist.ts +51 -0
  184. package/src/hooks/useDataPersist/useSource.ts +23 -0
  185. package/src/hooks/useDebounce.ts +25 -0
  186. package/src/hooks/useDynamicList.ts +137 -0
  187. package/src/hooks/useEventHandler.ts +26 -0
  188. package/src/hooks/useHierarchicalSelection.ts +13 -0
  189. package/src/hooks/useIsomorphicLayoutEffect.ts +11 -0
  190. package/src/hooks/useLocalStorage.ts +19 -0
  191. package/src/hooks/useModalOpenState.ts +37 -0
  192. package/src/hooks/usePopstateSubscription.ts +14 -0
  193. package/src/hooks/useSwipeable.ts +85 -0
  194. package/src/hooks/useUncontrolledProp.ts +65 -0
  195. package/src/hooks/useValueControl.ts +41 -0
  196. package/src/index.ts +3 -0
  197. package/src/types/index.ts +2 -0
  198. package/src/types/polymorphic.ts +4 -0
  199. package/src/types/valueOf.ts +5 -0
  200. package/src/utils/componentPropsProcessors.ts +55 -0
  201. package/src/utils/copyToClipboard.ts +46 -0
  202. package/src/utils/debounce.ts +31 -0
  203. package/src/utils/hierarchicalSelection/applySelectionDiff.ts +20 -0
  204. package/src/utils/hierarchicalSelection/checkGroupSelection.ts +22 -0
  205. package/src/utils/hierarchicalSelection/createHierarchicalSelectionHandlers.ts +35 -0
  206. package/src/utils/hierarchicalSelection/getNodeSelectionState.ts +29 -0
  207. package/src/utils/hierarchicalSelection/index.ts +6 -0
  208. package/src/utils/hierarchicalSelection/toggleHierarchicalSelection.ts +109 -0
  209. package/src/utils/hierarchicalSelection/types.ts +26 -0
  210. package/src/utils/index.ts +10 -0
  211. package/src/utils/innerRef.ts +37 -0
  212. package/src/utils/isBrowser.ts +9 -0
  213. package/src/utils/keyboard.ts +68 -0
  214. package/src/utils/stopEventPropagation.ts +5 -0
  215. package/src/utils/throttle.ts +55 -0
  216. package/src/utils/typeGuards.ts +9 -0
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VERTICAL_ARROW_KEYS = exports.ARROW_KEYS = void 0;
4
+ exports.isArrowKey = isArrowKey;
5
+ exports.isVerticalArrowKey = isVerticalArrowKey;
6
+ exports.preventScrollOnArrowKeys = preventScrollOnArrowKeys;
7
+ exports.preventScrollOnVerticalArrows = preventScrollOnVerticalArrows;
8
+ exports.focusWithoutScroll = focusWithoutScroll;
9
+ /**
10
+ * Клавиши-стрелки. Внутри компонентов с собственной клавиатурной навигацией (списки, сетка
11
+ * календаря, roving-кнопки) их нативное поведение — прокрутка страницы — нужно гасить, иначе
12
+ * событие «улетает» за компонент и скроллит документ.
13
+ */
14
+ exports.ARROW_KEYS = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'];
15
+ /**
16
+ * Вертикальные стрелки. Для текстовых полей гасить нужно только их (горизонтальные `ArrowLeft` /
17
+ * `ArrowRight` двигают каретку — их перехватывать нельзя), тогда как в сетках/списках/roving-кнопках
18
+ * без текста гасятся все четыре (см. {@link ARROW_KEYS}).
19
+ */
20
+ exports.VERTICAL_ARROW_KEYS = ['ArrowUp', 'ArrowDown'];
21
+ /** `true`, если клавиша — одна из стрелок навигации. */
22
+ function isArrowKey(key) {
23
+ return exports.ARROW_KEYS.includes(key);
24
+ }
25
+ /** `true`, если клавиша — вертикальная стрелка (`ArrowUp` / `ArrowDown`). */
26
+ function isVerticalArrowKey(key) {
27
+ return exports.VERTICAL_ARROW_KEYS.includes(key);
28
+ }
29
+ /**
30
+ * Гасит нативную прокрутку страницы для клавиш-стрелок: если нажата стрелка — вызывает
31
+ * `preventDefault()` и возвращает `true`. Использовать в начале keydown-обработчика компонента,
32
+ * который сам обрабатывает стрелочную навигацию.
33
+ *
34
+ * @example
35
+ * const handleKeyDown = (e) => {
36
+ * preventScrollOnArrowKeys(e);
37
+ * // …собственная навигация
38
+ * };
39
+ */
40
+ function preventScrollOnArrowKeys(event) {
41
+ if (isArrowKey(event.key)) {
42
+ event.preventDefault();
43
+ return true;
44
+ }
45
+ return false;
46
+ }
47
+ /**
48
+ * Как {@link preventScrollOnArrowKeys}, но только для вертикальных стрелок (`ArrowUp` / `ArrowDown`).
49
+ * Для текстовых полей: гасит прокрутку страницы по Up/Down, не трогая горизонтальные стрелки —
50
+ * каретка по тексту продолжает двигаться.
51
+ */
52
+ function preventScrollOnVerticalArrows(event) {
53
+ if (isVerticalArrowKey(event.key)) {
54
+ event.preventDefault();
55
+ return true;
56
+ }
57
+ return false;
58
+ }
59
+ /**
60
+ * Программный фокус без авто-прокрутки страницы к элементу (`focus({ preventScroll: true })`).
61
+ * Применять при перемещении фокуса по клавиатуре внутри уже видимого компонента (ячейка
62
+ * календаря, элемент списка), иначе браузер скроллит документ к новому фокусу.
63
+ */
64
+ function focusWithoutScroll(element) {
65
+ element?.focus({ preventScroll: true });
66
+ }
@@ -0,0 +1,4 @@
1
+ import { KeyboardEvent, MouseEvent } from 'react';
2
+ export declare function stopEventPropagation(event: MouseEvent<HTMLElement> | KeyboardEvent<HTMLElement>): void;
3
+ export declare function preventEventDefault(event: MouseEvent<HTMLElement> | KeyboardEvent<HTMLElement>): void;
4
+ export declare function preventEventDefaultAndPropagation(event: MouseEvent<HTMLElement> | KeyboardEvent<HTMLElement>): void;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.stopEventPropagation = stopEventPropagation;
4
+ exports.preventEventDefault = preventEventDefault;
5
+ exports.preventEventDefaultAndPropagation = preventEventDefaultAndPropagation;
6
+ function stopEventPropagation(event) {
7
+ event.stopPropagation();
8
+ }
9
+ function preventEventDefault(event) {
10
+ event.preventDefault();
11
+ }
12
+ function preventEventDefaultAndPropagation(event) {
13
+ stopEventPropagation(event);
14
+ preventEventDefault(event);
15
+ }
@@ -0,0 +1,2 @@
1
+ import { KeyboardEvent, MouseEvent } from 'react';
2
+ export declare function stopEventPropagation(event: MouseEvent<HTMLElement> | KeyboardEvent<HTMLElement>): void;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.stopEventPropagation = stopEventPropagation;
4
+ function stopEventPropagation(event) {
5
+ event.stopPropagation();
6
+ }
@@ -0,0 +1,11 @@
1
+ /** Функция-обёртка `throttle`: вызывается как исходная, плюс умеет отменить отложенный вызов. */
2
+ export type Throttled<TArgs extends unknown[]> = {
3
+ (...args: TArgs): void;
4
+ /** Отменяет запланированный вызов по заднему фронту. */
5
+ cancel(): void;
6
+ };
7
+ /**
8
+ * Ограничивает частоту вызовов `callback` до одного раза в `wait` мс.
9
+ * Срабатывает по обоим фронтам: первый вызов проходит сразу, последний в окне — по его истечении.
10
+ */
11
+ export declare function throttle<TArgs extends unknown[]>(callback: (...args: TArgs) => void, wait?: number): Throttled<TArgs>;
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.throttle = throttle;
4
+ /**
5
+ * Ограничивает частоту вызовов `callback` до одного раза в `wait` мс.
6
+ * Срабатывает по обоим фронтам: первый вызов проходит сразу, последний в окне — по его истечении.
7
+ */
8
+ function throttle(callback, wait = 0) {
9
+ let lastInvokeTime = 0;
10
+ let timer;
11
+ let pendingArgs;
12
+ const invoke = (args) => {
13
+ lastInvokeTime = Date.now();
14
+ callback(...args);
15
+ };
16
+ const cancel = () => {
17
+ if (timer !== undefined) {
18
+ clearTimeout(timer);
19
+ timer = undefined;
20
+ }
21
+ pendingArgs = undefined;
22
+ };
23
+ const throttled = (...args) => {
24
+ const remaining = wait - (Date.now() - lastInvokeTime);
25
+ if (remaining <= 0) {
26
+ cancel();
27
+ invoke(args);
28
+ return;
29
+ }
30
+ pendingArgs = args;
31
+ if (timer === undefined) {
32
+ timer = setTimeout(() => {
33
+ timer = undefined;
34
+ if (pendingArgs !== undefined) {
35
+ const args = pendingArgs;
36
+ pendingArgs = undefined;
37
+ invoke(args);
38
+ }
39
+ }, remaining);
40
+ }
41
+ };
42
+ return Object.assign(throttled, { cancel });
43
+ }
@@ -0,0 +1,5 @@
1
+ export declare const isNil: (value: unknown) => value is undefined | null;
2
+ export declare const isBoolean: (value: unknown) => value is boolean;
3
+ export declare const isString: (value: unknown) => value is string;
4
+ export declare const isArray: <T>(value: unknown) => value is Array<T>;
5
+ export declare const isObject: (value: unknown) => value is object;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isObject = exports.isArray = exports.isString = exports.isBoolean = exports.isNil = void 0;
4
+ const isNil = (value) => value === undefined || value === null;
5
+ exports.isNil = isNil;
6
+ const isBoolean = (value) => typeof value === 'boolean';
7
+ exports.isBoolean = isBoolean;
8
+ const isString = (value) => typeof value === 'string';
9
+ exports.isString = isString;
10
+ const isArray = (value) => Array.isArray(value);
11
+ exports.isArray = isArray;
12
+ const isObject = (value) => typeof value === 'object' && !(0, exports.isArray)(value);
13
+ exports.isObject = isObject;
@@ -0,0 +1,13 @@
1
+ export * from './useDebounce.js';
2
+ export * from './useDynamicList.js';
3
+ export * from './useEventHandler.js';
4
+ export * from './useHierarchicalSelection.js';
5
+ export * from './useIsomorphicLayoutEffect.js';
6
+ export * from './useSwipeable.js';
7
+ export * from './useUncontrolledProp.js';
8
+ export * from './useValueControl.js';
9
+ export * from './useDataPersist/index.js';
10
+ export * from './usePopstateSubscription.js';
11
+ export * from './useModalOpenState.js';
12
+ export * from './useCopyToClipboard.js';
13
+ export * from './useLocalStorage.js';
@@ -0,0 +1,13 @@
1
+ export * from './useDebounce.js';
2
+ export * from './useDynamicList.js';
3
+ export * from './useEventHandler.js';
4
+ export * from './useHierarchicalSelection.js';
5
+ export * from './useIsomorphicLayoutEffect.js';
6
+ export * from './useSwipeable.js';
7
+ export * from './useUncontrolledProp.js';
8
+ export * from './useValueControl.js';
9
+ export * from './useDataPersist/index.js';
10
+ export * from './usePopstateSubscription.js';
11
+ export * from './useModalOpenState.js';
12
+ export * from './useCopyToClipboard.js';
13
+ export * from './useLocalStorage.js';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Хук для копирования значения в буфер обмена и визуального подтверждения действия.
3
+ * @function React hook
4
+ * @param durationMs - Продолжительность отображения иконки подтверждения в миллисекундах. По-умолчанию 1000 мс.
5
+ */
6
+ export declare function useCopyToClipboard(durationMs?: number): {
7
+ isChecked: boolean;
8
+ copy: (value: string) => void;
9
+ };
@@ -0,0 +1,24 @@
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
+ import { copyToClipboard } from '../utils/copyToClipboard.js';
3
+ const DEFAULT_CHECKED_DURATION_MS = 1000;
4
+ /**
5
+ * Хук для копирования значения в буфер обмена и визуального подтверждения действия.
6
+ * @function React hook
7
+ * @param durationMs - Продолжительность отображения иконки подтверждения в миллисекундах. По-умолчанию 1000 мс.
8
+ */
9
+ export function useCopyToClipboard(durationMs = DEFAULT_CHECKED_DURATION_MS) {
10
+ const [isChecked, setIsChecked] = useState(false);
11
+ const timerRef = useRef();
12
+ const copy = useCallback((value) => {
13
+ if (!value)
14
+ return;
15
+ copyToClipboard(value).catch(() => undefined);
16
+ setIsChecked(true);
17
+ clearTimeout(timerRef.current);
18
+ timerRef.current = setTimeout(() => setIsChecked(false), durationMs);
19
+ }, [durationMs]);
20
+ useEffect(() => () => {
21
+ clearTimeout(timerRef.current);
22
+ }, []);
23
+ return { isChecked, copy };
24
+ }
@@ -0,0 +1,2 @@
1
+ export * from './useDataPersist.js';
2
+ export * from './sources/index.js';
@@ -0,0 +1,2 @@
1
+ export * from './useDataPersist.js';
2
+ export * from './sources/index.js';
@@ -0,0 +1,11 @@
1
+ export declare abstract class BaseSource<TData> {
2
+ filterKey: string;
3
+ private validate;
4
+ protected parser: (value: string) => TData;
5
+ protected serializer: (value: TData) => string;
6
+ protected abstract getFromSource(): string;
7
+ protected abstract setToSource(value: string): void;
8
+ constructor(filterKey: string, validate: (value: unknown) => value is TData, parser?: (value: string) => TData, serializer?: (value: TData) => string);
9
+ getData: () => TData | null;
10
+ setData: (filter: TData) => void;
11
+ }
@@ -0,0 +1,31 @@
1
+ export class BaseSource {
2
+ filterKey;
3
+ validate;
4
+ parser = JSON.parse;
5
+ serializer = JSON.stringify;
6
+ constructor(filterKey, validate, parser, serializer) {
7
+ this.filterKey = filterKey;
8
+ this.validate = validate;
9
+ if (parser) {
10
+ this.parser = parser;
11
+ }
12
+ if (serializer) {
13
+ this.serializer = serializer;
14
+ }
15
+ }
16
+ getData = () => {
17
+ try {
18
+ const filterFromQueryParams = this.getFromSource();
19
+ const parsedValue = filterFromQueryParams ? this.parser(filterFromQueryParams) : null;
20
+ const isValid = parsedValue && this.validate(parsedValue);
21
+ return isValid ? parsedValue : null;
22
+ }
23
+ catch {
24
+ return null;
25
+ }
26
+ };
27
+ setData = (filter) => {
28
+ const encodedValue = this.serializer(filter);
29
+ this.setToSource(encodedValue);
30
+ };
31
+ }
@@ -0,0 +1,3 @@
1
+ export * from './queryParamSource.js';
2
+ export * from './localStorageSource.js';
3
+ export * from './baseSource.js';
@@ -0,0 +1,3 @@
1
+ export * from './queryParamSource.js';
2
+ export * from './localStorageSource.js';
3
+ export * from './baseSource.js';
@@ -0,0 +1,5 @@
1
+ import { BaseSource } from './baseSource.js';
2
+ export declare class LocalStorageSource<TData> extends BaseSource<TData> {
3
+ getFromSource(): string;
4
+ setToSource(value: string): void;
5
+ }
@@ -0,0 +1,15 @@
1
+ import { isBrowser } from '../../../utils/index.js';
2
+ import { BaseSource } from './baseSource.js';
3
+ export class LocalStorageSource extends BaseSource {
4
+ getFromSource() {
5
+ if (isBrowser()) {
6
+ return localStorage.getItem(this.filterKey) || '';
7
+ }
8
+ return '';
9
+ }
10
+ setToSource(value) {
11
+ if (isBrowser()) {
12
+ localStorage.setItem(this.filterKey, value);
13
+ }
14
+ }
15
+ }
@@ -0,0 +1,5 @@
1
+ import { BaseSource } from './baseSource.js';
2
+ export declare class QueryParamSource<TData> extends BaseSource<TData> {
3
+ getFromSource(): string;
4
+ setToSource(value: string): void;
5
+ }
@@ -0,0 +1,17 @@
1
+ import { isBrowser } from '../../../utils/index.js';
2
+ import { BaseSource } from './baseSource.js';
3
+ export class QueryParamSource extends BaseSource {
4
+ getFromSource() {
5
+ if (isBrowser()) {
6
+ return new URL(window.location.href).searchParams.get(this.filterKey) || '';
7
+ }
8
+ return '';
9
+ }
10
+ setToSource(value) {
11
+ if (isBrowser()) {
12
+ const url = new URL(window.location.href);
13
+ url.searchParams.set(this.filterKey, value);
14
+ history.replaceState({}, '', url);
15
+ }
16
+ }
17
+ }
@@ -0,0 +1,16 @@
1
+ import { type StateProps } from './useSource.js';
2
+ export type DataPersistOptions<T> = {
3
+ queryKey: string;
4
+ localStorageKey: string;
5
+ validateData(value: unknown): value is T;
6
+ };
7
+ type DataPersistProps<TData> = Omit<StateProps<TData>, 'source'> & {
8
+ options?: DataPersistOptions<TData>;
9
+ parser?(jsonData: string): TData;
10
+ serializer?(data: TData): string;
11
+ };
12
+ export declare const useDataPersist: <TData>({ options, parser, serializer }: DataPersistProps<TData>) => {
13
+ getDefaultData: () => NonNullable<TData> | undefined;
14
+ setDataToStorages: (data: TData) => void;
15
+ };
16
+ export {};
@@ -0,0 +1,22 @@
1
+ import { useCallback, useMemo } from 'react';
2
+ import { LocalStorageSource, QueryParamSource } from './sources/index.js';
3
+ import { useSource } from './useSource.js';
4
+ export const useDataPersist = ({ options, parser, serializer }) => {
5
+ const querySource = useMemo(() => options ? new QueryParamSource(options.queryKey, options.validateData, parser, serializer) : undefined, [options, parser, serializer]);
6
+ const localStorageSource = useMemo(() => (options ? new LocalStorageSource(options.localStorageKey, options.validateData) : undefined), [options]);
7
+ const { getData: getLocalStorageData, setData: setLocalStorageData } = useSource({
8
+ source: localStorageSource,
9
+ });
10
+ const { getData: getQueryParamsData, setData: setQueryParamsData } = useSource({
11
+ source: querySource,
12
+ });
13
+ const setDataToStorages = useCallback((data) => {
14
+ setLocalStorageData(data);
15
+ setQueryParamsData(data);
16
+ }, [setLocalStorageData, setQueryParamsData]);
17
+ const getDefaultData = useCallback(() => {
18
+ const queryData = getQueryParamsData();
19
+ return queryData ?? getLocalStorageData();
20
+ }, [getLocalStorageData, getQueryParamsData]);
21
+ return { getDefaultData, setDataToStorages };
22
+ };
@@ -0,0 +1,8 @@
1
+ import { BaseSource } from './sources/index.js';
2
+ export type StateProps<T> = {
3
+ source?: BaseSource<T>;
4
+ };
5
+ export declare const useSource: <T>({ source }: StateProps<T>) => {
6
+ getData: () => NonNullable<T> | undefined;
7
+ setData: (data: T) => void;
8
+ };
@@ -0,0 +1,11 @@
1
+ import { useCallback } from 'react';
2
+ export const useSource = ({ source }) => {
3
+ const setData = useCallback((data) => {
4
+ if (!source) {
5
+ return;
6
+ }
7
+ source.setData(data);
8
+ }, [source]);
9
+ const getData = useCallback(() => source?.getData() ?? undefined, [source]);
10
+ return { getData, setData };
11
+ };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Хук задерживает выполнение функции или обновление значения до тех пор,
3
+ * пока не пройдет определенный период времени без новых вызовов.
4
+ * @function React hook
5
+ */
6
+ export declare function useDebounce(callback: () => void, timeout?: number): () => void;
@@ -0,0 +1,22 @@
1
+ import { useRef } from 'react';
2
+ import { isBrowser } from '../utils/index.js';
3
+ import { useEventHandler } from './useEventHandler.js';
4
+ /**
5
+ * Хук задерживает выполнение функции или обновление значения до тех пор,
6
+ * пока не пройдет определенный период времени без новых вызовов.
7
+ * @function React hook
8
+ */
9
+ export function useDebounce(callback, timeout = 0) {
10
+ const timeStampRef = useRef(0);
11
+ const timerIdRef = useRef(null);
12
+ return useEventHandler(() => {
13
+ isBrowser() &&
14
+ requestAnimationFrame(timestamp => {
15
+ if (timestamp < timeStampRef.current && timerIdRef.current !== null) {
16
+ clearTimeout(timerIdRef.current);
17
+ }
18
+ timerIdRef.current = setTimeout(callback, timeout);
19
+ timeStampRef.current = timestamp + timeout;
20
+ });
21
+ });
22
+ }
@@ -0,0 +1,18 @@
1
+ import { RefObject } from 'react';
2
+ type UseDynamicListProps<T extends object> = {
3
+ items: T[];
4
+ resizingContainerRef?: RefObject<HTMLDivElement>;
5
+ parentContainerRef: RefObject<HTMLDivElement>;
6
+ maxVisibleItems?: number;
7
+ };
8
+ type UseDynamicListReturnType<T extends object> = {
9
+ visibleItems: T[];
10
+ hiddenItems: T[];
11
+ };
12
+ /**
13
+ * Хук позволяет распределять элементы списка на две группы: видимые и невидимые,
14
+ * в зависимости от ширины контейнера
15
+ * @function React hook
16
+ */
17
+ export declare function useDynamicList<T extends object>({ parentContainerRef, resizingContainerRef, items, maxVisibleItems, }: UseDynamicListProps<T>): UseDynamicListReturnType<T>;
18
+ export {};
@@ -0,0 +1,96 @@
1
+ import { useEffect, useRef, useState } from 'react';
2
+ import { useEventHandler } from './useEventHandler.js';
3
+ import { useLayoutEffect } from './useIsomorphicLayoutEffect.js';
4
+ /**
5
+ * Хук позволяет распределять элементы списка на две группы: видимые и невидимые,
6
+ * в зависимости от ширины контейнера
7
+ * @function React hook
8
+ */
9
+ export function useDynamicList({ parentContainerRef, resizingContainerRef = parentContainerRef, items, maxVisibleItems = Infinity, }) {
10
+ const adjustAmount = (value) => Math.min(maxVisibleItems, value);
11
+ const [visibleItemsAmount, setVisibleItemsAmount] = useState(adjustAmount(items.length));
12
+ const [width, setWidth] = useState(Infinity);
13
+ const widthRef = useRef(width);
14
+ const handleVisibleItemsAmount = param => {
15
+ if (typeof param === 'number') {
16
+ setVisibleItemsAmount(adjustAmount(param));
17
+ }
18
+ else {
19
+ setVisibleItemsAmount(v => param(adjustAmount(v)));
20
+ }
21
+ };
22
+ const tryHidingItem = useEventHandler(() => {
23
+ const container = parentContainerRef.current;
24
+ if (container && container.scrollWidth - container.offsetWidth > 0) {
25
+ const itemToHide = items[visibleItemsAmount - 1];
26
+ if (itemToHide) {
27
+ handleVisibleItemsAmount(value => value - 1);
28
+ }
29
+ }
30
+ });
31
+ const tryShowingItem = useEventHandler(() => {
32
+ const itemToShow = items[visibleItemsAmount];
33
+ if (itemToShow) {
34
+ handleVisibleItemsAmount(value => value + 1);
35
+ }
36
+ });
37
+ const toggleItemWidth = useEventHandler(({ changedWidth, initialWidth }) => {
38
+ if (changedWidth > initialWidth) {
39
+ //try to add extra item
40
+ if (visibleItemsAmount < maxVisibleItems) {
41
+ tryShowingItem();
42
+ }
43
+ }
44
+ else if (changedWidth < initialWidth) {
45
+ // check if item should be hidden
46
+ tryHidingItem();
47
+ }
48
+ });
49
+ useEffect(() => {
50
+ const listener = () => {
51
+ tryHidingItem();
52
+ if (parentContainerRef.current) {
53
+ setWidth(parentContainerRef.current.scrollWidth);
54
+ }
55
+ };
56
+ document.fonts.addEventListener('loadingdone', listener);
57
+ return () => document.fonts.removeEventListener('loadingdone', listener);
58
+ }, [parentContainerRef, tryHidingItem]);
59
+ useEffect(() => {
60
+ const container = resizingContainerRef.current;
61
+ if (container) {
62
+ const observer = new ResizeObserver(entities => entities.forEach(entity => {
63
+ if (entity.target === container) {
64
+ const newWidth = entity.contentBoxSize[0]?.inlineSize;
65
+ if (newWidth !== undefined) {
66
+ setWidth(Math.floor(newWidth));
67
+ }
68
+ }
69
+ }));
70
+ observer.observe(container);
71
+ return () => observer.disconnect();
72
+ }
73
+ }, [resizingContainerRef]);
74
+ useLayoutEffect(() => {
75
+ if (parentContainerRef.current) {
76
+ toggleItemWidth({
77
+ initialWidth: parentContainerRef.current.scrollWidth,
78
+ changedWidth: widthRef.current,
79
+ });
80
+ }
81
+ }, [items, parentContainerRef, toggleItemWidth]);
82
+ useLayoutEffect(() => {
83
+ toggleItemWidth({
84
+ initialWidth: widthRef.current,
85
+ changedWidth: width,
86
+ });
87
+ widthRef.current = width;
88
+ }, [width, toggleItemWidth]);
89
+ useLayoutEffect(() => {
90
+ tryHidingItem();
91
+ }, [tryHidingItem, visibleItemsAmount]);
92
+ return {
93
+ visibleItems: items.slice(0, visibleItemsAmount),
94
+ hiddenItems: items.slice(visibleItemsAmount),
95
+ };
96
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Хук для создания обработчика событий с неизменной функцией.
3
+ * @function React hook
4
+ * @param handler коллбек события, без параметров
5
+ */
6
+ export declare function useEventHandler<T extends (...args: never[]) => unknown>(handler: T): T;
@@ -0,0 +1,20 @@
1
+ import { useCallback, useRef } from 'react';
2
+ import { useLayoutEffect } from './useIsomorphicLayoutEffect.js';
3
+ /**
4
+ * Хук для создания обработчика событий с неизменной функцией.
5
+ * @function React hook
6
+ * @param handler коллбек события, без параметров
7
+ */
8
+ export function useEventHandler(handler) {
9
+ const handlerRef = useRef(null);
10
+ useLayoutEffect(() => {
11
+ handlerRef.current = handler;
12
+ });
13
+ return useCallback((...args) => {
14
+ const handler = handlerRef.current;
15
+ if (handler === null) {
16
+ console.warn('The event handler cannot be called during render');
17
+ }
18
+ return handler?.(...args);
19
+ }, []);
20
+ }
@@ -0,0 +1,12 @@
1
+ import { HierarchicalSelectionConfig } from '../utils/hierarchicalSelection/createHierarchicalSelectionHandlers.js';
2
+ /**
3
+ * Возвращает стабильные обработчики иерархического выбора с единым `includeParentsInValue`.
4
+ */
5
+ export declare function useHierarchicalSelection({ includeParentsInValue }: HierarchicalSelectionConfig): {
6
+ getSelectionState<TId extends string | number>(params: import("..").HierarchicalSelectionNodeParams<TId>): {
7
+ checked: boolean;
8
+ indeterminate: boolean;
9
+ };
10
+ toggleSelection<TId extends string | number>(params: import("..").HierarchicalSelectionToggleParams<TId>): TId[];
11
+ checkGroupSelection: typeof import("..").checkGroupSelection;
12
+ };
@@ -0,0 +1,8 @@
1
+ import { useMemo } from 'react';
2
+ import { createHierarchicalSelectionHandlers, } from '../utils/hierarchicalSelection/createHierarchicalSelectionHandlers.js';
3
+ /**
4
+ * Возвращает стабильные обработчики иерархического выбора с единым `includeParentsInValue`.
5
+ */
6
+ export function useHierarchicalSelection({ includeParentsInValue }) {
7
+ return useMemo(() => createHierarchicalSelectionHandlers({ includeParentsInValue }), [includeParentsInValue]);
8
+ }
@@ -0,0 +1,7 @@
1
+ import { useLayoutEffect as useLayoutEffectInternal } from 'react';
2
+ /**
3
+ * Хук используется вместо стандартного useLayoutEffect-а,
4
+ * нужен для корректной работы SSR
5
+ * @function React hook
6
+ */
7
+ export declare const useLayoutEffect: typeof useLayoutEffectInternal;
@@ -0,0 +1,9 @@
1
+ // eslint-disable-next-line no-restricted-imports
2
+ import { useEffect, useLayoutEffect as useLayoutEffectInternal } from 'react';
3
+ import { isBrowser } from '../utils/index.js';
4
+ /**
5
+ * Хук используется вместо стандартного useLayoutEffect-а,
6
+ * нужен для корректной работы SSR
7
+ * @function React hook
8
+ */
9
+ export const useLayoutEffect = isBrowser() ? useLayoutEffectInternal : useEffect;
@@ -0,0 +1 @@
1
+ export declare function useLocalStorage<T extends string = string>(key: string, defaultValue: T): [T, (newValue: T) => void];