Skip to content

Commit 8ef1ad4

Browse files
committed
change to css svg logic
1 parent c85b84e commit 8ef1ad4

1 file changed

Lines changed: 183 additions & 142 deletions

File tree

packages/react-icons/src/createIcon.tsx

Lines changed: 183 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -29,171 +29,212 @@ export interface SVGIconProps extends Omit<React.HTMLProps<SVGElement>, 'ref'> {
2929
}
3030

3131
let 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
*/
3768
export 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

Comments
 (0)