Skip to content

Commit 0c6dfeb

Browse files
ericfennisCopilot
andauthored
feat(context-providers): Adding Context providers (#3315)
* Add context provider for svelte package * Add context provider for react * Remove old vue 2 package * Add @lucide/vue package * Remove old vue 2 doc * Update docs * Adjust export template * Add context provider to solid package * Fix context * Implement context providers * Add context provider to preact * Adjust vue package! * Fix tests * Format code * Add context provider for Vue * Improve memoization * Update packages/lucide-react-native/tests/context.spec.tsx Co-authored-by: Copilot <[email protected]> * Apply fixes * Cleanup * Formatting * Move on * Update packages/lucide-react/tests/context.spec.tsx Co-authored-by: Copilot <[email protected]> * Update packages/lucide-preact/tests/context.spec.tsx Co-authored-by: Copilot <[email protected]> * Update packages/vue/tests/context.spec.ts Co-authored-by: Copilot <[email protected]> * Remove x * update export template * apply feedback * fix export template * Fixes types and tests * fix tests * apply feedback * Remove class * Formatting * Update packages/svelte/src/context.ts Co-authored-by: Copilot <[email protected]> * Remove fill form context providers * formatting * Add classname to context provider * Apply feedback * Format code * Rename files * Rename tsx to ts * Format file --------- Co-authored-by: Copilot <[email protected]>
1 parent 5fab4e7 commit 0c6dfeb

47 files changed

Lines changed: 1277 additions & 269 deletions

Some content is hidden

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

packages/lucide-preact/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mts --renderUniqueKey --withAliases --aliasesFileExtension=.ts --iconFileExtension=.ts --exportFileName=index.ts",
3838
"build:bundles": "rollup -c ./rollup.config.mjs",
3939
"test": "pnpm build:icons && vitest run",
40+
"test:watch": "vitest watch",
4041
"version": "pnpm version --git-tag-version=false"
4142
},
4243
"devDependencies": {
@@ -47,7 +48,7 @@
4748
"@testing-library/jest-dom": "^6.6.3",
4849
"@testing-library/preact": "^3.2.3",
4950
"jest-serializer-html": "^7.1.0",
50-
"preact": "^10.19.2",
51+
"preact": "^10.26.9",
5152
"rollup": "^4.53.3",
5253
"rollup-plugin-dts": "^6.2.3",
5354
"typescript": "^5.8.3",

packages/lucide-preact/src/Icon.ts

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { h, toChildArray } from 'preact';
22
import defaultAttributes from './defaultAttributes';
33
import type { IconNode, LucideProps } from './types';
4+
import { useLucideContext } from './context';
5+
import { mergeClasses } from '@lucide/shared';
46

57
interface IconComponentProps extends LucideProps {
68
iconNode: IconNode;
@@ -22,29 +24,41 @@ interface IconComponentProps extends LucideProps {
2224
* @returns {ForwardRefExoticComponent} LucideIcon
2325
*/
2426
const Icon = ({
25-
color = 'currentColor',
26-
size = 24,
27-
strokeWidth = 2,
27+
color,
28+
size,
29+
strokeWidth,
2830
absoluteStrokeWidth,
2931
children,
3032
iconNode,
3133
class: classes = '',
3234
...rest
33-
}: IconComponentProps) =>
34-
h(
35+
}: IconComponentProps) => {
36+
const {
37+
size: contextSize = 24,
38+
strokeWidth: contextStrokeWidth = 2,
39+
absoluteStrokeWidth: contextAbsoluteStrokeWidth = false,
40+
color: contextColor = 'currentColor',
41+
class: contextClass = '',
42+
} = useLucideContext() ?? {};
43+
44+
const calculatedStrokeWidth =
45+
absoluteStrokeWidth ?? contextAbsoluteStrokeWidth
46+
? (Number(strokeWidth ?? contextStrokeWidth) * 24) / Number(size ?? contextSize)
47+
: strokeWidth ?? contextStrokeWidth;
48+
49+
return h(
3550
'svg',
3651
{
3752
...defaultAttributes,
38-
width: String(size),
39-
height: size,
40-
stroke: color,
41-
['stroke-width' as 'strokeWidth']: absoluteStrokeWidth
42-
? (Number(strokeWidth) * 24) / Number(size)
43-
: strokeWidth,
44-
class: ['lucide', classes].join(' '),
53+
width: size ?? contextSize ?? 24,
54+
height: size ?? contextSize ?? 24,
55+
stroke: color ?? contextColor,
56+
['stroke-width' as 'strokeWidth']: calculatedStrokeWidth,
57+
class: mergeClasses('lucide', contextClass, classes),
4558
...rest,
4659
},
4760
[...iconNode.map(([tag, attrs]) => h(tag, attrs)), ...toChildArray(children)],
4861
);
62+
};
4963

5064
export default Icon;
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { createContext, h, type ComponentChildren } from 'preact';
2+
import { useContext, useMemo } from 'preact/hooks';
3+
4+
const LucideContext = createContext<{
5+
size?: number;
6+
color?: string;
7+
strokeWidth?: number;
8+
absoluteStrokeWidth?: boolean;
9+
class?: string;
10+
}>({
11+
size: 24,
12+
color: 'currentColor',
13+
strokeWidth: 2,
14+
absoluteStrokeWidth: false,
15+
class: '',
16+
});
17+
18+
interface LucideProviderProps {
19+
children: ComponentChildren;
20+
size?: number;
21+
color?: string;
22+
strokeWidth?: number;
23+
absoluteStrokeWidth?: boolean;
24+
class?: string;
25+
}
26+
27+
export function LucideProvider({
28+
children,
29+
size,
30+
color,
31+
strokeWidth,
32+
absoluteStrokeWidth,
33+
class: className,
34+
}: LucideProviderProps) {
35+
const value = useMemo(
36+
() => ({
37+
size,
38+
color,
39+
strokeWidth,
40+
absoluteStrokeWidth,
41+
class: className,
42+
}),
43+
[size, color, strokeWidth, absoluteStrokeWidth, className],
44+
);
45+
46+
return h(LucideContext.Provider, { value }, children);
47+
}
48+
49+
export const useLucideContext = () => useContext(LucideContext);

packages/lucide-preact/src/lucide-preact.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export * from './icons';
22
export * as icons from './icons';
33
export * from './aliases';
44
export * from './types';
5+
export * from './context';
56

67
export { default as createLucideIcon } from './createLucideIcon';
78
export { default as Icon } from './Icon';

packages/lucide-preact/tests/__snapshots__/Icon.spec.tsx.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
exports[`Using Icon Component > should render icon and match snapshot 1`] = `
44
<svg
5-
class="lucide "
5+
class="lucide"
66
fill="none"
77
height="48"
88
stroke="red"
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2+
3+
exports[`Using LucideProvider > should render the icon with LucideProvider 1`] = `
4+
<svg
5+
class="lucide lucide-house"
6+
fill="none"
7+
height="48"
8+
stroke="red"
9+
stroke-linecap="round"
10+
stroke-linejoin="round"
11+
stroke-width="2"
12+
viewBox="0 0 24 24"
13+
width="48"
14+
xmlns="http://www.w3.org/2000/svg"
15+
>
16+
<path
17+
d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8"
18+
/>
19+
<path
20+
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"
21+
/>
22+
</svg>
23+
`;
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { render } from '@testing-library/preact';
2+
import { describe, expect, it } from 'vitest';
3+
import { House, LucideProvider } from '../src/lucide-preact';
4+
5+
describe('Using LucideProvider', () => {
6+
it('should render the icon with LucideProvider', () => {
7+
const { container } = render(
8+
<LucideProvider
9+
size={48}
10+
color="red"
11+
>
12+
<House />
13+
</LucideProvider>,
14+
);
15+
16+
expect(container.firstChild).toMatchSnapshot();
17+
});
18+
19+
it('should render the icon with LucideProvider and custom strokeWidth', () => {
20+
const { container } = render(
21+
<LucideProvider
22+
size={48}
23+
color="red"
24+
strokeWidth={4}
25+
>
26+
<House />
27+
</LucideProvider>,
28+
);
29+
30+
const IconComponent = container.firstElementChild;
31+
32+
expect(IconComponent).toHaveAttribute('width', '48');
33+
expect(IconComponent).toHaveAttribute('height', '48');
34+
expect(IconComponent).toHaveAttribute('stroke', 'red');
35+
expect(IconComponent).toHaveAttribute('stroke-width', '4');
36+
});
37+
38+
it('should render the icon with LucideProvider and custom absoluteStrokeWidth', () => {
39+
const { container } = render(
40+
<LucideProvider
41+
size={48}
42+
color="red"
43+
absoluteStrokeWidth
44+
>
45+
<House />
46+
</LucideProvider>,
47+
);
48+
49+
const IconComponent = container.firstElementChild;
50+
51+
expect(IconComponent).toHaveAttribute('stroke-width', '1');
52+
});
53+
54+
it("should override the provider's global props when passing props to the icon", () => {
55+
const { container } = render(
56+
<LucideProvider
57+
size={48}
58+
color="red"
59+
strokeWidth={4}
60+
>
61+
<House
62+
size={24}
63+
color="blue"
64+
strokeWidth={2}
65+
/>
66+
</LucideProvider>,
67+
);
68+
69+
const IconComponent = container.firstElementChild;
70+
71+
expect(IconComponent).toHaveAttribute('width', '24');
72+
expect(IconComponent).toHaveAttribute('height', '24');
73+
expect(IconComponent).toHaveAttribute('stroke', 'blue');
74+
expect(IconComponent).toHaveAttribute('stroke-width', '2');
75+
});
76+
77+
it('should merge class names from LucideProvider and icon props', () => {
78+
const { container } = render(
79+
<LucideProvider class="provider-class">
80+
<House class="icon-class" />
81+
</LucideProvider>,
82+
);
83+
84+
const IconComponent = container.firstElementChild;
85+
86+
expect(IconComponent).toHaveAttribute('class', 'lucide provider-class lucide-house icon-class');
87+
});
88+
});

packages/lucide-react-native/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mts --renderUniqueKey --iconFileExtension=.ts --exportFileName=index.ts --withAliases --aliasesFileExtension=.ts",
5353
"build:bundles": "rollup -c ./rollup.config.mjs",
5454
"test": "pnpm build:icons && vitest run",
55+
"test:watch": "vitest watch",
5556
"version": "pnpm version --git-tag-version=false"
5657
},
5758
"devDependencies": {

packages/lucide-react-native/src/Icon.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ import { createElement, forwardRef, type FunctionComponent } from 'react';
22
import * as NativeSvg from 'react-native-svg';
33
import defaultAttributes, { childDefaultAttributes } from './defaultAttributes';
44
import { IconNode, LucideProps } from './types';
5+
import { useLucideContext } from './context';
56

67
interface IconComponentProps extends LucideProps {
78
iconNode: IconNode;
9+
testID?: string;
10+
className?: string;
811
}
912

1013
/**
@@ -25,20 +28,33 @@ interface IconComponentProps extends LucideProps {
2528
const Icon = forwardRef<SVGSVGElement, IconComponentProps>(
2629
(
2730
{
28-
color = 'currentColor',
29-
size = 24,
30-
strokeWidth = 2,
31+
color,
32+
size,
33+
strokeWidth,
3134
absoluteStrokeWidth,
3235
children,
3336
iconNode,
3437
className,
38+
testID,
3539
...rest
3640
},
3741
ref,
3842
) => {
43+
const {
44+
size: contextSize = 24,
45+
strokeWidth: contextStrokeWidth = 2,
46+
absoluteStrokeWidth: contextAbsoluteStrokeWidth = false,
47+
color: contextColor = 'currentColor',
48+
} = useLucideContext() ?? {};
49+
50+
const calculatedStrokeWidth =
51+
absoluteStrokeWidth ?? contextAbsoluteStrokeWidth
52+
? (Number(strokeWidth ?? contextStrokeWidth) * 24) / Number(size ?? contextSize)
53+
: strokeWidth ?? contextStrokeWidth;
54+
3955
const customAttrs = {
40-
stroke: color,
41-
strokeWidth: absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth,
56+
stroke: color ?? contextColor ?? defaultAttributes.stroke,
57+
strokeWidth: calculatedStrokeWidth,
4258
...rest,
4359
};
4460

@@ -47,9 +63,10 @@ const Icon = forwardRef<SVGSVGElement, IconComponentProps>(
4763
{
4864
ref,
4965
...defaultAttributes,
66+
width: size ?? contextSize ?? defaultAttributes.width,
67+
height: size ?? contextSize ?? defaultAttributes.height,
68+
'data-testid': testID,
5069
className,
51-
width: size,
52-
height: size,
5370
...customAttrs,
5471
},
5572
[
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { createContext, createElement, type ReactNode, useContext, useMemo } from 'react';
2+
3+
const LucideContext = createContext<{
4+
size?: number;
5+
color?: string;
6+
strokeWidth?: number;
7+
absoluteStrokeWidth?: boolean;
8+
}>({
9+
size: 24,
10+
color: 'currentColor',
11+
strokeWidth: 2,
12+
absoluteStrokeWidth: false,
13+
});
14+
15+
interface LucideProviderProps {
16+
children: ReactNode;
17+
size?: number;
18+
color?: string;
19+
strokeWidth?: number;
20+
absoluteStrokeWidth?: boolean;
21+
}
22+
23+
export function LucideProvider({
24+
children,
25+
size,
26+
color,
27+
strokeWidth,
28+
absoluteStrokeWidth,
29+
}: LucideProviderProps) {
30+
const value = useMemo(
31+
() => ({
32+
size,
33+
color,
34+
strokeWidth,
35+
absoluteStrokeWidth,
36+
}),
37+
[size, color, strokeWidth, absoluteStrokeWidth],
38+
);
39+
40+
return createElement(LucideContext.Provider, { value }, children);
41+
}
42+
43+
export const useLucideContext = () => useContext(LucideContext);

0 commit comments

Comments
 (0)