Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(icons): ✨ add new icon & createIcon component #69

Draft
wants to merge 2 commits into
base: ariakit-system
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 36 additions & 11 deletions example/src/modules/primitives/BadgeScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,47 @@
import React from "react";
import { Path } from "react-native-svg";
import {
BadgeNew,
BadgeText,
BadgeWrapper,
AdaptIcon,
Box,
createAdaptIcon,
} from "@adaptui/react-native-tailwind";

const ClockIcon = createAdaptIcon({
path: (
<Path
fillRule="evenodd"
clipRule="evenodd"
d="M1.36104 5.99994C1.36104 3.43794 3.43794 1.36104 5.99994 1.36104C8.56193 1.36104 10.6388 3.43794 10.6388 5.99994C10.6388 8.56193 8.56193 10.6388 5.99994 10.6388C3.43794 10.6388 1.36104 8.56193 1.36104 5.99994ZM5.99994 0.0610352C2.71997 0.0610352 0.0610352 2.71997 0.0610352 5.99994C0.0610352 9.2799 2.71997 11.9388 5.99994 11.9388C9.2799 11.9388 11.9388 9.2799 11.9388 5.99994C11.9388 2.71997 9.2799 0.0610352 5.99994 0.0610352ZM6.6501 3.99995C6.6501 3.64096 6.35908 3.34995 6.0001 3.34995C5.64111 3.34995 5.3501 3.64096 5.3501 3.99995V6.49995C5.3501 6.71727 5.45871 6.92023 5.63954 7.04078L7.13954 8.04078C7.43824 8.23991 7.8418 8.15919 8.04093 7.8605C8.24006 7.56181 8.15935 7.15824 7.86065 6.95911L6.6501 6.15208V3.99995Z"
fill="#000"
/>
),
});

const DeleteIcon = createAdaptIcon({
d: "M4.65 0.199219C4.29101 0.199219 4 0.490234 4 0.849219C4 1.2082 4.29101 1.49922 4.65 1.49922H7.35C7.70899 1.49922 8 1.2082 8 0.849219C8 0.490234 7.70899 0.199219 7.35 0.199219H4.65ZM0 2.84922C0 2.49023 0.291015 2.19922 0.65 2.19922H11.35C11.709 2.19922 12 2.49023 12 2.84922C12 3.2082 11.709 3.49922 11.35 3.49922H0.65C0.291015 3.49922 0 3.2082 0 2.84922ZM2.65 4.99922C2.65 4.64023 2.35898 4.34922 2 4.34922C1.64101 4.34922 1.35 4.64023 1.35 4.99922V8.99922C1.35 10.4628 2.53645 11.6492 4 11.6492H8C9.46355 11.6492 10.65 10.4628 10.65 8.99922V4.99922C10.65 4.64023 10.359 4.34922 10 4.34922C9.64102 4.34922 9.35 4.64023 9.35 4.99922V8.99922C9.35 9.7448 8.74558 10.3492 8 10.3492H4C3.25442 10.3492 2.65 9.7448 2.65 8.99922V4.99922ZM6.65 4.99922C6.65 4.64023 6.35898 4.34922 6 4.34922C5.64102 4.34922 5.35 4.64023 5.35 4.99922V7.49922C5.35 7.8582 5.64102 8.14922 6 8.14922C6.35898 8.14922 6.65 7.8582 6.65 7.49922V4.99922Z",
pathFill: "#000",
});

export const BadgeScreen = () => {
return (
<Box className="flex-1 justify-center items-center bg-white-900">
<BadgeNew size="lg" className="my-1" themeColor="secondary">
Scheduled
</BadgeNew>
<BadgeNew size="md" className="my-1 bg-teal-500" themeColor="secondary">
<>Badge</>
<BadgeWrapper className="bg-yellow-500" />
<BadgeText className="text-red-500" />
</BadgeNew>
<AdaptIcon className="m-2" />
<AdaptIcon viewBox="0 0 12 12" className="m-2">
<Path
fillRule="evenodd"
clipRule="evenodd"
d="M6.65156 2.00156C6.65156 1.64258 6.36055 1.35156 6.00156 1.35156C5.64258 1.35156 5.35156 1.64258 5.35156 2.00156V5.35156H2.00156C1.64258 5.35156 1.35156 5.64258 1.35156 6.00156C1.35156 6.36055 1.64258 6.65156 2.00156 6.65156H5.35156V10.0016C5.35156 10.3605 5.64258 10.6516 6.00156 10.6516C6.36055 10.6516 6.65156 10.3605 6.65156 10.0016V6.65156H10.0016C10.3605 6.65156 10.6516 6.36055 10.6516 6.00156C10.6516 5.64258 10.3605 5.35156 10.0016 5.35156H6.65156V2.00156Z"
fill={"#000"}
/>
</AdaptIcon>
<AdaptIcon
viewBox="0 0 12 12"
className="m-2"
pathFill="#000"
d="M3.00156 0.351562C1.53801 0.351562 0.351562 1.53801 0.351562 3.00156V9.00156C0.351562 10.4651 1.53801 11.6516 3.00156 11.6516H9.00156C10.4651 11.6516 11.6516 10.4651 11.6516 9.00156V3.00156C11.6516 1.53801 10.4651 0.351562 9.00156 0.351562H3.00156ZM1.65156 3.00156C1.65156 2.25598 2.25598 1.65156 3.00156 1.65156H9.00156C9.74715 1.65156 10.3516 2.25598 10.3516 3.00156V9.00156C10.3516 9.74715 9.74715 10.3516 9.00156 10.3516H3.00156C2.25598 10.3516 1.65156 9.74715 1.65156 9.00156V3.00156ZM4.00156 2.85156C3.64258 2.85156 3.35156 3.14258 3.35156 3.50156C3.35156 3.86055 3.64258 4.15156 4.00156 4.15156H8.00156C8.36055 4.15156 8.65156 3.86055 8.65156 3.50156C8.65156 3.14258 8.36055 2.85156 8.00156 2.85156H4.00156Z"
/>
<ClockIcon className="m-2" />
<DeleteIcon className="m-2" />
</Box>
);
};
59 changes: 59 additions & 0 deletions src/primitives/icons/AdaptIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import Svg, { Path } from "react-native-svg";

