diff --git a/packages/core/app-extensions/src/notification/modules/modal/ModalDisplay/StyledComponents.js b/packages/core/app-extensions/src/notification/modules/modal/ModalDisplay/StyledComponents.js index e7c1c50fe4..2b04df5833 100644 --- a/packages/core/app-extensions/src/notification/modules/modal/ModalDisplay/StyledComponents.js +++ b/packages/core/app-extensions/src/notification/modules/modal/ModalDisplay/StyledComponents.js @@ -1,11 +1,11 @@ import styled, {createGlobalStyle} from 'styled-components' -import {Button, scale, StyledScrollbar, StyledTether, theme} from 'tocco-ui' +import {Button, scale, StyledScrollbar, StyledTether, themeSelector} from 'tocco-ui' export const basePadding = scale.space(0.5) export const StyledModalContent = styled.div` position: relative; - background-color: ${theme.color('paper')}; + background-color: ${themeSelector.color('paper')}; padding: ${basePadding}; display: grid; grid-template-rows: [header] auto [body] 1fr; @@ -77,7 +77,6 @@ export const StyledTitleWrapper = styled.div` export const StyledModalBody = styled.div` grid-row-start: body; overflow: hidden auto; - padding-right: ${scale.space(0)}; ${StyledScrollbar} ` diff --git a/packages/core/entity-list/src/components/Table/NavigationCellHeader.js b/packages/core/entity-list/src/components/Table/NavigationCellHeader.js index 5a66a0288f..6295fb5f30 100644 --- a/packages/core/entity-list/src/components/Table/NavigationCellHeader.js +++ b/packages/core/entity-list/src/components/Table/NavigationCellHeader.js @@ -1,25 +1,33 @@ import PropTypes from 'prop-types' import {FormattedMessage} from 'react-intl' -import {connect} from 'react-redux' import {BallMenu, MenuItem} from 'tocco-ui' -import {displayColumnModal, resetSorting, resetPreferences, resetColumns} from '../../modules/preferences/actions' - -const NavigationCellHeader = props => - !props.disablePreferencesMenu ? ( +const NavigationCellHeader = ({ + disablePreferencesMenu, + displayColumnModal, + resetColumns, + sortable, + resetSorting, + resetPreferences, + displayTableRowsModal +}) => + !disablePreferencesMenu ? ( - + - + + + + - {props.sortable && ( - + {sortable && ( + )} - + @@ -31,19 +39,8 @@ NavigationCellHeader.propTypes = { resetPreferences: PropTypes.func.isRequired, resetColumns: PropTypes.func.isRequired, sortable: PropTypes.bool, - disablePreferencesMenu: PropTypes.bool + disablePreferencesMenu: PropTypes.bool, + displayTableRowsModal: PropTypes.func.isRequired } -const mapActionCreators = { - displayColumnModal, - resetSorting, - resetPreferences, - resetColumns -} - -const mapStateToProps = (state, props) => ({ - sortable: state.input.sortable, - disablePreferencesMenu: state.list.disablePreferencesMenu -}) - -export default connect(mapStateToProps, mapActionCreators)(NavigationCellHeader) +export default NavigationCellHeader diff --git a/packages/core/entity-list/src/components/Table/NavigationCellHeaderContainer.js b/packages/core/entity-list/src/components/Table/NavigationCellHeaderContainer.js new file mode 100644 index 0000000000..575cef5884 --- /dev/null +++ b/packages/core/entity-list/src/components/Table/NavigationCellHeaderContainer.js @@ -0,0 +1,27 @@ +import {connect} from 'react-redux' + +import { + displayColumnModal, + resetSorting, + resetPreferences, + resetColumns, + displayTableRowsModal +} from '../../modules/preferences/actions' +import NavigationCellHeader from './NavigationCellHeader' + +const mapActionCreators = { + displayColumnModal, + resetSorting, + resetPreferences, + resetColumns, + displayTableRowsModal +} + +const mapStateToProps = (state, props) => { + return { + sortable: state.list.sortable, + disablePreferencesMenu: state.list.disablePreferencesMenu + } +} + +export default connect(mapStateToProps, mapActionCreators)(NavigationCellHeader) diff --git a/packages/core/entity-list/src/components/Table/SelectRowNums.js b/packages/core/entity-list/src/components/Table/SelectRowNums.js new file mode 100644 index 0000000000..68ea5b8994 --- /dev/null +++ b/packages/core/entity-list/src/components/Table/SelectRowNums.js @@ -0,0 +1,47 @@ +import PropTypes from 'prop-types' +import {useState} from 'react' +import {injectIntl, FormattedMessage} from 'react-intl' +import {EditableValue, Button} from 'tocco-ui' + +import {StyledButtonWrapper, StyledEditableValueWrapper} from './StyledComponents' + +const SelectNumRows = injectIntl(({intl, onOk, numOfRows}) => { + const msg = id => intl.formatMessage({id}) + const options = [ + {key: 25, display: '25'}, + {key: 50, display: '50'}, + {key: 100, display: '100'} + ] + const matchingValue = options.find(option => option.key === numOfRows) + const [value, setValue] = useState(matchingValue) + + return ( + <> + + + + + + + + ) +}) + +SelectNumRows.propTypes = { + intl: PropTypes.object.isRequired, + onOk: PropTypes.func.isRequired, + numOfRows: PropTypes.number +} + +export default SelectNumRows diff --git a/packages/core/entity-list/src/components/Table/StyledComponents.js b/packages/core/entity-list/src/components/Table/StyledComponents.js index bd5e9d8247..4c5e4e7538 100644 --- a/packages/core/entity-list/src/components/Table/StyledComponents.js +++ b/packages/core/entity-list/src/components/Table/StyledComponents.js @@ -1,5 +1,5 @@ import styled from 'styled-components' -import {themeSelector, declareFont} from 'tocco-ui' +import {themeSelector, declareFont, scale, StyledButton, colorizeBorder} from 'tocco-ui' export const StyledMarkingWrapper = styled.span` ${declareFont()} @@ -13,3 +13,22 @@ export const StyledMarkingWrapper = styled.span` } ${({marked, theme}) => marked && `color: ${theme.colors.secondary};`} ` + +export const StyledButtonWrapper = styled.div` + position: sticky; + bottom: 0; + padding-top: ${scale.space(0)}; + background-color: ${themeSelector.color('paper')}; + display: flex; + justify-content: flex-end; + + ${StyledButton} { + margin-right: 0; + } +` + +export const StyledEditableValueWrapper = styled.div` + border: 1px solid ${colorizeBorder.shade1}; + padding-right: ${scale.space(-1)}; + padding-left: ${scale.space(-1)}; +` diff --git a/packages/core/entity-list/src/components/Table/navigationCell.js b/packages/core/entity-list/src/components/Table/navigationCell.js index d16ff02f66..0f383d912a 100644 --- a/packages/core/entity-list/src/components/Table/navigationCell.js +++ b/packages/core/entity-list/src/components/Table/navigationCell.js @@ -1,7 +1,7 @@ import PropTypes from 'prop-types' import {Icon} from 'tocco-ui' -import NavigationCellHeader from './NavigationCellHeader' +import NavigationCellHeader from './NavigationCellHeaderContainer' const CellRenderer = ({showNavigation, rowData, navigationStrategy, parent}) => showNavigation && navigationStrategy.DetailLinkRelative ? ( diff --git a/packages/core/entity-list/src/containers/TableContainer.js b/packages/core/entity-list/src/containers/TableContainer.js index 397f89d17b..0bbf659938 100644 --- a/packages/core/entity-list/src/containers/TableContainer.js +++ b/packages/core/entity-list/src/containers/TableContainer.js @@ -6,6 +6,7 @@ import {changePage, refresh, initialize, onRowClick, setSortingInteractive} from import {changePosition, resetSorting, changeWidth} from '../modules/preferences/actions' import {onSelectChange, setSelection} from '../modules/selection/actions' import {getFormDefinition, getClickable, getDisablePreferencesMenu, getSelectable} from '../util/api/forms' +import {getActualLimit} from '../util/preferences' import {getTableSelectionStyle} from '../util/selection' const mapActionCreators = { @@ -25,7 +26,7 @@ const mapStateToProps = (state, props) => ({ currentPage: state.list.currentPage, entities: state.list.entities, entityCount: state.list.entityCount, - limit: state.input.limit, + limit: getActualLimit(state), inProgress: state.list.inProgress, tableSelectionStyle: getTableSelectionStyle(state.input.selectionStyle, getSelectable(getFormDefinition(state))), clickable: getClickable(getFormDefinition(state)), diff --git a/packages/core/entity-list/src/modules/list/sagas.js b/packages/core/entity-list/src/modules/list/sagas.js index 5124564f55..e91cb174b9 100644 --- a/packages/core/entity-list/src/modules/list/sagas.js +++ b/packages/core/entity-list/src/modules/list/sagas.js @@ -16,6 +16,7 @@ import { getSorting, splitFormId } from '../../util/api/forms' +import {getActualLimit} from '../../util/preferences' import * as preferencesActions from '../preferences/actions' import * as searchFormActions from '../searchForm/actions' import {getSearchFormValues} from '../searchForm/sagas' @@ -287,7 +288,7 @@ export function* setLazyDataMarked(entityName, markings) { export function* fetchEntitiesAndAddToStore(page) { const state = yield select(stateSelector) - const {entityName, scope, limit} = state.input + const {entityName, scope} = state.input const {columns: columnPreferences} = state.preferences const {entityStore, sorting} = state.list if (!entityStore[page]) { @@ -299,7 +300,7 @@ export function* fetchEntitiesAndAddToStore(page) { ...basicQuery, page, sorting, - limit, + limit: getActualLimit(state), paths } @@ -339,7 +340,8 @@ export function* delayedPreloadNextPage(page) { } export function* preloadNextPage(currentPage) { - const {limit} = yield select(inputSelector) + const state = yield select(stateSelector) + const actualLimit = getActualLimit(state) const list = yield select(listSelector) const {entityStore} = list let {entityCount} = list @@ -350,7 +352,7 @@ export function* preloadNextPage(currentPage) { entityCount = setCountAction.payload.entityCount } - if (currentPage * limit < entityCount && !entityStore[nextPage]) { + if (currentPage * actualLimit < entityCount && !entityStore[nextPage]) { yield call(fetchEntitiesAndAddToStore, nextPage) } } diff --git a/packages/core/entity-list/src/modules/preferences/actions.js b/packages/core/entity-list/src/modules/preferences/actions.js index d788a379c7..e784bf4086 100644 --- a/packages/core/entity-list/src/modules/preferences/actions.js +++ b/packages/core/entity-list/src/modules/preferences/actions.js @@ -9,6 +9,8 @@ export const RESET_COLUMNS = 'preferences/RESET_COLUMNS' export const RESET_PREFERENCES = 'preferences/RESET_PREFERENCES' export const DISPLAY_COLUMN_MODAL = 'preferences/DISPLAY_COLUMN_MODAL' export const SET_PREFERENCES_LOADED = 'preferences/SET_PREFERENCES_LOADED' +export const SET_NUMBER_OF_TABLE_ROWS = 'preferences/SET_NUMBER_OF_TABLE_ROWS' +export const DISPLAY_TABLE_ROWS_MODAL = 'preferences/DISPLAY_TABLE_ROWS_MODAL' export const loadPreferences = () => ({ type: LOAD_PREFERENCES @@ -78,3 +80,12 @@ export const resetColumns = () => ({ type: RESET_COLUMNS, payload: {} }) + +export const displayTableRowsModal = () => ({ + type: DISPLAY_TABLE_ROWS_MODAL +}) + +export const setNumberOfTableRows = numOfRows => ({ + type: SET_NUMBER_OF_TABLE_ROWS, + payload: {numOfRows} +}) diff --git a/packages/core/entity-list/src/modules/preferences/reducer.js b/packages/core/entity-list/src/modules/preferences/reducer.js index f06f97de5b..5ed284890c 100644 --- a/packages/core/entity-list/src/modules/preferences/reducer.js +++ b/packages/core/entity-list/src/modules/preferences/reducer.js @@ -25,7 +25,13 @@ const resetPreferences = state => ({ ...state, positions: {}, sorting: [], - columns: {} + columns: {}, + numOfRows: undefined +}) + +const setNumberOfTableRows = (state, {payload: {numOfRows}}) => ({ + ...state, + numOfRows }) const ACTION_HANDLERS = { @@ -36,7 +42,8 @@ const ACTION_HANDLERS = { [actions.SET_PREFERENCES_LOADED]: reducerUtil.singleTransferReducer('preferencesLoaded'), [actions.RESET_SORTING]: resetSorting, [actions.RESET_COLUMNS]: resetColumns, - [actions.RESET_PREFERENCES]: resetPreferences + [actions.RESET_PREFERENCES]: resetPreferences, + [actions.SET_NUMBER_OF_TABLE_ROWS]: setNumberOfTableRows } const initialState = { @@ -44,6 +51,7 @@ const initialState = { sorting: [], columns: {}, widths: {}, + numOfRows: undefined, preferencesLoaded: false } diff --git a/packages/core/entity-list/src/modules/preferences/sagas.js b/packages/core/entity-list/src/modules/preferences/sagas.js index 92806afdcf..d610348935 100644 --- a/packages/core/entity-list/src/modules/preferences/sagas.js +++ b/packages/core/entity-list/src/modules/preferences/sagas.js @@ -3,11 +3,12 @@ import {all, call, put, select, take, takeLatest} from 'redux-saga/effects' import {rest, notification} from 'tocco-app-extensions' import ColumnModal from '../../components/ColumnModal' +import SelectNumRows from '../../components/Table/SelectRowNums' import {getTableColumns} from '../../util/api/forms' import * as util from '../../util/preferences' import * as listActions from '../list/actions' import * as listSagas from '../list/sagas' -import {setPositions, setSorting, setColumns, setPreferencesLoaded} from './actions' +import {setPositions, setSorting, setColumns, setPreferencesLoaded, setNumberOfTableRows} from './actions' import * as actions from './actions' export const inputSelector = state => state.input @@ -22,7 +23,8 @@ export default function* sagas() { takeLatest(actions.RESET_SORTING, resetSorting), takeLatest(actions.RESET_COLUMNS, resetColumns), takeLatest(actions.RESET_PREFERENCES, resetPreferences), - takeLatest(actions.DISPLAY_COLUMN_MODAL, displayColumnModal) + takeLatest(actions.DISPLAY_COLUMN_MODAL, displayColumnModal), + takeLatest(actions.DISPLAY_TABLE_ROWS_MODAL, displayTableRowsModal) ]) } @@ -33,6 +35,7 @@ export function* loadPreferences() { yield put(setPositions(util.getPositions(preferences))) yield put(setSorting(util.getSorting(preferences))) yield put(setColumns(util.getColumns(preferences))) + yield put(setNumberOfTableRows(Number(preferences[`${formName}.numOfRows`]))) yield put(setPreferencesLoaded(true)) } @@ -76,6 +79,16 @@ export function* saveSorting() { } } +export function* saveNumberOfTableRows(answerChannel) { + const {numOfRows} = yield take(answerChannel) + const inputState = yield select(inputSelector) + const formName = `${inputState.formName}_${inputState.scope}` + + yield put(setNumberOfTableRows(numOfRows)) + yield call(listSagas.reloadData) + yield call(rest.savePreferences, {[`${formName}.numOfRows`]: numOfRows}) +} + export function* resetSorting() { const inputState = yield select(inputSelector) yield all([ @@ -120,6 +133,31 @@ export function* displayColumnModal() { yield saveColumnPreferences(answerChannel, preferencesColumns, formDefinition) } +export function* displayTableRowsModal() { + const {formDefinition} = yield select(listSagas.listSelector) + const answerChannel = yield call(channel) + const {numOfRows: preferencesNumOfRows} = yield select(preferencesSelector) + + yield put( + notification.modal( + `${formDefinition.id}-numOfRows-setting`, + 'client.entity-list.preferences.numOfRows', + null, + ({close}) => { + const onOk = numOfRows => { + close() + answerChannel.put({numOfRows}) + } + + return + }, + true + ) + ) + + yield call(saveNumberOfTableRows, answerChannel) +} + function* saveColumnPreferences(answerChannel, preferencesColumns, formDefinition) { const columns = (yield take(answerChannel)).reduce( (accumulator, item) => ({ diff --git a/packages/core/entity-list/src/util/preferences.js b/packages/core/entity-list/src/util/preferences.js index 6e46ee08af..50826d2995 100644 --- a/packages/core/entity-list/src/util/preferences.js +++ b/packages/core/entity-list/src/util/preferences.js @@ -96,3 +96,5 @@ export const getColumnPreferencesToSave = (formName, columns) => }), {} ) + +export const getActualLimit = state => state.preferences.numOfRows || state.input.limit