Files
NapCatQQ/.pnpm-store/v3/files/85/35c1f74dd88d09af43304f48c8064e40ba730e968e0e345ec1c467aa720996e652ee9563454f9d806de89f8e5dc355929f2f2d837c34a7008582411d79bde8
T
TanTan e311ca3275
Build NapCat Artifacts / Type Check & Test (push) Waiting to run
docs: 仓库链接指向本镜像仓库
2026-11-13 09:25:58 +08:00

131 lines
4.7 KiB
Plaintext

/*
* Copyright 2020 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
import {DisabledBehavior, FocusStrategy, Key, MultipleSelection, SelectionBehavior, SelectionMode} from '@react-types/shared';
import {MultipleSelectionState} from './types';
import {Selection} from './Selection';
import {useControlledState} from '@react-stately/utils';
import {useEffect, useMemo, useRef, useState} from 'react';
function equalSets(setA, setB) {
if (setA.size !== setB.size) {
return false;
}
for (let item of setA) {
if (!setB.has(item)) {
return false;
}
}
return true;
}
export interface MultipleSelectionStateProps extends MultipleSelection {
/** How multiple selection should behave in the collection. */
selectionBehavior?: SelectionBehavior,
/** Whether onSelectionChange should fire even if the new set of keys is the same as the last. */
allowDuplicateSelectionEvents?: boolean,
/** Whether `disabledKeys` applies to all interactions, or only selection. */
disabledBehavior?: DisabledBehavior
}
/**
* Manages state for multiple selection and focus in a collection.
*/
export function useMultipleSelectionState(props: MultipleSelectionStateProps): MultipleSelectionState {
let {
selectionMode = 'none' as SelectionMode,
disallowEmptySelection = false,
allowDuplicateSelectionEvents,
selectionBehavior: selectionBehaviorProp = 'toggle',
disabledBehavior = 'all'
} = props;
// We want synchronous updates to `isFocused` and `focusedKey` after their setters are called.
// But we also need to trigger a react re-render. So, we have both a ref (sync) and state (async).
let isFocusedRef = useRef(false);
let [, setFocused] = useState(false);
let focusedKeyRef = useRef<Key | null>(null);
let childFocusStrategyRef = useRef<FocusStrategy | null>(null);
let [, setFocusedKey] = useState<Key | null>(null);
let selectedKeysProp = useMemo(() => convertSelection(props.selectedKeys), [props.selectedKeys]);
let defaultSelectedKeys = useMemo(() => convertSelection(props.defaultSelectedKeys, new Selection()), [props.defaultSelectedKeys]);
let [selectedKeys, setSelectedKeys] = useControlledState(
selectedKeysProp,
defaultSelectedKeys!,
props.onSelectionChange
);
let disabledKeysProp = useMemo(() =>
props.disabledKeys ? new Set(props.disabledKeys) : new Set<Key>()
, [props.disabledKeys]);
let [selectionBehavior, setSelectionBehavior] = useState(selectionBehaviorProp);
// If the selectionBehavior prop is set to replace, but the current state is toggle (e.g. due to long press
// to enter selection mode on touch), and the selection becomes empty, reset the selection behavior.
if (selectionBehaviorProp === 'replace' && selectionBehavior === 'toggle' && typeof selectedKeys === 'object' && selectedKeys.size === 0) {
setSelectionBehavior('replace');
}
// If the selectionBehavior prop changes, update the state as well.
let lastSelectionBehavior = useRef(selectionBehaviorProp);
useEffect(() => {
if (selectionBehaviorProp !== lastSelectionBehavior.current) {
setSelectionBehavior(selectionBehaviorProp);
lastSelectionBehavior.current = selectionBehaviorProp;
}
}, [selectionBehaviorProp]);
return {
selectionMode,
disallowEmptySelection,
selectionBehavior,
setSelectionBehavior,
get isFocused() {
return isFocusedRef.current;
},
setFocused(f) {
isFocusedRef.current = f;
setFocused(f);
},
get focusedKey() {
return focusedKeyRef.current;
},
get childFocusStrategy() {
return childFocusStrategyRef.current;
},
setFocusedKey(k, childFocusStrategy = 'first') {
focusedKeyRef.current = k;
childFocusStrategyRef.current = childFocusStrategy;
setFocusedKey(k);
},
selectedKeys,
setSelectedKeys(keys) {
if (allowDuplicateSelectionEvents || !equalSets(keys, selectedKeys)) {
setSelectedKeys(keys);
}
},
disabledKeys: disabledKeysProp,
disabledBehavior
};
}
function convertSelection(selection: 'all' | Iterable<Key> | null | undefined, defaultValue?: Selection): 'all' | Set<Key> | undefined {
if (!selection) {
return defaultValue;
}
return selection === 'all'
? 'all'
: new Selection(selection);
}