@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,19 @@
1
+ import { AriaAttributes } from 'react';
2
+ export type WithSupportProps<T> = {
3
+ 'data-test-id'?: string;
4
+ } & AriaAttributes & T;
5
+ /**
6
+ * Функция для исключения вспомогательных свойств (data-test-id & aria-*)
7
+ * @function helper
8
+ */
9
+ export declare function excludeSupportProps<T extends Record<string, unknown>>(props: T): {};
10
+ /**
11
+ * Функция для отбора свойства data-test-id
12
+ * @function helper
13
+ */
14
+ export declare function extractDataTestProps<T extends Record<string, unknown>>(props: T): Record<string, unknown>;
15
+ /**
16
+ * Функция для отбора вспомогательных свойств (data-test-id & aria-*)
17
+ * @function helper
18
+ */
19
+ export declare function extractSupportProps<T extends Record<string, unknown>>(props: T): Record<string, unknown>;
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.excludeSupportProps = excludeSupportProps;
4
+ exports.extractDataTestProps = extractDataTestProps;
5
+ exports.extractSupportProps = extractSupportProps;
6
+ const DATA_TEST_REGEXP = /^data-test-/;
7
+ const DATA_AND_ARIA_REGEXP = /^(data|aria)-/;
8
+ function excludeProps(props, regexp) {
9
+ return Object.keys(props)
10
+ .filter(prop => !prop.match(regexp))
11
+ .reduce((nextProps, prop) => ({
12
+ ...nextProps,
13
+ [prop]: props[prop],
14
+ }), {});
15
+ }
16
+ function extractProps(props, regex) {
17
+ return Object.keys(props).reduce((nextProps, prop) => {
18
+ if (prop.match(regex))
19
+ nextProps[prop] = props[prop];
20
+ return nextProps;
21
+ }, {});
22
+ }
23
+ /**
24
+ * Функция для исключения вспомогательных свойств (data-test-id & aria-*)
25
+ * @function helper
26
+ */
27
+ function excludeSupportProps(props) {
28
+ return excludeProps(props, DATA_AND_ARIA_REGEXP);
29
+ }
30
+ /**
31
+ * Функция для отбора свойства data-test-id
32
+ * @function helper
33
+ */
34
+ function extractDataTestProps(props) {
35
+ return extractProps(props, DATA_TEST_REGEXP);
36
+ }
37
+ /**
38
+ * Функция для отбора вспомогательных свойств (data-test-id & aria-*)
39
+ * @function helper
40
+ */
41
+ function extractSupportProps(props) {
42
+ return extractProps(props, DATA_AND_ARIA_REGEXP);
43
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Копирует текст в буфер обмена. Сначала пробует Async Clipboard API, при отказе
3
+ * (нет разрешения, insecure-context) падает на `execCommand('copy')` со скрытым textarea.
4
+ *
5
+ * Возвращает `true`, если запись удалась — по этому признаку поля и кнопки показывают
6
+ * индикатор «скопировано».
7
+ *
8
+ * Проверки `isBrowser()` продублированы внутри веток намеренно: правило
9
+ * `@cloud-ru/ssr-safe-react/domApi` требует, чтобы доступ к `navigator`/`document` стоял
10
+ * непосредственно внутри такого условия, а ранний выход оно не учитывает.
11
+ */
12
+ export declare function copyToClipboard(value: string): Promise<boolean>;
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.copyToClipboard = copyToClipboard;
4
+ const isBrowser_1 = require("./isBrowser");
5
+ /**
6
+ * Копирует текст в буфер обмена. Сначала пробует Async Clipboard API, при отказе
7
+ * (нет разрешения, insecure-context) падает на `execCommand('copy')` со скрытым textarea.
8
+ *
9
+ * Возвращает `true`, если запись удалась — по этому признаку поля и кнопки показывают
10
+ * индикатор «скопировано».
11
+ *
12
+ * Проверки `isBrowser()` продублированы внутри веток намеренно: правило
13
+ * `@cloud-ru/ssr-safe-react/domApi` требует, чтобы доступ к `navigator`/`document` стоял
14
+ * непосредственно внутри такого условия, а ранний выход оно не учитывает.
15
+ */
16
+ async function copyToClipboard(value) {
17
+ if (!(0, isBrowser_1.isBrowser)())
18
+ return false;
19
+ if ((0, isBrowser_1.isBrowser)() && navigator.clipboard?.writeText) {
20
+ try {
21
+ await navigator.clipboard.writeText(value);
22
+ return true;
23
+ }
24
+ catch {
25
+ // fall through to execCommand fallback
26
+ }
27
+ }
28
+ if ((0, isBrowser_1.isBrowser)()) {
29
+ const textarea = document.createElement('textarea');
30
+ textarea.value = value;
31
+ textarea.setAttribute('readonly', '');
32
+ textarea.style.position = 'fixed';
33
+ textarea.style.opacity = '0';
34
+ document.body.appendChild(textarea);
35
+ textarea.select();
36
+ try {
37
+ return document.execCommand('copy');
38
+ }
39
+ catch {
40
+ return false;
41
+ }
42
+ finally {
43
+ document.body.removeChild(textarea);
44
+ }
45
+ }
46
+ return false;
47
+ }
@@ -0,0 +1,11 @@
1
+ /** Функция-обёртка `debounce`: вызывается как исходная, плюс умеет отменить отложенный вызов. */
2
+ export type Debounced<TArgs extends unknown[]> = {
3
+ (...args: TArgs): void;
4
+ /** Отменяет запланированный вызов. Нужен в cleanup эффекта, чтобы не стрелять после размонтирования. */
5
+ cancel(): void;
6
+ };
7
+ /**
8
+ * Откладывает вызов `callback` на `wait` мс, перезапуская таймер на каждом новом вызове.
9
+ * Срабатывает по заднему фронту — с аргументами последнего вызова.
10
+ */
11
+ export declare function debounce<TArgs extends unknown[]>(callback: (...args: TArgs) => void, wait?: number): Debounced<TArgs>;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.debounce = debounce;
4
+ /**
5
+ * Откладывает вызов `callback` на `wait` мс, перезапуская таймер на каждом новом вызове.
6
+ * Срабатывает по заднему фронту — с аргументами последнего вызова.
7
+ */
8
+ function debounce(callback, wait = 0) {
9
+ let timer;
10
+ const cancel = () => {
11
+ if (timer !== undefined) {
12
+ clearTimeout(timer);
13
+ timer = undefined;
14
+ }
15
+ };
16
+ const debounced = (...args) => {
17
+ cancel();
18
+ timer = setTimeout(() => {
19
+ timer = undefined;
20
+ callback(...args);
21
+ }, wait);
22
+ };
23
+ return Object.assign(debounced, { cancel });
24
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Применяет diff к набору выбранных id.
3
+ */
4
+ export declare function applySelectionDiff<TId extends string | number>(selectedIds: TId[], added: TId[], removed: TId[]): TId[];
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.applySelectionDiff = applySelectionDiff;
4
+ /**
5
+ * Применяет diff к набору выбранных id.
6
+ */
7
+ function applySelectionDiff(selectedIds, added, removed) {
8
+ const result = new Set(selectedIds);
9
+ for (const id of added) {
10
+ result.add(id);
11
+ }
12
+ for (const id of removed) {
13
+ result.delete(id);
14
+ }
15
+ return [...result];
16
+ }
@@ -0,0 +1,5 @@
1
+ import { GroupSelectionState } from './types';
2
+ /**
3
+ * Проверяет состояние выбора группы по flat-списку дочерних id.
4
+ */
5
+ export declare function checkGroupSelection<TId extends string | number>(childIds: TId[], selectedIds: TId[]): GroupSelectionState;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkGroupSelection = checkGroupSelection;
4
+ /**
5
+ * Проверяет состояние выбора группы по flat-списку дочерних id.
6
+ */
7
+ function checkGroupSelection(childIds, selectedIds) {
8
+ if (!childIds.length) {
9
+ return { allSelected: false, someSelected: false };
10
+ }
11
+ const selectedCount = childIds.filter(id => selectedIds.includes(id)).length;
12
+ const someSelected = selectedCount > 0;
13
+ const allSelected = someSelected && selectedCount === childIds.length;
14
+ return {
15
+ allSelected,
16
+ someSelected: !allSelected && someSelected,
17
+ };
18
+ }
@@ -0,0 +1,19 @@
1
+ import { checkGroupSelection } from './checkGroupSelection';
2
+ import { GetNodeSelectionStateParams, ToggleHierarchicalSelectionParams } from './types';
3
+ export type HierarchicalSelectionConfig = {
4
+ includeParentsInValue: boolean;
5
+ };
6
+ export type HierarchicalSelectionNodeParams<TId extends string | number = string> = Omit<GetNodeSelectionStateParams<TId>, 'includeParentsInValue'>;
7
+ export type HierarchicalSelectionToggleParams<TId extends string | number = string> = Omit<ToggleHierarchicalSelectionParams<TId>, 'includeParentsInValue'>;
8
+ /**
9
+ * Создаёт набор обработчиков иерархического выбора с единым `includeParentsInValue`.
10
+ */
11
+ export declare function createHierarchicalSelectionHandlers({ includeParentsInValue }: HierarchicalSelectionConfig): {
12
+ getSelectionState<TId extends string | number>(params: HierarchicalSelectionNodeParams<TId>): {
13
+ checked: boolean;
14
+ indeterminate: boolean;
15
+ };
16
+ toggleSelection<TId extends string | number>(params: HierarchicalSelectionToggleParams<TId>): TId[];
17
+ checkGroupSelection: typeof checkGroupSelection;
18
+ };
19
+ export type HierarchicalSelectionHandlers = ReturnType<typeof createHierarchicalSelectionHandlers>;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createHierarchicalSelectionHandlers = createHierarchicalSelectionHandlers;
4
+ const checkGroupSelection_1 = require("./checkGroupSelection");
5
+ const getNodeSelectionState_1 = require("./getNodeSelectionState");
6
+ const toggleHierarchicalSelection_1 = require("./toggleHierarchicalSelection");
7
+ /**
8
+ * Создаёт набор обработчиков иерархического выбора с единым `includeParentsInValue`.
9
+ */
10
+ function createHierarchicalSelectionHandlers({ includeParentsInValue }) {
11
+ return {
12
+ getSelectionState(params) {
13
+ return (0, getNodeSelectionState_1.getNodeSelectionState)({ ...params, includeParentsInValue });
14
+ },
15
+ toggleSelection(params) {
16
+ return (0, toggleHierarchicalSelection_1.toggleHierarchicalSelection)({ ...params, includeParentsInValue });
17
+ },
18
+ checkGroupSelection: checkGroupSelection_1.checkGroupSelection,
19
+ };
20
+ }
@@ -0,0 +1,8 @@
1
+ import { GetNodeSelectionStateParams } from './types';
2
+ /**
3
+ * Единая формула checked/indeterminate для иерархического мультивыбора.
4
+ */
5
+ export declare function getNodeSelectionState<TId extends string | number>({ nodeId, childIds, selectedIds, includeParentsInValue, }: GetNodeSelectionStateParams<TId>): {
6
+ checked: boolean;
7
+ indeterminate: boolean;
8
+ };
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getNodeSelectionState = getNodeSelectionState;
4
+ const checkGroupSelection_1 = require("./checkGroupSelection");
5
+ /**
6
+ * Единая формула checked/indeterminate для иерархического мультивыбора.
7
+ */
8
+ function getNodeSelectionState({ nodeId, childIds, selectedIds, includeParentsInValue, }) {
9
+ const { allSelected, someSelected } = (0, checkGroupSelection_1.checkGroupSelection)(childIds, selectedIds);
10
+ if (!includeParentsInValue) {
11
+ return {
12
+ checked: allSelected,
13
+ indeterminate: someSelected,
14
+ };
15
+ }
16
+ const inValue = selectedIds.includes(nodeId);
17
+ const checked = inValue || allSelected;
18
+ return {
19
+ checked,
20
+ indeterminate: !checked && someSelected,
21
+ };
22
+ }
@@ -0,0 +1,6 @@
1
+ export * from './applySelectionDiff';
2
+ export * from './checkGroupSelection';
3
+ export * from './createHierarchicalSelectionHandlers';
4
+ export * from './getNodeSelectionState';
5
+ export * from './toggleHierarchicalSelection';
6
+ export * from './types';
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./applySelectionDiff"), exports);
18
+ __exportStar(require("./checkGroupSelection"), exports);
19
+ __exportStar(require("./createHierarchicalSelectionHandlers"), exports);
20
+ __exportStar(require("./getNodeSelectionState"), exports);
21
+ __exportStar(require("./toggleHierarchicalSelection"), exports);
22
+ __exportStar(require("./types"), exports);
@@ -0,0 +1,5 @@
1
+ import { ToggleHierarchicalSelectionParams } from './types';
2
+ /**
3
+ * Пересчитывает выбранные id после toggle узла с каскадом вниз и опциональным walk вверх.
4
+ */
5
+ export declare function toggleHierarchicalSelection<TId extends string | number>(params: ToggleHierarchicalSelectionParams<TId>): TId[];
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toggleHierarchicalSelection = toggleHierarchicalSelection;
4
+ const checkGroupSelection_1 = require("./checkGroupSelection");
5
+ function resolveIsSelected({ nodeId, descendantIds, selectableDescendantIds, childIds, selectedIds, includeParentsInValue, }) {
6
+ const hasDescendants = descendantIds.length > 0;
7
+ if (!includeParentsInValue && hasDescendants) {
8
+ const idsForChecked = childIds ?? descendantIds;
9
+ const selectable = selectableDescendantIds ?? descendantIds;
10
+ const allChildrenSelected = idsForChecked.length > 0 && idsForChecked.every(id => selectedIds.includes(id));
11
+ const allEnabledSelected = selectable.length > 0 && selectable.every(id => selectedIds.includes(id));
12
+ return allChildrenSelected || allEnabledSelected;
13
+ }
14
+ if (hasDescendants) {
15
+ const { allSelected, someSelected } = (0, checkGroupSelection_1.checkGroupSelection)(descendantIds, selectedIds);
16
+ return allSelected || someSelected;
17
+ }
18
+ return selectedIds.includes(nodeId);
19
+ }
20
+ function toggleWithParentsInValue({ nodeId, descendantIds, selectedIds, ancestors, isSelected, }) {
21
+ const idsFromNode = [nodeId, ...descendantIds];
22
+ let updatedSelectedIds = isSelected
23
+ ? selectedIds.filter(id => !idsFromNode.includes(id))
24
+ : [...selectedIds, ...idsFromNode.filter(id => !selectedIds.includes(id))];
25
+ if (!ancestors?.length) {
26
+ return updatedSelectedIds;
27
+ }
28
+ for (const ancestor of ancestors) {
29
+ if (!ancestor.childIds.length) {
30
+ continue;
31
+ }
32
+ const parentSelection = (0, checkGroupSelection_1.checkGroupSelection)(ancestor.childIds, updatedSelectedIds);
33
+ if (isSelected) {
34
+ if (!parentSelection.allSelected) {
35
+ updatedSelectedIds = updatedSelectedIds.filter(id => id !== ancestor.id);
36
+ }
37
+ }
38
+ else if (parentSelection.allSelected && !updatedSelectedIds.includes(ancestor.id)) {
39
+ updatedSelectedIds = [...updatedSelectedIds, ancestor.id];
40
+ }
41
+ }
42
+ return updatedSelectedIds;
43
+ }
44
+ function toggleWithoutParentsInValue({ nodeId, descendantIds, selectableDescendantIds, selectedIds, isSelected, }) {
45
+ if (!descendantIds.length) {
46
+ if (isSelected) {
47
+ return selectedIds.filter(id => id !== nodeId);
48
+ }
49
+ if (selectedIds.includes(nodeId)) {
50
+ return selectedIds;
51
+ }
52
+ return [...selectedIds, nodeId];
53
+ }
54
+ const selectable = selectableDescendantIds ?? descendantIds;
55
+ if (isSelected) {
56
+ return selectedIds.filter(id => id !== nodeId && !selectable.includes(id));
57
+ }
58
+ return [...new Set([...selectedIds, ...selectable])];
59
+ }
60
+ /**
61
+ * Пересчитывает выбранные id после toggle узла с каскадом вниз и опциональным walk вверх.
62
+ */
63
+ function toggleHierarchicalSelection(params) {
64
+ const isSelected = resolveIsSelected(params);
65
+ if (params.includeParentsInValue) {
66
+ return toggleWithParentsInValue({ ...params, isSelected });
67
+ }
68
+ return toggleWithoutParentsInValue({ ...params, isSelected });
69
+ }
@@ -0,0 +1,23 @@
1
+ export type GroupSelectionState = {
2
+ allSelected: boolean;
3
+ someSelected: boolean;
4
+ };
5
+ export type HierarchicalAncestor<TId extends string | number = string> = {
6
+ id: TId;
7
+ childIds: TId[];
8
+ };
9
+ export type GetNodeSelectionStateParams<TId extends string | number = string> = {
10
+ nodeId: TId;
11
+ childIds: TId[];
12
+ selectedIds: TId[];
13
+ includeParentsInValue: boolean;
14
+ };
15
+ export type ToggleHierarchicalSelectionParams<TId extends string | number = string> = {
16
+ nodeId: TId;
17
+ descendantIds: TId[];
18
+ selectableDescendantIds?: TId[];
19
+ childIds?: TId[];
20
+ selectedIds: TId[];
21
+ ancestors?: HierarchicalAncestor<TId>[];
22
+ includeParentsInValue: boolean;
23
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,10 @@
1
+ export * from './componentPropsProcessors';
2
+ export * from './debounce';
3
+ export * from './hierarchicalSelection';
4
+ export * from './innerRef';
5
+ export * from './isBrowser';
6
+ export * from './copyToClipboard';
7
+ export * from './keyboard';
8
+ export * from './throttle';
9
+ export * from './typeGuards';
10
+ export * from './stopEventPropagation';
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./componentPropsProcessors"), exports);
18
+ __exportStar(require("./debounce"), exports);
19
+ __exportStar(require("./hierarchicalSelection"), exports);
20
+ __exportStar(require("./innerRef"), exports);
21
+ __exportStar(require("./isBrowser"), exports);
22
+ __exportStar(require("./copyToClipboard"), exports);
23
+ __exportStar(require("./keyboard"), exports);
24
+ __exportStar(require("./throttle"), exports);
25
+ __exportStar(require("./typeGuards"), exports);
26
+ __exportStar(require("./stopEventPropagation"), exports);
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Маркер «компонент принимает DOM-ноду своего корня через проп `innerRef`».
3
+ *
4
+ * Нужен потому, что интроспекция пропсов функционального компонента в рантайме невозможна: обёртки вроде
5
+ * `Popover` / `Tooltip` / `Dropdown` получают триггер как `ReactElement` и должны решить, каким каналом отдать
6
+ * ему reference-ноду. У `forwardRef`-компонента это `ref` (определяется через `react-is`), у обычной функции —
7
+ * проп `innerRef`, и узнать о его поддержке можно только по явной пометке.
8
+ *
9
+ * Ключ — `Symbol.for`, поэтому маркер общий для всех копий `@cloud-ru/ds-utils` в дереве зависимостей.
10
+ */
11
+ export declare const SUPPORTS_INNER_REF: unique symbol;
12
+ /**
13
+ * Помечает компонент как принимающий `innerRef`. Вызывается один раз рядом с объявлением компонента:
14
+ *
15
+ * ```tsx
16
+ * export function Button<T extends ElementType = 'button'>({ innerRef, ...rest }: ButtonProps<T>) { … }
17
+ * withInnerRefSupport(Button);
18
+ * ```
19
+ *
20
+ * Помечать нужно только те компоненты, которые прокидывают `innerRef` на **корневой** DOM-узел — именно его
21
+ * ждёт позиционирование поповера. Компоненты, у которых `innerRef` ведёт на внутренний слот (например, на
22
+ * нативный `input`), не помечаем.
23
+ */
24
+ export declare function withInnerRefSupport<T extends object>(component: T): T;
25
+ /** Принимает ли тип элемента DOM-ноду через проп `innerRef`. */
26
+ export declare function supportsInnerRef(type: unknown): boolean;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SUPPORTS_INNER_REF = void 0;
4
+ exports.withInnerRefSupport = withInnerRefSupport;
5
+ exports.supportsInnerRef = supportsInnerRef;
6
+ /**
7
+ * Маркер «компонент принимает DOM-ноду своего корня через проп `innerRef`».
8
+ *
9
+ * Нужен потому, что интроспекция пропсов функционального компонента в рантайме невозможна: обёртки вроде
10
+ * `Popover` / `Tooltip` / `Dropdown` получают триггер как `ReactElement` и должны решить, каким каналом отдать
11
+ * ему reference-ноду. У `forwardRef`-компонента это `ref` (определяется через `react-is`), у обычной функции —
12
+ * проп `innerRef`, и узнать о его поддержке можно только по явной пометке.
13
+ *
14
+ * Ключ — `Symbol.for`, поэтому маркер общий для всех копий `@cloud-ru/ds-utils` в дереве зависимостей.
15
+ */
16
+ exports.SUPPORTS_INNER_REF = Symbol.for('@cloud-ru/ds-supports-inner-ref');
17
+ /**
18
+ * Помечает компонент как принимающий `innerRef`. Вызывается один раз рядом с объявлением компонента:
19
+ *
20
+ * ```tsx
21
+ * export function Button<T extends ElementType = 'button'>({ innerRef, ...rest }: ButtonProps<T>) { … }
22
+ * withInnerRefSupport(Button);
23
+ * ```
24
+ *
25
+ * Помечать нужно только те компоненты, которые прокидывают `innerRef` на **корневой** DOM-узел — именно его
26
+ * ждёт позиционирование поповера. Компоненты, у которых `innerRef` ведёт на внутренний слот (например, на
27
+ * нативный `input`), не помечаем.
28
+ */
29
+ function withInnerRefSupport(component) {
30
+ Object.defineProperty(component, exports.SUPPORTS_INNER_REF, { value: true });
31
+ return component;
32
+ }
33
+ /** Принимает ли тип элемента DOM-ноду через проп `innerRef`. */
34
+ function supportsInnerRef(type) {
35
+ return ((typeof type === 'function' || (typeof type === 'object' && type !== null)) &&
36
+ Boolean(type[exports.SUPPORTS_INNER_REF]));
37
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Функция проверки текущей среды выполнения кода,
3
+ * нужна для корректной работы SSR
4
+ * @function helper
5
+ */
6
+ export declare function isBrowser(): boolean;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isBrowser = isBrowser;
4
+ /**
5
+ * Функция проверки текущей среды выполнения кода,
6
+ * нужна для корректной работы SSR
7
+ * @function helper
8
+ */
9
+ function isBrowser() {
10
+ // eslint-disable-next-line @cloud-ru/ssr-safe-react/domApi
11
+ return Boolean(typeof window !== 'undefined' && window.document && window.document.createElement);
12
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Клавиши-стрелки. Внутри компонентов с собственной клавиатурной навигацией (списки, сетка
3
+ * календаря, roving-кнопки) их нативное поведение — прокрутка страницы — нужно гасить, иначе
4
+ * событие «улетает» за компонент и скроллит документ.
5
+ */
6
+ export declare const ARROW_KEYS: readonly ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"];
7
+ /**
8
+ * Вертикальные стрелки. Для текстовых полей гасить нужно только их (горизонтальные `ArrowLeft` /
9
+ * `ArrowRight` двигают каретку — их перехватывать нельзя), тогда как в сетках/списках/roving-кнопках
10
+ * без текста гасятся все четыре (см. {@link ARROW_KEYS}).
11
+ */
12
+ export declare const VERTICAL_ARROW_KEYS: readonly ["ArrowUp", "ArrowDown"];
13
+ export type ArrowKey = (typeof ARROW_KEYS)[number];
14
+ /** `true`, если клавиша — одна из стрелок навигации. */
15
+ export declare function isArrowKey(key: string): key is ArrowKey;
16
+ /** `true`, если клавиша — вертикальная стрелка (`ArrowUp` / `ArrowDown`). */
17
+ export declare function isVerticalArrowKey(key: string): boolean;
18
+ /**
19
+ * Гасит нативную прокрутку страницы для клавиш-стрелок: если нажата стрелка — вызывает
20
+ * `preventDefault()` и возвращает `true`. Использовать в начале keydown-обработчика компонента,
21
+ * который сам обрабатывает стрелочную навигацию.
22
+ *
23
+ * @example
24
+ * const handleKeyDown = (e) => {
25
+ * preventScrollOnArrowKeys(e);
26
+ * // …собственная навигация
27
+ * };
28
+ */
29
+ export declare function preventScrollOnArrowKeys(event: Pick<KeyboardEvent, 'key' | 'preventDefault'>): boolean;
30
+ /**
31
+ * Как {@link preventScrollOnArrowKeys}, но только для вертикальных стрелок (`ArrowUp` / `ArrowDown`).
32
+ * Для текстовых полей: гасит прокрутку страницы по Up/Down, не трогая горизонтальные стрелки —
33
+ * каретка по тексту продолжает двигаться.
34
+ */
35
+ export declare function preventScrollOnVerticalArrows(event: Pick<KeyboardEvent, 'key' | 'preventDefault'>): boolean;
36
+ /**
37
+ * Программный фокус без авто-прокрутки страницы к элементу (`focus({ preventScroll: true })`).
38
+ * Применять при перемещении фокуса по клавиатуре внутри уже видимого компонента (ячейка
39
+ * календаря, элемент списка), иначе браузер скроллит документ к новому фокусу.
40
+ */
41
+ export declare function focusWithoutScroll(element: HTMLElement | null | undefined): void;