diff --git a/assets/src/components/form/base/form-placeholder.css b/assets/src/components/form/base/form-placeholder.css index 3b078afda8b..dcf8b3b814d 100644 --- a/assets/src/components/form/base/form-placeholder.css +++ b/assets/src/components/form/base/form-placeholder.css @@ -1,7 +1,7 @@ .ee-form-placeholder-div { border: 1px dashed var(--ee-color-grey-11); - margin: var(--ee-margin-default) var(--ee-margin-tiny); + margin: var(--ee-margin-default) 0; } .ee-editor-div > .ee-form-placeholder-div { diff --git a/assets/src/components/form/layouts/two-column-admin/two-column-admin.css b/assets/src/components/form/layouts/two-column-admin/two-column-admin.css index 6a6589a65de..7e5cfbc812f 100644 --- a/assets/src/components/form/layouts/two-column-admin/two-column-admin.css +++ b/assets/src/components/form/layouts/two-column-admin/two-column-admin.css @@ -527,7 +527,7 @@ margin: var(--ee-margin-small) 0 0; } - .ee-two-column-admin .px-3 .ee-form-row:not(li) { + .ee-two-column-admin .ee-form-row:not(li) { margin: 0 0 var(--ee-margin-small); } } @@ -617,7 +617,7 @@ /* Anything bigger than Smartphone */ @media screen and (min-width: 481px) { - .ee-two-column-admin .px-3 .ee-form-row:not(li) { + .ee-two-column-admin .ee-form-row:not(li) { margin: 0 0 var(--ee-margin-small); } @@ -825,7 +825,7 @@ } .ee-two-column-admin .ee-form-row, - .ee-two-column-admin .px-3 .ee-form-row:not(li) { + .ee-two-column-admin .ee-form-row:not(li) { flex-flow: column nowrap; justify-content: center; min-height: unset; diff --git a/assets/src/components/ui/drop-down-menu/style.css b/assets/src/components/ui/drop-down-menu/style.css index 2dcd8fa35fc..2ac85209460 100644 --- a/assets/src/components/ui/drop-down-menu/style.css +++ b/assets/src/components/ui/drop-down-menu/style.css @@ -8,7 +8,10 @@ background: none !important; border: 1px solid var(--ee-background-color) !important; color: var(--ee-default-text-color) !important; + height: calc(var(--ee-icon-button-size) + 2px); + margin: 0; padding: var(--ee-padding-tiny); + width: calc(var(--ee-icon-button-size) + 2px); } .ee-drop-down-menu.components-dropdown-menu .components-dropdown-menu__toggle:hover, diff --git a/assets/src/components/ui/entity-list/index.js b/assets/src/components/ui/entity-list/index.js index a4fae8bd118..62fd74628a3 100644 --- a/assets/src/components/ui/entity-list/index.js +++ b/assets/src/components/ui/entity-list/index.js @@ -1 +1,2 @@ export { default as EntityList } from './entity-list'; +export { default as useReorderEntities } from './use-reorder-entities'; diff --git a/assets/src/components/ui/entity-list/use-reorder-entities.js b/assets/src/components/ui/entity-list/use-reorder-entities.js new file mode 100644 index 00000000000..e7948f79df4 --- /dev/null +++ b/assets/src/components/ui/entity-list/use-reorder-entities.js @@ -0,0 +1,113 @@ +/** + * External imports + */ +import { sortBy } from 'lodash'; +import { useCallback } from '@wordpress/element'; +import { isModelEntityOfModel } from '@eventespresso/validators'; + +/** + * returns a custom hook + * for changing the order of the provided array of entities, + * given indexes for an entity's existing location in + * that array and the desired new location. + * Because the provided array of entities may NOT be + * the full set of entities, the full list must also + * be supplied and it will be reorder as well, + * with the entities from the subset array moved + * to the beginning of the full set. + * + * @function + * @param {string} modelName + * @param {Function} setSortBy + * @param {Function} setEntityIds + * @return {Function} custom hook + */ +const useReorderEntities = ( { modelName, setSortBy, setEntityIds } ) => { + /** + * @function + * @param {Array} entities a subset of filtered entities + * @param {Array} allEntities a list of ALL entities of the same type + * that may not have been present in the subset list that was sorted. It + * will be reorder as well. + * @param {number|string} oldIndex existing location of entity to be + * moved + * @param {number|string} newIndex target location for entity in subset + * array + */ + return useCallback( ( + entities, + allEntities, + oldIndex, + newIndex, + ) => { + oldIndex = parseInt( oldIndex, 10 ); + newIndex = parseInt( newIndex, 10 ); + if ( newIndex === oldIndex ) { + return; + } + if ( newIndex < 0 || oldIndex < 0 ) { + throw new Error( + 'Can not reorder the entity list because' + + ' indexes can not be negative!' + + "\n oldIndex: " + JSON.stringify( oldIndex ) + + "\n newIndex: " + JSON.stringify( newIndex ) + ); + } + if ( ! Array.isArray( entities ) || + ! Array.isArray( allEntities ) ) { + throw new Error( + 'Can not reorder the entity list because one or more of the' + + ' supplied entity lists were invalid!' + + "\n entities: " + JSON.stringify( entities ) + + "\n allEntities: " + JSON.stringify( allEntities ) + ); + } + // remove entity from existing location in filtered list + const [ removed ] = entities.splice( oldIndex, 1 ); + // insert removed entity into new location in same list + entities.splice( newIndex, 0, removed ); + // now loop thru entities in filtered list + entities.forEach( ( entity, index ) => { + if ( isModelEntityOfModel( entity, modelName ) ) { + // reset the order property for all entities in filtered list + entity.order = index + 1; + // grab index of reordered entities in list of all entities + const indexInAll = allEntities.indexOf( entity ); + // remove reordered entities from list of all entities + allEntities.splice( indexInAll, 1 ); + } else { + throw new Error( + 'Can not reorder the entity list because' + + ' an invalid entity was supplied!' + + "\n entity: " + JSON.stringify( entity ) + ); + } + } ); + // reorder the list of all entities as well... + // reverse the reordered list of entities + const reversed = entities.reverse(); + reversed.forEach( ( entity ) => { + // add each entity to the beginning of the allEntities array + allEntities.unshift( entity ); + // so we previously removed these entities, but now we are added + // them back onto the array at the beginning and in reverse + // order. So we add #3 to the top, then #2, then #1, + // so that the final order of the array will be #1, #2, #3, + // followed by all of the other entities previously in the array + } ); + // but now we need to reset the order properties for ALL entities + allEntities.forEach( ( entity, index ) => { + // add 1 so we don't end up with order: 0 + entity.order = index + 1; + } ); + allEntities = sortBy( allEntities, [ 'order' ] ); + setEntityIds( allEntities.map( ( entity ) => entity.id ) ); + setSortBy( 'by-order' ); + }, [ + modelName, + setSortBy, + setEntityIds, + ] ); +}; + +export default useReorderEntities; diff --git a/assets/src/components/ui/responsive-table/index.js b/assets/src/components/ui/responsive-table/index.js index 5c150ecc0ca..d169480323e 100644 --- a/assets/src/components/ui/responsive-table/index.js +++ b/assets/src/components/ui/responsive-table/index.js @@ -3,7 +3,7 @@ import TableHeader from './table-header'; import TableBody from './table-body'; import TableFooter from './table-footer'; import TableRow from './table-row'; -import TableHeadingCell from './table-heading-cell'; +import TableHeaderCell from './table-header-cell'; import TableDataCell from './table-data-cell'; export const EspressoTable = { @@ -12,7 +12,7 @@ export const EspressoTable = { TableBody, TableRow, TableFooter, - TableHeadingCell, + TableHeaderCell, TableDataCell, }; diff --git a/assets/src/components/ui/responsive-table/responsive-table.js b/assets/src/components/ui/responsive-table/responsive-table.js index 08ec720d803..2e517254db0 100644 --- a/assets/src/components/ui/responsive-table/responsive-table.js +++ b/assets/src/components/ui/responsive-table/responsive-table.js @@ -1,12 +1,10 @@ /** * External imports */ -import { isArray, isEmpty, isFunction } from 'lodash'; -import warning from 'warning'; import PropTypes from 'prop-types'; -import { withInstanceId } from '@wordpress/compose'; -import { Component } from '@wordpress/element'; import classNames from 'classnames'; +import { find, isEmpty } from 'lodash'; +import { withInstanceId } from '@wordpress/compose'; /** * Internal dependencies @@ -15,10 +13,6 @@ import Table from './table'; import TableHeader from './table-header'; import TableBody from './table-body'; import TableFooter from './table-footer'; -import TableRow from './table-row'; -import TableHeadingCell from './table-heading-cell'; -import TableDataCell from './table-data-cell'; -import ResponsiveCell from './responsive-cell'; import './style.css'; /** @@ -26,309 +20,136 @@ import './style.css'; * produces a table like structure for displaying tabular data * in a grid that collapses properly on smaller screens * - * @param {Array} columnHeaders - * @param {Array} rowHeaders - * @param {Array} tableData + * @param {string} instanceId + * @param {Array} headerRows + * @param {Array} tableRows + * @param {Array} footerRows * @param {Object} metaData + * @param {Object} classes + * @param {Function} onBeforeDragStart + * @param {Function} onDragStart + * @param {Function} onDragUpdate + * @param {Function} onDragEnd */ -class ResponsiveTable extends Component { - static propTypes = { - instanceId: PropTypes.number, - columns: PropTypes.arrayOf( - PropTypes.shape( { - type: PropTypes.string.isRequired, - value: PropTypes.oneOfType( [ - PropTypes.object, - PropTypes.number, - PropTypes.string, - ] ).isRequired, - id: PropTypes.string, - class: PropTypes.string, - extraProps: PropTypes.object, - } ) - ).isRequired, - rowData: PropTypes.arrayOf( - PropTypes.arrayOf( - PropTypes.shape( { - type: PropTypes.string.isRequired, - value: PropTypes.oneOfType( [ - PropTypes.object, - PropTypes.number, - PropTypes.string, - ] ).isRequired, - id: PropTypes.string, - class: PropTypes.string, - render: PropTypes.func, - extraProps: PropTypes.object, - } ) - ) - ).isRequired, - metaData: PropTypes.shape( { - tableCaption: PropTypes.string.isRequired, - showTableFooter: PropTypes.bool, - hasRowHeaders: PropTypes.bool, - } ).isRequired, - classes: PropTypes.shape( { - tableClass: PropTypes.string, - headerClass: PropTypes.string, - headerRowClass: PropTypes.string, - headerThClass: PropTypes.string, - bodyClass: PropTypes.string, - bodyRowClass: PropTypes.string, - bodyThClass: PropTypes.string, - bodyTdClass: PropTypes.string, - footerClass: PropTypes.string, - footerRowClass: PropTypes.string, - footerThClass: PropTypes.string, - } ), - }; - - /** - * @function - * @param {number} instanceId - * @param {Object} metaData - */ - setMetaData = ( instanceId, metaData = {} ) => { - this.tableId = `ee-rspnsv-table-${ instanceId }`; - this.tableCaption = metaData.tableCaption || ''; - this.captionID = `${ this.tableId }-caption`; - this.showTableFooter = typeof metaData.showTableFooter === 'undefined' ? - true : metaData.showTableFooter; - this.hasRowHeaders = !! metaData.hasRowHeaders; - }; - - /** - * @function - * @param {Object} classes - */ - setCssClasses = ( classes ) => { - this.classes = { - tableClass: classes.tableClass || '', - headerClass: classes.headerClass || '', - headerRowClass: classes.headerRowClass || '', - headerThClass: classes.headerThClass || '', - bodyClass: classes.bodyClass || '', - bodyRowClass: classes.bodyRowClass || '', - bodyThClass: classes.bodyThClass || '', - bodyTdClass: classes.bodyTdClass || '', - footerClass: classes.footerClass || '', - footerRowClass: classes.footerRowClass || '', - footerThClass: classes.footerThClass || '', - }; - this.classes.tableClass = classNames( - this.classes.tableClass, - { - 'ee-rspnsv-table-has-row-headers': this.hasRowHeaders - } - ); - }; - - /** - * @function - * @param {Array} columns - * @param {boolean} isFooter - * @return {Object} rendered headings row - */ - tableHeader = ( columns, isFooter = false ) => { - this.rowNumber++; - const rowType = isFooter === true ? 'footer' : 'header'; - let rowProps = {}; - let indexMod = 0; - const headerCells = columns.map( - ( column, colNumber ) => { - column.rowType = column.rowType || rowType; - if ( column.type && column.type === 'row' ) { - rowProps = column; - indexMod++; - return null; - } - this.columns.push( column ); - colNumber -= indexMod; - const hasRenderCallback = isFunction( column.render ); - warning( - hasRenderCallback || column.hasOwnProperty( 'value' ), - `Missing "value" property for ${ rowType } column ${ colNumber }.` - ); - const renderCallback = hasRenderCallback ? - column.render : - this.headingCell; - return renderCallback( this.rowNumber, colNumber, column ); - } - ); - return this.tableRow( headerCells, rowProps ); - }; +const ResponsiveTable = ( { + instanceId, + headerRows = [], + tableRows = [], + footerRows = [], + metaData = {}, + classes = {}, + onBeforeDragStart, + onDragStart, + onDragUpdate, + onDragEnd, +} ) => { + const primaryHeader = find( headerRows, 'primary' ); + if ( ! primaryHeader || isEmpty( tableRows ) ) { + return null; + } + const tableId = metaData.tableId || `ee-rspnsv-table-${ instanceId }`; + const tableCaption = metaData.tableCaption || ''; + const captionID = `${ tableId }-caption`; + const hasRowHeaders = !! metaData.hasRowHeaders; + const headerRowCount = headerRows.length; + const tableRowCount = tableRows.length; - /** - * @function - * @param {Array} cells - * @param {Object} rowProps - * @return {Object} rendered element - */ - tableRow = ( cells, rowProps = {} ) => { - return ( - - { cells } - - ); + const cssClasses = { + tableClass: classNames( + classes.tableClass, + { 'ee-rspnsv-table-has-row-headers': hasRowHeaders } + ), + headerClass: classes.headerClass || '', + headerRowClass: classes.headerRowClass || '', + headerThClass: classes.headerThClass || '', + bodyClass: classes.bodyClass || '', + bodyRowClass: classes.bodyRowClass || '', + bodyThClass: classes.bodyThClass || '', + bodyTdClass: classes.bodyTdClass || '', + footerClass: classes.footerClass || '', + footerRowClass: classes.footerRowClass || '', + footerThClass: classes.footerThClass || '', }; - /** - * @function - * @param {number} rowNumber - * @param {number} colNumber - * @param {Object} cellProps - * @return {Object} rendered column header cell - */ - headingCell = ( rowNumber, colNumber, cellProps ) => { - return ( - - { cellProps.value || '' } - - ); - }; + cssClasses.tableClass = classNames( + cssClasses.tableClass, + `ee-rspnsv-table-column-count-${ primaryHeader.cells.length }` + ); - /** - * @function - * @param {Array} dataRow - * @return {Object} rendered data row - */ - dataRow = ( dataRow ) => { - this.rowNumber++; - warning( - isArray( dataRow ), - `Data for row ${ this.rowNumber } is not an array.` - ); - let rowProps = {}; - let indexMod = 0; - const rowCells = dataRow.map( - ( cellData, colNumber ) => { - cellData.rowType = cellData.rowType || 'body'; - if ( cellData.type && cellData.type === 'row' ) { - rowProps = cellData; - indexMod++; - return null; - } - // adjust column number used in IDs - // before grabbing element from column data - colNumber -= indexMod; - const column = this.columns[ colNumber ]; - if ( ! column ) { - warning( - false, - `Missing data for column ${ colNumber } ` + - `in row ${ this.rowNumber }.` - ); - return null; - } - const hasRenderCallback = isFunction( cellData.render ); - warning( - hasRenderCallback || cellData.hasOwnProperty( 'value' ), - `Missing "value" property for table cell at ` + - `row ${ this.rowNumber } column ${ colNumber }.` - ); - const renderCallback = hasRenderCallback ? - cellData.render : - this.dataCell; - return renderCallback( - this.rowNumber, - colNumber, - column, - cellData - ); - } - ); - return this.tableRow( rowCells, rowProps ); - }; + return ( + + + + +
+ ); +}; - /** - * @function - * @param {number} rowNumber - * @param {number} colNumber - * @param {Object} column - * @param {Object} cellData - * @return {Object} rendered headings row - */ - dataCell = ( rowNumber, colNumber, column, cellData ) => { - return this.hasRowHeaders && colNumber === 0 ? ( - this.headingCell( rowNumber, colNumber, cellData ) - ) : ( - - - - ); - }; - - render() { - const { - instanceId, - columns = [], - rowData = [], - footerData = [], - metaData = {}, - classes = {}, - } = this.props; - if ( isEmpty( columns ) ) { - return null; - } - this.setMetaData( instanceId, metaData ); - this.setCssClasses( classes || {} ); - this.columns = []; - this.rowNumber = -1; - const tableHeader = this.tableHeader( columns ); - this.classes.tableClass = classNames( - this.classes.tableClass, - `ee-rspnsv-table-column-count-${ this.columns.length }` - ); - this.showTableFooter = this.showTableFooter && ! isEmpty( footerData ); - return ( - - - { tableHeader } - - - { rowData.map( - ( dataRow ) => this.dataRow( dataRow ) - ) } - - - { this.tableHeader( footerData, true ) } - -
- ); - } -} +const cellShape = PropTypes.shape( { + type: PropTypes.string.isRequired, + key: PropTypes.string.isRequired, + value: PropTypes.node.isRequired, + id: PropTypes.string, + class: PropTypes.string, + render: PropTypes.func, + extraProps: PropTypes.object, +} ); +const rowShape = PropTypes.shape( { + type: PropTypes.string.isRequired, + key: PropTypes.string.isRequired, + id: PropTypes.string, + class: PropTypes.string, + extraProps: PropTypes.object, + cells: PropTypes.arrayOf( cellShape ).isRequired, +} ); +ResponsiveTable.propTypes = { + instanceId: PropTypes.number.isRequired, + headerRows: PropTypes.arrayOf( rowShape ).isRequired, + tableRows: PropTypes.arrayOf( rowShape ).isRequired, + footerRows: PropTypes.arrayOf( rowShape ), + metaData: PropTypes.shape( { + tableCaption: PropTypes.string.isRequired, + tableId: PropTypes.string, + hasRowHeaders: PropTypes.bool, + } ).isRequired, + classes: PropTypes.shape( { + tableClass: PropTypes.string, + headerClass: PropTypes.string, + headerRowClass: PropTypes.string, + headerThClass: PropTypes.string, + bodyClass: PropTypes.string, + bodyRowClass: PropTypes.string, + bodyThClass: PropTypes.string, + bodyTdClass: PropTypes.string, + footerClass: PropTypes.string, + footerRowClass: PropTypes.string, + footerThClass: PropTypes.string, + } ), +}; export default withInstanceId( ResponsiveTable ); diff --git a/assets/src/components/ui/responsive-table/style.css b/assets/src/components/ui/responsive-table/style.css index 93eb4da26d7..5ea729dbef1 100644 --- a/assets/src/components/ui/responsive-table/style.css +++ b/assets/src/components/ui/responsive-table/style.css @@ -98,7 +98,9 @@ .ee-rspnsv-table .ee-rspnsv-table-header-th.ee-entity-list-status-stripe, .ee-rspnsv-table .ee-rspnsv-table-body-td.ee-entity-list-status-stripe { - width: var(--ee-padding-micro); + max-width: 10px; + min-width: 5px; + width: .25%; } .ee-rspnsv-table .ee-status-stripe-div { @@ -112,6 +114,12 @@ } +.ee-rspnsv-table .ee-drop-down-menu, +.ee-rspnsv-table .ee-icon-menu-item { + margin: var(--ee-margin-nano); +} + + /* COLUMN SIZES */ @@ -1188,9 +1196,18 @@ visibility: hidden; width: 0; } + + .ee-rspnsv-table .ee-rspnsv-table-header-th.ee-entity-list-status-stripe, + .ee-rspnsv-table .ee-rspnsv-table-body-td.ee-entity-list-status-stripe { + width: .275%; + } } @media only screen and (max-width: 1366px) { + .ee-rspnsv-table .ee-rspnsv-table-header-th.ee-entity-list-status-stripe, + .ee-rspnsv-table .ee-rspnsv-table-body-td.ee-entity-list-status-stripe { + width: .37%; + } } @media only screen and (max-width: 1280px) { @@ -1337,9 +1354,22 @@ padding: var(--ee-padding-micro) var(--ee-padding-smaller); width: 100%; } + + .ee-rspnsv-table .ee-rspnsv-table-header-th.ee-entity-list-status-stripe, + .ee-rspnsv-table .ee-rspnsv-table-body-td.ee-entity-list-status-stripe { + width: .4%; + } + + .columns-2 .ee-rspnsv-table .ee-rspnsv-table-body-td.ee-entity-list-status-stripe { + width: 100%; + } } @media only screen and (max-width: 1024px) { + .ee-rspnsv-table .ee-rspnsv-table-header-th.ee-entity-list-status-stripe, + .ee-rspnsv-table .ee-rspnsv-table-body-td.ee-entity-list-status-stripe { + width: .5%; + } } /* WordPress Admin Media Query Breakpoint: Auto-folding of the admin menu */ @@ -1495,6 +1525,9 @@ width: 100%; } + .ee-rspnsv-table .ee-rspnsv-table-body-td.ee-entity-list-status-stripe { + width: 100%; + } } /* WordPress Admin Media Query Breakpoint: Smartphone */ diff --git a/assets/src/components/ui/responsive-table/table-body.js b/assets/src/components/ui/responsive-table/table-body.js index c0a5389d818..eca197ce6c2 100644 --- a/assets/src/components/ui/responsive-table/table-body.js +++ b/assets/src/components/ui/responsive-table/table-body.js @@ -2,33 +2,207 @@ * External imports */ import classNames from 'classnames'; +import { isFunction } from 'lodash'; import PropTypes from 'prop-types'; +import warning from 'warning'; +import { DragDropContext, Droppable } from 'react-beautiful-dnd'; /** - * @param {Array} children - * @param {string} htmlClass + * Internal dependencies + */ +import TableRow from './table-row'; +import TableHeaderCell from './table-header-cell'; +import TableDataCell from './table-data-cell'; +import ResponsiveCell from './responsive-cell'; + +/** + * @param {string} tableId + * @param {Array} tableRows + * @param {Object} cssClasses + * @param {boolean} hasRowHeaders + * @param {Object} primaryHeader + * @param {number} headerRowCount + * @param {Function} onBeforeDragStart + * @param {Function} onDragStart + * @param {Function} onDragUpdate + * @param {Function} onDragEnd * @param {Object} extraProps * @return {Object} rendered thead */ -const TableBody = ( { children, htmlClass, ...extraProps } ) => { - htmlClass = classNames( - htmlClass, +const TableBody = ( { + tableId, + tableRows, + cssClasses, + hasRowHeaders, + primaryHeader, + headerRowCount, + onBeforeDragStart, + onDragStart, + onDragUpdate, + onDragEnd, + ...extraProps +} ) => { + console.log( '%c TableBody', 'color: #1BE7FF;' ); + console.log( '%c > typeof onDragEnd: ', 'color: #BCBDAC;', typeof onDragEnd ); + console.log( '%c > onDragEnd: ', 'color: #BCBDAC;', onDragEnd ); + /** + * @function + * @param {number} rowNumber + * @param {number} colNumber + * @param {Object} column + * @param {Object} cellData + * @return {Object} rendered headings row + */ + const tableCell = ( rowNumber, colNumber, column, cellData ) => { + return hasRowHeaders && colNumber === 0 ? ( + + { cellData.value || '' } + + ) : ( + + + + ); + }; + + const tableBodyRows = tableRows.map( ( row, rowNumber ) => { + return ( + + { row.cells.map( ( cellData, colNumber ) => { + const column = primaryHeader.cells[ colNumber ]; + warning( + column !== undefined, + `Missing data for column ${ colNumber } ` + + `in row ${ rowNumber }.` + ); + warning( + cellData.hasOwnProperty( 'value' ), + `Missing "value" property for table cell at ` + + `row ${ rowNumber } column ${ colNumber }.` + ); + if ( isFunction( cellData.render ) ) { + return cellData.render( + rowNumber, + colNumber, + column, + cellData + ); + } + return tableCell( rowNumber, colNumber, column, cellData ); + } ) } + + ) + } ); + + const htmlClass = classNames( + cssClasses.bodyClass, 'ee-rspnsv-table-body' ); - return ( - - { children } + + return onDragEnd !== null ? ( + + + { ( + { innerRef, droppableProps, placeholder }, + { isDraggingOver } + ) => ( + + { tableBodyRows } + { placeholder } + + ) } + + + ) : ( + + { tableBodyRows } ); }; TableBody.propTypes = { - children: PropTypes.node, - htmlClass: PropTypes.string, + tableId: PropTypes.string.isRequired, + headerRowCount: PropTypes.number.isRequired, + tableRows: PropTypes.arrayOf( + PropTypes.shape( { + type: PropTypes.string.isRequired, + key: PropTypes.string.isRequired, + id: PropTypes.string, + class: PropTypes.string, + extraProps: PropTypes.object, + cells: PropTypes.arrayOf( + PropTypes.shape( { + type: PropTypes.string.isRequired, + key: PropTypes.string.isRequired, + value: PropTypes.node.isRequired, + id: PropTypes.string, + class: PropTypes.string, + render: PropTypes.func, + extraProps: PropTypes.object, + } ) + ).isRequired, + } ) + ).isRequired, + cssClasses: PropTypes.object, + hasRowHeaders: PropTypes.bool, + primaryHeader: PropTypes.object, + onBeforeDragStart: PropTypes.func, + onDragStart: PropTypes.func, + onDragUpdate: PropTypes.func, + onDragEnd: PropTypes.func, }; TableBody.defaultProps = { - htmlClass: '', + cssClasses: {}, + onBeforeDragStart: null, + onDragStart: null, + onDragUpdate: null, + onDragEnd: null, }; export default TableBody; diff --git a/assets/src/components/ui/responsive-table/table-data-cell.js b/assets/src/components/ui/responsive-table/table-data-cell.js index 7634de8d9cf..4ceddc75bd9 100644 --- a/assets/src/components/ui/responsive-table/table-data-cell.js +++ b/assets/src/components/ui/responsive-table/table-data-cell.js @@ -8,10 +8,9 @@ import PropTypes from 'prop-types'; * @param {mixed} children * @param {number} rowNumber * @param {number} colNumber - * @param {string} rowType * @param {string} htmlId * @param {string} htmlClass - * @param {Object} classes + * @param {Object} cssClasses * @param {Object} extraProps * @return {Object} rendered heading cell */ @@ -21,7 +20,7 @@ const TableDataCell = ( { colNumber, htmlId, htmlClass, - classes, + cssClasses, ...extraProps } ) => { htmlId = htmlId ? @@ -31,7 +30,7 @@ const TableDataCell = ( { htmlClass, 'ee-rspnsv-table-body-td', `ee-col-${ colNumber }`, - classes.bodyTdClass + cssClasses.bodyTdClass ); return ( @@ -46,13 +45,13 @@ TableDataCell.propTypes = { colNumber: PropTypes.number.isRequired, htmlId: PropTypes.string, htmlClass: PropTypes.string, - classes: PropTypes.object, + cssClasses: PropTypes.object, }; TableDataCell.defaultProps = { htmlId: '', htmlClass: '', - classes: {}, + cssClasses: {}, }; export default TableDataCell; diff --git a/assets/src/components/ui/responsive-table/table-footer.js b/assets/src/components/ui/responsive-table/table-footer.js index ac94cea9cf1..e75349d5029 100644 --- a/assets/src/components/ui/responsive-table/table-footer.js +++ b/assets/src/components/ui/responsive-table/table-footer.js @@ -2,35 +2,106 @@ * External imports */ import classNames from 'classnames'; +import { isFunction } from 'lodash'; import PropTypes from 'prop-types'; +import warning from 'warning'; /** - * @param {boolean} showFooter - * @param {Array} children - * @param {string} htmlClass + * Internal dependencies + */ +import TableRow from './table-row'; +import TableHeaderCell from './table-header-cell'; + +const EMPTY_ARRAY = []; + +/** + * @param {string} tableId + * @param {Array} footerRows + * @param {Object} cssClasses + * @param {number} rowCount * @param {Object} extraProps * @return {Object} rendered thead */ -const TableFooter = ( { showFooter, children, htmlClass, ...extraProps } ) => { - htmlClass = classNames( - htmlClass, +const TableFooter = ( { + tableId, + cssClasses, + footerRows, + rowCount, + ...extraProps +} ) => { + const htmlClass = classNames( + cssClasses.footerClass, 'ee-rspnsv-table-footer' ); - return showFooter ? ( + return footerRows !== EMPTY_ARRAY ? ( - { children } + { footerRows.map( ( footerRow, row ) => { + row += rowCount; + return ( + + { footerRow.cells.map( ( column, col ) => { + warning( + column.hasOwnProperty( 'value' ), + `Missing "value" property for footer column ${ col }.` + ); + return isFunction( column.render ) ? + column.render( row, col, column ) : + + { column.value || '' } + + } ) } + + ); + } ) } ) : null; }; TableFooter.propTypes = { - showFooter: PropTypes.bool.isRequired, - children: PropTypes.node, - htmlClass: PropTypes.string, + tableId: PropTypes.string.isRequired, + rowCount: PropTypes.number.isRequired, + cssClasses: PropTypes.object, + footerRows: PropTypes.arrayOf( + PropTypes.shape( { + type: PropTypes.string.isRequired, + key: PropTypes.string.isRequired, + id: PropTypes.string, + class: PropTypes.string, + extraProps: PropTypes.object, + cells: PropTypes.arrayOf( + PropTypes.shape( { + type: PropTypes.string.isRequired, + key: PropTypes.string.isRequired, + value: PropTypes.node.isRequired, + id: PropTypes.string, + class: PropTypes.string, + render: PropTypes.func, + extraProps: PropTypes.object, + } ) + ).isRequired, + } ) + ), }; TableFooter.defaultProps = { - htmlClass: '', + footerRows: EMPTY_ARRAY, + cssClasses: {}, }; export default TableFooter; diff --git a/assets/src/components/ui/responsive-table/table-heading-cell.js b/assets/src/components/ui/responsive-table/table-header-cell.js similarity index 80% rename from assets/src/components/ui/responsive-table/table-heading-cell.js rename to assets/src/components/ui/responsive-table/table-header-cell.js index 6e6573d48e1..a09574e7edc 100644 --- a/assets/src/components/ui/responsive-table/table-heading-cell.js +++ b/assets/src/components/ui/responsive-table/table-header-cell.js @@ -10,18 +10,18 @@ import PropTypes from 'prop-types'; * @param {string} rowType * @param {string} htmlId * @param {string} htmlClass - * @param {Object} classes + * @param {Object} cssClasses * @param {Object} extraProps * @return {Object} rendered heading cell */ -const TableHeadingCell = ( { +const TableHeaderCell = ( { children, rowNumber, colNumber, rowType, htmlId, htmlClass, - classes, + cssClasses, ...extraProps } ) => { htmlId = htmlId ? @@ -31,8 +31,8 @@ const TableHeadingCell = ( { `${ htmlClass } ee-rspnsv-table-${ rowType }-th ee-col-${ colNumber }` : `ee-rspnsv-table-${ rowType }-th ee-col-${ colNumber }`; const rowTypeClass = rowType + 'ThClass'; - htmlClass = classes[ rowTypeClass ] ? - `${ htmlClass } ${ classes[ rowTypeClass ] }` : + htmlClass = cssClasses[ rowTypeClass ] ? + `${ htmlClass } ${ cssClasses[ rowTypeClass ] }` : htmlClass; if ( rowType === 'header' ) { extraProps.role = "columnheader"; @@ -47,21 +47,21 @@ const TableHeadingCell = ( { ); }; -TableHeadingCell.propTypes = { +TableHeaderCell.propTypes = { children: PropTypes.node, rowNumber: PropTypes.number.isRequired, colNumber: PropTypes.number.isRequired, rowType: PropTypes.string, htmlId: PropTypes.string, htmlClass: PropTypes.string, - classes: PropTypes.object, + cssClasses: PropTypes.object, }; -TableHeadingCell.defaultProps = { +TableHeaderCell.defaultProps = { rowType: 'body', htmlId: '', htmlClass: '', - classes: {}, + cssClasses: {}, }; -export default TableHeadingCell; +export default TableHeaderCell; diff --git a/assets/src/components/ui/responsive-table/table-header.js b/assets/src/components/ui/responsive-table/table-header.js index 63cde1ff648..ff9bfe6ce1c 100644 --- a/assets/src/components/ui/responsive-table/table-header.js +++ b/assets/src/components/ui/responsive-table/table-header.js @@ -2,34 +2,97 @@ * External imports */ import classNames from 'classnames'; +import { isFunction } from 'lodash'; import PropTypes from 'prop-types'; +import warning from 'warning'; /** - * @param {Array} children - * @param {string} htmlClass + * Internal dependencies + */ +import TableRow from './table-row'; +import TableHeaderCell from './table-header-cell'; + +/** + * @param {string} tableId + * @param {Array} headerRows + * @param {Object} cssClasses * @param {Object} extraProps * @return {Object} rendered thead */ -const TableHeader = ( { children, htmlClass, ...extraProps } ) => { - htmlClass = classNames( - htmlClass, +const TableHeader = ( { + tableId, + headerRows, + cssClasses, + ...extraProps +} ) => { + const htmlClass = classNames( + cssClasses.headerClass, 'ee-rspnsv-table-header' ); return ( - { children } + { headerRows.map( ( headerRow, row ) => ( + + { headerRow.cells.map( ( column, col ) => { + warning( + column.hasOwnProperty( 'value' ), + `Missing "value" property for header column ${ col }.` + ); + return isFunction( column.render ) ? + column.render( row, col, column ) : + + { column.value || '' } + + } ) } + + ) ) } ); }; TableHeader.propTypes = { - children: PropTypes.node, - htmlClass: PropTypes.string, + tableId: PropTypes.string.isRequired, + cssClasses: PropTypes.object, + headerRows: PropTypes.arrayOf( + PropTypes.shape( { + type: PropTypes.string.isRequired, + key: PropTypes.string.isRequired, + id: PropTypes.string, + class: PropTypes.string, + extraProps: PropTypes.object, + cells: PropTypes.arrayOf( + PropTypes.shape( { + type: PropTypes.string.isRequired, + key: PropTypes.string.isRequired, + value: PropTypes.node.isRequired, + id: PropTypes.string, + class: PropTypes.string, + render: PropTypes.func, + extraProps: PropTypes.object, + } ) + ).isRequired, + } ) + ).isRequired, }; TableHeader.defaultProps = { - htmlClass: '', + cssClasses: {}, }; - export default TableHeader; diff --git a/assets/src/components/ui/responsive-table/table-row.js b/assets/src/components/ui/responsive-table/table-row.js index 0d222c5184c..71d7fa7ba4a 100644 --- a/assets/src/components/ui/responsive-table/table-row.js +++ b/assets/src/components/ui/responsive-table/table-row.js @@ -3,57 +3,107 @@ */ import classNames from 'classnames'; import PropTypes from 'prop-types'; +import { Draggable } from 'react-beautiful-dnd'; /** + * @param {Object} rowData * @param {Array} children * @param {number} rowNumber + * @param {number} headerRowCount * @param {string} htmlId * @param {string} htmlClass - * @param {Object} classes - * @param {Object} classes + * @param {Object} cssClasses * @param {string} rowType + * @param {boolean} sortable * @param {Object} extraProps * @return {Object} rendered tr */ const TableRow = ( { + rowData, children, rowNumber, + headerRowCount, htmlId, htmlClass, - classes, + cssClasses, rowType, + sortable, ...extraProps } ) => { - htmlId = htmlId ? + if ( ! rowData ) { + return null; + } + const id = htmlId ? `${ htmlId }-row-${ rowNumber }` : `ee-rspnsv-table-row-${ rowNumber }`; - htmlClass = classNames( + const css = classNames( htmlClass, `ee-rspnsv-table-${ rowType }-row`, `ee-row-${ rowNumber }`, - classes[ `${ rowType }RowClass` ], + cssClasses[ `${ rowType }RowClass` ], ); - return ( - + console.log( '%c > sortable: ', 'color: #BCBDAC;', sortable ); + return sortable ? ( + + { ( + { innerRef, draggableProps, dragHandleProps }, + { isDragging } + ) => { + // incrementing row number here + // because the Draggable needs + // indexes to start at 0 + rowNumber += headerRowCount; + return ( + + { children } + + ) + } } + + ) : ( + { children } ); }; TableRow.propTypes = { - children: PropTypes.node, + rowData: PropTypes.object.isRequired, + children: PropTypes.node.isRequired, rowNumber: PropTypes.number.isRequired, rowType: PropTypes.string, htmlId: PropTypes.string, htmlClass: PropTypes.string, - classes: PropTypes.object, + cssClasses: PropTypes.object, + sortable: PropTypes.bool, }; TableRow.defaultProps = { rowType: 'body', htmlId: '', htmlClass: '', - classes: {}, + cssClasses: {}, + sortable: false, }; export default TableRow; diff --git a/assets/src/components/ui/responsive-table/utils.js b/assets/src/components/ui/responsive-table/utils.js index 5b685bff38e..ff29f3f50af 100644 --- a/assets/src/components/ui/responsive-table/utils.js +++ b/assets/src/components/ui/responsive-table/utils.js @@ -51,7 +51,7 @@ export const getTableRows = ( children ) => { export const getTableRowCells = ( children ) => { return filterChildren( children, ( child ) => { - return isTableHeadingCell( child ) || isTableDataCell( child ); + return isTableHeaderCell( child ) || isTableDataCell( child ); } ); }; @@ -86,15 +86,15 @@ export const isArrayOfTableCells = ( elements ) => { elements = castArray( elements ); let allAreCells = true; elements.forEach( ( cell ) => { - allAreCells = isTableHeadingCell( cell ) || isTableDataCell( cell ) ? + allAreCells = isTableHeaderCell( cell ) || isTableDataCell( cell ) ? allAreCells : false; } ); return allAreCells; }; -export const isTableHeadingCell = ( element ) => { - return isElement( element, 'TableHeadingCell' ); +export const isTableHeaderCell = ( element ) => { + return isElement( element, 'TableHeaderCell' ); }; export const isTableDataCell = ( element ) => { @@ -106,19 +106,16 @@ export const isTableDataCell = ( element ) => { * except those whose table row cell "key" is in the exclude array * * @function - * @param {[][]} formRows + * @param {Object[]} formRows * @param {Array} exclude * @return {Array} columns */ -export const addZebraStripesOnMobile = ( - formRows, - exclude -) => { - return Array.isArray( formRows ) ? formRows.map( - ( formRow ) => { +export const addZebraStripesOnMobile = ( formRows, exclude ) => { + return Array.isArray( formRows ) ? + formRows.map( ( formRow ) => { let x = 0; - return Array.isArray( formRow ) ? formRow.map( - ( cell ) => { + formRow.cells = formRow.cells && Array.isArray( formRow.cells ) ? + formRow.cells.map( ( cell ) => { if ( ! cell.key || exclude.indexOf( cell.key ) > -1 ) { return cell; } @@ -129,10 +126,9 @@ export const addZebraStripesOnMobile = ( 'ee-zebra-stripe-on-mobile'; } return cell; - } - ) : []; - } - ) : []; + } ) : []; + return formRow; + } ) : []; }; /** diff --git a/assets/src/editor/events/dates-and-times/editor-date/actions-menu/editor-date-entity-actions-menu.js b/assets/src/editor/events/dates-and-times/editor-date/actions-menu/editor-date-entity-actions-menu.js index 607b51195d7..140f972d17c 100644 --- a/assets/src/editor/events/dates-and-times/editor-date/actions-menu/editor-date-entity-actions-menu.js +++ b/assets/src/editor/events/dates-and-times/editor-date/actions-menu/editor-date-entity-actions-menu.js @@ -1,7 +1,7 @@ /** * External imports */ -import { useEffect } from '@wordpress/element'; +import { useEffect, useState } from '@wordpress/element'; import { useEntityActionMenuItems } from '@eventespresso/components'; import { ifValidDateEntity } from '@eventespresso/editor-hocs'; import PropTypes from 'prop-types'; @@ -18,6 +18,7 @@ import useEventDateEditorId from '../edit-form/use-event-date-editor-id'; const EditorDateEntityActionsMenu = ( { dateEntity } ) => { const editorId = useEventDateEditorId( dateEntity ); + const [ menuItems, setMenuItems ] = useState( [] ); const { getActionsMenuForEntity, registerEntityActionsMenuItem, @@ -54,7 +55,12 @@ const EditorDateEntityActionsMenu = ( { dateEntity } ) => { /> ) ); - } ); + setMenuItems( getActionsMenuForEntity( dateEntity ) ); + }, [ + dateEntity, + getActionsMenuForEntity, + registerEntityActionsMenuItem, + ] ); return ( <> @@ -62,7 +68,7 @@ const EditorDateEntityActionsMenu = ( { dateEntity } ) => { id={ `ee-editor-date-actions-menu-${ dateEntity.id }` } className={ 'ee-editor-date-actions-menu' } > - { getActionsMenuForEntity( dateEntity ) } + { menuItems } { showDates, datesSortedBy, displayDates, + filteredDateIds, } = useDatesListFilterState( { listId } ); const { view, @@ -71,7 +72,10 @@ const EditorDateEntitiesList = ( { ...otherProps } ) => { paginatedEntities, } = useEntityPagination( perPage, filteredDates ); // update the date ids in state whenever the filters change - const { setFilteredDates } = useDatesListFilterStateSetters( listId ); + const { + setFilteredDates, + setDatesSortedBy, + } = useDatesListFilterStateSetters( listId ); useEffect( () => { if ( Array.isArray( paginatedEntities ) ) { const eventDateIds = paginatedEntities.map( @@ -80,6 +84,7 @@ const EditorDateEntitiesList = ( { ...otherProps } ) => { setFilteredDates( eventDateIds ); } }, [ currentPage, perPage, showDates, datesSortedBy, eventDates.length ] ); + const entityOrder = filteredDateIds.join( '-' ); return ( { { ...entityListFilters } /> { /> { - return [ - { - key: 'row', - type: 'row', - class: 'ee-editor-date-list-items-header-row', - value: '', - }, - { - key: 'stripe', - type: 'cell', - class: 'ee-date-list-col-hdr ee-entity-list-status-stripe ee-rspnsv-table-column-micro', - value: '', - }, - { - key: 'id', - type: 'cell', - class: 'ee-date-list-col-hdr ee-date-list-col-id ee-number-column ee-rspnsv-table-column-tiny', - value: __( 'ID', 'event_espresso' ), - }, - { - key: 'name', - type: 'cell', - class: 'ee-date-list-col-hdr ee-date-list-col-name ee-rspnsv-table-column-huge', - value: __( 'Name', 'event_espresso' ), - }, - { - key: 'start', - type: 'cell', - class: 'ee-date-list-col-hdr ee-date-list-col-name-start ee-rspnsv-table-column-default', - value: ( - <> + return { + key: 'dates-list-header', + type: 'row', + primary: true, + class: 'ee-editor-date-list-items-header-row', + cells: [ + { + key: 'stripe', + type: 'cell', + class: 'ee-date-list-col-hdr ee-entity-list-status-stripe ee-rspnsv-table-column-micro', + value: '', + }, + { + key: 'id', + type: 'cell', + class: 'ee-date-list-col-hdr ee-date-list-col-id ee-number-column ee-rspnsv-table-column-tiny', + value: __( 'ID', 'event_espresso' ), + }, + { + key: 'name', + type: 'cell', + class: 'ee-date-list-col-hdr ee-date-list-col-name ee-rspnsv-table-column-huge', + value: __( 'Name', 'event_espresso' ), + }, + { + key: 'start', + type: 'cell', + class: 'ee-date-list-col-hdr ee-date-list-col-name-start ee-rspnsv-table-column-default', + value: ( + <> + + { __( 'Start Date', 'event_espresso' ) } + + + { __( 'Start', 'event_espresso' ) } + + + ), + }, + { + key: 'end', + type: 'cell', + class: 'ee-date-list-col-hdr ee-date-list-col-end ee-rspnsv-table-column-default', + value: ( + <> + + { __( 'End Date', 'event_espresso' ) } + + + { __( 'End', 'event_espresso' ) } + + + ), + }, + { + key: 'capacity', + type: 'cell', + class: 'ee-date-list-col-hdr ee-date-list-col-capacity ee-rspnsv-table-column-tiny ee-number-column', + value: ( + <> + + { __( 'Capacity', 'event_espresso' ) } + + + { __( 'Cap', 'event_espresso' ) } + + + ), + }, + { + key: 'sold', + type: 'cell', + class: 'ee-date-list-col-hdr ee-date-list-col-sold ee-rspnsv-table-column-tiny ee-number-column', + value: __( 'Sold', 'event_espresso' ), + }, + { + key: 'reserved', + type: 'cell', + class: 'ee-date-list-col-hdr ee-date-list-col-reserved ee-rspnsv-table-column-tiny ee-number-column', + value: ( + <> + + { __( 'Reserved', 'event_espresso' ) } + + + { __( 'Rsrvd', 'event_espresso' ) } + + + ), + }, + { + key: 'registrants', + type: 'cell', + class: 'ee-date-list-col-hdr ee-date-list-col-registrants ee-rspnsv-table-column-smaller ee-centered-column', + value: ( + <> + + { __( 'Registrants', 'event_espresso' ) } + + + { __( 'Regs', 'event_espresso' ) } + + + ), + }, + { + key: 'actions', + type: 'cell', + class: 'ee-date-list-col-hdr ee-date-list-col-actions ee-rspnsv-table-column-big ee-centered-column', + value: ( - { __( 'Start Date', 'event_espresso' ) } + { __( 'Actions', 'event_espresso' ) } - - { __( 'Start', 'event_espresso' ) } - - - ), - }, - { - key: 'end', - type: 'cell', - class: 'ee-date-list-col-hdr ee-date-list-col-end ee-rspnsv-table-column-default', - value: ( - <> - - { __( 'End Date', 'event_espresso' ) } - - - { __( 'End', 'event_espresso' ) } - - - ), - }, - { - key: 'capacity', - type: 'cell', - class: 'ee-date-list-col-hdr ee-date-list-col-capacity ee-rspnsv-table-column-tiny ee-number-column', - value: ( - <> - - { __( 'Capacity', 'event_espresso' ) } - - - { __( 'Cap', 'event_espresso' ) } - - - ), - }, - { - key: 'sold', - type: 'cell', - class: 'ee-date-list-col-hdr ee-date-list-col-sold ee-rspnsv-table-column-tiny ee-number-column', - value: __( 'Sold', 'event_espresso' ), - }, - { - key: 'reserved', - type: 'cell', - class: 'ee-date-list-col-hdr ee-date-list-col-reserved ee-rspnsv-table-column-tiny ee-number-column', - value: ( - <> - - { __( 'Reserved', 'event_espresso' ) } - - - { __( 'Rsrvd', 'event_espresso' ) } - - - ), - }, - { - key: 'registrants', - type: 'cell', - class: 'ee-date-list-col-hdr ee-date-list-col-registrants ee-rspnsv-table-column-smaller ee-centered-column', - value: ( - <> - - { __( 'Registrants', 'event_espresso' ) } - - - { __( 'Regs', 'event_espresso' ) } - - - ), - }, - { - key: 'actions', - type: 'cell', - class: 'ee-date-list-col-hdr ee-date-list-col-actions ee-rspnsv-table-column-big ee-centered-column', - value: ( - - { __( 'Actions', 'event_espresso' ) } - - ), - }, - ]; + ), + }, + ], + }; }; export default datesListTableHeader; diff --git a/assets/src/editor/events/dates-and-times/editor-date/list-view/dates-list-table-row.js b/assets/src/editor/events/dates-and-times/editor-date/list-view/dates-list-table-row.js index 59709563ae4..7db32d39aa5 100644 --- a/assets/src/editor/events/dates-and-times/editor-date/list-view/dates-list-table-row.js +++ b/assets/src/editor/events/dates-and-times/editor-date/list-view/dates-list-table-row.js @@ -2,6 +2,8 @@ * External imports */ import { shortenCuid } from '@eventespresso/utils'; +import { InfinitySymbol } from '@eventespresso/value-objects'; +import { dateTimeModel } from '@eventespresso/model'; /** * Internal dependencies @@ -11,6 +13,7 @@ import EditorDateEntityActionsMenu import EventDateRegistrationsLink from '../event-date-registrations-link'; const DATE_TIME_FORMAT = 'ddd MMM YY h:mm a'; +const { getBackgroundColorClass, status } = dateTimeModel; /** * EditorDateEntityListItem @@ -18,102 +21,91 @@ const DATE_TIME_FORMAT = 'ddd MMM YY h:mm a'; * * @function * @param {Object} dateEntity Event Date entity - * @param {string} capacity - * @param {string} statusClass - * @param {string} bgClass - * @param {Function} doRefresh * @param {Object} otherProps - * @return {Array} row data for the provided date entity + * @return {Object} row data for the provided date entity */ -const datesListTableRow = ( - dateEntity, - capacity, - statusClass, - bgClass, - doRefresh, - otherProps -) => { - return [ - { - key: 'row', - type: 'row', - id: `ee-editor-date-list-view-row-${ dateEntity.id }`, - class: `ee-editor-date-list-view-row ${ statusClass }`, - value: '', - }, - { - key: 'stripe', - type: 'cell', - class: `ee-date-list-cell ee-entity-list-status-stripe ${ bgClass } ee-rspnsv-table-column-micro`, - value: ( -
- { dateEntity.name } -
- ), - }, - { - key: 'id', - type: 'cell', - class: 'ee-date-list-cell ee-date-list-col-id ee-rspnsv-table-column-tiny ee-number-column', - value: shortenCuid( dateEntity.id ), - }, - { - key: 'name', - type: 'cell', - class: 'ee-date-list-cell ee-date-list-col-name ee-rspnsv-table-column-bigger ee-rspnsv-table-hide-on-mobile', - value: dateEntity.name, - }, - { - key: 'start', - type: 'cell', - class: 'ee-date-list-cell ee-date-list-col-start ee-rspnsv-table-column-default', - value: dateEntity.start.toFormat( DATE_TIME_FORMAT ), - }, - { - key: 'end', - type: 'cell', - class: 'ee-date-list-cell ee-date-list-col-end ee-rspnsv-table-column-default', - value: dateEntity.end.toFormat( DATE_TIME_FORMAT ), - }, - { - key: 'capacity', - type: 'cell', - class: 'ee-date-list-cell ee-date-list-col-capacity ee-rspnsv-table-column-tiny ee-number-column', - value: capacity, - }, - { - key: 'sold', - type: 'cell', - class: 'ee-date-list-cell ee-date-list-col-sold ee-rspnsv-table-column-tiny ee-number-column', - value: dateEntity.sold, - }, - { - key: 'reserved', - type: 'cell', - class: 'ee-date-list-cell ee-date-list-col-reserved ee-rspnsv-table-column-tiny ee-number-column', - value: dateEntity.reserved, - }, - { - key: 'registrants', - type: 'cell', - class: 'ee-date-list-cell ee-date-list-col-registrants ee-rspnsv-table-column-smaller ee-centered-column', - value: ( - - ), - }, - { - key: 'actions', - type: 'cell', - class: 'ee-date-list-cell ee-date-list-col-actions ee-rspnsv-table-column-big', - value: ( - - ), - }, - ]; +const datesListTableRow = ( dateEntity, otherProps ) => { + const statusClass = status( dateEntity ); + const bgClass = getBackgroundColorClass( dateEntity ); + return { + key: `row-${ dateEntity.id }`, + type: 'row', + id: `ee-editor-date-list-view-row-${ dateEntity.id }`, + class: `ee-editor-date-list-view-row ${ statusClass }`, + cells: [ + { + key: 'stripe', + type: 'cell', + class: `ee-date-list-cell ee-entity-list-status-stripe ${ bgClass } ee-rspnsv-table-column-micro`, + value: ( +
+ { dateEntity.name } +
+ ), + }, + { + key: 'id', + type: 'cell', + class: 'ee-date-list-cell ee-date-list-col-id ee-rspnsv-table-column-tiny ee-number-column', + value: shortenCuid( dateEntity.id ), + }, + { + key: 'name', + type: 'cell', + class: 'ee-date-list-cell ee-date-list-col-name ee-rspnsv-table-column-bigger ee-rspnsv-table-hide-on-mobile', + value: dateEntity.name, + }, + { + key: 'start', + type: 'cell', + class: 'ee-date-list-cell ee-date-list-col-start ee-rspnsv-table-column-default', + value: dateEntity.start.toFormat( DATE_TIME_FORMAT ), + }, + { + key: 'end', + type: 'cell', + class: 'ee-date-list-cell ee-date-list-col-end ee-rspnsv-table-column-default', + value: dateEntity.end.toFormat( DATE_TIME_FORMAT ), + }, + { + key: 'capacity', + type: 'cell', + class: 'ee-date-list-cell ee-date-list-col-capacity ee-rspnsv-table-column-tiny ee-number-column', + value: , + }, + { + key: 'sold', + type: 'cell', + class: 'ee-date-list-cell ee-date-list-col-sold ee-rspnsv-table-column-tiny ee-number-column', + value: dateEntity.sold, + }, + { + key: 'reserved', + type: 'cell', + class: 'ee-date-list-cell ee-date-list-col-reserved ee-rspnsv-table-column-tiny ee-number-column', + value: dateEntity.reserved, + }, + { + key: 'registrants', + type: 'cell', + class: 'ee-date-list-cell ee-date-list-col-registrants ee-rspnsv-table-column-smaller ee-centered-column', + value: ( + + ), + }, + { + key: 'actions', + type: 'cell', + class: 'ee-date-list-cell ee-date-list-col-actions ee-rspnsv-table-column-big', + value: ( + + ), + }, + ], + }; }; export default datesListTableRow; diff --git a/assets/src/editor/events/dates-and-times/editor-date/list-view/editor-date-entities-list-view.js b/assets/src/editor/events/dates-and-times/editor-date/list-view/editor-date-entities-list-view.js index 3da0f52baac..a4449c4dae0 100644 --- a/assets/src/editor/events/dates-and-times/editor-date/list-view/editor-date-entities-list-view.js +++ b/assets/src/editor/events/dates-and-times/editor-date/list-view/editor-date-entities-list-view.js @@ -9,9 +9,7 @@ import { filterColumnsByKey, ResponsiveTable, } from '@eventespresso/components'; -import { dateTimeModel } from '@eventespresso/model'; import { isModelEntityOfModel } from '@eventespresso/validators'; -import { InfinitySymbol } from '@eventespresso/value-objects'; import PropTypes from 'prop-types'; /** @@ -19,9 +17,9 @@ import PropTypes from 'prop-types'; */ import datesListTableHeader from './dates-list-table-header'; import datesListTableRow from './dates-list-table-row'; +import useReorderDates from './use-reorder-dates'; import './editor-date-entities-list-view.css'; -const { getBackgroundColorClass, status } = dateTimeModel; const noZebraStripe = [ 'row', 'stripe', 'name', 'actions' ]; /** @@ -29,30 +27,28 @@ const noZebraStripe = [ 'row', 'stripe', 'name', 'actions' ]; * Displays event date details in a standard list table like view * * @function - * @param {Array} entities array of JSON objects defining the Event Dates - * @param {string} showDate - * @param {string} htmlClass - * @param {Function} doRefresh - * @param {Object} otherProps - * @return {Component} list of rendered Event Dates + * @param {Object} props + * @member {Array} entities filtered array of Event Date model objects + * @member {Array} allEventDates array of ALL Event Date model objects + * @member {string} showDate + * @member {string} htmlClass + * @member {Object} otherProps + * @return {Object} rendered table of Event Dates */ const EditorDateEntitiesListView = ( { entities, + allEventDates, showDate, + setEntityIds, + setSortBy, htmlClass, - doRefresh, ...otherProps } ) => { - htmlClass = classNames( htmlClass, 'ee-dates-list-list-view' ); - - const getCapacity = useCallback( - /** - * @function - * @param {number|string} cap AKA reg limit - * @return {number|string} Event Date Capacity - */ - ( cap ) => , - [] + const reorderDates = useReorderDates( + entities, + allEventDates, + setEntityIds, + setSortBy ); /** @@ -65,15 +61,16 @@ const EditorDateEntitiesListView = ( { const filterColumns = useCallback( /** * @function - * @param {Array} columns - * @return {Array} columns + * @param {Object} columns + * @return {Object} columns */ ( columns ) => { const colSwap = { start: 'end', end: 'start' }; const exclude = colSwap[ showDate ] ? colSwap[ showDate ] : ''; - return filterColumnsByKey( columns, exclude ); + columns.cells = filterColumnsByKey( columns.cells, exclude ); + return columns; }, - [] + [ showDate ] ); const formRows = entities.map( @@ -84,39 +81,37 @@ const EditorDateEntitiesListView = ( { */ ( dateEntity ) => { const columns = isModelEntityOfModel( dateEntity, 'datetime' ) ? - datesListTableRow( - dateEntity, - getCapacity( dateEntity.regLimit ), - status( dateEntity ), - getBackgroundColorClass( dateEntity ), - doRefresh, - otherProps - ) : null; + datesListTableRow( dateEntity, otherProps ) : + null; return filterColumns( columns ); } ); + htmlClass = classNames( htmlClass, 'ee-dates-list-list-view' ); + return ( ); }; EditorDateEntitiesListView.propTypes = { entities: PropTypes.array.isRequired, + allEventDates: PropTypes.array.isRequired, showDate: PropTypes.string, htmlClass: PropTypes.string, doRefresh: PropTypes.func, }; EditorDateEntitiesListView.defaultProps = { - entities: [], showDate: '', htmlClass: '', }; diff --git a/assets/src/editor/events/dates-and-times/editor-date/list-view/use-reorder-dates.js b/assets/src/editor/events/dates-and-times/editor-date/list-view/use-reorder-dates.js new file mode 100644 index 00000000000..126af93cd2e --- /dev/null +++ b/assets/src/editor/events/dates-and-times/editor-date/list-view/use-reorder-dates.js @@ -0,0 +1,51 @@ +/** + * External imports + */ +import { useCallback } from '@wordpress/element'; +import { useReorderEntities } from '@eventespresso/components'; + +/** + * @function + * @param {Array} filteredEventDates + * @param {Array} allEventDates + * @param {Function} setEntityIds + * @param {Function} setSortBy + * @return {Function} callback for reordering ticket entity list + */ +const useReorderDates = ( + filteredEventDates, + allEventDates, + setEntityIds, + setSortBy +) => { + const reorderEntities = useReorderEntities( { + modelName: 'datetime', + setEntityIds, + setSortBy, + } ); + /** + * @function + * @param {Object} result + * @return {Function} + */ + return useCallback( ( result ) => { + const { destination, source } = result; + if ( + ! destination || ( + source.index === destination.index && + destination.droppableId === source.droppableId + ) || + destination.droppableId !== 'date-entities-list-view-droppable' + ) { + return; + } + reorderEntities( + filteredEventDates, + allEventDates, + source.index, + destination.index + ); + }, [ filteredEventDates, allEventDates, reorderEntities ] ); +}; + +export default useReorderDates; diff --git a/assets/src/editor/events/ticket-assignments-manager/table/ticket-assignments-table.js b/assets/src/editor/events/ticket-assignments-manager/table/ticket-assignments-table.js index 01490404e00..e3b30c38875 100644 --- a/assets/src/editor/events/ticket-assignments-manager/table/ticket-assignments-table.js +++ b/assets/src/editor/events/ticket-assignments-manager/table/ticket-assignments-table.js @@ -44,8 +44,8 @@ const TicketAssignmentsTable = ( { } ); return dateCount > 0 && ticketCount > 0 ? ( { const statusClass = getBackgroundColorClass( eventDate ); return { type: 'cell', + key: `tam-date-cell-${ eventDate.id }`, class: 'ee-tam-date-label', value: (
diff --git a/assets/src/editor/events/ticket-assignments-manager/table/use-generate-date-row.js b/assets/src/editor/events/ticket-assignments-manager/table/use-generate-date-row.js index d0cf33fdb8f..73b5ee8d622 100644 --- a/assets/src/editor/events/ticket-assignments-manager/table/use-generate-date-row.js +++ b/assets/src/editor/events/ticket-assignments-manager/table/use-generate-date-row.js @@ -42,15 +42,14 @@ const useGenerateDateRow = ( * @return {Array} array of row data */ ( eventDate ) => { - const rowData = [ - { - type: 'row', - class: 'ee-tam-date-row', - value: '', - }, - ]; + const rowData = { + key: `date-row-${ eventDate.id }`, + type: 'row', + class: 'ee-tam-date-row', + cells: [], + }; if ( dateCount > 1 ) { - rowData.push( dateHeader( eventDate ) ); + rowData.cells.push( dateHeader( eventDate ) ); } const dateTicketEntities = isModelEntityOfModel( eventDate, 'datetime' ) && ticketDateMap[ eventDate.id ] ? @@ -62,7 +61,7 @@ const useGenerateDateRow = ( isModelEntityOfModel( ticket, 'ticket' ), 'Invalid EE Ticket model object!' ); - rowData.push( + rowData.cells.push( ticketCell( eventDate.id, ticket.id, dateTicketEntities ) ); } diff --git a/assets/src/editor/events/ticket-assignments-manager/table/use-generate-ticket-cell.js b/assets/src/editor/events/ticket-assignments-manager/table/use-generate-ticket-cell.js index 254df4a362c..03c0fa3b5f4 100644 --- a/assets/src/editor/events/ticket-assignments-manager/table/use-generate-ticket-cell.js +++ b/assets/src/editor/events/ticket-assignments-manager/table/use-generate-ticket-cell.js @@ -29,6 +29,7 @@ const useGenerateTicketCell = ( { assignmentCounts, } ); return { + key: `tam-ticket-cell-${ dateId }-${ ticketId }`, type: 'cell', class: `ee-tam-date-row-ticket${ assignmentsErrorClass }`, value: ( diff --git a/assets/src/editor/events/ticket-assignments-manager/table/use-generate-year-row.js b/assets/src/editor/events/ticket-assignments-manager/table/use-generate-year-row.js index b4a8a6e4cd8..cad2c30461c 100644 --- a/assets/src/editor/events/ticket-assignments-manager/table/use-generate-year-row.js +++ b/assets/src/editor/events/ticket-assignments-manager/table/use-generate-year-row.js @@ -3,6 +3,9 @@ */ import PropTypes from 'prop-types'; import { useCallback } from '@wordpress/element'; +import { EspressoTable } from '@eventespresso/components'; + +const { TableDataCell } = EspressoTable; /** * @function @@ -16,38 +19,36 @@ const useGenerateYearRow = ( ticketCount ) => useCallback( * @return {Array} data for a row showing the year */ ( year ) => { - const rowData = [ - { - type: 'row', - value: '', - class: 'ee-tam-year-row', - }, - { + const yearRow = { + type: 'row', + key: `tam-year-row-${ year }`, + class: 'ee-tam-year-row', + cells: [ { type: 'cell', - value: year, + key: `tam-date-label-${ year }`, class: 'ee-tam-date-label', - }, - ]; + value: year, + } ], + }; for ( let x = 0; x < ticketCount; x++ ) { - rowData.push( - { - type: 'cell', - value: '', - render: ( rowNumber, colNumber ) => ( - - - ), - } - ); + yearRow.cells.push( { + type: 'cell', + key: `tam-ticket-col-${ x + 1 }`, + value: '', + render: ( rowNumber, colNumber ) => ( + + + ), + } ); } - return rowData; + return yearRow; }, - [] + [ ticketCount ] ); useGenerateYearRow.propTypes = { diff --git a/assets/src/editor/events/ticket-assignments-manager/table/use-ticket-headers.js b/assets/src/editor/events/ticket-assignments-manager/table/use-ticket-headers.js index 6e71838a99a..0a6f0a41f91 100644 --- a/assets/src/editor/events/ticket-assignments-manager/table/use-ticket-headers.js +++ b/assets/src/editor/events/ticket-assignments-manager/table/use-ticket-headers.js @@ -18,14 +18,16 @@ import TicketHeaderCell from './ticket-header-cell'; */ const useTicketHeaders = ( { dateCount, ticketEntities } ) => useMemo( () => { - const headerCells = [ { + const headerRow = { type: 'row', - class: '', - value: '', - } ]; + primary: true, + key: 'tam-header-row', + cells: [], + }; if ( dateCount > 1 ) { - headerCells.push( { + headerRow.cells.push( { type: 'cell', + key: 'tam-date-header', class: 'ee-tam-dates-header', value: '', } ); @@ -35,13 +37,14 @@ const useTicketHeaders = ( { dateCount, ticketEntities } ) => useMemo( isModelEntityOfModel( ticket, 'ticket' ), 'Invalid EE Ticket model object!' ); - headerCells.push( { + headerRow.cells.push( { type: 'cell', + key: `tam-ticket-header-${ ticket.id }`, class: 'ee-tam-ticket-header', value: , } ); } ); - return headerCells; + return headerRow; }, [ ticketEntities, dateCount ] ); diff --git a/assets/src/editor/events/ticket-assignments-manager/ticket-assignments-manager.css b/assets/src/editor/events/ticket-assignments-manager/ticket-assignments-manager.css index 1b69719666f..624e0dabfcf 100644 --- a/assets/src/editor/events/ticket-assignments-manager/ticket-assignments-manager.css +++ b/assets/src/editor/events/ticket-assignments-manager/ticket-assignments-manager.css @@ -67,14 +67,16 @@ .ee-ticket-assignments-manager .ee-tam-date-id, .ee-ticket-assignments-manager .ee-tam-ticket-id { - color: var(--ee-color-grey-7); + color: var(--ee-color-grey-6); font-size: var(--ee-font-size-micro); font-weight: normal; } .ee-ticket-assignments-manager .ee-tam-date-id { + left: var(--ee-margin-micro); line-height: var(--ee-font-size-tiny); - margin: var(--ee-margin-micro) 0 0; + position: absolute; + top: calc(var(--ee-margin-nano)*-1); } .ee-ticket-assignments-manager .ee-tam-ticket-id { @@ -132,7 +134,7 @@ .ee-ticket-assignments-manager .ee-tam-date-label-text { font-size: var(--ee-font-size-small); line-height: var(--ee-font-size-default); - margin: 0; + margin: var(--ee-margin-nano) 0 0; } .ee-ticket-assignments-manager .ee-tam-date-row-ticket { diff --git a/assets/src/editor/events/tickets/editor-ticket/actions-menu/editor-ticket-actions-menu.js b/assets/src/editor/events/tickets/editor-ticket/actions-menu/editor-ticket-actions-menu.js index 8f4fda1c5a6..47456c016a5 100644 --- a/assets/src/editor/events/tickets/editor-ticket/actions-menu/editor-ticket-actions-menu.js +++ b/assets/src/editor/events/tickets/editor-ticket/actions-menu/editor-ticket-actions-menu.js @@ -1,7 +1,7 @@ /** * External imports */ -import { useEffect } from '@wordpress/element'; +import { useEffect, useState } from '@wordpress/element'; import { useEntityActionMenuItems } from '@eventespresso/components'; import { ifValidTicketEntity } from '@eventespresso/editor-hocs'; import PropTypes from 'prop-types'; @@ -20,15 +20,14 @@ import TicketPriceCalculatorMenuItem from '../price-calculator/ticket-price-calculator-menu-item'; import useTicketEditorId from '../edit-form/use-ticket-editor-id'; -const EditorTicketActionsMenu = ( { - ticketEntity, -} ) => { +const EditorTicketActionsMenu = ( { ticketEntity } ) => { const editorId = useTicketEditorId( ticketEntity ); + const [ menuItems, setMenuItems ] = useState( [] ); const { getActionsMenuForEntity, registerEntityActionsMenuItem, } = useEntityActionMenuItems(); - const menuItems = getActionsMenuForEntity( ticketEntity ); + useEffect( () => { if ( Array.isArray( menuItems ) && menuItems.length < 1 ) { registerEntityActionsMenuItem( @@ -71,8 +70,13 @@ const EditorTicketActionsMenu = ( { /> ), ); + setMenuItems( getActionsMenuForEntity( ticketEntity ) ); } - }, [ ticketEntity ] ); + }, [ + ticketEntity, + getActionsMenuForEntity, + registerEntityActionsMenuItem, + ] ); return ( <>
diff --git a/assets/src/editor/events/tickets/editor-ticket/editor-ticket-entities-list.js b/assets/src/editor/events/tickets/editor-ticket/editor-ticket-entities-list.js index 73c424ffc63..6a55e709d1e 100644 --- a/assets/src/editor/events/tickets/editor-ticket/editor-ticket-entities-list.js +++ b/assets/src/editor/events/tickets/editor-ticket/editor-ticket-entities-list.js @@ -38,7 +38,7 @@ const EditorTicketEntitiesList = ( { ...otherProps } ) => { showTickets, ticketsSortedBy, displayTicketDate, - ...ticketListFilters + filteredTicketIds, } = useTicketsListFilterState( { listId } ); const { tickets, @@ -55,8 +55,8 @@ const EditorTicketEntitiesList = ( { ...otherProps } ) => { showTickets, ticketsSortedBy, displayTicketDate, + filteredTicketIds, ...entityListFilters, - ...ticketListFilters, } ); const { currentPage, @@ -64,7 +64,10 @@ const EditorTicketEntitiesList = ( { ...otherProps } ) => { paginatedEntities, } = useEntityPagination( perPage, filteredTickets ); // update the ticket ids in state whenever the filters change - const { setFilteredTickets } = useTicketsListFilterStateSetters( listId ); + const { + setFilteredTickets, + setTicketsSortedBy, + } = useTicketsListFilterStateSetters( listId ); useEffect( () => { if ( Array.isArray( paginatedEntities ) ) { setFilteredTickets( @@ -79,6 +82,7 @@ const EditorTicketEntitiesList = ( { ...otherProps } ) => { ticketsSortedBy, Array.isArray( tickets ) ? tickets.length : 0, ] ); + const entityOrder = filteredTicketIds.join( '-' ); return ( { showTickets={ showTickets } ticketsSortedBy={ ticketsSortedBy } displayTicketDate={ displayTicketDate } - { ...ticketListFilters } + filteredTicketIds={ filteredTicketIds } { ...entityListFilters } /> { /> { - htmlClass = classNames( htmlClass, 'ee-tickets-list-list-view' ); - const getQuantity = useCallback( - /** - * @function - * @param {number|string} qty - * @return {number|string} number of available tickets - */ - ( qty ) => { - qty = parseInt( qty, 10 ) || -1; - return qty === -1 || qty === Infinity ? - : - qty; - }, - [] + const reorderTickets = useReorderTickets( + entities, + allTickets, + setEntityIds, + setSortBy ); - /** * toggles display of start and end date columns * based on incoming value of showDate @@ -64,13 +58,13 @@ const EditorTicketEntitiesListView = ( { * @param {Array} columns * @return {Array} columns */ - const filterColumns = ( columns ) => { + const filterColumns = useCallback( ( columns ) => { const colSwap = { start: 'end', end: 'start' }; const exclude = colSwap[ displayTicketDate ] ? colSwap[ displayTicketDate ] : ''; return filterColumnsByKey( columns, exclude ); - }; + }, [ displayTicketDate ] ); const formRows = entities.map( /** @@ -80,31 +74,30 @@ const EditorTicketEntitiesListView = ( { */ ( ticketEntity ) => { const columns = isModelEntityOfModel( ticketEntity, 'ticket' ) ? - ticketsListTableRow( - ticketEntity, - getQuantity( ticketEntity.regLimit ), - status( ticketEntity ), - getBackgroundColorClass( ticketEntity ), - otherProps - ) : null; + ticketsListTableRow( ticketEntity, otherProps ) : + null; return filterColumns( columns ); } ); + htmlClass = classNames( htmlClass, 'ee-tickets-list-list-view' ); return ( ); }; EditorTicketEntitiesListView.propTypes = { entities: PropTypes.array.isRequired, + allTickets: PropTypes.array.isRequired, displayTicketDate: PropTypes.string, htmlClass: PropTypes.string, }; diff --git a/assets/src/editor/events/tickets/editor-ticket/list-view/tickets-list-table-header.js b/assets/src/editor/events/tickets/editor-ticket/list-view/tickets-list-table-header.js index 0b877a9de2a..ffce6ec17c1 100644 --- a/assets/src/editor/events/tickets/editor-ticket/list-view/tickets-list-table-header.js +++ b/assets/src/editor/events/tickets/editor-ticket/list-view/tickets-list-table-header.js @@ -8,132 +8,132 @@ import { __ } from '@eventespresso/i18n'; * header details for the Tickets list table * * @function - * @return {Array} of Ticket list table header details + * @return {Object} of Ticket list table header details */ const ticketsListTableHeader = () => { - return [ - { - key: 'row', - type: 'row', - class: 'ee-editor-ticket-list-items-header-row', - value: '', - }, - { - key: 'stripe', - type: 'cell', - class: 'ee-ticket-list-col-hdr ee-entity-list-status-stripe ee-rspnsv-table-column-micro', - value: '', - }, - { - key: 'id', - type: 'cell', - class: 'ee-ticket-list-col-hdr ee-ticket-list-col-id ee-number-column ee-rspnsv-table-column-tiny', - value: __( 'ID', 'event_espresso' ), - }, - { - key: 'name', - type: 'cell', - class: 'ee-ticket-list-col-hdr ee-ticket-list-col-name ee-rspnsv-table-column-bigger', - value: __( 'Name', 'event_espresso' ), - }, - { - key: 'price', - type: 'cell', - class: 'ee-ticket-list-col-hdr ee-ticket-list-col-price ee-rspnsv-table-column-tiny ee-number-column', - value: __( 'Price', 'event_espresso' ), - }, - { - key: 'start', - type: 'cell', - class: 'ee-ticket-list-col-hdr ee-ticket-list-col-name-start ee-rspnsv-table-column-default', - value: ( - <> + return { + type: 'row', + primary: true, + key: 'ticket-header-row', + class: 'ee-editor-ticket-list-items-header-row', + cells: [ + { + key: 'stripe', + type: 'cell', + class: 'ee-ticket-list-col-hdr ee-entity-list-status-stripe ee-rspnsv-table-column-micro', + value: '', + }, + { + key: 'id', + type: 'cell', + class: 'ee-ticket-list-col-hdr ee-ticket-list-col-id ee-number-column ee-rspnsv-table-column-tiny', + value: __( 'ID', 'event_espresso' ), + }, + { + key: 'name', + type: 'cell', + class: 'ee-ticket-list-col-hdr ee-ticket-list-col-name ee-rspnsv-table-column-bigger', + value: __( 'Name', 'event_espresso' ), + }, + { + key: 'start', + type: 'cell', + class: 'ee-ticket-list-col-hdr ee-ticket-list-col-name-start ee-rspnsv-table-column-default', + value: ( + <> + + { __( 'Goes on Sale', 'event_espresso' ) } + + + { __( 'On Sale', 'event_espresso' ) } + + + ), + }, + { + key: 'end', + type: 'cell', + class: 'ee-ticket-list-col-hdr ee-ticket-list-col-end ee-rspnsv-table-column-default', + value: ( + <> + + { __( 'Sale Ends', 'event_espresso' ) } + + + { __( 'Ends', 'event_espresso' ) } + + + ), + }, + { + key: 'price', + type: 'cell', + class: 'ee-ticket-list-col-hdr ee-ticket-list-col-price ee-rspnsv-table-column-tiny ee-number-column', + value: __( 'Price', 'event_espresso' ), + }, + { + key: 'capacity', + type: 'cell', + class: 'ee-ticket-list-col-hdr ee-ticket-list-col-qty ee-rspnsv-table-column-tiny ee-number-column', + value: ( + <> + + { __( 'Quantity', 'event_espresso' ) } + + + { __( 'Qty', 'event_espresso' ) } + + + ), + }, + { + key: 'sold', + type: 'cell', + class: 'ee-ticket-list-col-hdr ee-ticket-list-col-sold ee-rspnsv-table-column-tiny ee-number-column', + value: __( 'Sold', 'event_espresso' ), + }, + { + key: 'reserved', + type: 'cell', + class: 'ee-ticket-list-col-hdr ee-ticket-list-col-reserved ee-rspnsv-table-column-tiny ee-number-column', + value: ( + <> + + { __( 'Reserved', 'event_espresso' ) } + + + { __( 'Rsrvd', 'event_espresso' ) } + + + ), + }, + { + key: 'registrants', + type: 'cell', + class: 'ee-ticket-list-col-hdr ee-ticket-list-col-registrants ee-rspnsv-table-column-smaller ee-centered-column', + value: ( + <> + + { __( 'Registrants', 'event_espresso' ) } + + + { __( 'Regs', 'event_espresso' ) } + + + ), + }, + { + key: 'actions', + type: 'cell', + class: 'ee-ticket-list-col-hdr ee-ticket-list-col-actions ee-rspnsv-table-column-big ee-centered-column', + value: ( - { __( 'Goes on Sale', 'event_espresso' ) } + { __( 'Actions', 'event_espresso' ) } - - { __( 'On Sale', 'event_espresso' ) } - - - ), - }, - { - key: 'end', - type: 'cell', - class: 'ee-ticket-list-col-hdr ee-ticket-list-col-end ee-rspnsv-table-column-default', - value: ( - <> - - { __( 'Sale Ends', 'event_espresso' ) } - - - { __( 'Ends', 'event_espresso' ) } - - - ), - }, - { - key: 'capacity', - type: 'cell', - class: 'ee-ticket-list-col-hdr ee-ticket-list-col-qty ee-rspnsv-table-column-tiny ee-number-column', - value: ( - <> - - { __( 'Quantity', 'event_espresso' ) } - - - { __( 'Qty', 'event_espresso' ) } - - - ), - }, - { - key: 'sold', - type: 'cell', - class: 'ee-ticket-list-col-hdr ee-ticket-list-col-sold ee-rspnsv-table-column-tiny ee-number-column', - value: __( 'Sold', 'event_espresso' ), - }, - { - key: 'reserved', - type: 'cell', - class: 'ee-ticket-list-col-hdr ee-ticket-list-col-reserved ee-rspnsv-table-column-tiny ee-number-column', - value: ( - <> - - { __( 'Reserved', 'event_espresso' ) } - - - { __( 'Rsrvd', 'event_espresso' ) } - - - ), - }, - { - key: 'registrants', - type: 'cell', - class: 'ee-ticket-list-col-hdr ee-ticket-list-col-registrants ee-rspnsv-table-column-smaller ee-centered-column', - value: ( - <> - - { __( 'Registrants', 'event_espresso' ) } - - - { __( 'Regs', 'event_espresso' ) } - - - ), - }, - { - key: 'actions', - type: 'cell', - class: 'ee-ticket-list-col-hdr ee-ticket-list-col-actions ee-rspnsv-table-column-big ee-centered-column', - value: ( - - { __( 'Actions', 'event_espresso' ) } - - ), - }, - ]; + ), + }, + ], + }; }; export default ticketsListTableHeader; diff --git a/assets/src/editor/events/tickets/editor-ticket/list-view/tickets-list-table-row.js b/assets/src/editor/events/tickets/editor-ticket/list-view/tickets-list-table-row.js index 6f1510c6f0f..52acbabe7ef 100644 --- a/assets/src/editor/events/tickets/editor-ticket/list-view/tickets-list-table-row.js +++ b/assets/src/editor/events/tickets/editor-ticket/list-view/tickets-list-table-row.js @@ -1,7 +1,9 @@ /** * External imports */ +import { ticketModel } from '@eventespresso/model'; import { shortenCuid } from '@eventespresso/utils'; +import { InfinitySymbol } from '@eventespresso/value-objects'; /** * Internal dependencies @@ -9,6 +11,8 @@ import { shortenCuid } from '@eventespresso/utils'; import EditorTicketActionsMenu from '../../../tickets/editor-ticket/actions-menu/editor-ticket-actions-menu'; +const { getBackgroundColorClass, status } = ticketModel; + const DATE_TIME_FORMAT = 'ddd MMM YY h:mm a'; /** @@ -17,106 +21,101 @@ const DATE_TIME_FORMAT = 'ddd MMM YY h:mm a'; * * @function * @param {Object} ticketEntity Event Date entity - * @param {string} quantity - * @param {string} statusClass - * @param {string} bgClass * @param {Object} otherProps * @return {Array} row data for the provided ticket entity */ const ticketsListTableRow = ( ticketEntity, - quantity, - statusClass, - bgClass, otherProps ) => { - return [ - { - key: 'row', - type: 'row', - id: `ee-editor-ticket-list-view-row-${ ticketEntity.id }`, - class: `ee-editor-ticket-list-view-row ${ statusClass }`, - value: '', - }, - { - key: 'stripe', - type: 'cell', - class: `ee-ticket-list-cell ee-entity-list-status-stripe ${ bgClass } ee-rspnsv-table-column-micro`, - value: ( -
- { ticketEntity.name } -
- ), - }, - { - key: 'id', - type: 'cell', - class: 'ee-ticket-list-cell ee-ticket-list-col-id ee-rspnsv-table-column-tiny ee-number-column', - value: shortenCuid( ticketEntity.id ), - }, - { - key: 'name', - type: 'cell', - class: 'ee-ticket-list-cell ee-ticket-list-col-name ee-rspnsv-table-column-bigger ee-rspnsv-table-hide-on-mobile', - value: ticketEntity.name, - }, - { - key: 'price', - type: 'cell', - class: 'ee-ticket-list-col-hdr ee-ticket-list-col-price ee-rspnsv-table-column-tiny ee-number-column', - value: ticketEntity.price.formatter.formatMoney( - ticketEntity.price.amount, - ticketEntity.price.formatter.settings - ), - }, - { - key: 'start', - type: 'cell', - class: 'ee-ticket-list-cell ee-ticket-list-col-start ee-rspnsv-table-column-default', - value: ticketEntity.startDate.toFormat( DATE_TIME_FORMAT ), - }, - { - key: 'end', - type: 'cell', - class: 'ee-ticket-list-cell ee-ticket-list-col-end ee-rspnsv-table-column-default', - value: ticketEntity.endDate.toFormat( DATE_TIME_FORMAT ), - }, - { - key: 'quantity', - type: 'cell', - class: 'ee-ticket-list-cell ee-ticket-list-col-capacity ee-rspnsv-table-column-tiny ee-number-column', - value: quantity, - }, - { - key: 'sold', - type: 'cell', - class: 'ee-ticket-list-cell ee-ticket-list-col-sold ee-rspnsv-table-column-tiny ee-number-column', - value: ticketEntity.sold, - }, - { - key: 'reserved', - type: 'cell', - class: 'ee-ticket-list-cell ee-ticket-list-col-reserved ee-rspnsv-table-column-tiny ee-number-column', - value: ticketEntity.reserved, - }, - { - key: 'registrants', - type: 'cell', - class: 'ee-ticket-list-cell ee-ticket-list-col-registrants ee-rspnsv-table-column-smaller ee-centered-column', - value: ticketEntity.sold, // should be count of related registrations - }, - { - key: 'actions', - type: 'cell', - class: 'ee-ticket-list-cell ee-ticket-list-col-actions ee-rspnsv-table-column-big', - value: ( - - ), - }, - ]; + const statusClass = status( ticketEntity ); + const bgClass = getBackgroundColorClass( ticketEntity ); + return { + type: 'row', + key: `ticket-row-${ ticketEntity.id }`, + id: `ee-editor-ticket-list-view-row-${ ticketEntity.id }`, + class: `ee-editor-ticket-list-view-row ${ statusClass }`, + cells: [ + { + key: 'stripe', + type: 'cell', + class: `ee-ticket-list-cell ee-entity-list-status-stripe ${ bgClass } ee-rspnsv-table-column-micro`, + value: ( +
+ { ticketEntity.name } +
+ ), + }, + { + key: 'id', + type: 'cell', + class: 'ee-ticket-list-cell ee-ticket-list-col-id ee-rspnsv-table-column-tiny ee-number-column', + value: shortenCuid( ticketEntity.id ), + }, + { + key: 'name', + type: 'cell', + class: 'ee-ticket-list-cell ee-ticket-list-col-name ee-rspnsv-table-column-bigger ee-rspnsv-table-hide-on-mobile', + value: ticketEntity.name, + }, + { + key: 'start', + type: 'cell', + class: 'ee-ticket-list-cell ee-ticket-list-col-start ee-rspnsv-table-column-default', + value: ticketEntity.startDate.toFormat( DATE_TIME_FORMAT ), + }, + { + key: 'end', + type: 'cell', + class: 'ee-ticket-list-cell ee-ticket-list-col-end ee-rspnsv-table-column-default', + value: ticketEntity.endDate.toFormat( DATE_TIME_FORMAT ), + }, + { + key: 'price', + type: 'cell', + class: 'ee-ticket-list-col-hdr ee-ticket-list-col-price ee-rspnsv-table-column-tiny ee-number-column', + value: ticketEntity.price.formatter.formatMoney( + ticketEntity.price.amount, + ticketEntity.price.formatter.settings + ), + }, + { + key: 'quantity', + type: 'cell', + class: 'ee-ticket-list-cell ee-ticket-list-col-capacity ee-rspnsv-table-column-tiny ee-number-column', + value: , + }, + { + key: 'sold', + type: 'cell', + class: 'ee-ticket-list-cell ee-ticket-list-col-sold ee-rspnsv-table-column-tiny ee-number-column', + value: ticketEntity.sold, + }, + { + key: 'reserved', + type: 'cell', + class: 'ee-ticket-list-cell ee-ticket-list-col-reserved ee-rspnsv-table-column-tiny ee-number-column', + value: ticketEntity.reserved, + }, + { + key: 'registrants', + type: 'cell', + class: 'ee-ticket-list-cell ee-ticket-list-col-registrants ee-rspnsv-table-column-smaller ee-centered-column', + value: ticketEntity.sold, // should be count of related registrations + }, + { + key: 'actions', + type: 'cell', + class: 'ee-ticket-list-cell ee-ticket-list-col-actions ee-rspnsv-table-column-big', + value: ( + + ), + }, + ], + }; }; export default ticketsListTableRow; diff --git a/assets/src/editor/events/tickets/editor-ticket/list-view/use-reorder-tickets.js b/assets/src/editor/events/tickets/editor-ticket/list-view/use-reorder-tickets.js new file mode 100644 index 00000000000..4472160c880 --- /dev/null +++ b/assets/src/editor/events/tickets/editor-ticket/list-view/use-reorder-tickets.js @@ -0,0 +1,57 @@ +/** + * External imports + */ +import { useCallback } from '@wordpress/element'; +import { useReorderEntities } from '@eventespresso/components'; + +/** + * @function + * @param {Array} filteredTickets + * @param {Array} allTickets + * @param {Function} setEntityIds + * @param {Function} setSortBy + * @return {Function} callback for reordering ticket entity list + */ +const useReorderTickets = ( + filteredTickets, + allTickets, + setEntityIds, + setSortBy +) => { + const reorderEntities = useReorderEntities( { + modelName: 'ticket', + setEntityIds, + setSortBy, + } ); + /** + * @function + * @param {Object} result + * @return {Function} + */ + return useCallback( ( result ) => { + const { destination, source } = result; + if ( + ! destination || + ( + source.index === destination.index && + destination.droppableId === source.droppableId + ) || + destination.droppableId !== + 'ticket-entities-list-view-droppable' + ) { + return; + } + reorderEntities( + filteredTickets, + allTickets, + source.index, + destination.index + ); + }, [ + filteredTickets, + allTickets, + reorderEntities, + ] ); +}; + +export default useReorderTickets; diff --git a/assets/src/editor/events/tickets/editor-ticket/price-calculator/hooks/use-generate-price-modifier-row.js b/assets/src/editor/events/tickets/editor-ticket/price-calculator/hooks/use-generate-price-modifier-row.js index a56fec937db..31b8de3e187 100644 --- a/assets/src/editor/events/tickets/editor-ticket/price-calculator/hooks/use-generate-price-modifier-row.js +++ b/assets/src/editor/events/tickets/editor-ticket/price-calculator/hooks/use-generate-price-modifier-row.js @@ -32,90 +32,96 @@ const useGeneratePriceModifierRow = ( ticketPrefix, values ) => { const prefix = `${ ticketPrefix }-price-${ priceId }`; const priceTypeId = normalizeEntityId( values[ `${ prefix }-type` ] ) || 0; const priceType = getPriceType( priceTypeId, priceTypes ); - return [ - { - type: 'row', - class: 'ee-ticket-price-calculator-price-row', - value: '', - }, - { - type: 'cell', - class: 'ee-ticket-price-calculator-price-id ee-number-column', - value: ( - - ), - }, - { - type: 'cell', - class: 'ee-ticket-price-calculator-price-type', - value: ( - - ), - }, - { - type: 'cell', - class: 'ee-ticket-price-calculator-price-name', - value: ( - - ), - }, - { - type: 'cell', - class: 'ee-ticket-price-calculator-price-desc', - value: ( - - ), - }, - { - type: 'cell', - class: 'ee-ticket-price-calculator-price-amount ee-number-column', - value: ( - - ), - }, - { - type: 'cell', - class: 'ee-ticket-price-calculator-price-actions', - value: ( - - + ), + }, + { + key: 'type', + type: 'cell', + class: 'ee-ticket-price-calculator-price-type', + value: ( + + ), + }, + { + key: 'name', + type: 'cell', + class: 'ee-ticket-price-calculator-price-name', + value: ( + - + ), + }, + { + key: 'amount', + type: 'cell', + class: 'ee-ticket-price-calculator-price-amount ee-number-column', + value: ( + - - ), - }, - ]; + ), + }, + { + key: 'actions', + type: 'cell', + class: 'ee-ticket-price-calculator-price-actions', + value: ( + + + + + ), + }, + ], + }; }, [ values, ticketPrefix, diff --git a/assets/src/editor/events/tickets/editor-ticket/price-calculator/hooks/use-ticket-price-calculator-form-decorator.js b/assets/src/editor/events/tickets/editor-ticket/price-calculator/hooks/use-ticket-price-calculator-form-decorator.js index b4705810c71..2eabf7c72ed 100644 --- a/assets/src/editor/events/tickets/editor-ticket/price-calculator/hooks/use-ticket-price-calculator-form-decorator.js +++ b/assets/src/editor/events/tickets/editor-ticket/price-calculator/hooks/use-ticket-price-calculator-form-decorator.js @@ -19,7 +19,10 @@ const useTicketPriceCalculatorFormDecorator = ( setFormData ) => { { field: /^(.*?(\b-amount\b))$/, updates: ( value, name, formData, prevData ) => { - if ( ! amountsMatch( formData[ name ], prevData[ name ] ) ) { + if ( + prevData[ name ] !== undefined && + ! amountsMatch( formData[ name ], prevData[ name ] ) + ) { formData.updated = true; setFormData( formData ); } @@ -29,7 +32,10 @@ const useTicketPriceCalculatorFormDecorator = ( setFormData ) => { { field: /^(.*?(\b-type\b))$/, updates: ( value, name, formData, prevData ) => { - if ( formData[ name ] !== prevData[ name ] ) { + if ( + prevData[ name ] !== undefined && + formData[ name ] !== prevData[ name ] + ) { formData.updated = true; setFormData( formData ); } @@ -39,7 +45,10 @@ const useTicketPriceCalculatorFormDecorator = ( setFormData ) => { { field: 'ticketTotal', updates: ( value, name, formData, prevData ) => { - if ( ! amountsMatch( formData[ name ], prevData[ name ] ) ) { + if ( + prevData[ name ] !== undefined && + ! amountsMatch( formData[ name ], prevData[ name ] ) + ) { formData.updated = true; setFormData( formData ); } diff --git a/assets/src/editor/events/tickets/editor-ticket/price-calculator/hooks/use-ticket-price-calculator-form-header.js b/assets/src/editor/events/tickets/editor-ticket/price-calculator/hooks/use-ticket-price-calculator-form-header.js index 26960c001b9..3f601634848 100644 --- a/assets/src/editor/events/tickets/editor-ticket/price-calculator/hooks/use-ticket-price-calculator-form-header.js +++ b/assets/src/editor/events/tickets/editor-ticket/price-calculator/hooks/use-ticket-price-calculator-form-header.js @@ -12,44 +12,51 @@ import { __ } from '@eventespresso/i18n'; * @return {Array} form header data */ const useTicketPriceCalculatorFormHeader = () => useMemo( () => { - return [ - { - type: 'row', - class: 'ee-ticket-price-calculator-header-row', - value: '', - }, - { - type: 'cell', - class: 'ee-ticket-price-calculator-id ee-number-column', - value: __( 'ID', 'event_espresso' ), - }, - { - type: 'cell', - class: 'ee-ticket-price-calculator-type', - value: __( 'Price Type', 'event_espresso' ), - }, - { - type: 'cell', - class: 'ee-ticket-price-calculator-name', - value: __( 'Label', 'event_espresso' ), - }, - { - type: 'cell', - class: 'ee-ticket-price-calculator-desc', - value: __( 'Description', 'event_espresso' ), - }, - { - type: 'cell', - class: 'ee-ticket-price-calculator-amount ' + - 'ee-number-column ' + currencySignPositionClass(), - value: __( 'Amount', 'event_espresso' ), - }, - { - type: 'cell', - class: 'ee-ticket-price-calculator-actions', - value: __( 'Actions', 'event_espresso' ), - }, - ]; + return { + type: 'row', + primary: true, + key: 'price-header-row', + class: 'ee-ticket-price-calculator-header-row', + cells: [ + { + key: 'id', + type: 'cell', + class: 'ee-ticket-price-calculator-id ee-number-column', + value: __( 'ID', 'event_espresso' ), + }, + { + key: 'type', + type: 'cell', + class: 'ee-ticket-price-calculator-type', + value: __( 'Price Type', 'event_espresso' ), + }, + { + key: 'name', + type: 'cell', + class: 'ee-ticket-price-calculator-name', + value: __( 'Label', 'event_espresso' ), + }, + { + key: 'desc', + type: 'cell', + class: 'ee-ticket-price-calculator-desc', + value: __( 'Description', 'event_espresso' ), + }, + { + key: 'amount', + type: 'cell', + class: 'ee-ticket-price-calculator-amount ' + + 'ee-number-column ' + currencySignPositionClass(), + value: __( 'Amount', 'event_espresso' ), + }, + { + key: 'actions', + type: 'cell', + class: 'ee-ticket-price-calculator-actions', + value: __( 'Actions', 'event_espresso' ), + }, + ], + }; } ); export default useTicketPriceCalculatorFormHeader; diff --git a/assets/src/editor/events/tickets/editor-ticket/price-calculator/hooks/use-ticket-price-calculator-form-schema.js b/assets/src/editor/events/tickets/editor-ticket/price-calculator/hooks/use-ticket-price-calculator-form-schema.js index 7d1941fee0e..e720c2662a7 100644 --- a/assets/src/editor/events/tickets/editor-ticket/price-calculator/hooks/use-ticket-price-calculator-form-schema.js +++ b/assets/src/editor/events/tickets/editor-ticket/price-calculator/hooks/use-ticket-price-calculator-form-schema.js @@ -61,10 +61,10 @@ const buildPricesData = ( formData, ticket, prices ) => { if ( isModelEntityOfModel( price, 'price' ) ) { const priceId = shortenCuid( price.id ); priceIDs.push( priceId ); - priceTypes.push( price.prtId ); + priceTypes.push( price.PRT_ID ); const pricePrefix = `${ prefix }-${ priceId }`; formData[ `${ pricePrefix }-id` ] = priceId; - formData[ `${ pricePrefix }-type` ] = parseInt( price.prtId, 10 ); + formData[ `${ pricePrefix }-type` ] = parseInt( price.PRT_ID, 10 ); formData[ `${ pricePrefix }-name` ] = price.name || ''; formData[ `${ pricePrefix }-desc` ] = price.desc || ''; formData[ `${ pricePrefix }-amount` ] = getMoneyAmount( price.amount ); diff --git a/assets/src/editor/events/tickets/editor-ticket/price-calculator/hooks/use-ticket-price-calculator-form-total-row.js b/assets/src/editor/events/tickets/editor-ticket/price-calculator/hooks/use-ticket-price-calculator-form-total-row.js index e7e658c739c..046e719829d 100644 --- a/assets/src/editor/events/tickets/editor-ticket/price-calculator/hooks/use-ticket-price-calculator-form-total-row.js +++ b/assets/src/editor/events/tickets/editor-ticket/price-calculator/hooks/use-ticket-price-calculator-form-total-row.js @@ -35,100 +35,106 @@ const useTicketPriceCalculatorFormTotalRow = ( const calcDirText = ticket.reverseCalculate ? __( 'reverse calculate base price from total', 'event_espresso' ) : __( 'calculate total from base price', 'event_espresso' ); - return [ - { - type: 'row', - class: 'ee-ticket-price-calculator-total-row', - value: '', - }, - { - type: 'cell', - class: '', - value: '', - }, - { - type: 'cell', - class: '', - value: '', - }, - { - type: 'cell', - class: '', - value: '', - }, - { - type: 'cell', - class: 'ee-ticket-price-calculator-total-label' + - ' ee-number-column', - value: ( - - ), - }, - { - type: 'cell', - class: 'ee-ticket-price-calculator-total ee-number-column', - value: ( - - - { - if ( ! amountsMatch( value, prev ) ) { - ticket.price = new Money( - parseMoneyValue( value ), - SiteCurrency - ); + return { + key: 'price-total-row', + type: 'row', + class: 'ee-ticket-price-calculator-total-row', + cells: [ + { + key: 'id', + type: 'cell', + class: '', + value: '', + }, + { + key: 'type', + type: 'cell', + class: '', + value: '', + }, + { + key: 'name', + type: 'cell', + class: '', + value: '', + }, + { + key: 'desc', + type: 'cell', + class: 'ee-ticket-price-calculator-total-label' + + ' ee-number-column', + value: ( + + ), + }, + { + key: 'amount', + type: 'cell', + class: 'ee-ticket-price-calculator-total ee-number-column', + value: ( + + + { + if ( ! amountsMatch( value, prev ) ) { + ticket.price = new Money( + parseMoneyValue( value ), + SiteCurrency + ); + } } } - } - disabled={ ticket.reverseCalculate === false } - format={ ( value ) => { - return ticket.price.formatter.formatNumber( - parseMoneyValue( value ) - ); - } } - formatOnBlur - /> - - ), - }, - { - type: 'cell', - class: 'ee-ticket-price-calculator-total-actions', - value: ( - - { - const value = ! ticket.reverseCalculate; - ticket.reverseCalculate = value; - updateField( 'reverseCalculate', value ); + disabled={ ticket.reverseCalculate === false } + format={ ( value ) => { + return ticket.price.formatter.formatNumber( + parseMoneyValue( value ) + ); + } } + formatOnBlur + /> + + ), + }, + { + key: 'actions', + type: 'cell', + class: 'ee-ticket-price-calculator-total-actions', + value: ( + + { + const value = ! ticket.reverseCalculate; + ticket.reverseCalculate = value; + updateField( 'reverseCalculate', value ); + } } - } - /> - - ), - }, - ]; + /> + + ), + }, + ], + }; }; export default useTicketPriceCalculatorFormTotalRow; diff --git a/assets/src/editor/events/tickets/editor-ticket/price-calculator/ticket-price-calculator-form-modal.js b/assets/src/editor/events/tickets/editor-ticket/price-calculator/ticket-price-calculator-form-modal.js index f992b50d3f3..756b71f24ec 100644 --- a/assets/src/editor/events/tickets/editor-ticket/price-calculator/ticket-price-calculator-form-modal.js +++ b/assets/src/editor/events/tickets/editor-ticket/price-calculator/ticket-price-calculator-form-modal.js @@ -2,7 +2,7 @@ * External imports */ import { useEffect } from '@wordpress/element'; -import { EditorModal, ifValidTicketEntity } from '@eventespresso/editor-hocs'; +import { EditorModal, ifValidTicketEntity, useIsEditorOpen } from '@eventespresso/editor-hocs'; import { __, _x, sprintf } from '@eventespresso/i18n'; import { FormHandler } from '@eventespresso/components'; import PropTypes from 'prop-types'; @@ -34,17 +34,19 @@ const TicketPriceCalculatorFormModal = ( { ...otherProps } ) => { const editorId = useTicketPriceCalculatorEditorId( ticket ); + const isEditorOpen = useIsEditorOpen( editorId ); const { formData, setFormData, } = useTicketPriceCalculatorFormData( ticket, prices ); const calculateTicketPrices = useCalculateTicketPrices( prices, setFormData ); const formDecorator = useTicketPriceCalculatorFormDecorator( setFormData ); - useEffect( - () => calculateTicketPrices( formData ), - [ formData ] - ); - return editorId ? ( + useEffect( () => { + if ( pricesLoaded && formData.updated ) { + calculateTicketPrices( formData ); + } + }, [ calculateTicketPrices, formData, prices ] ); + return editorId && pricesLoaded && isEditorOpen ? ( . */ /** * Event Espresso diff --git a/js-green-licenses.json b/js-green-licenses.json index 56fac48040b..5032bf4b1a3 100644 --- a/js-green-licenses.json +++ b/js-green-licenses.json @@ -1,5 +1,6 @@ { "greenLicenses": [ + "Apache-2.0", "GPL-2.0", "GPL-2.0-or-later", "GPL-2.0+", diff --git a/license.txt b/license.txt index 0bab81490c3..eb89f9b45bf 100644 --- a/license.txt +++ b/license.txt @@ -1,281 +1,399 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 51 Franklin St, Fifth Floor, Boston, MA 02110, USA - - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS +GNU GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 +Copyright © 2007 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +Preamble +The GNU General Public License is a free, copyleft license for software and other kinds of works. + +The licenses for most software and other practical works are designed to take away your freedom to share and change the +works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all +versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use +the GNU General Public License for most of our software; it applies also to any other work released this way by its +authors. You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make + sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you + receive source code or can get it if you want it, that you can change the software or use pieces of it in new free + programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. +Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: +responsibilities to respect the freedom of others. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients + the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you + must show them these terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer +you this License giving you legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. +For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems +will not be attributed erroneously to authors of previous versions. + +Some devices are designed to deny users access to install or run modified versions of the software inside them, although + the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the + software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is + precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice + for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to + those domains in future versions of the GPL, as needed to protect the freedom of users. + +Finally, every program is threatened constantly by software patents. States should not allow patents to restrict +development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger +that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and modification follow. + +TERMS AND CONDITIONS +0. Definitions. +“This License” refers to version 3 of the GNU General Public License. + +“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. + +“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. +“Licensees” and “recipients” may be individuals or organizations. + +To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, +other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work +“based on” the earlier work. + +A “covered work” means either the unmodified Program or a work based on the Program. + +To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily +liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. +Propagation includes copying, distribution (with or without modification), making available to the public, and in some +countries other activities as well. + +To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction +with a user through a computer network, with no transfer of a copy, is not conveying. + +An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and +prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no +warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this + License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as + a menu, a prominent item in the list meets this criterion. + +1. Source Code. +The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means +any non-source form of a work. + +A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, +or, in the case of interfaces specified for a particular programming language, one that is widely used among developers +working in that language. + +The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in +the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to +enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is +available to the public in source code form. A “Major Component”, in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a +compiler used to produce the work, or an object code interpreter used to run it. + +The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and +(for an executable work) run the object code and to modify the work, including scripts to control those activities. +However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs +which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding +Source includes interface definition files associated with source files for the work, and the source code for shared +libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data + communication or control flow between those subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can regenerate automatically from other parts of the +Corresponding Source. + +The Corresponding Source for a work in source code form is that same work. + +2. Basic Permissions. +All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided + the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. + The output from running a covered work is covered by this License only if the output, given its content, constitutes a + covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, without conditions so long as your license +otherwise remains in force. You may convey covered works to others for the sole purpose of having them make +modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with + the terms of this License in conveying all material for which you do not control copyright. Those thus making or + running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms + that prohibit them from making any copies of your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not +allowed; section 10 makes it unnecessary. + +3. Protecting Users' Legal Rights From Anti-Circumvention Law. +No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling +obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or +restricting circumvention of such measures. + +When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the +extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you +disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, + your or third parties' legal rights to forbid circumvention of technological measures. + +4. Conveying Verbatim Copies. +You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating +that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices + of the absence of any warranty; and give all recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for + a fee. + +5. Conveying Modified Source Versions. +You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source +code under the terms of section 4, provided that you also meet all of these conditions: + +a) The work must carry prominent notices stating that you modified it, and giving a relevant date. +b) The work must carry prominent notices stating that it is released under this License and any conditions added under +section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. +c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This +License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all +its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, +but it does not invalidate such permission if you have separately received it. +d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has + interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. +A compilation of a covered work with other separate and independent works, which are not by their nature extensions of +the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or + distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the + access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work + in an aggregate does not cause this License to apply to the other parts of the aggregate. + +6. Conveying Non-Source Forms. +You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the + machine-readable Corresponding Source under the terms of this License, in one of these ways: + +a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied + by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. +b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied + by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support + for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for + all the software in the product that is covered by this License, on a durable physical medium customarily used for + software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, + or (2) access to copy the Corresponding Source from a network server at no charge. +c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. +This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an + offer, in accord with subsection 6b. +d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent +access to the Corresponding Source in the same way through the same place at no further charge. You need not require +recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a +network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports +equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the +Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it +is available for as long as needed to satisfy these requirements. +e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and +Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. +A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, + need not be included in conveying the object code work. + +A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used +for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In +determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a +particular product received by a particular user, “normally used” refers to a typical or common use of that class of +product, regardless of the status of the particular user or of the way in which the particular user actually uses, or +expects or is expected to use, the product. A product is a consumer product regardless of whether the product has +substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of +the product. + +“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information +required to install and execute modified versions of a covered work in that User Product from a modified version of its +Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code +is in no case prevented or interfered with solely because modification has been made. + +If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the +conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to + the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding + Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not + apply if neither you nor any third party retains the ability to install modified object code on the User Product (for + example, the work has been installed in ROM). + +The requirement to provide Installation Information does not include a requirement to continue to provide support +service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product +in which it has been modified or installed. Access to a network may be denied when the modification itself materially +and adversely affects the operation of the network or violates the rules and protocols for communication across the +network. + +Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format +that is publicly documented (and with an implementation available to the public in source code form), and must require +no special password or key for unpacking, reading or copying. + +7. Additional Terms. +“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of +its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were +included in this License, to the extent that they are valid under applicable law. If additional permissions apply only +to part of the Program, that part may be used separately under those permissions, but the entire Program remains +governed by this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or +from any part of it. (Additional permissions may be written to require their own removal in certain cases when you +modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have +or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by +the copyright holders of that material) supplement the terms of this License with terms: + +a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or +b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the +Appropriate Legal Notices displayed by works containing it; or +c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be + marked in reasonable ways as different from the original version; or +d) Limiting the use for publicity purposes of names of licensors or authors of the material; or +e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or +f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified +versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual +assumptions directly impose on those licensors and authors. +All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the + Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with + a term that is a further restriction, you may remove that term. If a license document contains a further restriction + but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms + of that license document, provided that the further restriction does not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a +statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as +exceptions; the above requirements apply either way. + +8. Termination. +You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to + propagate or modify it is void, and will automatically terminate your rights under this License (including any patent + licenses granted under the third paragraph of section 11). + +However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated +(a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) +permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days +after the cessation. + +Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you + of the violation by some reasonable means, this is the first time you have received notice of violation of this License + (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the + notice. + +Termination of your rights under this section does not terminate the licenses of parties who have received copies or +rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not +qualify to receive new licenses for the same material under section 10. + +9. Acceptance Not Required for Having Copies. +You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a + covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not + require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered + work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a + covered work, you indicate your acceptance of this License to do so. + +10. Automatic Licensing of Downstream Recipients. +Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, + modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third + parties with this License. + +An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or + subdividing an organization, or merging organizations. If propagation of a covered work results from an entity + transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work + the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the + Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with + reasonable efforts. + +You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For +example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, +and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent +claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. + +11. Patents. +A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the +Program is based. The work thus licensed is called the contributor's “contributor version”. + +A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already +acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or +selling its contributor version, but do not include claims that would be infringed only as a consequence of further +modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent +sublicenses in a manner consistent with the requirements of this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential +patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its +contributor version. + +In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not +to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). + To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent + against the party. + +If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not +available for anyone to copy, free of charge and under the terms of this License, through a publicly available network +server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or + (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a + manner consistent with the requirements of this License, to extend the patent license to downstream recipients. + “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work + in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents + in that country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring +conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing +them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is +automatically extended to all recipients of the covered work and works based on it. + +A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, + or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. + You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of + distributing software, under which you make payment to the third party based on the extent of your activity of + conveying the work, and under which the third party grants, to any of the parties who would receive the covered work + from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies + made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the + covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to +infringement that may otherwise be available to you under applicable patent law. + +12. No Surrender of Others' Freedom. +If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this + License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to + satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence + you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further + conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License + would be to refrain entirely from conveying the Program. + +13. Use with the GNU Affero General Public License. +Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work + licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the + resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special + requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply + to the combination as such. + +14. Revised Versions of this License. +The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time +. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems +or concerns. + +Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the +GNU General Public License “or any later version” applies to it, you have the option of following the terms and +conditions either of that numbered version or of any later version published by the Free Software Foundation. If the +Program does not specify a version number of the GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, +that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the +Program. + +Later license versions may give you additional or different permissions. However, no additional obligations are imposed +on any author or copyright holder as a result of your choosing to follow a later version. + +15. Disclaimer of Warranty. +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING + THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED + OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE + DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. Limitation of Liability. +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO +MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, +INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO + LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM + TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + DAMAGES. + +17. Interpretation of Sections 15 and 16. +If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to + their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil + liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the + Program in return for a fee. + +END OF TERMS AND CONDITIONS diff --git a/package-lock.json b/package-lock.json index c55c5f81e97..de5092ae54a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1462,6 +1462,22 @@ "regenerator-runtime": "^0.13.2" } }, + "@babel/runtime-corejs2": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.6.2.tgz", + "integrity": "sha512-wdyVKnTv9Be4YlwF/7pByYNfcl23qC21aAQ0aIaZOo2ZOvhFEyJdBLJClYZ9i+Pmrz7sUQgg/MwbJa2RZTkygg==", + "requires": { + "core-js": "^2.6.5", + "regenerator-runtime": "^0.13.2" + }, + "dependencies": { + "core-js": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" + } + } + }, "@babel/runtime-corejs3": { "version": "7.6.2", "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.6.2.tgz", @@ -7821,6 +7837,14 @@ "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=" }, + "css-box-model": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.0.tgz", + "integrity": "sha512-lri0br+jSNV0kkkiGEp9y9y3Njq2PmpqbeGWRFQJuZteZzY9iC9GZhQ8Y4WpPwM/2YocjHePxy14igJY7YKzkA==", + "requires": { + "tiny-invariant": "^1.0.6" + } + }, "css-color-names": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", @@ -9598,7 +9622,7 @@ }, "eslint-plugin-react": { "version": "7.14.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.14.3.tgz", + "resolved": "http://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.14.3.tgz", "integrity": "sha512-EzdyyBWC4Uz2hPYBiEJrKCUi2Fn+BJ9B/pJQcjw5X+x/H2Nm59S4MJIvL4O5NEE0+WbnQwEBxWY03oUk+Bc3FA==", "dev": true, "requires": { @@ -11597,6 +11621,14 @@ "minimalistic-crypto-utils": "^1.0.1" } }, + "hoist-non-react-statics": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz", + "integrity": "sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==", + "requires": { + "react-is": "^16.7.0" + } + }, "homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", @@ -20175,6 +20207,11 @@ "performance-now": "^2.1.0" } }, + "raf-schd": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.2.tgz", + "integrity": "sha512-VhlMZmGy6A6hrkJWHLNTGl5gtgMUm+xfGza6wbwnE914yeQ5Ybm18vgM734RZhMgfw4tacUrWseGZlpUrrakEQ==" + }, "railroad-diagrams": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", @@ -20290,6 +20327,21 @@ "prop-types": "^15.5.6" } }, + "react-beautiful-dnd": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-11.0.5.tgz", + "integrity": "sha512-7llby9U+jIfkINcyxPHVWU0HFYzqxMemUYgGHsFsbx4fZo1n/pW6sYKYzhxGxR3Ap5HxqswcQkKUZX4uEUWhlw==", + "requires": { + "@babel/runtime-corejs2": "^7.4.5", + "css-box-model": "^1.1.2", + "memoize-one": "^5.0.4", + "raf-schd": "^4.0.0", + "react-redux": "^7.0.3", + "redux": "^4.0.1", + "tiny-invariant": "^1.0.4", + "use-memo-one": "^1.1.0" + } + }, "react-dates": { "version": "17.2.0", "resolved": "https://registry.npmjs.org/react-dates/-/react-dates-17.2.0.tgz", @@ -20371,8 +20423,7 @@ "react-is": { "version": "16.8.6", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", - "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==", - "dev": true + "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==" }, "react-js-pagination": { "version": "3.0.2", @@ -20482,6 +20533,34 @@ "prop-types": "^15.5.8" } }, + "react-redux": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.1.1.tgz", + "integrity": "sha512-QsW0vcmVVdNQzEkrgzh2W3Ksvr8cqpAv5FhEk7tNEft+5pp7rXxAudTz3VOPawRkLIepItpkEIyLcN/VVXzjTg==", + "requires": { + "@babel/runtime": "^7.5.5", + "hoist-non-react-statics": "^3.3.0", + "invariant": "^2.2.4", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-is": "^16.9.0" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.2.tgz", + "integrity": "sha512-EXxN64agfUqqIGeEjI5dL5z0Sw0ZwWo1mLTi4mQowCZ42O59b7DRpZAnTC6OqdF28wMBMFKNb/4uFGrVaigSpg==", + "requires": { + "regenerator-runtime": "^0.13.2" + } + }, + "react-is": { + "version": "16.10.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.10.1.tgz", + "integrity": "sha512-BXUMf9sIOPXXZWqr7+c5SeOKJykyVr2u0UDzEf4LNGc6taGkQe1A9DFD07umCIXz45RLr9oAAwZbAJ0Pkknfaw==" + } + } + }, "react-select": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/react-select/-/react-select-3.0.5.tgz", @@ -20802,7 +20881,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.1.tgz", "integrity": "sha512-R7bAtSkk7nY6O/OYMVR9RiBI+XghjF9rlbl5806HJbQph0LJVHZrU5oaO4q70eUKiqMRqm4y07KLTlMZ2BlVmg==", - "dev": true, "requires": { "loose-envify": "^1.4.0", "symbol-observable": "^1.2.0" @@ -22821,8 +22899,7 @@ "symbol-observable": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", - "dev": true + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" }, "symbol-tree": { "version": "3.2.4", @@ -23281,6 +23358,11 @@ "integrity": "sha512-2NM0auVBGft5tee/OxP4PI3d8WItkDM+fPnaRAVo6xTDI2knbz9eC5ArWGqtGlYqiH3RU5yMpdyTTO7MguC4ow==", "dev": true }, + "tiny-invariant": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.0.6.tgz", + "integrity": "sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA==" + }, "tiny-lr": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.1.1.tgz", @@ -23985,6 +24067,11 @@ "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" }, + "use-memo-one": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.1.tgz", + "integrity": "sha512-oFfsyun+bP7RX8X2AskHNTxu+R3QdE/RC5IefMbqptmACAA/gfol1KDD5KRzPsGMa62sWxGZw+Ui43u6x4ddoQ==" + }, "util": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", diff --git a/package.json b/package.json index f6e40072187..89b996c3359 100644 --- a/package.json +++ b/package.json @@ -121,6 +121,7 @@ "pluralize": "^8.0.0", "prop-types": "^15.7.2", "querystringify": "^2.1.1", + "react-beautiful-dnd": "^11.0.4", "react-final-form": "^6.3.0", "react-final-form-listeners": "^1.0.2", "react-js-pagination": "^3.0.2",