Skip to content

Commit 1edd3ce

Browse files
committed
add tabler icons
1 parent f78415c commit 1edd3ce

File tree

6,847 files changed

+54866
-6883
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

6,847 files changed

+54866
-6883
lines changed

icon-sprite/scripts/gen-wrappers.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,14 @@ import { renderUse, type IconProps } from "../_shared.js";
171171
export function ${componentName}(props: IconProps) {
172172
if (process.env.NODE_ENV !== "production" && DevIcon) {
173173
const { size, width, height, ...rest } = props;
174-
return <DevIcon {...(rest as any)} size={size ?? 24} width={width} height={height} />;
174+
return (
175+
<DevIcon
176+
{...(rest as any)}
177+
size={size ?? 24}
178+
{...(width != null ? { width } : {})}
179+
{...(height != null ? { height } : {})}
180+
/>
181+
);
175182
}
176183
return renderUse("${spriteId}", SPRITE_PATH, props);
177184
}
@@ -211,7 +218,14 @@ import { renderUse, type IconProps } from "../_shared.js";
211218
export function ${componentName}(props: IconProps) {
212219
if (process.env.NODE_ENV !== "production" && DevIcon) {
213220
const { size, width, height, ...rest } = props;
214-
return <DevIcon {...(rest as any)} size={size ?? 24} width={width} height={height} />;
221+
return (
222+
<DevIcon
223+
{...(rest as any)}
224+
size={size ?? 24}
225+
{...(width != null ? { width } : {})}
226+
{...(height != null ? { height } : {})}
227+
/>
228+
);
215229
}
216230
return renderUse("${spriteId}", SPRITE_PATH, props);
217231
}

icon-sprite/scripts/scan-icons.js

Lines changed: 87 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,22 @@ import { loadConfig } from "../dist/loadConfig.js";
1111
const __dirname = path.dirname(fileURLToPath(import.meta.url));
1212
const ICONS = new Set();
1313

14+
// Props that may behave differently in prod (sprite mode) vs dev
15+
const RISKY_PROPS = new Set([
16+
// Color props - should use className="text-*" instead
17+
"stroke", "fill", "color",
18+
// Stroke presentation attributes - don't cascade into <use> shadow DOM
19+
"strokeLinecap", "strokeLinejoin", "strokeDasharray",
20+
"strokeDashoffset", "strokeMiterlimit", "strokeOpacity",
21+
// Fill presentation attributes
22+
"fillOpacity", "fillRule",
23+
// Other presentation attributes
24+
"opacity", "transform", "vectorEffect",
25+
]);
26+
27+
// Track warnings: { file, line, icon, prop }
28+
const riskyPropWarnings = [];
29+
1430
// Load user config (merged with defaults)
1531
const { IMPORT_NAME, ROOT_DIR, IGNORE_ICONS, EXCLUDE_DIRS } = await loadConfig();
1632

@@ -53,49 +69,69 @@ function collect(dir) {
5369
});
5470
if (!ast) continue;
5571

56-
// Track local "Icon" aliases in this file
72+
// Track local icon names imported from our library
5773
const iconLocalNames = new Set();
74+
const iconImports = new Map(); // local name -> imported name
5875

