mirror of
https://github.com/ansible/awx.git
synced 2026-03-08 21:19:26 -02:30
Standardize chip height (#213)
* make all chips the same size * create DetailList, Detail components; clean up Chips, ChipGroup * delete BasicChip in favor of <Chip isReadOnly> * create our own ChipGroup to handle overflow
This commit is contained in:
@@ -1,21 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { Chip } from '@patternfly/react-core';
|
||||
import './basicChip.scss';
|
||||
|
||||
const BasicChip = ({ children, onToggle, isOverflowChip }) => (
|
||||
<Chip
|
||||
className="awx-c-chip--basic"
|
||||
onClick={onToggle}
|
||||
isOverflowChip={isOverflowChip}
|
||||
>
|
||||
{children}
|
||||
</Chip>
|
||||
);
|
||||
|
||||
BasicChip.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
};
|
||||
|
||||
export default BasicChip;
|
||||
@@ -1,18 +0,0 @@
|
||||
.awx-c-chip--basic {
|
||||
padding: 3px 8px;
|
||||
height: 24px;
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&.pf-c-chip {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
&.pf-m-overflow {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&:not(.pf-m-overflow) .pf-c-button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
18
src/components/Chip/Chip.jsx
Normal file
18
src/components/Chip/Chip.jsx
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
import { Chip } from '@patternfly/react-core';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export default styled(Chip)`
|
||||
--pf-c-chip--m-read-only--PaddingTop: 3px;
|
||||
--pf-c-chip--m-read-only--PaddingRight: 8px;
|
||||
--pf-c-chip--m-read-only--PaddingBottom: 3px;
|
||||
--pf-c-chip--m-read-only--PaddingLeft: 8px;
|
||||
|
||||
& > .pf-c-button {
|
||||
padding: 3px 8px;
|
||||
}
|
||||
|
||||
${props => (props.isOverflowChip && `
|
||||
padding: 0;
|
||||
`)}
|
||||
`;
|
||||
41
src/components/Chip/ChipGroup.jsx
Normal file
41
src/components/Chip/ChipGroup.jsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import React, { useState } from 'react';
|
||||
import { number } from 'prop-types';
|
||||
import styled from 'styled-components';
|
||||
import Chip from './Chip';
|
||||
|
||||
const ChipGroup = ({ children, className, showOverflowAfter, ...props }) => {
|
||||
const [isExpanded, setIsExpanded] = useState(!showOverflowAfter);
|
||||
const toggleIsOpen = () => setIsExpanded(!isExpanded);
|
||||
|
||||
const mappedChildren = React.Children.map(children, c => (
|
||||
React.cloneElement(c, { component: 'li' })
|
||||
));
|
||||
const showOverflowToggle = showOverflowAfter && children.length > showOverflowAfter;
|
||||
const numToShow = isExpanded
|
||||
? children.length
|
||||
: Math.min(showOverflowAfter, children.length);
|
||||
const expandedText = 'Show Less';
|
||||
const collapsedText = `${children.length - showOverflowAfter} more`;
|
||||
|
||||
return (
|
||||
<ul className={`pf-c-chip-group ${className}`} {...props}>
|
||||
{mappedChildren.slice(0, numToShow)}
|
||||
{showOverflowToggle && (
|
||||
<Chip isOverflowChip onClick={toggleIsOpen} component="li">
|
||||
{isExpanded ? expandedText : collapsedText}
|
||||
</Chip>
|
||||
)}
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
ChipGroup.propTypes = {
|
||||
showOverflowAfter: number,
|
||||
};
|
||||
ChipGroup.defaultProps = {
|
||||
showOverflowAfter: null,
|
||||
};
|
||||
|
||||
export default styled(ChipGroup)`
|
||||
--pf-c-chip-group--c-chip--MarginRight: 10px;
|
||||
--pf-c-chip-group--c-chip--MarginBottom: 10px;
|
||||
`;
|
||||
2
src/components/Chip/index.js
Normal file
2
src/components/Chip/index.js
Normal file
@@ -0,0 +1,2 @@
|
||||
export { default as ChipGroup } from './ChipGroup';
|
||||
export { default as Chip } from './Chip';
|
||||
56
src/components/DetailList/Detail.jsx
Normal file
56
src/components/DetailList/Detail.jsx
Normal file
@@ -0,0 +1,56 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { node, bool } from 'prop-types';
|
||||
import { TextListItem, TextListItemVariants } from '@patternfly/react-core';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const DetailName = styled(({ fullWidth, ...props }) => (
|
||||
<TextListItem {...props} />
|
||||
))`
|
||||
font-weight: var(--pf-global--FontWeight--bold);
|
||||
text-align: right;
|
||||
${props => props.fullWidth && `
|
||||
grid-column: 1;
|
||||
`}
|
||||
`;
|
||||
|
||||
const DetailValue = styled(({ fullWidth, ...props }) => (
|
||||
<TextListItem {...props} />
|
||||
))`
|
||||
word-break: break-all;
|
||||
${props => props.fullWidth && `
|
||||
grid-column: 2 / -1;
|
||||
`}
|
||||
`;
|
||||
|
||||
const Detail = ({ label, value, fullWidth }) => {
|
||||
if (!value) return null;
|
||||
return (
|
||||
<Fragment>
|
||||
<DetailName
|
||||
component={TextListItemVariants.dt}
|
||||
fullWidth={fullWidth}
|
||||
>
|
||||
{label}
|
||||
</DetailName>
|
||||
<DetailValue
|
||||
component={TextListItemVariants.dd}
|
||||
fullWidth={fullWidth}
|
||||
>
|
||||
{value}
|
||||
</DetailValue>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
Detail.propTypes = {
|
||||
label: node.isRequired,
|
||||
value: node,
|
||||
fullWidth: bool,
|
||||
};
|
||||
Detail.defaultProps = {
|
||||
value: null,
|
||||
fullWidth: false,
|
||||
};
|
||||
|
||||
export default Detail;
|
||||
export { DetailName };
|
||||
export { DetailValue };
|
||||
28
src/components/DetailList/DetailList.jsx
Normal file
28
src/components/DetailList/DetailList.jsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
import { TextList, TextListVariants } from '@patternfly/react-core';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const DetailList = ({ children, stacked, ...props }) => (
|
||||
<TextList component={TextListVariants.dl} {...props}>
|
||||
{children}
|
||||
</TextList>
|
||||
);
|
||||
|
||||
export default styled(DetailList)`
|
||||
display: grid;
|
||||
grid-gap: 20px;
|
||||
${props => (props.stacked ? (`
|
||||
grid-template-columns: auto 1fr;
|
||||
`) : (`
|
||||
--column-count: 1;
|
||||
grid-template-columns: repeat(var(--column-count), auto minmax(10em, 1fr));
|
||||
|
||||
@media (min-width: 920px) {
|
||||
--column-count: 2;
|
||||
}
|
||||
|
||||
@media (min-width: 1210px) {
|
||||
--column-count: 3;
|
||||
}
|
||||
`))}
|
||||
`;
|
||||
2
src/components/DetailList/index.js
Normal file
2
src/components/DetailList/index.js
Normal file
@@ -0,0 +1,2 @@
|
||||
export { default as DetailList } from './DetailList';
|
||||
export { default as Detail, DetailName, DetailValue } from './Detail';
|
||||
@@ -5,7 +5,6 @@ import { SearchIcon } from '@patternfly/react-icons';
|
||||
import {
|
||||
Button,
|
||||
ButtonVariant,
|
||||
Chip,
|
||||
InputGroup,
|
||||
Modal,
|
||||
} from '@patternfly/react-core';
|
||||
@@ -17,6 +16,7 @@ import PaginatedDataList from '../PaginatedDataList';
|
||||
import DataListToolbar from '../DataListToolbar';
|
||||
import CheckboxListItem from '../ListItem';
|
||||
import SelectedList from '../SelectedList';
|
||||
import { ChipGroup, Chip } from '../Chip';
|
||||
import { getQSConfig, parseNamespacedQueryString } from '../../util/qs';
|
||||
|
||||
class Lookup extends React.Component {
|
||||
@@ -128,13 +128,13 @@ class Lookup extends React.Component {
|
||||
const header = lookupHeader || i18n._(t`items`);
|
||||
|
||||
const chips = value ? (
|
||||
<div className="pf-c-chip-group">
|
||||
<ChipGroup>
|
||||
{value.map(chip => (
|
||||
<Chip key={chip.id} onClick={() => this.toggleSelected(chip)}>
|
||||
{chip.name}
|
||||
</Chip>
|
||||
))}
|
||||
</div>
|
||||
</ChipGroup>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,40 +1,24 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Chip
|
||||
} from '@patternfly/react-core';
|
||||
|
||||
import BasicChip from '../BasicChip/BasicChip';
|
||||
import { Split as PFSplit, SplitItem } from '@patternfly/react-core';
|
||||
import styled from 'styled-components';
|
||||
import { ChipGroup, Chip } from '../Chip';
|
||||
import VerticalSeparator from '../VerticalSeparator';
|
||||
|
||||
const selectedRowStyling = {
|
||||
paddingTop: '15px',
|
||||
paddingBottom: '5px',
|
||||
borderLeft: '0',
|
||||
borderRight: '0'
|
||||
};
|
||||
const Split = styled(PFSplit)`
|
||||
padding-top: 15px;
|
||||
padding-bottom: 5px;
|
||||
border-bottom: #ebebeb var(--pf-global--BorderWidth--sm) solid;
|
||||
align-items: baseline;
|
||||
`;
|
||||
|
||||
const selectedLabelStyling = {
|
||||
alignSelf: 'center',
|
||||
fontSize: '14px',
|
||||
fontWeight: 'bold'
|
||||
};
|
||||
const SplitLabelItem = styled(SplitItem)`
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
word-break: initial;
|
||||
`;
|
||||
|
||||
class SelectedList extends Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
showOverflow: false
|
||||
};
|
||||
|
||||
this.showOverflow = this.showOverflow.bind(this);
|
||||
}
|
||||
|
||||
showOverflow = () => {
|
||||
this.setState({ showOverflow: true });
|
||||
};
|
||||
|
||||
render () {
|
||||
const {
|
||||
label,
|
||||
@@ -44,58 +28,26 @@ class SelectedList extends Component {
|
||||
displayKey,
|
||||
isReadOnly
|
||||
} = this.props;
|
||||
const { showOverflow } = this.state;
|
||||
const visibleItems = selected.slice(0, showOverflow ? selected.length : showOverflowAfter);
|
||||
return (
|
||||
<div className="awx-selectedList">
|
||||
<div className="pf-l-split" style={selectedRowStyling}>
|
||||
<div className="pf-l-split__item" style={selectedLabelStyling}>
|
||||
{label}
|
||||
</div>
|
||||
<VerticalSeparator />
|
||||
<div className="pf-l-split__item">
|
||||
<div className="pf-c-chip-group">
|
||||
{isReadOnly ? (
|
||||
<Fragment>
|
||||
{visibleItems
|
||||
.map(selectedItem => (
|
||||
<BasicChip
|
||||
key={selectedItem.id}
|
||||
>
|
||||
{selectedItem[displayKey]}
|
||||
</BasicChip>
|
||||
))
|
||||
}
|
||||
</Fragment>
|
||||
) : (
|
||||
<Fragment>
|
||||
{visibleItems
|
||||
.map(selectedItem => (
|
||||
<Chip
|
||||
key={selectedItem.id}
|
||||
onClick={() => onRemove(selectedItem)}
|
||||
>
|
||||
{selectedItem[displayKey]}
|
||||
</Chip>
|
||||
))
|
||||
}
|
||||
</Fragment>
|
||||
)}
|
||||
{(
|
||||
!showOverflow
|
||||
&& selected.length > showOverflowAfter
|
||||
) && (
|
||||
<Chip
|
||||
isOverflowChip
|
||||
onClick={() => this.showOverflow()}
|
||||
>
|
||||
{`${(selected.length - showOverflowAfter).toString()} more`}
|
||||
</Chip>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Split>
|
||||
<SplitLabelItem>
|
||||
{label}
|
||||
</SplitLabelItem>
|
||||
<VerticalSeparator />
|
||||
<SplitItem>
|
||||
<ChipGroup showOverflowAfter={showOverflowAfter}>
|
||||
{selected.map(item => (
|
||||
<Chip
|
||||
key={item.id}
|
||||
isReadOnly={isReadOnly}
|
||||
onClick={() => onRemove(item)}
|
||||
>
|
||||
{item[displayKey]}
|
||||
</Chip>
|
||||
))}
|
||||
</ChipGroup>
|
||||
</SplitItem>
|
||||
</Split>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
.awx-selectedList {
|
||||
--awx-selectedList--BackgroundColor: var(--pf-global--BackgroundColor--light-100);
|
||||
--awx-selectedList--BorderColor: #ebebeb;
|
||||
--awx-selectedList--BorderWidth: var(--pf-global--BorderWidth--sm);
|
||||
--awx-selectedList--FontSize: var(--pf-c-chip__text--FontSize);
|
||||
|
||||
|
||||
.pf-l-split {
|
||||
padding-top: 20px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: var(--awx-selectedList--BorderWidth) solid var(--awx-selectedList--BorderColor);
|
||||
}
|
||||
.pf-l-split__item:first-child {
|
||||
display: flex;
|
||||
white-space: nowrap;
|
||||
height: 30px;
|
||||
}
|
||||
.pf-c-chip {
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,19 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Separator = styled.span`
|
||||
display: inline-block;
|
||||
width: 1px;
|
||||
height: 30px;
|
||||
margin-right: 20px;
|
||||
margin-left: 20px;
|
||||
background-color: #d7d7d7;
|
||||
vertical-align: middle;
|
||||
`;
|
||||
|
||||
const VerticalSeparator = () => (
|
||||
<div>
|
||||
<span style={{
|
||||
content: '',
|
||||
backgroundColor: '#d7d7d7',
|
||||
width: '1px',
|
||||
height: '30px',
|
||||
display: 'inline-block',
|
||||
verticalAlign: 'middle',
|
||||
marginLeft: '20px',
|
||||
marginRight: '20px'
|
||||
}}
|
||||
/>
|
||||
<Separator />
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user