diff --git a/components/GridToolBox/style.css b/components/GridToolBox/style.css index 0d4b1edd..6f1097c7 100644 --- a/components/GridToolBox/style.css +++ b/components/GridToolBox/style.css @@ -3,7 +3,6 @@ .toolbarWrap { width: 100%; - overflow: hidden; } .fixedHeight { @@ -24,12 +23,28 @@ justify-content: space-between; } -.toolbarWrap>div, +.tableButtonsShowed .pullRight { + margin-left: auto; + flex-shrink: 0; +} + +.tableButtonsShowed .spacer { + display: flex; + align-items: center; +} + +/* Keep .tableCell for non-toolbarWrap usages elsewhere */ .tableCell { display: table-cell; vertical-align: middle; } +/* Override table-cell for direct children of the flex toolbar row */ +.toolbarWrap.table > div { + display: flex; + align-items: center; +} + .toolbarWrap .toolbarElement { padding-right: 20px; } @@ -41,6 +56,8 @@ .toolbarWrap .label { text-align: left; white-space: nowrap; + flex-shrink: 1; + min-width: 0; } .toolbarWrap .link { @@ -55,8 +72,8 @@ } .toolbarWrap .pullRight { - float: right; - text-align: right; + display: flex; + align-items: center; } .toolbarWrap .noRightMargin { @@ -153,129 +170,17 @@ } .filterActionWrap { - flex-grow: 1; - margin-left: 20px; -} - -.filterActionWrap button { - float: right; + display: flex; + align-items: center; + margin-left: auto; + padding-left: 20px; } .iframeWrap { - width: 13rem;; + width: 13rem; } .wrapFilters { outline: none; } -@media only screen and (min-device-width: 280px) and (max-device-width:  1365px) { - .toolbarWrap .minWidthed { - min-width: auto; - } - - .table { - padding-top: 10px; - } - } - - -@media only screen and (min-device-width: 930px) and (max-device-width:  1365px) { - .toolbarWrap .minWidthed { - min-width: auto; - } - - .table { - height: auto; - padding-top: 10px; - flex-direction: column; - padding-bottom: 10px; - } - - .toolbarWrap .toolbarElement { - padding-bottom: 10px; - } - - .toolbarElementsContainer { - display: flex; - flex-direction: row; - } - } - - @media only screen and (min-device-width: 670px) and (max-device-width: 929px) { - .table { - height: auto; - padding-bottom: 10px; - flex-direction: column; - } - - .toolbarWrap .toolbarElement { - padding-right: 0px; - padding-bottom: 10px; - } - - .toolbarElementsContainer { - flex-direction: column; - height: auto; - } - - } - - - @media only screen and (min-device-width: 280px) and (max-device-width: 669px) { - .toolbarWrap .toolbarElement { - padding-right: 0px; - padding-bottom: 10px; - } - - .toolbarElementsContainer { - flex-direction: column; - height: auto; - } -} - -@media only screen and (min-device-width: 280px) and (max-device-width: 420px) { - .table { - width: 55%; - height: auto; - flex-direction: column; - align-items: flex-start; - padding-bottom: 5px; - } -} - -@media only screen and (min-device-width: 421px) and (max-device-width: 429px) { - .table { - width: 48%; - height: auto; - flex-direction: column; - padding-bottom: 5px; - } - - .toolbarWrap .pullRight { -text-align: -webkit-center; - } -} - - -@media only screen and (min-device-width: 430px) and (max-device-width: 589px) { - .table { - width: 70%; - height: auto; - flex-direction: column; - padding-bottom: 5px; - } - - .toolbarWrap .pullRight { - text-align: -webkit-center; - } -} - -@media only screen and (min-device-width: 590px) and (max-device-width: 669px) { - .table { - width: 80%; - height: auto; - flex-direction: column; - padding-bottom: 5px; - } -} diff --git a/components/Input/Dropdown.js b/components/Input/Dropdown.js index 8d9fc65d..b95fbc84 100644 --- a/components/Input/Dropdown.js +++ b/components/Input/Dropdown.js @@ -17,12 +17,14 @@ export class Dropdown extends Component { this.state = { open: false, value: props.defaultSelected || this.props.placeholderValue, - valid: {isValid: this.props.isValid, errorMessage: this.props.errorMessage} + valid: {isValid: this.props.isValid, errorMessage: this.props.errorMessage}, + searchQuery: '' }; this.handleChange = this.handleChange.bind(this); this.handleOpen = this.handleOpen.bind(this); this.handleClose = this.handleClose.bind(this); + this.handleSearchChange = this.handleSearchChange.bind(this); } static propTypes = { @@ -97,7 +99,11 @@ export class Dropdown extends Component { } handleOpen(event) { - this.setState({open: true, anchorEl: event.currentTarget}); + this.setState({open: true, anchorEl: event.currentTarget, searchQuery: ''}); + } + + handleSearchChange(event) { + this.setState({searchQuery: event.target.value}); } handleClose(event) { @@ -127,9 +133,17 @@ export class Dropdown extends Component { getMenuItems(width) { const { data, placeholder, canSelectPlaceholder, cssStyle, mergeStyles } = this.props; + const { searchQuery } = this.state; const ddstyles = mergeStyles ? Object.assign({}, style, mergeStyles) : cssStyle || style; const menuItems = []; + const filteredData = searchQuery + ? data.filter(item => { + const name = this.getTitle(item.name); + return name && String(name).toLowerCase().includes(searchQuery.toLowerCase()); + }) + : data; + menuItems.push( ); - data.forEach((item, i) => { + filteredData.forEach((item, i) => { menuItems.push( + {this.props.data.length > 10 && ( +
+ e.stopPropagation()} + /> +
+ )} item.name && String(item.name).toLowerCase().includes(searchQuery.toLowerCase())) + : data; + let menuItems = [ ]; - data.forEach((item) => { + filteredData.forEach((item) => { const isChecked = (data.length === defaultSelected.length || defaultSelected.findIndex(d => d.key === item.key) > -1); menuItems.push( + {this.props.data.length > 10 && ( +
+ e.stopPropagation()} + /> +
+ )} {this.state.open && this.getMenuItems()} diff --git a/components/Input/style.css b/components/Input/style.css index 221b787c..5a1e368a 100644 --- a/components/Input/style.css +++ b/components/Input/style.css @@ -495,3 +495,22 @@ div [data-reactroot] div [style*="color: rgb(255, 64, 129)"] { overflow-x: hidden !important; text-overflow: ellipsis; } + +.dropdownSearchWrap { + padding: 5px 8px; + position: sticky; + top: 0; + background: #fff; + z-index: 1; + border-bottom: 1px solid #D6D6D6; +} + +.dropdownSearch { + width: 100%; + height: 30px; + border: 1px solid #D6D6D6; + padding: 10px 8px; + box-sizing: border-box; + font-size: 14px; + outline: none; +} diff --git a/components/Layout/verticalStyles.css b/components/Layout/verticalStyles.css index a41303b9..8b2d4397 100644 --- a/components/Layout/verticalStyles.css +++ b/components/Layout/verticalStyles.css @@ -4,13 +4,15 @@ } .vertical { - overflow: hidden; + overflow-x: visible; width: 100%; + min-width: 0; } .innerContent { height: 100%; - overflow: hidden; + overflow-x: visible; display: flex; flex-direction: column; + min-width: 0; } diff --git a/components/Tab/MultiTab.js b/components/Tab/MultiTab.js index 06d5e18a..91006930 100644 --- a/components/Tab/MultiTab.js +++ b/components/Tab/MultiTab.js @@ -90,16 +90,25 @@ export default class MultiTab extends Component { ref={(element) => { this.rootElement = element; }} data-type={multiTab} > - - {tab.title} - {this.props.rightArrowIcon && } - + {tab.routeName + ? + {tab.title} + {this.props.rightArrowIcon && } + + : + {tab.title} + {this.props.rightArrowIcon && } + + } { - return permission.indexOf('!') === 0 - ? !this.context.checkPermission(permission.substr(1)) - : this.context.checkPermission(permission); - }); + constructor(props, context) { + super(props, context); + + this.state = { visibleCount: Infinity }; + this.containerRef = React.createRef(); + this.tabRefs = []; + this.tabWidths = []; // cached per-tab widths, populated on first full render + this.recalculate = this.recalculate.bind(this); } - isPermissionCheckRequired(tab) { - return tab.permission !== undefined; + componentDidMount() { + this.resizeObserver = new ResizeObserver(this.recalculate); + this.resizeObserver.observe(this.containerRef.current); + this.recalculate(); } - isMulti(tab) { - return !!tab.multi; + componentWillUnmount() { + this.resizeObserver && this.resizeObserver.disconnect(); } - getTabComponents(tabset) { - tabset = permissionPreCheck(fromJS(tabset)).toJS(); + recalculate() { + const container = this.containerRef.current; + if (!container) return; + const available = container.offsetWidth; + if (available === 0) return; // layout not ready yet + + // Update cached widths for currently rendered (visible) tabs + this.tabRefs.forEach((el, i) => { + if (el && el.offsetWidth > 0) { + this.tabWidths[i] = el.offsetWidth; + } + }); - return tabset.reduce((memo, tab, i) => { - const isMulti = this.isMulti(tab); + if (!this.tabWidths.length) return; - memo.push( -
- {isMulti ? : } -
- ); + const MORE_BUTTON_WIDTH = 70; + let used = 0; + let count = 0; + for (let i = 0; i < this.tabWidths.length; i++) { + const w = this.tabWidths[i] || 0; + if (!w) break; // width unknown for this tab — stop here + const isLast = i === this.tabWidths.length - 1; + const wouldOverflow = isLast + ? used + w > available + : available - used - w < MORE_BUTTON_WIDTH; + if (wouldOverflow) break; + used += w; + count++; + } + if (count !== this.state.visibleCount) { + this.setState({ visibleCount: count }); + } + } - return memo; - }, []); + hasPermission(permissions) { + return permissions.every((permission) => { + return permission.indexOf('!') === 0 + ? !this.context.checkPermission(permission.substr(1)) + : this.context.checkPermission(permission); + }); } render() { const { tabset, className } = this.props; - const tabs = this.getTabComponents(tabset); + const { visibleCount } = this.state; + const tabs = permissionPreCheck(fromJS(tabset)).toJS(); + + const visibleTabs = tabs.slice(0, visibleCount); + const overflowTabs = tabs.slice(visibleCount); + const moreTab = { title: ..., multi: overflowTabs }; + + this.tabRefs = []; return ( - - {tabs} + + {visibleTabs.map((tab, i) => ( +
{ this.tabRefs[i] = el; }} + > + {tab.multi ? : } +
+ ))} + {overflowTabs.length > 0 && ( +
+ +
+ )}
); } diff --git a/containers/HeaderNew/index.js b/containers/HeaderNew/index.js index 117d4c58..d3f5b6ad 100644 --- a/containers/HeaderNew/index.js +++ b/containers/HeaderNew/index.js @@ -44,7 +44,7 @@ class HeaderNew extends Component { tabset={tabset} className={classNames(styles.tabsContainer, classTabsContainer)} /> - + +
-
+
{this.props.children}
diff --git a/pages/Layout/style.css b/pages/Layout/style.css index 7d7e6bc5..a3dcb7df 100644 --- a/pages/Layout/style.css +++ b/pages/Layout/style.css @@ -9,3 +9,15 @@ line-height: 46px; text-decoration: none; } + +.appContent { + display: block; + width: 100%; + overflow: auto; +} + +.mainLayout { + min-width: 1024px; + width: 100%; + box-sizing: border-box; +} \ No newline at end of file