diff --git a/docs b/docs
index 87985133..b5e4ec43 160000
--- a/docs
+++ b/docs
@@ -1 +1 @@
-Subproject commit 879851338eb312b9b92e487343af6ef5cb9fe9b6
+Subproject commit b5e4ec4390be0a84eb007854c2956a6849b76e91
diff --git a/src/components.d.ts b/src/components.d.ts
index b230ac8f..a09d9b58 100644
--- a/src/components.d.ts
+++ b/src/components.d.ts
@@ -5,7 +5,7 @@
* It contains typing information for all components that exist in this project.
*/
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
-import { AfterEditEvent, AllDimensionType, ApplyFocusEvent, BeforeCellRenderEvent, BeforeEdit, BeforeRangeSaveDataDetails, BeforeRowRenderEvent, BeforeSaveDataDetails, Cell, ChangedRange, ColumnDataSchemaModel, ColumnGrouping, ColumnProp, ColumnRegular, ColumnType, DataFormat, DataType, DimensionCols, DimensionRows, DimensionSettingsState, DimensionType, DimensionTypeCol, DragStartEvent, EditCell, EditorCtr, Editors, ElementScroll, FocusRenderEvent, FocusTemplateFunc, InitialHeaderClick, MultiDimensionType, Nullable, PluginBaseComponent, PositionItem, RangeArea, RangeClipboardCopyEventProps, RangeClipboardPasteEvent, RowDefinition, RowHeaders, SaveDataDetails, SelectionStoreState, TempRange, Theme, ViewportData, ViewPortResizeEvent, ViewPortScrollEvent, ViewportState, ViewSettingSizeProp } from "./types/index";
+import { AfterEditEvent, AllDimensionType, ApplyFocusEvent, BeforeCellRenderEvent, BeforeEdit, BeforeRangeSaveDataDetails, BeforeRowRenderEvent, BeforeSaveDataDetails, Cell, ChangedRange, ColumnDataSchemaModel, ColumnGrouping, ColumnProp, ColumnRegular, ColumnType, DataFormat, DataType, DimensionCols, DimensionRows, DimensionSettingsState, DimensionType, DimensionTypeCol, DragStartEvent, EditCell, EditorCtr, Editors, ElementScroll, FocusAfterRenderEvent, FocusRenderEvent, FocusTemplateFunc, InitialHeaderClick, MultiDimensionType, Nullable, PluginBaseComponent, PositionItem, RangeArea, RangeClipboardCopyEventProps, RangeClipboardPasteEvent, RowDefinition, RowHeaders, SaveDataDetails, SelectionStoreState, TempRange, Theme, ViewportData, ViewPortResizeEvent, ViewPortScrollEvent, ViewportState, ViewSettingSizeProp } from "./types/index";
import { GridPlugin } from "./plugins/base.plugin";
import { AutoSizeColumnConfig } from "./plugins/column.auto-size.plugin";
import { ColumnFilterConfig, FilterCaptions, FilterCollection } from "./plugins/filter/filter.plugin";
@@ -21,7 +21,7 @@ import { LogicFunction } from "./plugins/filter/filter.types";
import { ResizeProps } from "./components/header/resizable.directive";
import { Cell as Cell1, ColumnRegular as ColumnRegular1, DataType as DataType1, DimensionCols as DimensionCols1, DimensionRows as DimensionRows1, DimensionSettingsState as DimensionSettingsState1, Observable as Observable1, SelectionStoreState as SelectionStoreState1 } from "./components";
import { EventData } from "./components/overlay/selection.utils";
-export { AfterEditEvent, AllDimensionType, ApplyFocusEvent, BeforeCellRenderEvent, BeforeEdit, BeforeRangeSaveDataDetails, BeforeRowRenderEvent, BeforeSaveDataDetails, Cell, ChangedRange, ColumnDataSchemaModel, ColumnGrouping, ColumnProp, ColumnRegular, ColumnType, DataFormat, DataType, DimensionCols, DimensionRows, DimensionSettingsState, DimensionType, DimensionTypeCol, DragStartEvent, EditCell, EditorCtr, Editors, ElementScroll, FocusRenderEvent, FocusTemplateFunc, InitialHeaderClick, MultiDimensionType, Nullable, PluginBaseComponent, PositionItem, RangeArea, RangeClipboardCopyEventProps, RangeClipboardPasteEvent, RowDefinition, RowHeaders, SaveDataDetails, SelectionStoreState, TempRange, Theme, ViewportData, ViewPortResizeEvent, ViewPortScrollEvent, ViewportState, ViewSettingSizeProp } from "./types/index";
+export { AfterEditEvent, AllDimensionType, ApplyFocusEvent, BeforeCellRenderEvent, BeforeEdit, BeforeRangeSaveDataDetails, BeforeRowRenderEvent, BeforeSaveDataDetails, Cell, ChangedRange, ColumnDataSchemaModel, ColumnGrouping, ColumnProp, ColumnRegular, ColumnType, DataFormat, DataType, DimensionCols, DimensionRows, DimensionSettingsState, DimensionType, DimensionTypeCol, DragStartEvent, EditCell, EditorCtr, Editors, ElementScroll, FocusAfterRenderEvent, FocusRenderEvent, FocusTemplateFunc, InitialHeaderClick, MultiDimensionType, Nullable, PluginBaseComponent, PositionItem, RangeArea, RangeClipboardCopyEventProps, RangeClipboardPasteEvent, RowDefinition, RowHeaders, SaveDataDetails, SelectionStoreState, TempRange, Theme, ViewportData, ViewPortResizeEvent, ViewPortScrollEvent, ViewportState, ViewSettingSizeProp } from "./types/index";
export { GridPlugin } from "./plugins/base.plugin";
export { AutoSizeColumnConfig } from "./plugins/column.auto-size.plugin";
export { ColumnFilterConfig, FilterCaptions, FilterCollection } from "./plugins/filter/filter.plugin";
@@ -52,6 +52,11 @@ export namespace Components {
* @example focus-rgCol-rgRow - focus layer for main data. Applies extra elements in .
*/
interface RevoGrid {
+ /**
+ * Enable accessibility. If disabled, the grid will not be accessible.
+ * @default true
+ */
+ "accessible": boolean;
/**
* Add trimmed by type
*/
@@ -962,10 +967,7 @@ declare global {
interface HTMLRevogrFocusElementEventMap {
"beforefocusrender": FocusRenderEvent;
"beforescrollintoview": { el: HTMLElement };
- "afterfocus": {
- model: any;
- column: ColumnRegular;
- };
+ "afterfocus": FocusAfterRenderEvent;
}
/**
* Focus component. Shows focus layer around the cell that is currently in focus.
@@ -1207,6 +1209,11 @@ declare namespace LocalJSX {
* @example focus-rgCol-rgRow - focus layer for main data. Applies extra elements in .
*/
interface RevoGrid {
+ /**
+ * Enable accessibility. If disabled, the grid will not be accessible.
+ * @default true
+ */
+ "accessible"?: boolean;
/**
* Additional data to be passed to plugins, renders or editors. For example if you need to pass Vue component instance.
*/
@@ -1308,7 +1315,7 @@ declare namespace LocalJSX {
*/
"onAfteredit"?: (event: RevoGridCustomEvent) => void;
/**
- * Triggered after focus render finished. Can be used to access a focus element through `event.target`
+ * Triggered after focus render finished. Can be used to access a focus element through `event.target`. This is just a duplicate of `afterfocus` from `revogr-focus.tsx`.
*/
"onAfterfocus"?: (event: RevoGridCustomEvent<{
model: any;
@@ -1746,10 +1753,7 @@ declare namespace LocalJSX {
/**
* Used to setup properties after focus was rendered
*/
- "onAfterfocus"?: (event: RevogrFocusCustomEvent<{
- model: any;
- column: ColumnRegular;
- }>) => void;
+ "onAfterfocus"?: (event: RevogrFocusCustomEvent) => void;
/**
* Before focus render event. Can be prevented by event.preventDefault(). If preventDefault used slot will be rendered.
*/
diff --git a/src/components/data/column.service.ts b/src/components/data/column.service.ts
index 9b446978..23a07ff0 100644
--- a/src/components/data/column.service.ts
+++ b/src/components/data/column.service.ts
@@ -322,7 +322,7 @@ export default class ColumnService {
prop: ColumnProp;
rowIndex: number;
colIndex: number;
- model: DataType;
+ model?: DataType;
colType: DimensionCols;
type: DimensionRows;
}[] = [];
diff --git a/src/components/data/revogr-data-style.scss b/src/components/data/revogr-data-style.scss
index c9a78319..999e4fa0 100644
--- a/src/components/data/revogr-data-style.scss
+++ b/src/components/data/revogr-data-style.scss
@@ -70,6 +70,7 @@ revogr-data {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
+ outline: none;
&.align-center {
text-align: center;
diff --git a/src/components/overlay/keyboard.service.ts b/src/components/overlay/keyboard.service.ts
index 73c76cb3..7ff623e2 100644
--- a/src/components/overlay/keyboard.service.ts
+++ b/src/components/overlay/keyboard.service.ts
@@ -8,6 +8,7 @@ import {
isEnterKeyValue,
isLetterKey,
isPaste,
+ isTab,
} from '../../utils/key.utils';
import { timeout } from '../../utils';
import {
@@ -80,7 +81,7 @@ export class KeyboardService {
}
// tab key means same as arrow right
- if (codesLetter.TAB === e.code) {
+ if (isTab(e.code)) {
this.keyChangeSelection(e, canRange);
return;
}
diff --git a/src/components/overlay/revogr-overlay-selection.tsx b/src/components/overlay/revogr-overlay-selection.tsx
index ccd49858..68bfd7be 100644
--- a/src/components/overlay/revogr-overlay-selection.tsx
+++ b/src/components/overlay/revogr-overlay-selection.tsx
@@ -790,6 +790,7 @@ export class OverlaySelection {
range,
...this.types,
});
+
if (canPaste) {
return;
}
diff --git a/src/components/overlay/selection.utils.ts b/src/components/overlay/selection.utils.ts
index c9af3837..baf01889 100644
--- a/src/components/overlay/selection.utils.ts
+++ b/src/components/overlay/selection.utils.ts
@@ -19,7 +19,7 @@ export type EventData = {
};
export function collectModelsOfRange(data: DataLookup, store: Observable>) {
- const models: DataLookup = {};
+ const models: Partial = {};
for (let i in data) {
const rowIndex = parseInt(i, 10);
models[rowIndex] = getSourceItem(
diff --git a/src/components/revoGrid/revo-grid.tsx b/src/components/revoGrid/revo-grid.tsx
index fc5aae5b..2ce1e63b 100644
--- a/src/components/revoGrid/revo-grid.tsx
+++ b/src/components/revoGrid/revo-grid.tsx
@@ -12,6 +12,38 @@ import {
Host,
} from '@stencil/core';
+import type {
+ MultiDimensionType,
+ DimensionRows,
+ DimensionCols,
+ DimensionType,
+ DimensionTypeCol,
+ RowHeaders,
+ ColumnRegular,
+ ColumnGrouping,
+ DataType,
+ RowDefinition,
+ ColumnType,
+ FocusTemplateFunc,
+ PositionItem,
+ ColumnProp,
+ ViewPortScrollEvent,
+ InitialHeaderClick,
+ AllDimensionType,
+ Editors,
+ BeforeSaveDataDetails,
+ BeforeRangeSaveDataDetails,
+ Cell,
+ ChangedRange,
+ RangeArea,
+ AfterEditEvent,
+ Theme,
+ PluginBaseComponent,
+ HeaderProperties,
+ PluginProviders,
+ FocusAfterRenderEvent,
+} from '@type';
+
import ColumnDataProvider from '../../services/column.data.provider';
import { DataProvider } from '../../services/data.provider';
import { DSourceState, getVisibleSourceItem } from '@store';
@@ -45,39 +77,10 @@ import { rowDefinitionByType, rowDefinitionRemoveByType } from './grid.helpers';
import ColumnPlugin from '../../plugins/moveColumn/column.drag.plugin';
import { getPropertyFromEvent } from '../../utils/events';
import { isMobileDevice } from '../../utils/mobile';
-import {
- MultiDimensionType,
- DimensionRows,
- DimensionCols,
- DimensionType,
- DimensionTypeCol,
- RowHeaders,
- ColumnRegular,
- ColumnGrouping,
- DataType,
- RowDefinition,
- ColumnType,
- FocusTemplateFunc,
- PositionItem,
- ColumnProp,
- ViewPortScrollEvent,
- InitialHeaderClick,
- AllDimensionType,
- Editors,
- BeforeSaveDataDetails,
- BeforeRangeSaveDataDetails,
- Cell,
- ChangedRange,
- RangeArea,
- AfterEditEvent,
- Theme,
- PluginBaseComponent,
- HeaderProperties,
- PluginProviders,
-} from '@type';
import type { Observable } from '../../utils/store.utils';
import type { GridPlugin } from '../../plugins/base.plugin';
import { ColumnCollection, getColumnByProp, getColumns } from '../../utils/column.utils';
+import { WCAGPlugin } from '../../plugins/wcag';
/**
@@ -285,6 +288,13 @@ export class RevoGridComponent {
*/
@Prop() registerVNode: VNode[] = [];
+
+ /**
+ * Enable accessibility. If disabled, the grid will not be accessible.
+ * @default true
+ */
+ @Prop() accessible = true;
+
// #endregion
// #region Events
@@ -327,12 +337,10 @@ export class RevoGridComponent {
/**
* Triggered after focus render finished.
- * Can be used to access a focus element through `event.target`
+ * Can be used to access a focus element through `event.target`.
+ * This is just a duplicate of `afterfocus` from `revogr-focus.tsx`.
*/
- @Event() afterfocus: EventEmitter<{
- model: any;
- column: ColumnRegular;
- }>;
+ @Event() afterfocus: EventEmitter;
/**
* This event is triggered before the order of `rgRow` is applied.
@@ -1215,6 +1223,10 @@ export class RevoGridComponent {
selection: this.selectionStoreConnector,
};
+ if (this.accessible) {
+ this.internalPlugins.push(new WCAGPlugin(this.element, pluginData));
+ }
+
// register auto size plugin
if (this.autoSizeColumn) {
this.internalPlugins.push(
diff --git a/src/components/scroll/revogr-viewport-scroll-style.scss b/src/components/scroll/revogr-viewport-scroll-style.scss
index 219e6f74..07fd1c8f 100644
--- a/src/components/scroll/revogr-viewport-scroll-style.scss
+++ b/src/components/scroll/revogr-viewport-scroll-style.scss
@@ -67,6 +67,7 @@ revogr-viewport-scroll {
position: relative;
width: 100%;
flex-grow: 1;
+ outline: none; // avoid accessibility focus issue
@include noScroll;
revogr-data,
diff --git a/src/components/selectionFocus/revogr-focus.tsx b/src/components/selectionFocus/revogr-focus.tsx
index d56b0e7b..fda6aae0 100644
--- a/src/components/selectionFocus/revogr-focus.tsx
+++ b/src/components/selectionFocus/revogr-focus.tsx
@@ -20,6 +20,7 @@ import {
FocusTemplateFunc,
DimensionCols,
DimensionRows,
+ FocusAfterRenderEvent,
} from '@type';
import { Observable } from '../../utils/store.utils';
@@ -78,29 +79,11 @@ export class RevogrFocus {
/**
* Used to setup properties after focus was rendered
*/
- @Event({ eventName: 'afterfocus' }) afterFocus: EventEmitter<{
- model: any;
- column: ColumnRegular;
- }>;
+ @Event({ eventName: 'afterfocus' }) afterFocus: EventEmitter;
@Element() el: HTMLElement;
private activeFocus: Cell | null = null;
- private changed(e: HTMLElement, focus: Cell) {
- const beforeScrollIn = this.beforeScrollIntoView.emit({ el: e });
- if (!beforeScrollIn.defaultPrevented) {
- e.scrollIntoView({
- block: 'nearest',
- inline: 'nearest',
- });
- }
- const model = getSourceItem(this.dataStore, focus.y);
- const column = getSourceItem(this.colData, focus.x);
- this.afterFocus.emit({
- model,
- column,
- });
- }
componentDidRender() {
const currentFocus = this.selectionStore.get('focus');
@@ -112,7 +95,23 @@ export class RevogrFocus {
}
this.activeFocus = currentFocus;
if (currentFocus && this.el) {
- this.changed(this.el, currentFocus);
+ const beforeScrollIn = this.beforeScrollIntoView.emit({ el: this.el });
+ if (!beforeScrollIn.defaultPrevented) {
+ this.el.scrollIntoView({
+ block: 'nearest',
+ inline: 'nearest',
+ });
+ }
+ const model = getSourceItem(this.dataStore, currentFocus.y);
+ const column = getSourceItem(this.colData, currentFocus.x);
+ this.afterFocus.emit({
+ model,
+ column,
+ rowType: this.rowType,
+ colType: this.colType,
+ rowIndex: currentFocus.y,
+ colIndex: currentFocus.x,
+ });
}
}
diff --git a/src/plugins/column.auto-size.plugin.ts b/src/plugins/column.auto-size.plugin.ts
index e4eb6fd6..a2444ea7 100644
--- a/src/plugins/column.auto-size.plugin.ts
+++ b/src/plugins/column.auto-size.plugin.ts
@@ -250,7 +250,7 @@ export default class AutoSizeColumnPlugin extends BasePlugin {
s.store.get('items'),
(prev, _row, i) => {
const item = getSourceItem(s.store, i);
- return Math.max(prev || 0, this.getLength(item[rgCol.prop]));
+ return Math.max(prev || 0, this.getLength(item?.[rgCol.prop]));
},
0,
);
diff --git a/src/plugins/groupingRow/grouping.row.plugin.ts b/src/plugins/groupingRow/grouping.row.plugin.ts
index fc3cc0a9..58466de1 100644
--- a/src/plugins/groupingRow/grouping.row.plugin.ts
+++ b/src/plugins/groupingRow/grouping.row.plugin.ts
@@ -102,14 +102,14 @@ export default class GroupingRowPlugin extends BasePlugin {
}
// grouping filter
- if (!isGrouping(model)) {
- result.source.push(model);
- result.oldNewIndexes[i] = index;
- index++;
- } else {
+ if (isGrouping(model)) {
if (model[GROUP_EXPANDED]) {
result.prevExpanded[model[PSEUDO_GROUP_ITEM_VALUE]] = true;
}
+ } else {
+ result.source.push(model);
+ result.oldNewIndexes[i] = index;
+ index++;
}
return result;
},
diff --git a/src/plugins/groupingRow/grouping.service.ts b/src/plugins/groupingRow/grouping.service.ts
index 57967fad..c4e4a4bb 100644
--- a/src/plugins/groupingRow/grouping.service.ts
+++ b/src/plugins/groupingRow/grouping.service.ts
@@ -24,7 +24,6 @@ function getGroupValueDefault(item: DataType, prop: string | number) {
return item[prop] || null;
}
-
/**
* Gather data for grouping
* @param array - flat data array
@@ -34,11 +33,17 @@ function getGroupValueDefault(item: DataType, prop: string | number) {
export function gatherGrouping(
array: DataType[],
groupIds: ColumnProp[],
- { prevExpanded, expandedAll, getGroupValue = getGroupValueDefault }: ExpandedOptions,
+ {
+ prevExpanded,
+ expandedAll,
+ getGroupValue = getGroupValueDefault,
+ }: ExpandedOptions,
) {
const groupedItems: GroupedData = new Map();
array.forEach((item, originalIndex) => {
- const groupLevelValues = groupIds.map(groupId => getGroupValue(item, groupId));
+ const groupLevelValues = groupIds.map(groupId =>
+ getGroupValue(item, groupId),
+ );
const lastLevelValue = groupLevelValues.pop();
let currentGroupLevel = groupedItems;
groupLevelValues.forEach(value => {
@@ -50,9 +55,7 @@ export function gatherGrouping(
if (!currentGroupLevel.has(lastLevelValue)) {
currentGroupLevel.set(lastLevelValue, []);
}
- const lastLevelItems = currentGroupLevel.get(
- lastLevelValue,
- ) as DataType[];
+ const lastLevelItems = currentGroupLevel.get(lastLevelValue) as DataType[];
lastLevelItems.push({
...item,
[GROUP_ORIGINAL_INDEX]: originalIndex,
@@ -128,8 +131,15 @@ export function getGroupingName(rgRow?: DataType) {
return rgRow && rgRow[PSEUDO_GROUP_ITEM];
}
-export function isGrouping(rgRow?: DataType) {
- return rgRow && typeof rgRow[PSEUDO_GROUP_ITEM] !== 'undefined';
+type GroupingItem = {
+ [PSEUDO_GROUP_ITEM]: string;
+ [GROUP_EXPANDED]: boolean;
+ [PSEUDO_GROUP_ITEM_VALUE]: string;
+ [GROUP_DEPTH]: number;
+};
+
+export function isGrouping(rgRow?: DataType): rgRow is GroupingItem {
+ return typeof rgRow?.[PSEUDO_GROUP_ITEM] !== 'undefined';
}
export function isGroupingColumn(column?: ColumnRegular) {
@@ -147,7 +157,7 @@ export function measureEqualDepth(groupA: T[], groupB: T[]) {
return i;
}
-export function getParsedGroup(id: string){
+export function getParsedGroup(id: string) {
const parseGroup = JSON.parse(id);
// extra precaution and type safeguard
if (!Array.isArray(parseGroup)) {
diff --git a/src/plugins/wcag/index.ts b/src/plugins/wcag/index.ts
new file mode 100644
index 00000000..4d209889
--- /dev/null
+++ b/src/plugins/wcag/index.ts
@@ -0,0 +1,119 @@
+import { PluginProviders } from '@type';
+import { BasePlugin } from '../base.plugin';
+import { ColumnCollection } from 'src/utils';
+
+/**
+ * WCAG Plugin is responsible for enhancing the accessibility features of the RevoGrid component.
+ * It ensures that the grid is fully compliant with Web Content Accessibility Guidelines (WCAG) 2.1.
+ * This plugin should be the last plugin you add, as it modifies the grid's default behavior.
+ *
+ * The WCAG Plugin performs the following tasks:
+ * - Sets the 'dir' attribute to 'ltr' for left-to-right text direction.
+ * - Sets the 'role' attribute to 'treegrid' for treelike hierarchical structure.
+ * - Sets the 'aria-keyshortcuts' attribute to 'Enter' and 'Esc' for keyboard shortcuts.
+ * - Adds event listeners for keyboard navigation and editing.
+ *
+ * By default, the plugin adds ARIA roles and properties to the grid elements, providing semantic information
+ * for assistive technologies. These roles include 'grid', 'row', and 'gridcell'. The plugin also sets
+ * ARIA attributes such as 'aria-rowindex', 'aria-colindex', and 'aria-selected'.
+ *
+ * The WCAG Plugin ensures that the grid is fully functional and usable for users with various disabilities,
+ * including visual impairments, deaf-blindness, and cognitive disabilities.
+ *
+ * Note: The WCAG Plugin should be added as the last plugin in the list of plugins, as it modifies the grid's
+ * default behavior and may conflict with other plugins if added earlier.
+ */
+export class WCAGPlugin extends BasePlugin {
+ constructor(revogrid: HTMLRevoGridElement, providers: PluginProviders) {
+ super(revogrid, providers);
+
+ revogrid.setAttribute('dir', 'ltr');
+ revogrid.setAttribute('role', 'treegrid');
+ revogrid.setAttribute('aria-keyshortcuts', 'Enter');
+ revogrid.setAttribute('aria-multiselectable', 'true');
+ revogrid.setAttribute('tabindex', '0');
+
+ /**
+ * Before Columns Set Event
+ */
+ this.addEventListener(
+ 'beforecolumnsset',
+ ({ detail }: CustomEvent) => {
+ const columns = [
+ ...detail.columns.colPinStart,
+ ...detail.columns.rgCol,
+ ...detail.columns.colPinEnd,
+ ];
+
+ revogrid.setAttribute('aria-colcount', `${columns.length}`);
+
+ columns.forEach((column, index) => {
+ const { columnProperties, cellProperties } = column;
+
+ column.columnProperties = (...args) => {
+ const result = columnProperties?.(...args) || {};
+
+ result.role = 'columnheader';
+ result['aria-colindex'] = index;
+
+ return result;
+ };
+
+ column.cellProperties = (...args) => {
+ const columnProps = cellProperties?.(...args) || {};
+
+ return {
+ role: 'gridcell',
+ ['aria-colindex']: index,
+ ['aria-rowindex']: args[0].rowIndex,
+ ['tabindex']: -1,
+ ...columnProps,
+ };
+ };
+ });
+ },
+ );
+
+ /**
+ * Before Row Set Event
+ */
+ this.addEventListener(
+ 'beforesourceset',
+ ({
+ detail,
+ }: CustomEvent) => {
+ revogrid.setAttribute('aria-rowcount', `${detail.source.length}`);
+ },
+ );
+ this.addEventListener(
+ 'beforerowrender',
+ ({
+ detail,
+ }: CustomEvent) => {
+ detail.node.$attrs$ = {
+ ...detail.node.$attrs$,
+ role: 'row',
+ ['aria-rowindex']: detail.item.itemIndex,
+ };
+ },
+ );
+
+ // focuscell
+ this.addEventListener(
+ 'afterfocus',
+ async (
+ e: CustomEvent,
+ ) => {
+ if (e.defaultPrevented) {
+ return;
+ }
+ const el = this.revogrid.querySelector(
+ `revogr-data[type="${e.detail.rowType}"][col-type="${e.detail.colType}"] [data-rgrow="${e.detail.rowIndex}"][data-rgcol="${e.detail.colIndex}"]`,
+ );
+ if (el instanceof HTMLElement) {
+ el.focus();
+ }
+ },
+ );
+ }
+}
diff --git a/src/serve/index.html b/src/serve/index.html
index 25861991..f1211ca3 100644
--- a/src/serve/index.html
+++ b/src/serve/index.html
@@ -125,7 +125,7 @@ Themes
-
+
diff --git a/src/store/dataSource/data.store.ts b/src/store/dataSource/data.store.ts
index b33222c7..abd7113c 100644
--- a/src/store/dataSource/data.store.ts
+++ b/src/store/dataSource/data.store.ts
@@ -143,10 +143,10 @@ export function getVisibleSourceItem(
* @param store - store to process
* @param virtualIndex - virtual index to process
*/
-export const getSourceItem = (
- store: Observable>,
+export const getSourceItem = (
+ store: Observable>,
virtualIndex: number,
-): any | undefined => {
+) => {
const items = store.get('items');
const source = store.get('source');
return source[items[virtualIndex]];
@@ -160,7 +160,7 @@ export const getSourceItem = (
*/
export function setSourceByVirtualIndex(
store: Observable>,
- modelByIndex: Record,
+ modelByIndex: Record,
mutate = true,
) {
const items = store.get('items');
@@ -168,7 +168,8 @@ export function setSourceByVirtualIndex(
for (let virtualIndex in modelByIndex) {
const realIndex = items[virtualIndex];
- source[realIndex] = modelByIndex[virtualIndex];
+ const item = modelByIndex[virtualIndex];
+ source[realIndex] = item as T;
}
if (mutate) {
store.set('source', [...source]);
diff --git a/src/types/interfaces.ts b/src/types/interfaces.ts
index 484708db..03e1be0c 100644
--- a/src/types/interfaces.ts
+++ b/src/types/interfaces.ts
@@ -186,7 +186,7 @@ export type Order = 'asc' | 'desc' | undefined;
/**
* Interface for regular column definition.
* Regular column can be any column that is not a grouping column.
- *
+ *
*/
/**
* ColumnRegular interface represents regular column definition.
@@ -253,7 +253,7 @@ export interface ColumnTemplateProp extends ColumnRegular {
* Index of the column, used for mapping value to cell from data source model/row.
*/
index: number;
-};
+}
export type ColumnPropProp = ColumnGrouping | ColumnTemplateProp;
// Column prop used for mapping value to cell from data source model/row, used for indexing.
@@ -385,7 +385,6 @@ export type FocusTemplateFunc = (
detail: FocusRenderEvent,
) => any;
-
/**
* `CellCompareFunc` is a function that takes the column property to compare,
* the data of the first cell, and the data of the second cell. It returns a
@@ -759,6 +758,19 @@ export interface FocusRenderEvent extends AllDimensionType {
*/
next?: Partial;
}
+
+export interface FocusAfterRenderEvent extends AllDimensionType {
+ model?: any;
+ column?: ColumnRegular;
+ /**
+ * Index of the row in the viewport
+ */
+ rowIndex: number;
+ /**
+ * Index of the column in the viewport
+ */
+ colIndex: number;
+}
/**
* Represents the event object that is emitted when scrolling occurs.
* The `type` property indicates the type of dimension (row or column) being scrolled.
@@ -778,13 +790,10 @@ export type ScrollCoordinateEvent = {
coordinate: number;
};
-
/** Range paste. */
export type RangeClipboardPasteEvent = {
data: DataLookup;
- models: {
- [rowIndex: number]: DataType;
- };
+ models: Partial;
range: RangeArea | null;
} & AllDimensionType;
@@ -793,4 +802,4 @@ export type RangeClipboardCopyEventProps = {
data: DataFormat[][];
range: RangeArea;
mapping: OldNewRangeMapping;
-} & AllDimensionType;
\ No newline at end of file
+} & AllDimensionType;
diff --git a/src/types/selection.ts b/src/types/selection.ts
index b21002dc..dcdb3009 100644
--- a/src/types/selection.ts
+++ b/src/types/selection.ts
@@ -114,9 +114,7 @@ export type BeforeSaveDataDetails = {
};
export type BeforeRangeSaveDataDetails = {
data: DataLookup;
- models: {
- [rowIndex: number]: DataType;
- };
+ models: Partial;
type: DimensionRows;
};
|