@@ -29,171 +29,212 @@ export interface SVGIconProps extends Omit<React.HTMLProps<SVGElement>, 'ref'> {
2929}
3030
3131let currentId = 0 ;
32- const canUseDOM = ! ! ( typeof window !== 'undefined' && window . document && window . document . createElement ) ;
32+ // const canUseDOM = !!(typeof window !== 'undefined' && window.document && window.document.createElement);
33+
34+ const createSvg = ( icon : IconDefinition , iconClassName : string ) => {
35+ const { xOffset, yOffset, width, height, svgPathData, svgClassName } = icon ?? { } ;
36+ const _xOffset = xOffset ?? 0 ;
37+ const _yOffset = yOffset ?? 0 ;
38+ const viewBox = [ _xOffset , _yOffset , width , height ] . join ( ' ' ) ;
39+
40+ const classNames = [ ] ;
41+
42+ if ( svgClassName ) {
43+ classNames . push ( svgClassName ) ;
44+ }
45+ if ( iconClassName ) {
46+ classNames . push ( iconClassName ) ;
47+ }
48+
49+ const svgPaths =
50+ svgPathData && Array . isArray ( svgPathData ) ? (
51+ svgPathData . map ( ( pathObject , index ) => (
52+ < path className = { pathObject . className } key = { `${ pathObject . path } -${ index } ` } d = { pathObject . path } />
53+ ) )
54+ ) : (
55+ < path d = { svgPathData as string } />
56+ ) ;
57+
58+ return (
59+ < svg viewBox = { viewBox } className = { classNames . join ( ' ' ) } >
60+ { svgPaths }
61+ </ svg >
62+ ) ;
63+ } ;
3364
3465/**
3566 * Factory to create Icon class components for consumers
3667 */
3768export function createIcon ( { name, icon, rhUiIcon = null } : CreateIconProps ) : React . ComponentClass < SVGIconProps > {
38- return class SVGIcon extends Component < SVGIconProps , { themeClassVersion : number } > {
69+ return class SVGIcon extends Component < SVGIconProps > {
70+ // return class SVGIcon extends Component<SVGIconProps, { themeClassVersion: number }> {
3971 static displayName = name ;
4072
4173 id = `icon-title-${ currentId ++ } ` ;
42- private observer : MutationObserver | null = null ;
74+ // private observer: MutationObserver | null = null;
4375
4476 constructor ( props : SVGIconProps ) {
4577 super ( props ) ;
46- this . state = { themeClassVersion : 0 } ;
78+ // this.state = { themeClassVersion: 0 };
4779 }
4880
49- componentDidMount ( ) {
50- if ( rhUiIcon !== null && canUseDOM ) {
51- this . observer = new MutationObserver ( ( mutations ) => {
52- for ( const mutation of mutations ) {
53- if ( mutation . type === 'attributes' && mutation . attributeName === 'class' ) {
54- const target = mutation . target as HTMLElement ;
55- const hadClass = ( mutation . oldValue || '' ) . includes ( 'pf-v6-theme-unified' ) ;
56- const hasClass = target . classList . contains ( 'pf-v6-theme-unified' ) ;
57-
58- if ( hadClass !== hasClass && this . props . set === undefined ) {
59- this . setState ( ( prevState ) => ( {
60- themeClassVersion : prevState . themeClassVersion + 1
61- } ) ) ;
62- }
63- }
64- }
65- } ) ;
66-
67- this . observer . observe ( document . documentElement , {
68- attributes : true ,
69- attributeFilter : [ 'class' ] ,
70- attributeOldValue : true
71- } ) ;
72- }
73- }
74-
75- componentWillUnmount ( ) {
76- if ( this . observer ) {
77- this . observer . disconnect ( ) ;
78- this . observer = null ;
79- }
80- }
81+ // componentDidMount() {
82+ // if (rhUiIcon !== null && canUseDOM) {
83+ // this.observer = new MutationObserver((mutations) => {
84+ // for (const mutation of mutations) {
85+ // if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
86+ // const target = mutation.target as HTMLElement;
87+ // const hadClass = (mutation.oldValue || '').includes('pf-v6-theme-unified');
88+ // const hasClass = target.classList.contains('pf-v6-theme-unified');
89+
90+ // if (hadClass !== hasClass && this.props.set === undefined) {
91+ // this.setState((prevState) => ({
92+ // themeClassVersion: prevState.themeClassVersion + 1
93+ // }));
94+ // }
95+ // }
96+ // }
97+ // });
98+
99+ // this.observer.observe(document.documentElement, {
100+ // attributes: true,
101+ // attributeFilter: ['class'],
102+ // attributeOldValue: true
103+ // });
104+ // }
105+ // }
106+
107+ // componentWillUnmount() {
108+ // if (this.observer) {
109+ // this.observer.disconnect();
110+ // this.observer = null;
111+ // }
112+ // }
81113
82114 render ( ) {
83115 const { title, className : propsClassName , set, ...props } = this . props ;
84116
85- const shouldUseAltData =
86- rhUiIcon !== null &&
87- ( set === 'unified' ||
88- ( set === undefined && canUseDOM && document . documentElement . classList . contains ( 'pf-v6-theme-unified' ) ) ) ;
89-
90- const iconData = shouldUseAltData ? rhUiIcon : icon ;
91- const { xOffset, yOffset, width, height, svgClassName, svgPathData } = iconData ?? { } ;
92- const _xOffset = xOffset ?? 0 ;
93- const _yOffset = yOffset ?? 0 ;
94-
95117 const hasTitle = Boolean ( title ) ;
96- const viewBox = [ _xOffset , _yOffset , width , height ] . join ( ' ' ) ;
97-
98118 const classNames = [ 'pf-v6-svg' ] ;
99- if ( svgClassName ) {
100- classNames . push ( svgClassName ) ;
101- }
119+
102120 if ( propsClassName ) {
103121 classNames . push ( propsClassName ) ;
104122 }
105123
106- const svgPaths = Array . isArray ( svgPathData ) ? (
107- svgPathData . map ( ( pathObject , index ) => (
108- < path className = { pathObject . className } key = { `${ pathObject . path } -${ index } ` } d = { pathObject . path } />
109- ) )
110- ) : (
111- < path d = { svgPathData as string } />
112- ) ;
113-
114- return (
115- < svg
116- className = { classNames . join ( ' ' ) }
117- viewBox = { viewBox }
118- fill = "currentColor"
119- aria-labelledby = { hasTitle ? this . id : null }
120- aria-hidden = { hasTitle ? null : true }
121- role = "img"
122- width = "1em"
123- height = "1em"
124- { ...( props as Omit < React . SVGProps < SVGElement > , 'ref' > ) } // Lie.
125- >
126- { hasTitle && < title id = { this . id } > { title } </ title > }
127- { svgPaths }
128- </ svg >
129- ) ;
130-
131- // Alternate CSS method tinkering
132- // TODO: remove or refactor to use this method
133- // Below works for paths, but not viewbox without needing the MutationObserver which would be nice to not have when using CSS method
134- // May be able to use two separate svgs instead of paths instead if going this route
135-
136- // const defaultSvgPathData = Array.isArray(icon?.svgPathData) ? (
137- // icon?.svgPathData.map((pathObject, index) => (
138- // <path className={pathObject.className} key={`${pathObject.path}-${index}`} d={pathObject.path} />
139- // ))
140- // ) : (
141- // <path d={icon?.svgPathData as string} />
142- // );
143-
144- // const rhUiSvgPathData =
145- // rhUiIcon?.svgPathData && Array.isArray(rhUiIcon?.svgPathData) ? (
146- // rhUiIcon?.svgPathData.map((pathObject, index) => (
147- // <path className={pathObject.className} key={`${pathObject.path}-${index}`} d={pathObject.path} />
148- // ))
149- // ) : (
150- // <path d={rhUiIcon?.svgPathData as string} />
151- // );
152-
153- // const finalSvgPath =
154- // rhUiIcon !== null ? (
155- // <>
156- // <g className="pf-icon">{defaultSvgPathData}</g>
157- // <g className="rh-icon">{rhUiSvgPathData}</g>
158- // </>
159- // ) : (
160- // pfSvgPathData
161- // );
162-
163- // return (
164- // <svg
165- // className={classNames.join(' ')}
166- // viewBox={viewBox}
167- // fill="currentColor"
168- // aria-labelledby={hasTitle ? this.id : null}
169- // aria-hidden={hasTitle ? null : true}
170- // role="img"
171- // width="1em"
172- // height="1em"
173- // {...(props as Omit<React.SVGProps<SVGElement>, 'ref'>)} // Lie.
174- // >
175- // {hasTitle && <title id={this.id}>{title}</title> }
176- // <style type="text/css">
177- // {/* Testing CSS visibility switching */}
178- // {/* TODO: move to css file, add to global styles */}
179- // {`
180- // .pf-v6-theme-unified {
181- // .pf-icon {
182- // display: none;
183- // }
184- // .rh-icon {
185- // display: block;
186- // }
187- // }
188- // .rh-icon {
189- // display: none;
190- // }
191-
192- // `}
193- // </style>
194- // {finalSvgPath}
195- // </svg>
196- // );
124+ if ( ( set === undefined && rhUiIcon === null ) || set !== undefined ) {
125+ const iconData = set !== undefined && set === 'unified' ? rhUiIcon : icon ;
126+ const { xOffset, yOffset, width, height, svgPathData, svgClassName } = iconData ?? { } ;
127+ const _xOffset = xOffset ?? 0 ;
128+ const _yOffset = yOffset ?? 0 ;
129+ const viewBox = [ _xOffset , _yOffset , width , height ] . join ( ' ' ) ;
130+
131+ if ( svgClassName ) {
132+ classNames . push ( svgClassName ) ;
133+ }
134+
135+ const svgPaths =
136+ svgPathData && Array . isArray ( svgPathData ) ? (
137+ svgPathData . map ( ( pathObject , index ) => (
138+ < path className = { pathObject . className } key = { `${ pathObject . path } -${ index } ` } d = { pathObject . path } />
139+ ) )
140+ ) : (
141+ < path d = { svgPathData as string } />
142+ ) ;
143+
144+ return (
145+ < svg
146+ className = { classNames . join ( ' ' ) }
147+ fill = "currentColor"
148+ viewBox = { viewBox }
149+ aria-labelledby = { hasTitle ? this . id : null }
150+ aria-hidden = { hasTitle ? null : true }
151+ role = "img"
152+ width = "1em"
153+ height = "1em"
154+ { ...( props as Omit < React . SVGProps < SVGElement > , 'ref' > ) } // Lie.
155+ >
156+ { hasTitle && < title id = { this . id } > { title } </ title > }
157+ { svgPaths }
158+ </ svg >
159+ ) ;
160+ } else {
161+ let defaultIconClassName ;
162+ let rhUiIconClassName ;
163+
164+ const shouldRenderDefault = set === 'default' || ( set === undefined && rhUiIcon !== null ) ;
165+ const shouldRenderRhUi = set === 'unified' || ( set === undefined && rhUiIcon !== null ) ;
166+
167+ if ( shouldRenderDefault ) {
168+ defaultIconClassName = 'pf-v6-icon-default' ;
169+ }
170+ if ( shouldRenderRhUi ) {
171+ rhUiIconClassName = 'pf-v6-icon-unified' ;
172+ }
173+
174+ return (
175+ < svg
176+ className = { classNames . join ( ' ' ) }
177+ fill = "currentColor"
178+ aria-labelledby = { hasTitle ? this . id : null }
179+ aria-hidden = { hasTitle ? null : true }
180+ role = "img"
181+ width = "1em"
182+ height = "1em"
183+ { ...( props as Omit < React . SVGProps < SVGElement > , 'ref' > ) } // Lie.
184+ >
185+ { hasTitle && < title id = { this . id } > { title } </ title > }
186+ { icon && shouldRenderDefault && createSvg ( icon , set === undefined && defaultIconClassName ) }
187+ { rhUiIcon && shouldRenderRhUi && createSvg ( rhUiIcon , set === undefined && rhUiIconClassName ) }
188+ </ svg >
189+ ) ;
190+ }
191+
192+ // React method w/ MutationObserver
193+ // const shouldUseAltData =
194+ // rhUiIcon !== null &&
195+ // (set === 'unified' ||
196+ // (set === undefined && canUseDOM && document.documentElement.classList.contains('pf-v6-theme-unified')));
197+
198+ // const iconData = shouldUseAltData ? rhUiIcon : icon;
199+ // const { xOffset, yOffset, width, height, svgClassName, svgPathData } = iconData ?? {};
200+ // const _xOffset = xOffset ?? 0;
201+ // const _yOffset = yOffset ?? 0;
202+
203+ // const hasTitle = Boolean(title);
204+ // const viewBox = [_xOffset, _yOffset, width, height].join(' ');
205+
206+ // const classNames = ['pf-v6-svg'];
207+ // if (svgClassName) {
208+ // classNames.push(svgClassName);
209+ // }
210+ // if (propsClassName) {
211+ // classNames.push(propsClassName);
212+ // }
213+
214+ // const svgPaths = Array.isArray(svgPathData) ? (
215+ // svgPathData.map((pathObject, index) => (
216+ // <path className={pathObject.className} key={`${pathObject.path}-${index}`} d={pathObject.path} />
217+ // ))
218+ // ) : (
219+ // <path d={svgPathData as string} />
220+ // );
221+
222+ // return (
223+ // <svg
224+ // className={classNames.join(' ')}
225+ // viewBox={viewBox}
226+ // fill="currentColor"
227+ // aria-labelledby={hasTitle ? this.id : null}
228+ // aria-hidden={hasTitle ? null : true}
229+ // role="img"
230+ // width="1em"
231+ // height="1em"
232+ // {...(props as Omit<React.SVGProps<SVGElement>, 'ref'>)} // Lie.
233+ // >
234+ // {hasTitle && <title id={this.id}>{title}</title> }
235+ // {svgPaths}
236+ // </svg>
237+ // );
197238 }
198239 } ;
199240}
0 commit comments