Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"eslint.validate": [
"javascript",
"typescript"
],
"eslint.validate": ["javascript", "typescript"],
"typescript.experimental.useTsgo": true,
"typescript.format.semicolons": "insert",
"vitest.rootConfig": "./test/vitest.config.mts",
"typescript.tsdk": "node_modules\\typescript\\lib",
"prettier.prettierPath": "node_modules/prettier/index.cjs",
"prettier.configPath": ".prettierrc"
}
}
11 changes: 8 additions & 3 deletions packages/common/src/core/slickCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* @module Core
* @namespace Slick
*/
import { setStyles } from '@slickgrid-universal/utils';
import type { MergeTypes } from '../enums/index.js';
import type { DragRange, EditController } from '../interfaces/index.js';
import type { SlickGrid } from './slickGrid.js';
Expand Down Expand Up @@ -734,11 +735,15 @@ export class Utils {
Utils.setStyleSize(el, 'width', value);
}

public static setStyleSize(el: HTMLElement, style: string, val?: number | string | Function): void {
public static setStyleSize<P extends Pick<CSSStyleDeclaration, 'height' | 'width' | 'left' | 'right' | 'top' | 'bottom'>>(
el: HTMLElement,
style: keyof P,
val?: number | string | Function
): void {
if (typeof val === 'function') {
val = val();
val = val() as number | string;
}
el.style.setProperty(style, typeof val === 'string' ? val : `${val}px`);
setStyles(el, { [style]: typeof val === 'string' ? val : `${val}px` } as P);
}

public static isHidden(el: HTMLElement): boolean {
Expand Down
22 changes: 10 additions & 12 deletions packages/common/src/core/slickGrid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
isDefinedNumber,
isPrimitiveOrHTML,
queueMicrotaskPolyfill,
type CSSStyleDeclarationWritable,
} from '@slickgrid-universal/utils';
import type { SortableEvent, Options as SortableOptions } from 'sortablejs';
import Sortable from 'sortablejs/modular/sortable.core.esm.js';
Expand Down Expand Up @@ -1102,23 +1103,20 @@ export class SlickGrid<TData = any, C extends Column<TData> = Column<TData>, O e
});
}

