@@ -39,7 +39,30 @@ export default class HTMLCodeBlockElement extends HTMLElement {
3939 return endgine . highlightAuto ( src ) ;
4040 }
4141
42- #shadowRoot: ShadowRoot ;
42+ #slots = ( ( ) => {
43+ /**
44+ * @param name - The value of name attribute for the slot element
45+ * @returns - The slot element
46+ */
47+ const mkslot = ( name : string , id ?: string ) => {
48+ const slot = document . createElement ( 'slot' ) ;
49+
50+ slot . name = name ;
51+
52+ if ( id ) {
53+ slot . id = id ;
54+ }
55+
56+ return slot ;
57+ } ;
58+
59+ return {
60+ name : mkslot ( 'name' , 'name' ) ,
61+ copyButton : mkslot ( 'copy-button' ) ,
62+ code : mkslot ( 'code' ) ,
63+ } ;
64+ } ) ( ) ;
65+ #a11yName: HTMLElement ;
4366 #codeBlock: HTMLElement ;
4467 #codeWrap: HTMLPreElement ;
4568 /** Actual value of the accessor `value` */
@@ -50,22 +73,24 @@ export default class HTMLCodeBlockElement extends HTMLElement {
5073 #language: string = '' ;
5174 /** Actual value of the accessor `controls` */
5275 #controls: boolean = false ;
53-
5476 /** Outputs the resulting syntax-highlighted markup to the DOM. */
5577 #render = function ( this : HTMLCodeBlockElement ) {
5678 if ( ! this . parentNode ) {
5779 return ;
5880 }
5981
6082 /** The resulting syntax-highlighted markup */
61- const markup = HTMLCodeBlockElement . highlight ( this . #value, {
83+ const { value : markup } = HTMLCodeBlockElement . highlight ( this . #value, {
6284 language : this . #language,
63- } ) . value ;
85+ } ) ;
6486
6587 // initialize
6688 this . textContent = '' ;
89+ this . #a11yName. textContent = this . #label;
90+ this . #slots. name . hidden = ! this . #label;
6791 this . #codeBlock. textContent = '' ;
6892 this . #codeBlock. insertAdjacentHTML ( 'afterbegin' , markup ) ;
93+ this . append ( this . #a11yName) ;
6994 this . append ( this . #codeWrap) ;
7095 }
7196
@@ -87,14 +112,13 @@ export default class HTMLCodeBlockElement extends HTMLElement {
87112 return this . #label;
88113 }
89114
90- set label ( name : string ) {
91- // TODO: Accessiblity Treeにアクセシブルネームを提供する
92- this . #label = name || '' ;
93-
94- if ( this . #label) {
95- this . setAttribute ( 'label' , name ) ;
96- } else {
115+ set label ( value : string ) {
116+ if ( value === null ) {
117+ this . #label = '' ;
97118 this . removeAttribute ( 'label' ) ;
119+ } else {
120+ this . #label = String ( value ) ;
121+ this . setAttribute ( 'label' , this . #label) ;
98122 }
99123
100124 this . #render( ) ;
@@ -108,13 +132,13 @@ export default class HTMLCodeBlockElement extends HTMLElement {
108132 return this . #language;
109133 }
110134
111- set language ( name : string ) {
112- this . #language = name || '' ;
113-
114- if ( this . #language) {
115- this . setAttribute ( 'language' , name ) ;
116- } else {
135+ set language ( value : any ) {
136+ if ( value === null ) {
137+ this . #language = '' ;
117138 this . removeAttribute ( 'language' ) ;
139+ } else {
140+ this . #language = String ( value ) ;
141+ this . setAttribute ( 'language' , this . #language) ;
118142 }
119143
120144 this . #render( ) ;
@@ -128,9 +152,9 @@ export default class HTMLCodeBlockElement extends HTMLElement {
128152 return this . #controls;
129153 }
130154
131- set controls ( flag : boolean ) {
132- // TODO: コピーボタン、ラベルの表示切り替え
133- this . #controls = flag ;
155+ set controls ( value : boolean ) {
156+ // TODO: コピーボタンの表示切り替え
157+ this . #controls = value ;
134158
135159 if ( this . #controls) {
136160 this . setAttribute ( 'controls' , '' ) ;
@@ -164,7 +188,7 @@ export default class HTMLCodeBlockElement extends HTMLElement {
164188 // string
165189 case 'label' :
166190 case 'language' :
167- this [ attrName ] = newValue || '' ;
191+ this [ attrName ] = newValue ;
168192
169193 break ;
170194
@@ -181,41 +205,74 @@ export default class HTMLCodeBlockElement extends HTMLElement {
181205 constructor ( ) {
182206 super ( ) ;
183207
208+ /* -------------------------------------------------------------------------
209+ * Setup Shadow DOM contents
210+ * ---------------------------------------------------------------------- */
184211 /**
185- * @param name - The value of name attribute for the slot element
186- * @returns - The slot element
212+ * The container of minimum text that will be read even
213+ * if the accessible name (label attribute value) is omitted.
187214 */
188- const mkslot = ( name : string ) => {
189- const slot = document . createElement ( 'slot' ) ;
190-
191- slot . name = name ;
215+ const a11yNamePrefix = ( ( ) => {
216+ const span = document . createElement ( 'span' ) ;
217+
218+ span . id = 'semantics' ;
219+ span . hidden = true ;
220+ span . textContent = 'Code Block' ;
221+
222+ return span ;
223+ } ) ( )
224+ /** Container of accessible names (label attribute values). */
225+ const a11yName = ( ( ) => {
226+ const span = document . createElement ( 'span' ) ;
227+
228+ span . slot = 'name' ;
229+ span . textContent = this . getAttribute ( 'label' ) || '' ;
230+
231+ return span ;
232+ } ) ( ) ;
233+ const codeElm = ( ( ) => {
234+ const code = document . createElement ( 'code' ) ;
235+
236+ code . tabIndex = 0 ;
237+ code . className = 'hljs' ; // TODO: Make it variable
238+
239+ return code ;
240+ } ) ( ) ;
241+ const preElm = ( ( ) => {
242+ const pre = document . createElement ( 'pre' ) ;
243+
244+ pre . slot = 'code' ;
245+ pre . append ( codeElm ) ;
246+
247+ return pre ;
248+ } ) ( ) ;
249+ const container = ( ( ) => {
250+ const div = document . createElement ( 'div' ) ;
251+
252+ div . append ( ...Object . values ( this . #slots) ) ;
253+ div . setAttribute ( 'role' , 'group' ) ;
254+ div . setAttribute ( 'aria-labelledby' , 'semantics name' ) ;
255+
256+ return div ;
257+ } ) ( ) ;
258+ const shadowRoot = this . attachShadow ( {
259+ mode : 'closed' ,
260+ } ) ;
192261
193- return slot ;
194- }
195- const slots = [
196- mkslot ( 'label' ) ,
197- mkslot ( 'copy-button' ) ,
198- mkslot ( 'code' ) ,
199- ] ;
200- const pre = document . createElement ( 'pre' ) ;
201- const code = document . createElement ( 'code' ) ;
262+ shadowRoot . append ( a11yNamePrefix ) ;
263+ shadowRoot . append ( container ) ;
202264
203- code . tabIndex = 0 ;
204- code . className = 'hljs' ; // TODO: Make it variable
205- pre . slot = 'code' ;
206- pre . append ( code ) ;
207265
208- // Hard private props initialize
209- this . #value = ( this . textContent || '' ) . replace ( / ^ \n / , '' ) ;
210- this . #label = this . getAttribute ( 'label' ) || '' ;
266+ /* -------------------------------------------------------------------------
267+ * Hard private props initialize
268+ * ---------------------------------------------------------------------- */
269+ this . #value = ( this . textContent || '' ) . replace ( / ^ \n / , '' ) . replace ( / \n $ / , '' ) ;
270+ this . #label = a11yName . textContent || '' ;
211271 this . #language = this . getAttribute ( 'language' ) || '' ;
212272 this . #controls = this . getAttribute ( 'controls' ) !== null ;
213- this . #shadowRoot = this . attachShadow ( {
214- mode : 'closed' ,
215- } ) ;
216- this . #codeBlock = code ;
217- this . #codeWrap = pre ;
218- this . #shadowRoot. append ( ...slots ) ;
273+ this . #a11yName = a11yName ;
274+ this . #codeBlock = codeElm ;
275+ this . #codeWrap = preElm ;
219276 }
220277}
221278
0 commit comments