@@ -27,7 +27,26 @@ class HTMLCodeBlockElement extends HTMLElement {
27
27
}
28
28
return endgine . highlightAuto ( src ) ;
29
29
}
30
- #shadowRoot;
30
+ #slots = ( ( ) => {
31
+ /**
32
+ * @param name - The value of name attribute for the slot element
33
+ * @returns - The slot element
34
+ */
35
+ const mkslot = ( name , id ) => {
36
+ const slot = document . createElement ( 'slot' ) ;
37
+ slot . name = name ;
38
+ if ( id ) {
39
+ slot . id = id ;
40
+ }
41
+ return slot ;
42
+ } ;
43
+ return {
44
+ name : mkslot ( 'name' , 'name' ) ,
45
+ copyButton : mkslot ( 'copy-button' ) ,
46
+ code : mkslot ( 'code' ) ,
47
+ } ;
48
+ } ) ( ) ;
49
+ #a11yName;
31
50
#codeBlock;
32
51
#codeWrap;
33
52
/** Actual value of the accessor `value` */
@@ -44,13 +63,16 @@ class HTMLCodeBlockElement extends HTMLElement {
44
63
return ;
45
64
}
46
65
/** The resulting syntax-highlighted markup */
47
- const markup = HTMLCodeBlockElement . highlight ( this . #value, {
66
+ const { value : markup } = HTMLCodeBlockElement . highlight ( this . #value, {
48
67
language : this . #language,
49
- } ) . value ;
68
+ } ) ;
50
69
// initialize
51
70
this . textContent = '' ;
71
+ this . #a11yName. textContent = this . #label;
72
+ this . #slots. name . hidden = ! this . #label;
52
73
this . #codeBlock. textContent = '' ;
53
74
this . #codeBlock. insertAdjacentHTML ( 'afterbegin' , markup ) ;
75
+ this . append ( this . #a11yName) ;
54
76
this . append ( this . #codeWrap) ;
55
77
} ;
56
78
/** @returns - Syntax Highlighted Source Code */
@@ -68,14 +90,14 @@ class HTMLCodeBlockElement extends HTMLElement {
68
90
get label ( ) {
69
91
return this . #label;
70
92
}
71
- set label ( name ) {
72
- // TODO: Accessiblity Treeにアクセシブルネームを提供する
73
- this . #label = name || '' ;
74
- if ( this . #label) {
75
- this . setAttribute ( 'label' , name ) ;
93
+ set label ( value ) {
94
+ if ( value === null ) {
95
+ this . #label = '' ;
96
+ this . removeAttribute ( 'label' ) ;
76
97
}
77
98
else {
78
- this . removeAttribute ( 'label' ) ;
99
+ this . #label = String ( value ) ;
100
+ this . setAttribute ( 'label' , this . #label) ;
79
101
}
80
102
this . #render( ) ;
81
103
}
@@ -86,13 +108,14 @@ class HTMLCodeBlockElement extends HTMLElement {
86
108
get language ( ) {
87
109
return this . #language;
88
110
}
89
- set language ( name ) {
90
- this . #language = name || '' ;
91
- if ( this . #language) {
92
- this . setAttribute ( 'language' , name ) ;
111
+ set language ( value ) {
112
+ if ( value === null ) {
113
+ this . #language = '' ;
114
+ this . removeAttribute ( 'language' ) ;
93
115
}
94
116
else {
95
- this . removeAttribute ( 'language' ) ;
117
+ this . #language = String ( value ) ;
118
+ this . setAttribute ( 'language' , this . #language) ;
96
119
}
97
120
this . #render( ) ;
98
121
}
@@ -103,9 +126,9 @@ class HTMLCodeBlockElement extends HTMLElement {
103
126
get controls ( ) {
104
127
return this . #controls;
105
128
}
106
- set controls ( flag ) {
107
- // TODO: コピーボタン、ラベルの表示切り替え
108
- this . #controls = flag ;
129
+ set controls ( value ) {
130
+ // TODO: コピーボタンの表示切り替え
131
+ this . #controls = value ;
109
132
if ( this . #controls) {
110
133
this . setAttribute ( 'controls' , '' ) ;
111
134
}
@@ -131,7 +154,7 @@ class HTMLCodeBlockElement extends HTMLElement {
131
154
// string
132
155
case 'label' :
133
156
case 'language' :
134
- this [ attrName ] = newValue || '' ;
157
+ this [ attrName ] = newValue ;
135
158
break ;
136
159
// boolean
137
160
case 'controls' :
@@ -143,37 +166,61 @@ class HTMLCodeBlockElement extends HTMLElement {
143
166
}
144
167
constructor ( ) {
145
168
super ( ) ;
169
+ /* -------------------------------------------------------------------------
170
+ * Setup Shadow DOM contents
171
+ * ---------------------------------------------------------------------- */
146
172
/**
147
- * @param name - The value of name attribute for the slot element
148
- * @returns - The slot element
173
+ * The container of minimum text that will be read even
174
+ * if the accessible name (label attribute value) is omitted.
149
175
*/
150
- const mkslot = ( name ) => {
151
- const slot = document . createElement ( 'slot' ) ;
152
- slot . name = name ;
153
- return slot ;
154
- } ;
155
- const slots = [
156
- mkslot ( 'label' ) ,
157
- mkslot ( 'copy-button' ) ,
158
- mkslot ( 'code' ) ,
159
- ] ;
160
- const pre = document . createElement ( 'pre' ) ;
161
- const code = document . createElement ( 'code' ) ;
162
- code . tabIndex = 0 ;
163
- code . className = 'hljs' ; // TODO: Make it variable
164
- pre . slot = 'code' ;
165
- pre . append ( code ) ;
166
- // Hard private props initialize
167
- this . #value = ( this . textContent || '' ) . replace ( / ^ \n / , '' ) ;
168
- this . #label = this . getAttribute ( 'label' ) || '' ;
169
- this . #language = this . getAttribute ( 'language' ) || '' ;
170
- this . #controls = this . getAttribute ( 'controls' ) !== null ;
171
- this . #shadowRoot = this . attachShadow ( {
176
+ const a11yNamePrefix = ( ( ) => {
177
+ const span = document . createElement ( 'span' ) ;
178
+ span . id = 'semantics' ;
179
+ span . hidden = true ;
180
+ span . textContent = 'Code Block' ;
181
+ return span ;
182
+ } ) ( ) ;
183
+ /** Container of accessible names (label attribute values). */
184
+ const a11yName = ( ( ) => {
185
+ const span = document . createElement ( 'span' ) ;
186
+ span . slot = 'name' ;
187
+ span . textContent = this . getAttribute ( 'label' ) || '' ;
188
+ return span ;
189
+ } ) ( ) ;
190
+ const codeElm = ( ( ) => {
191
+ const code = document . createElement ( 'code' ) ;
192
+ code . tabIndex = 0 ;
193
+ code . className = 'hljs' ; // TODO: Make it variable
194
+ return code ;
195
+ } ) ( ) ;
196
+ const preElm = ( ( ) => {
197
+ const pre = document . createElement ( 'pre' ) ;
198
+ pre . slot = 'code' ;
199
+ pre . append ( codeElm ) ;
200
+ return pre ;
201
+ } ) ( ) ;
202
+ const container = ( ( ) => {
203
+ const div = document . createElement ( 'div' ) ;
204
+ div . append ( ...Object . values ( this . #slots) ) ;
205
+ div . setAttribute ( 'role' , 'group' ) ;
206
+ div . setAttribute ( 'aria-labelledby' , 'semantics name' ) ;
207
+ return div ;
208
+ } ) ( ) ;
209
+ const shadowRoot = this . attachShadow ( {
172
210
mode : 'closed' ,
173
211
} ) ;
174
- this . #codeBlock = code ;
175
- this . #codeWrap = pre ;
176
- this . #shadowRoot. append ( ...slots ) ;
212
+ shadowRoot . append ( a11yNamePrefix ) ;
213
+ shadowRoot . append ( container ) ;
214
+ /* -------------------------------------------------------------------------
215
+ * Hard private props initialize
216
+ * ---------------------------------------------------------------------- */
217
+ this . #value = ( this . textContent || '' ) . replace ( / ^ \n / , '' ) . replace ( / \n $ / , '' ) ;
218
+ this . #label = a11yName . textContent || '' ;
219
+ this . #language = this . getAttribute ( 'language' ) || '' ;
220
+ this . #controls = this . getAttribute ( 'controls' ) !== null ;
221
+ this . #a11yName = a11yName ;
222
+ this . #codeBlock = codeElm ;
223
+ this . #codeWrap = preElm ;
177
224
}
178
225
}
179
226
exports . default = HTMLCodeBlockElement ;
0 commit comments