@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.
- package/CHANGELOG.md +8 -0
- package/LICENSE +201 -0
- package/README.md +222 -0
- package/dist/cjs/hooks/index.d.ts +13 -0
- package/dist/cjs/hooks/index.js +29 -0
- package/dist/cjs/hooks/useCopyToClipboard.d.ts +9 -0
- package/dist/cjs/hooks/useCopyToClipboard.js +27 -0
- package/dist/cjs/hooks/useDataPersist/index.d.ts +2 -0
- package/dist/cjs/hooks/useDataPersist/index.js +18 -0
- package/dist/cjs/hooks/useDataPersist/sources/baseSource.d.ts +11 -0
- package/dist/cjs/hooks/useDataPersist/sources/baseSource.js +35 -0
- package/dist/cjs/hooks/useDataPersist/sources/index.d.ts +3 -0
- package/dist/cjs/hooks/useDataPersist/sources/index.js +19 -0
- package/dist/cjs/hooks/useDataPersist/sources/localStorageSource.d.ts +5 -0
- package/dist/cjs/hooks/useDataPersist/sources/localStorageSource.js +19 -0
- package/dist/cjs/hooks/useDataPersist/sources/queryParamSource.d.ts +5 -0
- package/dist/cjs/hooks/useDataPersist/sources/queryParamSource.js +21 -0
- package/dist/cjs/hooks/useDataPersist/useDataPersist.d.ts +16 -0
- package/dist/cjs/hooks/useDataPersist/useDataPersist.js +26 -0
- package/dist/cjs/hooks/useDataPersist/useSource.d.ts +8 -0
- package/dist/cjs/hooks/useDataPersist/useSource.js +15 -0
- package/dist/cjs/hooks/useDebounce.d.ts +6 -0
- package/dist/cjs/hooks/useDebounce.js +25 -0
- package/dist/cjs/hooks/useDynamicList.d.ts +18 -0
- package/dist/cjs/hooks/useDynamicList.js +99 -0
- package/dist/cjs/hooks/useEventHandler.d.ts +6 -0
- package/dist/cjs/hooks/useEventHandler.js +23 -0
- package/dist/cjs/hooks/useHierarchicalSelection.d.ts +12 -0
- package/dist/cjs/hooks/useHierarchicalSelection.js +11 -0
- package/dist/cjs/hooks/useIsomorphicLayoutEffect.d.ts +7 -0
- package/dist/cjs/hooks/useIsomorphicLayoutEffect.js +12 -0
- package/dist/cjs/hooks/useLocalStorage.d.ts +1 -0
- package/dist/cjs/hooks/useLocalStorage.js +15 -0
- package/dist/cjs/hooks/useModalOpenState.d.ts +18 -0
- package/dist/cjs/hooks/useModalOpenState.js +27 -0
- package/dist/cjs/hooks/usePopstateSubscription.d.ts +1 -0
- package/dist/cjs/hooks/usePopstateSubscription.js +14 -0
- package/dist/cjs/hooks/useSwipeable.d.ts +21 -0
- package/dist/cjs/hooks/useSwipeable.js +50 -0
- package/dist/cjs/hooks/useUncontrolledProp.d.ts +21 -0
- package/dist/cjs/hooks/useUncontrolledProp.js +23 -0
- package/dist/cjs/hooks/useValueControl.d.ts +20 -0
- package/dist/cjs/hooks/useValueControl.js +20 -0
- package/dist/cjs/index.d.ts +3 -0
- package/dist/cjs/index.js +19 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/types/index.d.ts +2 -0
- package/dist/cjs/types/index.js +18 -0
- package/dist/cjs/types/polymorphic.d.ts +3 -0
- package/dist/cjs/types/polymorphic.js +2 -0
- package/dist/cjs/types/valueOf.d.ts +5 -0
- package/dist/cjs/types/valueOf.js +2 -0
- package/dist/cjs/utils/componentPropsProcessors.d.ts +19 -0
- package/dist/cjs/utils/componentPropsProcessors.js +43 -0
- package/dist/cjs/utils/copyToClipboard.d.ts +12 -0
- package/dist/cjs/utils/copyToClipboard.js +47 -0
- package/dist/cjs/utils/debounce.d.ts +11 -0
- package/dist/cjs/utils/debounce.js +24 -0
- package/dist/cjs/utils/hierarchicalSelection/applySelectionDiff.d.ts +4 -0
- package/dist/cjs/utils/hierarchicalSelection/applySelectionDiff.js +16 -0
- package/dist/cjs/utils/hierarchicalSelection/checkGroupSelection.d.ts +5 -0
- package/dist/cjs/utils/hierarchicalSelection/checkGroupSelection.js +18 -0
- package/dist/cjs/utils/hierarchicalSelection/createHierarchicalSelectionHandlers.d.ts +19 -0
- package/dist/cjs/utils/hierarchicalSelection/createHierarchicalSelectionHandlers.js +20 -0
- package/dist/cjs/utils/hierarchicalSelection/getNodeSelectionState.d.ts +8 -0
- package/dist/cjs/utils/hierarchicalSelection/getNodeSelectionState.js +22 -0
- package/dist/cjs/utils/hierarchicalSelection/index.d.ts +6 -0
- package/dist/cjs/utils/hierarchicalSelection/index.js +22 -0
- package/dist/cjs/utils/hierarchicalSelection/toggleHierarchicalSelection.d.ts +5 -0
- package/dist/cjs/utils/hierarchicalSelection/toggleHierarchicalSelection.js +69 -0
- package/dist/cjs/utils/hierarchicalSelection/types.d.ts +23 -0
- package/dist/cjs/utils/hierarchicalSelection/types.js +2 -0
- package/dist/cjs/utils/index.d.ts +10 -0
- package/dist/cjs/utils/index.js +26 -0
- package/dist/cjs/utils/innerRef.d.ts +26 -0
- package/dist/cjs/utils/innerRef.js +37 -0
- package/dist/cjs/utils/isBrowser.d.ts +6 -0
- package/dist/cjs/utils/isBrowser.js +12 -0
- package/dist/cjs/utils/keyboard.d.ts +41 -0
- package/dist/cjs/utils/keyboard.js +66 -0
- package/dist/cjs/utils/preventEvent.d.ts +4 -0
- package/dist/cjs/utils/preventEvent.js +15 -0
- package/dist/cjs/utils/stopEventPropagation.d.ts +2 -0
- package/dist/cjs/utils/stopEventPropagation.js +6 -0
- package/dist/cjs/utils/throttle.d.ts +11 -0
- package/dist/cjs/utils/throttle.js +43 -0
- package/dist/cjs/utils/typeGuards.d.ts +5 -0
- package/dist/cjs/utils/typeGuards.js +13 -0
- package/dist/esm/hooks/index.d.ts +13 -0
- package/dist/esm/hooks/index.js +13 -0
- package/dist/esm/hooks/useCopyToClipboard.d.ts +9 -0
- package/dist/esm/hooks/useCopyToClipboard.js +24 -0
- package/dist/esm/hooks/useDataPersist/index.d.ts +2 -0
- package/dist/esm/hooks/useDataPersist/index.js +2 -0
- package/dist/esm/hooks/useDataPersist/sources/baseSource.d.ts +11 -0
- package/dist/esm/hooks/useDataPersist/sources/baseSource.js +31 -0
- package/dist/esm/hooks/useDataPersist/sources/index.d.ts +3 -0
- package/dist/esm/hooks/useDataPersist/sources/index.js +3 -0
- package/dist/esm/hooks/useDataPersist/sources/localStorageSource.d.ts +5 -0
- package/dist/esm/hooks/useDataPersist/sources/localStorageSource.js +15 -0
- package/dist/esm/hooks/useDataPersist/sources/queryParamSource.d.ts +5 -0
- package/dist/esm/hooks/useDataPersist/sources/queryParamSource.js +17 -0
- package/dist/esm/hooks/useDataPersist/useDataPersist.d.ts +16 -0
- package/dist/esm/hooks/useDataPersist/useDataPersist.js +22 -0
- package/dist/esm/hooks/useDataPersist/useSource.d.ts +8 -0
- package/dist/esm/hooks/useDataPersist/useSource.js +11 -0
- package/dist/esm/hooks/useDebounce.d.ts +6 -0
- package/dist/esm/hooks/useDebounce.js +22 -0
- package/dist/esm/hooks/useDynamicList.d.ts +18 -0
- package/dist/esm/hooks/useDynamicList.js +96 -0
- package/dist/esm/hooks/useEventHandler.d.ts +6 -0
- package/dist/esm/hooks/useEventHandler.js +20 -0
- package/dist/esm/hooks/useHierarchicalSelection.d.ts +12 -0
- package/dist/esm/hooks/useHierarchicalSelection.js +8 -0
- package/dist/esm/hooks/useIsomorphicLayoutEffect.d.ts +7 -0
- package/dist/esm/hooks/useIsomorphicLayoutEffect.js +9 -0
- package/dist/esm/hooks/useLocalStorage.d.ts +1 -0
- package/dist/esm/hooks/useLocalStorage.js +12 -0
- package/dist/esm/hooks/useModalOpenState.d.ts +18 -0
- package/dist/esm/hooks/useModalOpenState.js +24 -0
- package/dist/esm/hooks/usePopstateSubscription.d.ts +1 -0
- package/dist/esm/hooks/usePopstateSubscription.js +11 -0
- package/dist/esm/hooks/useSwipeable.d.ts +21 -0
- package/dist/esm/hooks/useSwipeable.js +46 -0
- package/dist/esm/hooks/useUncontrolledProp.d.ts +21 -0
- package/dist/esm/hooks/useUncontrolledProp.js +20 -0
- package/dist/esm/hooks/useValueControl.d.ts +20 -0
- package/dist/esm/hooks/useValueControl.js +17 -0
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.js +3 -0
- package/dist/esm/types/index.d.ts +2 -0
- package/dist/esm/types/index.js +2 -0
- package/dist/esm/types/polymorphic.d.ts +3 -0
- package/dist/esm/types/polymorphic.js +1 -0
- package/dist/esm/types/valueOf.d.ts +5 -0
- package/dist/esm/types/valueOf.js +1 -0
- package/dist/esm/utils/componentPropsProcessors.d.ts +19 -0
- package/dist/esm/utils/componentPropsProcessors.js +38 -0
- package/dist/esm/utils/copyToClipboard.d.ts +12 -0
- package/dist/esm/utils/copyToClipboard.js +44 -0
- package/dist/esm/utils/debounce.d.ts +11 -0
- package/dist/esm/utils/debounce.js +21 -0
- package/dist/esm/utils/hierarchicalSelection/applySelectionDiff.d.ts +4 -0
- package/dist/esm/utils/hierarchicalSelection/applySelectionDiff.js +13 -0
- package/dist/esm/utils/hierarchicalSelection/checkGroupSelection.d.ts +5 -0
- package/dist/esm/utils/hierarchicalSelection/checkGroupSelection.js +15 -0
- package/dist/esm/utils/hierarchicalSelection/createHierarchicalSelectionHandlers.d.ts +19 -0
- package/dist/esm/utils/hierarchicalSelection/createHierarchicalSelectionHandlers.js +17 -0
- package/dist/esm/utils/hierarchicalSelection/getNodeSelectionState.d.ts +8 -0
- package/dist/esm/utils/hierarchicalSelection/getNodeSelectionState.js +19 -0
- package/dist/esm/utils/hierarchicalSelection/index.d.ts +6 -0
- package/dist/esm/utils/hierarchicalSelection/index.js +6 -0
- package/dist/esm/utils/hierarchicalSelection/toggleHierarchicalSelection.d.ts +5 -0
- package/dist/esm/utils/hierarchicalSelection/toggleHierarchicalSelection.js +66 -0
- package/dist/esm/utils/hierarchicalSelection/types.d.ts +23 -0
- package/dist/esm/utils/hierarchicalSelection/types.js +1 -0
- package/dist/esm/utils/index.d.ts +10 -0
- package/dist/esm/utils/index.js +10 -0
- package/dist/esm/utils/innerRef.d.ts +26 -0
- package/dist/esm/utils/innerRef.js +32 -0
- package/dist/esm/utils/isBrowser.d.ts +6 -0
- package/dist/esm/utils/isBrowser.js +9 -0
- package/dist/esm/utils/keyboard.d.ts +41 -0
- package/dist/esm/utils/keyboard.js +58 -0
- package/dist/esm/utils/preventEvent.d.ts +4 -0
- package/dist/esm/utils/preventEvent.js +10 -0
- package/dist/esm/utils/stopEventPropagation.d.ts +2 -0
- package/dist/esm/utils/stopEventPropagation.js +3 -0
- package/dist/esm/utils/throttle.d.ts +11 -0
- package/dist/esm/utils/throttle.js +40 -0
- package/dist/esm/utils/typeGuards.d.ts +5 -0
- package/dist/esm/utils/typeGuards.js +5 -0
- package/dist/tsconfig.cjs.tsbuildinfo +1 -0
- package/dist/tsconfig.esm.tsbuildinfo +1 -0
- package/package.json +38 -0
- package/src/hooks/index.ts +13 -0
- package/src/hooks/useCopyToClipboard.ts +38 -0
- package/src/hooks/useDataPersist/index.ts +2 -0
- package/src/hooks/useDataPersist/sources/baseSource.ts +36 -0
- package/src/hooks/useDataPersist/sources/index.ts +3 -0
- package/src/hooks/useDataPersist/sources/localStorageSource.ts +17 -0
- package/src/hooks/useDataPersist/sources/queryParamSource.ts +19 -0
- package/src/hooks/useDataPersist/useDataPersist.ts +51 -0
- package/src/hooks/useDataPersist/useSource.ts +23 -0
- package/src/hooks/useDebounce.ts +25 -0
- package/src/hooks/useDynamicList.ts +137 -0
- package/src/hooks/useEventHandler.ts +26 -0
- package/src/hooks/useHierarchicalSelection.ts +13 -0
- package/src/hooks/useIsomorphicLayoutEffect.ts +11 -0
- package/src/hooks/useLocalStorage.ts +19 -0
- package/src/hooks/useModalOpenState.ts +37 -0
- package/src/hooks/usePopstateSubscription.ts +14 -0
- package/src/hooks/useSwipeable.ts +85 -0
- package/src/hooks/useUncontrolledProp.ts +65 -0
- package/src/hooks/useValueControl.ts +41 -0
- package/src/index.ts +3 -0
- package/src/types/index.ts +2 -0
- package/src/types/polymorphic.ts +4 -0
- package/src/types/valueOf.ts +5 -0
- package/src/utils/componentPropsProcessors.ts +55 -0
- package/src/utils/copyToClipboard.ts +46 -0
- package/src/utils/debounce.ts +31 -0
- package/src/utils/hierarchicalSelection/applySelectionDiff.ts +20 -0
- package/src/utils/hierarchicalSelection/checkGroupSelection.ts +22 -0
- package/src/utils/hierarchicalSelection/createHierarchicalSelectionHandlers.ts +35 -0
- package/src/utils/hierarchicalSelection/getNodeSelectionState.ts +29 -0
- package/src/utils/hierarchicalSelection/index.ts +6 -0
- package/src/utils/hierarchicalSelection/toggleHierarchicalSelection.ts +109 -0
- package/src/utils/hierarchicalSelection/types.ts +26 -0
- package/src/utils/index.ts +10 -0
- package/src/utils/innerRef.ts +37 -0
- package/src/utils/isBrowser.ts +9 -0
- package/src/utils/keyboard.ts +68 -0
- package/src/utils/stopEventPropagation.ts +5 -0
- package/src/utils/throttle.ts +55 -0
- package/src/utils/typeGuards.ts +9 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
|
+
import { isBrowser } from '../utils/index.js';
|
|
3
|
+
export function useLocalStorage(key, defaultValue) {
|
|
4
|
+
const [value, setValueState] = useState(((isBrowser() ? localStorage.getItem(key) : null) ?? defaultValue));
|
|
5
|
+
const setValue = useCallback((newValue) => {
|
|
6
|
+
if (isBrowser()) {
|
|
7
|
+
localStorage.setItem(key, newValue);
|
|
8
|
+
}
|
|
9
|
+
setValueState(newValue);
|
|
10
|
+
}, [key]);
|
|
11
|
+
return [value, setValue];
|
|
12
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
type UseModalOpenStateOptions = {
|
|
2
|
+
/** закрывать модалку при переходе по истории браузера (по умолчанию false) */
|
|
3
|
+
closeOnPopstate?: boolean;
|
|
4
|
+
/** закрывать модалку через CloseWatcher (по умолчанию true) */
|
|
5
|
+
closeByCloseWatcher?: boolean;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Хук для управления состоянием модалки
|
|
9
|
+
* @returns [open, onClose]
|
|
10
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/CloseWatcher
|
|
11
|
+
* @function hook
|
|
12
|
+
*/
|
|
13
|
+
export declare function useModalOpenState(
|
|
14
|
+
/** состояние модалки */
|
|
15
|
+
open: boolean,
|
|
16
|
+
/** колбек закрытия */
|
|
17
|
+
onClose: () => void, { closeOnPopstate, closeByCloseWatcher }: UseModalOpenStateOptions): void;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { useEventHandler } from './useEventHandler.js';
|
|
3
|
+
import { usePopstateSubscription } from './usePopstateSubscription.js';
|
|
4
|
+
/**
|
|
5
|
+
* Хук для управления состоянием модалки
|
|
6
|
+
* @returns [open, onClose]
|
|
7
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/CloseWatcher
|
|
8
|
+
* @function hook
|
|
9
|
+
*/
|
|
10
|
+
export function useModalOpenState(
|
|
11
|
+
/** состояние модалки */
|
|
12
|
+
open,
|
|
13
|
+
/** колбек закрытия */
|
|
14
|
+
onClose, { closeOnPopstate = false, closeByCloseWatcher = true }) {
|
|
15
|
+
const closeHandler = useEventHandler(onClose);
|
|
16
|
+
usePopstateSubscription(() => open && closeHandler(), closeOnPopstate);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (open && 'CloseWatcher' in window && closeByCloseWatcher) {
|
|
19
|
+
const watcher = new CloseWatcher();
|
|
20
|
+
watcher.onclose = () => closeHandler();
|
|
21
|
+
return () => watcher.destroy();
|
|
22
|
+
}
|
|
23
|
+
}, [open, closeHandler, closeByCloseWatcher]);
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function usePopstateSubscription(callback: (e: PopStateEvent) => void, enabled?: boolean): void;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { useEventHandler } from './useEventHandler.js';
|
|
3
|
+
export function usePopstateSubscription(callback, enabled = true) {
|
|
4
|
+
const handler = useEventHandler(callback);
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
if (!enabled)
|
|
7
|
+
return;
|
|
8
|
+
window.addEventListener('popstate', handler);
|
|
9
|
+
return () => window.removeEventListener('popstate', handler);
|
|
10
|
+
}, [enabled, handler]);
|
|
11
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { SwipeableHandlers, SwipeableProps, SwipeCallback, SwipeDirections, SwipeEventData } from 'react-swipeable';
|
|
2
|
+
export declare const DATA_SWIPE_DIRECTIONS_ATTRIBUTE = "data-swipe-directions";
|
|
3
|
+
export type UseSwipeProps = {
|
|
4
|
+
/**
|
|
5
|
+
* Включен ли свайп
|
|
6
|
+
*/
|
|
7
|
+
enabled?: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Направления, в которых будет работать свайп.
|
|
10
|
+
* Укажите это свойство, чтобы предотвратить конфликты и заблокировать свайп в родительских элементах по этим направлениям.
|
|
11
|
+
*/
|
|
12
|
+
availableDirections?: SwipeDirections[];
|
|
13
|
+
} & SwipeableProps;
|
|
14
|
+
export type UseSwipeReturnType = SwipeableHandlers & {
|
|
15
|
+
[DATA_SWIPE_DIRECTIONS_ATTRIBUTE]?: string;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Хук для работы с событиями свайпа
|
|
19
|
+
*/
|
|
20
|
+
export declare function useSwipeable({ availableDirections, enabled, ...options }: UseSwipeProps): UseSwipeReturnType;
|
|
21
|
+
export type { SwipeCallback, SwipeDirections, SwipeEventData };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { useRef } from 'react';
|
|
2
|
+
import { useSwipeable as useSwipeableInternal, } from 'react-swipeable';
|
|
3
|
+
export const DATA_SWIPE_DIRECTIONS_ATTRIBUTE = 'data-swipe-directions';
|
|
4
|
+
/**
|
|
5
|
+
* Хук для работы с событиями свайпа
|
|
6
|
+
*/
|
|
7
|
+
export function useSwipeable({ availableDirections, enabled = true, ...options }) {
|
|
8
|
+
const canSwipe = useRef(true);
|
|
9
|
+
const onSwipeStart = eventData => {
|
|
10
|
+
if (availableDirections) {
|
|
11
|
+
if (!availableDirections.includes(eventData.dir)) {
|
|
12
|
+
canSwipe.current = false;
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (!options.onSwipeStart) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
eventData.event.stopPropagation();
|
|
19
|
+
}
|
|
20
|
+
options.onSwipeStart?.(eventData);
|
|
21
|
+
};
|
|
22
|
+
const onSwiping = eventData => {
|
|
23
|
+
if (availableDirections) {
|
|
24
|
+
if (!canSwipe.current) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
eventData.event.stopPropagation();
|
|
28
|
+
}
|
|
29
|
+
options.onSwiping?.(eventData);
|
|
30
|
+
};
|
|
31
|
+
const onSwiped = eventData => {
|
|
32
|
+
if (availableDirections) {
|
|
33
|
+
if (!canSwipe.current) {
|
|
34
|
+
canSwipe.current = true;
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
eventData.event.stopPropagation();
|
|
38
|
+
}
|
|
39
|
+
options.onSwiped?.(eventData);
|
|
40
|
+
};
|
|
41
|
+
const handlers = useSwipeableInternal(enabled ? { ...options, onSwipeStart, onSwiping, onSwiped } : {});
|
|
42
|
+
return {
|
|
43
|
+
...handlers,
|
|
44
|
+
[DATA_SWIPE_DIRECTIONS_ATTRIBUTE]: enabled && availableDirections ? availableDirections.join(' ') : undefined,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Сигнатура колбэка смены значения.
|
|
3
|
+
*
|
|
4
|
+
* `any` здесь — не небрежность, а воспроизведение публичного типа пакета `uncontrollable`,
|
|
5
|
+
* на месте которого стоит этот хук. Свободный тип позволяет и колбэки с дополнительными
|
|
6
|
+
* аргументами (`onSelect(key, node)`), и функциональные апдейтеры (`setValue(prev => …)`,
|
|
7
|
+
* которые дальше применяет `useState`). Сузить его нельзя, не переписав типы `ToggleGroup`,
|
|
8
|
+
* `SelectionProvider` и `TreeContext`, — это отдельная задача.
|
|
9
|
+
*/
|
|
10
|
+
export type UncontrolledHandler = (value: any, ...args: any[]) => any;
|
|
11
|
+
/**
|
|
12
|
+
* Пара «значение + сеттер» для пропа, который работает и в controlled, и в uncontrolled режиме.
|
|
13
|
+
* Пока `propValue` задан — источник истины он, а сеттер только зовёт `handler`. Как только
|
|
14
|
+
* `propValue` становится `undefined`, состояние переходит во внутренний `useState`
|
|
15
|
+
* и сбрасывается на `defaultValue`.
|
|
16
|
+
*
|
|
17
|
+
* Сеттер стабилен между рендерами, пока не меняется `handler`.
|
|
18
|
+
* @function React hook
|
|
19
|
+
*/
|
|
20
|
+
export declare function useUncontrolledProp<TValue, THandler extends UncontrolledHandler = UncontrolledHandler>(propValue: TValue | undefined, defaultValue: TValue, handler?: THandler): readonly [TValue, THandler];
|
|
21
|
+
export declare function useUncontrolledProp<TValue, THandler extends UncontrolledHandler = UncontrolledHandler>(propValue: TValue | undefined, defaultValue?: TValue, handler?: THandler): readonly [TValue | undefined, (...args: Parameters<THandler>) => ReturnType<THandler> | void];
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useCallback, useRef, useState } from 'react';
|
|
2
|
+
export function useUncontrolledProp(propValue, defaultValue, handler) {
|
|
3
|
+
const isProp = propValue !== undefined;
|
|
4
|
+
const wasPropRef = useRef(isProp);
|
|
5
|
+
const [stateValue, setStateValue] = useState(defaultValue);
|
|
6
|
+
const wasProp = wasPropRef.current;
|
|
7
|
+
wasPropRef.current = isProp;
|
|
8
|
+
// Переход controlled -> uncontrolled: внутреннее состояние отстало от `defaultValue`,
|
|
9
|
+
// возвращаем его к дефолту, иначе компонент «залипнет» на последнем controlled-значении.
|
|
10
|
+
if (!isProp && wasProp && stateValue !== defaultValue) {
|
|
11
|
+
setStateValue(defaultValue);
|
|
12
|
+
}
|
|
13
|
+
const setValue = useCallback((...args) => {
|
|
14
|
+
const [value, ...rest] = args;
|
|
15
|
+
const returnValue = handler?.(value, ...rest);
|
|
16
|
+
setStateValue(value);
|
|
17
|
+
return returnValue;
|
|
18
|
+
}, [handler]);
|
|
19
|
+
return [isProp ? propValue : stateValue, setValue];
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
type UseValueControl<TValue> = {
|
|
2
|
+
/**
|
|
3
|
+
* Значение состояния
|
|
4
|
+
*/
|
|
5
|
+
value?: TValue;
|
|
6
|
+
/**
|
|
7
|
+
* Значение по умолчанию
|
|
8
|
+
*/
|
|
9
|
+
defaultValue?: TValue;
|
|
10
|
+
/**
|
|
11
|
+
* Колбек, вызываемый на смену состояния
|
|
12
|
+
*/
|
|
13
|
+
onChange?(value: TValue): void;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Хук для работы с состоянием.
|
|
17
|
+
* Нужен для поддержки controlled/uncontrolled поведения, в зависимости от того были ли переданы входные аргументы
|
|
18
|
+
*/
|
|
19
|
+
export declare function useValueControl<TValue>({ value, onChange, defaultValue }: UseValueControl<TValue>): readonly [TValue | undefined, (value: any, ...args: any[]) => any];
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useCallback, useRef } from 'react';
|
|
2
|
+
import { useUncontrolledProp } from './useUncontrolledProp.js';
|
|
3
|
+
/**
|
|
4
|
+
* Хук для работы с состоянием.
|
|
5
|
+
* Нужен для поддержки controlled/uncontrolled поведения, в зависимости от того были ли переданы входные аргументы
|
|
6
|
+
*/
|
|
7
|
+
export function useValueControl({ value, onChange, defaultValue }) {
|
|
8
|
+
// Значение читаем из ref: иначе handler пересоздавался бы на каждую смену `value`,
|
|
9
|
+
// а вместе с ним — и сеттер из `useUncontrolledProp`.
|
|
10
|
+
const valueRef = useRef(value);
|
|
11
|
+
valueRef.current = value;
|
|
12
|
+
const handleChange = useCallback((newValue) => {
|
|
13
|
+
const newState = typeof newValue === 'function' ? newValue(valueRef.current) : newValue;
|
|
14
|
+
onChange?.(newState);
|
|
15
|
+
}, [onChange]);
|
|
16
|
+
return useUncontrolledProp(value, defaultValue, handleChange);
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -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,38 @@
|
|
|
1
|
+
const DATA_TEST_REGEXP = /^data-test-/;
|
|
2
|
+
const DATA_AND_ARIA_REGEXP = /^(data|aria)-/;
|
|
3
|
+
function excludeProps(props, regexp) {
|
|
4
|
+
return Object.keys(props)
|
|
5
|
+
.filter(prop => !prop.match(regexp))
|
|
6
|
+
.reduce((nextProps, prop) => ({
|
|
7
|
+
...nextProps,
|
|
8
|
+
[prop]: props[prop],
|
|
9
|
+
}), {});
|
|
10
|
+
}
|
|
11
|
+
function extractProps(props, regex) {
|
|
12
|
+
return Object.keys(props).reduce((nextProps, prop) => {
|
|
13
|
+
if (prop.match(regex))
|
|
14
|
+
nextProps[prop] = props[prop];
|
|
15
|
+
return nextProps;
|
|
16
|
+
}, {});
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Функция для исключения вспомогательных свойств (data-test-id & aria-*)
|
|
20
|
+
* @function helper
|
|
21
|
+
*/
|
|
22
|
+
export function excludeSupportProps(props) {
|
|
23
|
+
return excludeProps(props, DATA_AND_ARIA_REGEXP);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Функция для отбора свойства data-test-id
|
|
27
|
+
* @function helper
|
|
28
|
+
*/
|
|
29
|
+
export function extractDataTestProps(props) {
|
|
30
|
+
return extractProps(props, DATA_TEST_REGEXP);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Функция для отбора вспомогательных свойств (data-test-id & aria-*)
|
|
34
|
+
* @function helper
|
|
35
|
+
*/
|
|
36
|
+
export function extractSupportProps(props) {
|
|
37
|
+
return extractProps(props, DATA_AND_ARIA_REGEXP);
|
|
38
|
+
}
|
|
@@ -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,44 @@
|
|
|
1
|
+
import { isBrowser } from './isBrowser.js';
|
|
2
|
+
/**
|
|
3
|
+
* Копирует текст в буфер обмена. Сначала пробует Async Clipboard API, при отказе
|
|
4
|
+
* (нет разрешения, insecure-context) падает на `execCommand('copy')` со скрытым textarea.
|
|
5
|
+
*
|
|
6
|
+
* Возвращает `true`, если запись удалась — по этому признаку поля и кнопки показывают
|
|
7
|
+
* индикатор «скопировано».
|
|
8
|
+
*
|
|
9
|
+
* Проверки `isBrowser()` продублированы внутри веток намеренно: правило
|
|
10
|
+
* `@cloud-ru/ssr-safe-react/domApi` требует, чтобы доступ к `navigator`/`document` стоял
|
|
11
|
+
* непосредственно внутри такого условия, а ранний выход оно не учитывает.
|
|
12
|
+
*/
|
|
13
|
+
export async function copyToClipboard(value) {
|
|
14
|
+
if (!isBrowser())
|
|
15
|
+
return false;
|
|
16
|
+
if (isBrowser() && navigator.clipboard?.writeText) {
|
|
17
|
+
try {
|
|
18
|
+
await navigator.clipboard.writeText(value);
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// fall through to execCommand fallback
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (isBrowser()) {
|
|
26
|
+
const textarea = document.createElement('textarea');
|
|
27
|
+
textarea.value = value;
|
|
28
|
+
textarea.setAttribute('readonly', '');
|
|
29
|
+
textarea.style.position = 'fixed';
|
|
30
|
+
textarea.style.opacity = '0';
|
|
31
|
+
document.body.appendChild(textarea);
|
|
32
|
+
textarea.select();
|
|
33
|
+
try {
|
|
34
|
+
return document.execCommand('copy');
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
document.body.removeChild(textarea);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
@@ -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,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Откладывает вызов `callback` на `wait` мс, перезапуская таймер на каждом новом вызове.
|
|
3
|
+
* Срабатывает по заднему фронту — с аргументами последнего вызова.
|
|
4
|
+
*/
|
|
5
|
+
export function debounce(callback, wait = 0) {
|
|
6
|
+
let timer;
|
|
7
|
+
const cancel = () => {
|
|
8
|
+
if (timer !== undefined) {
|
|
9
|
+
clearTimeout(timer);
|
|
10
|
+
timer = undefined;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
const debounced = (...args) => {
|
|
14
|
+
cancel();
|
|
15
|
+
timer = setTimeout(() => {
|
|
16
|
+
timer = undefined;
|
|
17
|
+
callback(...args);
|
|
18
|
+
}, wait);
|
|
19
|
+
};
|
|
20
|
+
return Object.assign(debounced, { cancel });
|
|
21
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Применяет diff к набору выбранных id.
|
|
3
|
+
*/
|
|
4
|
+
export function applySelectionDiff(selectedIds, added, removed) {
|
|
5
|
+
const result = new Set(selectedIds);
|
|
6
|
+
for (const id of added) {
|
|
7
|
+
result.add(id);
|
|
8
|
+
}
|
|
9
|
+
for (const id of removed) {
|
|
10
|
+
result.delete(id);
|
|
11
|
+
}
|
|
12
|
+
return [...result];
|
|
13
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Проверяет состояние выбора группы по flat-списку дочерних id.
|
|
3
|
+
*/
|
|
4
|
+
export function checkGroupSelection(childIds, selectedIds) {
|
|
5
|
+
if (!childIds.length) {
|
|
6
|
+
return { allSelected: false, someSelected: false };
|
|
7
|
+
}
|
|
8
|
+
const selectedCount = childIds.filter(id => selectedIds.includes(id)).length;
|
|
9
|
+
const someSelected = selectedCount > 0;
|
|
10
|
+
const allSelected = someSelected && selectedCount === childIds.length;
|
|
11
|
+
return {
|
|
12
|
+
allSelected,
|
|
13
|
+
someSelected: !allSelected && someSelected,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { checkGroupSelection } from './checkGroupSelection.js';
|
|
2
|
+
import { GetNodeSelectionStateParams, ToggleHierarchicalSelectionParams } from './types.js';
|
|
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,17 @@
|
|
|
1
|
+
import { checkGroupSelection } from './checkGroupSelection.js';
|
|
2
|
+
import { getNodeSelectionState } from './getNodeSelectionState.js';
|
|
3
|
+
import { toggleHierarchicalSelection } from './toggleHierarchicalSelection.js';
|
|
4
|
+
/**
|
|
5
|
+
* Создаёт набор обработчиков иерархического выбора с единым `includeParentsInValue`.
|
|
6
|
+
*/
|
|
7
|
+
export function createHierarchicalSelectionHandlers({ includeParentsInValue }) {
|
|
8
|
+
return {
|
|
9
|
+
getSelectionState(params) {
|
|
10
|
+
return getNodeSelectionState({ ...params, includeParentsInValue });
|
|
11
|
+
},
|
|
12
|
+
toggleSelection(params) {
|
|
13
|
+
return toggleHierarchicalSelection({ ...params, includeParentsInValue });
|
|
14
|
+
},
|
|
15
|
+
checkGroupSelection,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { GetNodeSelectionStateParams } from './types.js';
|
|
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,19 @@
|
|
|
1
|
+
import { checkGroupSelection } from './checkGroupSelection.js';
|
|
2
|
+
/**
|
|
3
|
+
* Единая формула checked/indeterminate для иерархического мультивыбора.
|
|
4
|
+
*/
|
|
5
|
+
export function getNodeSelectionState({ nodeId, childIds, selectedIds, includeParentsInValue, }) {
|
|
6
|
+
const { allSelected, someSelected } = checkGroupSelection(childIds, selectedIds);
|
|
7
|
+
if (!includeParentsInValue) {
|
|
8
|
+
return {
|
|
9
|
+
checked: allSelected,
|
|
10
|
+
indeterminate: someSelected,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
const inValue = selectedIds.includes(nodeId);
|
|
14
|
+
const checked = inValue || allSelected;
|
|
15
|
+
return {
|
|
16
|
+
checked,
|
|
17
|
+
indeterminate: !checked && someSelected,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from './applySelectionDiff.js';
|
|
2
|
+
export * from './checkGroupSelection.js';
|
|
3
|
+
export * from './createHierarchicalSelectionHandlers.js';
|
|
4
|
+
export * from './getNodeSelectionState.js';
|
|
5
|
+
export * from './toggleHierarchicalSelection.js';
|
|
6
|
+
export * from './types.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from './applySelectionDiff.js';
|
|
2
|
+
export * from './checkGroupSelection.js';
|
|
3
|
+
export * from './createHierarchicalSelectionHandlers.js';
|
|
4
|
+
export * from './getNodeSelectionState.js';
|
|
5
|
+
export * from './toggleHierarchicalSelection.js';
|
|
6
|
+
export * from './types.js';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ToggleHierarchicalSelectionParams } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Пересчитывает выбранные id после toggle узла с каскадом вниз и опциональным walk вверх.
|
|
4
|
+
*/
|
|
5
|
+
export declare function toggleHierarchicalSelection<TId extends string | number>(params: ToggleHierarchicalSelectionParams<TId>): TId[];
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { checkGroupSelection } from './checkGroupSelection.js';
|
|
2
|
+
function resolveIsSelected({ nodeId, descendantIds, selectableDescendantIds, childIds, selectedIds, includeParentsInValue, }) {
|
|
3
|
+
const hasDescendants = descendantIds.length > 0;
|
|
4
|
+
if (!includeParentsInValue && hasDescendants) {
|
|
5
|
+
const idsForChecked = childIds ?? descendantIds;
|
|
6
|
+
const selectable = selectableDescendantIds ?? descendantIds;
|
|
7
|
+
const allChildrenSelected = idsForChecked.length > 0 && idsForChecked.every(id => selectedIds.includes(id));
|
|
8
|
+
const allEnabledSelected = selectable.length > 0 && selectable.every(id => selectedIds.includes(id));
|
|
9
|
+
return allChildrenSelected || allEnabledSelected;
|
|
10
|
+
}
|
|
11
|
+
if (hasDescendants) {
|
|
12
|
+
const { allSelected, someSelected } = checkGroupSelection(descendantIds, selectedIds);
|
|
13
|
+
return allSelected || someSelected;
|
|
14
|
+
}
|
|
15
|
+
return selectedIds.includes(nodeId);
|
|
16
|
+
}
|
|
17
|
+
function toggleWithParentsInValue({ nodeId, descendantIds, selectedIds, ancestors, isSelected, }) {
|
|
18
|
+
const idsFromNode = [nodeId, ...descendantIds];
|
|
19
|
+
let updatedSelectedIds = isSelected
|
|
20
|
+
? selectedIds.filter(id => !idsFromNode.includes(id))
|
|
21
|
+
: [...selectedIds, ...idsFromNode.filter(id => !selectedIds.includes(id))];
|
|
22
|
+
if (!ancestors?.length) {
|
|
23
|
+
return updatedSelectedIds;
|
|
24
|
+
}
|
|
25
|
+
for (const ancestor of ancestors) {
|
|
26
|
+
if (!ancestor.childIds.length) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const parentSelection = checkGroupSelection(ancestor.childIds, updatedSelectedIds);
|
|
30
|
+
if (isSelected) {
|
|
31
|
+
if (!parentSelection.allSelected) {
|
|
32
|
+
updatedSelectedIds = updatedSelectedIds.filter(id => id !== ancestor.id);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else if (parentSelection.allSelected && !updatedSelectedIds.includes(ancestor.id)) {
|
|
36
|
+
updatedSelectedIds = [...updatedSelectedIds, ancestor.id];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return updatedSelectedIds;
|
|
40
|
+
}
|
|
41
|
+
function toggleWithoutParentsInValue({ nodeId, descendantIds, selectableDescendantIds, selectedIds, isSelected, }) {
|
|
42
|
+
if (!descendantIds.length) {
|
|
43
|
+
if (isSelected) {
|
|
44
|
+
return selectedIds.filter(id => id !== nodeId);
|
|
45
|
+
}
|
|
46
|
+
if (selectedIds.includes(nodeId)) {
|
|
47
|
+
return selectedIds;
|
|
48
|
+
}
|
|
49
|
+
return [...selectedIds, nodeId];
|
|
50
|
+
}
|
|
51
|
+
const selectable = selectableDescendantIds ?? descendantIds;
|
|
52
|
+
if (isSelected) {
|
|
53
|
+
return selectedIds.filter(id => id !== nodeId && !selectable.includes(id));
|
|
54
|
+
}
|
|
55
|
+
return [...new Set([...selectedIds, ...selectable])];
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Пересчитывает выбранные id после toggle узла с каскадом вниз и опциональным walk вверх.
|
|
59
|
+
*/
|
|
60
|
+
export function toggleHierarchicalSelection(params) {
|
|
61
|
+
const isSelected = resolveIsSelected(params);
|
|
62
|
+
if (params.includeParentsInValue) {
|
|
63
|
+
return toggleWithParentsInValue({ ...params, isSelected });
|
|
64
|
+
}
|
|
65
|
+
return toggleWithoutParentsInValue({ ...params, isSelected });
|
|
66
|
+
}
|