From ff1ba55b25c6c1f04bb0f580d29abf3d86be4cc6 Mon Sep 17 00:00:00 2001 From: Brendan Date: Mon, 9 Jun 2025 14:39:43 -0700 Subject: [PATCH 1/2] feat: update day picker to work with later version, add progress --- bun.lock | 7 +++ mirascope-ui/ui/calendar.stories.tsx | 36 +++++++++++- mirascope-ui/ui/calendar.tsx | 84 +++++++++++++--------------- mirascope-ui/ui/progress.stories.tsx | 46 +++++++++++++++ mirascope-ui/ui/progress.tsx | 26 +++++++++ package.json | 1 + registry.json | 14 +++++ 7 files changed, 165 insertions(+), 49 deletions(-) create mode 100644 mirascope-ui/ui/progress.stories.tsx create mode 100644 mirascope-ui/ui/progress.tsx diff --git a/bun.lock b/bun.lock index dcc470f..15e4ab9 100644 --- a/bun.lock +++ b/bun.lock @@ -13,6 +13,7 @@ "@radix-ui/react-icons": "^1.3.2", "@radix-ui/react-label": "^2.1.4", "@radix-ui/react-popover": "^1.1.13", + "@radix-ui/react-progress": "^1.1.7", "@radix-ui/react-scroll-area": "^1.2.8", "@radix-ui/react-select": "^2.2.4", "@radix-ui/react-separator": "^1.1.6", @@ -560,6 +561,8 @@ "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.2", "", { "dependencies": { "@radix-ui/react-slot": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw=="], + "@radix-ui/react-progress": ["@radix-ui/react-progress@1.1.7", "", { "dependencies": { "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg=="], + "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.9", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-collection": "1.1.6", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.2", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-ZzrIFnMYHHCNqSNCsuN6l7wlewBEq0O0BCSBkabJMFXVO51LRUTq71gLP1UxFvmrXElqmPjA5VX7IqC9VpazAQ=="], "@radix-ui/react-scroll-area": ["@radix-ui/react-scroll-area@1.2.8", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.4", "@radix-ui/react-primitive": "2.1.2", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-K5h1RkYA6M0Sn61BV5LQs686zqBsSC0sGzL4/Gw4mNnjzrQcGSc6YXfC6CRFNaGydSdv5+M8cb0eNsOGo0OXtQ=="], @@ -2394,6 +2397,8 @@ "@pmmmwh/react-refresh-webpack-plugin/loader-utils": ["loader-utils@2.0.4", "", { "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", "json5": "^2.1.2" } }, "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw=="], + "@radix-ui/react-progress/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + "@storybook/react-docgen-typescript-plugin/find-cache-dir": ["find-cache-dir@3.3.2", "", { "dependencies": { "commondir": "^1.0.1", "make-dir": "^3.0.2", "pkg-dir": "^4.1.0" } }, "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig=="], "@storybook/react-docgen-typescript-plugin/flat-cache": ["flat-cache@3.2.0", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", "rimraf": "^3.0.2" } }, "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw=="], @@ -2604,6 +2609,8 @@ "@next/eslint-plugin-next/fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "@radix-ui/react-progress/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + "@storybook/react-docgen-typescript-plugin/find-cache-dir/pkg-dir": ["pkg-dir@4.2.0", "", { "dependencies": { "find-up": "^4.0.0" } }, "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ=="], "@ts-morph/common/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], diff --git a/mirascope-ui/ui/calendar.stories.tsx b/mirascope-ui/ui/calendar.stories.tsx index ca6beb3..c6b4a64 100644 --- a/mirascope-ui/ui/calendar.stories.tsx +++ b/mirascope-ui/ui/calendar.stories.tsx @@ -1,8 +1,12 @@ import type { Meta, StoryObj } from "@storybook/react"; import { useState } from "react"; -import { addDays } from "date-fns"; +import { addDays, format } from "date-fns"; import { Calendar } from "@/mirascope-ui/ui/calendar"; +import { Popover, PopoverContent, PopoverTrigger } from "@/mirascope-ui/ui/popover"; +import { Button } from "@/mirascope-ui/ui/button"; +import { cn } from "@/mirascope-ui/lib/utils"; +import { CalendarIcon } from "lucide-react"; const meta = { title: "UI/Calendar", @@ -52,9 +56,10 @@ export const Default: Story = {}; */ export const SingleSelection: Story = { render: () => { - const [date, setDate] = useState(new Date()); return ( - +
+ +
); }, }; @@ -129,6 +134,31 @@ export const DisabledDates: Story = { }, }; +export const ButtonCalendar: Story = { + render: () => { + const [date, setDate] = useState(new Date()); + return ( + + + + + + setDate(date)} autoFocus /> + + + ); + }, +}; + /** * Calendar with a custom CSS class to control the size. */ diff --git a/mirascope-ui/ui/calendar.tsx b/mirascope-ui/ui/calendar.tsx index 5414cb1..1ef98b6 100644 --- a/mirascope-ui/ui/calendar.tsx +++ b/mirascope-ui/ui/calendar.tsx @@ -1,74 +1,66 @@ import * as React from "react"; -import { ChevronLeft, ChevronRight } from "lucide-react"; -import { DayPicker } from "react-day-picker"; +import { DayFlag, DayPicker, SelectionState, UI } from "react-day-picker"; import { cn } from "@/mirascope-ui/lib/utils"; -import { buttonVariants } from "@/mirascope-ui/ui/button"; +import { buttonVariants } from "./button"; +import { ChevronLeft, ChevronRight } from "lucide-react"; + +export type CalendarProps = React.ComponentProps; -function Calendar({ +export const Calendar = ({ className, classNames, showOutsideDays = true, ...props -}: React.ComponentProps) { +}: CalendarProps) => { return ( .day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md" - : "[&:has([aria-selected])]:rounded-md" + [UI.NextMonthButton]: cn( + buttonVariants({ variant: "outline" }), + "absolute right-1 top-0 h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100" ), - day: cn( + [UI.MonthGrid]: "w-full border-collapse space-y-1", + [UI.Weekdays]: "flex", + [UI.Weekday]: "text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]", + [UI.Week]: "flex w-full mt-2", + [UI.Day]: + "h-9 w-9 text-center rounded-md text-sm p-0 relative [&:has([aria-selected].day-range-end)]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20", + [UI.DayButton]: cn( buttonVariants({ variant: "ghost" }), - "size-8 p-0 font-normal aria-selected:opacity-100" + "h-9 w-9 p-0 font-normal aria-selected:opacity-100 hover:bg-primary hover:text-primary-foreground" ), - day_range_start: - "day-range-start aria-selected:bg-primary aria-selected:text-primary-foreground", - day_range_end: - "day-range-end aria-selected:bg-primary aria-selected:text-primary-foreground", - day_selected: + [SelectionState.range_end]: "day-range-end", + [SelectionState.selected]: "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground", - day_today: "bg-accent text-accent-foreground", - day_outside: "day-outside text-muted-foreground aria-selected:text-muted-foreground", - day_disabled: "text-muted-foreground opacity-50", - day_range_middle: "aria-selected:bg-accent aria-selected:text-accent-foreground", - day_hidden: "invisible", + [SelectionState.range_middle]: + "aria-selected:bg-accent aria-selected:text-accent-foreground", + [DayFlag.today]: "bg-accent text-accent-foreground", + [DayFlag.outside]: + "day-outside text-muted-foreground opacity-50 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30", + [DayFlag.disabled]: "text-muted-foreground opacity-50", + [DayFlag.hidden]: "invisible", ...classNames, }} components={{ - PreviousMonthButton: (props) => ( - + PreviousMonthButton: ({ className }: { className?: string }) => ( + ), - NextMonthButton: (props) => ( - + NextMonthButton: ({ className }: { className?: string }) => ( + ), }} {...props} /> ); -} - -export { Calendar }; +}; diff --git a/mirascope-ui/ui/progress.stories.tsx b/mirascope-ui/ui/progress.stories.tsx new file mode 100644 index 0000000..abe889b --- /dev/null +++ b/mirascope-ui/ui/progress.stories.tsx @@ -0,0 +1,46 @@ +import type { Meta, StoryObj } from "@storybook/react"; + +import { Progress } from "@/mirascope-ui/ui/progress"; + +/** + * Displays an indicator showing the completion progress of a task, typically + * displayed as a progress bar. + */ +const meta = { + title: "ui/Progress", + component: Progress, + tags: ["autodocs"], + argTypes: {}, + args: { + "aria-label": "Progress", + value: 30, + max: 100, + }, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +/** + * The default form of the progress. + */ +export const Default: Story = {}; + +/** + * When the progress is indeterminate. + */ +export const Indeterminate: Story = { + args: { + value: undefined, + }, +}; + +/** + * When the progress is completed. + */ +export const Completed: Story = { + args: { + value: 100, + }, +}; diff --git a/mirascope-ui/ui/progress.tsx b/mirascope-ui/ui/progress.tsx new file mode 100644 index 0000000..da04aeb --- /dev/null +++ b/mirascope-ui/ui/progress.tsx @@ -0,0 +1,26 @@ +import * as React from "react"; +import * as ProgressPrimitive from "@radix-ui/react-progress"; + +import { cn } from "@/mirascope-ui/lib/utils"; + +function Progress({ + className, + value, + ...props +}: React.ComponentProps) { + return ( + + + + ); +} + +export { Progress }; diff --git a/package.json b/package.json index 628cdf5..adee0fc 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "@radix-ui/react-icons": "^1.3.2", "@radix-ui/react-label": "^2.1.4", "@radix-ui/react-popover": "^1.1.13", + "@radix-ui/react-progress": "^1.1.7", "@radix-ui/react-scroll-area": "^1.2.8", "@radix-ui/react-select": "^2.2.4", "@radix-ui/react-separator": "^1.1.6", diff --git a/registry.json b/registry.json index c092b63..6c7ad2c 100644 --- a/registry.json +++ b/registry.json @@ -416,6 +416,20 @@ } ] }, + { + "name": "progress", + "type": "registry:ui", + "title": "Progress", + "description": "Displays an indicator showing the completion progress of a task, typically displayed as a progress bar.", + "dependencies": ["@radix-ui/react-progress"], + "registryDependencies": ["utils"], + "files": [ + { + "path": "mirascope-ui/ui/progress.tsx", + "type": "registry:ui" + } + ] + }, { "name": "sonner", "type": "registry:ui", From bd34b696bcf74ab0af309e1f2d85ae8df4efd42b Mon Sep 17 00:00:00 2001 From: Brendan Date: Mon, 9 Jun 2025 15:22:02 -0700 Subject: [PATCH 2/2] feat: add dynamic color to progress --- mirascope-ui/ui/progress.stories.tsx | 7 +++++++ mirascope-ui/ui/progress.tsx | 7 +++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/mirascope-ui/ui/progress.stories.tsx b/mirascope-ui/ui/progress.stories.tsx index abe889b..df26eb5 100644 --- a/mirascope-ui/ui/progress.stories.tsx +++ b/mirascope-ui/ui/progress.stories.tsx @@ -44,3 +44,10 @@ export const Completed: Story = { value: 100, }, }; + +export const Color: Story = { + args: { + value: 100, + indicatorClassName: "bg-red-500", + }, +}; diff --git a/mirascope-ui/ui/progress.tsx b/mirascope-ui/ui/progress.tsx index da04aeb..19a7c0b 100644 --- a/mirascope-ui/ui/progress.tsx +++ b/mirascope-ui/ui/progress.tsx @@ -6,8 +6,11 @@ import { cn } from "@/mirascope-ui/lib/utils"; function Progress({ className, value, + indicatorClassName, ...props -}: React.ComponentProps) { +}: React.ComponentProps & { + indicatorClassName?: string; +}) { return (