// @ts-nocheck /* * Copyright 2021 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 {AriaCardProps, SpectrumCardProps} from '@react-types/card'; import {Checkbox} from '@react-spectrum/checkbox'; import {classNames, SlotProvider, useDOMRef, useHasChild, useStyleProps} from '@react-spectrum/utils'; import {DOMRef, Node} from '@react-types/shared'; import {filterDOMProps, mergeProps, nodeContains, useLayoutEffect, useResizeObserver, useSlotId} from '@react-aria/utils'; import {FocusRing, getFocusableTreeWalker} from '@react-aria/focus'; import React, {HTMLAttributes, useCallback, useMemo, useRef, useState} from 'react'; import styles from '@adobe/spectrum-css-temp/components/card/vars.css'; import {useCardViewContext} from './CardViewContext'; import {useFocusWithin, useHover} from '@react-aria/interactions'; import {useProviderProps} from '@react-spectrum/provider'; interface CardBaseProps extends SpectrumCardProps { articleProps?: HTMLAttributes, item?: Node } /** * TODO: Add description of component here. */ export const CardBase = React.forwardRef(function CardBase(props: CardBaseProps, ref: DOMRef) { props = useProviderProps(props); let context = useCardViewContext() || {}; // we can call again here, won't change from Card.tsx let {state} = context; let manager = state?.selectionManager; let { isQuiet, orientation = 'vertical', articleProps = {role: 'article'}, item, layout, children } = props; let key = item?.key; let isSelected = manager?.isSelected(key); let isDisabled = state?.disabledKeys.has(key); let onChange = () => manager?.select(key); let {styleProps} = useStyleProps(props); let {cardProps, titleProps, contentProps} = useCard(props); let domRef = useDOMRef(ref); let gridRef = useRef(undefined); let checkboxRef = useRef(null); // cards are only interactive if there is a selection manager and it allows selection let {hoverProps, isHovered} = useHover({isDisabled: manager === undefined || manager?.selectionMode === 'none' || isDisabled}); let [isFocused, setIsFocused] = useState(false); let {focusWithinProps} = useFocusWithin({ onFocusWithinChange: setIsFocused, isDisabled }); // ToDo: see css for comment about avatar under selector .spectrum-Card--noLayout.spectrum-Card--default let hasPreviewImage = useHasChild(`.${styles['spectrum-Card-image']}`, gridRef); let hasPreviewIllustration = useHasChild(`.${styles['spectrum-Card-illustration']}`, gridRef); let hasPreview = hasPreviewImage || hasPreviewIllustration; // this is for horizontal cards let [height, setHeight] = useState(NaN); let updateHeight = useCallback(() => { if (orientation !== 'horizontal') { return; } let cardHeight = gridRef.current.getBoundingClientRect().height; setHeight(cardHeight); }, [orientation, gridRef, setHeight]); useResizeObserver({ref: gridRef, onResize: updateHeight}); let aspectRatioEnforce = undefined; if (orientation === 'horizontal' && !isNaN(height)) { aspectRatioEnforce = { height: `${height}px`, width: `${height}px` }; } let slots = useMemo(() => ({ image: {UNSAFE_className: classNames(styles, 'spectrum-Card-image'), objectFit: orientation === 'horizontal' ? 'cover' : 'contain', alt: '', ...aspectRatioEnforce}, illustration: {UNSAFE_className: classNames(styles, 'spectrum-Card-illustration'), ...aspectRatioEnforce}, avatar: {UNSAFE_className: classNames(styles, 'spectrum-Card-avatar'), size: 'avatar-size-400'}, heading: {UNSAFE_className: classNames(styles, 'spectrum-Card-heading'), ...titleProps}, content: {UNSAFE_className: classNames(styles, 'spectrum-Card-content'), ...contentProps}, detail: {UNSAFE_className: classNames(styles, 'spectrum-Card-detail')} // eslint-disable-next-line react-hooks/exhaustive-deps }), [titleProps, contentProps, height, isQuiet, orientation]); useLayoutEffect(() => { if (gridRef?.current) { let walker = getFocusableTreeWalker(gridRef.current); let nextNode = walker.nextNode(); while (nextNode != null) { if (checkboxRef.current && !nodeContains(checkboxRef.current.UNSAFE_getDOMNode(), nextNode)) { console.warn('Card does not support focusable elements, please contact the team regarding your use case.'); break; } nextNode = walker.nextNode(); } } }, [children]); return (
{manager && manager.selectionMode !== 'none' && (
)} {children}
); }); interface AriaCardOptions extends AriaCardProps { } interface CardAria { cardProps: HTMLAttributes, titleProps: HTMLAttributes, contentProps: HTMLAttributes } function useCard(props: AriaCardOptions): CardAria { let titleId = useSlotId(); let descriptionId = useSlotId(); let titleProps = useMemo(() => ({ id: titleId }), [titleId]); let contentProps = useMemo(() => ({ id: descriptionId }), [descriptionId]); return { cardProps: { ...filterDOMProps(props), 'aria-labelledby': titleId, 'aria-describedby': descriptionId, tabIndex: 0 }, titleProps, contentProps }; }