Web Components Tree Grid Keyboard Navigation
Web Components Tree Grid의 Ignite UI for Web Components 키보드 탐색 기능은 사용자에게 다양한 키보드 상호 작용을 제공합니다. IgcTreeGridComponent
의 접근성을 향상시키고 사용자가 내부의 모든 유형의 요소(셀, 행, 열 머리글, 도구 모음, 바닥글 등)를 탐색할 수 있도록 합니다. 이 기능은 기본적으로 활성화되어 있으며 개발자는 모든 기본 동작을 쉽게 재정의할 수 있습니다.
탐색이 W3C 접근성 표준을 준수하고 사용하기 편리하도록 표 IgcTreeGridComponent
가 축소되었습니다.
현재 다음과 같은 탭 정지가 IgcTreeGridComponent
도입되었습니다.
- GroupBy 또는 도구 모음 영역 (활성화된 경우)
- Tree Grid header.
- Tree Grid body.
- Column summaries (if enabled).
- Tree Grid paginator (활성화된 경우).
이 변경으로 인해 IgcTreeGridComponent에서 탭과 Shift + Tab을 사용하여 셀 사이를 탐색하는 것이 더 이상 지원되지 않습니다. 이제 Tab 키를 누르면 GroupBy / Toolbar -> Headers -> Body -> Summaries -> Footer / Paginator 순서로 탭 정지가 진행됩니다.
템플릿을 통해 포커스 가능한 요소를 IgcTreeGridComponent 본문에 노출하면 기본 브라우저 동작이 금지되지 않으므로 키보드 탐색에 부작용이 발생할 수 있습니다. 이를 적절하게 방지하거나 수정하는 것은 개발자의 책임입니다.
헤더 탐색
전체 키보드 탐색에서 지원 IgcTreeGridComponent
이제 헤더가 도입되었습니다. 열 머리글은 화살표 키를 사용하여 쉽게 탐색할 수 있습니다. 또한 다음과 같은 열에서 작업을 트리거하는 여러 키 조합이 있습니다. 필터링, 정렬, 그룹화 그리고 등등. 때 IgcTreeGridComponent
헤더 컨테이너에 초점이 맞춰져 있는 경우 다음 키 조합을 사용할 수 있습니다.
주요 조합
↑ 헤더에서 한 셀 위로 이동합니다(반복 없음). 다중 행 레이아웃(MRL) 또는 다중 열 헤더(MCH)가 정의된 경우에만 사용할 수 있습니다.
↓ 헤더에서 한 셀 아래로 이동합니다(줄 바꿈 없음). 다중 행 레이아웃(MRL) 또는 다중 열 헤더(MCH)가 정의된 경우에만 사용할 수 있습니다.
← 한 셀을 왼쪽으로 이동합니다(반복 없음).
→ 한 셀 오른쪽으로 이동합니다(줄 사이에 줄 바꿈 없음).
Ctrl + ←는 행의 가장 왼쪽 셀로 이동합니다. MRL 또는 MCH가 활성화된 경우 동일한 수준의 가장 왼쪽 셀로 이동합니다.
홈은 행의 가장 왼쪽 셀로 이동합니다. MRL 또는 MCH가 활성화된 경우 동일한 수준의 가장 왼쪽 셀로 이동합니다.
Ctrl + → 행의 가장 오른쪽 셀로 이동합니다. MRL 또는 MCH가 활성화된 경우 동일한 수준의 가장 오른쪽 셀로 이동합니다.
End는 행의 가장 오른쪽 셀로 이동합니다. MRL 또는 MCH가 활성화된 경우 동일한 수준의 가장 오른쪽 셀로 이동합니다.
고급 필터링이 활성화된 경우 Alt + L은 고급 필터링 대화 상자를 엽니다.
Ctrl + Shift + L은 Excel 스타일 필터 대화 상자를 열거나 열을 필터링할 수 있는 경우 기본(행) 필터를 엽니다.
Ctrl + ↑는 활성 열 헤더를 ASC 순서로 정렬합니다. 열이 이미 ASC로 정렬되어 있는 경우 정렬 상태가 지워집니다.
Ctrl + ↓는 활성 열 머리글을 DSC 순서로 정렬합니다. DSC에서 열이 이미 정렬된 경우 정렬 상태가 지워집니다.
Space는 열을 선택합니다. 열이 이미 선택된 경우 선택이 취소됩니다.
Shift + Alt + → 열이 그룹화 가능으로 표시된 경우 열의 그룹을 해제합니다.
Alt + ← 또는 Alt + ↑는 헤더가 아직 축소되지 않은 경우 열 그룹 헤더를 축소합니다.
Alt + → 또는 Alt + ↓는 헤더가 아직 확장되지 않은 경우 열 그룹 헤더를 확장합니다.
본체 탐색
몸에 초점이 IgcTreeGridComponent
맞춰지면 다음과 같은 키 조합을 사용할 수 있습니다.
키 조합
- ↑- 한 셀 위로 이동합니다.
- ↓ 한 셀 아래로 이동합니다.
- ← 한 셀 왼쪽으로 이동합니다(줄 바꿈 없음).
- →- 한 셀 오른쪽으로 이동합니다(줄 바꿈 없음).
- Ctrl + ←는 행의 가장 왼쪽 셀로 이동합니다.
- Ctrl + →는 행의 가장 오른쪽 셀로 이동합니다.
- Ctrl + ↑는 열의 첫 번째 셀로 이동합니다.
- Ctrl + ↓는 열의 마지막 셀로 이동합니다.
- 홈은 행의 가장 왼쪽 셀로 이동합니다.
- End는 행의 가장 오른쪽 셀로 이동합니다.
- Ctrl + Home은 그리드의 가장 왼쪽 상단 데이터 셀로 이동합니다.
- Ctrl + End는 그리드의 가장 오른쪽 하단 데이터 셀로 이동합니다.
- Page Up은 한 페이지(뷰 포트) 위로 스크롤합니다.
- Page Down은 한 페이지(뷰 포트) 아래로 스크롤합니다.
- Enter enters edit mode.
- F2 enters edit mode.
- Esc exits edit mode.
- 편집 모드에 셀이 있는 경우에만 탭을 사용할 수 있습니다. 행의 편집 가능한 다음 셀로 포커스를 이동합니다. 행의 마지막 셀에 도달한 후 다음 행의 편집 가능한 첫 번째 셀로 포커스를 이동합니다. 행 편집이 활성화되면 포커스는 가장 오른쪽의 편집 가능한 셀에서 CANCEL 및 DONE 버튼으로 이동하고 DONE 버튼에서 행의 가장 왼쪽의 편집 가능한 셀로 이동됩니다.
- Shift + Tab- 편집 모드에 셀이 있는 경우에만 사용할 수 있습니다. 행의 편집 가능한 이전 셀로 포커스를 이동합니다. 행의 첫 번째 셀에 도달한 후 이전 행의 편집 가능한 마지막 셀로 포커스를 이동합니다. 행 편집이 활성화되면 행의 가장 오른쪽 편집 가능한 셀에서 CANCEL 및 DONE 버튼으로 포커스가 이동하고 DONE 버튼에서 행의 가장 오른쪽 편집 가능한 셀로 포커스가 이동됩니다.
- 공백-행 선택이 활성화된 경우 행을 선택합니다.
- Alt + ← or Alt + ↑ -
현재 노드를 축소합니다.
- Alt + → 또는 Alt + ↓- 그룹 행 위 - 그룹을 확장합니다.
현재 노드를 확장합니다.
아래 데모 샘플에서 위에 언급된 모든 작업을 연습해 보세요. 탐색 가능한 그리드 요소에 초점을 맞추면 해당 요소에 사용 가능한 일부 작업이 포함된 목록이 표시되어 안내됩니다.
데모
export class EmployeesFlatDetailsItem {
public constructor(init: Partial<EmployeesFlatDetailsItem>) {
Object.assign(this, init);
}
public Address: string;
public Age: number;
public City: string;
public Country: string;
public Fax: string;
public HireDate: string;
public ID: number;
public Name: string;
public ParentID: number;
public Phone: string;
public PostalCode: string;
public Title: string;
public LastName: string;
public FullAddress: string;
}
export class EmployeesFlatDetails extends Array<EmployeesFlatDetailsItem> {
public constructor() {
super();
this.push(new EmployeesFlatDetailsItem(
{
Address: `Obere Str. 57`,
Age: 55,
City: `Berlin`,
Country: `Germany`,
Fax: `030-0076545`,
HireDate: `2008, 3, 20`,
ID: 1,
Name: `Johnathan Winchester`,
ParentID: -1,
Phone: `030-0074321`,
PostalCode: `12209`,
Title: `Development Manager`,
LastName: `Winchester`,
FullAddress: `Obere Str. 57, Berlin, Germany`
}));
this.push(new EmployeesFlatDetailsItem(
{
Address: `Avda. de la Constitución 2222`,
Age: 42,
City: `México D.F.`,
Country: `Mexico`,
Fax: `(5) 555-3745`,
HireDate: `2014, 1, 22`,
ID: 4,
Name: `Ana Sanders`,
ParentID: -1,
Phone: `(5) 555-4729`,
PostalCode: `05021`,
Title: `CEO`,
LastName: `Sanders`,
FullAddress: `Avda. de la Constitución 2222, México D.F., Mexico`
}));
this.push(new EmployeesFlatDetailsItem(
{
Address: `Mataderos 2312`,
Age: 49,
City: `México D.F.`,
Country: `Mexico`,
Fax: `(5) 555-3995`,
HireDate: `2014, 1, 22`,
ID: 18,
Name: `Victoria Lincoln`,
ParentID: -1,
Phone: `(5) 555-3932`,
PostalCode: `05023`,
Title: `Accounting Manager`,
LastName: `Lincoln`,
FullAddress: `Mataderos 2312, México D.F., Mexico`
}));
this.push(new EmployeesFlatDetailsItem(
{
Address: `120 Hanover Sq.`,
Age: 61,
City: `London`,
Country: `UK`,
Fax: `(171) 555-6750`,
HireDate: `2010, 1, 1`,
ID: 10,
Name: `Yang Wang`,
ParentID: -1,
Phone: `(171) 555-7788`,
PostalCode: `WA1 1DP`,
Title: `Localization Manager`,
LastName: `Wang`,
FullAddress: `120 Hanover Sq., London, UK`
}));
this.push(new EmployeesFlatDetailsItem(
{
Address: `Berguvsvägen 8`,
Age: 43,
City: `Luleå`,
Country: `Sweden`,
Fax: `0921-12 34 67`,
HireDate: `2011, 6, 3`,
ID: 3,
Name: `Michael Burke`,
ParentID: 1,
Phone: `0921-12 34 65`,
PostalCode: `S-958 22`,
Title: `Senior Software Developer`,
LastName: `Burke`,
FullAddress: `Berguvsvägen 8, Luleå, Sweden`
}));
this.push(new EmployeesFlatDetailsItem(
{
Address: `Forsterstr. 57`,
Age: 29,
City: `Mannheim`,
Country: `Germany`,
Fax: `0621-08924`,
HireDate: `2009, 6, 19`,
ID: 2,
Name: `Thomas Anderson`,
ParentID: 1,
Phone: `0621-08460`,
PostalCode: `68306`,
Title: `Senior Software Developer`,
LastName: `Anderson`,
FullAddress: `Forsterstr. 57, Mannheim, Germany`
}));
this.push(new EmployeesFlatDetailsItem(
{
Address: `24, place Kléber`,
Age: 31,
City: `Strasbourg`,
Country: `France`,
Fax: `88.60.15.32`,
HireDate: `2014, 8, 18`,
ID: 11,
Name: `Monica Reyes`,
ParentID: 1,
Phone: `88.60.15.31`,
PostalCode: `67000`,
Title: `Software Development Team Lead`,
LastName: `Reyes`,
FullAddress: `24, place Kléber, Strasbourg, France`
}));
this.push(new EmployeesFlatDetailsItem(
{
Address: `C/ Araquil, 67`,
Age: 35,
City: `Madrid`,
Country: `Spain`,
Fax: `(91) 555 91 99`,
HireDate: `2015, 9, 17`,
ID: 6,
Name: `Roland Mendel`,
ParentID: 11,
Phone: `(91) 555 22 82`,
PostalCode: `28023`,
Title: `Senior Software Developer`,
LastName: `Mendel`,
FullAddress: `C/ Araquil, 67, Madrid, Spain`
}));
this.push(new EmployeesFlatDetailsItem(
{
Address: `12, rue des Bouchers`,
Age: 44,
City: `Marseille`,
Country: `France`,
Fax: `91.24.45.41`,
HireDate: `2009, 10, 11`,
ID: 12,
Name: `Sven Cooper`,
ParentID: 11,
Phone: `91.24.45.40`,
PostalCode: `13008`,
Title: `Senior Software Developer`,
LastName: `Cooper`,
FullAddress: `12, rue des Bouchers, Marseille, France`
}));
this.push(new EmployeesFlatDetailsItem(
{
Address: `23 Tsawassen Blvd.`,
Age: 44,
City: `Tsawassen`,
Country: `Canada`,
Fax: `(604) 555-3745`,
HireDate: `2014, 4, 4`,
ID: 14,
Name: `Laurence Johnson`,
ParentID: 4,
Phone: `(604) 555-4729`,
PostalCode: `T2F 8M4`,
Title: `Director`,
LastName: `Johnson`,
FullAddress: `23 Tsawassen Blvd., Tsawassen, Canada`
}));
this.push(new EmployeesFlatDetailsItem(
{
Address: `Fauntleroy Circus`,
Age: 25,
City: `London`,
Country: `UK`,
Fax: `(5) 555-3798`,
HireDate: `2017, 11, 9`,
ID: 5,
Name: `Elizabeth Richards`,
ParentID: 4,
Phone: `(171) 555-1212`,
PostalCode: `EC2 5NT`,
Title: `Vice President`,
LastName: `Richards`,
FullAddress: `Fauntleroy Circus, London, UK`
}));
this.push(new EmployeesFlatDetailsItem(
{
Address: `Cerrito 333`,
Age: 39,
City: `Buenos Aires`,
Country: `Argentina`,
Fax: `(1) 135-4892`,
HireDate: `2010, 3, 22`,
ID: 13,
Name: `Trevor Ashworth`,
ParentID: 5,
Phone: `(1) 135-5555`,
PostalCode: `1010`,
Title: `Director`,
LastName: `Ashworth`,
FullAddress: `Cerrito 333, Buenos Aires, Argentina`
}));
this.push(new EmployeesFlatDetailsItem(
{
Address: `Sierras de Granada 9993`,
Age: 44,
City: `México D.F.`,
Country: `Mexico`,
Fax: `(5) 555-7293`,
HireDate: `2014, 4, 4`,
ID: 17,
Name: `Antonio Moreno`,
ParentID: 18,
Phone: `(5) 555-3392`,
PostalCode: `05022`,
Title: `Senior Accountant`,
LastName: `Moreno`,
FullAddress: `Sierras de Granada 9993, México D.F., Mexico`
}));
this.push(new EmployeesFlatDetailsItem(
{
Address: `Hauptstr. 29`,
Age: 50,
City: `Sao Paulo`,
Country: `Brazil`,
Fax: `(5) 555-6691`,
HireDate: `2007, 11, 18`,
ID: 7,
Name: `Pedro Rodriguez`,
ParentID: 10,
Phone: `0452-076545`,
PostalCode: `3012`,
Title: `Senior Localization Developer`,
LastName: `Rodriguez`,
FullAddress: `Hauptstr. 29, Sao Paulo, Brazil`
}));
this.push(new EmployeesFlatDetailsItem(
{
Address: `Av. dos Lusíadas, 23`,
Age: 27,
City: `Bern`,
Country: `Switzerland`,
Fax: ``,
HireDate: `2016, 2, 19`,
ID: 8,
Name: `Casey Harper`,
ParentID: 10,
Phone: `(11) 555-7647`,
PostalCode: `05432-043`,
Title: `Senior Localization`,
LastName: `Harper`,
FullAddress: `Av. dos Lusíadas, 23, Bern, Switzerland`
}));
this.push(new EmployeesFlatDetailsItem(
{
Address: `Berkeley Gardens 12`,
Age: 25,
City: `London`,
Country: `UK`,
Fax: `(171) 555-9199`,
HireDate: `2017, 11, 9`,
ID: 15,
Name: `Patricia Simpson`,
ParentID: 7,
Phone: `(171) 555-2282`,
PostalCode: `WX1 6LT`,
Title: `Localization Intern`,
LastName: `Simpson`,
FullAddress: `Berkeley Gardens 12, London, UK`
}));
this.push(new EmployeesFlatDetailsItem(
{
Address: `Walserweg 21`,
Age: 39,
City: `Aachen`,
Country: `Germany`,
Fax: `0241-059428`,
HireDate: `2010, 3, 22`,
ID: 9,
Name: `Francisco Chang`,
ParentID: 7,
Phone: `0241-039123`,
PostalCode: `52066`,
Title: `Localization Intern`,
LastName: `Chang`,
FullAddress: `Walserweg 21, Aachen, Germany`
}));
this.push(new EmployeesFlatDetailsItem(
{
Address: `35 King George`,
Age: 25,
City: `London`,
Country: `UK`,
Fax: `(171) 555-3373`,
HireDate: `2018, 3, 18`,
ID: 16,
Name: `Peter Lewis`,
ParentID: 7,
Phone: `(171) 555-0297`,
PostalCode: `WX3 6FW`,
Title: `Localization Intern`,
LastName: `Lewis`,
FullAddress: `35 King George, London, UK`
}));
}
}
tsexport enum ItemAction {
Filterable,
Sortable,
Selectable,
Groupable,
Collapsible,
Expandable,
Editable,
Always
}
export enum GridSection {
THEAD = 'igx-grid__thead-wrapper',
TBODY = 'igx-grid__tbody-content',
FOOTER = 'igx-grid__tfoot'
}
export class Item {
public title: string;
public subTitle: string;
public action: ItemAction;
public active = false;
private _completed: boolean;
public constructor(title: string, subTitle: string, completed: boolean, itemAction?: ItemAction) {
this.title = title;
this.subTitle = subTitle;
this.completed = completed;
this.action = itemAction;
if (itemAction === ItemAction.Always) {
this.active = true;
}
}
public set completed(value: boolean) {
if (this.active || (!value && !this.completed)) {
this._completed = value;
}
}
public get completed() {
return this._completed;
}
}
tsimport 'igniteui-webcomponents-grids/grids/combined';
import { IgcActiveNodeChangeEventArgs, IgcCellType, IgcColumnComponent, IgcColumnGroupComponent, IgcGridComponent, IgcGridMasterDetailContext, IgcTreeGridComponent, SortingDirection } from 'igniteui-webcomponents-grids/grids';
import { EmployeesFlatDetails } from './EmployeesFlatDetails';
import { html } from 'igniteui-webcomponents-core';
import "igniteui-webcomponents-grids/grids/themes/light/bootstrap.css";
import "./index.css";
import { Item, ItemAction, GridSection } from './Item';
import { defineComponents, IgcListComponent, IgcCheckboxComponent } from 'igniteui-webcomponents';
defineComponents(IgcListComponent, IgcCheckboxComponent);
const theadKeyCombinations = [
new Item('space key', 'select column', false, ItemAction.Selectable),
new Item('ctrl + arrow up/down', 'sorts the column asc/desc', false, ItemAction.Sortable),
new Item('shift + alt + arrow left/right', 'group/ungroup the active column', false, ItemAction.Groupable),
new Item('alt + arrow left/right/up/down', 'expand/collapse active multi column header',
false, ItemAction.Collapsible),
new Item('ctrl + shift + l', 'opens the excel style filtering', false, ItemAction.Filterable),
new Item('alt + l', 'opens the advanced filtering', false, ItemAction.Filterable)
];
const tbodyKeyCombinations: Item[] = [
new Item('enter', 'enter in edit mode', false, ItemAction.Editable),
new Item('alt + arrow left/up', 'collapse master details row', false, ItemAction.Collapsible),
new Item('alt + arrow right/down', 'expand master details row', false, ItemAction.Collapsible),
new Item('alt + arrow right/left', 'expand/collapse the group row', false, ItemAction.Expandable),
new Item('ctrl + Home/End', 'navigates to the upper-left/bottom-right cell', false, ItemAction.Always)
];
const summaryCombinations: Item[] = [
new Item('ArrowLeft', 'navigates one summary cell left', false, ItemAction.Always),
new Item('ArrowRight', 'navigates one summary cell right', false, ItemAction.Always),
new Item('Home', 'navigates to the first summary cell', false, ItemAction.Always),
new Item('End', 'navigates to the last summary cell', false, ItemAction.Always)
];
export class Sample {
private grid: IgcTreeGridComponent;
private list: IgcListComponent;
private activeCollection: Item[];
private gridSection: GridSection;
private activeNode: any;
constructor() {
var grid = this.grid = document.getElementById('grid') as IgcTreeGridComponent;
var list = this.list = document.getElementById('list') as IgcListComponent;
this.onActiveNodeChange = this.onActiveNodeChange.bind(this);
this.gridKeydown = this.gridKeydown.bind(this);
this.keydown = this.keydown.bind(this);
grid.data = this.data;
grid.addEventListener("activeNodeChange", this.onActiveNodeChange);
grid.addEventListener("gridKeydown", this.gridKeydown);
grid.addEventListener("keydown", this.keydown);
}
public changeCombinationsCollection(gridSection: GridSection, evt: any) {
switch (gridSection) {
case GridSection.THEAD:
this.activeCollection = theadKeyCombinations;
this.toggleHeaderCombinations(evt);
break;
case GridSection.TBODY:
this.activeCollection = tbodyKeyCombinations;
this.toggleBodyCombinations(evt);
break;
case GridSection.FOOTER:
this.activeCollection = summaryCombinations;
break;
default:
this.activeCollection = [];
return;
}
this.updateList();
}
public updateList() {
this.list.innerHTML = this.listTemplate();
}
public toggleHeaderCombinations(activeNode: any) {
const currColumn = this.grid.columns
.find(c => c.visibleIndex === activeNode.column && c.level === activeNode.level) as IgcColumnGroupComponent;
this.activeCollection.forEach(x => x.active = true);
const actions = this.extractColumnActions(currColumn);
this.activeCollection.filter(x => actions.indexOf(x.action) === -1 && x.action !== ItemAction.Always)?.forEach(x => x.active = false);
}
public toggleBodyCombinations(activeNode: any) {
const rowRef = this.grid.getRowByIndex(activeNode.row);
if (rowRef.isGroupByRow) {
this.activeCollection.forEach(x => x.active = false);
this.activeCollection.filter(x => x.action === ItemAction.Expandable || x.action === ItemAction.Always)?.forEach(x => x.active = true);
} else {
const currColumn = this.grid.columns.filter(x => !x.columnGroup)
.find(c => c.visibleIndex === activeNode.column);
const cell = this.grid.getCellByColumn(activeNode.row, currColumn.field);
this.toggleCellCombinations(cell);
}
}
public toggleCellCombinations(cell?: IgcCellType) {
this.activeCollection.forEach(x => x.active = true);
const actions = this.extractCellActions(cell);
this.activeCollection.filter(x => actions.indexOf(x.action) === -1 && x.action !== ItemAction.Always)?.forEach(x => x.active = false);
}
public extractCellActions(cell: IgcCellType) {
const res: any[] = [];
if(!cell) return res;
if (cell?.editable) {
res.push(ItemAction.Editable);
}
res.push(ItemAction.Collapsible);
return res;
}
public extractColumnActions(col: IgcColumnGroupComponent) {
const res = [];
if (col.sortable) {
res.push(ItemAction.Sortable);
}
if (col.filterable && !col.columnGroup) {
res.push(ItemAction.Filterable);
}
if (col.collapsible) {
res.push(ItemAction.Collapsible);
}
if (col.groupable) {
res.push(ItemAction.Groupable);
}
if (col.selectable) {
res.push(ItemAction.Selectable);
}
return res;
}
public onActiveNodeChange(event: any) {
const evt = (event as any).detail;
this.activeNode = evt;
const row = this.grid.getRowByIndex(evt.row);
this.gridSection = evt.row < 0 ? GridSection.THEAD : row === undefined || row.isSummaryRow ?
GridSection.FOOTER : GridSection.TBODY;
this.changeCombinationsCollection(this.gridSection, evt);
}
public keydown(event: any) {
const key = event.key.toLowerCase();
if (key === 'tab') { return; }
if (this.gridSection === GridSection.FOOTER) {
switch (key) {
case 'end':
this.activeCollection.at(3).completed = true;
break;
case 'home':
this.activeCollection.at(2).completed = true;
break;
case 'arrowleft':
this.activeCollection.at(0).completed = true;
break;
case 'arrowright':
this.activeCollection.at(1).completed = true;
break;
default:
break;
}
return;
}
const activeNode = this.activeNode;
if (this.gridSection === GridSection.THEAD) {
if (key === 'l' && event.altKey) {
this.activeCollection.at(5).completed = true;
}
const col = this.grid.columns
.find(c => c.visibleIndex === activeNode.column && c.level === activeNode.level);
if (key === 'l' && event.ctrlKey && event.shiftKey && col && !col.columnGroup && col.filterable) {
this.activeCollection.at(4).completed = true;
}
if ((key === 'arrowleft' || key === 'arrowright') && event.altKey && event.shiftKey &&
col && !col.columnGroup && col.groupable) {
this.activeCollection.at(2).completed = true;
}
if ((key === 'arrowup' || key === 'arrowdown') && event.ctrlKey) {
if (col && !col.columnGroup && col.sortable) {
this.activeCollection.at(1).completed = true;
}
}
if (key === " ") {
this.activeCollection.at(0).completed = true;
}
if (col && col.columnGroup && (key === 'arrowup' || key === 'arrowdown' || key === 'arrowleft' || key === 'arrowright') && event.altKey) {
this.activeCollection.at(3).completed = true;
}
}
if (this.gridSection === GridSection.TBODY) {
if (key === 'enter') {
const currColumn = this.grid.columns.filter(x => !x.columnGroup)
.find(c => c.visibleIndex === activeNode.column);
const cell = this.grid.getCellByColumn(activeNode.row, currColumn.field);
if (cell && cell.column.editable) {
this.activeCollection.at(0).completed = true;
}
}
if ((key === 'end' || key === 'home') && event.ctrlKey) {
this.activeCollection.at(4).completed = true;
}
const rowRef = this.grid.getRowByIndex(activeNode.row);
const isGroupByRow = rowRef.isGroupByRow;
if (!isGroupByRow && (key === 'arrowdown' || key === 'arrowright') && event.altKey) {
this.activeCollection.at(2).completed = true;
}
if (!isGroupByRow && (key === 'arrowup' || key === 'arrowleft') && event.altKey) {
this.activeCollection.at(1).completed = true;
}
if (isGroupByRow && (key === 'arrowright' || key === 'arrowleft') && event.altKey ) {
this.activeCollection.at(3).completed = true;
}
}
this.updateList();
}
public gridKeydown(event: any) {
const evt = event.detail.event;
this.keydown(evt);
}
public listTemplate = () => {
let htmlContent = "";
(window as any).onChangeHandler = (i: number) => {
this.activeCollection.at(i).completed = (event.currentTarget as any).checked;
}
let i = 0;
const headerText = this.gridSection === GridSection.THEAD ? "HEADER COMBINATIONS" : this.gridSection === GridSection.TBODY ? "BODY COMBINATIONS" : "SUMMARY COMBINATIONS";
const header = "<igc-list-header><h1>"+ headerText +"</h1></igc-list-header>";
htmlContent += header;
for (const elem of this.activeCollection) {
const checkbox = elem.completed ? "<input type='checkbox' slot='end' checked onchange='onChangeHandler("+i+")'></input>" : "<input type='checkbox' onchange='onChangeHandler("+i+")' slot='end'></input>";
const disabledClass = !elem.active ? "disabled" : "";
htmlContent += "<igc-list-item class='" + disabledClass +
"'> <h2 slot='title'>" + elem.title + "</h2>" +
"<span slot='subtitle'>" + elem.subTitle +"</span>" +
checkbox +
"</igc-list-item>";
i++;
}
return htmlContent;
}
private _data: EmployeesFlatDetails = null;
public get data(): EmployeesFlatDetails {
if (this._data == null)
{
this._data = new EmployeesFlatDetails();
}
return this._data;
}
}
new Sample();
ts<!DOCTYPE html>
<html>
<head>
<title>Sample | Ignite UI | Web Components | infragistics</title>
<meta charset="UTF-8" />
<link rel="shortcut icon" href="https://static.infragistics.com/xplatform/images/browsers/wc.png">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Kanit&display=swap" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Titillium Web" />
<link rel="stylesheet" href="https://static.infragistics.com/xplatform/css/samples/shared.v8.css" />
<link rel="stylesheet" href="/src/index.css" type="text/css" />
</head>
<body>
<div id="root">
<div class="container sample">
<div class="container fill">
<div class="grid_wrapper">
<igc-tree-grid column-selection="single" auto-generate="false" name="grid" id="grid" primary-key="ID" foreign-key="ParentID"
allow-advanced-filtering="true" allow-filtering="true">
<igc-paginator></igc-paginator>
<igc-grid-toolbar></igc-grid-toolbar>
<igc-column-group header="General Information" >
<igc-column field="Name" data-type="string" sortable="true" editable="true">
</igc-column>
<igc-column-group header="Personel Details" collapsible="true">
<igc-column field="ID" data-type="number" sortable="true" visible-when-collapsed="true">
</igc-column>
<igc-column field="Title" data-type="string" editable="true" sortable="true" visible-when-collapsed="false">
</igc-column>
<igc-column field="HireDate" data-type="date" editable="true" sortable="true" visible-when-collapsed="true">
</igc-column>
<igc-column field="Age" data-type="number" editable="true" sortable="true" visible-when-collapsed="false">
</igc-column>
</igc-column-group>
</igc-column-group>
<igc-column-group header="Address Information">
<igc-column-group header="Location" collapsible="true" expanded="false">
<igc-column field="Address" editable="true" data-type="string" sortable="true" visible-when-collapsed="true">
</igc-column>
<igc-column field="City" editable="true" data-type="string" sortable="true" visible-when-collapsed="false">
</igc-column>
<igc-column field="Country" editable="true" data-type="string" sortable="true" visible-when-collapsed="false">
</igc-column>
</igc-column-group>
<igc-column-group header="Contact Information">
<igc-column field="Fax" editable="true" data-type="string" sortable="true">
</igc-column>
<igc-column field="PostalCode" editable="true" header="Postal Code" data-type="string" sortable="true">
</igc-column>
<igc-column field="Phone" editable="true" data-type="string" sortable="true">
</igc-column>
</igc-column-group>
</igc-column-group>
</igc-tree-grid>
</div>
<div class="list_wrapper">
<igc-list id="list">
<span class="empty-list">
<h6>Use the native navigation of the browser until you reach some of the following grid
sections below:</h6>
<ul>
<li>Header</li>
<li>Body</li>
<li>Summary</li>
</ul>
<h6>When reached, an <b>action list</b> will be shown.</h6>
</span>
</div>
</div>
</div>
</div>
<!-- This script is needed only for parcel and it will be excluded for webpack -->
<% if (false) { %>
<script src="src/index.ts"></script>
<% } %>
</body>
</html>
html/* shared styles are loaded from: */
/* https://static.infragistics.com/xplatform/css/samples */
css
이 샘플이 마음에 드시나요? 당사의 완전한 Ignite UI for Web Components 툴킷에 액세스하여 몇 분 만에 나만의 앱을 빌드하기 시작하세요. 무료로 다운로드하세요.
사용자 정의 키보드 탐색
특정 키 또는 키 조합에 대한 기본 동작을 재정의하는 것은 키보드 탐색 기능이 제공하는 이점 중 하나입니다. 예를 들어 다음 셀이나 아래 셀로 이동하려면 Enter 또는 Tab 키를 누르세요. 이 탐색 시나리오나 다른 탐색 시나리오는 키보드 탐색 API를 통해 쉽게 달성할 수 있습니다.
API | 설명 | 인수 |
---|---|---|
GridKeydown |
위에서 설명한 키 누름/조합이 수행될 때 발생하는 이벤트입니다. 취소될 수 있습니다. 다른 키 누르기/조합의 경우 기본값을 사용하십시오.onkeydown 이벤트. |
IgcGridKeydownEventArgs |
ActiveNodeChange |
활성 노드가 변경될 때 발생하는 이벤트입니다. 이를 사용하여 활성 포커스 위치(헤더, tbody 등), 열 인덱스, 행 인덱스 또는 중첩 수준을 결정할 수 있습니다. | IgcActiveNodeChangeEventArgs |
NavigateTo |
제공된Rowindex andVisibleColumnIndex 를 기반으로 그리드의 위치로 이동합니다. 또한 type{ targetType: GridKeydownTargetType, target: Object } 의 param을 허용하는 콜백 함수를 통해 대상 요소에 대해 사용자 지정 논리를 실행할 수 있습니다. 사용법:grid.navigateTo(10, 3, (args) => { args.target.nativeElement.focus(); }); |
RowIndex: number, VisibleColumnIndex: number, callback: ({ targetType: GridKeydownTargetType, target: Object } ) => {} |
GetNextCell |
andICellPosition 로 다음 셀을 정의하는 object를RowIndex 반환합니다VisibleColumnIndex . 콜백 함수는 method의GetNextCell 세 번째 매개 변수로 전달될 수 있습니다. 콜백 함수는 매개 변수로 받아들이고IgcColumnComponent 주어진 기준이 충족되면 값 표시를 반환합니다boolean .const nextEditableCell = grid.getNextCell(0, 4, (col) => col.editable); |
currentRowIndex: number, currentVisibleColumnIndex: number, callback: (Column) => boolean |
GetPreviousCell |
이전 셀을 andICellPosition 로 정의하는 object를RowIndex 반환합니다VisibleColumnIndex . 콜백 함수는 method의GetPreviousCell 세 번째 매개 변수로 전달될 수 있습니다. 콜백 함수는 매개 변수로 받아들이고IgcColumnComponent 주어진 기준이 충족되면 값 표시를 반환합니다boolean .const prevEditableCell = grid.getPreviousCell(0, 4, (col) => col.editable); |
CurrentRowIndex: number, CurrentVisibleColumnIndex: number, callback: (Column) => boolean |
사용자 입력 유효성 검사 및 사용자 지정 탐색과 같은 일반적인 시나리오를 달성하는 방법을 보여주기 위해 API를 사용해 보겠습니다. 먼저 GridKeydown
이벤트에 대한 이벤트 핸들러를 등록해야 합니다.
<igc-tree-grid id="grid1" primary-key="ProductID">
</igc-tree-grid>
html
constructor() {
var grid = this.grid = document.getElementById('grid') as IgcTreeGridComponent;
grid.data = this.data
grid.addEventListener("gridKeydown", this.customKeydown);
}
ts
이벤트 인수 값을 기반으로 우리는 자체 논리를 제공할 수 있는 두 가지 사례를 식별했습니다(위 참조). 이제 API의 메서드를 사용하여 원하는 작업을 수행해 보겠습니다. 사용자가 편집 모드에서 셀 위에서 Tab 키를 누르면 입력에 대한 유효성 검사가 수행됩니다. 사용자가 셀 위에서 Enter 키를 누르면 초점이 다음 행의 셀로 이동됩니다.
// 1. USER INPUT VALIDATION ON TAB
if (target.column.dataType === 'number' && target.editValue < 10) {
// alert the user that the input is invalid
return;
}
// 2. CUSTOM NAVIGATION ON ENTER KEY PRESS
this.grid1.navigateTo(target.row.index + 1, target.column.visibleIndex, (obj) => {
obj.target.activate();
});
typescript
전체 구현 세부정보는 샘플 코드를 참조하세요.
아래 데모를 사용하여 방금 구현한 사용자 지정 시나리오를 시험해 보세요.
- 숫자 열의 셀을 두 번 클릭하거나 F2 키를 누르고 값을 7로 변경한 다음 Tab 키를 누릅니다. 프롬프트 메시지가 표시됩니다.
- 셀을 선택하고 Enter 키를 두 번 누릅니다. 키를 누를 때마다 동일한 열 아래의 다음 행에 있는 셀로 초점이 이동합니다.
데모
export class EmployeesNestedDataItem {
public constructor(init: Partial<EmployeesNestedDataItem>) {
Object.assign(this, init);
}
public ID: number;
public Age: number;
public Salary: number;
public Productivity: number;
public City: string;
public Country: string;
public Phone: string;
public HireDate: string;
public Name: string;
public Title: string;
public Employees: EmployeesNestedDataItem_EmployeesItem[];
}
export class EmployeesNestedDataItem_EmployeesItem {
public constructor(init: Partial<EmployeesNestedDataItem_EmployeesItem>) {
Object.assign(this, init);
}
public Age: number;
public Salary: number;
public Productivity: number;
public City: string;
public Country: string;
public Phone: string;
public HireDate: string;
public ID: number;
public Name: string;
public Title: string;
}
export class EmployeesNestedData extends Array<EmployeesNestedDataItem> {
public constructor(items: Array<EmployeesNestedDataItem> | number = -1) {
if (Array.isArray(items)) {
super(...items);
} else {
const newItems = [
new EmployeesNestedDataItem(
{
ID: 1,
Age: 55,
Salary: 80000,
Productivity: 90,
City: `Berlin`,
Country: `Germany`,
Phone: `609-202-505`,
HireDate: `2008-03-20`,
Name: `John Winchester`,
Title: `Development Manager`,
Employees: [
new EmployeesNestedDataItem_EmployeesItem(
{
Age: 43,
Salary: 70000,
Productivity: 80,
City: `Hamburg`,
Country: `Germany`,
Phone: `609-444-555`,
HireDate: `2011-06-03`,
ID: 3,
Name: `Michael Burke`,
Title: `Senior Software Developer`
}),
new EmployeesNestedDataItem_EmployeesItem(
{
Age: 29,
Salary: 60000,
Productivity: 80,
City: `Munich`,
Country: `Germany`,
Phone: `609-333-444`,
HireDate: `2009-06-19`,
ID: 2,
Name: `Thomas Anderson`,
Title: `Senior Software Developer`
}),
new EmployeesNestedDataItem_EmployeesItem(
{
Age: 31,
Salary: 90000,
Productivity: 80,
City: `Warasw`,
Country: `Poland`,
Phone: `609-222-205`,
HireDate: `2014-08-18`,
ID: 11,
Name: `Monica Reyes`,
Title: `Software Development Team Lead`
}),
new EmployeesNestedDataItem_EmployeesItem(
{
Age: 35,
Salary: 70000,
Productivity: 70,
City: `Koln`,
Country: `Germany`,
Phone: `609-502-525`,
HireDate: `2015-09-17`,
ID: 6,
Name: `Roland Mendel`,
Title: `Senior Software Developer`
})]
}),
new EmployeesNestedDataItem(
{
ID: 4,
Age: 42,
Salary: 90000,
Productivity: 80,
City: `Kielce`,
Country: `Poland`,
Phone: `609-202-505`,
HireDate: `2014-01-22`,
Name: `Ana Sanders`,
Title: `CEO`,
Employees: [
new EmployeesNestedDataItem_EmployeesItem(
{
Age: 44,
Salary: 80000,
Productivity: 80,
City: `Warasw`,
Country: `Poland`,
Phone: `609-202-505`,
HireDate: `2014-04-04`,
ID: 14,
Name: `Laurence Johnson`,
Title: `Director`
}),
new EmployeesNestedDataItem_EmployeesItem(
{
Age: 25,
Salary: 85000,
Productivity: 55,
City: `Paris`,
Country: `France`,
Phone: `609-202-505`,
HireDate: `2017-11-09`,
ID: 5,
Name: `Elizabeth Richards`,
Title: `Vice President`
}),
new EmployeesNestedDataItem_EmployeesItem(
{
Age: 39,
Salary: 88000,
Productivity: 88,
City: `London`,
Country: `UK`,
Phone: `609-202-505`,
HireDate: `2010-03-22`,
ID: 13,
Name: `Trevor Ashworth`,
Title: `Director`
})]
}),
new EmployeesNestedDataItem(
{
ID: 18,
Age: 49,
Salary: 77000,
Productivity: 70,
City: `Manchester`,
Country: `UK`,
Phone: `222-555-577`,
HireDate: `2014-01-22`,
Name: `Victoria Lincoln`,
Title: `Senior Accountant`,
Employees: [
new EmployeesNestedDataItem_EmployeesItem(
{
Age: 43,
Salary: 70000,
Productivity: 80,
City: `Hamburg`,
Country: `Germany`,
Phone: `609-444-555`,
HireDate: `2011-06-03`,
ID: 23,
Name: `Thomas Burke`,
Title: `Senior Accountant`
}),
new EmployeesNestedDataItem_EmployeesItem(
{
Age: 29,
Salary: 60000,
Productivity: 80,
City: `Munich`,
Country: `Germany`,
Phone: `609-333-444`,
HireDate: `2009-06-19`,
ID: 22,
Name: `Michael Anderson`,
Title: `Junior Accountant`
}),
new EmployeesNestedDataItem_EmployeesItem(
{
Age: 31,
Salary: 90000,
Productivity: 80,
City: `Warasw`,
Country: `Poland`,
Phone: `609-222-205`,
HireDate: `2014-08-18`,
ID: 21,
Name: `Roland Reyes`,
Title: `Accountant Team Lead`
}),
new EmployeesNestedDataItem_EmployeesItem(
{
Age: 35,
Salary: 70000,
Productivity: 70,
City: `Koln`,
Country: `Germany`,
Phone: `609-502-525`,
HireDate: `2015-09-17`,
ID: 24,
Name: `Monica Mendel`,
Title: `Senior Software Developer`
})]
}),
new EmployeesNestedDataItem(
{
ID: 10,
Age: 61,
Salary: 85000,
Productivity: 890,
City: `Lyon`,
Country: `France`,
Phone: `259-266-887`,
HireDate: `2010-01-01`,
Name: `Yang Wang`,
Title: `Localization Developer`,
Employees: [
new EmployeesNestedDataItem_EmployeesItem(
{
Age: 31,
Salary: 90000,
Productivity: 80,
City: `Warasw`,
Country: `Poland`,
Phone: `609-222-205`,
HireDate: `2014-08-18`,
ID: 11,
Name: `Monica Reyes`,
Title: `Software Development Team Lead`
}),
new EmployeesNestedDataItem_EmployeesItem(
{
Age: 35,
Salary: 70000,
Productivity: 70,
City: `Koln`,
Country: `Germany`,
Phone: `609-502-525`,
HireDate: `2015-09-17`,
ID: 6,
Name: `Roland Mendel`,
Title: `Senior Software Developer`
})]
}),
new EmployeesNestedDataItem(
{
ID: 35,
Age: 35,
Salary: 75000,
Productivity: 75,
City: `Warasw`,
Country: `Poland`,
Phone: `688-244-844`,
HireDate: `2014-01-22`,
Name: `Janine Munoz`,
Title: `HR`,
Employees: [
new EmployeesNestedDataItem_EmployeesItem(
{
Age: 43,
Salary: 70000,
Productivity: 80,
City: `Hamburg`,
Country: `Germany`,
Phone: `609-444-555`,
HireDate: `2011-06-03`,
ID: 3,
Name: `Michael Burke`,
Title: `Senior Software Developer`
}),
new EmployeesNestedDataItem_EmployeesItem(
{
Age: 31,
Salary: 90000,
Productivity: 80,
City: `Warasw`,
Country: `Poland`,
Phone: `609-222-205`,
HireDate: `2014-08-18`,
ID: 11,
Name: `Monica Reyes`,
Title: `Software Development Team Lead`
})]
}),
new EmployeesNestedDataItem(
{
ID: 10,
Age: 49,
Salary: 95000,
Productivity: 80,
City: `Krakow`,
Country: `Poland`,
Phone: `677-266-555`,
HireDate: `2010-01-01`,
Name: `Yang Wang`,
Title: `Sales Manager`,
Employees: [
new EmployeesNestedDataItem_EmployeesItem(
{
Age: 29,
Salary: 60000,
Productivity: 80,
City: `Munich`,
Country: `Germany`,
Phone: `609-333-444`,
HireDate: `2009-06-19`,
ID: 2,
Name: `Thomas Anderson`,
Title: `Senior Software Developer`
}),
new EmployeesNestedDataItem_EmployeesItem(
{
Age: 35,
Salary: 70000,
Productivity: 70,
City: `Koln`,
Country: `Germany`,
Phone: `609-502-525`,
HireDate: `2015-09-17`,
ID: 6,
Name: `Roland Mendel`,
Title: `Senior Software Developer`
})]
}),
];
super(...newItems.slice(0));
}
}
}
tsimport { IgcPropertyEditorPanelModule } from 'igniteui-webcomponents-layouts';
import 'igniteui-webcomponents-grids/grids/combined';
import { ComponentRenderer, PropertyEditorPanelDescriptionModule, WebTreeGridDescriptionModule } from 'igniteui-webcomponents-core';
import { IgcTreeGridComponent } from 'igniteui-webcomponents-grids/grids';
import { EmployeesNestedDataItem, EmployeesNestedDataItem_EmployeesItem, EmployeesNestedData } from './EmployeesNestedData';
import { IgcGridComponent, IgcGridKeydownEventArgs, GridKeydownTargetType } from 'igniteui-webcomponents-grids/grids';
import "igniteui-webcomponents-grids/grids/themes/light/bootstrap.css";
import { ModuleManager } from 'igniteui-webcomponents-core';
import "./index.css";
ModuleManager.register(
IgcPropertyEditorPanelModule
);
export class Sample {
private treeGrid: IgcTreeGridComponent
private _bind: () => void;
constructor() {
var treeGrid = this.treeGrid = document.getElementById('treeGrid') as IgcTreeGridComponent;
this.webGridCustomKBNav = this.webGridCustomKBNav.bind(this);
this._bind = () => {
treeGrid.data = this.employeesNestedData;
treeGrid.addEventListener("gridKeydown", this.webGridCustomKBNav);
}
this._bind();
}
private _employeesNestedData: EmployeesNestedData = null;
public get employeesNestedData(): EmployeesNestedData {
if (this._employeesNestedData == null)
{
this._employeesNestedData = new EmployeesNestedData();
}
return this._employeesNestedData;
}
private _componentRenderer: ComponentRenderer = null;
public get renderer(): ComponentRenderer {
if (this._componentRenderer == null) {
this._componentRenderer = new ComponentRenderer();
var context = this._componentRenderer.context;
PropertyEditorPanelDescriptionModule.register(context);
WebTreeGridDescriptionModule.register(context);
}
return this._componentRenderer;
}
public webGridCustomKBNav(evtArgs: CustomEvent<IgcGridKeydownEventArgs>): void {
const args = evtArgs.detail;
const target = args.target;
const evt = args.event;
const type = args.targetType;
var grid = this.treeGrid as any;
if (type === "dataCell" && target.editMode && (evt as any).key.toLowerCase() === 'tab') {
// Value validation for number column.
// This covers both 'tab' and 'shift+tab' key interactions.
args.event.preventDefault();
args.cancel = true;
if (target.column.dataType === 'number' && target.editValue < 10) {
alert('The value should be bigger than 10');
return;
}
const cell = (evt as any).shiftKey ?
grid.getPreviousCell(target.row.index, target.column.visibleIndex, (col: any) => col.editable) :
grid.getNextCell(target.row.index, target.column.visibleIndex, (col: any) => col.editable);
grid.navigateTo(cell.rowIndex, cell.visibleColumnIndex,
(obj: any) => { obj.target.activate(); });
} else if (type === "dataCell" && (evt as any).key.toLowerCase() === 'enter') {
// Perform column based kb navigation with 'enter' key press
args.cancel = true;
grid.navigateTo(target.row.index + 1, target.column.visibleIndex, (obj: any) => {
obj.target.activate();
});
}
}
}
new Sample();
ts<!DOCTYPE html>
<html>
<head>
<title>Sample | Ignite UI | Web Components | infragistics</title>
<meta charset="UTF-8" />
<link rel="shortcut icon" href="https://static.infragistics.com/xplatform/images/browsers/wc.png" >
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Kanit&display=swap" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Titillium Web" />
<link rel="stylesheet" href="https://static.infragistics.com/xplatform/css/samples/shared.v8.css" />
<link rel="stylesheet" href="/src/index.css" type="text/css" />
</head>
<body>
<div id="root">
<div class="container sample ig-typography">
<div class="container fill">
<igc-tree-grid
auto-generate="false"
name="treeGrid"
id="treeGrid"
id="treeGrid"
child-data-key="Employees"
row-selection="multiple"
moving="true"
allow-filtering="true">
<igc-paginator
per-page="15">
</igc-paginator>
<igc-column
field="Name"
header="Name"
data-type="string"
sortable="true"
editable="true"
resizable="true">
</igc-column>
<igc-column
field="HireDate"
header="Hire Date"
data-type="date"
sortable="true"
editable="true"
resizable="true">
</igc-column>
<igc-column
field="Age"
header="Age"
data-type="number"
sortable="true"
editable="true"
resizable="true">
</igc-column>
</igc-tree-grid>
</div>
</div>
</div>
<!-- This script is needed only for parcel and it will be excluded for webpack -->
<% if (false) { %><script src="src/index.ts"></script><% } %>
</body>
</html>
html/* shared styles are loaded from: */
/* https://static.infragistics.com/xplatform/css/samples */
css
알려진 제한 사항
한정 | 설명 |
---|---|
스크롤 가능한 상위 컨테이너를 사용하여 그리드 내부를 탐색합니다. | 그리드가 스크롤 가능한 상위 컨테이너 내부에 배치되고 사용자가 보이지 않는 그리드 셀로 이동하는 경우 상위 컨테이너는 스크롤되지 않습니다. |
추가 리소스
우리 커뮤니티는 활동적이며 항상 새로운 아이디어를 환영합니다.