11import TableExtension from "./TableExtension.js" ;
2- import I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js" ;
3- import { getTabbableElements } from "@ui5/webcomponents-base/dist/util/TabbableElements.js" ;
4- import type { AccessibilityInfo } from "@ui5/webcomponents-base" ;
2+ import { updateInvisibleText , getAccessibilityDescription } from "./CustomAnnouncement.js" ;
53import type Table from "./Table.js" ;
64import type TableRow from "./TableRow.js" ;
75import type TableCell from "./TableCell.js" ;
@@ -14,112 +12,8 @@ import {
1412 TABLE_ROW_NAVIGABLE ,
1513 TABLE_ROW_NAVIGATED ,
1614 TABLE_COLUMN_HEADER_ROW ,
17- TABLE_CELL_SINGLE_CONTROL ,
18- TABLE_CELL_MULTIPLE_CONTROLS ,
19- TABLE_ACC_STATE_EMPTY ,
20- TABLE_ACC_STATE_REQUIRED ,
21- TABLE_ACC_STATE_DISABLED ,
22- TABLE_ACC_STATE_READONLY ,
2315} from "./generated/i18n/i18n-defaults.js" ;
2416
25- let invisibleText : HTMLElement ;
26- const i18nBundle = new I18nBundle ( "@ui5/webcomponents" ) ;
27-
28- const checkVisibility = ( element : HTMLElement ) : boolean => {
29- return element . checkVisibility ( ) || getComputedStyle ( element ) . display === "contents" ;
30- } ;
31-
32- const updateInvisibleText = ( element : any , text : string | string [ ] = [ ] ) => {
33- if ( ! invisibleText || ! invisibleText . isConnected ) {
34- invisibleText = document . createElement ( "span" ) ;
35- invisibleText . id = "ui5-table-invisible-text" ;
36- invisibleText . ariaHidden = "true" ;
37- invisibleText . style . display = "none" ;
38- document . body . appendChild ( invisibleText ) ;
39- }
40-
41- const ariaLabelledByElements = [ ...( element . ariaLabelledByElements || [ ] ) ] ;
42- const invisibleTextIndex = ariaLabelledByElements . indexOf ( invisibleText ) ;
43- text = Array . isArray ( text ) ? text . filter ( Boolean ) . join ( " . " ) . trim ( ) : text . trim ( ) ;
44- invisibleText . textContent = text ;
45-
46- if ( text && invisibleTextIndex === - 1 ) {
47- ariaLabelledByElements . unshift ( invisibleText ) ;
48- element . ariaLabelledByElements = ariaLabelledByElements ;
49- } else if ( ! text && invisibleTextIndex > - 1 ) {
50- ariaLabelledByElements . splice ( invisibleTextIndex , 1 ) ;
51- element . ariaLabelledByElements = ariaLabelledByElements . length ? ariaLabelledByElements : null ;
52- }
53- } ;
54-
55- const getAccessibilityDescription = ( element : Node , lessDetails : boolean = false , _isRootElement : boolean = true ) : string => {
56- if ( ! element ) {
57- return "" ;
58- }
59-
60- if ( element . nodeType === Node . TEXT_NODE ) {
61- return ( element as Text ) . data . trim ( ) ;
62- }
63-
64- if ( ! ( element instanceof HTMLElement ) ) {
65- return "" ;
66- }
67-
68- if ( element . hasAttribute ( "data-ui5-table-acc-text" ) ) {
69- return element . getAttribute ( "data-ui5-table-acc-text" ) || "" ;
70- }
71-
72- if ( element . ariaHidden === "true" || ! checkVisibility ( element ) ) {
73- return _isRootElement ? i18nBundle . getText ( TABLE_ACC_STATE_EMPTY ) : "" ;
74- }
75-
76- let childNodes = [ ] as Array < Node > ;
77- const descriptions = [ ] as Array < string > ;
78- const accessibilityInfo = ( element as any ) . accessibilityInfo as AccessibilityInfo | undefined ;
79- if ( accessibilityInfo ) {
80- const {
81- type, description, required, disabled, readonly, children,
82- } = accessibilityInfo ;
83-
84- childNodes = children || [ ] ;
85- type && descriptions . push ( type ) ;
86- description && descriptions . push ( description ) ;
87-
88- if ( ! lessDetails ) {
89- required && descriptions . push ( i18nBundle . getText ( TABLE_ACC_STATE_REQUIRED ) ) ;
90- disabled && descriptions . push ( i18nBundle . getText ( TABLE_ACC_STATE_DISABLED ) ) ;
91- readonly && descriptions . push ( i18nBundle . getText ( TABLE_ACC_STATE_READONLY ) ) ;
92- }
93- } else if ( element . localName === "slot" ) {
94- childNodes = ( element as HTMLSlotElement ) . assignedNodes ( { flatten : true } ) ;
95- } else {
96- childNodes = element . shadowRoot ? [ ...element . shadowRoot . childNodes ] : [ ...element . childNodes ] ;
97- }
98-
99- childNodes . forEach ( child => {
100- const childDescription = getAccessibilityDescription ( child , lessDetails , false ) ;
101- childDescription && descriptions . push ( childDescription ) ;
102- } ) ;
103-
104- if ( _isRootElement ) {
105- const hasDescription = descriptions . length > 0 ;
106- if ( ! hasDescription || ! lessDetails ) {
107- const tabbables = getTabbableElements ( element ) ;
108- const bundleKey = [
109- hasDescription ? "" : TABLE_ACC_STATE_EMPTY ,
110- TABLE_CELL_SINGLE_CONTROL ,
111- TABLE_CELL_MULTIPLE_CONTROLS ,
112- ] [ Math . min ( tabbables . length , 2 ) ] ;
113- if ( bundleKey ) {
114- hasDescription && descriptions . push ( "." ) ;
115- descriptions . push ( i18nBundle . getText ( bundleKey ) ) ;
116- }
117- }
118- }
119-
120- return descriptions . join ( " " ) . trim ( ) ;
121- } ;
122-
12317/**
12418 * Handles the custom announcement for the ui5-table.
12519 *
@@ -135,6 +29,10 @@ class TableCustomAnnouncement extends TableExtension {
13529 this . _table = table ;
13630 }
13731
32+ private get i18nBundle ( ) {
33+ return ( this . _table . constructor as typeof Table ) . i18nBundle ;
34+ }
35+
13836 _onfocusin ( e : FocusEvent , eventOrigin : HTMLElement ) {
13937 const tableAttribute = this . _tableAttributes . find ( attr => eventOrigin . hasAttribute ( attr ) ) ;
14038 if ( ! tableAttribute ) {
@@ -163,15 +61,15 @@ class TableCustomAnnouncement extends TableExtension {
16361
16462 _handleTableHeaderRowFocusin ( headerRow : TableHeaderRow ) {
16563 const descriptions = [
166- i18nBundle . getText ( TABLE_COLUMN_HEADER_ROW ) ,
64+ this . i18nBundle . getText ( TABLE_COLUMN_HEADER_ROW ) ,
16765 ] ;
16866
16967 if ( headerRow . _hasSelector ) {
17068 descriptions . push ( headerRow . _isMultiSelect ? headerRow . _selectionCellAriaDescription ! : headerRow . _i18nSelection ) ;
17169 }
17270
17371 headerRow . _visibleCells . forEach ( headerCell => {
174- const cellDescription = getAccessibilityDescription ( headerCell , true ) ;
72+ const cellDescription = getAccessibilityDescription ( headerCell , { lessDetails : true } ) ;
17573 descriptions . push ( cellDescription ) ;
17674 } ) ;
17775
@@ -188,25 +86,25 @@ class TableCustomAnnouncement extends TableExtension {
18886 }
18987
19088 const descriptions = [
191- i18nBundle . getText ( TABLE_ROW ) ,
192- i18nBundle . getText ( TABLE_ROW_INDEX , row . ariaRowIndex ! , this . _table . _ariaRowCount ) ,
89+ this . i18nBundle . getText ( TABLE_ROW ) ,
90+ this . i18nBundle . getText ( TABLE_ROW_INDEX , row . ariaRowIndex ! , this . _table . _ariaRowCount ) ,
19391 ] ;
19492
19593 if ( row . _isSelected ) {
196- descriptions . push ( i18nBundle . getText ( TABLE_ROW_SELECTED ) ) ;
94+ descriptions . push ( this . i18nBundle . getText ( TABLE_ROW_SELECTED ) ) ;
19795 }
19896
19997 if ( row . _isNavigable ) {
200- descriptions . push ( i18nBundle . getText ( TABLE_ROW_NAVIGABLE ) ) ;
98+ descriptions . push ( this . i18nBundle . getText ( TABLE_ROW_NAVIGABLE ) ) ;
20199 } else if ( row . interactive ) {
202- descriptions . push ( i18nBundle . getText ( TABLE_ROW_ACTIVE ) ) ;
100+ descriptions . push ( this . i18nBundle . getText ( TABLE_ROW_ACTIVE ) ) ;
203101 }
204102
205103 const cells = [ ...row . _visibleCells , ...row . _popinCells ] ;
206104 cells . flatMap ( cell => {
207105 return cell . _popin ? [ cell . _popinHeader ! , cell . _popinContent ! ] : [ cell . _headerCell ! , cell ] ;
208106 } ) . forEach ( node => {
209- const nodeDescription = getAccessibilityDescription ( node , true ) ;
107+ const nodeDescription = getAccessibilityDescription ( node , { lessDetails : true } ) ;
210108 descriptions . push ( nodeDescription ) ;
211109 } ) ;
212110
@@ -215,7 +113,7 @@ class TableCustomAnnouncement extends TableExtension {
215113 }
216114
217115 if ( row . _renderNavigated && row . navigated ) {
218- descriptions . push ( i18nBundle . getText ( TABLE_ROW_NAVIGATED ) ) ;
116+ descriptions . push ( this . i18nBundle . getText ( TABLE_ROW_NAVIGATED ) ) ;
219117 }
220118
221119 updateInvisibleText ( row , descriptions ) ;
0 commit comments