restoreCssFromHiddenInit(): void {
// Finish handling display:none on container or container parents
// - Put values back the way they were
if (this._hiddenParents && this.oldProps) {
this._hiddenParents.forEach((el: HTMLElement, index: number) => {
const old: CSSStyleDeclaration = this.oldProps[index] as CSSStyleDeclaration;

restoreCssFromHiddenInit<P extends Partial<CSSStyleDeclarationWritable>>(): void {
// finish handle display:none on container or container parents
// - put values back the way they were
let i = 0;
if (this._hiddenParents) {
this._hiddenParents.forEach((el) => {
const old = this.oldProps[i++];
Object.keys(this.cssShow).forEach((name) => {
if (this.cssShow) {
const value = old[name as keyof CSSStyleDeclaration];
el.style.setProperty(name, value != null ? String(value) : '');
(el.style as unknown as P)[name as keyof P] = (old as any)[name];
}
});
});

// Clear the hidden parents array
this._hiddenParents.length = 0; // Correct way to clear the array
this._hiddenParents.length = 0;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ describe('CellRangeDecorator Plugin', () => {
expect(plugin.addonElement!.style.left).toEqual('');
expect(plugin.addonElement!.style.height).toEqual('');
expect(plugin.addonElement!.style.width).toEqual('');
expect(plugin.addonElement?.style.border).toBe('2px dashed red');
expect(plugin.addonElement?.style.zIndex).toBe('9999');
});

it('should Show range when called and calculate new position when getCellNodeBox returns a cell position', () => {
Expand All @@ -70,12 +72,39 @@ describe('CellRangeDecorator Plugin', () => {
expect(plugin.addonElement!.style.left).toEqual('31px'); // 26 + 5px
expect(plugin.addonElement!.style.height).toEqual('20px'); // 12 - 25 + 33px
expect(plugin.addonElement!.style.width).toEqual('13px'); // 27 - 26 + 12px
expect(plugin.addonElement?.style.border).toBe('2px dashed red');
expect(plugin.addonElement?.style.zIndex).toBe('9999');
});

it('should be able to set selection CSS', () => {
const setSelectionCss = { border: '2px solid green', zIndex: '9998' } as CSSStyleDeclaration;
plugin = new SlickCellRangeDecorator(gridStub, { offset: { top: 20, left: 5, width: 12, height: 33 } });
plugin.setSelectionCss({ border: '2px solid green', zIndex: '9998' } as CSSStyleDeclaration);
plugin.setSelectionCss(setSelectionCss);

expect(plugin.getSelectionCss()).toEqual({ border: '2px solid green', zIndex: '9998' } as CSSStyleDeclaration);
});

it('should be able to define different selection CSS and then override them with setSelectionCss()', () => {
const ctorSelectionCss = { border: '3px solid purple', zIndex: '9997' };
const setSelectionCss = { border: '2px solid green', zIndex: '9998' } as CSSStyleDeclaration;
plugin = new SlickCellRangeDecorator(gridStub, {
offset: { top: 20, left: 5, width: 12, height: 33 },
selectionCss: ctorSelectionCss,
});
plugin.setSelectionCss(setSelectionCss);

expect(plugin.getSelectionCss()).toEqual(setSelectionCss);
});

it('should be able to define different selection CSS', () => {
const ctorSelectionCss = { border: '3px solid purple', zIndex: '9997' };
plugin = new SlickCellRangeDecorator(gridStub, {
offset: { top: 20, left: 5, width: 12, height: 33 },
selectionCss: ctorSelectionCss,
});
plugin.show({ fromCell: 1, fromRow: 2, toCell: 3, toRow: 4 } as SlickRange);

expect(plugin.addonElement?.style.border).toBe(ctorSelectionCss.border);
expect(plugin.addonElement?.style.zIndex).toBe(ctorSelectionCss.zIndex);
});
});
19 changes: 7 additions & 12 deletions packages/common/src/extensions/slickCellRangeDecorator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createDomElement, deepMerge } from '@slickgrid-universal/utils';
import { createDomElement, deepMerge, setStyles } from '@slickgrid-universal/utils';
import type { SlickGrid, SlickRange } from '../core/index.js';
import type { CellRangeDecoratorOption } from '../interfaces/index.js';

Expand All @@ -16,7 +16,7 @@ export class SlickCellRangeDecorator {

protected _options: CellRangeDecoratorOption;
protected _elem?: HTMLDivElement | null;
protected _selectionCss: CSSStyleDeclaration;
protected _selectionCss: Partial<CSSStyleDeclaration>;
protected _defaults = {
selectionCssClass: 'slick-range-decorator',
selectionCss: {
Expand All @@ -35,7 +35,7 @@ export class SlickCellRangeDecorator {
options?: Partial<CellRangeDecoratorOption>
) {
this._options = deepMerge(this._defaults, options);
this._selectionCss = options?.selectionCss || ({} as CSSStyleDeclaration);
this._selectionCss = this._options?.selectionCss || ({} as Partial<CSSStyleDeclaration>);
}

get addonOptions(): CellRangeDecoratorOption {
Expand All @@ -53,11 +53,11 @@ export class SlickCellRangeDecorator {

init(): void {}

getSelectionCss(): CSSStyleDeclaration {
getSelectionCss(): Partial<CSSStyleDeclaration> {
return this._selectionCss;
}

setSelectionCss(cssProps: CSSStyleDeclaration): void {
setSelectionCss(cssProps: Partial<CSSStyleDeclaration>): void {
this._selectionCss = cssProps;
}

Expand All @@ -74,15 +74,10 @@ export class SlickCellRangeDecorator {
}

// Determine which CSS style to use
const css = isCopyTo && this._options.copyToSelectionCss ? this._options.copyToSelectionCss : this._options.selectionCss;
const css = isCopyTo && this._options.copyToSelectionCss ? this._options.copyToSelectionCss : this._selectionCss;

// Apply styles to the element
Object.keys(css).forEach((cssStyleKey: string) => {
const value = css[cssStyleKey as keyof CSSStyleDeclaration];
if (this._elem!.style[cssStyleKey as keyof CSSStyleDeclaration] !== value) {
this._elem!.style.setProperty(cssStyleKey, String(value));
}
});
setStyles(this._elem, css);

// Get the boxes for the selected cells
const from = this.grid.getCellNodeBox(range.fromRow, range.fromCell);
Expand Down
4 changes: 2 additions & 2 deletions packages/common/src/interfaces/cellRange.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ export interface CellRangeDecoratorOption {
selectionCssClass: string;

/** CSS styling to add to decorator (for example blue background on cell) */
selectionCss: CSSStyleDeclaration;
selectionCss: Partial<CSSStyleDeclaration>;

/** CSS styling for drag-fill rangel marker (optional) */
copyToSelectionCss: CSSStyleDeclaration;
copyToSelectionCss: Partial<CSSStyleDeclaration>;

/** offset to add to the cell range outer box size calculation */
offset: { top: number; left: number; height: number; width: number };
Expand Down
9 changes: 0 additions & 9 deletions packages/common/src/interfaces/cssDeclaration.interface.ts

This file was deleted.

1 change: 0 additions & 1 deletion packages/common/src/interfaces/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ export type * from './compositeEditorOption.interface.js';
export type * from './contextMenu.interface.js';
export type * from './contextMenuLabel.interface.js';
export type * from './contextMenuOption.interface.js';
export type * from './cssDeclaration.interface.js';
export type * from './currentColumn.interface.js';
export type * from './currentFilter.interface.js';
export type * from './currentPagination.interface.js';
Expand Down
19 changes: 19 additions & 0 deletions packages/utils/src/__tests__/domUtils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
htmlEncode,
htmlEncodeWithPadding,
insertAfterElement,
setStyles,
} from '../domUtils.js';

describe('Service/domUtilies', () => {
Expand Down Expand Up @@ -419,4 +420,22 @@ describe('Service/domUtilies', () => {
expect(div.outerHTML).toBe(`<div class="div-one"><span class="span-one"></span><span class="span-three"></span><span class="span-two"></span></div>`);
});
});

describe('setStyles() method', () => {
it('should assign CSS style properties', () => {
const div = document.createElement('div');
setStyles(div, { backgroundColor: 'red', border: '1px solid blue' });

expect(div.style.backgroundColor).toEqual('red');
expect(div.style.border).toEqual('1px solid blue');
});

it('should not assign anything when value is undefined', () => {
const div = document.createElement('div');
setStyles(div, { backgroundColor: null as any, border: '1px solid blue' });

expect(div.style.backgroundColor).toEqual('');
expect(div.style.border).toEqual('1px solid blue');
});
});
});
22 changes: 22 additions & 0 deletions packages/utils/src/domUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@ import type { HtmlElementPosition } from './models/interfaces.js';
import type { InferDOMType } from './models/types.js';
import { isDefined } from './utils.js';

export type CSSStyleDeclarationReadonly =
| 'length'
| 'parentRule'
| 'getPropertyPriority'
| 'getPropertyValue'
| 'item'
| 'removeProperty'
| 'setProperty';
export type CSSStyleDeclarationWritable = Omit<CSSStyleDeclaration, CSSStyleDeclarationReadonly>;

/** Calculate available space for each side of the DOM element */
export function calculateAvailableSpace(element: HTMLElement): { top: number; bottom: number; left: number; right: number } {
const vh = window.innerHeight || 0;
Expand Down Expand Up @@ -54,6 +64,18 @@ export function createDomElement<T extends keyof HTMLElementTagNameMap, K extend
return elm;
}

export function setStyles<T extends HTMLElement, P extends Partial<CSSStyleDeclarationWritable>>(element: T, styles: P): void {
Object.keys(styles).forEach((key) => {
const camelStyleKey = key as keyof P;
const value = styles[camelStyleKey];

// Ensure value is valid and assignable to the style property
if (value !== undefined && value !== null) {
(element.style as unknown as P)[camelStyleKey] = value;
}
});
}

/**
* Accepts string containing the class or space-separated list of classes, and
* returns list of individual classes.
Expand Down
Loading