Skip to content

Commit

Permalink
feat(form):
Browse files Browse the repository at this point in the history
- ✨ Add FormDescription component for form field descriptions
- 🏗️ Move context logic to dedicated form-context.ts file
- 🛠️ Add useFormFieldContext hook with better error handling
- ♻️ Improve error message types and render prop patterns

refactor(form): 🔄 Enhance form component architecture and API

style(form):
- 🎨 Rename compound component exports to use -parts suffix
- 🔤 Update provider names for better DX
  • Loading branch information
Ryan-Zayne committed Jan 8, 2025
1 parent c091513 commit f02d029
Show file tree
Hide file tree
Showing 51 changed files with 1,058 additions and 869 deletions.
17 changes: 17 additions & 0 deletions .changeset/gorgeous-guests-do.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
"@zayne-labs/ui-react": minor
---

feat(form):

- ✨ Add FormDescription component for form field descriptions
- 🏗️ Move context logic to dedicated form-context.ts file
- 🛠️ Add useFormFieldContext hook with better error handling
- ♻️ Improve error message types and render prop patterns

refactor(form): 🔄 Enhance form component architecture and API

style(form):

- 🎨 Rename compound component exports to use -parts suffix
- 🔤 Update provider names for better DX
6 changes: 3 additions & 3 deletions dev/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@
"preview": "vite preview"
},
"dependencies": {
"@types/react": "19.0.2",
"@types/react": "19.0.3",
"@types/react-dom": "19.0.2",
"@zayne-labs/ui-react": "workspace:*",
"react": "19.0.0",
"react-dom": "19.0.0"
},
"devDependencies": {
"@types/node": "^22.10.3",
"@types/node": "^22.10.5",
"@vitejs/plugin-react-swc": "^3.7.2",
"@zayne-labs/tsconfig": "0.2.1",
"tsx": "^4.19.1",
"typescript": "5.7.2",
"vite": "^6.0.1"
"vite": "^6.0.7"
}
}
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@
"devDependencies": {
"@arethetypeswrong/cli": "^0.17.2",
"@changesets/cli": "^2.27.9",
"@eslint-react/eslint-plugin": "^1.23.0",
"@types/node": "^22.10.1",
"@zayne-labs/eslint-config": "0.4.3",
"@eslint-react/eslint-plugin": "^1.23.2",
"@types/node": "^22.10.5",
"@zayne-labs/eslint-config": "0.5.0",
"@zayne-labs/tsconfig": "0.2.1",
"eslint": "9.17.0",
"eslint-plugin-react-hooks": "^5.1.0",
Expand Down
File renamed without changes.
18 changes: 9 additions & 9 deletions packages/ui-react/package.json → packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,28 +67,28 @@
}
},
"dependencies": {
"@zayne-labs/toolkit-core": "^0.8.22",
"@zayne-labs/toolkit-react": "^0.8.22",
"@zayne-labs/toolkit-type-helpers": "^0.8.22",
"@zayne-labs/toolkit-core": "^0.8.26",
"@zayne-labs/toolkit-react": "^0.8.26",
"@zayne-labs/toolkit-type-helpers": "^0.8.26",
"zustand": "^5.0.2"
},
"devDependencies": {
"@arethetypeswrong/cli": "0.17.0",
"@arethetypeswrong/cli": "0.17.2",
"@size-limit/esbuild-why": "11.1.6",
"@size-limit/preset-small-lib": "11.1.6",
"@total-typescript/ts-reset": "0.6.1",
"@types/react": "^19.0.2",
"@types/react": "^19.0.3",
"@types/react-dom": "^19.0.2",
"@zayne-labs/tsconfig": "0.2.1",
"concurrently": "^9.0.1",
"cross-env": "^7.0.3",
"publint": "^0.2.11",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"publint": "^0.3.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-hook-form": "^7.53.2",
"size-limit": "11.1.6",
"tailwind-merge": "^2.5.5",
"terser": "5.36.0",
"terser": "5.37.0",
"tsup": "8.3.5",
"typescript": "^5.7.2"
},
Expand Down
File renamed without changes.
2 changes: 2 additions & 0 deletions packages/react/src/components/common/show/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./show";
export * as Show from "./show";
6 changes: 6 additions & 0 deletions packages/react/src/components/common/show/show-parts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export {
ShowContent as Content,
ShowFallback as Fallback,
ShowFallback as OtherWise,
ShowRoot as Root,
} from "./show";
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export function ShowRoot({ children, fallback, when }: ShowProps) {
return when ? (contentSlot ?? otherChildren) : (fallBackSlot ?? fallback);
}

export const Show = ShowRoot;

export function ShowContent({ children }: Pick<ShowProps, "children">) {
return children;
}
Expand All @@ -42,13 +44,3 @@ export function ShowFallback({ children }: Pick<ShowProps, "children">) {
return children;
}
ShowFallback.slot = Symbol.for("show-fallback");

export const Show = ShowRoot;

export const Root = ShowRoot;

export const Fallback = ShowFallback;

export const Content = ShowContent;

export const OtherWise = ShowFallback;
3 changes: 3 additions & 0 deletions packages/react/src/components/common/switch/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./switch";

