Compare commits

..
Author SHA1 Message Date
Kesley Davidgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>Copilot Autofix powered by AIEric Fennis
03eae6069e feat(icons): added asterism icon (#4687)
* feat(icons): added `asterism` icon

Adds asterism for issue #4077.
Also updates emoji category icon reference after smile rename.

* chore(icons): expand asterism metadata from PR suggestions

Apply curated tags, categories, and use-cases from automated
metadata review suggestions.

* Update icons/asterism.json

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Remove duplicate use-case entry in asterism.json

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Update use cases for asterism symbol in JSON

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Adjust metadata

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-11-10 13:48:06 +01:00
109 changed files with 938 additions and 124 deletions
+2
View File
@@ -15,6 +15,7 @@ monstera
# Search tags and terms used in icon and category metadata
checkmark
activitypub
ageusia
artboard
barbeque
@@ -22,6 +23,7 @@ choy
cicd
ecommerce
enby
fediverse
flexbox
futbol
gddr
+4
View File
@@ -19,6 +19,10 @@ interface LucideProps extends SVGAttributes<SVGSVGElement> {
size?: number | string;
'stroke-width'?: number | string;
nonScalingStroke?: boolean;
/**
* @deprecated
*/
absoluteStrokeWidth?: boolean;
[key: string]: any; // Any other SVG attributes
}
```
+4
View File
@@ -18,6 +18,10 @@ interface LucideProps {
color?: string;
strokeWidth?: number;
nonScalingStroke?: boolean;
/**
* @deprecated
*/
absoluteStrokeWidth?: boolean;
[key: string]: any; // Any other SVG attributes
}
```
@@ -18,6 +18,10 @@ interface LucideProps {
color?: string;
strokeWidth?: number;
nonScalingStroke?: boolean;
/**
* @deprecated
*/
absoluteStrokeWidth?: boolean;
[key: string]: any; // Any other SVG props, supported by react-native-svg
}
```
+4
View File
@@ -17,6 +17,10 @@ interface LucideProps {
color?: string;
strokeWidth?: number;
nonScalingStroke?: boolean;
/**
* @deprecated
*/
absoluteStrokeWidth?: boolean;
[key: string]: any; // Any other SVG attributes
}
```
+4
View File
@@ -18,6 +18,10 @@ interface LucideProps extends SVGAttributes {
color?: string;
strokeWidth?: number;
nonScalingStroke?: boolean;
/**
* @deprecated
*/
absoluteStrokeWidth?: boolean;
[key: string]: any; // Any other SVG attributes
}
```
+4
View File
@@ -19,6 +19,10 @@ interface LucideProps extends SVGAttributes<SVGSVGElement> {
size?: number | string;
strokeWidth?: number | string;
nonScalingStroke?: boolean;
/**
* @deprecated
*/
absoluteStrokeWidth?: boolean;
children?: Snippet;
[key: string]: any; // Any other SVG attributes
}
+4
View File
@@ -18,6 +18,10 @@ interface LucideProps {
color?: string;
strokeWidth?: number;
nonScalingStroke?: boolean;
/**
* @deprecated
*/
absoluteStrokeWidth?: boolean;
[key: string]: any; // Any other SVG attributes
}
```
+31
View File
@@ -0,0 +1,31 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"KesleyDavid",
"karsa-mistmere"
],
"use-cases": [
"Marking interoperable Fediverse profile links in social clients",
"Representing a three-star typographic symbol in publishing tools",
"Separating article sections with a decorative typographic mark",
"Representing star patterns in astronomy apps"
],
"tags": [
"asterisk",
"fediverse",
"activitypub",
"symbol",
"typography",
"social",
"decentralized",
"federation",
"stars",
"three",
"ornament",
"punctuation",
"divider"
],
"categories": [
"text"
]
}
+21
View File
@@ -0,0 +1,21 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 2v8" />
<path d="m14.5 15 7 4" />
<path d="m14.5 19 7-4" />
<path d="M18 13v8" />
<path d="m2.5 15 7 4" />
<path d="m2.5 19 7-4" />
<path d="M6 13v8" />
<path d="m8.5 4 7 4" />
<path d="m8.5 8 7-4" />
</svg>

After

Width:  |  Height:  |  Size: 440 B