import { useTheme } from "../../theme";
import { cx } from "../../utils";
import {
As,
createComponentType,
createElement,
createHook,
Props,
} from "../../utils/system";
import { BoxOptions, useBox } from "../box";

import { fallbackIcon } from "./__utils";

export const useAdaptIcon = createHook<AdaptIconOptions>(
({ d, pathFill = "#000", ...props }) => {
console.log("%cprops", "color: #ffa280", props);
const _viewBox = props.viewBox ?? fallbackIcon.viewBox;
const fallback = d ? <Path fill={pathFill} d={d} /> : fallbackIcon.children;
const children = props.children ?? fallback;

const iconStyles = useTheme("icon");
const className = cx(iconStyles.base, props.className);

props = {
...props,
viewBox: _viewBox,
children,
className,
};

props = useBox(props);

return props;
},
);

export const AdaptIcon = createComponentType<AdaptIconOptions>(props => {
const htmlProps = useAdaptIcon(props);

return createElement(Svg, htmlProps);
}, "AdaptIcon");

export type AdaptIconOptions<T extends As = typeof Svg> = BoxOptions<T> & {
/**
* If the has a single path, simply copy the path's `d` attribute
*/
d?: string;
/**
* Fill color for the AdaptIcon Component.
* @default "#000"
*/
pathFill?: string;
};

export type AdaptIconProps<T extends As = typeof Svg> = Props<
AdaptIconOptions<T>
>;
20 changes: 20 additions & 0 deletions src/primitives/icons/__utils.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Circle, G, Path } from "react-native-svg";

export const fallbackIcon = {
viewBox: "0 0 24 24",
children: (
<G stroke="#000" strokeWidth="1.5">
<Path
fill="none"
strokeLinecap="round"
d="M9,9a3,3,0,1,1,4,2.829,1.5,1.5,0,0,0-1,1.415V14.25"
/>
<Path
fill="#000"
strokeLinecap="round"
d="M12,17.25a.375.375,0,1,0,.375.375A.375.375,0,0,0,12,17.25h0"
/>
<Circle fill="none" strokeMiterlimit="10" cx="12" cy="12" r="11.25" />
</G>
),
};
66 changes: 66 additions & 0 deletions src/primitives/icons/createAdaptIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Credits to https://github.com/chakra-ui/chakra-ui/tree/main/packages/icon
import * as React from "react";
import { forwardRef } from "react";
import { View } from "react-native";

import { AdaptIcon, AdaptIconProps } from "./AdaptIcon";

export interface CreateAdaptIconOptions {
/**
* The icon `svg` viewBox
*
* @default "0 0 24 24"
*/
viewBox?: string;
/**
* The `svg` path or group element
*
* @type React.ReactNode | React.ReactNode[]
*/
path?: React.ReactNode | React.ReactNode[];
/**
* If the has a single path, simply copy the path's `d` attribute
*/
d?: string;
/**
* Fill color for the AdaptIcon Component.
* @default "#000"
*/
pathFill?: string;
/**
* The display name useful in the dev tools
*/
displayName?: string;
/**
* Default props automatically passed to the component; overwriteable
*/
defaultProps?: AdaptIconProps;
}

export function createAdaptIcon(options: CreateAdaptIconOptions) {
const {
path,
viewBox = "0 0 12 12",
displayName,
defaultProps = {},
...restOptions
} = options;

const AdaptIconComponent = forwardRef<View, AdaptIconProps>((props, ref) => {
return (
<AdaptIcon
ref={ref}
viewBox={viewBox}
{...restOptions}
{...defaultProps}
{...props}
>
{path}
</AdaptIcon>
);
});

AdaptIconComponent.displayName = displayName;

return AdaptIconComponent;
}
3 changes: 3 additions & 0 deletions src/primitives/icons/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./__utils";
export * from "./AdaptIcon";
export * from "./createAdaptIcon";
1 change: 1 addition & 0 deletions src/primitives/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from "./animated-box";
export * from "./box";
export * from "./icons";
export * from "./rn-textinput";
export * from "./text";
export * from "./touchable";
Expand Down
3 changes: 3 additions & 0 deletions src/theme/defaultTheme/icon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const icon = {
base: "w-4 h-4",
};
2 changes: 2 additions & 0 deletions src/theme/defaultTheme/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { badge } from "./badge";
import { button } from "./button";
import { checkbox } from "./checkbox";
import { circularProgress } from "./circularProgress";
import { icon } from "./icon";
import { input } from "./input";
import { meter } from "./meter";
import { progress } from "./progress";
Expand All @@ -22,6 +23,7 @@ const extendedTheme = {
button,
spinner,
circularProgress,
icon,
tag,
input,
tooltip,
Expand Down