export * from "./switch-parts";
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { SwitchDefault as Default, SwitchMatch as Match, SwitchRoot as Root } from "./switch";
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ export function SwitchRoot<TCondition = true>(props: SwitchProps<TCondition>) {
throwOnMultipleSlotMatch: true,
});

const childrenCasesArray = getOtherChildren(children, Default);
const childrenCasesArray = getOtherChildren(children, SwitchDefault);

const matchedCase = childrenCasesArray.find((child) => child.props.when === condition);

return matchedCase ?? defaultCase;
}

export const Switch = SwitchRoot;

export function SwitchMatch<TWhen>({ children }: SwitchMatchProps<TWhen>) {
return children;
}
Expand All @@ -39,11 +41,3 @@ export function SwitchDefault({ children }: Pick<SwitchMatchProps, "children">)
return children;
}
SwitchDefault.slot = Symbol.for("switch-default");

export const Switch = SwitchRoot;

export const Root = SwitchRoot;

export const Match = SwitchMatch;

export const Default = SwitchDefault;
File renamed without changes.
8 changes: 8 additions & 0 deletions packages/react/src/components/ui/card/card-parts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export {
CardContent as Content,
CardDescription as Description,
CardFooter as Footer,
CardHeader as Header,
CardRoot as Root,
CardTitle as Title,
} from "./card";
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,47 @@ import { Slot } from "@/components/common/slot";
import { cnMerge } from "@/lib/utils/cn";
import type { PolymorphicProps } from "@zayne-labs/toolkit-react/utils";

