@@ -2,38 +2,47 @@ import CogIcon from '@patternfly/react-icons/dist/esm/icons/cog-icon';
22import MapIcon from '@patternfly/react-icons/dist/esm/icons/map-icon' ;
33import MoonIcon from '@patternfly/react-icons/dist/esm/icons/moon-icon' ;
44import HashtagIcon from '@patternfly/react-icons/dist/esm/icons/hashtag-icon' ;
5+ import FontIcon from '@patternfly/react-icons/dist/esm/icons/font-icon' ;
56import { CodeEditor , CodeEditorControl } from '@patternfly/react-code-editor' ;
6- import { Flex , FlexItem , Icon , Modal , ModalBody , ModalHeader , Switch , SwitchProps } from '@patternfly/react-core' ;
7+ import {
8+ Flex ,
9+ FlexItem ,
10+ Icon ,
11+ Modal ,
12+ ModalBody ,
13+ ModalHeader ,
14+ NumberInput ,
15+ Switch ,
16+ SwitchProps
17+ } from '@patternfly/react-core' ;
718import { useState } from 'react' ;
819
920interface ConfigModalItemProps {
1021 /** Icon rendered inside the configuration modal. */
1122 icon ?: React . ReactNode ;
1223 /** Description of the configuration option. */
1324 description : string ;
14- /** Flag indicating whether the option is enabled or disabled. */
15- isChecked ?: SwitchProps [ 'isChecked' ] ;
16- /** onChange handler for the switch. */
17- onChange ?: SwitchProps [ 'onChange' ] ;
1825 /** Title of the configuration option. We assume that titles are unique. */
1926 title : string ;
20- /** Labels for the enabled and disabled states of the switch. */
21- labels ?: {
22- enabled : string ;
23- disabled : string ;
24- } ;
25- /** Optional OUIA ID of the configuration option. Also used as a prefix for the ids of inner elements. */
27+ /**
28+ * Optional OUIA ID of the configuration option. Also used as a prefix for the ids of inner elements.
29+ * - `${ouiaId}-title` for the element which contains the title
30+ * - `${ouiaId}-description` for the element which contains the description
31+ */
2632 ouiaId ?: string ;
33+ /**
34+ * Slot to render inside the configuration modal. Remember to add `aria-labelledby` and `aria-describedby` props
35+ * to the control inside the slot, pointing to the title and description ids respectively.
36+ */
37+ slot ?: React . ReactNode ;
2738}
2839
2940const ConfigModalItem : React . FunctionComponent < ConfigModalItemProps > = ( {
3041 icon = < CogIcon /> ,
3142 description,
32- isChecked = false ,
33- labels = { enabled : undefined , disabled : undefined } ,
34- onChange,
3543 title,
36- ouiaId
44+ ouiaId = `ConfigModalItem-${ title . replace ( / \s + / g, '-' ) . toLowerCase ( ) } ` ,
45+ slot
3746} ) => (
3847 < Flex
3948 alignItems = { { default : 'alignItemsCenter' } }
@@ -50,7 +59,37 @@ const ConfigModalItem: React.FunctionComponent<ConfigModalItemProps> = ({
5059
5160 < div id = { `${ ouiaId } -description` } > { description } </ div >
5261 </ FlexItem >
53- < FlexItem alignSelf = { { default : 'alignSelfCenter' } } >
62+ < FlexItem alignSelf = { { default : 'alignSelfCenter' } } > { slot } </ FlexItem >
63+ </ Flex >
64+ ) ;
65+
66+ interface ConfigModalSwitchProps extends Omit < ConfigModalItemProps , 'slot' > {
67+ /** Flag indicating whether the option is enabled or disabled. */
68+ isChecked ?: SwitchProps [ 'isChecked' ] ;
69+ /** onChange handler for the switch. */
70+ onChange ?: SwitchProps [ 'onChange' ] ;
71+ /** Labels for the enabled and disabled states of the switch. */
72+ labels ?: {
73+ enabled : string ;
74+ disabled : string ;
75+ } ;
76+ }
77+
78+ const ConfigModalSwitch : React . FunctionComponent < ConfigModalSwitchProps > = ( {
79+ icon = < CogIcon /> ,
80+ description,
81+ title,
82+ ouiaId = `ConfigModalSwitch-${ title . replace ( / \s + / g, '-' ) . toLowerCase ( ) } ` ,
83+ isChecked = false ,
84+ onChange,
85+ labels = { enabled : undefined , disabled : undefined }
86+ } ) => (
87+ < ConfigModalItem
88+ icon = { icon }
89+ description = { description }
90+ title = { title }
91+ ouiaId = { ouiaId }
92+ slot = {
5493 < Switch
5594 aria-labelledby = { `${ ouiaId } -title` }
5695 aria-describedby = { `${ ouiaId } -description` }
@@ -60,13 +99,13 @@ const ConfigModalItem: React.FunctionComponent<ConfigModalItemProps> = ({
6099 label = { isChecked ? labels . enabled : labels . disabled }
61100 onChange = { onChange }
62101 />
63- </ FlexItem >
64- </ Flex >
102+ }
103+ / >
65104) ;
66105
67106interface ConfigModalControlProps {
68- /** Array of configuration controls to be rendered inside the modal. */
69- controls : ConfigModalItemProps [ ] ;
107+ /** Controls to be rendered inside the configuration modal. */
108+ children : React . ReactNode ;
70109 /** Title of the configuration modal. */
71110 title ?: string ;
72111 /** Description of the configuration modal. */
@@ -76,11 +115,11 @@ interface ConfigModalControlProps {
76115}
77116
78117const ConfigModalControl : React . FunctionComponent < ConfigModalControlProps > = ( {
79- controls ,
118+ children ,
80119 title = 'Editor settings' ,
81120 description = 'Settings will be applied immediately' ,
82121 ouiaId = 'CodeEditorConfigurationModal'
83- } ) => {
122+ } : ConfigModalControlProps ) => {
84123 const [ isModalOpen , setIsModalOpen ] = useState ( false ) ;
85124
86125 return (
@@ -96,13 +135,7 @@ const ConfigModalControl: React.FunctionComponent<ConfigModalControlProps> = ({
96135 < ModalHeader title = { title } description = { description } labelId = { `${ ouiaId } -title` } />
97136 < ModalBody id = { `${ ouiaId } -body` } >
98137 < Flex direction = { { default : 'column' } } spaceItems = { { default : 'spaceItemsMd' } } >
99- { controls . map ( ( control ) => (
100- < ConfigModalItem
101- key = { control . title }
102- ouiaId = { `${ ouiaId } -${ control . title . replace ( / \s + / g, '-' ) . toLowerCase ( ) } ` }
103- { ...control }
104- />
105- ) ) }
138+ { children }
106139 </ Flex >
107140 </ ModalBody >
108141 </ Modal >
@@ -123,37 +156,58 @@ export const CodeEditorConfigurationModal: React.FunctionComponent = () => {
123156 const [ isMinimapVisible , setIsMinimapVisible ] = useState ( true ) ;
124157 const [ isDarkTheme , setIsDarkTheme ] = useState ( false ) ;
125158 const [ isLineNumbersVisible , setIsLineNumbersVisible ] = useState ( true ) ;
159+ const [ fontSize , setFontSize ] = useState ( 14 ) ;
126160
127161 const onChange = ( code : string ) => {
128162 setCode ( code ) ;
129163 } ;
130164
131165 const customControl = (
132- < ConfigModalControl
133- controls = { [
134- {
135- title : 'Minimap' ,
136- description : 'Show a preview of the full code on the side of the editor' ,
137- isChecked : isMinimapVisible ,
138- onChange : ( _e , checked ) => setIsMinimapVisible ( checked ) ,
139- icon : < MapIcon />
140- } ,
141- {
142- title : 'Dark theme' ,
143- description : 'Switch the editor to a dark color theme' ,
144- isChecked : isDarkTheme ,
145- onChange : ( _e , checked ) => setIsDarkTheme ( checked ) ,
146- icon : < MoonIcon />
147- } ,
148- {
149- title : 'Line numbers' ,
150- description : 'Show line numbers to the left of each line of code' ,
151- isChecked : isLineNumbersVisible ,
152- onChange : ( _e , checked ) => setIsLineNumbersVisible ( checked ) ,
153- icon : < HashtagIcon />
166+ < ConfigModalControl >
167+ < ConfigModalSwitch
168+ key = "minimap-switch"
169+ title = "Minimap"
170+ description = "Show a preview of the full code on the side of the editor"
171+ isChecked = { isMinimapVisible }
172+ onChange = { ( _e , checked ) => setIsMinimapVisible ( checked ) }
173+ icon = { < MapIcon /> }
174+ />
175+ < ConfigModalSwitch
176+ key = "dark-theme-switch"
177+ title = "Dark theme"
178+ description = "Switch the editor to a dark color theme"
179+ isChecked = { isDarkTheme }
180+ onChange = { ( _e , checked ) => setIsDarkTheme ( checked ) }
181+ icon = { < MoonIcon /> }
182+ />
183+ < ConfigModalSwitch
184+ key = "line-numbers-switch"
185+ title = "Line numbers"
186+ description = "Show line numbers to the left of each line of code"
187+ isChecked = { isLineNumbersVisible }
188+ onChange = { ( _e , checked ) => setIsLineNumbersVisible ( checked ) }
189+ icon = { < HashtagIcon /> }
190+ />
191+ < ConfigModalItem
192+ key = "font-size"
193+ title = "Font size"
194+ description = "Adjust the font size of the code editor"
195+ ouia-id = "font-size"
196+ icon = { < FontIcon /> }
197+ slot = {
198+ < NumberInput
199+ aria-labelledby = "font-size-title"
200+ aria-describedby = "font-size-description"
201+ value = { fontSize }
202+ min = { 5 }
203+ onMinus = { ( ) => setFontSize ( ( size ) => Math . max ( 5 , size - 1 ) ) }
204+ onChange = { ( event ) => setFontSize ( Number ( ( event . target as HTMLInputElement ) . value ) ) }
205+ onPlus = { ( ) => setFontSize ( ( size ) => size + 1 ) }
206+ widthChars = { 2 }
207+ />
154208 }
155- ] }
156- / >
209+ />
210+ </ ConfigModalControl >
157211 ) ;
158212
159213 return (
@@ -165,6 +219,7 @@ export const CodeEditorConfigurationModal: React.FunctionComponent = () => {
165219 isLineNumbersVisible = { isLineNumbersVisible }
166220 isMinimapVisible = { isMinimapVisible }
167221 onChange = { onChange }
222+ options = { { fontSize } }
168223 />
169224 ) ;
170225} ;
0 commit comments