/* * SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. * * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch B.V. licenses this file 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 CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import React, { CSSProperties, Fragment, FunctionComponent, ReactElement, ReactNode, TdHTMLAttributes, } from 'react'; import classNames from 'classnames'; import { CommonProps } from '../common'; import { HorizontalAlignment, LEFT_ALIGNMENT, RIGHT_ALIGNMENT, CENTER_ALIGNMENT, } from '../../services'; import { resolveWidthAsStyle } from './utils'; import { useIsWithinBreakpoints } from '../../services/hooks/useIsWithinBreakpoints'; interface OuiTableRowCellSharedPropsShape { /** * Horizontal alignment of the text in the cell */ align?: HorizontalAlignment; /** * Don't allow line breaks within cells */ showOnHover?: boolean; /** * Setting `textOnly` to `false` will break words unnecessarily on FF and * IE. To combat this problem on FF, wrap contents with the css utility * `.oui-textBreakWord`. */ textOnly?: boolean; /** * _Should only be used for action cells_ */ truncateText?: boolean; width?: string | number; } interface OuiTableRowCellMobileOptionsShape { /** * If false, will not render the cell at all for mobile */ show?: boolean; /** * Only show for mobile? If true, will not render the column at all for desktop */ only?: boolean; /** * Custom render/children if different from desktop */ render?: ReactNode; /** * The column's header for use in mobile view (automatically passed down * when using `OuiBasicTable`). * Or pass `false` to not show a header at all. */ header?: ReactNode | boolean; /** * Increase text size compared to rest of cells */ enlarge?: boolean; /** * Allocates 100% of the width of the container in mobile view * (typically cells are contained to 50%) */ fullWidth?: boolean; /** * Applies the value to the width of the cell in mobile view (typically 50%) */ width?: CSSProperties['width']; } export interface OuiTableRowCellProps { /** * Indicates if the column is dedicated to icon-only actions (currently * affects mobile only) */ hasActions?: boolean; /** * _DEPRECATED: use `mobileOptions.header`_ * The column's header title for use in mobile view (will be added as a * data-attr) */ header?: string; /** * _DEPRECATED: use `mobileOptions.show = false`_ * Indicates if the column should not show for mobile users (typically * hidden because a custom mobile header utilizes the column's contents) */ hideForMobile?: boolean; /** * Indicates if the column is dedicated as the expandable row toggle */ isExpander?: boolean; /** * _DEPRECATED: use `mobileOptions.fullWidth`_ * Allocates 100% of the width of the container in mobile view * (typically cells are contained to 50%) */ isMobileFullWidth?: boolean; /** * _DEPRECATED: use `mobileOptions.only = true & mobileOptions.header = * false`_ * Indicates if the column was created to be the row's heading in mobile * view. It won't display column's header inline and it the column will * be hidden at larger screens) */ isMobileHeader?: boolean; /** * Mobile options for displaying differently at small screens */ mobileOptions?: OuiTableRowCellMobileOptionsShape & OuiTableRowCellSharedPropsShape; /** * Indicates whether the cell should be marked as the heading for its row */ setScopeRow?: boolean; } type Props = CommonProps & TdHTMLAttributes & OuiTableRowCellSharedPropsShape & OuiTableRowCellProps; export const OuiTableRowCell: FunctionComponent = ({ align = LEFT_ALIGNMENT, children, className, truncateText, setScopeRow, showOnHover, textOnly = true, hasActions, isExpander, mobileOptions = { show: true, }, // Soon to be deprecated for {...mobileOptions} header, hideForMobile, isMobileHeader, isMobileFullWidth, style, width, ...rest }) => { const cellClasses = classNames('ouiTableRowCell', { 'ouiTableRowCell--hasActions': hasActions, 'ouiTableRowCell--isExpander': isExpander, 'ouiTableRowCell--hideForDesktop': mobileOptions.only || isMobileHeader, 'ouiTableRowCell--enlargeForMobile': mobileOptions.enlarge || isMobileHeader, 'ouiTableRowCell--isMobileFullWidth': mobileOptions.fullWidth || isMobileFullWidth || isMobileHeader, }); const contentClasses = classNames('ouiTableCellContent', className, { 'ouiTableCellContent--alignRight': align === RIGHT_ALIGNMENT, 'ouiTableCellContent--alignCenter': align === CENTER_ALIGNMENT, 'ouiTableCellContent--showOnHover': showOnHover, 'ouiTableCellContent--truncateText': truncateText, // We're doing this rigamarole instead of creating `ouiTableCellContent--textOnly` for BWC // purposes for the time-being. 'ouiTableCellContent--overflowingContent': textOnly !== true, }); const mobileContentClasses = classNames('ouiTableCellContent', className, { 'ouiTableCellContent--alignRight': mobileOptions.align === RIGHT_ALIGNMENT || align === RIGHT_ALIGNMENT, 'ouiTableCellContent--alignCenter': mobileOptions.align === CENTER_ALIGNMENT || align === RIGHT_ALIGNMENT, 'ouiTableCellContent--showOnHover': mobileOptions.showOnHover || showOnHover, 'ouiTableCellContent--truncateText': mobileOptions.truncateText || truncateText, // We're doing this rigamarole instead of creating `ouiTableCellContent--textOnly` for BWC // purposes for the time-being. 'ouiTableCellContent--overflowingContent': mobileOptions.textOnly !== true || textOnly !== true, }); const childClasses = classNames({ ouiTableCellContent__text: textOnly === true, ouiTableCellContent__hoverItem: showOnHover, }); const widthValue = useIsWithinBreakpoints(['xs', 's', 'm']) && mobileOptions.width ? mobileOptions.width : width; const styleObj = resolveWidthAsStyle(style, widthValue); function modifyChildren(children: ReactNode) { let modifiedChildren = children; if (textOnly === true) { modifiedChildren = {children}; } else if (React.isValidElement(children)) { modifiedChildren = React.Children.map( children, (child: ReactElement) => React.cloneElement(child, { className: classNames(child.props.className, childClasses), }) ); } return modifiedChildren; } const childrenNode = modifyChildren(children); const hideForMobileClasses = 'ouiTableRowCell--hideForMobile'; const showForMobileClasses = 'ouiTableRowCell--hideForDesktop'; const Element = setScopeRow ? 'th' : 'td'; const sharedProps = { scope: setScopeRow ? 'row' : undefined, style: styleObj, ...rest, }; if (mobileOptions.show === false || hideForMobile) { return (
{childrenNode}
); } else { return ( {/* Mobile-only header */} {(mobileOptions.header || header) && !isMobileHeader && (
{mobileOptions.header || header}
)} {/* Content depending on mobile render existing */} {mobileOptions.render ? (
{modifyChildren(mobileOptions.render)}
{childrenNode}
) : (
{childrenNode}
)}
); } };