@@ -13,7 +13,7 @@ export default function IconShowcase() {
color="red"
size={48}
strokeWidth={4}
nonScalingStroke
absoluteStrokeWidth
/>
<LucideProvider
color="purple"
@@ -17,15 +17,9 @@ test('renders Lucide public APIs in a real browser', async () => {
await expect.element(customIcon).toHaveAttribute('width', '48');
await expect.element(customIcon).toHaveAttribute('height', '48');
await expect.element(customIcon).toHaveAttribute('stroke', 'red');
await expect.element(customIcon).toHaveAttribute('stroke-width', '4');
await expect.element(customIcon).toHaveAttribute('stroke-width', '2');
await expect.element(customIcon).toHaveClass('consumer-icon');
const customIconChildren = Array.from(customIcon.element().children);
expect(customIconChildren.length).toBeGreaterThan(0);
for (const child of customIconChildren) {
expect(child.getAttribute('vector-effect')).toBe('non-scaling-stroke');
}
const providerIcon = screen.getByTestId('provider-icon');
await expect.element(providerIcon).toHaveAttribute('width', '32');
await expect.element(providerIcon).toHaveAttribute('stroke', 'purple');
@@ -12,7 +12,7 @@ export default function IconShowcase() {
color="red"
size={48}
strokeWidth={4}
nonScalingStroke
absoluteStrokeWidth
/>
<LucideProvider
color="purple"
@@ -37,12 +37,6 @@ test('renders Lucide public APIs and survives router navigation', async () => {
expect(aliasIcon.element().innerHTML).toBe(canonicalIcon.element().innerHTML);
await expect.element(customIcon).toHaveClass('consumer-icon');
const customIconChildren = Array.from(customIcon.element().children);
expect(customIconChildren.length).toBeGreaterThan(0);
for (const child of customIconChildren) {
expect(child.getAttribute('vector-effect')).toBe('non-scaling-stroke');
}
const providerIcon = screen.getByTestId('provider-icon');
await expect.element(providerIcon).toHaveAttribute('width', '32');
await expect.element(providerIcon).toHaveAttribute('stroke', 'purple');
@@ -11,7 +11,7 @@ export default function IconShowcase() {
color="red"
size={48}
strokeWidth={4}
nonScalingStroke
absoluteStrokeWidth
/>
<LucideProvider
color="purple"
@@ -22,15 +22,9 @@ test('renders Lucide public APIs and survives router navigation', async () => {
await expect.element(customIcon).toHaveAttribute('width', '48');
await expect.element(customIcon).toHaveAttribute('height', '48');
await expect.element(customIcon).toHaveAttribute('stroke', 'red');
await expect.element(customIcon).toHaveAttribute('stroke-width', '4');
await expect.element(customIcon).toHaveAttribute('stroke-width', '2');
await expect.element(customIcon).toHaveClass('consumer-icon');
const customIconChildren = Array.from(customIcon.element().children);
expect(customIconChildren.length).toBeGreaterThan(0);
for (const child of customIconChildren) {
expect(child.getAttribute('vector-effect')).toBe('non-scaling-stroke');
}
const providerIcon = screen.getByTestId('provider-icon');
await expect.element(providerIcon).toHaveAttribute('width', '32');
await expect.element(providerIcon).toHaveAttribute('stroke', 'purple');
@@ -11,7 +11,7 @@ export default function IconShowcase() {
color="red"
size={48}
strokeWidth={4}
nonScalingStroke
absoluteStrokeWidth
/>
<LucideProvider
color="purple"
@@ -17,15 +17,9 @@ test('renders Lucide public APIs in a real browser', async () => {
await expect.element(customIcon).toHaveAttribute('width', '48');
await expect.element(customIcon).toHaveAttribute('height', '48');
await expect.element(customIcon).toHaveAttribute('stroke', 'red');
await expect.element(customIcon).toHaveAttribute('stroke-width', '4');
await expect.element(customIcon).toHaveAttribute('stroke-width', '2');
await expect.element(customIcon).toHaveClass('consumer-icon');
const customIconChildren = Array.from(customIcon.element().children);
expect(customIconChildren.length).toBeGreaterThan(0);
for (const child of customIconChildren) {
expect(child.getAttribute('vector-effect')).toBe('non-scaling-stroke');
}
const providerIcon = screen.getByTestId('provider-icon');
await expect.element(providerIcon).toHaveAttribute('width', '32');
await expect.element(providerIcon).toHaveAttribute('stroke', 'purple');
@@ -13,7 +13,7 @@ export default function IconShowcase() {
color="red"
size={48}
strokeWidth={4}
nonScalingStroke
absoluteStrokeWidth
/>
<LucideProvider
color="purple"
@@ -23,15 +23,9 @@ test('renders Lucide public APIs in a real browser', async () => {
await expect.element(customIcon).toHaveAttribute('width', '48');
await expect.element(customIcon).toHaveAttribute('height', '48');
await expect.element(customIcon).toHaveAttribute('stroke', 'red');
await expect.element(customIcon).toHaveAttribute('stroke-width', '4');
await expect.element(customIcon).toHaveAttribute('stroke-width', '2');
await expect.element(customIcon).toHaveClass('consumer-icon');
const customIconChildren = Array.from(customIcon.element().children);
expect(customIconChildren.length).toBeGreaterThan(0);
for (const child of customIconChildren) {
expect(child.getAttribute('vector-effect')).toBe('non-scaling-stroke');
}
const providerIcon = screen.getByTestId('provider-icon');
await expect.element(providerIcon).toHaveAttribute('width', '32');
await expect.element(providerIcon).toHaveAttribute('stroke', 'purple');
@@ -11,7 +11,7 @@ export default function IconShowcase() {
color="red"
size={48}
strokeWidth={4}
nonScalingStroke
absoluteStrokeWidth
/>
<LucideProvider
color="purple"
@@ -22,15 +22,9 @@ test('renders Lucide public APIs and survives router navigation', async () => {
await expect.element(customIcon).toHaveAttribute('width', '48');
await expect.element(customIcon).toHaveAttribute('height', '48');
await expect.element(customIcon).toHaveAttribute('stroke', 'red');
await expect.element(customIcon).toHaveAttribute('stroke-width', '4');
await expect.element(customIcon).toHaveAttribute('stroke-width', '2');
await expect.element(customIcon).toHaveClass('consumer-icon');
const customIconChildren = Array.from(customIcon.element().children);
expect(customIconChildren.length).toBeGreaterThan(0);
for (const child of customIconChildren) {
expect(child.getAttribute('vector-effect')).toBe('non-scaling-stroke');
}
const providerIcon = page.getByTestId('provider-icon');
await expect.element(providerIcon).toHaveAttribute('width', '32');
await expect.element(providerIcon).toHaveAttribute('stroke', 'purple');
@@ -11,7 +11,7 @@ export default function IconShowcase() {
color="red"
size={48}
strokeWidth={4}
nonScalingStroke
absoluteStrokeWidth
/>
<LucideProvider
color="purple"
@@ -30,15 +30,9 @@ test('renders Lucide public APIs and survives router navigation', async () => {
await expect.element(customIcon).toHaveAttribute('width', '48');
await expect.element(customIcon).toHaveAttribute('height', '48');
await expect.element(customIcon).toHaveAttribute('stroke', 'red');
await expect.element(customIcon).toHaveAttribute('stroke-width', '4');
await expect.element(customIcon).toHaveAttribute('stroke-width', '2');
await expect.element(customIcon).toHaveClass('consumer-icon');
const customIconChildren = Array.from(customIcon.element().children);
expect(customIconChildren.length).toBeGreaterThan(0);
for (const child of customIconChildren) {
expect(child.getAttribute('vector-effect')).toBe('non-scaling-stroke');
}
const providerIcon = screen.getByTestId('provider-icon');
await expect.element(providerIcon).toHaveAttribute('width', '32');
await expect.element(providerIcon).toHaveAttribute('stroke', 'purple');
@@ -11,7 +11,7 @@ export default function IconShowcase() {
color="red"
size={48}
strokeWidth={4}
nonScalingStroke
absoluteStrokeWidth
/>
<LucideProvider
color="purple"
@@ -17,15 +17,9 @@ test('renders Lucide public APIs in a real browser', async () => {
await expect.element(customIcon).toHaveAttribute('width', '48');
await expect.element(customIcon).toHaveAttribute('height', '48');
await expect.element(customIcon).toHaveAttribute('stroke', 'red');
await expect.element(customIcon).toHaveAttribute('stroke-width', '4');
await expect.element(customIcon).toHaveAttribute('stroke-width', '2');
await expect.element(customIcon).toHaveClass('consumer-icon');
const customIconChildren = Array.from(customIcon.element().children);
expect(customIconChildren.length).toBeGreaterThan(0);
for (const child of customIconChildren) {
expect(child.getAttribute('vector-effect')).toBe('non-scaling-stroke');
}
const providerIcon = screen.getByTestId('provider-icon');
await expect.element(providerIcon).toHaveAttribute('width', '32');
await expect.element(providerIcon).toHaveAttribute('stroke', 'purple');
@@ -11,7 +11,7 @@
color="red"
size={48}
strokeWidth={4}
nonScalingStroke
absoluteStrokeWidth
/>
<LucideProvider>
<House data-testid="provider-icon" />
@@ -17,15 +17,9 @@ test('renders Lucide public APIs in a real browser', async () => {
await expect.element(customIcon).toHaveAttribute('width', '48');
await expect.element(customIcon).toHaveAttribute('height', '48');
await expect.element(customIcon).toHaveAttribute('stroke', 'red');
await expect.element(customIcon).toHaveAttribute('stroke-width', '4');
await expect.element(customIcon).toHaveAttribute('stroke-width', '2');
await expect.element(customIcon).toHaveClass('consumer-icon');
const customIconChildren = Array.from(customIcon.element().children);
expect(customIconChildren.length).toBeGreaterThan(0);
for (const child of customIconChildren) {
expect(child.getAttribute('vector-effect')).toBe('non-scaling-stroke');
}
const providerIcon = screen.getByTestId('provider-icon');
await expect.element(providerIcon).toHaveAttribute('width', '32');
await expect.element(providerIcon).toHaveAttribute('stroke', 'purple');
@@ -11,7 +11,7 @@
color="red"
size={48}
strokeWidth={4}
nonScalingStroke
absoluteStrokeWidth
/>
<LucideProvider>
<House data-testid="provider-icon" />
@@ -17,15 +17,9 @@ test('renders Lucide public APIs in a real browser', async () => {
await expect.element(customIcon).toHaveAttribute('width', '48');
await expect.element(customIcon).toHaveAttribute('height', '48');
await expect.element(customIcon).toHaveAttribute('stroke', 'red');
await expect.element(customIcon).toHaveAttribute('stroke-width', '4');
await expect.element(customIcon).toHaveAttribute('stroke-width', '2');
await expect.element(customIcon).toHaveClass('consumer-icon');
const customIconChildren = Array.from(customIcon.element().children);
expect(customIconChildren.length).toBeGreaterThan(0);
for (const child of customIconChildren) {
expect(child.getAttribute('vector-effect')).toBe('non-scaling-stroke');
}
const providerIcon = screen.getByTestId('provider-icon');
await expect.element(providerIcon).toHaveAttribute('width', '32');
await expect.element(providerIcon).toHaveAttribute('stroke', 'purple');
@@ -12,7 +12,7 @@ import LucideProvider from './LucideProvider.vue';
color="red"
:size="48"
:stroke-width="4"
non-scaling-stroke
absolute-stroke-width
/>
<LucideProvider>
<House data-testid="provider-icon" />
@@ -17,15 +17,9 @@ test('renders Lucide public APIs in a real browser', async () => {
await expect.element(customIcon).toHaveAttribute('width', '48');
await expect.element(customIcon).toHaveAttribute('height', '48');
await expect.element(customIcon).toHaveAttribute('stroke', 'red');
await expect.element(customIcon).toHaveAttribute('stroke-width', '4');
await expect.element(customIcon).toHaveAttribute('stroke-width', '2');
await expect.element(customIcon).toHaveClass('consumer-icon');
const customIconChildren = Array.from(customIcon.element().children);
expect(customIconChildren.length).toBeGreaterThan(0);
for (const child of customIconChildren) {
expect(child.getAttribute('vector-effect')).toBe('non-scaling-stroke');
}
const providerIcon = screen.getByTestId('provider-icon');
await expect.element(providerIcon).toHaveAttribute('width', '32');
await expect.element(providerIcon).toHaveAttribute('stroke', 'purple');
@@ -12,7 +12,7 @@ import LucideProvider from './LucideProvider.vue';
color="red"
:size="48"
:stroke-width="4"
non-scaling-stroke
absolute-stroke-width
/>
<LucideProvider>
<House data-testid="provider-icon" />
@@ -17,15 +17,9 @@ test('renders Lucide public APIs in a real browser', async () => {
await expect.element(customIcon).toHaveAttribute('width', '48');
await expect.element(customIcon).toHaveAttribute('height', '48');
await expect.element(customIcon).toHaveAttribute('stroke', 'red');
await expect.element(customIcon).toHaveAttribute('stroke-width', '4');
await expect.element(customIcon).toHaveAttribute('stroke-width', '2');
await expect.element(customIcon).toHaveClass('consumer-icon');
const customIconChildren = Array.from(customIcon.element().children);
expect(customIconChildren.length).toBeGreaterThan(0);
for (const child of customIconChildren) {
expect(child.getAttribute('vector-effect')).toBe('non-scaling-stroke');
}
const providerIcon = screen.getByTestId('provider-icon');
await expect.element(providerIcon).toHaveAttribute('width', '32');
await expect.element(providerIcon).toHaveAttribute('stroke', 'purple');
@@ -12,7 +12,7 @@ import LucideProvider from './LucideProvider.vue';
color="red"
:size="48"
:stroke-width="4"
non-scaling-stroke
absolute-stroke-width
/>
<LucideProvider>
<House data-testid="provider-icon" />
@@ -17,15 +17,9 @@ test('renders Lucide public APIs in a real browser', async () => {
await expect.element(customIcon).toHaveAttribute('width', '48');
await expect.element(customIcon).toHaveAttribute('height', '48');
await expect.element(customIcon).toHaveAttribute('stroke', 'red');
await expect.element(customIcon).toHaveAttribute('stroke-width', '4');
await expect.element(customIcon).toHaveAttribute('stroke-width', '2');
await expect.element(customIcon).toHaveClass('consumer-icon');
const customIconChildren = Array.from(customIcon.element().children);
expect(customIconChildren.length).toBeGreaterThan(0);
for (const child of customIconChildren) {
expect(child.getAttribute('vector-effect')).toBe('non-scaling-stroke');
}
const providerIcon = screen.getByTestId('provider-icon');
await expect.element(providerIcon).toHaveAttribute('width', '32');
await expect.element(providerIcon).toHaveAttribute('stroke', 'purple');
File diff suppressed because one or more lines are too long
+6
View File
@@ -19,6 +19,11 @@ export interface LucideConfig {
* @default 2
*/
strokeWidth: number;
/**
* Whether to use absolute stroke width.
* @deprecated Use `nonScalingStroke` instead.
*/
absoluteStrokeWidth: boolean;
/**
* Whether stroke width should be scaled to appear uniform regardless of icon size.
* @default false
@@ -41,6 +46,7 @@ export const lucideDefaultConfig: LucideConfig = {
color: 'currentColor',
size: 24,
strokeWidth: 2,
absoluteStrokeWidth: false,
nonScalingStroke: false,
};
+33 -2
View File
@@ -46,6 +46,7 @@ describe('LucideIconBase', () => {
let color: WritableSignal<string | undefined>;
let size: WritableSignal<string | number | undefined>;
let strokeWidth: WritableSignal<string | number | undefined>;
let absoluteStrokeWidth: WritableSignal<boolean | undefined>;
let nonScalingStroke: WritableSignal<boolean | undefined>;
const getSvgAttribute = (attr: string) => fixture.nativeElement.getAttribute(attr);
const expectSvgClasses = (classes: string[], svg: SVGElement = fixture.nativeElement) => {
@@ -62,6 +63,7 @@ describe('LucideIconBase', () => {
inputBinding('color', color),
inputBinding('size', size),
inputBinding('strokeWidth', strokeWidth),
inputBinding('absoluteStrokeWidth', absoluteStrokeWidth),
inputBinding('nonScalingStroke', nonScalingStroke),
],
});
@@ -71,6 +73,7 @@ describe('LucideIconBase', () => {
color = signal(undefined);
size = signal(undefined);
strokeWidth = signal(undefined);
absoluteStrokeWidth = signal(undefined);
nonScalingStroke = signal(undefined);
fixture = createComponent();
component = fixture.componentInstance;
@@ -159,6 +162,22 @@ describe('LucideIconBase', () => {
});
});
describe('absoluteStrokeWidth', () => {
it('should adjust stroke width', () => {
strokeWidth.set(2);
size.set(32);
absoluteStrokeWidth.set(true);
fixture.detectChanges();
expect(getSvgAttribute('stroke-width')).toBe('1.5');
});
it('should not set vector-effect on children', () => {
absoluteStrokeWidth.set(true);
for (const child of fixture.nativeElement.children) {
expect(child.getAttribute('vector-effect')).toBeNull();
}
});
});
describe('nonScalingStroke', () => {
it('should not adjust stroke width', () => {
strokeWidth.set(2);
@@ -213,6 +232,7 @@ describe('LucideIconBase', () => {
strokeWidth: 1,
size: 12,
nonScalingStroke: true,
absoluteStrokeWidth: true,
}),
],
});
@@ -234,12 +254,12 @@ describe('LucideIconBase', () => {
describe('strokeWidth', () => {
it('should use stroke width from config', () => {
fixture.detectChanges();
expect(getSvgAttribute('stroke-width')).toBe('1');
expect(getSvgAttribute('stroke-width')).toBe('2');
});
it('should use override stroke width from config', () => {
strokeWidth.set(3);
fixture.detectChanges();
expect(getSvgAttribute('stroke-width')).toBe('3');
expect(getSvgAttribute('stroke-width')).toBe('6');
});
});
describe('size', () => {
@@ -255,6 +275,17 @@ describe('LucideIconBase', () => {
expect(getSvgAttribute('height')).toBe('48');
});
});
describe('absoluteStrokeWidth', () => {
it('should use absoluteStrokeWidth from config', () => {
fixture.detectChanges();
expect(getSvgAttribute('stroke-width')).toBe('2');
});
it('should override absoluteStrokeWidth', () => {
absoluteStrokeWidth.set(false);
fixture.detectChanges();
expect(getSvgAttribute('stroke-width')).toBe('1');
});
});
describe('nonScalingStroke', () => {
it('should use nonScalingStroke from config', () => {
for (const child of fixture.nativeElement.children) {
+7
View File
@@ -73,6 +73,12 @@ export abstract class LucideIconBase {
readonly strokeWidth = input(this.iconConfig.strokeWidth, {
transform: (value: Nullable<string | number>) => value ?? this.iconConfig.strokeWidth,
});
/**
* Whether to use absolute stroke width.
*/
readonly absoluteStrokeWidth = input(this.iconConfig.absoluteStrokeWidth, {
transform: (value: Nullable<boolean>) => value ?? this.iconConfig.absoluteStrokeWidth,
});
/**
* If set to true, it adds [`vector-effect="non-scaling-stroke"`](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/vector-effect) to child elements.
*/
@@ -91,6 +97,7 @@ export abstract class LucideIconBase {
height: this.height() ?? this.size(),
color: this.color(),
strokeWidth: this.strokeWidth(),
absoluteStrokeWidth: this.absoluteStrokeWidth(),
nonScalingStroke: this.nonScalingStroke(),
className: this.class() ?? undefined,
hasA11yProp: this.title() != null,
+4
View File
@@ -20,6 +20,10 @@ interface LucideIconProps {
size: Signal<Nullable<number | string>>;
color: Signal<Nullable<string>>;
strokeWidth: Signal<Nullable<number | string>>;
/**
* @deprecated Use `nonScalingStroke` instead.
*/
absoluteStrokeWidth: Signal<Nullable<boolean>>;
nonScalingStroke: Signal<Nullable<boolean>>;
}
+2
View File
@@ -9,6 +9,7 @@ const {
width,
height,
'stroke-width': strokeWidth = 2,
absoluteStrokeWidth = false,
nonScalingStroke = false,
iconNode = [],
icon = {
@@ -25,6 +26,7 @@ const [, builtIconAttributes, builtIconNode = []] = buildLucideIconNode(icon, {
width: width ?? size,
height: height ?? size,
strokeWidth,
absoluteStrokeWidth,
nonScalingStroke,
...(className !== undefined && { className }),
hasA11yProp: hasA11yProp(rest),
+4
View File
@@ -11,6 +11,10 @@ export type LucideProps = SVGAttributes & {
color?: string;
size?: number | string;
'stroke-width'?: number | string;
/**
* @deprecated Use `nonScalingStroke` instead.
*/
absoluteStrokeWidth?: boolean;
nonScalingStroke?: boolean;
class?: string;
title?: string;
+4 -1
View File
@@ -5,7 +5,7 @@ import { Icon, Rocket } from '../src/lucide-astro';
describe('Using Icon Component', async () => {
const { container } = await render(Icon, {
props: { iconNode: airVent, size: 48, stroke: 'red' },
props: { iconNode: airVent, size: 48, stroke: 'red', absoluteStrokeWidth: true },
});
it('should render icon and match snapshot', async () => {
@@ -24,6 +24,7 @@ describe('Icon Component Accessibility', () => {
iconNode: airVent,
size: 48,
stroke: 'red',
absoluteStrokeWidth: true,
},
});
@@ -35,6 +36,7 @@ describe('Icon Component Accessibility', () => {
props: {
size: 48,
stroke: 'red',
absoluteStrokeWidth: true,
'aria-label': 'Release icon',
},
});
@@ -47,6 +49,7 @@ describe('Icon Component Accessibility', () => {
props: {
size: 48,
stroke: 'red',
absoluteStrokeWidth: true,
title: 'Release icon',
},
});
@@ -7,7 +7,7 @@ exports[`Using Icon Component > should render icon and match snapshot 1`] = `
viewbox="0 0 24 24"
fill="none"
stroke="red"
stroke-width="2"
stroke-width="1"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide"
@@ -208,3 +208,34 @@ exports[`Using lucide icon components > should render the icon with default attr
</path>
</svg>
`;
exports[`Using lucide icon components > should scale the strokeWidth when absoluteStrokeWidth is set 1`] = `
<svg xmlns="http://www.w3.org/2000/svg"
width="48"
height="48"
viewbox="0 0 24 24"
fill="none"
stroke="red"
stroke-width="1"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3"
aria-hidden="true"
>
<rect width="18"
height="18"
x="3"
y="3"
rx="2"
>
</rect>
<path d="M3 9h18">
</path>
<path d="M3 15h18">
</path>
<path d="M9 3v18">
</path>
<path d="M15 3v18">
</path>
</svg>
`;
+19
View File
@@ -65,6 +65,25 @@ describe('Using lucide icon components', () => {
expect(PenIconContainer.innerHTML).toBe(Edit2Container.innerHTML);
});
it('should scale the strokeWidth when absoluteStrokeWidth is set', async () => {
const { container } = await render(Grid, {
props: {
size: 48,
color: 'red',
absoluteStrokeWidth: true,
},
});
const SVGElement = container.firstElementChild;
expect(SVGElement).toHaveAttribute('stroke', 'red');
expect(SVGElement).toHaveAttribute('width', '48');
expect(SVGElement).toHaveAttribute('height', '48');
expect(SVGElement).toHaveAttribute('stroke-width', '1');
expect(container.innerHTML).toMatchSnapshot();
});
it('should apply vector-effect when nonScalingStroke is set', async () => {
const { container } = await render(Grid, {
props: {
@@ -52,6 +52,26 @@ describe('buildLucideIconNode', () => {
expect(HouseSVG[1]['stroke-width']).toBe(12);
});
it('should scale the stroke width', () => {
const HouseSVG = buildLucideIconNode(House, {
size: 12,
strokeWidth: 2,
absoluteStrokeWidth: true,
});
expect(HouseSVG[1]['stroke-width']).toBe(4);
});
it('should not scale the stroke width', () => {
const HouseSVG = buildLucideIconNode(House, {
size: 12,
strokeWidth: 2,
absoluteStrokeWidth: false,
});
expect(HouseSVG[1]['stroke-width']).toBe(2);
});
it('should set non-scaling-stroke to child nodes', () => {
const HouseSVG = buildLucideIconNode(House, { nonScalingStroke: true });
+4
View File
@@ -23,6 +23,7 @@ type IconComponentProps = LucideProps &
* @param {string} props.color - The color of the icon
* @param {number} props.size - The size of the icon
* @param {number} props.strokeWidth - The stroke width of the icon
* @param {boolean} props.absoluteStrokeWidth - Whether to use absolute stroke width
* @param {boolean} props.nonScalingStroke - Whether to use non scaling strokes
* @param {string} props.class - The class name of the icon
* @param {LucideIconNode[]} props.children - The children of the icon
@@ -36,6 +37,7 @@ const Icon = ({
width,
height,
strokeWidth,
absoluteStrokeWidth,
nonScalingStroke,
children,
iconNode = [],
@@ -50,6 +52,7 @@ const Icon = ({
const {
size: contextSize = 24,
strokeWidth: contextStrokeWidth = 2,
absoluteStrokeWidth: contextAbsoluteStrokeWidth = false,
nonScalingStroke: contextNonScalingStroke = false,
color: contextColor = 'currentColor',
class: contextClass = '',
@@ -60,6 +63,7 @@ const Icon = ({
width: width ?? size ?? contextSize,
height: height ?? size ?? contextSize,
strokeWidth: strokeWidth ?? contextStrokeWidth,
absoluteStrokeWidth: absoluteStrokeWidth ?? contextAbsoluteStrokeWidth,
nonScalingStroke: nonScalingStroke ?? contextNonScalingStroke,
className: mergeClasses(contextClass, classes as string),
hasA11yProp: Boolean(children) || hasA11yProp(rest),
+12 -1
View File
@@ -5,12 +5,17 @@ const LucideContext = createContext<{
size?: number;
color?: string;
strokeWidth?: number;
/**
* @deprecated Use `nonScalingStroke` instead.
*/
absoluteStrokeWidth?: boolean;
nonScalingStroke?: boolean;
class?: string;
}>({
size: 24,
color: 'currentColor',
strokeWidth: 2,
absoluteStrokeWidth: false,
nonScalingStroke: false,
class: '',
});
@@ -20,6 +25,10 @@ interface LucideProviderProps {
size?: number;
color?: string;
strokeWidth?: number;
/**
* @deprecated Use `nonScalingStroke` instead.
*/
absoluteStrokeWidth?: boolean;
nonScalingStroke?: boolean;
class?: string;
}
@@ -29,6 +38,7 @@ export function LucideProvider({
size,
color,
strokeWidth,
absoluteStrokeWidth,
nonScalingStroke,
class: className,
}: LucideProviderProps) {
@@ -37,10 +47,11 @@ export function LucideProvider({
size,
color,
strokeWidth,
absoluteStrokeWidth,
nonScalingStroke,
class: className,
}),
[size, color, strokeWidth, nonScalingStroke, className],
[size, color, strokeWidth, absoluteStrokeWidth, nonScalingStroke, className],
);
return h(LucideContext.Provider, { value }, children);
+4
View File
@@ -19,6 +19,10 @@ export interface LucideProps extends Partial<Omit<SVGAttributes, 'ref' | 'size'>
width?: string | number;
height?: string | number;
strokeWidth?: string | number;
/**
* @deprecated Use `nonScalingStroke` instead.
*/
absoluteStrokeWidth?: boolean;
nonScalingStroke?: boolean;
}
@@ -11,6 +11,7 @@ describe('Using Icon Component', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
/>,
);
@@ -23,6 +24,7 @@ describe('Using Icon Component', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
/>,
);
@@ -37,6 +39,7 @@ describe('Icon Component Accessibility', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
aria-label="Air conditioning"
/>,
);
@@ -50,6 +53,7 @@ describe('Icon Component Accessibility', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
title="Air conditioning"
/>,
);
@@ -63,6 +67,7 @@ describe('Icon Component Accessibility', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
>
<title>Some title</title>
</Icon>,
@@ -77,6 +82,7 @@ describe('Icon Component Accessibility', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
aria-hidden={false}
/>,
);
@@ -9,7 +9,7 @@ exports[`Using Icon Component > should render icon and match snapshot 1`] = `
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
stroke-width="1"
viewBox="0 0 24 24"
width="48"
xmlns="http://www.w3.org/2000/svg"
@@ -101,3 +101,34 @@ exports[`Using lucide icon components > should render an component 1`] = `
</path>
</svg>
`;
exports[`Using lucide icon components > should scale the strokeWidth when absoluteStrokeWidth is set 1`] = `
<svg xmlns="http://www.w3.org/2000/svg"
width="48"
height="48"
viewbox="0 0 24 24"
fill="none"
stroke="red"
stroke-width="1"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3"
aria-hidden="true"
>
<rect width="18"
height="18"
x="3"
y="3"
rx="2"
>
</rect>
<path d="M3 9h18">
</path>
<path d="M3 15h18">
</path>
<path d="M9 3v18">
</path>
<path d="M15 3v18">
</path>
</svg>
`;
@@ -35,6 +35,22 @@ describe('Using LucideProvider', () => {
expect(IconComponent).toHaveAttribute('stroke-width', '4');
});
it('should render the icon with LucideProvider and custom absoluteStrokeWidth', () => {
const { container } = render(
<LucideProvider
size={48}
color="red"
absoluteStrokeWidth
>
<House />
</LucideProvider>,
);
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('stroke-width', '1');
});
it('should render the icon with LucideProvider and custom nonScalingStroke', () => {
const { container } = render(
<LucideProvider
@@ -70,6 +70,25 @@ describe('Using lucide icon components', () => {
expect(PenIconRenderedHTML).toBe(Edit2Container.innerHTML);
});
it('should scale the strokeWidth when absoluteStrokeWidth is set', () => {
const { container } = render(
<Grid
size={48}
stroke="red"
absoluteStrokeWidth
/>,
);
const SVGElement = container.firstElementChild;
expect(SVGElement).toHaveAttribute('stroke', 'red');
expect(SVGElement).toHaveAttribute('width', '48');
expect(SVGElement).toHaveAttribute('height', '48');
expect(SVGElement).toHaveAttribute('stroke-width', '1');
expect(container.innerHTML).toMatchSnapshot();
});
it('should apply vector-effect when nonScalingStroke is set', () => {
const { container } = render(
<Grid
+4
View File
@@ -44,6 +44,7 @@ const toNativeSvgAttributes = (attributes: Record<string, unknown>) => {
* @param {string} props.color - The color of the icon
* @param {number} props.size - The size of the icon
* @param {number} props.strokeWidth - The stroke width of the icon
* @param {boolean} props.absoluteStrokeWidth - Whether to use absolute stroke width
* @param {string} props.className - The class name of the icon
* @param {LucideIconNode[]} props.children - The children of the icon
* @param {LucideIconNode[]} props.iconNode - The icon node of the icon
@@ -58,6 +59,7 @@ const Icon = forwardRef<SVGSVGElement, IconComponentProps>(
width = size,
height = size,
strokeWidth,
absoluteStrokeWidth,
nonScalingStroke,
children,
iconNode = [],
@@ -74,6 +76,7 @@ const Icon = forwardRef<SVGSVGElement, IconComponentProps>(
const {
size: contextSize = 24,
strokeWidth: contextStrokeWidth = 2,
absoluteStrokeWidth: contextAbsoluteStrokeWidth = false,
nonScalingStroke: contextNonScalingStroke = false,
color: contextColor = 'currentColor',
className: contextClass = '',
@@ -86,6 +89,7 @@ const Icon = forwardRef<SVGSVGElement, IconComponentProps>(
width: width ?? size ?? contextSize,
height: height ?? size ?? contextSize,
strokeWidth: strokeWidth ?? contextStrokeWidth,
absoluteStrokeWidth: absoluteStrokeWidth ?? contextAbsoluteStrokeWidth,
nonScalingStroke: nonScalingStroke ?? contextNonScalingStroke,
className: mergeClasses(contextClass, className),
hasA11yProp: hasAccessibleProp,
+12 -1
View File
@@ -4,12 +4,17 @@ const LucideContext = createContext<{
size?: number;
color?: string;
strokeWidth?: number;
/**
* @deprecated Use `nonScalingStroke` instead.
*/
absoluteStrokeWidth?: boolean;
nonScalingStroke?: boolean;
className?: string;
}>({
size: 24,
color: 'currentColor',
strokeWidth: 2,
absoluteStrokeWidth: false,
nonScalingStroke: false,
className: '',
});
@@ -19,6 +24,10 @@ interface LucideProviderProps {
size?: number;
color?: string;
strokeWidth?: number;
/**
* @deprecated Use `nonScalingStroke` instead.
*/
absoluteStrokeWidth?: boolean;
nonScalingStroke?: boolean;
className?: string;
}
@@ -28,6 +37,7 @@ export function LucideProvider({
size,
color,
strokeWidth,
absoluteStrokeWidth,
nonScalingStroke,
className,
}: LucideProviderProps) {
@@ -36,10 +46,11 @@ export function LucideProvider({
size,
color,
strokeWidth,
absoluteStrokeWidth,
nonScalingStroke,
className,
}),
[size, color, strokeWidth, nonScalingStroke, className],
[size, color, strokeWidth, absoluteStrokeWidth, nonScalingStroke, className],
);
return createElement(LucideContext.Provider, { value }, children);
@@ -19,6 +19,10 @@ export interface LucideProps extends SvgProps {
width?: string | number;
height?: string | number;
className?: string;
/**
* @deprecated Use `nonScalingStroke` instead.
*/
absoluteStrokeWidth?: boolean;
nonScalingStroke?: boolean;
'data-testid'?: string;
}
@@ -17,6 +17,7 @@ describe('Using Icon Component', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
/>,
);
@@ -29,6 +30,7 @@ describe('Using Icon Component', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
/>,
);
@@ -9,7 +9,7 @@ exports[`Using Icon Component > should render icon and match snapshot 1`] = `
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
stroke-width="1"
viewBox="0 0 24 24"
width="48"
xmlns="http://www.w3.org/2000/svg"
@@ -20,7 +20,7 @@ exports[`Using Icon Component > should render icon and match snapshot 1`] = `
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
stroke-width="1"
/>
<path
d="M6 8h12"
@@ -28,7 +28,7 @@ exports[`Using Icon Component > should render icon and match snapshot 1`] = `
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
stroke-width="1"
/>
<path
d="M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12"
@@ -36,7 +36,7 @@ exports[`Using Icon Component > should render icon and match snapshot 1`] = `
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
stroke-width="1"
/>
<path
d="M6.6 15.6A2 2 0 1 0 10 17v-5"
@@ -44,7 +44,7 @@ exports[`Using Icon Component > should render icon and match snapshot 1`] = `
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
stroke-width="1"
/>
</svg>
`;
@@ -248,6 +248,66 @@ exports[`Using lucide icon components > should render an component 1`] = `
</svg>
`;
exports[`Using lucide icon components > should scale the strokeWidth when absoluteStrokeWidth is set 1`] = `
<svg xmlns="http://www.w3.org/2000/svg"
width="48"
height="48"
viewbox="0 0 24 24"
fill="none"
stroke="red"
stroke-width="1"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3"
aria-hidden="true"
>
<rect fill="none"
stroke="red"
stroke-width="1"
stroke-linecap="round"
stroke-linejoin="round"
width="18"
height="18"
x="3"
y="3"
rx="2"
>
</rect>
<path fill="none"
stroke="red"
stroke-width="1"
stroke-linecap="round"
stroke-linejoin="round"
d="M3 9h18"
>
</path>
<path fill="none"
stroke="red"
stroke-width="1"
stroke-linecap="round"
stroke-linejoin="round"
d="M3 15h18"
>
</path>
<path fill="none"
stroke="red"
stroke-width="1"
stroke-linecap="round"
stroke-linejoin="round"
d="M9 3v18"
>
</path>
<path fill="none"
stroke="red"
stroke-width="1"
stroke-linecap="round"
stroke-linejoin="round"
d="M15 3v18"
>
</path>
</svg>
`;
exports[`Using lucide icon components > should work with a single child component 1`] = `
<svg xmlns="http://www.w3.org/2000/svg"
width="24"
@@ -37,6 +37,23 @@ describe('Using LucideProvider', () => {
expect(IconComponent).toHaveAttribute('stroke-width', '4');
});
it('should render the icon with LucideProvider and custom absoluteStrokeWidth', () => {
cleanup();
const { container } = render(
<LucideProvider
size={48}
color="red"
absoluteStrokeWidth
>
<House />
</LucideProvider>,
);
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('stroke-width', '1');
});
it("should override the provider's global props when passing props to the icon", () => {
cleanup();
const { container } = render(
@@ -63,6 +63,25 @@ describe('Using lucide icon components', () => {
expect(penIcon).toStrictEqual(edit2Icon);
});
it('should scale the strokeWidth when absoluteStrokeWidth is set', () => {
const { container } = render(
<Grid
size={48}
stroke="red"
absoluteStrokeWidth
/>,
);
const SVGElement = container.firstElementChild;
expect(SVGElement).toHaveAttribute('stroke', 'red');
expect(SVGElement).toHaveAttribute('width', '48');
expect(SVGElement).toHaveAttribute('height', '48');
expect(SVGElement).toHaveAttribute('stroke-width', '1');
expect(container.innerHTML).toMatchSnapshot();
});
it('should apply vector-effect when nonScalingStroke is set', () => {
const { container } = render(
<Grid
+1
View File
@@ -42,6 +42,7 @@ async function getIconData(name: IconName) {
* @param {string} props.color - The color of the icon
* @param {number} props.size - The size of the icon
* @param {number} props.strokeWidth - The stroke width of the icon
* @param {boolean} props.absoluteStrokeWidth - Whether to use absolute stroke width
* @param {boolean} props.nonScalingStroke - Whether to use non scaling strokes
* @param {string} props.className - The class name of the icon
* @param {LucideIconNode[]} props.children - The children of the icon
+4
View File
@@ -25,6 +25,7 @@ type IconComponentProps = LucideProps &
* @param {string} props.color - The color of the icon
* @param {number} props.size - The size of the icon
* @param {number} props.strokeWidth - The stroke width of the icon
* @param {boolean} props.absoluteStrokeWidth - Whether to use absolute stroke width
* @param {boolean} props.nonScalingStroke - Whether to use non scaling strokes
* @param {string} props.className - The class name of the icon
* @param {LucideIconNode[]} props.children - The children of the icon
@@ -40,6 +41,7 @@ const Icon = forwardRef<SVGSVGElement, IconComponentProps>(
width,
height,
strokeWidth,
absoluteStrokeWidth,
nonScalingStroke,
className = '',
children,
@@ -56,6 +58,7 @@ const Icon = forwardRef<SVGSVGElement, IconComponentProps>(
const {
size: contextSize = 24,
strokeWidth: contextStrokeWidth = 2,
absoluteStrokeWidth: contextAbsoluteStrokeWidth = false,
nonScalingStroke: contextNonScalingStroke = false,
color: contextColor = 'currentColor',
className: contextClass = '',
@@ -68,6 +71,7 @@ const Icon = forwardRef<SVGSVGElement, IconComponentProps>(
width: width ?? size ?? contextSize,
height: height ?? size ?? contextSize,
strokeWidth: strokeWidth ?? contextStrokeWidth,
absoluteStrokeWidth: absoluteStrokeWidth ?? contextAbsoluteStrokeWidth,
nonScalingStroke: nonScalingStroke ?? contextNonScalingStroke,
className: mergeClasses(contextClass, className),
hasA11yProp: hasAccessibleProp,
+7 -1
View File
@@ -7,6 +7,10 @@ type LucideConfig = {
size: number;
color: string;
strokeWidth: number;
/**
* @deprecated Use `nonScalingStroke` instead.
*/
absoluteStrokeWidth: boolean;
nonScalingStroke: boolean;
className: string;
};
@@ -22,6 +26,7 @@ export function LucideProvider({
size,
color,
strokeWidth,
absoluteStrokeWidth,
nonScalingStroke,
className,
}: LucideProviderProps) {
@@ -30,10 +35,11 @@ export function LucideProvider({
size,
color,
strokeWidth,
absoluteStrokeWidth,
nonScalingStroke,
className,
}),
[size, color, strokeWidth, nonScalingStroke, className],
[size, color, strokeWidth, absoluteStrokeWidth, nonScalingStroke, className],
);
return createElement(LucideContext.Provider, { value }, children);
+4
View File
@@ -19,6 +19,10 @@ type ElementAttributes = RefAttributes<SVGSVGElement> & SVGAttributes;
export interface LucideProps extends ElementAttributes {
size?: string | number;
/**
* @deprecated Use `nonScalingStroke` instead.
*/
absoluteStrokeWidth?: boolean;
nonScalingStroke?: boolean;
}
@@ -70,6 +70,7 @@ describe('Using DynamicIcon Component', () => {
name="circle"
size={48}
stroke="red"
absoluteStrokeWidth
/>,
);
@@ -11,6 +11,7 @@ describe('Using Icon Component', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
/>,
);
@@ -23,6 +24,7 @@ describe('Using Icon Component', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
/>,
);
@@ -37,6 +39,7 @@ describe('Icon Component Accessibility', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
/>,
);
@@ -49,6 +52,7 @@ describe('Icon Component Accessibility', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
aria-label="Air conditioning"
/>,
);
@@ -62,6 +66,7 @@ describe('Icon Component Accessibility', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
// @ts-expect-error
title="Air conditioning"
/>,
@@ -76,6 +81,7 @@ describe('Icon Component Accessibility', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
>
<title>Some title</title>
</Icon>,
@@ -90,6 +96,7 @@ describe('Icon Component Accessibility', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
aria-hidden={false}
/>,
);
@@ -9,7 +9,7 @@ exports[`Using Icon Component > should render icon and match snapshot 1`] = `
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
stroke-width="1"
viewBox="0 0 24 24"
width="48"
xmlns="http://www.w3.org/2000/svg"
@@ -23,6 +23,29 @@ exports[`Using LucideProvider > should render the icon with LucideProvider 1`] =
</svg>
`;
exports[`Using LucideProvider > should render the icon with LucideProvider and custom absoluteStrokeWidth 1`] = `
<svg
aria-hidden="true"
class="lucide lucide-house lucide-home"
fill="none"
height="48"
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1"
viewBox="0 0 24 24"
width="48"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8"
/>
<path
d="M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"
/>
</svg>
`;
exports[`Using LucideProvider > should render the icon with LucideProvider and custom nonScalingStroke 1`] = `
<svg
aria-hidden="true"
@@ -7,7 +7,7 @@ exports[`Using dynamicImports > should render icons dynamically by using the dyn
viewbox="0 0 24 24"
fill="none"
stroke="red"
stroke-width="2"
stroke-width="1"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-face-slightly-smiling lucide-smile"
@@ -101,3 +101,34 @@ exports[`Using lucide icon components > should render an component 1`] = `
</path>
</svg>
`;
exports[`Using lucide icon components > should scale the strokeWidth when absoluteStrokeWidth is set 1`] = `
<svg xmlns="http://www.w3.org/2000/svg"
width="48"
height="48"
viewbox="0 0 24 24"
fill="none"
stroke="red"
stroke-width="1"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3"
aria-hidden="true"
>
<rect width="18"
height="18"
x="3"
y="3"
rx="2"
>
</rect>
<path d="M3 9h18">
</path>
<path d="M3 15h18">
</path>
<path d="M9 3v18">
</path>
<path d="M15 3v18">
</path>
</svg>
`;
@@ -46,6 +46,23 @@ describe('Using LucideProvider', () => {
expect(IconComponent).toHaveAttribute('stroke-width', '4');
});
it('should render the icon with LucideProvider and custom absoluteStrokeWidth', () => {
const { container } = render(
<LucideProvider
size={48}
color="red"
absoluteStrokeWidth
>
<House />
</LucideProvider>,
);
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('stroke-width', '1');
expect(container.firstChild).toMatchSnapshot();
});
it('should render the icon with LucideProvider and custom nonScalingStroke', () => {
const { container } = render(
<LucideProvider
@@ -27,6 +27,7 @@ describe('Using dynamicImports', () => {
name="smile"
size={48}
stroke="red"
absoluteStrokeWidth
/>,
);
@@ -69,6 +69,25 @@ describe('Using lucide icon components', () => {
expect(PenIconRenderedHTML).toBe(Edit2Container.innerHTML);
});
it('should scale the strokeWidth when absoluteStrokeWidth is set', () => {
const { container, getByTestId } = render(
<Grid
size={48}
stroke="red"
absoluteStrokeWidth
/>,
);
const SVGElement = container.firstElementChild;
expect(SVGElement).toHaveAttribute('stroke', 'red');
expect(SVGElement).toHaveAttribute('width', '48');
expect(SVGElement).toHaveAttribute('height', '48');
expect(SVGElement).toHaveAttribute('stroke-width', '1');
expect(container.innerHTML).toMatchSnapshot();
});
it('should apply vector-effect when nonScalingStroke is set', () => {
const { container, getByTestId } = render(
<Grid
+2
View File
@@ -25,6 +25,7 @@ const Icon = (props: LucideProps & IconProps) => {
'class',
'icon',
'iconNode',
'absoluteStrokeWidth',
'nonScalingStroke',
]);
@@ -45,6 +46,7 @@ const Icon = (props: LucideProps & IconProps) => {
width: localProps.width ?? localProps.size ?? globalProps.size,
height: localProps.height ?? localProps.size ?? globalProps.size,
strokeWidth: localProps.strokeWidth ?? globalProps.strokeWidth,
absoluteStrokeWidth: localProps.absoluteStrokeWidth ?? globalProps.absoluteStrokeWidth,
nonScalingStroke: localProps.nonScalingStroke ?? globalProps.nonScalingStroke,
className: mergeClasses('lucide-icon', globalProps.class, localProps.class),
hasA11yProp: Boolean(localProps.children) || hasA11yProp(rest),
+10
View File
@@ -4,12 +4,17 @@ export const LucideContext = createContext<{
size?: number;
color?: string;
strokeWidth?: number;
/**
* @deprecated Use `nonScalingStroke` instead.
*/
absoluteStrokeWidth?: boolean;
nonScalingStroke?: boolean;
class?: string;
}>({
size: 24,
color: 'currentColor',
strokeWidth: 2,
absoluteStrokeWidth: false,
nonScalingStroke: false,
class: '',
});
@@ -19,6 +24,10 @@ interface LucideProviderProps {
size?: number;
color?: string;
strokeWidth?: number;
/**
* @deprecated Use `nonScalingStroke` instead.
*/
absoluteStrokeWidth?: boolean;
nonScalingStroke?: boolean;
class?: string;
}
@@ -28,6 +37,7 @@ export function LucideProvider(props: LucideProviderProps) {
'size',
'color',
'strokeWidth',
'absoluteStrokeWidth',
'nonScalingStroke',
'class',
]);
+4
View File
@@ -23,6 +23,10 @@ export interface LucideProps extends SVGAttributes {
height?: string | number;
color?: string;
strokeWidth?: string | number;
/**
* @deprecated Use `nonScalingStroke` instead.
*/
absoluteStrokeWidth?: boolean;
nonScalingStroke?: boolean;
}
@@ -14,6 +14,7 @@ describe('Using Icon Component', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
/>
));
@@ -26,6 +27,7 @@ describe('Using Icon Component', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
/>
));
@@ -84,6 +86,7 @@ describe('Icon Component Accessibility', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
/>
));
@@ -96,6 +99,7 @@ describe('Icon Component Accessibility', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
aria-label="Air conditioning"
/>
));
@@ -109,6 +113,7 @@ describe('Icon Component Accessibility', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
// @ts-expect-error
title="Air conditioning"
/>
@@ -123,6 +128,7 @@ describe('Icon Component Accessibility', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
>
<title>Some title</title>
</Icon>
@@ -137,6 +143,7 @@ describe('Icon Component Accessibility', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
aria-hidden={false}
/>
));
@@ -9,7 +9,7 @@ exports[`Using Icon Component > should render icon and match snapshot 1`] = `
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
stroke-width="1"
viewBox="0 0 24 24"
width="48"
xmlns="http://www.w3.org/2000/svg"
@@ -80,3 +80,44 @@ exports[`Using lucide icon components > should render a component 1`] = `
</path>
</svg>
`;
exports[`Using lucide icon components > should scale the strokeWidth when absoluteStrokeWidth is set 1`] = `
<svg xmlns="http://www.w3.org/2000/svg"
width="48"
height="48"
viewbox="0 0 24 24"
fill="none"
stroke="red"
stroke-width="1"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3 lucide-icon"
aria-hidden="true"
data-testid="grid-icon"
>
<rect width="18"
height="18"
x="3"
y="3"
rx="2"
key="afitv7"
>
</rect>
<path d="M3 9h18"
key="1pudct"
>
</path>
<path d="M3 15h18"
key="5xshup"
>
</path>
<path d="M9 3v18"
key="fh3hqa"
>
</path>
<path d="M15 3v18"
key="14nvp0"
>
</path>
</svg>
`;
@@ -64,6 +64,22 @@ describe('Using LucideProvider', () => {
expect(container.firstElementChild).toHaveAttribute('height', '48');
});
it('should render the icon with LucideProvider and custom absoluteStrokeWidth', () => {
const { container } = render(() => (
<LucideProvider
size={48}
color="red"
absoluteStrokeWidth
>
<House />
</LucideProvider>
));
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('stroke-width', '1');
});
it("should override the provider's global props when passing props to the icon", () => {
const { container } = render(() => (
<LucideProvider
@@ -57,6 +57,28 @@ describe('Using lucide icon components', () => {
expect(PenIconRenderedHTML).toBe(Edit2Container.innerHTML);
});
it('should scale the strokeWidth when absoluteStrokeWidth is set', () => {
const testId = 'grid-icon';
const { container, getByTestId } = render(() => (
<Grid
data-testid={testId}
size={48}
color="red"
absoluteStrokeWidth
/>
));
const { attributes } = getByTestId(testId) as unknown as {
attributes: Record<string, { value: string }>;
};
expect(attributes.stroke.value).toBe('red');
expect(attributes.width.value).toBe('48');
expect(attributes.height.value).toBe('48');
expect(attributes['stroke-width'].value).toBe('1');
expect(container.innerHTML).toMatchSnapshot();
});
it('should add all classes to the element', () => {
const testClass = 'my-class';
const { container } = render(() => <Droplet class={testClass} />);
@@ -30,6 +30,12 @@ function buildLucideIconNode(icon: LucideIconData, params: LucideBuildParams = {
? mergeClasses(...classNamesFromClassName)
: mergeClasses('lucide', ...iconClassNames, ...classNamesFromClassName);
const calculatedStrokeWidth = params.absoluteStrokeWidth
? (Number(params.strokeWidth ?? defaultAttributes['stroke-width']) *
Number(icon.size ?? icon.width ?? defaultAttributes['width'])) /
Number(params.size ?? params.width ?? defaultAttributes['width'])
: (params.strokeWidth ?? defaultAttributes['stroke-width']);
const attributes = {
...Object.entries(defaultAttributes).reduce((attrs, [attrName, value]) => {
attrs[getAttributeName(attrName)] = value;
@@ -52,7 +58,7 @@ function buildLucideIconNode(icon: LucideIconData, params: LucideBuildParams = {
isDefined(params.height) && {
[getAttributeName('height')]: params.height,
}),
[getAttributeName('stroke-width')]: params.strokeWidth ?? defaultAttributes['stroke-width'],
[getAttributeName('stroke-width')]: calculatedStrokeWidth,
...(className && {
[getAttributeName('class')]: className,
}),
+5
View File
@@ -38,6 +38,11 @@ export type LucideBuildParams<TProps extends Record<string, unknown> = SVGProps>
* The stroke width.
*/
strokeWidth?: string | number;
/**
* Whether to use absolute stroke width.
* @deprecated Use `nonScalingStroke` instead.
*/
absoluteStrokeWidth?: boolean;
/**
* Adds [`vector-effect="non-scaling-stroke"`](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/vector-effect) to child elements.
*/
+2
View File
@@ -24,6 +24,7 @@ const LOCAL_PROP_KEYS = [
'class',
'icon',
'iconNode',
'absoluteStrokeWidth',
'nonScalingStroke',
] as const;
@@ -47,6 +48,7 @@ const Icon = (props: LucideProps & IconProps) => {
width: props.width ?? props.size ?? globalProps.size,
height: props.height ?? props.size ?? globalProps.size,
strokeWidth: props.strokeWidth ?? globalProps.strokeWidth,
absoluteStrokeWidth: props.absoluteStrokeWidth ?? globalProps.absoluteStrokeWidth,
nonScalingStroke: props.nonScalingStroke ?? globalProps.nonScalingStroke,
className: mergeClasses('lucide-icon', globalProps.class, props.class),
hasA11yProp: Boolean(props.children) || hasA11yProp(rest()),
+9
View File
@@ -4,12 +4,17 @@ export const LucideContext = createContext<{
size?: number;
color?: string;
strokeWidth?: number;
/**
* @deprecated Use `nonScalingStroke` instead.
*/
absoluteStrokeWidth?: boolean;
nonScalingStroke?: boolean;
class?: string;
}>({
size: 24,
color: 'currentColor',
strokeWidth: 2,
absoluteStrokeWidth: false,
nonScalingStroke: false,
class: '',
});
@@ -19,6 +24,10 @@ interface LucideProviderProps {
size?: number;
color?: string;
strokeWidth?: number;
/**
* @deprecated Use `nonScalingStroke` instead.
*/
absoluteStrokeWidth?: boolean;
nonScalingStroke?: boolean;
class?: string;
}
+4
View File
@@ -23,6 +23,10 @@ export interface LucideProps extends SVGAttributes {
height?: string | number;
color?: string;
strokeWidth?: string | number;
/**
* @deprecated Use `nonScalingStroke` instead.
*/
absoluteStrokeWidth?: boolean;
nonScalingStroke?: boolean;
}
+7
View File
@@ -14,6 +14,7 @@ describe('Using Icon Component', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
/>
));
@@ -26,6 +27,7 @@ describe('Using Icon Component', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
/>
));
@@ -85,6 +87,7 @@ describe('Icon Component Accessibility', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
/>
));
@@ -97,6 +100,7 @@ describe('Icon Component Accessibility', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
aria-label="Air conditioning"
/>
));
@@ -110,6 +114,7 @@ describe('Icon Component Accessibility', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
// @ts-expect-error
title="Air conditioning"
/>
@@ -124,6 +129,7 @@ describe('Icon Component Accessibility', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
>
<title>Some title</title>
</Icon>
@@ -138,6 +144,7 @@ describe('Icon Component Accessibility', () => {
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
aria-hidden="false"
/>
));
@@ -9,7 +9,7 @@ exports[`Using Icon Component > should render icon and match snapshot 1`] = `
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
stroke-width="1"
viewBox="0 0 24 24"
width="48"
xmlns="http://www.w3.org/2000/svg"
@@ -80,3 +80,44 @@ exports[`Using lucide icon components > should render a component 1`] = `
</path>
</svg>
`;
exports[`Using lucide icon components > should scale the strokeWidth when absoluteStrokeWidth is set 1`] = `
<svg xmlns="http://www.w3.org/2000/svg"
width="48"
height="48"
viewbox="0 0 24 24"
fill="none"
stroke="red"
stroke-width="1"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3 lucide-icon"
aria-hidden="true"
data-testid="grid-icon"
>
<rect width="18"
height="18"
x="3"
y="3"
rx="2"
key="afitv7"
>
</rect>
<path d="M3 9h18"
key="1pudct"
>
</path>
<path d="M3 15h18"
key="5xshup"
>
</path>
<path d="M9 3v18"
key="fh3hqa"
>
</path>
<path d="M15 3v18"
key="14nvp0"
>
</path>
</svg>
`;
+16
View File
@@ -65,6 +65,22 @@ describe('Using LucideProvider', () => {
expect(container.firstElementChild).toHaveAttribute('height', '48');
});
it('should render the icon with LucideProvider and custom absoluteStrokeWidth', () => {
const { container } = render(() => (
<LucideProvider
size={48}
color="red"
absoluteStrokeWidth
>
<House />
</LucideProvider>
));
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('stroke-width', '1');
});
it("should override the provider's global props when passing props to the icon", () => {
const { container } = render(() => (
<LucideProvider
@@ -57,6 +57,28 @@ describe('Using lucide icon components', () => {
expect(PenIconRenderedHTML).toBe(Edit2Container.innerHTML);
});
it('should scale the strokeWidth when absoluteStrokeWidth is set', () => {
const testId = 'grid-icon';
const { container, getByTestId } = render(() => (
<Grid
data-testid={testId}
size={48}
color="red"
absoluteStrokeWidth
/>
));
const { attributes } = getByTestId(testId) as unknown as {
attributes: Record<string, { value: string }>;
};
expect(attributes.stroke.value).toBe('red');
expect(attributes.width.value).toBe('48');
expect(attributes.height.value).toBe('48');
expect(attributes['stroke-width'].value).toBe('1');
expect(container.innerHTML).toMatchSnapshot();
});
it('should add all classes to the element', () => {
const testClass = 'my-class';
const { container } = render(() => <Droplet class={testClass} />);
+2
View File
@@ -13,6 +13,7 @@
width = size,
height = size,
strokeWidth = globalProps.strokeWidth ?? 2,
absoluteStrokeWidth = globalProps.absoluteStrokeWidth ?? false,
nonScalingStroke = globalProps.nonScalingStroke ?? false,
iconNode = [],
icon = {
@@ -37,6 +38,7 @@
width,
height,
strokeWidth,
absoluteStrokeWidth,
nonScalingStroke,
// @TODO: maybe drop the extra `lucide-icon` class altogether.
className: mergeClasses('lucide-icon', globalProps.class),
+4
View File
@@ -6,6 +6,10 @@ export interface LucideGlobalContext {
color?: string;
size?: number;
strokeWidth?: number;
/**
* @deprecated Use `nonScalingStroke` instead.
*/
absoluteStrokeWidth?: boolean;
nonScalingStroke?: boolean;
class?: string;
}
+4
View File
@@ -22,6 +22,10 @@ export type LucideProps = Attrs & {
color?: string;
size?: number | string;
strokeWidth?: number | string;
/**
* @deprecated Use `nonScalingStroke` instead.
*/
absoluteStrokeWidth?: boolean;
nonScalingStroke?: boolean;
children?: Snippet;
title?: string;
+7
View File
@@ -12,6 +12,7 @@ describe('Using Icon Component', () => {
iconNode: airVent,
size: 48,
color: 'red',
absoluteStrokeWidth: true,
},
});
@@ -24,6 +25,7 @@ describe('Using Icon Component', () => {
iconNode: airVent,
size: 48,
color: 'red',
absoluteStrokeWidth: true,
},
});
@@ -38,6 +40,7 @@ describe('Icon Component Accessibility', () => {
iconNode: airVent,
size: 48,
color: 'red',
absoluteStrokeWidth: true,
},
});
@@ -50,6 +53,7 @@ describe('Icon Component Accessibility', () => {
iconNode: airVent,
size: 48,
color: 'red',
absoluteStrokeWidth: true,
'aria-label': 'Air conditioning',
},
});
@@ -63,6 +67,7 @@ describe('Icon Component Accessibility', () => {
iconNode: airVent,
size: 48,
color: 'red',
absoluteStrokeWidth: true,
title: 'Air conditioning',
},
});
@@ -76,6 +81,7 @@ describe('Icon Component Accessibility', () => {
iconNode: airVent,
size: 48,
color: 'red',
absoluteStrokeWidth: true,
'aria-label': 'Air conditioning',
},
});
@@ -89,6 +95,7 @@ describe('Icon Component Accessibility', () => {
iconNode: airVent,
size: 48,
color: 'red',
absoluteStrokeWidth: true,
'aria-hidden': 'false',
},
});
@@ -9,7 +9,7 @@ exports[`Using Icon Component > should render icon and match snapshot 1`] = `
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
stroke-width="1"
viewBox="0 0 24 24"
width="48"
xmlns="http://www.w3.org/2000/svg"

Some files were not shown because too many files have changed in this diff Show More