UNPKG

@awsui/components-react

Version:

On July 19th, 2022, we launched [Cloudscape Design System](https://cloudscape.design). Cloudscape is an evolution of AWS-UI. It consists of user interface guidelines, front-end components, design resources, and development tools for building intuitive, en

135 lines • 10.7 kB
import { __rest } from "tslib"; // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import React, { useCallback, useImperativeHandle, useRef } from 'react'; import clsx from 'clsx'; import { useContainerQuery } from '@awsui/component-toolkit'; import { InternalContainerAsSubstep } from '../container/internal'; import { useInternalI18n } from '../i18n/context'; import { AnalyticsFunnelSubStep } from '../internal/analytics/components/analytics-funnel'; import { getBaseProps } from '../internal/base-component'; import { CollectionLabelContext } from '../internal/context/collection-label-context'; import { LinkDefaultVariantContext } from '../internal/context/link-default-variant-context'; import useBaseComponent from '../internal/hooks/use-base-component'; import { useMergeRefs } from '../internal/hooks/use-merge-refs'; import { useMobile } from '../internal/hooks/use-mobile'; import useMouseDownTarget from '../internal/hooks/use-mouse-down-target'; import { useVisualRefresh } from '../internal/hooks/use-visual-mode'; import { applyDisplayName } from '../internal/utils/apply-display-name'; import InternalLiveRegion from '../live-region/internal'; import InternalStatusIndicator from '../status-indicator/internal'; import { focusMarkers, SelectionControl, useSelection, useSelectionFocusMove, } from '../table/selection'; import stickyScrolling from '../table/sticky-scrolling'; import ToolsHeader from '../table/tools-header'; import { getItemKey } from '../table/utils'; import { getCardsPerRow } from './cards-layout-helper'; import styles from './styles.css.js'; const Cards = React.forwardRef(function (_a, ref) { var { items = [], cardDefinition, cardsPerRow = [], header, filter, pagination, preferences, empty, loading, loadingText, trackBy, selectedItems, selectionType, isItemDisabled, onSelectionChange, ariaLabels, visibleSections, stickyHeader, stickyHeaderVerticalOffset, variant = 'container', renderAriaLive, firstIndex = 1, totalItemsCount, entireCardClickable } = _a, rest = __rest(_a, ["items", "cardDefinition", "cardsPerRow", "header", "filter", "pagination", "preferences", "empty", "loading", "loadingText", "trackBy", "selectedItems", "selectionType", "isItemDisabled", "onSelectionChange", "ariaLabels", "visibleSections", "stickyHeader", "stickyHeaderVerticalOffset", "variant", "renderAriaLive", "firstIndex", "totalItemsCount", "entireCardClickable"]); const { __internalRootRef } = useBaseComponent('Cards', { props: { entireCardClickable, selectionType, stickyHeader, variant }, metadata: { usesVisibleSections: !!visibleSections }, }); const baseProps = getBaseProps(rest); const isRefresh = useVisualRefresh(); const isMobile = useMobile(); const computedVariant = isRefresh ? variant : 'container'; const headerIdRef = useRef(undefined); const setHeaderRef = useCallback((id) => { headerIdRef.current = id; }, []); const isLabelledByHeader = !(ariaLabels === null || ariaLabels === void 0 ? void 0 : ariaLabels.cardsLabel) && !!header; const [columns, measureRef] = useContainerQuery(({ contentBoxWidth }) => getCardsPerRow(contentBoxWidth, cardsPerRow), [cardsPerRow]); const refObject = useRef(null); const mergedRef = useMergeRefs(measureRef, refObject, __internalRootRef); const getMouseDownTarget = useMouseDownTarget(); const i18n = useInternalI18n('cards'); const { isItemSelected, getItemSelectionProps } = useSelection({ items, trackBy, selectedItems, selectionType, isItemDisabled, onSelectionChange, ariaLabels: { itemSelectionLabel: ariaLabels === null || ariaLabels === void 0 ? void 0 : ariaLabels.itemSelectionLabel, selectionGroupLabel: i18n('ariaLabels.selectionGroupLabel', ariaLabels === null || ariaLabels === void 0 ? void 0 : ariaLabels.selectionGroupLabel), }, }); const hasToolsHeader = header || filter || pagination || preferences; const hasFooterPagination = isMobile && variant === 'full-page' && !!pagination; const headerRef = useRef(null); const { scrollToTop, scrollToItem } = stickyScrolling(refObject, headerRef); stickyHeader = !isMobile && stickyHeader; const onCardFocus = event => { // When an element inside card receives focus we want to adjust the scroll. // However, that behavior is unwanted when the focus is received as result of a click // as it causes the click to never reach the target element. if (stickyHeader && !event.currentTarget.contains(getMouseDownTarget())) { scrollToItem(event.currentTarget); } }; useImperativeHandle(ref, () => ({ scrollToTop: () => { if (stickyHeader) { scrollToTop(); } }, }), [stickyHeader, scrollToTop]); let status; if (loading) { status = (React.createElement("div", { className: styles.loading }, React.createElement(InternalStatusIndicator, { type: "loading" }, React.createElement(InternalLiveRegion, { tagName: "span" }, loadingText)))); } else if (empty && !items.length) { status = React.createElement("div", { className: styles.empty }, empty); } return (React.createElement(LinkDefaultVariantContext.Provider, { value: { defaultVariant: 'primary' } }, React.createElement(AnalyticsFunnelSubStep, null, React.createElement("div", Object.assign({}, baseProps, { className: clsx(baseProps.className, styles.root), ref: mergedRef }), React.createElement(InternalContainerAsSubstep, { header: hasToolsHeader && (React.createElement("div", { className: clsx(styles.header, isRefresh && styles['header-refresh'], styles[`header-variant-${computedVariant}`]) }, React.createElement(CollectionLabelContext.Provider, { value: { assignId: setHeaderRef } }, React.createElement(ToolsHeader, { header: header, filter: filter, pagination: pagination, preferences: preferences })))), footer: hasFooterPagination && React.createElement("div", { className: styles['footer-pagination'] }, pagination), disableContentPaddings: true, disableHeaderPaddings: computedVariant === 'full-page', variant: computedVariant === 'container' ? 'cards' : computedVariant, __stickyHeader: stickyHeader, __stickyOffset: stickyHeaderVerticalOffset, __headerRef: headerRef, __fullPage: computedVariant === 'full-page', __disableFooterDivider: true }, React.createElement("div", { className: clsx(hasToolsHeader && styles['has-header'], isRefresh && styles.refresh, styles[`header-variant-${computedVariant}`]) }, !!renderAriaLive && !!firstIndex && (React.createElement(InternalLiveRegion, { hidden: true, tagName: "span" }, React.createElement("span", null, renderAriaLive({ totalItemsCount, firstIndex, lastIndex: firstIndex + items.length - 1 })))), status !== null && status !== void 0 ? status : (React.createElement(CardsList, { items: items, cardDefinition: cardDefinition, trackBy: trackBy, selectionType: selectionType, columns: columns, isItemSelected: isItemSelected, getItemSelectionProps: getItemSelectionProps, visibleSections: visibleSections, onFocus: onCardFocus, ariaLabel: ariaLabels === null || ariaLabels === void 0 ? void 0 : ariaLabels.cardsLabel, ariaLabelledby: isLabelledByHeader && headerIdRef.current ? headerIdRef.current : undefined, entireCardClickable: entireCardClickable })))))))); }); export default Cards; const CardsList = ({ items, cardDefinition, trackBy, selectionType, columns, isItemSelected, getItemSelectionProps, visibleSections, onFocus, ariaLabelledby, ariaLabel, entireCardClickable, }) => { const selectable = !!selectionType; const canClickEntireCard = selectable && entireCardClickable; const isRefresh = useVisualRefresh(); const { moveFocusDown, moveFocusUp } = useSelectionFocusMove(selectionType, items.length); let visibleSectionsDefinition = cardDefinition.sections || []; visibleSectionsDefinition = visibleSections ? visibleSectionsDefinition.filter((section) => section.id && visibleSections.indexOf(section.id) !== -1) : visibleSectionsDefinition; let listRole = undefined; let listItemRole = undefined; if (selectable) { listRole = 'group'; listItemRole = 'presentation'; } return (React.createElement("ol", Object.assign({ className: clsx(styles.list, styles[`list-grid-${columns || 1}`]), role: listRole, "aria-labelledby": ariaLabelledby, "aria-label": ariaLabel }, (focusMarkers && focusMarkers.root)), items.map((item, index) => (React.createElement("li", Object.assign({ className: clsx(styles.card, { [styles['card-selectable']]: selectable, [styles['card-selected']]: selectable && isItemSelected(item), }), key: getItemKey(trackBy, item, index), onFocus: onFocus }, (focusMarkers && focusMarkers.item), { role: listItemRole }), React.createElement("div", { className: clsx(styles['card-inner'], isRefresh && styles.refresh), onClick: canClickEntireCard ? event => { var _a; getItemSelectionProps === null || getItemSelectionProps === void 0 ? void 0 : getItemSelectionProps(item).onChange(); // Manually move focus to the native input (checkbox or radio button) (_a = event.currentTarget.querySelector('input')) === null || _a === void 0 ? void 0 : _a.focus(); } : undefined }, React.createElement("div", { className: styles['card-header'] }, React.createElement("div", { className: styles['card-header-inner'] }, cardDefinition.header ? cardDefinition.header(item) : ''), getItemSelectionProps && (React.createElement("div", { className: styles['selection-control'] }, React.createElement(SelectionControl, Object.assign({ onFocusDown: moveFocusDown, onFocusUp: moveFocusUp }, getItemSelectionProps(item)))))), visibleSectionsDefinition.map(({ width = 100, header, content, id }, index) => (React.createElement("div", { key: id || index, className: styles.section, style: { width: `${width}%` } }, header ? React.createElement("div", { className: styles['section-header'] }, header) : '', content ? React.createElement("div", { className: styles['section-content'] }, content(item)) : ''))))))))); }; applyDisplayName(Cards, 'Cards'); //# sourceMappingURL=index.js.map