diff --git a/examples/webpack-demo-vanilla-bundle/package.json b/examples/webpack-demo-vanilla-bundle/package.json index 1dfe0908f..bc659cce1 100644 --- a/examples/webpack-demo-vanilla-bundle/package.json +++ b/examples/webpack-demo-vanilla-bundle/package.json @@ -36,7 +36,7 @@ "@types/jquery": "^3.5.4", "@types/moment": "^2.13.0", "@types/node": "^14.14.7", - "@types/webpack": "^4.41.24", + "@types/webpack": "^4.41.25", "clean-webpack-plugin": "^3.0.0", "copy-webpack-plugin": "^6.3.0", "css-loader": "^5.0.1", @@ -44,7 +44,7 @@ "fork-ts-checker-webpack-plugin": "^6.0.1", "html-loader": "^1.3.2", "html-webpack-plugin": "^4.5.0", - "mini-css-extract-plugin": "^1.3.0", + "mini-css-extract-plugin": "^1.3.1", "node-sass": "5.0.0", "sass-loader": "^10.0.5", "style-loader": "^2.0.0", diff --git a/examples/webpack-demo-vanilla-bundle/src/examples/example12.ts b/examples/webpack-demo-vanilla-bundle/src/examples/example12.ts index a826c326b..035ddfcd7 100644 --- a/examples/webpack-demo-vanilla-bundle/src/examples/example12.ts +++ b/examples/webpack-demo-vanilla-bundle/src/examples/example12.ts @@ -124,7 +124,7 @@ export class Example12 { initializeGrid() { this.columnDefinitions = [ { - id: 'title', name: 'Title', field: 'title', sortable: true, type: FieldType.string, + id: 'title', name: 'Title', field: 'title', sortable: true, type: FieldType.string, minWidth: 100, filterable: true, columnGroup: 'Common Factor', filter: { model: Filters.compoundInputText }, formatter: Formatters.multiple, params: { formatters: [Formatters.uppercase, Formatters.bold] }, @@ -142,13 +142,13 @@ export class Example12 { editor: { model: Editors.float, massUpdate: true, decimal: 2, valueStep: 1, minValue: 0, maxValue: 10000, alwaysSaveOnEnterKey: true, required: true }, }, { - id: 'cost', name: 'Cost', field: 'cost', width: 90, + id: 'cost', name: 'Cost', field: 'cost', width: 90, minWidth: 100, sortable: true, filterable: true, type: FieldType.number, columnGroup: 'Analysis', filter: { model: Filters.compoundInputNumber }, formatter: Formatters.dollar, }, { - id: 'percentComplete', name: '% Complete', field: 'percentComplete', + id: 'percentComplete', name: '% Complete', field: 'percentComplete', minWidth: 100, type: FieldType.number, sortable: true, filterable: true, columnGroup: 'Analysis', filter: { model: Filters.compoundSlider, operator: '>=' }, @@ -170,7 +170,7 @@ export class Example12 { }, }, { - id: 'start', name: 'Start', field: 'start', sortable: true, + id: 'start', name: 'Start', field: 'start', sortable: true, minWidth: 100, formatter: Formatters.dateUs, columnGroup: 'Period', type: FieldType.dateIso, outputType: FieldType.dateUs, filterable: true, filter: { model: Filters.compoundDate }, @@ -190,7 +190,7 @@ export class Example12 { // editor: { model: Editors.singleSelect, collection: [{ value: true, label: 'Yes' }, { value: false, label: 'No' }], }, }, { - id: 'finish', name: 'Finish', field: 'finish', sortable: true, + id: 'finish', name: 'Finish', field: 'finish', sortable: true, minWidth: 100, formatter: Formatters.dateUs, columnGroup: 'Period', type: FieldType.dateIso, outputType: FieldType.dateUs, filterable: true, filter: { model: Filters.compoundDate }, @@ -278,7 +278,7 @@ export class Example12 { } }, { - id: 'action', name: 'Action', field: 'action', width: 70, maxWidth: 70, + id: 'action', name: 'Action', field: 'action', width: 70, minWidth: 70, maxWidth: 70, excludeFromExport: true, formatter: () => `
`, cellMenu: { diff --git a/packages/common/package.json b/packages/common/package.json index 2517188d6..e44938fe0 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -80,11 +80,11 @@ "autoprefixer": "^10.0.1", "copyfiles": "^2.4.0", "cross-env": "^7.0.2", - "mini-css-extract-plugin": "^1.3.0", + "mini-css-extract-plugin": "^1.3.1", "node-sass": "5.0.0", "nodemon": "^2.0.6", "npm-run-all": "^4.1.5", - "postcss": "^8.1.6", + "postcss": "^8.1.7", "postcss-cli": "^8.2.0", "rimraf": "^3.0.2" }, diff --git a/packages/common/src/global-grid-options.ts b/packages/common/src/global-grid-options.ts index 71f1bf5ec..7954eb019 100644 --- a/packages/common/src/global-grid-options.ts +++ b/packages/common/src/global-grid-options.ts @@ -96,13 +96,17 @@ export const GlobalGridOptions: GridOption = { defaultSlickgridEventPrefix: '', editable: false, editorTypingDebounce: 450, - enableEmptyDataWarningMessage: false, + enableEmptyDataWarningMessage: true, emptyDataWarning: { - class: 'slick-empty-data-warning', + className: 'slick-empty-data-warning', message: 'No data to display.', messageKey: 'EMPTY_DATA_WARNING_MESSAGE', - marginTop: 100, - marginLeft: 10 + hideFrozenLeftWarning: false, + hideFrozenRightWarning: false, + leftViewportMarginLeft: '40%', + rightViewportMarginLeft: '40%', + frozenLeftViewportMarginLeft: '0px', + frozenRightViewportMarginLeft: '40%', }, enableAutoResize: true, enableAutoSizeColumns: true, diff --git a/packages/common/src/interfaces/emptyWarning.interface.ts b/packages/common/src/interfaces/emptyWarning.interface.ts index caff74e55..a318fd845 100644 --- a/packages/common/src/interfaces/emptyWarning.interface.ts +++ b/packages/common/src/interfaces/emptyWarning.interface.ts @@ -6,11 +6,23 @@ export interface EmptyWarning { messageKey?: string; /** DOM Element class name, defaults to "empty-data-warning" */ - class?: string; + className?: string; - /** Top margin position, number in pixel, of where the warning message will be displayed, default calculation is (header title row + filter row + 5px) */ - marginTop?: number; + /** Defaults to False, when using a frozen/pinned grid, do we want to hide the warning on the left side? */ + hideFrozenLeftWarning?: boolean; - /** Left margin position, number in pixel, of where the warning message will be displayed, defaults to 10px */ - marginLeft?: number; + /** Defaults to False, when using a frozen/pinned grid, do we want to hide the warning on the right side? */ + hideFrozenRightWarning?: boolean; + + /** Defaults to "40%", what is the margin-left CSS style to use when we have a regular grid (non-frozen grid)? */ + leftViewportMarginLeft?: number | string; + + /** Defaults to "10px", what is the margin-left CSS style to use when the grid is a frozen/pinned grid? */ + frozenLeftViewportMarginLeft?: number | string; + + /** Defaults to "40%", what is the margin-left CSS style to use when we have a regular grid (non-frozen grid)? */ + rightViewportMarginLeft?: number | string; + + /** Defaults to "10px", what is the margin-left CSS style to use when the grid is a frozen/pinned grid? */ + frozenRightViewportMarginLeft?: number | string; } diff --git a/packages/common/src/services/groupingAndColspan.service.ts b/packages/common/src/services/groupingAndColspan.service.ts index c56341afd..293bda520 100644 --- a/packages/common/src/services/groupingAndColspan.service.ts +++ b/packages/common/src/services/groupingAndColspan.service.ts @@ -135,9 +135,8 @@ export class GroupingAndColspanService { let header; let lastColumnGroup = ''; let widthTotal = 0; - const frozenColumn = this._gridOptions?.frozenColumn ?? -1; const frozenHeaderWidthCalcDifferential = this._gridOptions?.frozenHeaderWidthCalcDifferential ?? 0; - const isFrozenGrid = frozenColumn >= 0; + const isFrozenGrid = (this._gridOptions?.frozenRow !== undefined && this._gridOptions.frozenRow >= 0); for (let i = start; i < end; i++) { colDef = this._columnDefinitions[i]; diff --git a/packages/common/src/styles/_variables.scss b/packages/common/src/styles/_variables.scss index f1ba8ecff..a7d651e1c 100644 --- a/packages/common/src/styles/_variables.scss +++ b/packages/common/src/styles/_variables.scss @@ -868,4 +868,6 @@ $empty-data-warning-font-family: $font-family !default; $empty-data-warning-font-size: calc(#{$font-size-base} + 2px) !default; $empty-data-warning-font-style: italic !default; $empty-data-warning-line-height: 18px !default; -$empty-data-warning-z-index: 9999 !default; +$empty-data-warning-margin: 0px !default; +$empty-data-warning-padding: 8px !default; +$empty-data-warning-z-index: 10 !default; diff --git a/packages/common/src/styles/slick-component.scss b/packages/common/src/styles/slick-component.scss index c72adba4f..8a9f2be4f 100644 --- a/packages/common/src/styles/slick-component.scss +++ b/packages/common/src/styles/slick-component.scss @@ -45,13 +45,15 @@ // ---------------------------------------------- .slick-empty-data-warning { - position: absolute; + position: relative; color: $empty-data-warning-color; font-family: $empty-data-warning-font-family; font-size: $empty-data-warning-font-size; font-style: $empty-data-warning-font-style; line-height: $empty-data-warning-line-height; z-index: $empty-data-warning-z-index; + margin: $empty-data-warning-margin; + padding: $empty-data-warning-padding; } diff --git a/packages/vanilla-bundle/dist-grid-bundle-zip/slickgrid-vanilla-bundle.zip b/packages/vanilla-bundle/dist-grid-bundle-zip/slickgrid-vanilla-bundle.zip index 2094b2bf6..3b9cd7931 100644 Binary files a/packages/vanilla-bundle/dist-grid-bundle-zip/slickgrid-vanilla-bundle.zip and b/packages/vanilla-bundle/dist-grid-bundle-zip/slickgrid-vanilla-bundle.zip differ diff --git a/packages/vanilla-bundle/package.json b/packages/vanilla-bundle/package.json index 11f7adb73..3463d1c74 100644 --- a/packages/vanilla-bundle/package.json +++ b/packages/vanilla-bundle/package.json @@ -52,7 +52,7 @@ "isomorphic-fetch": "^3.0.0" }, "devDependencies": { - "@types/webpack": "^4.41.24", + "@types/webpack": "^4.41.25", "archiver": "^5.0.2", "cross-env": "^7.0.2", "dts-bundle-webpack": "^1.0.2", diff --git a/packages/vanilla-bundle/src/components/__tests__/slick-empty-warning.spec.ts b/packages/vanilla-bundle/src/components/__tests__/slick-empty-warning.spec.ts index fde11be89..b6bf3b01e 100644 --- a/packages/vanilla-bundle/src/components/__tests__/slick-empty-warning.spec.ts +++ b/packages/vanilla-bundle/src/components/__tests__/slick-empty-warning.spec.ts @@ -1,4 +1,4 @@ -import { GridOption, SlickGrid } from '@slickgrid-universal/common'; +import { EmptyWarning, GridOption, SlickGrid } from '@slickgrid-universal/common'; import { SlickEmptyWarningComponent } from '../slick-empty-warning.component'; import { TranslateServiceStub } from '../../../../../test/translateServiceStub'; @@ -6,7 +6,7 @@ const GRID_UID = 'slickgrid_123456'; const mockGridOptions = { enableTranslate: false, - showCustomFooter: true, + frozenRow: 0, } as GridOption; const gridStub = { @@ -23,7 +23,13 @@ describe('Slick-Empty-Warning Component', () => { beforeEach(() => { div = document.createElement('div'); + const canvasLeft = document.createElement('div'); + const canvasRight = document.createElement('div'); + canvasLeft.className = 'grid-canvas grid-canvas-left'; + canvasRight.className = 'grid-canvas grid-canvas-right'; div.className = GRID_UID; + div.appendChild(canvasLeft); + div.appendChild(canvasRight); document.body.appendChild(div); translateService = new TranslateServiceStub(); @@ -44,43 +50,183 @@ describe('Slick-Empty-Warning Component', () => { component = new SlickEmptyWarningComponent(gridStub); component.showEmptyDataMessage(false); - const componentElm = document.querySelector('div.slickgrid_123456.slick-empty-data-warning') as HTMLSelectElement; + const componentLeftElm = document.querySelector('div.slickgrid_123456 .grid-canvas.grid-canvas-left .slick-empty-data-warning') as HTMLSelectElement; + const componentRightElm = document.querySelector('div.slickgrid_123456 .grid-canvas.grid-canvas-right .slick-empty-data-warning') as HTMLSelectElement; expect(component).toBeTruthy(); expect(component.constructor).toBeDefined(); - expect(componentElm).toBeFalsy(); + expect(componentLeftElm).toBeFalsy(); + expect(componentRightElm).toBeFalsy(); }); - it('should expect the Slick-Empty-Warning to be created and rendered and passing true as 2nd argument', () => { + it('should expect the Slick-Empty-Warning to be created in both viewports and rendered and passing true as 2nd argument', () => { component = new SlickEmptyWarningComponent(gridStub); component.showEmptyDataMessage(true); - const componentElm = document.querySelector('div.slickgrid_123456.slick-empty-data-warning') as HTMLSelectElement; + const componentLeftElm = document.querySelector('div.slickgrid_123456 .grid-canvas.grid-canvas-left .slick-empty-data-warning') as HTMLSelectElement; + const componentRightElm = document.querySelector('div.slickgrid_123456 .grid-canvas.grid-canvas-right .slick-empty-data-warning') as HTMLSelectElement; + + expect(component).toBeTruthy(); + expect(component.constructor).toBeDefined(); + expect(componentLeftElm).toBeTruthy(); + expect(componentLeftElm.style.display).toBe('block'); + expect(componentRightElm.style.display).toBe('block'); + expect(componentLeftElm.textContent).toBe('No data to display.'); + expect(componentRightElm.textContent).toBe('No data to display.'); + }); + + it('should expect the Slick-Empty-Warning to be created in both viewports when using Frozen Grid but NOT displayed on left when "hideFrozenLeftWarning" flag is enabled', () => { + mockGridOptions.frozenRow = 2; + (mockGridOptions.emptyDataWarning as EmptyWarning).hideFrozenLeftWarning = true; + (mockGridOptions.emptyDataWarning as EmptyWarning).hideFrozenRightWarning = false; + component = new SlickEmptyWarningComponent(gridStub); + component.showEmptyDataMessage(true); + + const componentLeftElm = document.querySelector('div.slickgrid_123456 .grid-canvas.grid-canvas-left .slick-empty-data-warning') as HTMLSelectElement; + const componentRightElm = document.querySelector('div.slickgrid_123456 .grid-canvas.grid-canvas-right .slick-empty-data-warning') as HTMLSelectElement; + + expect(component).toBeTruthy(); + expect(component.constructor).toBeDefined(); + expect(componentLeftElm).toBeTruthy(); + expect(componentLeftElm.style.display).toBe('none'); + expect(componentRightElm.style.display).toBe('block'); + expect(componentLeftElm.style.marginLeft).toBe('10px'); + expect(componentRightElm.style.marginLeft).toBe('10px'); + expect(componentLeftElm.textContent).toBe('No data to display.'); + expect(componentRightElm.textContent).toBe('No data to display.'); + }); + + it('should expect the Slick-Empty-Warning to be created and use different left margin when "leftViewportMarginLeft" is set', () => { + mockGridOptions.frozenRow = -1; + (mockGridOptions.emptyDataWarning as EmptyWarning).leftViewportMarginLeft = '40%'; + component = new SlickEmptyWarningComponent(gridStub); + component.showEmptyDataMessage(true); + + const componentLeftElm = document.querySelector('div.slickgrid_123456 .grid-canvas.grid-canvas-left .slick-empty-data-warning') as HTMLSelectElement; + const componentRightElm = document.querySelector('div.slickgrid_123456 .grid-canvas.grid-canvas-right .slick-empty-data-warning') as HTMLSelectElement; + + expect(component).toBeTruthy(); + expect(component.constructor).toBeDefined(); + expect(componentLeftElm).toBeTruthy(); + expect(componentLeftElm.style.display).toBe('block'); + expect(componentRightElm.style.display).toBe('block'); + expect(componentLeftElm.style.marginLeft).toBe('40%'); + expect(componentRightElm.style.marginLeft).toBe('0px'); + expect(componentLeftElm.textContent).toBe('No data to display.'); + expect(componentRightElm.textContent).toBe('No data to display.'); + }); + + it('should expect the Slick-Empty-Warning to be created and use different left margin when "rightViewportMarginLeft" is set', () => { + mockGridOptions.frozenRow = -1; + (mockGridOptions.emptyDataWarning as EmptyWarning).rightViewportMarginLeft = '40%'; + jest.spyOn(gridStub, 'getOptions').mockReturnValue(mockGridOptions); + + component = new SlickEmptyWarningComponent(gridStub); + component.showEmptyDataMessage(true); + + const componentLeftElm = document.querySelector('div.slickgrid_123456 .grid-canvas.grid-canvas-left .slick-empty-data-warning') as HTMLSelectElement; + const componentRightElm = document.querySelector('div.slickgrid_123456 .grid-canvas.grid-canvas-right .slick-empty-data-warning') as HTMLSelectElement; + + expect(component).toBeTruthy(); + expect(component.constructor).toBeDefined(); + expect(componentLeftElm).toBeTruthy(); + expect(componentLeftElm.style.display).toBe('block'); + expect(componentRightElm.style.display).toBe('block'); + expect(componentLeftElm.style.marginLeft).toBe('0px'); + expect(componentRightElm.style.marginLeft).toBe('40%'); + expect(componentLeftElm.textContent).toBe('No data to display.'); + expect(componentRightElm.textContent).toBe('No data to display.'); + }); + + it('should expect the Slick-Empty-Warning to be created in both viewports and use different left margin when "frozenLeftViewportMarginLeft" is set', () => { + mockGridOptions.frozenRow = 2; + (mockGridOptions.emptyDataWarning as EmptyWarning).leftViewportMarginLeft = '40%'; + (mockGridOptions.emptyDataWarning as EmptyWarning).frozenLeftViewportMarginLeft = '15px'; + component = new SlickEmptyWarningComponent(gridStub); + component.showEmptyDataMessage(true); + + const componentLeftElm = document.querySelector('div.slickgrid_123456 .grid-canvas.grid-canvas-left .slick-empty-data-warning') as HTMLSelectElement; + const componentRightElm = document.querySelector('div.slickgrid_123456 .grid-canvas.grid-canvas-right .slick-empty-data-warning') as HTMLSelectElement; + + expect(component).toBeTruthy(); + expect(component.constructor).toBeDefined(); + expect(componentLeftElm).toBeTruthy(); + expect(componentLeftElm.style.display).toBe('block'); + expect(componentRightElm.style.display).toBe('block'); + expect(componentLeftElm.style.marginLeft).toBe('15px'); + expect(componentRightElm.style.marginLeft).toBe('10px'); + expect(componentLeftElm.textContent).toBe('No data to display.'); + expect(componentRightElm.textContent).toBe('No data to display.'); + }); + + it('should expect the Slick-Empty-Warning to be created in both viewports and use different left margin when "frozenRightViewportMarginLeft" is set', () => { + mockGridOptions.frozenRow = 2; + (mockGridOptions.emptyDataWarning as EmptyWarning).leftViewportMarginLeft = '40%'; + (mockGridOptions.emptyDataWarning as EmptyWarning).frozenRightViewportMarginLeft = '22px'; + component = new SlickEmptyWarningComponent(gridStub); + component.showEmptyDataMessage(true); + + const componentLeftElm = document.querySelector('div.slickgrid_123456 .grid-canvas.grid-canvas-left .slick-empty-data-warning') as HTMLSelectElement; + const componentRightElm = document.querySelector('div.slickgrid_123456 .grid-canvas.grid-canvas-right .slick-empty-data-warning') as HTMLSelectElement; + + expect(component).toBeTruthy(); + expect(component.constructor).toBeDefined(); + expect(componentLeftElm).toBeTruthy(); + expect(componentLeftElm.style.display).toBe('block'); + expect(componentRightElm.style.display).toBe('block'); + expect(componentLeftElm.style.marginLeft).toBe('10px'); + expect(componentRightElm.style.marginLeft).toBe('22px'); + expect(componentLeftElm.textContent).toBe('No data to display.'); + expect(componentRightElm.textContent).toBe('No data to display.'); + }); + + it('should expect the Slick-Empty-Warning to be created in both viewports when using Frozen Grid but NOT displayed on right when "hideFrozenRightWarning" flag is enabled', () => { + mockGridOptions.frozenRow = 2; + (mockGridOptions.emptyDataWarning as EmptyWarning).hideFrozenLeftWarning = false; + (mockGridOptions.emptyDataWarning as EmptyWarning).hideFrozenRightWarning = true; + component = new SlickEmptyWarningComponent(gridStub); + component.showEmptyDataMessage(true); + + const componentLeftElm = document.querySelector('div.slickgrid_123456 .grid-canvas.grid-canvas-left .slick-empty-data-warning') as HTMLSelectElement; + const componentRightElm = document.querySelector('div.slickgrid_123456 .grid-canvas.grid-canvas-right .slick-empty-data-warning') as HTMLSelectElement; + + expect(component).toBeTruthy(); + expect(component.constructor).toBeDefined(); + expect(componentLeftElm).toBeTruthy(); + expect(componentLeftElm.style.display).toBe('block'); + expect(componentRightElm.style.display).toBe('none'); + expect(componentLeftElm.textContent).toBe('No data to display.'); + expect(componentRightElm.textContent).toBe('No data to display.'); + }); + + it('should expect the Slick-Empty-Warning to change some options and display a different message when provided as an option', () => { + const mockOptions = { message: ' No Record found.', className: 'custom-class', marginTop: 22, marginLeft: 11 }; + component = new SlickEmptyWarningComponent(gridStub); + component.showEmptyDataMessage(true, mockOptions); + + const componentElm = document.querySelector('div.slickgrid_123456 .grid-canvas .custom-class') as HTMLSelectElement; expect(component).toBeTruthy(); expect(component.constructor).toBeDefined(); expect(componentElm).toBeTruthy(); expect(componentElm.style.display).toBe('block'); - expect(componentElm.textContent).toBe('No data to display.'); + expect(componentElm.classList.contains('custom-class')).toBeTruthy(); + expect(componentElm.innerHTML).toBe(' No Record found.'); }); - it('should expect the Slick-Empty-Warning to change some options and display a different message when provided as an option', () => { - const mockGridPosition = { top: 500, left: 42, bottom: 34, right: 15, height: 800, width: 450, visible: true }; - const mockOptions = { message: 'No Record found.', class: 'custom-class', marginTop: 22, marginLeft: 11 }; - jest.spyOn(gridStub, 'getGridPosition').mockReturnValue(mockGridPosition); + it('should expect the Slick-Empty-Warning provide html text and expect script to be sanitized out of the final html', () => { + const mockOptions = { message: ` No Record found.`, className: 'custom-class', marginTop: 22, marginLeft: 11 }; component = new SlickEmptyWarningComponent(gridStub); component.showEmptyDataMessage(true, mockOptions); - const componentElm = document.querySelector('div.slickgrid_123456.custom-class') as HTMLSelectElement; + const componentElm = document.querySelector('div.slickgrid_123456 .grid-canvas .custom-class') as HTMLSelectElement; expect(component).toBeTruthy(); expect(component.constructor).toBeDefined(); expect(componentElm).toBeTruthy(); expect(componentElm.style.display).toBe('block'); - expect(componentElm.style.top).toBe(`${mockGridPosition.top + 22}px`); // 500 + 22 - expect(componentElm.style.left).toBe(`${mockGridPosition.left + 11}px`); // 42 + 11 expect(componentElm.classList.contains('custom-class')).toBeTruthy(); - expect(componentElm.textContent).toBe('No Record found.'); + expect(componentElm.innerHTML).toBe(' No Record found.'); }); it('should expect the Slick-Empty-Warning message to be translated to French when providing a Translater Service and "messageKey" property', () => { @@ -89,7 +235,7 @@ describe('Slick-Empty-Warning Component', () => { component = new SlickEmptyWarningComponent(gridStub, translateService); component.showEmptyDataMessage(true); - const componentElm = document.querySelector('div.slickgrid_123456.slick-empty-data-warning') as HTMLSelectElement; + const componentElm = document.querySelector('div.slickgrid_123456 .grid-canvas .slick-empty-data-warning') as HTMLSelectElement; expect(component).toBeTruthy(); expect(component.constructor).toBeDefined(); diff --git a/packages/vanilla-bundle/src/components/slick-empty-warning.component.ts b/packages/vanilla-bundle/src/components/slick-empty-warning.component.ts index fc0a98ac6..f3d06f687 100644 --- a/packages/vanilla-bundle/src/components/slick-empty-warning.component.ts +++ b/packages/vanilla-bundle/src/components/slick-empty-warning.component.ts @@ -1,7 +1,18 @@ -import { EmptyWarning, getHtmlElementOffset, GridOption, sanitizeTextByAvailableSanitizer, SlickGrid, TranslaterService } from '@slickgrid-universal/common'; +import { + EmptyWarning, + GridOption, + sanitizeTextByAvailableSanitizer, + SlickGrid, + SlickNamespace, + TranslaterService +} from '@slickgrid-universal/common'; + +// using external non-typed js libraries +declare const Slick: SlickNamespace; export class SlickEmptyWarningComponent { - private _warningElement: HTMLDivElement | null; + private _warningLeftElement: HTMLDivElement | null; + private _warningRightElement: HTMLDivElement | null; /** Getter for the Grid Options pulled through the Grid Object */ get gridOptions(): GridOption { @@ -11,8 +22,10 @@ export class SlickEmptyWarningComponent { constructor(private grid: SlickGrid, private translaterService?: TranslaterService) { } dispose() { - this._warningElement?.remove(); - this._warningElement = null; + this._warningLeftElement?.remove(); + this._warningRightElement?.remove(); + this._warningLeftElement = null; + this._warningRightElement = null; } /** @@ -25,50 +38,72 @@ export class SlickEmptyWarningComponent { const gridUid = this.grid.getUID(); const defaultMessage = 'No data to display.'; const mergedOptions: EmptyWarning = { message: defaultMessage, ...this.gridOptions.emptyDataWarning, ...options }; - const emptyDataClassName = mergedOptions?.class ?? 'slick-empty-data-warning'; - const finalClassNames = [gridUid, emptyDataClassName]; - this._warningElement = document.querySelector(`.${finalClassNames.join('.')}`); - - // calculate margins - const gridHeaderFilterRowHeight = this.gridOptions?.headerRowHeight ?? 30; // filter row height - const headerRowCount = 2; // header title row is calculated by SASS and defined as (17px * headerRowCount + paddingTopBottom) - const headerRowPaddingTopBottom = 10; // 5px (2x for both top/bottom), this is different in each SASS Theme - const headerRowHeight = 17 * headerRowCount + headerRowPaddingTopBottom; + const emptyDataClassName = mergedOptions?.className ?? 'slick-empty-data-warning'; + this._warningLeftElement = document.querySelector(`.${emptyDataClassName}`); + const gridCanvasLeftElm = document.querySelector(`.${gridUid} .grid-canvas.grid-canvas-left`); + const gridCanvasRightElm = document.querySelector(`.${gridUid} .grid-canvas.grid-canvas-right`); + const leftElementMarginLeft = mergedOptions.leftViewportMarginLeft ?? 0; + const rightElementMarginLeft = mergedOptions.rightViewportMarginLeft ?? 0; + const leftElementFrozenMarginLeft = mergedOptions.frozenLeftViewportMarginLeft ?? 10; + const rightElementFrozenMarginLeft = mergedOptions.frozenRightViewportMarginLeft ?? 10; + const isFrozenGrid = (this.gridOptions?.frozenRow !== undefined && this.gridOptions.frozenRow >= 0); + const leftViewportMarginLeft = typeof leftElementMarginLeft === 'string' ? leftElementMarginLeft : `${leftElementMarginLeft}px`; + const rightViewportMarginLeft = typeof rightElementMarginLeft === 'string' ? rightElementMarginLeft : `${rightElementMarginLeft}px`; + + if (!this._warningLeftElement && !isShowing) { + return false; + } + + // warning message could come from a translation key or by the warning options let warningMessage = mergedOptions.message; if (this.gridOptions.enableTranslate && this.translaterService && mergedOptions?.messageKey) { warningMessage = this.translaterService.translate(mergedOptions.messageKey); } - const preHeaderRowHeight = this.gridOptions.showPreHeaderPanel && this.gridOptions.preHeaderPanelHeight || 0; - const marginTop = (mergedOptions.marginTop ?? (headerRowHeight + gridHeaderFilterRowHeight + 5)) + preHeaderRowHeight; - const marginLeft = mergedOptions.marginLeft ?? 10; - if (!this._warningElement && !isShowing) { - return isShowing; - } - - if (!this._warningElement) { + if (!this._warningLeftElement && gridCanvasLeftElm && gridCanvasRightElm) { const sanitizedOptions = this.gridOptions && this.gridOptions.sanitizeHtmlOptions || {}; const sanitizedText = sanitizeTextByAvailableSanitizer(this.gridOptions, warningMessage, sanitizedOptions); - this._warningElement = document.createElement('div'); - this._warningElement.className = finalClassNames.join(' '); - this._warningElement.innerHTML = sanitizedText; - document.body.appendChild(this._warningElement); + this._warningLeftElement = document.createElement('div'); + this._warningLeftElement.classList.add(emptyDataClassName); + this._warningLeftElement.classList.add('left'); + this._warningLeftElement.innerHTML = sanitizedText; + + // clone the warning element and add the "right" class to it so we can distinguish + this._warningRightElement = this._warningLeftElement.cloneNode(true) as HTMLDivElement; + this._warningRightElement.classList.add('right'); + + // append both warning elements to both left/right canvas + gridCanvasRightElm.appendChild(this._warningRightElement); + gridCanvasLeftElm.appendChild(this._warningLeftElement); } // if we did find the Slick-Empty-Warning element then we'll display/hide at the grid position with some margin offsets (we need to position under the headerRow and filterRow) - if (this._warningElement) { - if (isShowing) { - const gridPosition = this.grid.getGridPosition(); - const gridOffset = getHtmlElementOffset(document.querySelector(`.${this.grid.getUID()}`) as HTMLDivElement); - - // SF seems to have problem with getGridPosition() so we can use getHtmlElementOffset when that happens - const gridPosTop = !isNaN(gridPosition.top) ? gridPosition.top : (gridOffset?.top ?? 0); - const gridPosLeft = !isNaN(gridPosition.left) ? gridPosition.left : (gridOffset?.left ?? 0); - this._warningElement.style.top = `${gridPosTop + marginTop}px`; - this._warningElement.style.left = `${gridPosLeft + marginLeft}px`; + // when using a frozen/pinned grid, we also have extra options to hide left/right message + if (this._warningLeftElement) { + // display/hide right/left messages + let leftDisplay = isShowing ? 'block' : 'none'; + if (isFrozenGrid && isShowing) { + leftDisplay = (mergedOptions.hideFrozenLeftWarning) ? 'none' : 'block'; + } + this._warningLeftElement.style.display = leftDisplay; + + // use correct left margin (defaults to 40% on regular grid or 10px on frozen grid) + const leftFrozenMarginLeft = typeof leftElementFrozenMarginLeft === 'string' ? leftElementFrozenMarginLeft : `${leftElementFrozenMarginLeft}px`; + this._warningLeftElement.style.marginLeft = isFrozenGrid ? leftFrozenMarginLeft : leftViewportMarginLeft; + } + + if (this._warningRightElement) { + // use correct left margin (defaults to 40% on regular grid or 10px on frozen grid) + let rightDisplay = isShowing ? 'block' : 'none'; + if (isFrozenGrid && isShowing) { + rightDisplay = (mergedOptions.hideFrozenRightWarning) ? 'none' : 'block'; } - this._warningElement.style.display = isShowing ? 'block' : 'none'; + this._warningRightElement.style.display = rightDisplay; + + // use correct left margin (defaults to 40% on regular grid or 10px on frozen grid) + const rightFrozenMarginLeft = typeof rightElementFrozenMarginLeft === 'string' ? rightElementFrozenMarginLeft : `${rightElementFrozenMarginLeft}px`; + this._warningRightElement.style.marginLeft = isFrozenGrid ? rightFrozenMarginLeft : rightViewportMarginLeft; } return isShowing; diff --git a/packages/vanilla-bundle/src/components/slick-vanilla-grid-bundle.ts b/packages/vanilla-bundle/src/components/slick-vanilla-grid-bundle.ts index f52d07cab..8b61ddf47 100644 --- a/packages/vanilla-bundle/src/components/slick-vanilla-grid-bundle.ts +++ b/packages/vanilla-bundle/src/components/slick-vanilla-grid-bundle.ts @@ -423,7 +423,7 @@ export class SlickVanillaGridBundle { } initialization(gridContainerElm: HTMLElement, eventHandler: SlickEventHandler) { - if (this.gridOptions?.frozenRow !== undefined && this.gridOptions?.frozenRow >= 0) { + if (this.gridOptions?.frozenRow !== undefined && this.gridOptions.frozenRow >= 0) { this.loadJqueryMousewheelDynamically(); } diff --git a/packages/vanilla-bundle/src/salesforce-global-grid-options.ts b/packages/vanilla-bundle/src/salesforce-global-grid-options.ts index 476880b47..18ece24e7 100644 --- a/packages/vanilla-bundle/src/salesforce-global-grid-options.ts +++ b/packages/vanilla-bundle/src/salesforce-global-grid-options.ts @@ -12,12 +12,8 @@ export const SalesforceGlobalGridOptions = { }, datasetIdPropertyName: 'Id', defaultFilterPlaceholder: '', - enableEmptyDataWarningMessage: false, emptyDataWarning: { - class: 'slick-empty-data-warning', message: ` No data to display.`, - marginTop: 90, - marginLeft: 10 }, enableAutoTooltip: true, enableDeepCopyDatasetOnPageLoad: true,