@factorialco/f0-react-native 0.29.0 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/module/components/Avatars/IconAvatar/index.js +1 -1
- package/lib/module/components/Avatars/IconAvatar/index.js.map +1 -1
- package/lib/module/components/Avatars/ModuleAvatar/index.js +1 -1
- package/lib/module/components/Avatars/ModuleAvatar/index.js.map +1 -1
- package/lib/module/components/Badge/index.js +1 -1
- package/lib/module/components/Badge/index.js.map +1 -1
- package/lib/module/components/Button/index.js +1 -1
- package/lib/module/components/Button/index.js.map +1 -1
- package/lib/module/components/Button/index.spec.js +1 -1
- package/lib/module/components/Button/index.spec.js.map +1 -1
- package/lib/module/components/Icon/index.js +1 -1
- package/lib/module/components/Icon/index.js.map +1 -1
- package/lib/module/components/OneChip/index.js +1 -1
- package/lib/module/components/OneChip/index.js.map +1 -1
- package/lib/module/components/Tags/AlertTab/index.js +1 -1
- package/lib/module/components/Tags/AlertTab/index.js.map +1 -1
- package/lib/module/components/Tags/RawTag/index.js +1 -1
- package/lib/module/components/Tags/RawTag/index.js.map +1 -1
- package/lib/module/components/experimental/Lists/DataList/ItemContainer.js +1 -1
- package/lib/module/components/experimental/Lists/DataList/ItemContainer.js.map +1 -1
- package/lib/module/components/experimental/Lists/DataList/actions/CopyAction.js +1 -1
- package/lib/module/components/experimental/Lists/DataList/actions/CopyAction.js.map +1 -1
- package/lib/module/components/experimental/Lists/DataList/actions/GenericAction.js +1 -1
- package/lib/module/components/experimental/Lists/DataList/actions/GenericAction.js.map +1 -1
- package/lib/module/components/exports.js +1 -1
- package/lib/module/components/exports.js.map +1 -1
- package/lib/module/components/primitives/F0Icon/F0Icon.js +2 -0
- package/lib/module/components/primitives/F0Icon/F0Icon.js.map +1 -0
- package/lib/module/components/primitives/F0Icon/F0Icon.md +187 -0
- package/lib/module/components/primitives/F0Icon/F0Icon.styles.js +2 -0
- package/lib/module/components/primitives/F0Icon/F0Icon.styles.js.map +1 -0
- package/lib/module/components/primitives/F0Icon/F0Icon.types.js +2 -0
- package/lib/module/components/primitives/F0Icon/F0Icon.types.js.map +1 -0
- package/lib/module/components/primitives/F0Icon/index.js +2 -0
- package/lib/module/components/primitives/F0Icon/index.js.map +1 -0
- package/lib/module/icons/index.js +1 -1
- package/lib/module/icons/index.js.map +1 -1
- package/lib/typescript/components/Activity/ActivityItem/index.d.ts +1 -1
- package/lib/typescript/components/Activity/ActivityItem/index.d.ts.map +1 -1
- package/lib/typescript/components/Avatars/IconAvatar/index.d.ts +1 -1
- package/lib/typescript/components/Avatars/IconAvatar/index.d.ts.map +1 -1
- package/lib/typescript/components/Avatars/ModuleAvatar/index.d.ts +1 -1
- package/lib/typescript/components/Avatars/ModuleAvatar/index.d.ts.map +1 -1
- package/lib/typescript/components/Badge/index.d.ts +1 -1
- package/lib/typescript/components/Badge/index.d.ts.map +1 -1
- package/lib/typescript/components/Button/index.d.ts +1 -1
- package/lib/typescript/components/Button/index.d.ts.map +1 -1
- package/lib/typescript/components/Icon/index.d.ts +7 -14
- package/lib/typescript/components/Icon/index.d.ts.map +1 -1
- package/lib/typescript/components/OneChip/index.d.ts +1 -1
- package/lib/typescript/components/OneChip/index.d.ts.map +1 -1
- package/lib/typescript/components/Tags/RawTag/index.d.ts +1 -1
- package/lib/typescript/components/Tags/RawTag/index.d.ts.map +1 -1
- package/lib/typescript/components/experimental/Lists/DataList/ItemContainer.d.ts +1 -1
- package/lib/typescript/components/experimental/Lists/DataList/ItemContainer.d.ts.map +1 -1
- package/lib/typescript/components/experimental/Lists/DataList/actions/CopyAction.d.ts.map +1 -1
- package/lib/typescript/components/experimental/Lists/DataList/index.d.ts +1 -1
- package/lib/typescript/components/experimental/Lists/DataList/index.d.ts.map +1 -1
- package/lib/typescript/components/exports.d.ts +2 -1
- package/lib/typescript/components/exports.d.ts.map +1 -1
- package/lib/typescript/components/primitives/F0Icon/F0Icon.d.ts +25 -0
- package/lib/typescript/components/primitives/F0Icon/F0Icon.d.ts.map +1 -0
- package/lib/typescript/components/primitives/F0Icon/F0Icon.styles.d.ts +90 -0
- package/lib/typescript/components/primitives/F0Icon/F0Icon.styles.d.ts.map +1 -0
- package/lib/typescript/components/primitives/F0Icon/F0Icon.types.d.ts +47 -0
- package/lib/typescript/components/primitives/F0Icon/F0Icon.types.d.ts.map +1 -0
- package/lib/typescript/components/primitives/F0Icon/index.d.ts +10 -0
- package/lib/typescript/components/primitives/F0Icon/index.d.ts.map +1 -0
- package/lib/typescript/icons/index.d.ts +0 -1
- package/lib/typescript/icons/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/Activity/ActivityItem/index.spec.tsx +1 -1
- package/src/components/Activity/ActivityItem/index.tsx +1 -1
- package/src/components/Avatars/IconAvatar/index.tsx +6 -2
- package/src/components/Avatars/ModuleAvatar/index.tsx +1 -1
- package/src/components/Badge/index.tsx +2 -2
- package/src/components/Button/index.spec.tsx +3 -4
- package/src/components/Button/index.tsx +19 -13
- package/src/components/Icon/__tests__/Icon.spec.tsx +0 -4
- package/src/components/Icon/index.tsx +7 -26
- package/src/components/OneChip/index.tsx +3 -3
- package/src/components/Tags/AlertTab/index.tsx +2 -2
- package/src/components/Tags/RawTag/index.tsx +2 -2
- package/src/components/experimental/Lists/DataList/ItemContainer.tsx +2 -2
- package/src/components/experimental/Lists/DataList/actions/CopyAction.tsx +7 -10
- package/src/components/experimental/Lists/DataList/actions/GenericAction.tsx +2 -2
- package/src/components/experimental/Lists/DataList/index.tsx +1 -1
- package/src/components/experimental/Lists/DetailsItem/__snapshots__/index.spec.tsx.snap +4 -4
- package/src/components/experimental/Lists/DetailsItemsList/__snapshots__/index.spec.tsx.snap +1 -1
- package/src/components/exports.ts +2 -1
- package/src/components/primitives/F0Icon/F0Icon.md +187 -0
- package/src/components/primitives/F0Icon/F0Icon.styles.ts +43 -0
- package/src/components/primitives/F0Icon/F0Icon.tsx +73 -0
- package/src/components/primitives/F0Icon/F0Icon.types.ts +77 -0
- package/src/components/primitives/F0Icon/__tests__/F0Icon.spec.tsx +131 -0
- package/src/components/primitives/F0Icon/__tests__/F0Icon.tokens.spec.ts +39 -0
- package/src/components/primitives/F0Icon/index.ts +10 -0
- package/src/icons/index.ts +0 -1
- package/lib/module/components/Icon/README.md +0 -63
- package/src/components/Icon/README.md +0 -63
|
@@ -3,7 +3,7 @@ import { Pressable, Text, View } from "react-native"
|
|
|
3
3
|
import { tv, type VariantProps } from "tailwind-variants"
|
|
4
4
|
|
|
5
5
|
import { cn } from "../../lib/utils"
|
|
6
|
-
import {
|
|
6
|
+
import { F0Icon, type IconType, type IconColor } from "../primitives/F0Icon"
|
|
7
7
|
|
|
8
8
|
export const variants = [
|
|
9
9
|
"default",
|
|
@@ -68,29 +68,35 @@ const pressedVariants = tv({
|
|
|
68
68
|
},
|
|
69
69
|
})
|
|
70
70
|
|
|
71
|
-
const getIconColor = (
|
|
71
|
+
const getIconColor = (
|
|
72
|
+
variant: ButtonVariant,
|
|
73
|
+
isPressed: boolean
|
|
74
|
+
): IconColor => {
|
|
72
75
|
switch (variant) {
|
|
73
76
|
case "default":
|
|
74
|
-
return "
|
|
77
|
+
return "inverse"
|
|
75
78
|
case "critical":
|
|
76
|
-
return isPressed ? "
|
|
79
|
+
return isPressed ? "inverse" : "critical-bold"
|
|
77
80
|
default:
|
|
78
|
-
return "
|
|
81
|
+
return "default"
|
|
79
82
|
}
|
|
80
83
|
}
|
|
81
84
|
|
|
82
|
-
const getIconOnlyColor = (
|
|
85
|
+
const getIconOnlyColor = (
|
|
86
|
+
variant: ButtonVariant,
|
|
87
|
+
isPressed: boolean
|
|
88
|
+
): IconColor => {
|
|
83
89
|
switch (variant) {
|
|
84
90
|
case "critical":
|
|
85
|
-
return isPressed ? "
|
|
91
|
+
return isPressed ? "inverse" : "critical-bold"
|
|
86
92
|
case "default":
|
|
87
|
-
return "
|
|
93
|
+
return "inverse"
|
|
88
94
|
case "outline":
|
|
89
95
|
case "neutral":
|
|
90
96
|
case "ghost":
|
|
91
97
|
case "promote":
|
|
92
98
|
default:
|
|
93
|
-
return "
|
|
99
|
+
return "bold"
|
|
94
100
|
}
|
|
95
101
|
}
|
|
96
102
|
|
|
@@ -191,15 +197,15 @@ export const Button = forwardRef<View, ButtonProps>(function Button(
|
|
|
191
197
|
accessibilityHint={accessibilityHint}
|
|
192
198
|
>
|
|
193
199
|
{icon && (
|
|
194
|
-
<
|
|
200
|
+
<F0Icon
|
|
195
201
|
icon={icon}
|
|
196
202
|
size={size === "sm" ? "sm" : "md"}
|
|
197
|
-
|
|
198
|
-
hideLabel && round ? undefined : "-ml-0.5",
|
|
203
|
+
color={
|
|
199
204
|
hideLabel && round
|
|
200
205
|
? getIconOnlyColor(variant, shouldShowPressed)
|
|
201
206
|
: getIconColor(variant, shouldShowPressed)
|
|
202
|
-
|
|
207
|
+
}
|
|
208
|
+
className={hideLabel && round ? undefined : "-ml-0.5"}
|
|
203
209
|
/>
|
|
204
210
|
)}
|
|
205
211
|
{emoji && (
|
|
@@ -20,8 +20,6 @@ describe("Icon", () => {
|
|
|
20
20
|
const { getByTestId } = render(
|
|
21
21
|
<Icon icon={Archive} size="lg" testID="icon" />
|
|
22
22
|
)
|
|
23
|
-
// In a real test environment, we could check the actual style props applied
|
|
24
|
-
// but for now we just ensure it renders
|
|
25
23
|
expect(getByTestId("icon")).toBeTruthy()
|
|
26
24
|
})
|
|
27
25
|
|
|
@@ -29,8 +27,6 @@ describe("Icon", () => {
|
|
|
29
27
|
const { getByTestId } = render(
|
|
30
28
|
<Icon icon={Archive} className="text-red-500" testID="icon" />
|
|
31
29
|
)
|
|
32
|
-
// In a real test environment, we could check the actual style props applied
|
|
33
|
-
// but for now we just ensure it renders
|
|
34
30
|
expect(getByTestId("icon")).toBeTruthy()
|
|
35
31
|
})
|
|
36
32
|
})
|
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
forwardRef,
|
|
3
|
-
ForwardRefExoticComponent,
|
|
4
|
-
RefAttributes,
|
|
5
|
-
} from "react"
|
|
1
|
+
import React, { forwardRef } from "react"
|
|
6
2
|
import { Svg, SvgProps } from "react-native-svg"
|
|
7
3
|
import { tv, type VariantProps } from "tailwind-variants"
|
|
8
|
-
import { withUniwind } from "uniwind"
|
|
9
4
|
|
|
10
5
|
import { cn } from "../../lib/utils"
|
|
6
|
+
import { applyIconInterop, type IconType } from "../primitives/F0Icon"
|
|
11
7
|
|
|
12
8
|
const iconVariants = tv({
|
|
13
9
|
base: "shrink-0",
|
|
@@ -33,26 +29,11 @@ export interface IconProps extends SvgProps, VariantProps<typeof iconVariants> {
|
|
|
33
29
|
isPressed?: boolean
|
|
34
30
|
}
|
|
35
31
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
>
|
|
42
|
-
|
|
43
|
-
// Keep track of icons that have already had withUniwind applied
|
|
44
|
-
const interopAppliedIcons = new WeakSet()
|
|
45
|
-
|
|
46
|
-
// Function to apply UniWind interop to an icon component
|
|
47
|
-
export function applyIconInterop(icon: IconType) {
|
|
48
|
-
if (!interopAppliedIcons.has(icon)) {
|
|
49
|
-
const wrappedIcon = withUniwind(icon)
|
|
50
|
-
interopAppliedIcons.add(wrappedIcon)
|
|
51
|
-
return wrappedIcon
|
|
52
|
-
}
|
|
53
|
-
return icon
|
|
54
|
-
}
|
|
55
|
-
|
|
32
|
+
/**
|
|
33
|
+
* @deprecated Use F0Icon instead. This component will be removed in a future version.
|
|
34
|
+
* Migration: Replace <Icon icon={X} /> with <F0Icon icon={X} />.
|
|
35
|
+
* F0Icon supports the same `icon`, `size`, `testID`, and `className` props, plus semantic `color` variants.
|
|
36
|
+
*/
|
|
56
37
|
export const Icon = forwardRef<Svg, IconProps>(function Icon(
|
|
57
38
|
{ size = "md", icon, className, testID, ...props },
|
|
58
39
|
ref
|
|
@@ -3,8 +3,8 @@ import { tv, type VariantProps } from "tailwind-variants"
|
|
|
3
3
|
|
|
4
4
|
import { CrossedCircle } from "../../icons/app"
|
|
5
5
|
import { cn } from "../../lib/utils"
|
|
6
|
-
import { Icon, type IconType } from "../Icon"
|
|
7
6
|
import { PressableFeedback } from "../PressableFeedback"
|
|
7
|
+
import { F0Icon, type IconType } from "../primitives/F0Icon"
|
|
8
8
|
|
|
9
9
|
export const chipContainerVariants = tv({
|
|
10
10
|
base: "flex items-center gap-1 rounded-full border border-solid border-f0-border px-2 py-0.5 grow-0",
|
|
@@ -61,7 +61,7 @@ export const OneChip = ({
|
|
|
61
61
|
>
|
|
62
62
|
<View className="flex flex-row items-center gap-0.5">
|
|
63
63
|
{icon && (
|
|
64
|
-
<
|
|
64
|
+
<F0Icon
|
|
65
65
|
icon={icon}
|
|
66
66
|
size="sm"
|
|
67
67
|
className={chipTextVariants({ variant })}
|
|
@@ -78,7 +78,7 @@ export const OneChip = ({
|
|
|
78
78
|
accessibilityRole="button"
|
|
79
79
|
accessibilityLabel="Close"
|
|
80
80
|
>
|
|
81
|
-
<
|
|
81
|
+
<F0Icon
|
|
82
82
|
icon={CrossedCircle}
|
|
83
83
|
className={chipTextVariants({ variant })}
|
|
84
84
|
size="sm"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AlertCircle, InfoCircle, Warning } from "../../../icons/app"
|
|
2
2
|
import { useTextFormatEnforcer } from "../../../lib/text"
|
|
3
3
|
import { cn } from "../../../lib/utils"
|
|
4
|
-
import {
|
|
4
|
+
import { F0Icon, type IconType } from "../../primitives/F0Icon"
|
|
5
5
|
import { BaseTag } from "../BaseTag"
|
|
6
6
|
|
|
7
7
|
type Level = "info" | "warning" | "critical"
|
|
@@ -40,7 +40,7 @@ export const AlertTag = <T extends string>({ text, level }: Props<T>) => {
|
|
|
40
40
|
}[level]
|
|
41
41
|
)}
|
|
42
42
|
left={
|
|
43
|
-
<
|
|
43
|
+
<F0Icon
|
|
44
44
|
icon={iconMap[level]}
|
|
45
45
|
className={cn(
|
|
46
46
|
{
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useTextFormatEnforcer } from "../../../lib/text"
|
|
2
2
|
import { cn } from "../../../lib/utils"
|
|
3
|
-
import {
|
|
3
|
+
import { F0Icon, type IconType } from "../../primitives/F0Icon"
|
|
4
4
|
import { BaseTag } from "../BaseTag"
|
|
5
5
|
|
|
6
6
|
export type RawTagProps = {
|
|
@@ -29,7 +29,7 @@ export const RawTag = ({
|
|
|
29
29
|
classNameText="text-f0-foreground"
|
|
30
30
|
left={
|
|
31
31
|
icon ? (
|
|
32
|
-
<
|
|
32
|
+
<F0Icon icon={icon} size="sm" color="default" aria-hidden />
|
|
33
33
|
) : null
|
|
34
34
|
}
|
|
35
35
|
text={text}
|
|
@@ -2,7 +2,7 @@ import { ReactElement, ReactNode } from "react"
|
|
|
2
2
|
import { View, Text } from "react-native"
|
|
3
3
|
|
|
4
4
|
import { cn } from "../../../../lib/utils"
|
|
5
|
-
import {
|
|
5
|
+
import { F0Icon, type IconType } from "../../../primitives/F0Icon"
|
|
6
6
|
|
|
7
7
|
import { CopyAction } from "./actions/CopyAction"
|
|
8
8
|
import { GenericAction } from "./actions/GenericAction"
|
|
@@ -34,7 +34,7 @@ export const ItemContainer = (props: ItemContainerProps) => {
|
|
|
34
34
|
(typeof LeftIcon === "function" ? (
|
|
35
35
|
LeftIcon({})
|
|
36
36
|
) : (
|
|
37
|
-
<
|
|
37
|
+
<F0Icon icon={LeftIcon} size="md" />
|
|
38
38
|
))}
|
|
39
39
|
<Text className="line-clamp-5 text-left text-f0-foreground">
|
|
40
40
|
{text}
|
|
@@ -5,7 +5,7 @@ import { Pressable, View } from "react-native"
|
|
|
5
5
|
import { CopyActionType } from ".."
|
|
6
6
|
import { CheckCircle, LayersFront } from "../../../../../icons/app"
|
|
7
7
|
import { cn } from "../../../../../lib/utils"
|
|
8
|
-
import {
|
|
8
|
+
import { F0Icon } from "../../../../primitives/F0Icon"
|
|
9
9
|
|
|
10
10
|
const COPIED_SHOWN_MS = 750
|
|
11
11
|
|
|
@@ -47,24 +47,21 @@ export const CopyAction = ({ text, children }: CopyActionProps) => {
|
|
|
47
47
|
<View className="flex flex-row items-center gap-1.5">{children}</View>
|
|
48
48
|
<View className="flex">
|
|
49
49
|
{!copied && (
|
|
50
|
-
<
|
|
50
|
+
<F0Icon
|
|
51
51
|
icon={LayersFront}
|
|
52
52
|
size="md"
|
|
53
53
|
aria-hidden={true}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
)}
|
|
54
|
+
color="bold"
|
|
55
|
+
className="col-start-1 col-end-2 row-start-1 row-end-2"
|
|
57
56
|
/>
|
|
58
57
|
)}
|
|
59
58
|
{copied && (
|
|
60
|
-
<
|
|
59
|
+
<F0Icon
|
|
61
60
|
icon={CheckCircle}
|
|
62
61
|
size="md"
|
|
63
62
|
aria-hidden={true}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
"text-f0-icon-positive"
|
|
67
|
-
)}
|
|
63
|
+
color="positive"
|
|
64
|
+
className="col-start-1 col-end-2 row-start-1 row-end-2"
|
|
68
65
|
/>
|
|
69
66
|
)}
|
|
70
67
|
</View>
|
|
@@ -4,7 +4,7 @@ import { Pressable, View } from "react-native"
|
|
|
4
4
|
import { GenericActionType } from ".."
|
|
5
5
|
import { ChevronRight } from "../../../../../icons/app"
|
|
6
6
|
import { cn } from "../../../../../lib/utils"
|
|
7
|
-
import {
|
|
7
|
+
import { F0Icon } from "../../../../primitives/F0Icon"
|
|
8
8
|
|
|
9
9
|
export type GenericActionProps = {
|
|
10
10
|
children: ReactNode
|
|
@@ -24,7 +24,7 @@ export const GenericAction = memo(
|
|
|
24
24
|
)}
|
|
25
25
|
>
|
|
26
26
|
<View className="flex flex-row items-center gap-1.5">{children}</View>
|
|
27
|
-
<
|
|
27
|
+
<F0Icon
|
|
28
28
|
aria-hidden={true}
|
|
29
29
|
icon={ChevronRight}
|
|
30
30
|
size="md"
|
|
@@ -5,7 +5,7 @@ import { cn } from "../../../../lib/utils"
|
|
|
5
5
|
import { CompanyAvatar } from "../../../Avatars/CompanyAvatar"
|
|
6
6
|
import { PersonAvatar } from "../../../Avatars/PersonAvatar"
|
|
7
7
|
import { TeamAvatar } from "../../../Avatars/TeamAvatar"
|
|
8
|
-
import { IconType } from "../../../
|
|
8
|
+
import { type IconType } from "../../../primitives/F0Icon"
|
|
9
9
|
import { DotTag, DotTagProps } from "../../../Tags/DotTag"
|
|
10
10
|
|
|
11
11
|
import { ItemContainer } from "./ItemContainer"
|
|
@@ -101,7 +101,7 @@ exports[`DetailsItem Snapshot type company 1`] = `
|
|
|
101
101
|
aria-hidden={true}
|
|
102
102
|
bbHeight="100%"
|
|
103
103
|
bbWidth="100%"
|
|
104
|
-
className="shrink-0 w-5 h-5 stroke-md col-start-1 col-end-2 row-start-1 row-end-2
|
|
104
|
+
className="shrink-0 w-5 h-5 stroke-md text-f0-icon-bold col-start-1 col-end-2 row-start-1 row-end-2"
|
|
105
105
|
fill="none"
|
|
106
106
|
focusable={false}
|
|
107
107
|
meetOrSlice={0}
|
|
@@ -343,7 +343,7 @@ exports[`DetailsItem Snapshot type item 1`] = `
|
|
|
343
343
|
aria-hidden={true}
|
|
344
344
|
bbHeight="100%"
|
|
345
345
|
bbWidth="100%"
|
|
346
|
-
className="shrink-0 w-5 h-5 stroke-md col-start-1 col-end-2 row-start-1 row-end-2
|
|
346
|
+
className="shrink-0 w-5 h-5 stroke-md text-f0-icon-bold col-start-1 col-end-2 row-start-1 row-end-2"
|
|
347
347
|
fill="none"
|
|
348
348
|
focusable={false}
|
|
349
349
|
meetOrSlice={0}
|
|
@@ -519,7 +519,7 @@ exports[`DetailsItem Snapshot type person 1`] = `
|
|
|
519
519
|
aria-hidden={true}
|
|
520
520
|
bbHeight="100%"
|
|
521
521
|
bbWidth="100%"
|
|
522
|
-
className="shrink-0 w-5 h-5 stroke-md col-start-1 col-end-2 row-start-1 row-end-2
|
|
522
|
+
className="shrink-0 w-5 h-5 stroke-md text-f0-icon-bold col-start-1 col-end-2 row-start-1 row-end-2"
|
|
523
523
|
fill="none"
|
|
524
524
|
focusable={false}
|
|
525
525
|
meetOrSlice={0}
|
|
@@ -695,7 +695,7 @@ exports[`DetailsItem Snapshot type team 1`] = `
|
|
|
695
695
|
aria-hidden={true}
|
|
696
696
|
bbHeight="100%"
|
|
697
697
|
bbWidth="100%"
|
|
698
|
-
className="shrink-0 w-5 h-5 stroke-md col-start-1 col-end-2 row-start-1 row-end-2
|
|
698
|
+
className="shrink-0 w-5 h-5 stroke-md text-f0-icon-bold col-start-1 col-end-2 row-start-1 row-end-2"
|
|
699
699
|
fill="none"
|
|
700
700
|
focusable={false}
|
|
701
701
|
meetOrSlice={0}
|
package/src/components/experimental/Lists/DetailsItemsList/__snapshots__/index.spec.tsx.snap
CHANGED
|
@@ -79,7 +79,7 @@ exports[`DetailsItemsList Snapshot 1`] = `
|
|
|
79
79
|
aria-hidden={true}
|
|
80
80
|
bbHeight="100%"
|
|
81
81
|
bbWidth="100%"
|
|
82
|
-
className="shrink-0 w-5 h-5 stroke-md col-start-1 col-end-2 row-start-1 row-end-2
|
|
82
|
+
className="shrink-0 w-5 h-5 stroke-md text-f0-icon-bold col-start-1 col-end-2 row-start-1 row-end-2"
|
|
83
83
|
fill="none"
|
|
84
84
|
focusable={false}
|
|
85
85
|
meetOrSlice={0}
|
|
@@ -5,7 +5,7 @@ export * from "./Badge"
|
|
|
5
5
|
export * from "./Button"
|
|
6
6
|
export * from "./Counter"
|
|
7
7
|
export * from "./ExampleComponent"
|
|
8
|
-
export
|
|
8
|
+
export { Icon, type IconProps } from "./Icon"
|
|
9
9
|
export * from "./Navigation/PageHeader"
|
|
10
10
|
export * from "./OneChip"
|
|
11
11
|
export * from "./OnePreset"
|
|
@@ -17,3 +17,4 @@ export * from "./experimental/Lists/DetailsItemsList"
|
|
|
17
17
|
|
|
18
18
|
// Export primitives
|
|
19
19
|
export * from "./primitives/F0Text"
|
|
20
|
+
export * from "./primitives/F0Icon"
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# F0Icon
|
|
2
|
+
|
|
3
|
+
Icon component for rendering SVG icons with consistent sizing and semantic colors from the F0 Design System.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
F0Icon is an atomic component that wraps SVG icon components with automatic UniWind interop, standardized sizing variants, and semantic icon colors. It follows the same pattern as F0Text: a `color` prop maps to design system tokens, with `className` available as an escape hatch.
|
|
8
|
+
|
|
9
|
+
## Architecture
|
|
10
|
+
|
|
11
|
+
- **Pattern:** Props API (Atomic Component)
|
|
12
|
+
- **Category:** Primitive Component
|
|
13
|
+
- **Location:** `src/components/primitives/F0Icon/`
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
<!-- prettier-ignore -->
|
|
18
|
+
```tsx
|
|
19
|
+
import { F0Icon } from "@factorialco/f0-react-native"
|
|
20
|
+
import { Archive } from "@factorialco/f0-react-native/icons/app"
|
|
21
|
+
import { Home } from "@factorialco/f0-react-native/icons/modules"
|
|
22
|
+
|
|
23
|
+
<F0Icon icon={Archive} />
|
|
24
|
+
|
|
25
|
+
<F0Icon icon={Home} size="lg" />
|
|
26
|
+
|
|
27
|
+
<F0Icon icon={Archive} color="critical" />
|
|
28
|
+
|
|
29
|
+
<F0Icon icon={Archive} size="sm" color="positive" testID="archive-icon" />
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Props
|
|
33
|
+
|
|
34
|
+
### `icon` (required)
|
|
35
|
+
|
|
36
|
+
- **Type:** `IconType`
|
|
37
|
+
- **Description:** SVG icon component to render (from `icons/` directory)
|
|
38
|
+
|
|
39
|
+
<!-- prettier-ignore -->
|
|
40
|
+
```tsx
|
|
41
|
+
import { Archive } from "@factorialco/f0-react-native/icons/app"
|
|
42
|
+
<F0Icon icon={Archive} />
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### `size`
|
|
46
|
+
|
|
47
|
+
- **Type:** `'xl' | 'lg' | 'md' | 'sm' | 'xs'`
|
|
48
|
+
- **Default:** `'md'`
|
|
49
|
+
- **Description:** Size variant for the icon
|
|
50
|
+
|
|
51
|
+
<!-- prettier-ignore -->
|
|
52
|
+
```tsx
|
|
53
|
+
<F0Icon icon={Archive} size="xl" /> // 32x32px
|
|
54
|
+
<F0Icon icon={Archive} size="lg" /> // 24x24px
|
|
55
|
+
<F0Icon icon={Archive} size="md" /> // 20x20px (default)
|
|
56
|
+
<F0Icon icon={Archive} size="sm" /> // 16x16px
|
|
57
|
+
<F0Icon icon={Archive} size="xs" /> // 12x12px
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### `color`
|
|
61
|
+
|
|
62
|
+
- **Type:** `IconColor` (see below)
|
|
63
|
+
- **Default:** none (no color class applied; icon inherits from parent)
|
|
64
|
+
- **Description:** Semantic icon color from the F0 design system. Maps to `f0-icon-*` tokens.
|
|
65
|
+
|
|
66
|
+
<!-- prettier-ignore -->
|
|
67
|
+
```tsx
|
|
68
|
+
<F0Icon icon={Archive} color="default" />
|
|
69
|
+
<F0Icon icon={Archive} color="critical" />
|
|
70
|
+
<F0Icon icon={Archive} color="positive" />
|
|
71
|
+
<F0Icon icon={Archive} color="info" />
|
|
72
|
+
<F0Icon icon={Archive} color="warning" />
|
|
73
|
+
<F0Icon icon={Archive} color="accent" />
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### `testID`
|
|
77
|
+
|
|
78
|
+
- **Type:** `string`
|
|
79
|
+
- **Description:** Test identifier for testing library
|
|
80
|
+
|
|
81
|
+
<!-- prettier-ignore -->
|
|
82
|
+
```tsx
|
|
83
|
+
<F0Icon icon={Archive} testID="my-icon" />
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### `className`
|
|
87
|
+
|
|
88
|
+
- **Type:** `string`
|
|
89
|
+
- **Description:** Tailwind classes for custom styling. Use as an escape hatch when `color` doesn't cover the need (e.g. layout adjustments, one-off colors).
|
|
90
|
+
|
|
91
|
+
<!-- prettier-ignore -->
|
|
92
|
+
```tsx
|
|
93
|
+
<F0Icon icon={Archive} className="-ml-0.5" />
|
|
94
|
+
<F0Icon icon={Star} className="text-yellow-500" />
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Other Props
|
|
98
|
+
|
|
99
|
+
F0Icon accepts all SVG props from `react-native-svg` except `style` (use `color` or `className` instead).
|
|
100
|
+
|
|
101
|
+
## Size Variants
|
|
102
|
+
|
|
103
|
+
| Variant | Width/Height | Stroke Width | Use Case |
|
|
104
|
+
| ------- | -------------- | ------------ | ---------------------------------- |
|
|
105
|
+
| `xl` | 32px (w-8 h-8) | stroke-xl | Large feature icons, hero sections |
|
|
106
|
+
| `lg` | 24px (w-6 h-6) | stroke-lg | Primary buttons, major actions |
|
|
107
|
+
| `md` | 20px (w-5 h-5) | stroke-md | Default size, most UI elements |
|
|
108
|
+
| `sm` | 16px (w-4 h-4) | stroke-sm | Secondary buttons, compact UI |
|
|
109
|
+
| `xs` | 12px (w-3 h-3) | stroke-xs | Inline icons, dense layouts |
|
|
110
|
+
|
|
111
|
+
## Color Variants
|
|
112
|
+
|
|
113
|
+
Derived from `f0-icon-*` tokens in `src/styles/theme.css`. Sync is enforced by `F0Icon.tokens.spec.ts`.
|
|
114
|
+
|
|
115
|
+
| Color | Token | Use Case |
|
|
116
|
+
| --------------------- | ---------------------------------- | ------------------------- |
|
|
117
|
+
| `default` | `text-f0-icon` | Standard icon color |
|
|
118
|
+
| `secondary` | `text-f0-icon-secondary` | Muted/secondary icons |
|
|
119
|
+
| `inverse` | `text-f0-icon-inverse` | Icons on dark backgrounds |
|
|
120
|
+
| `bold` | `text-f0-icon-bold` | High emphasis icons |
|
|
121
|
+
| `critical` | `text-f0-icon-critical` | Error/destructive |
|
|
122
|
+
| `critical-bold` | `text-f0-icon-critical-bold` | Bold critical emphasis |
|
|
123
|
+
| `accent` | `text-f0-icon-accent` | Brand accent |
|
|
124
|
+
| `info` | `text-f0-icon-info` | Informational |
|
|
125
|
+
| `warning` | `text-f0-icon-warning` | Warning states |
|
|
126
|
+
| `positive` | `text-f0-icon-positive` | Success/positive |
|
|
127
|
+
| `promote` | `text-f0-icon-promote` | Promotional highlights |
|
|
128
|
+
| `selected` | `text-f0-icon-selected` | Selected state |
|
|
129
|
+
| `selected-hover` | `text-f0-icon-selected-hover` | Selected hover state |
|
|
130
|
+
| `mood-super-negative` | `text-f0-icon-mood-super-negative` | Mood: very negative |
|
|
131
|
+
| `mood-negative` | `text-f0-icon-mood-negative` | Mood: negative |
|
|
132
|
+
| `mood-neutral` | `text-f0-icon-mood-neutral` | Mood: neutral |
|
|
133
|
+
| `mood-positive` | `text-f0-icon-mood-positive` | Mood: positive |
|
|
134
|
+
| `mood-super-positive` | `text-f0-icon-mood-super-positive` | Mood: very positive |
|
|
135
|
+
|
|
136
|
+
## Implementation Details
|
|
137
|
+
|
|
138
|
+
### UniWind Interop
|
|
139
|
+
|
|
140
|
+
F0Icon automatically applies UniWind interop to icon components using `withUniwind()`. Icons are cached in a `WeakSet` to ensure the wrapper is only applied once per icon type.
|
|
141
|
+
|
|
142
|
+
### Performance
|
|
143
|
+
|
|
144
|
+
- Wrapped in `React.memo` to prevent unnecessary re-renders
|
|
145
|
+
- `useMemo` for className generation
|
|
146
|
+
- Icon interop caching via `WeakSet`
|
|
147
|
+
|
|
148
|
+
### TypeScript
|
|
149
|
+
|
|
150
|
+
Style prop is blocked at compile-time to enforce `color`/`className` usage:
|
|
151
|
+
|
|
152
|
+
<!-- prettier-ignore -->
|
|
153
|
+
```tsx
|
|
154
|
+
<F0Icon icon={Archive} color="critical" />
|
|
155
|
+
|
|
156
|
+
// TypeScript error - style not allowed
|
|
157
|
+
<F0Icon icon={Archive} style={{ color: 'red' }} />
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Token Sync
|
|
161
|
+
|
|
162
|
+
`ICON_COLORS` in `F0Icon.types.ts` is kept in sync with `f0-icon-*` tokens from `theme.css` by a CI test (`F0Icon.tokens.spec.ts`). Adding or removing a token in theme.css without updating `ICON_COLORS` will fail the test.
|
|
163
|
+
|
|
164
|
+
## File Structure
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
src/components/primitives/F0Icon/
|
|
168
|
+
├── index.ts # Barrel exports
|
|
169
|
+
├── F0Icon.tsx # Component implementation
|
|
170
|
+
├── F0Icon.types.ts # TypeScript type definitions (ICON_COLORS, IconColor)
|
|
171
|
+
├── F0Icon.styles.ts # tailwind-variants configuration (size + color)
|
|
172
|
+
├── F0Icon.md # This documentation
|
|
173
|
+
└── __tests__/
|
|
174
|
+
├── F0Icon.spec.tsx # Component tests
|
|
175
|
+
└── F0Icon.tokens.spec.ts # Token sync test (theme.css ↔ ICON_COLORS)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Related Components
|
|
179
|
+
|
|
180
|
+
- **F0Text** - Text primitive with semantic `color` prop (foreground tokens)
|
|
181
|
+
- **Button** - Uses F0Icon for icon slots
|
|
182
|
+
- **Badge** - Can include F0Icon
|
|
183
|
+
|
|
184
|
+
## References
|
|
185
|
+
|
|
186
|
+
- Design tokens: `src/styles/theme.css`
|
|
187
|
+
- Icon source: `src/icons/`
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { tv } from "tailwind-variants"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* F0Icon tailwind-variants configuration
|
|
5
|
+
* Size and color variants from the F0 design system
|
|
6
|
+
*/
|
|
7
|
+
export const iconVariants = tv({
|
|
8
|
+
base: "shrink-0",
|
|
9
|
+
|
|
10
|
+
variants: {
|
|
11
|
+
size: {
|
|
12
|
+
xl: "w-8 h-8 stroke-xl",
|
|
13
|
+
lg: "w-6 h-6 stroke-lg",
|
|
14
|
+
md: "w-5 h-5 stroke-md",
|
|
15
|
+
sm: "w-4 h-4 stroke-sm",
|
|
16
|
+
xs: "w-3 h-3 stroke-xs",
|
|
17
|
+
},
|
|
18
|
+
color: {
|
|
19
|
+
default: "text-f0-icon",
|
|
20
|
+
secondary: "text-f0-icon-secondary",
|
|
21
|
+
inverse: "text-f0-icon-inverse",
|
|
22
|
+
bold: "text-f0-icon-bold",
|
|
23
|
+
critical: "text-f0-icon-critical",
|
|
24
|
+
"critical-bold": "text-f0-icon-critical-bold",
|
|
25
|
+
accent: "text-f0-icon-accent",
|
|
26
|
+
info: "text-f0-icon-info",
|
|
27
|
+
warning: "text-f0-icon-warning",
|
|
28
|
+
positive: "text-f0-icon-positive",
|
|
29
|
+
promote: "text-f0-icon-promote",
|
|
30
|
+
selected: "text-f0-icon-selected",
|
|
31
|
+
"selected-hover": "text-f0-icon-selected-hover",
|
|
32
|
+
"mood-super-negative": "text-f0-icon-mood-super-negative",
|
|
33
|
+
"mood-negative": "text-f0-icon-mood-negative",
|
|
34
|
+
"mood-neutral": "text-f0-icon-mood-neutral",
|
|
35
|
+
"mood-positive": "text-f0-icon-mood-positive",
|
|
36
|
+
"mood-super-positive": "text-f0-icon-mood-super-positive",
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
defaultVariants: {
|
|
41
|
+
size: "md",
|
|
42
|
+
},
|
|
43
|
+
})
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import React, { useMemo } from "react"
|
|
2
|
+
import type { Svg } from "react-native-svg"
|
|
3
|
+
import { withUniwind } from "uniwind"
|
|
4
|
+
|
|
5
|
+
import { cn } from "../../../lib/utils"
|
|
6
|
+
|
|
7
|
+
import { iconVariants } from "./F0Icon.styles"
|
|
8
|
+
import type { F0IconProps, IconType } from "./F0Icon.types"
|
|
9
|
+
|
|
10
|
+
// Cache original icon -> wrapped icon so withUniwind is only applied once per icon type
|
|
11
|
+
const interopCache = new WeakMap<IconType, IconType>()
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Applies UniWind interop to an icon component
|
|
15
|
+
* Ensures withUniwind is only applied once per icon type
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
18
|
+
export function applyIconInterop(icon: IconType): IconType {
|
|
19
|
+
let wrapped = interopCache.get(icon)
|
|
20
|
+
if (!wrapped) {
|
|
21
|
+
wrapped = withUniwind(icon) as IconType
|
|
22
|
+
interopCache.set(icon, wrapped)
|
|
23
|
+
}
|
|
24
|
+
return wrapped
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* F0Icon - Icon component for the F0 Design System
|
|
29
|
+
*
|
|
30
|
+
* Renders SVG icons with consistent sizing and semantic colors.
|
|
31
|
+
* Icons are automatically wrapped with UniWind for className support.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* import { Archive } from '@/icons/app';
|
|
35
|
+
*
|
|
36
|
+
* <F0Icon icon={Archive} size="lg" />
|
|
37
|
+
* <F0Icon icon={Archive} color="critical" />
|
|
38
|
+
* <F0Icon icon={Archive} size="sm" color="positive" />
|
|
39
|
+
*/
|
|
40
|
+
const F0Icon = React.memo(
|
|
41
|
+
React.forwardRef<Svg, F0IconProps>(
|
|
42
|
+
(
|
|
43
|
+
{ size = "md", color, icon, testID, className: customClassName, ...rest },
|
|
44
|
+
ref
|
|
45
|
+
) => {
|
|
46
|
+
const IconComponent = useMemo(
|
|
47
|
+
() => (icon ? applyIconInterop(icon) : null),
|
|
48
|
+
[icon]
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
const className = useMemo(
|
|
52
|
+
() => cn(iconVariants({ size, color }), customClassName),
|
|
53
|
+
[size, color, customClassName]
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
// Early return if no icon provided (after all hooks)
|
|
57
|
+
if (!icon || !IconComponent) return null
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<IconComponent
|
|
61
|
+
ref={ref}
|
|
62
|
+
className={className}
|
|
63
|
+
testID={testID}
|
|
64
|
+
{...rest}
|
|
65
|
+
/>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
F0Icon.displayName = "F0Icon"
|
|
72
|
+
|
|
73
|
+
export default F0Icon
|