UNPKG

terra-clinical-item-view

Version:

The Item View component allows displays to be organized into rows and column and themed, while providing means to add accessory elements and a comment.

347 lines (345 loc) 15.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.TextEmphasisTypes = exports.Layouts = exports.AccessoryAlignments = void 0; var _react = _interopRequireDefault(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _classnames = _interopRequireDefault(require("classnames")); var _bind = _interopRequireDefault(require("classnames/bind")); var _terraThemeContext = _interopRequireDefault(require("terra-theme-context")); var _terraClinicalItemDisplay = _interopRequireDefault(require("terra-clinical-item-display")); var _ItemComment = _interopRequireDefault(require("terra-clinical-item-display/lib/ItemComment")); var _ItemViewModule = _interopRequireDefault(require("./ItemView.module.scss")); var _excluded = ["layout", "trueColumn", "textEmphasis", "overrideDefaultStyling", "isTruncated", "accessoryAlignment", "startAccessory", "reserveStartAccessorySpace", "endAccessory", "displays", "comment", "refCallback", "contentWidth"]; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } var cx = _bind.default.bind(_ItemViewModule.default); var Layouts = exports.Layouts = { ONE_COLUMN: 'oneColumn', TWO_COLUMNS: 'twoColumns' }; var TextEmphasisTypes = exports.TextEmphasisTypes = { DEFAULT: 'default', START: 'start' }; var AccessoryAlignments = exports.AccessoryAlignments = { ALIGN_TOP: 'alignTop', ALIGN_CENTER: 'alignCenter' }; var propTypes = { /** * The visual column layout in which to present the displays. One of `oneColumn`, `twoColumn`. * When using the `twoColumn` layout, the displays are split between the columns by placing every other display into the second column. */ layout: _propTypes.default.oneOf(['oneColumn', 'twoColumns']), /** * Determines whether the displays are programmatically separated by row or as true columns when layout is set to `twoColumns`. * Screenreaders will read `trueColumn` displays from top to bottom, one column at a time. * However, this prop defaults to `false` and therefore the screenreader will read the displays in both columns together, left to right, one row at a time. * For accessibility purposes it is recommended to set trueColumn to `true`. */ trueColumn: _propTypes.default.bool, /** * The text color emphasis when using two columns. One of `default`, `start`. */ textEmphasis: _propTypes.default.oneOf(['default', 'start']), /** * Option to override the item view default styling so the original styling for item display is shown. */ overrideDefaultStyling: _propTypes.default.bool, /** * Whether or not all text on the view should be truncated. */ isTruncated: _propTypes.default.bool, /** * The vertical alignment of the start and end accessories. One of `alignTop`, `alignCenter`. */ accessoryAlignment: _propTypes.default.oneOf(['alignTop', 'alignCenter']), /** * The react element to be placed in the start aligned accessory position. */ startAccessory: _propTypes.default.node, /** * Indicates whether or not space is allocated for the start accessory if none is given. */ reserveStartAccessorySpace: _propTypes.default.bool, /** * The react element to be placed in the end aligned accessory position. */ endAccessory: _propTypes.default.node, /** * An array of terra-clinical-item-displays to be presented. * The Item View can only have 8 displays at a time, any additional displays passed in will be ignored. */ displays: _propTypes.default.arrayOf(_propTypes.default.element), /** * The react element for the comment element. */ comment: _propTypes.default.node, /** * Function callback for the ref of the outer most div. */ refCallback: _propTypes.default.func, /** * A string for content's width. Any valid css string. */ contentWidth: _propTypes.default.string }; var defaultProps = { layout: Layouts.ONE_COLUMN, trueColumn: false, textEmphasis: TextEmphasisTypes.DEFAULT, overrideDefaultStyling: false, isTruncated: false, accessoryAlignment: AccessoryAlignments.ALIGN_CENTER, startAccessory: undefined, reserveStartAccessorySpace: false, endAccessory: undefined, displays: [], comment: undefined, contentWidth: undefined }; var renderAccessory = function renderAccessory(accessory, reserveSpace, accessoryAlignment, type) { var accessorySection; if (accessory || reserveSpace) { var accessoryClassNames = cx('accessory', "".concat(type, "-accessory"), { 'accessory-align-center': accessoryAlignment === AccessoryAlignments.ALIGN_CENTER }, { 'accessory-align-top': accessoryAlignment === AccessoryAlignments.ALIGN_TOP }); accessorySection = /*#__PURE__*/_react.default.createElement("div", { className: accessoryClassNames }, accessory); } return accessorySection; }; var defaultEmphasisContentClassesFromIndexes = function defaultEmphasisContentClassesFromIndexes(rowIndex, rowCount) { var contentSize = 'content-primary-size'; var contentColor = 'content-primary-color'; if (rowIndex > 0) { contentSize = 'content-secondary-size'; } if (rowIndex >= 2 || rowCount === 2 && rowIndex === 1) { contentColor = 'content-secondary-color'; } return [contentSize, contentColor]; }; var startEmphasisContentClassesFromIndexes = function startEmphasisContentClassesFromIndexes(rowIndex, rowCount, contentIndex) { if (contentIndex > 0 || rowIndex >= 2) { return ['content-secondary-size', 'content-secondary-color']; } return defaultEmphasisContentClassesFromIndexes(rowIndex, rowCount); }; var classesForContent = function classesForContent(rowIndex, rowCount, contentIndex, emphasis) { var classes; if (emphasis === TextEmphasisTypes.START) { classes = startEmphasisContentClassesFromIndexes(rowIndex, rowCount, contentIndex); } else { classes = defaultEmphasisContentClassesFromIndexes(rowIndex, rowCount); } return ['content'].concat(classes); }; var renderRow = function renderRow(row, rowIndex, rowCount, emphasis, overrideDefaultStyling, contentWidth) { var rowKey = rowIndex; var style = contentWidth && { width: contentWidth }; return /*#__PURE__*/_react.default.createElement("li", { className: cx('row'), key: rowKey }, /*#__PURE__*/_react.default.createElement("ul", { className: cx('row-list'), key: rowKey }, row.map(function (display, displayIndex) { var displayKey = displayIndex; var contentClasses = overrideDefaultStyling ? 'content' : classesForContent(rowIndex, rowCount, displayIndex, emphasis); return ( /*#__PURE__*/ // eslint-disable-next-line react/forbid-dom-props _react.default.createElement("li", { style: style, className: cx(contentClasses), key: displayKey }, display) ); }))); }; var renderTwoColumns = function renderTwoColumns(displayGroup, displayGroupIndex, emphasis, overrideDefaultStyling, contentWidth) { var columnKey = displayGroupIndex; var displayCount = displayGroup.length; var containerStyling = displayGroupIndex === 0 ? 'primary-column' : 'secondary-column'; var style = contentWidth && { width: contentWidth }; return /*#__PURE__*/_react.default.createElement("li", { className: cx(containerStyling), key: columnKey }, /*#__PURE__*/_react.default.createElement("ul", { className: cx('column-list'), key: columnKey }, displayGroup.map(function (display, contentIndex) { var contentKey = contentIndex; var contentClasses = overrideDefaultStyling ? 'content' : classesForContent(contentIndex, displayCount, displayGroupIndex, emphasis); return ( /*#__PURE__*/ // eslint-disable-next-line react/forbid-dom-props _react.default.createElement("li", { style: style, className: cx(contentClasses), key: contentKey }, display) ); }))); }; var renderColumn = function renderColumn(displays, emphasis, overrideDefaultStyling, contentWidth) { var displayCount = displays.length; var style = contentWidth && { width: contentWidth }; return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("ul", { className: cx('column-list-container') }, displays.map(function (display, displayIndex) { var contentKey = displayIndex; /** * We are only ever rendering one column so zero is being passed into classesForContent for the contentIndex variable. * classesForContent would usually take in an index for that but in this case we don't have one explicitly. */ var contentClasses = overrideDefaultStyling ? 'content' : classesForContent(displayIndex, displayCount, 0, emphasis); return ( /*#__PURE__*/ // eslint-disable-next-line react/forbid-dom-props _react.default.createElement("li", { style: style, className: cx(contentClasses), key: contentKey }, display) ); }))); }; var renderByRowView = function renderByRowView(displays, emphasis, overrideDefaultStyling, contentWidth) { var displayGroups = []; while (displays.length) { displayGroups.push(displays.splice(0, 2)); } return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("ul", { className: cx('row-list-container') }, displayGroups.map(function (displayRow, rowIndex) { var row = renderRow(displayRow, rowIndex, displayGroups.length, emphasis, overrideDefaultStyling, contentWidth); return row; }))); }; var renderSingleDisplayView = function renderSingleDisplayView(singleDisplay, overrideDefaultStyling, contentWidth) { /** * Since this is always a singular display, the content styling will be the primary defaults if they are not overridden. * We don't have to call into the classesForContent method and instead can just set the primary size and color here. */ var contentClass = overrideDefaultStyling ? 'content' : ['content', 'content-primary-size', 'content-primary-color']; var style = contentWidth && { width: contentWidth }; return /*#__PURE__*/_react.default.createElement("div", { className: cx('single-result-column-container') }, /*#__PURE__*/_react.default.createElement("div", { style: style, className: cx(contentClass) }, singleDisplay)); }; var twoColumnGrouping = function twoColumnGrouping(displays) { var count = 0; var displayGroups = []; var primaryColumn = []; var secondaryColumn = []; while (displays.length) { count += 1; if (count % 2 === 0) { secondaryColumn.push(displays.splice(0, 1)); } else { primaryColumn.push(displays.splice(0, 1)); } } displayGroups.push(primaryColumn); displayGroups.push(secondaryColumn); return displayGroups; }; var renderView = function renderView(displays, layout, emphasis, overrideDefaultStyling, trueColumn, contentWidth) { if (displays === null || displays === undefined || !displays.length) { return undefined; } var displaysSlice = displays.slice(0, 8); /** * If there is only one display we don't want to return it as an item in a list. * The method renderSingleDisplayView here takes in the single display and returns it within simple divs instead. */ if (displaysSlice.length === 1) { return renderSingleDisplayView(displaysSlice, overrideDefaultStyling, contentWidth); } if (layout === Layouts.TWO_COLUMNS) { if (!trueColumn) { return renderByRowView(displaysSlice, emphasis, overrideDefaultStyling, contentWidth); } var displayGroups = twoColumnGrouping(displaysSlice); return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("ul", { className: cx('column-list-container') }, displayGroups.map(function (group, index) { var column = renderTwoColumns(group, index, emphasis, overrideDefaultStyling, contentWidth); return column; }))); } return /*#__PURE__*/_react.default.createElement("div", null, renderColumn(displaysSlice, emphasis, overrideDefaultStyling, contentWidth)); }; var isDisplaysTruncated = function isDisplaysTruncated(displays) { if (displays) { var displaysSlice = displays.slice(0, 8); for (var i = 0; i < displaysSlice.length; i += 1) { var _displaysSlice$i; if (((_displaysSlice$i = displaysSlice[i]) === null || _displaysSlice$i === void 0 ? void 0 : _displaysSlice$i.props.isTruncated) === true) { return true; } } } return false; }; var ItemView = function ItemView(_ref) { var layout = _ref.layout, trueColumn = _ref.trueColumn, textEmphasis = _ref.textEmphasis, overrideDefaultStyling = _ref.overrideDefaultStyling, isTruncated = _ref.isTruncated, accessoryAlignment = _ref.accessoryAlignment, startAccessory = _ref.startAccessory, reserveStartAccessorySpace = _ref.reserveStartAccessorySpace, endAccessory = _ref.endAccessory, displays = _ref.displays, comment = _ref.comment, refCallback = _ref.refCallback, contentWidth = _ref.contentWidth, customProps = _objectWithoutProperties(_ref, _excluded); var theme = _react.default.useContext(_terraThemeContext.default); var isTrueTwoColumnView = layout === Layouts.TWO_COLUMNS && trueColumn; var isTruncatedDisplay = isTruncated || isDisplaysTruncated(displays); var viewClassNames = (0, _classnames.default)(cx('item-view', { 'is-truncated': isTruncated }, { 'one-column': layout === Layouts.ONE_COLUMN }, { 'two-columns': isTrueTwoColumnView && !isTruncatedDisplay }, { 'truncated-two-columns': isTrueTwoColumnView && isTruncatedDisplay }, { 'two-columns-by-row': layout === Layouts.TWO_COLUMNS && !trueColumn }, theme.className), customProps.className); return /*#__PURE__*/_react.default.createElement("div", _extends({}, customProps, { className: viewClassNames, ref: refCallback }), renderAccessory(startAccessory, reserveStartAccessorySpace, accessoryAlignment, 'start'), /*#__PURE__*/_react.default.createElement("div", { className: cx('body') }, renderView(displays, layout, textEmphasis, overrideDefaultStyling, trueColumn, contentWidth), comment), renderAccessory(endAccessory, false, accessoryAlignment, 'end')); }; ItemView.propTypes = propTypes; ItemView.defaultProps = defaultProps; ItemView.Display = _terraClinicalItemDisplay.default; ItemView.Comment = _ItemComment.default; var _default = exports.default = ItemView;