5976
traverseImport.default(ast, {
60-
ImportDeclaration(path) {
61-
if (path.node.source.value !== IMPORT_NAME) return;
62-
for (const spec of path.node.specifiers) {
77+
ImportDeclaration(nodePath) {
78+
if (nodePath.node.source.value !== IMPORT_NAME) return;
79+
for (const spec of nodePath.node.specifiers) {
6380
if (t.isImportSpecifier(spec)) {
6481
const imported = spec.imported.name;
6582
const local = spec.local.name;
6683
if (imported === "Icon") {
67-
// generic Component: track for JSX
6884
iconLocalNames.add(local);
69-
// ignore CustomIcon imports
7085
} else if (!IGNORE_ICONS.includes(imported)) {
71-
// named icon import
7286
ICONS.add(imported);
87+
iconLocalNames.add(local);
88+
iconImports.set(local, imported);
7389
}
7490
}
7591
}
7692
},
77-
JSXElement(path) {
78-
const opening = path.get("openingElement");
93+
JSXElement(nodePath) {
94+
const opening = nodePath.get("openingElement");
7995
const nameNode = opening.get("name");
8096
if (!nameNode.isJSXIdentifier()) return;
8197
const tag = nameNode.node.name;
98+
99+
// Check if this is one of our icon components
82100
if (!iconLocalNames.has(tag)) return;
83-
// found <Icon ...>
101+
102+
const iconName = iconImports.get(tag) || tag;
103+
const line = nodePath.node.loc?.start?.line || "?";
104+
105+
// Check for risky props
84106
for (const attrPath of opening.get("attributes")) {
85107
if (!attrPath.isJSXAttribute()) continue;
86-
const attrName = attrPath.get("name");
87-
if (!attrName.isJSXIdentifier({ name: "name" })) continue;
88-
const valuePath = attrPath.get("value");
89-
if (valuePath.isStringLiteral()) {
90-
ICONS.add(valuePath.node.value);
91-
} else if (valuePath.isJSXExpressionContainer()) {
92-
const exprPath = valuePath.get("expression");
93-
if (exprPath.evaluate) {
94-
const res = exprPath.evaluate();
95-
if (res.confident && typeof res.value === "string") {
96-
ICONS.add(res.value);
97-
} else {
98-
throw path.buildCodeFrameError(`Unable to statically evaluate <Icon name={...}> at ${full}`);
108+
const attrNameNode = attrPath.get("name");
109+
if (!attrNameNode.isJSXIdentifier()) continue;
110+
const propName = attrNameNode.node.name;
111+
112+
if (RISKY_PROPS.has(propName)) {
113+
riskyPropWarnings.push({
114+
file: path.relative(projectRoot, full),
115+
line,
116+
icon: iconName,
117+
prop: propName,
118+
});
119+
}
120+
121+
// Handle <Icon name="..."> for generic Icon component
122+
if (propName === "name") {
123+
const valuePath = attrPath.get("value");
124+
if (valuePath.isStringLiteral()) {
125+
ICONS.add(valuePath.node.value);
126+
} else if (valuePath.isJSXExpressionContainer()) {
127+
const exprPath = valuePath.get("expression");
128+
if (exprPath.evaluate) {
129+
const res = exprPath.evaluate();
130+
if (res.confident && typeof res.value === "string") {
131+
ICONS.add(res.value);
132+
} else {
133+
throw nodePath.buildCodeFrameError(`Unable to statically evaluate <Icon name={...}> at ${full}`);
134+
}
99135
}
100136
}
101137
}
@@ -111,3 +147,31 @@ collect(scanRoot);
111147
const outFile = path.join(__dirname, "used-icons.js");
112148
fs.writeFileSync(outFile, `export const ICONS = ${JSON.stringify([...ICONS].sort(), null, 2)};\n`, "utf8");
113149
console.log(`✅ Found ${ICONS.size} icons; wrote to ${outFile}`);
150+
151+
// 4️⃣ Warn about risky props
152+
if (riskyPropWarnings.length > 0) {
153+
console.log("");
154+
console.warn(`⚠️ Found ${riskyPropWarnings.length} icon(s) with props that may differ in prod:`);
155+
156+
// Group by file for cleaner output
157+
const byFile = new Map();
158+
for (const w of riskyPropWarnings) {
159+
if (!byFile.has(w.file)) byFile.set(w.file, []);
160+
byFile.get(w.file).push(w);
161+
}
162+
163+
for (const [file, warnings] of byFile) {
164+
console.warn(` ${file}:`);
165+
for (const w of warnings.slice(0, 5)) {
166+
console.warn(` Line ${w.line}: <${w.icon} ${w.prop}="..." /> - "${w.prop}" may not work in prod`);
167+
}
168+
if (warnings.length > 5) {
169+
console.warn(` ... and ${warnings.length - 5} more`);
170+
}
171+
}
172+
173+
console.warn("");
174+
console.warn(` 💡 Tip: Use className="text-red-500" for colors (works via currentColor)`);
175+
console.warn(` 💡 Props like strokeLinecap, fill, stroke don't cascade into <use> shadow DOM`);
176+
console.warn("");
177+
}

icon-sprite/src/_shared.tsx

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
11
export type Dim = number | string | undefined;
22

3-
// Props that reliably work with SVG sprites
4-
export type IconProps = {
5-
className?: string;
6-
style?: React.CSSProperties;
3+
/**
4+
* Icon props - extends all SVG props for maximum compatibility.
5+
*
6+
* ✅ RELIABLE (work same in dev & prod):
7+
* - className, style, size, width, height
8+
* - strokeWidth (via CSS variable)
9+
* - id, role, aria-*, data-*
10+
*
11+
* ⚠️ COLOR (use className instead):
12+
* - stroke, fill, color → Use className="text-red-500" (works via currentColor)
13+
*
14+
* ❌ MAY DIFFER in prod (don't cascade into <use> shadow DOM):
15+
* - strokeLinecap, strokeLinejoin, strokeDasharray, etc.
16+
* - These work in dev but may not apply in prod sprite mode
17+
*/
18+
export type IconProps = React.SVGProps<SVGSVGElement> & {
719
size?: Dim;
8-
width?: Dim;
9-
height?: Dim;
10-
strokeWidth?: number | string;
11-
id?: string;
12-
role?: string;
13-
"aria-label"?: string;
14-
"aria-hidden"?: boolean | "true" | "false";
15-
"data-testid"?: string;
1620
};
1721

1822
// Ultra-minimal renderUse - all logic in one place

icon-sprite/src/icons/AArrowDown.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@ import { renderUse, type IconProps } from "../_shared.js";
55
export function AArrowDown(props: IconProps) {
66
if (process.env.NODE_ENV !== "production" && DevIcon) {
77
const { size, width, height, ...rest } = props;
8-
return <DevIcon {...(rest as any)} size={size ?? 24} width={width} height={height} />;
8+
return (
9+
<DevIcon
10+
{...(rest as any)}
11+
size={size ?? 24}
12+
{...(width != null ? { width } : {})}
13+
{...(height != null ? { height } : {})}
14+
/>
15+
);
916
}
1017
return renderUse("a-arrow-down", SPRITE_PATH, props);
1118
}

icon-sprite/src/icons/AArrowUp.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@ import { renderUse, type IconProps } from "../_shared.js";
55
export function AArrowUp(props: IconProps) {
66
if (process.env.NODE_ENV !== "production" && DevIcon) {
77
const { size, width, height, ...rest } = props;
8-
return <DevIcon {...(rest as any)} size={size ?? 24} width={width} height={height} />;
8+
return (
9+
<DevIcon
10+
{...(rest as any)}
11+
size={size ?? 24}
12+
{...(width != null ? { width } : {})}
13+
{...(height != null ? { height } : {})}
14+
/>
15+
);
916
}
1017
return renderUse("a-arrow-up", SPRITE_PATH, props);
1118
}

icon-sprite/src/icons/ALargeSmall.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@ import { renderUse, type IconProps } from "../_shared.js";
55
export function ALargeSmall(props: IconProps) {
66
if (process.env.NODE_ENV !== "production" && DevIcon) {
77
const { size, width, height, ...rest } = props;
8-
return <DevIcon {...(rest as any)} size={size ?? 24} width={width} height={height} />;
8+
return (
9+
<DevIcon
10+
{...(rest as any)}
11+
size={size ?? 24}
12+
{...(width != null ? { width } : {})}
13+
{...(height != null ? { height } : {})}
14+
/>
15+
);
916
}
1017
return renderUse("a-large-small", SPRITE_PATH, props);
1118
}

icon-sprite/src/icons/Accessibility.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@ import { renderUse, type IconProps } from "../_shared.js";
55
export function Accessibility(props: IconProps) {
66
if (process.env.NODE_ENV !== "production" && DevIcon) {
77
const { size, width, height, ...rest } = props;
8-
return <DevIcon {...(rest as any)} size={size ?? 24} width={width} height={height} />;
8+
return (
9+
<DevIcon
10+
{...(rest as any)}
11+
size={size ?? 24}
12+
{...(width != null ? { width } : {})}
13+
{...(height != null ? { height } : {})}
14+
/>
15+
);
916
}
1017
return renderUse("accessibility", SPRITE_PATH, props);
1118
}

icon-sprite/src/icons/Activity.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@ import { renderUse, type IconProps } from "../_shared.js";
55
export function Activity(props: IconProps) {
66
if (process.env.NODE_ENV !== "production" && DevIcon) {
77
const { size, width, height, ...rest } = props;
8-
return <DevIcon {...(rest as any)} size={size ?? 24} width={width} height={height} />;
8+
return (
9+
<DevIcon
10+
{...(rest as any)}
11+
size={size ?? 24}
12+
{...(width != null ? { width } : {})}
13+
{...(height != null ? { height } : {})}
14+
/>
15+
);
916
}
1017
return renderUse("activity", SPRITE_PATH, props);
1118
}

icon-sprite/src/icons/ActivitySquare.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@ import { renderUse, type IconProps } from "../_shared.js";
55
export function ActivitySquare(props: IconProps) {
66
if (process.env.NODE_ENV !== "production" && DevIcon) {
77
const { size, width, height, ...rest } = props;
8-
return <DevIcon {...(rest as any)} size={size ?? 24} width={width} height={height} />;
8+
return (
9+
<DevIcon
10+
{...(rest as any)}
11+
size={size ?? 24}
12+
{...(width != null ? { width } : {})}
13+
{...(height != null ? { height } : {})}
14+
/>
15+
);
916
}
1017
return renderUse("activity-square", SPRITE_PATH, props);
1118
}

icon-sprite/src/icons/AirVent.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@ import { renderUse, type IconProps } from "../_shared.js";
55
export function AirVent(props: IconProps) {
66
if (process.env.NODE_ENV !== "production" && DevIcon) {
77
const { size, width, height, ...rest } = props;
8-
return <DevIcon {...(rest as any)} size={size ?? 24} width={width} height={height} />;
8+
return (
9+
<DevIcon
10+
{...(rest as any)}
11+
size={size ?? 24}
12+
{...(width != null ? { width } : {})}
13+
{...(height != null ? { height } : {})}
14+
/>
15+
);
916
}
1017
return renderUse("air-vent", SPRITE_PATH, props);
1118
}

0 commit comments

Comments
 (0)