133 lines
4.2 KiB
JavaScript
133 lines
4.2 KiB
JavaScript
'use client';
|
|
import * as React from 'react';
|
|
import { getIntrinsicElementProps, useMergedRefs, slot } from '@fluentui/react-utilities';
|
|
import { useFocusableGroup, useFocusWithin } from '@fluentui/react-tabster';
|
|
import { useCardSelectable } from './useCardSelectable';
|
|
import { cardContextDefaultValue } from './CardContext';
|
|
const focusMap = {
|
|
off: undefined,
|
|
'no-tab': 'limited-trap-focus',
|
|
'tab-exit': 'limited',
|
|
'tab-only': 'unlimited'
|
|
};
|
|
/**
|
|
* Create the state for interactive cards.
|
|
*
|
|
* This internal hook defines if the card is interactive
|
|
* and control focus properties based on that.
|
|
*
|
|
* @param props - props from this instance of Card
|
|
*/ const useCardInteractive = ({ focusMode: initialFocusMode, disabled = false, ...props })=>{
|
|
const interactive = [
|
|
'onClick',
|
|
'onDoubleClick',
|
|
'onMouseUp',
|
|
'onMouseDown',
|
|
'onPointerUp',
|
|
'onPointerDown',
|
|
'onTouchStart',
|
|
'onTouchEnd',
|
|
'onDragStart',
|
|
'onDragEnd'
|
|
].some((prop)=>props[prop]);
|
|
// default focusMode to tab-only when interactive, and off when not
|
|
const focusMode = initialFocusMode !== null && initialFocusMode !== void 0 ? initialFocusMode : interactive ? 'no-tab' : 'off';
|
|
const groupperAttrs = useFocusableGroup({
|
|
tabBehavior: focusMap[focusMode]
|
|
});
|
|
if (disabled) {
|
|
return {
|
|
interactive: false,
|
|
focusAttributes: null
|
|
};
|
|
}
|
|
if (focusMode === 'off') {
|
|
return {
|
|
interactive,
|
|
focusAttributes: null
|
|
};
|
|
}
|
|
return {
|
|
interactive,
|
|
focusAttributes: {
|
|
...groupperAttrs,
|
|
tabIndex: 0
|
|
}
|
|
};
|
|
};
|
|
/**
|
|
* Create the state required to render Card.
|
|
*
|
|
* The returned state can be modified with hooks such as useCardStyles_unstable,
|
|
* before being passed to renderCard_unstable.
|
|
*
|
|
* @param props - props from this instance of Card
|
|
* @param ref - reference to the root element of Card
|
|
*/ export const useCard_unstable = (props, ref)=>{
|
|
const { appearance = 'filled', orientation = 'vertical', size = 'medium', ...cardProps } = props;
|
|
const state = useCardBase_unstable(cardProps, ref);
|
|
return {
|
|
...state,
|
|
appearance,
|
|
orientation,
|
|
size
|
|
};
|
|
};
|
|
/**
|
|
* Base hook for Card component, which manages state related to interactivity, selection,
|
|
* focus management, ARIA attributes, and slot structure without design props.
|
|
*
|
|
* @param props - props from this instance of Card
|
|
* @param ref - reference to the root element of Card
|
|
*/ export const useCardBase_unstable = (props, ref)=>{
|
|
const { disabled = false, ...restProps } = props;
|
|
const [referenceId, setReferenceId] = React.useState(cardContextDefaultValue.selectableA11yProps.referenceId);
|
|
const [referenceLabel, setReferenceLabel] = React.useState(cardContextDefaultValue.selectableA11yProps.referenceId);
|
|
const cardBaseRef = useFocusWithin();
|
|
const { selectable, selected, selectableCardProps, selectFocused, checkboxSlot, floatingActionSlot } = useCardSelectable(props, {
|
|
referenceId,
|
|
referenceLabel
|
|
}, cardBaseRef);
|
|
const cardRef = useMergedRefs(cardBaseRef, ref);
|
|
const { interactive, focusAttributes } = useCardInteractive(props);
|
|
let cardRootProps = {
|
|
...!selectable ? focusAttributes : null,
|
|
...restProps,
|
|
...selectableCardProps
|
|
};
|
|
if (disabled) {
|
|
cardRootProps = {
|
|
...restProps,
|
|
'aria-disabled': true,
|
|
onClick: undefined
|
|
};
|
|
}
|
|
return {
|
|
interactive,
|
|
selectable,
|
|
selectFocused,
|
|
selected,
|
|
disabled,
|
|
selectableA11yProps: {
|
|
setReferenceId,
|
|
referenceId,
|
|
referenceLabel,
|
|
setReferenceLabel
|
|
},
|
|
components: {
|
|
root: 'div',
|
|
floatingAction: 'div',
|
|
checkbox: 'input'
|
|
},
|
|
root: slot.always(getIntrinsicElementProps('div', {
|
|
ref: cardRef,
|
|
role: 'group',
|
|
...cardRootProps
|
|
}), {
|
|
elementType: 'div'
|
|
}),
|
|
floatingAction: floatingActionSlot,
|
|
checkbox: checkboxSlot
|
|
};
|
|
};
|