diff options
8 files changed, 292 insertions, 93 deletions
diff --git a/WebContent/VAADIN/themes/base/grid/grid.scss b/WebContent/VAADIN/themes/base/grid/grid.scss index ed068a5efc..a79420f7a9 100644 --- a/WebContent/VAADIN/themes/base/grid/grid.scss +++ b/WebContent/VAADIN/themes/base/grid/grid.scss @@ -85,7 +85,8 @@ $v-grid-editor-background-color: $v-grid-row-background-color !default; // Rows - .#{$primaryStyleName}-row > td { + .#{$primaryStyleName}-row > td, + .#{$primaryStyleName}-editor-cells > div { border-left: $v-grid-cell-vertical-border; border-bottom: $v-grid-cell-horizontal-border; @@ -221,22 +222,35 @@ $v-grid-editor-background-color: $v-grid-row-background-color !default; // Editor .#{$primaryStyleName}-editor { - // TODO should be fixed in offset calculations - margin-top: -1px; position: absolute; - overflow-y: visible; - background: $v-grid-editor-background-color; - @include box-shadow(0 0 10px 1px rgba(0,0,0,.3)); + z-index: 20; + overflow: hidden; + left: 0; + right: 0; + border: $v-grid-border; + margin-top: nth($v-grid-border, 1) * -1; + @include box-shadow(0 0 9px rgba(0,0,0,.2)); + } + + .#{$primaryStyleName}-editor-cells { + position: relative; + white-space: nowrap; > div { - position: absolute; + display: inline-block; @include box-sizing(border-box); - border-left: $v-grid-cell-vertical-border; + vertical-align: middle; + background: $v-grid-editor-background-color; &:first-child { border-left: none; } + > * { + vertical-align: middle; + display: inline-block; + } + .v-textfield, .v-datefield, .v-filterselect { @@ -244,24 +258,60 @@ $v-grid-editor-background-color: $v-grid-row-background-color !default; max-width: 100%; min-height: 100%; max-height: 100%; - border: none; - border-radius: 0; } - .v-textfield-focus, - .v-filterselect-focus input { - position: relative; - z-index: 1; + .v-select, + .v-select-select { + min-width: 100%; + max-width: 100%; } } } - .#{$primaryStyleName}-editor-save, - .#{$primaryStyleName}-editor-cancel { - position: absolute; - // TODO remove the inline size from the widgets - width: auto !important; - height: auto !important; + .#{$primaryStyleName}-editor-footer { + display: table; + height: $v-grid-row-height; + border-top: $v-grid-cell-horizontal-border; + margin-top: nth($v-grid-cell-horizontal-border, 1) * -1; + background: $v-grid-row-background-color; + padding: 0 5px; + + + .#{$primaryStyleName}-editor-cells > div { + border-bottom: none; + border-top: $v-grid-cell-horizontal-border; + } + + &:first-child { + border-top: none; + margin-top: 0; + border-bottom: $v-grid-cell-horizontal-border; + margin-bottom: nth($v-grid-cell-horizontal-border, 1) * -1; + } + } + + .#{$primaryStyleName}-editor-message, + .#{$primaryStyleName}-editor-buttons { + display: table-cell; + white-space: nowrap; + vertical-align: middle; + } + + .#{$primaryStyleName}-editor-message { + width: 100%; + position: relative; + + > div { + position: absolute; + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + line-height: $v-grid-row-height; + top: 0; + } + } + + .#{$primaryStyleName}-editor-save { + margin-right: 4px; } // Renderers diff --git a/WebContent/VAADIN/themes/reindeer/grid/grid.scss b/WebContent/VAADIN/themes/reindeer/grid/grid.scss index 8dacb3ccce..28c5977fda 100644 --- a/WebContent/VAADIN/themes/reindeer/grid/grid.scss +++ b/WebContent/VAADIN/themes/reindeer/grid/grid.scss @@ -57,4 +57,35 @@ background-image: url(img/desc-light.png); } + .#{$primaryStyleName}-editor-footer { + background: #dfe1e3; + } + + .#{$primaryStyleName}-editor-cells > div { + .v-textfield, + .v-textfield-focus, + .v-datefield, + .v-datefield .v-textfield-focus, + .v-filterselect, + .v-filterselect-input, + .v-filterselect-input:focus { + border: none; + border-radius: 0; + background: transparent; + } + + .v-filterselect { + overflow: hidden; + } + + .v-filterselect-input { + height: 100%; + } + + .v-filterselect-button { + // 24px is the height of v-filterselect + margin-top: round((24px - $v-grid-row-height) / -2) + } + } + } diff --git a/WebContent/VAADIN/themes/runo/grid/grid.scss b/WebContent/VAADIN/themes/runo/grid/grid.scss index a1081878cc..06a04ec626 100644 --- a/WebContent/VAADIN/themes/runo/grid/grid.scss +++ b/WebContent/VAADIN/themes/runo/grid/grid.scss @@ -50,4 +50,8 @@ background-image: url(img/sort-desc.png); } + .#{$primaryStyleName}-editor-footer { + background: #e7e9ea; + } + } diff --git a/WebContent/VAADIN/themes/valo/components/_grid.scss b/WebContent/VAADIN/themes/valo/components/_grid.scss index 50c7169225..c481f127a8 100644 --- a/WebContent/VAADIN/themes/valo/components/_grid.scss +++ b/WebContent/VAADIN/themes/valo/components/_grid.scss @@ -3,7 +3,7 @@ $v-grid-row-background-color: valo-table-background-color() !default; $v-grid-row-stripe-background-color: scale-color($v-grid-row-background-color, $lightness: if(color-luminance($v-grid-row-background-color) < 10, 4%, -4%)) !default; -$v-grid-border: valo-border($color: $v-grid-row-background-color, $strength: 0.8) !default; +$v-grid-border: flatten-list(valo-border($color: $v-grid-row-background-color, $strength: 0.8)) !default; $v-grid-cell-focused-border: max(2px, first-number($v-border)) solid $v-selection-color !default; $v-grid-row-height: $v-table-row-height !default; @@ -14,6 +14,8 @@ $v-grid-header-background-color: $v-background-color !default; $v-grid-cell-padding-horizontal: $v-table-cell-padding-horizontal !default; +$v-grid-animations-enabled: $v-animations-enabled !default; + @import "../../base/grid/grid"; @@ -68,10 +70,91 @@ $v-grid-cell-padding-horizontal: $v-table-cell-padding-horizontal !default; } } + .#{$primary-stylename}-editor { + @include valo-focus-style; + border-color: $v-focus-color; + } + + .#{$primary-stylename}-editor-footer { + font-size: $v-font-size--small; + padding: 0 round($v-layout-spacing-horizontal / 2); + background: $v-app-background-color; + @if $v-grid-animations-enabled { + @include animation(valo-grid-editor-footer-animate-in 200ms 120ms backwards); + } + } + + @if $v-grid-animations-enabled { + .#{$primary-stylename}-editor-footer:first-child { + @include animation(valo-grid-editor-footer-animate-in-alt 200ms 120ms backwards); + } + } + + .#{$primary-stylename}-editor-cells { + z-index: 1; + } + + .#{$primary-stylename}-editor-cells > div { + // Vertical centering for widgets + &:before { + content: ""; + display: inline-block; + height: 100%; + vertical-align: middle; + } + + .v-textfield, + .v-textfield-focus, + .v-datefield, + .v-datefield .v-textfield-focus, + .v-filterselect-input, + .v-filterselect-input:focus { + border: none; + border-radius: 0; + background: transparent; + + @if $v-textfield-bevel { + @include box-shadow(valo-bevel-and-shadow($bevel: $v-textfield-bevel)); + } @else { + @include box-shadow(none); + } + } + + .v-textfield-focus, + .v-datefield .v-textfield-focus, + .v-filterselect-input:focus { + position: relative; + } + + .v-select { + padding-left: round($v-grid-cell-padding-horizontal / 2); + padding-right: round($v-grid-cell-padding-horizontal / 2); + } + + .v-checkbox { + margin: 0 round($v-grid-cell-padding-horizontal / 2); + + label { + white-space: nowrap; + } + } + } + + .#{$primary-stylename}-editor-message > div:before { + display: inline-block; + @include valo-error-indicator-style($is-pseudo-element: true); + } + .#{$primary-stylename}-editor-save, .#{$primary-stylename}-editor-cancel { - @include valo-button-static-style; - @include valo-button-style($unit-size: $v-unit-size--small, $font-size: $v-font-size--small); + @include valo-link-style; + font-weight: $v-font-weight + 100; + text-decoration: none; + border: none; + background: transparent; + padding: round($v-layout-spacing-vertical / 2) round($v-layout-spacing-horizontal / 2); + margin: 0; + outline: none; } // Customize scrollbars @@ -98,3 +181,19 @@ $v-grid-cell-padding-horizontal: $v-table-cell-padding-horizontal !default; } } + + +@include keyframes(valo-grid-editor-footer-animate-in) { + 0% { + margin-top: -$v-grid-row-height; + } +} + +@include keyframes(valo-grid-editor-footer-animate-in-alt) { + 0% { + margin-bottom: -$v-grid-row-height - first-number($v-grid-cell-horizontal-border); + } + 100% { + margin-bottom: first-number($v-grid-cell-horizontal-border) * -1; + } +} diff --git a/WebContent/VAADIN/themes/valo/shared/_global.scss b/WebContent/VAADIN/themes/valo/shared/_global.scss index 4ce294b06a..b4e8564119 100644 --- a/WebContent/VAADIN/themes/valo/shared/_global.scss +++ b/WebContent/VAADIN/themes/valo/shared/_global.scss @@ -394,16 +394,22 @@ $valo-shared-pathPrefix: null; /** * Error indicator styles. The error indicator is by default a font character which you can style freely. * + * @param {boolean} $is-pseudo-element (false) - is the selector including this mixin targeting a pseudo element + * * @requires {mixin} valo-error-indicator-icon-style by default */ -@mixin valo-error-indicator-style { +@mixin valo-error-indicator-style ($is-pseudo-element: false) { color: $v-error-indicator-color; font-weight: 600; width: ceil($v-unit-size/2); text-align: center; - &:before { + @if $is-pseudo-element { @include valo-error-indicator-icon-style; + } @else { + &:before { + @include valo-error-indicator-icon-style; + } } } diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index 303b94763d..fb7ec3abb5 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -942,13 +942,6 @@ public class Grid<T> extends ResizeComposite implements */ protected static class Editor<T> { - private static final double BUTTON_HEIGHT = 25; - private static final double BUTTON_WIDTH = 50; - private static final double BUTTON_MARGIN = 5; - private static final double SAVE_BUTTON_LEFT_MARGIN_PX = 0; - private static final double CANCEL_BUTTON_LEFT_MARGIN_PX = SAVE_BUTTON_LEFT_MARGIN_PX - + BUTTON_WIDTH + BUTTON_MARGIN; - public static final int KEYCODE_SHOW = KeyCodes.KEY_ENTER; public static final int KEYCODE_HIDE = KeyCodes.KEY_ESCAPE; @@ -960,6 +953,16 @@ public class Grid<T> extends ResizeComposite implements private EditorHandler<T> handler; private DivElement editorOverlay = DivElement.as(DOM.createDiv()); + private DivElement cellWrapper = DivElement.as(DOM.createDiv()); + private DivElement messageAndButtonsWrapper = DivElement.as(DOM + .createDiv()); + + private DivElement messageWrapper = DivElement.as(DOM.createDiv()); + private DivElement buttonsWrapper = DivElement.as(DOM.createDiv()); + + // Element which contains the error message for the editor + // Should only be added to the DOM when there's a message to show + private DivElement message = DivElement.as(DOM.createDiv()); private Map<Column<?, T>, Widget> columnToWidget = new HashMap<Column<?, T>, Widget>(); @@ -1055,7 +1058,6 @@ public class Grid<T> extends ResizeComposite implements public Editor() { saveButton = new Button(); saveButton.setText(GridConstants.DEFAULT_SAVE_CAPTION); - saveButton.setStylePrimaryName("v-nativebutton"); saveButton.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { @@ -1065,7 +1067,6 @@ public class Grid<T> extends ResizeComposite implements cancelButton = new Button(); cancelButton.setText(GridConstants.DEFAULT_CANCEL_CAPTION); - cancelButton.setStylePrimaryName("v-nativebutton"); cancelButton.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { @@ -1274,23 +1275,7 @@ public class Grid<T> extends ResizeComposite implements */ protected void showOverlay(TableRowElement tr) { - DivElement tableWrapper = DivElement.as(tr.getParentElement() - .getParentElement().getParentElement()); - - AbstractRowContainer body = (AbstractRowContainer) grid - .getEscalator().getBody(); - - double rowTop = body.getRowTop(tr); - int bodyTop = body.getElement().getAbsoluteTop(); - int wrapperTop = tableWrapper.getAbsoluteTop(); - - double width = WidgetUtil - .getRequiredWidthBoundingClientRectDouble(tr); - double height = WidgetUtil - .getRequiredHeightBoundingClientRectDouble(tr); - double overlayTop = rowTop + bodyTop - wrapperTop; - setBounds(editorOverlay, tr.getOffsetLeft(), overlayTop, width, - height); + DivElement gridElement = DivElement.as(grid.getElement()); scrollHandler = grid.addScrollHandler(new ScrollHandler() { @Override @@ -1299,12 +1284,14 @@ public class Grid<T> extends ResizeComposite implements } }); - tableWrapper.appendChild(editorOverlay); + gridElement.appendChild(editorOverlay); + editorOverlay.appendChild(cellWrapper); + editorOverlay.appendChild(messageAndButtonsWrapper); for (int i = 0; i < tr.getCells().getLength(); i++) { Element cell = createCell(tr.getCells().getItem(i)); - editorOverlay.appendChild(cell); + cellWrapper.appendChild(cell); Column<?, T> column = grid.getColumn(i); if (column.isEditable()) { @@ -1316,43 +1303,58 @@ public class Grid<T> extends ResizeComposite implements } } - attachWidget(saveButton, editorOverlay); - attachWidget(cancelButton, editorOverlay); + // Only add these elements once + if (!messageAndButtonsWrapper.isOrHasChild(messageWrapper)) { + messageAndButtonsWrapper.appendChild(messageWrapper); + messageAndButtonsWrapper.appendChild(buttonsWrapper); + } - /* - * We can't use BUTTON_HEIGHT here, becuase it's ignored by at least - * Chrome and Firefox. So we measure it. - */ - double buttonTop = getButtonTop(tr, saveButton.getOffsetHeight()); - setBounds(saveButton.getElement(), SAVE_BUTTON_LEFT_MARGIN_PX, - buttonTop, BUTTON_WIDTH, BUTTON_HEIGHT); - setBounds(cancelButton.getElement(), CANCEL_BUTTON_LEFT_MARGIN_PX, - buttonTop, BUTTON_WIDTH, BUTTON_HEIGHT); + attachWidget(saveButton, buttonsWrapper); + attachWidget(cancelButton, buttonsWrapper); updateHorizontalScrollPosition(); - } - private double getButtonTop(TableRowElement tr, int buttonHeight) { - boolean buttonsShouldBeRenderedBelow = buttonsShouldBeRenderedBelow( - tr, buttonHeight); - final double buttonTop; - if (buttonsShouldBeRenderedBelow) { - buttonTop = tr.getOffsetHeight() + BUTTON_MARGIN; + AbstractRowContainer body = (AbstractRowContainer) grid + .getEscalator().getBody(); + double rowTop = body.getRowTop(tr); + + int bodyTop = body.getElement().getAbsoluteTop(); + int gridTop = gridElement.getAbsoluteTop(); + double overlayTop = rowTop + bodyTop - gridTop; + + if (buttonsShouldBeRenderedBelow(tr)) { + // Default case, editor buttons are below the edited row + editorOverlay.getStyle().setTop(overlayTop, Unit.PX); + editorOverlay.getStyle().clearBottom(); } else { - buttonTop = -(buttonHeight + BUTTON_MARGIN); + // Move message and buttons wrapper on top of cell wrapper if + // there is not enough space visible space under and fix the + // overlay from the bottom + editorOverlay.appendChild(cellWrapper); + int gridHeight = grid.getElement().getOffsetHeight(); + editorOverlay.getStyle() + .setBottom( + gridHeight - overlayTop - tr.getOffsetHeight(), + Unit.PX); + editorOverlay.getStyle().clearTop(); + } + + // Do not render over the vertical scrollbar + int nativeScrollbarSize = WidgetUtil.getNativeScrollbarSize(); + if (nativeScrollbarSize > 0) { + editorOverlay.getStyle().setRight(nativeScrollbarSize, Unit.PX); } - return buttonTop; } - private boolean buttonsShouldBeRenderedBelow(TableRowElement tr, - int buttonHeight) { + private boolean buttonsShouldBeRenderedBelow(TableRowElement tr) { TableSectionElement tfoot = grid.escalator.getFooter().getElement(); double tfootPageTop = WidgetUtil.getBoundingClientRect(tfoot) .getTop(); double trPageBottom = WidgetUtil.getBoundingClientRect(tr) .getBottom(); - double bottomOfButtons = trPageBottom + buttonHeight - + BUTTON_MARGIN; + int messageAndButtonsHeight = messageAndButtonsWrapper + .getOffsetHeight(); + double bottomOfButtons = trPageBottom + messageAndButtonsHeight; return bottomOfButtons < tfootPageTop; } @@ -1366,6 +1368,7 @@ public class Grid<T> extends ResizeComposite implements detachWidget(cancelButton); editorOverlay.removeAllChildren(); + cellWrapper.removeAllChildren(); editorOverlay.removeFromParent(); scrollHandler.removeHandler(); @@ -1374,14 +1377,27 @@ public class Grid<T> extends ResizeComposite implements protected void setStylePrimaryName(String primaryName) { if (styleName != null) { editorOverlay.removeClassName(styleName); + + cellWrapper.removeClassName(styleName + "-cells"); + messageAndButtonsWrapper.removeClassName(styleName + "-footer"); + + messageWrapper.removeClassName(styleName + "-message"); + buttonsWrapper.removeClassName(styleName + "-buttons"); + saveButton.removeStyleName(styleName + "-save"); cancelButton.removeStyleName(styleName + "-cancel"); } styleName = primaryName + "-editor"; - editorOverlay.addClassName(styleName); + editorOverlay.setClassName(styleName); - saveButton.addStyleName(styleName + "-save"); - cancelButton.addStyleName(styleName + "-cancel"); + cellWrapper.setClassName(styleName + "-cells"); + messageAndButtonsWrapper.setClassName(styleName + "-footer"); + + messageWrapper.setClassName(styleName + "-message"); + buttonsWrapper.setClassName(styleName + "-buttons"); + + saveButton.setStyleName(styleName + "-save"); + cancelButton.setStyleName(styleName + "-cancel"); } /** @@ -1425,13 +1441,7 @@ public class Grid<T> extends ResizeComposite implements private void updateHorizontalScrollPosition() { double scrollLeft = grid.getScrollLeft(); - - editorOverlay.getStyle().setLeft(-scrollLeft, Unit.PX); - - double saveLeftPx = scrollLeft + SAVE_BUTTON_LEFT_MARGIN_PX; - double cancelLeftPx = scrollLeft + CANCEL_BUTTON_LEFT_MARGIN_PX; - saveButton.getElement().getStyle().setLeft(saveLeftPx, Unit.PX); - cancelButton.getElement().getStyle().setLeft(cancelLeftPx, Unit.PX); + cellWrapper.getStyle().setLeft(-scrollLeft, Unit.PX); } protected void setGridEnabled(boolean enabled) { diff --git a/uitest/src/com/vaadin/testbench/elements/GridElement.java b/uitest/src/com/vaadin/testbench/elements/GridElement.java index 5a27bc1444..6b1279d2c2 100644 --- a/uitest/src/com/vaadin/testbench/elements/GridElement.java +++ b/uitest/src/com/vaadin/testbench/elements/GridElement.java @@ -106,8 +106,7 @@ public class GridElement extends AbstractComponentElement { * useless. */ public void save() { - List<WebElement> buttons = findElements(By.xpath("./button")); - buttons.get(0).click(); + findElement(By.className("v-grid-editor-save")).click(); } /** @@ -117,8 +116,7 @@ public class GridElement extends AbstractComponentElement { * useless. */ public void cancel() { - List<WebElement> buttons = findElements(By.xpath("./button")); - buttons.get(1).click(); + findElement(By.className("v-grid-editor-cancel")).click(); } } diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridEditorClientTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridEditorClientTest.java index f6f2fac074..30481ebc65 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridEditorClientTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridEditorClientTest.java @@ -117,8 +117,9 @@ public class GridEditorClientTest extends GridBasicClientFeaturesTest { selectMenuPath("Component", "State", "Selection mode", "multi"); selectMenuPath(EDIT_ROW_5); - WebElement editor = getEditor(); - List<WebElement> selectorDivs = editor.findElements(By + WebElement editorCells = findElement(By + .className("v-grid-editor-cells")); + List<WebElement> selectorDivs = editorCells.findElements(By .cssSelector("div")); assertTrue("selector column cell should've been empty", selectorDivs |