diff --git a/comps/ui/carousel.tsx b/comps/ui/carousel.tsx new file mode 100755 index 0000000..7d51928 --- /dev/null +++ b/comps/ui/carousel.tsx @@ -0,0 +1,260 @@ +import * as React from "react" +import useEmblaCarousel, { + type UseEmblaCarouselType, +} from "embla-carousel-react" +import { ArrowLeft, ArrowRight } from "lucide-react" + +import { cn } from "@/utils" +import { Button } from "@/comps/ui/button" + +type CarouselApi = UseEmblaCarouselType[1] +type UseCarouselParameters = Parameters +type CarouselOptions = UseCarouselParameters[0] +type CarouselPlugin = UseCarouselParameters[1] + +type CarouselProps = { + opts?: CarouselOptions + plugins?: CarouselPlugin + orientation?: "horizontal" | "vertical" + setApi?: (api: CarouselApi) => void +} + +type CarouselContextProps = { + carouselRef: ReturnType[0] + api: ReturnType[1] + scrollPrev: () => void + scrollNext: () => void + canScrollPrev: boolean + canScrollNext: boolean +} & CarouselProps + +const CarouselContext = React.createContext(null) + +function useCarousel() { + const context = React.useContext(CarouselContext) + + if (!context) { + throw new Error("useCarousel must be used within a ") + } + + return context +} + +const Carousel = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & CarouselProps +>( + ( + { + orientation = "horizontal", + opts, + setApi, + plugins, + className, + children, + ...props + }, + ref + ) => { + const [carouselRef, api] = useEmblaCarousel( + { + ...opts, + axis: orientation === "horizontal" ? "x" : "y", + }, + plugins + ) + const [canScrollPrev, setCanScrollPrev] = React.useState(false) + const [canScrollNext, setCanScrollNext] = React.useState(false) + + const onSelect = React.useCallback((api: CarouselApi) => { + if (!api) { + return + } + + setCanScrollPrev(api.canScrollPrev()) + setCanScrollNext(api.canScrollNext()) + }, []) + + const scrollPrev = React.useCallback(() => { + api?.scrollPrev() + }, [api]) + + const scrollNext = React.useCallback(() => { + api?.scrollNext() + }, [api]) + + const handleKeyDown = React.useCallback( + (event: React.KeyboardEvent) => { + if (event.key === "ArrowLeft") { + event.preventDefault() + scrollPrev() + } else if (event.key === "ArrowRight") { + event.preventDefault() + scrollNext() + } + }, + [scrollPrev, scrollNext] + ) + + React.useEffect(() => { + if (!api || !setApi) { + return + } + + setApi(api) + }, [api, setApi]) + + React.useEffect(() => { + if (!api) { + return + } + + onSelect(api) + api.on("reInit", onSelect) + api.on("select", onSelect) + + return () => { + api?.off("select", onSelect) + } + }, [api, onSelect]) + + return ( + +
+ {children} +
+
+ ) + } +) +Carousel.displayName = "Carousel" + +const CarouselContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const { carouselRef, orientation } = useCarousel() + + return ( +
+
+
+ ) +}) +CarouselContent.displayName = "CarouselContent" + +const CarouselItem = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const { orientation } = useCarousel() + + return ( +
+ ) +}) +CarouselItem.displayName = "CarouselItem" + +const CarouselPrevious = React.forwardRef< + HTMLButtonElement, + React.ComponentProps +>(({ className, variant = "outline", size = "icon", ...props }, ref) => { + const { orientation, scrollPrev, canScrollPrev } = useCarousel() + + return ( + + ) +}) +CarouselPrevious.displayName = "CarouselPrevious" + +const CarouselNext = React.forwardRef< + HTMLButtonElement, + React.ComponentProps +>(({ className, variant = "outline", size = "icon", ...props }, ref) => { + const { orientation, scrollNext, canScrollNext } = useCarousel() + + return ( + + ) +}) +CarouselNext.displayName = "CarouselNext" + +export { + type CarouselApi, + Carousel, + CarouselContent, + CarouselItem, + CarouselPrevious, + CarouselNext, +} diff --git a/exports.tsx b/exports.tsx index 021245d..9685de3 100755 --- a/exports.tsx +++ b/exports.tsx @@ -96,16 +96,17 @@ export const ScrollArea = lazify( ); export { FieldLoading } from "@/comps/ui/field-loading"; -export { fetchLinkParams } from "./comps/form/field/type/TypeLink"; +export { fetchLinkParams } from "@/comps/form/field/type/TypeLink"; export { prasi_gen } from "./gen/prasi_gen"; export { guessLabel } from "./utils/guess-label"; +export { lang } from "@/lang/lang"; import __get from "lodash.get"; import { sum } from "./utils/sum"; export const _sum = sum; export const _get = __get; - + /** Generator */ export { generateFilter as genereteFilter } from "@/comps/filter/gen/gen-filter"; export { generateRelation } from "@/comps/form/gen/gen-rel"; diff --git a/lang/en.ts b/lang/en.ts new file mode 100755 index 0000000..ccaf2de --- /dev/null +++ b/lang/en.ts @@ -0,0 +1,7 @@ +export const langEn = { + "Add New": "Add New", + Save: "Save", + Delete: "Delete", + "Record Saved": "Record Saved", + "Search": "Search", +}; diff --git a/lang/id.ts b/lang/id.ts new file mode 100755 index 0000000..521f239 --- /dev/null +++ b/lang/id.ts @@ -0,0 +1,9 @@ +import { LangKeyword } from "./type"; + +export const langId: Record = { + "Add New": "Tambah Baru", + Save: "Simpan", + Delete: "Hapus", + "Record Saved": "Data Tersimpan", + Search: "Cari", +}; diff --git a/lang/lang.ts b/lang/lang.ts new file mode 100755 index 0000000..67a4bb6 --- /dev/null +++ b/lang/lang.ts @@ -0,0 +1,16 @@ +import { AvailableLang, LangKeyword } from "./type"; + +export const lang = { + async init(current: AvailableLang) { + this.current = current; + if (current === "en") this.base = (await import("./en")).langEn as any; + if (current === "id") this.base = (await import("./id")).langId as any; + }, + current: "en" as AvailableLang, + t(keyword: LangKeyword, args?: Record): string { + let final = this.base?.[keyword] || keyword; + if (args) return final.replace(/{(.*?)}/g, (_, offset) => args[offset]); + return final; + }, + base: null as null | Record, +}; diff --git a/lang/type.ts b/lang/type.ts new file mode 100755 index 0000000..9d02847 --- /dev/null +++ b/lang/type.ts @@ -0,0 +1,5 @@ +import { langEn } from "./en"; + +export type LangKeyword = keyof typeof langEn; + +export type AvailableLang = "id" | "en"; \ No newline at end of file diff --git a/utils/prasi-events.ts b/utils/prasi-events.ts index 3004cc6..3a41ce7 100755 --- a/utils/prasi-events.ts +++ b/utils/prasi-events.ts @@ -1,6 +1,7 @@ import { FieldLocal } from "lib/comps/form/typings"; import { MDLocal } from "lib/comps/md/utils/typings"; import { FMLocal } from "../.."; +//@ts-ignore import { Prisma } from "../../typings/prisma"; import { set } from "./set";