diff --git a/packages/survey-vue3-ui/src/components/action-bar/ActionBarItem.vue b/packages/survey-vue3-ui/src/components/action-bar/ActionBarItem.vue index bf0cc69ea8..bfd03c934e 100644 --- a/packages/survey-vue3-ui/src/components/action-bar/ActionBarItem.vue +++ b/packages/survey-vue3-ui/src/components/action-bar/ActionBarItem.vue @@ -3,8 +3,8 @@ :class="item.getActionBarItemCss()" type="button" v-on:click=" - () => { - item.action(); + (args: any) => { + item.action(item, !!args.pointerType); } " v-on:keyup=" diff --git a/src/defaultCss/defaultV2Css.ts b/src/defaultCss/defaultV2Css.ts index 92026a7894..adfa83c992 100644 --- a/src/defaultCss/defaultV2Css.ts +++ b/src/defaultCss/defaultV2Css.ts @@ -429,6 +429,7 @@ export var defaultV2Css = { rootScroll: "sd-question--scroll", root: "sd-table sd-matrixdropdown", noHeader: "sd-table--no-header", + hasFooter: "sd-table--has-footer", rootVerticalAlignTop: "sd-table--align-top", rootVerticalAlignMiddle: "sd-table--align-middle", tableWrapper: "sd-table-wrapper", @@ -439,6 +440,9 @@ export var defaultV2Css = { errorsCellBottom: "sd-table__cell--error-bottom", itemCell: "sd-table__cell--item", row: "sd-table__row", + expandedRow: "sd-table__row--expanded", + rowHasPanel: "sd-table__row--has-panel", + rowHasEndActions: "sd-table__row--has-end-actions", headerCell: "sd-table__cell sd-table__cell--header", rowTextCell: "sd-table__cell sd-table__cell--row-text", columnTitleCell: "sd-table__cell--column-title", @@ -450,6 +454,8 @@ export var defaultV2Css = { detailIconId: "icon-expanddetail", detailIconExpandedId: "icon-collapsedetail", detailPanelCell: "sd-table__cell--detail-panel", + detailRowCell: "sd-table__cell--detail", + actionsCellPrefix: "sd-table__cell-action", actionsCell: "sd-table__cell sd-table__cell--actions", actionsCellDrag: "sd-table__cell--drag", emptyCell: "sd-table__cell--empty", @@ -463,10 +469,14 @@ export var defaultV2Css = { empty: "sd-question--empty", root: "sd-table sd-matrixdynamic", noHeader: "sd-table--no-header", + hasFooter: "sd-table--has-footer", tableWrapper: "sd-table-wrapper", content: "sd-matrixdynamic__content sd-question__content", cell: "sd-table__cell", row: "sd-table__row", + rowHasPanel: "sd-table__row--has-panel", + rowHasEndActions: "sd-table__row--has-end-actions", + expandedRow: "sd-table__row--expanded", itemCell: "sd-table__cell--item", headerCell: "sd-table__cell sd-table__cell--header", rowTextCell: "sd-table__cell sd-table__cell--row-text", @@ -481,6 +491,8 @@ export var defaultV2Css = { detailIconId: "icon-expanddetail", detailIconExpandedId: "icon-collapsedetail", detailPanelCell: "sd-table__cell--detail-panel", + detailRowCell: "sd-table__cell--detail", + actionsCellPrefix: "sd-table__cell-action", actionsCell: "sd-table__cell sd-table__cell--actions", actionsCellDrag: "sd-table__cell--drag", buttonAdd: "sd-matrixdynamic__add-btn", diff --git a/src/defaultV2-theme/blocks/sd-table.scss b/src/defaultV2-theme/blocks/sd-table.scss index 3bd323b8c4..abc6d30155 100644 --- a/src/defaultV2-theme/blocks/sd-table.scss +++ b/src/defaultV2-theme/blocks/sd-table.scss @@ -443,7 +443,8 @@ .sd-table { tr { - display: block; + display: flex; + flex-direction: column; } } @@ -452,11 +453,33 @@ padding-top: calcSize(1); } } + .sd-table__row { + padding-top: calcSize(2); + } + .sd-table__row--expanded { + .sd-table__cell-action--show-detail-mobile { + display: none; + } + } .sd-table:not(.sd-matrix__table) { - tr { - padding-bottom: calcSize(1); - + .sd-table__row { + padding-bottom: calcSize(2); + &.sd-table__row--has-end-actions { + padding-bottom: calcSize(0); + } + } + + tr:not(.sd-table__row--has-end-actions){ + &:not(:last-of-type) { + padding-bottom: calcSize(3); + &::after { + bottom: calcSize(-3); + } + } + padding-bottom: calcSize(0); + } + tr:not(.sd-table__row--expanded) { &::after { z-index: 12; content: " "; @@ -465,18 +488,26 @@ height: 1px; background-color: $border-light; left: calcSize(-2); - bottom: calcSize(-1); + bottom: calcSize(0); width: calc(100% + 4 * #{$base-unit}); z-index: 12; } } } + .sd-table:not(.sd-table--has-footer) .sd-table__row:last-of-type { + .sd-table__cell-action--show-detail-mobile, .sd-table__cell-action--remove-row { + margin-bottom: calcSize(-2); + } + } + .sd-matrix__label { justify-content: start; } .sd-table__cell { + border-top: none; + border-bottom: none; display: block; padding-top: 0; padding-bottom: 0; @@ -484,9 +515,18 @@ } .sd-table__cell { - margin-top: calcSize(1); + margin-top: calcSize(2); } + .sd-table__cell--error { + margin-top: calcSize(0); + &.sd-table__cell--error-bottom { + .sd-question__erbox { + margin-top: calcSize(1); + } + } + } + .sd-table__cell:first-of-type, .sd-matrix__cell { margin-top: 0; @@ -494,7 +534,6 @@ .sd-table__cell { &::before { - padding-top: calcSize(2); padding-bottom: calcSize(1); content: attr(data-responsive-title); font-weight: 600; @@ -511,15 +550,29 @@ content: none; } } + .sd-table__cell--detail-panel { + padding-top: 0; + padding-bottom: 0; + border-top: 0; + border-bottom: 0; + .sd-panel__content { + padding-top: 0; + } + } .sd-table__cell.sd-table__cell--actions { width: auto; + margin-top: calcSize(1); + margin-bottom: calcSize(1); .sd-action-bar { margin-right: calcSize(-3); - justify-content: flex-end; + margin-left: calcSize(-3); background: $background; } + #show-detail-mobile { + flex-grow: 1; + } } .sd-action.sd-action.sd-matrixdynamic__remove-btn { @@ -542,14 +595,9 @@ .sd-table__cell--row-text:not(.sd-matrix__cell) { color: $foreground-light; - padding-top: calcSize(2); } .sd-matrixdropdown.sd-table { - tr:not(:last-child) { - padding-bottom: calcSize(2); - } - tr::after { bottom: calcSize(-2); } @@ -569,14 +617,15 @@ .sd-table__cell--error-top { .sd-question__erbox { - margin-top: calcSize(2); - margin-bottom: calcSize(-2); + margin-top: calcSize(3); + margin-bottom: calcSize(-1); } } .sd-table__cell--error-top:first-of-type { .sd-question__erbox { - margin-bottom: calcSize(-1); + margin-top: calcSize(0); + margin-bottom: calcSize(1); } } } diff --git a/src/localization/english.ts b/src/localization/english.ts index d189189947..5759ee6cdf 100644 --- a/src/localization/english.ts +++ b/src/localization/english.ts @@ -61,6 +61,8 @@ export var englishStrings = { emptyRowsText: "There are no rows.", addPanel: "Add new", removePanel: "Remove", + showDetails: "Show Details", + hideDetails: "Hide Details", choices_Item: "item", matrix_column: "Column", matrix_row: "Row", diff --git a/src/martixBase.ts b/src/martixBase.ts index 3f8f1a16b1..5db72c068b 100644 --- a/src/martixBase.ts +++ b/src/martixBase.ts @@ -305,6 +305,7 @@ export class QuestionMatrixBaseModel extends Question { return new CssClassBuilder() .append(this.cssClasses.root) .append(this.cssClasses.noHeader, !this.showHeader) + .append(this.cssClasses.hasFooter, !!this.renderedTable?.showAddRowOnBottom) .append(this.cssClasses.rootAlternateRows, this.alternateRows) .append(this.cssClasses.rootVerticalAlignTop, (this.verticalAlign === "top")) .append(this.cssClasses.rootVerticalAlignMiddle, (this.verticalAlign === "middle")).toString(); diff --git a/src/question_matrixdropdownrendered.ts b/src/question_matrixdropdownrendered.ts index 88bc6c9505..c11574ae46 100644 --- a/src/question_matrixdropdownrendered.ts +++ b/src/question_matrixdropdownrendered.ts @@ -1,6 +1,6 @@ import { property, propertyArray } from "./jsonobject"; import { Question } from "./question"; -import { Base } from "./base"; +import { Base, ComputedUpdater } from "./base"; import { ItemValue } from "./itemvalue"; import { surveyLocalization } from "./surveyStrings"; import { LocalizableString } from "./localizablestring"; @@ -37,6 +37,7 @@ export class QuestionMatrixDropdownRenderedCell { public isActionsCell: boolean = false; public isErrorsCell: boolean = false; public isDragHandlerCell: boolean = false; + public isDetailRowCell: boolean = false; private classNameValue: string = ""; public constructor() { this.idValue = QuestionMatrixDropdownRenderedCell.counter++; @@ -140,6 +141,7 @@ export class QuestionMatrixDropdownRenderedRow extends Base { @property({ defaultValue: false }) isGhostRow: boolean; @property({ defaultValue: false }) isAdditionalClasses: boolean; @property({ defaultValue: true }) visible: boolean; + public hasEndActions: boolean = false; public row: MatrixDropdownRowModelBase; public isErrorsRow = false; private static counter = 1; @@ -161,6 +163,9 @@ export class QuestionMatrixDropdownRenderedRow extends Base { return new CssClassBuilder() .append(this.cssClasses.row) .append(this.cssClasses.detailRow, this.isDetailRow) + .append(this.cssClasses.rowHasPanel, this.row?.hasPanel) + .append(this.cssClasses.expandedRow, this.row?.isDetailPanelShowing && !this.isDetailRow) + .append(this.cssClasses.rowHasEndActions, this.hasEndActions) .append(this.cssClasses.ghostRow, this.isGhostRow) .append(this.cssClasses.rowAdditional, this.isAdditionalClasses) .toString(); @@ -542,9 +547,23 @@ export class QuestionMatrixDropdownRenderedTable extends Base { return cell; } private getActionsCellClassName(cell: QuestionMatrixDropdownRenderedCell = null): string { - return new CssClassBuilder().append(this.cssClasses.actionsCell).append(this.cssClasses.actionsCellDrag, cell?.isDragHandlerCell).append(this.cssClasses.verticalCell, !this.matrix.isColumnLayoutHorizontal).toString(); + const classBuilder = + new CssClassBuilder() + .append(this.cssClasses.actionsCell) + .append(this.cssClasses.actionsCellDrag, cell?.isDragHandlerCell) + .append(this.cssClasses.detailRowCell, cell?.isDetailRowCell) + .append(this.cssClasses.verticalCell, !this.matrix.isColumnLayoutHorizontal); + if (cell.isActionsCell) { + const actions = (cell.item.value as ActionContainer).actions; + if (this.cssClasses.actionsCellPrefix) { + actions.forEach(action => { + classBuilder.append(this.cssClasses.actionsCellPrefix + "--" + action.id); + }); + } + } + return classBuilder.toString(); } - private getRowActionsCell(rowIndex: number, location: "start" | "end") { + private getRowActionsCell(rowIndex: number, location: "start" | "end", isDetailRow: boolean = false) { const rowActions = this.getRowActions(rowIndex, location); if (!this.isValueEmpty(rowActions)) { const cell = new QuestionMatrixDropdownRenderedCell(); @@ -558,6 +577,7 @@ export class QuestionMatrixDropdownRenderedTable extends Base { cell.item = itemValue; cell.isActionsCell = true; cell.isDragHandlerCell = false; + cell.isDetailRowCell = isDetailRow; cell.className = this.getActionsCellClassName(cell); cell.row = this.matrix.visibleRows[rowIndex]; return cell; @@ -629,16 +649,31 @@ export class QuestionMatrixDropdownRenderedTable extends Base { } if (row.hasPanel) { - actions.push( - new Action({ - id: "show-detail", - title: this.matrix.getLocalizationString("editText"), - showTitle: false, - location: "start", - component: "sv-matrix-detail-button", - data: { row: row, question: this.matrix }, - }) - ); + if (this.matrix.isMobile) { + actions.unshift( + new Action({ + id: "show-detail-mobile", + title: "Show Details", + showTitle: true, + location: "end", + action: (context) => { + context.title = row.isDetailPanelShowing ? this.matrix.getLocalizationString("showDetails") : this.matrix.getLocalizationString("hideDetails"); + row.showHideDetailPanelClick(); + }, + }) + ); + } else { + actions.push( + new Action({ + id: "show-detail", + title: this.matrix.getLocalizationString("editText"), + showTitle: false, + location: "start", + component: "sv-matrix-detail-button", + data: { row: row, question: this.matrix }, + }) + ); + } } } private createErrorRow( @@ -713,12 +748,14 @@ export class QuestionMatrixDropdownRenderedTable extends Base { ) { var rowIndex = this.matrix.visibleRows.indexOf(row); if (this.hasActionCellInRows(location)) { - const actions = this.getRowActionsCell(rowIndex, location); + const actions = this.getRowActionsCell(rowIndex, location, renderedRow.isDetailRow); if (!!actions) { renderedRow.cells.push(actions); + renderedRow.hasEndActions = true; } else { var cell = new QuestionMatrixDropdownRenderedCell(); cell.isEmpty = true; + cell.isDetailRowCell = renderedRow.isDetailRow; renderedRow.cells.push(cell); } } @@ -749,8 +786,13 @@ export class QuestionMatrixDropdownRenderedTable extends Base { (!!actionsCell ? actionsCell.colSpans : 0); cell.className = this.cssClasses.detailPanelCell; res.cells.push(cell); + if (!!actionsCell) { - res.cells.push(actionsCell); + if (this.matrix.isMobile) { + this.addRowActionsCell(row, res, "end"); + } else { + res.cells.push(actionsCell); + } } if ( typeof this.matrix.onCreateDetailPanelRenderedRowCallback === "function" diff --git a/tests/markup/snapshots/matrixdropdown-vertical.snap.html b/tests/markup/snapshots/matrixdropdown-vertical.snap.html index 1e3eb12d0a..d9666e1f5e 100644 --- a/tests/markup/snapshots/matrixdropdown-vertical.snap.html +++ b/tests/markup/snapshots/matrixdropdown-vertical.snap.html @@ -13,7 +13,7 @@ - + @@ -29,7 +29,7 @@ - + diff --git a/tests/markup/snapshots/matrixdynamic-defaultV2.snap.html b/tests/markup/snapshots/matrixdynamic-defaultV2.snap.html index a9f70cc752..fe748d1363 100644 --- a/tests/markup/snapshots/matrixdynamic-defaultV2.snap.html +++ b/tests/markup/snapshots/matrixdynamic-defaultV2.snap.html @@ -1,6 +1,6 @@
- +
- + - - + -