function CardRoot<TElement extends React.ElementType = "article">(props: PolymorphicProps<TElement>) {
export function CardRoot<TElement extends React.ElementType = "article">(
props: PolymorphicProps<TElement>
) {
const { as: Element = "article", ...restOfProps } = props;

return <Element {...restOfProps} />;
}

function CardHeader<TElement extends React.ElementType = "header">(props: PolymorphicProps<TElement>) {
export function CardHeader<TElement extends React.ElementType = "header">(
props: PolymorphicProps<TElement>
) {
const { as: Element = "header", ...restOfProps } = props;

return <Element {...restOfProps} />;
}

function CardTitle<TElement extends React.ElementType = "h3">(props: PolymorphicProps<TElement>) {
export function CardTitle<TElement extends React.ElementType = "h3">(props: PolymorphicProps<TElement>) {
const { as: Element = "h3", className, ...restOfProps } = props;

return <Element className={cnMerge("font-semibold", className)} {...restOfProps} />;
}

function CardDescription<TElement extends React.ElementType = "p">(props: PolymorphicProps<TElement>) {
export function CardDescription<TElement extends React.ElementType = "p">(
props: PolymorphicProps<TElement>
) {
const { as: Element = "p", className, ...restOfProps } = props;

return (
<Element className={cnMerge("text-sm text-shadcn-muted-foreground", className)} {...restOfProps} />
);
}

function CardContent<TElement extends React.ElementType = "div">(props: PolymorphicProps<TElement>) {
export function CardContent<TElement extends React.ElementType = "div">(
props: PolymorphicProps<TElement>
) {
const { as: Element = "div", ...restOfProps } = props;

return <Element {...restOfProps} />;
}

function CardFooter<TElement extends React.ElementType = "footer">(
export function CardFooter<TElement extends React.ElementType = "footer">(
props: PolymorphicProps<TElement, { asChild?: boolean }>
) {
const { as: Element = "footer", asChild, ...restOfProps } = props;
Expand All @@ -45,15 +53,3 @@ function CardFooter<TElement extends React.ElementType = "footer">(

return <Component {...restOfProps} />;
}

export const Root = CardRoot;

export const Header = CardHeader;

export const Title = CardTitle;

export const Description = CardDescription;

export const Content = CardContent;

export const Footer = CardFooter;
3 changes: 3 additions & 0 deletions packages/react/src/components/ui/card/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./card";

export * as Card from "./card-parts";
11 changes: 11 additions & 0 deletions packages/react/src/components/ui/carousel/carousel-parts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export { CarouselContextProvider as Root } from "./carousel-store-context";
export {
CarouselContent as Content,
CarouselControls as Controls,
CarouselButton as Button,
CarouselItem as Item,
CarouselItemWrapper as ItemWrapper,
CarouselCaption as Caption,
CarouselIndicator as Indicator,
CarouselIndicatorWrapper as IndicatorWrapper,
} from "./carousel";
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useEffect } from "react";
import { create } from "zustand";
import type { CarouselProviderProps, CarouselStore, ImagesType } from "./types";

const [Provider, useCarouselStore] = createZustandContext<CarouselStore>({
const [Provider, useCarouselStoreContext] = createZustandContext<CarouselStore>({
hookName: "useCarouselStore",
name: "CarouselStoreContext",
providerName: "CarouselContextProvider",
Expand Down Expand Up @@ -61,9 +61,10 @@ const createCarouselStore = <TImages extends ImagesType>(
return useInitCarouselStore;
};

// == Provider Component
function CarouselContextProvider<TImages extends ImagesType>(props: CarouselProviderProps<TImages>) {
const { children, images, onSlideBtnClick } = props;
const useCarousel = <TImages extends ImagesType>(
props: Omit<CarouselProviderProps<TImages>, "children">
) => {
const { images, onSlideBtnClick } = props;

const useInitCarouselStore = useConstant(() => createCarouselStore({ images, onSlideBtnClick }));

Expand All @@ -73,8 +74,17 @@ function CarouselContextProvider<TImages extends ImagesType>(props: CarouselProv
// eslint-disable-next-line react-hooks/exhaustive-deps -- useInitCarouselStore is stable
}, [images]);

return useInitCarouselStore;
};

// == Provider Component
function CarouselContextProvider<TImages extends ImagesType>(props: CarouselProviderProps<TImages>) {
const { children, images, onSlideBtnClick } = props;

const useInitCarouselStore = useCarousel({ images, onSlideBtnClick });

return <Provider value={useInitCarouselStore}>{children}</Provider>;
}

// eslint-disable-next-line react-refresh/only-export-components -- It's fine
export { useCarouselStore, CarouselContextProvider };
export { useCarouselStoreContext, CarouselContextProvider };
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { getElementList } from "@/components/common/for";
import { ChevronLeftIcon } from "@/components/icons";
import { cnMerge } from "@/lib/utils/cn";
import type { MyCustomCss, PolymorphicProps } from "@zayne-labs/toolkit-react/utils";
import { useCarouselStore } from "./carouselStoreContext";
import { useCarouselStoreContext } from "./carousel-store-context";
import type {
CarouselButtonsProps,
CarouselContentProps,
Expand Down Expand Up @@ -60,7 +60,7 @@ export function CarouselContent<TElement extends React.ElementType = "article">(
export function CarouselButton(props: CarouselButtonsProps) {
const { classNames, icon, variant } = props;

const { goToNextSlide, goToPreviousSlide } = useCarouselStore((state) => state.actions);
const { goToNextSlide, goToPreviousSlide } = useCarouselStoreContext((state) => state.actions);

return (
<button
Expand Down Expand Up @@ -141,8 +141,8 @@ export function CarouselItemWrapper<TArrayItem>(props: CarouselWrapperProps<TArr
const { children, className, each, render } = props;

const [ItemList] = getElementList("base");
const currentSlide = useCarouselStore((state) => state.currentSlide);
const images = useCarouselStore((state) => each ?? (state.images as TArrayItem[]));
const currentSlide = useCarouselStoreContext((state) => state.currentSlide);
const images = useCarouselStoreContext((state) => each ?? (state.images as TArrayItem[]));

return (
<ul
Expand Down Expand Up @@ -193,7 +193,7 @@ export function CarouselCaption<TElement extends React.ElementType = "div">(
export function CarouselIndicatorWrapper<TArrayItem>(props: CarouselWrapperProps<TArrayItem>) {
const { children, className, each, render } = props;

const images = useCarouselStore((state) => each ?? (state.images as TArrayItem[]));
const images = useCarouselStoreContext((state) => each ?? (state.images as TArrayItem[]));
const [IndicatorList] = getElementList("base");

return (
Expand All @@ -219,7 +219,7 @@ export function CarouselIndicator(props: CarouselIndicatorProps) {
const {
actions: { goToSlide },
currentSlide,
} = useCarouselStore((state) => state);
} = useCarouselStoreContext((state) => state);

return (
<li className={cnMerge("inline-flex", classNames?.base)}>
Expand All @@ -236,21 +236,3 @@ export function CarouselIndicator(props: CarouselIndicatorProps) {
</li>
);
}

export { CarouselContextProvider as Root } from "./carouselStoreContext";

export const Content = CarouselContent;

export const Controls = CarouselControls;

export const Button = CarouselButton;

export const Item = CarouselItem;

export const ItemWrapper = CarouselItemWrapper;

export const Caption = CarouselCaption;

export const Indicator = CarouselIndicator;

export const IndicatorWrapper = CarouselIndicatorWrapper;
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./carousel";
export type * from "./types";
export * as Carousel from "./carousel-parts";
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useAnimationInterval, useCallbackRef } from "@zayne-labs/toolkit-react";
import { useState } from "react";
import { useCarouselStore } from "./carouselStoreContext";
import { useCarouselStoreContext } from "./carousel-store-context";

type CarouselOptions = {
autoSlideInterval?: number;
Expand All @@ -11,7 +11,7 @@ type CarouselOptions = {
const useCarouselOptions = (options: CarouselOptions = {}) => {
const { autoSlideInterval = 5000, hasAutoSlide = false, shouldPauseOnHover = false } = options;

const { goToNextSlide } = useCarouselStore((state) => state.actions);
const { goToNextSlide } = useCarouselStoreContext((state) => state.actions);

const [isPaused, setIsPaused] = useState(false);

Expand Down
1 change: 1 addition & 0 deletions packages/react/src/components/ui/drag-scroll/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { useDragScroll } from "./use-drag-scroll";
Loading

0 comments on commit f02d029

Please sign in to comment.