Skip to content

Commit

Permalink
feat(@kampus/ui-next): Add other Shadcn components (kamp-us#501)
Browse files Browse the repository at this point in the history
# Description

Adds shadcn components for our ui library.

Added components:
- Time ago
- User avatar
- Theme provider
- Theme toggle
- Social Media Buttons

### Checklist

- [x] discord username: `username#0001`
- [x] Closes kamp-us#412 
- [x] Closes kamp-us#505 
- [x] PR must be created for an issue from issues under "In progress"
column from [our project
board](https://github.com/orgs/kamp-us/projects/2/views/1).
- [x] A descriptive and understandable title: The PR title should
clearly describe the nature and purpose of the changes. The PR title
should be the first thing displayed when the PR is opened. And it should
follow the semantic commit rules, and should include the
app/package/service name in the title. For example, a title like
"docs(@kampus-apps/pano): Add README.md" can be used.
- [x] Related file selection: Only relevant files should be touched and
no other files should be affected.
- [ ] I ran `npx turbo run` at the root of the repository, and build was
successful.
- [x] I installed the npm packages using `npm install --save-exact
<package>` so my package is pinned to a specific npm version. Leave
empty if no package was installed. Leave empty if no package was
installed with this PR.

### How were these changes tested?

Please describe the tests you did to test the changes you made. Please
also specify your test configuration.
  • Loading branch information
cansirin authored Jul 23, 2023
1 parent 234b104 commit d63c9ee
Show file tree
Hide file tree
Showing 13 changed files with 271 additions and 16 deletions.
15 changes: 15 additions & 0 deletions apps/ui/.storybook/preview.ts → apps/ui/.storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import "@kampus/kampus/app/globals.css";
import "../styles.css";

import React from "react";
import type { StoryContext, StoryFn } from "@storybook/react";

import { ThemeProvider } from "@kampus/ui-next/components/theme-provider";

export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
Expand All @@ -27,3 +32,13 @@ export const parameters = {
],
},
};

const withThemeProvider = (StoryFn: StoryFn) => {
return (
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
<StoryFn />
</ThemeProvider>
);
};

export const decorators = [withThemeProvider];
17 changes: 8 additions & 9 deletions apps/ui/stories/Avatar.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import type { Meta, StoryObj } from "@storybook/react";

import { Avatar, AvatarFallback, AvatarImage } from "@kampus/ui-next/components/avatar";
import { UserAvatar } from "@kampus/ui-next/components/user-avatar";

const meta = {
component: Avatar,
} satisfies Meta<typeof Avatar>;
component: UserAvatar,
} satisfies Meta<typeof UserAvatar>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Default = {
render: () => (
<Avatar>
<AvatarImage src="https://github.com/kamp-us.png" alt="@kampus" />
<AvatarFallback>K</AvatarFallback>
</Avatar>
),
render: ({ login, src }) => <UserAvatar login={login} src={src} />,
args: {
login: "kampus",
src: "https://github.com/kamp-us.png",
},
} satisfies Story;
28 changes: 28 additions & 0 deletions apps/ui/stories/SocialMediaButtons.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { Meta, StoryObj } from "@storybook/react";

import {
FacebookShare,
RedditShare,
TwitterShare,
} from "@kampus/ui-next/components/social-media-buttons";

const meta = {
component: TwitterShare || FacebookShare || RedditShare,
} satisfies Meta<typeof TwitterShare>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Default = {
args: {
postUrl: "https://kampus.rs",
size: 32,
},
render: ({ postUrl, size }) => (
<div style={{ display: "flex", gap: "10px" }}>
<FacebookShare postUrl={postUrl} size={size} />
<TwitterShare postUrl={postUrl} size={size} />
<RedditShare postUrl={postUrl} size={size} />
</div>
),
} satisfies Story;
12 changes: 12 additions & 0 deletions apps/ui/stories/ThemeToggle.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { Meta, StoryObj } from "@storybook/react";

import { ThemeToggle } from "@kampus/ui-next/components/theme-toggle";

const meta = {
component: ThemeToggle,
} satisfies Meta<typeof ThemeToggle>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Default = {} satisfies Story;
15 changes: 15 additions & 0 deletions apps/ui/stories/TimeAgo.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { Meta, StoryObj } from "@storybook/react";

import { TimeAgo } from "@kampus/ui-next/components/time-ago";

const meta = {
component: TimeAgo,
args: {
date: new Date(),
},
} satisfies Meta<typeof TimeAgo>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Default = {} satisfies Story;
56 changes: 55 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions packages/ui/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,8 @@ export * from "./input-with-button";
export * from "./input";
export * from "./button";
export * from "./top-nav";
export * from "./theme-provider";
export * from "./theme-toggle";
export * from "./user-avatar";
export * from "./time-ago";
export * from "./social-media-buttons";
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use client";

import {
FacebookIcon,
FacebookShareButton,
Expand All @@ -7,28 +9,31 @@ import {
TwitterShareButton,
} from "react-share";

type SocialMediaButtonProps = {
interface SocialMediaButtonProps {
postUrl: string;
size: number;
};
}

export const FacebookShare = ({ postUrl, size }: SocialMediaButtonProps) => {
export const FacebookShare = (props: SocialMediaButtonProps) => {
const { postUrl, size } = props;
return (
<FacebookShareButton url={postUrl}>
<FacebookIcon round size={size} />
</FacebookShareButton>
);
};

export const TwitterShare = ({ postUrl, size }: SocialMediaButtonProps) => {
export const TwitterShare = (props: SocialMediaButtonProps) => {
const { postUrl, size } = props;
return (
<TwitterShareButton url={postUrl}>
<TwitterIcon round size={size} />
</TwitterShareButton>
);
};

export const RedditShare = ({ postUrl, size }: SocialMediaButtonProps) => {
export const RedditShare = (props: SocialMediaButtonProps) => {
const { postUrl, size } = props;
return (
<RedditShareButton url={postUrl}>
<RedditIcon round size={size} />
Expand Down
9 changes: 9 additions & 0 deletions packages/ui/components/theme-provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"use client";

import * as React from "react";
import { ThemeProvider as NextThemesProvider } from "next-themes";
import { type ThemeProviderProps } from "next-themes/dist/types";

export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
}
38 changes: 38 additions & 0 deletions packages/ui/components/theme-toggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"use client";

import * as React from "react";
import { Moon, Sun } from "lucide-react";
import { useTheme } from "next-themes";

import { cn } from "../utils";
import { Button } from "./button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "./dropdown-menu";

export function ThemeToggle() {
const { setTheme } = useTheme();

const baseClasses = "h-[1.2rem] w-[1.2rem] transition all";

return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="sm">
<Sun className={cn(baseClasses, "rotate-0 scale-100 dark:-rotate-90 dark:scale-0")} />
<Moon
className={cn(baseClasses, "absolute rotate-90 scale-0 dark:rotate-0 dark:scale-100")}
/>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => setTheme("light")}>Light</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("dark")}>Dark</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("system")}>System</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}
50 changes: 50 additions & 0 deletions packages/ui/components/time-ago.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"use client";

import { useEffect, useState } from "react";

const formatter = new Intl.RelativeTimeFormat("tr", {
// numeric: "auto",
});

const DIVISIONS = [
{ amount: 60, name: "seconds" },
{ amount: 60, name: "minutes" },
{ amount: 24, name: "hours" },
{ amount: 7, name: "days" },
{ amount: 4.34524, name: "weeks" },
{ amount: 12, name: "months" },
{ amount: Number.POSITIVE_INFINITY, name: "years" },
];

export function formatTimeAgo(date: Date) {
const now = new Date();
let duration = (date.getTime() - now.getTime()) / 1000;

for (let i = 0; i <= DIVISIONS.length; i++) {
const division = DIVISIONS[i] as (typeof DIVISIONS)[number];
if (Math.abs(duration) < division.amount) {
return formatter.format(Math.round(duration), division.name as Intl.RelativeTimeFormatUnit);
}
duration /= division.amount;
}
}

interface TimeagoProps {
date: Date;
}

export const TimeAgo = (props: TimeagoProps) => {
const [timeAgo, setTimeAgo] = useState(formatTimeAgo(props.date));

useEffect(() => {
const timer = setInterval(() => {
setTimeAgo(formatTimeAgo(props.date));
}, 10000);

return () => clearInterval(timer);
}, [props.date]);

// If we don't suppress the hydration warning, we get this error: https://nextjs.org/docs/messages/react-hydration-error
// https://nextjs.org/docs/messages/react-hydration-error#solution-3-using-suppresshydrationwarning
return <div suppressHydrationWarning>{timeAgo}</div>;
};
23 changes: 23 additions & 0 deletions packages/ui/components/user-avatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"use client";

import React, { forwardRef } from "react";

import { Avatar, AvatarFallback, AvatarImage } from "./avatar";

export interface UserAvatarProps {
login: string;
src?: string;
}

export const UserAvatar = forwardRef<HTMLSpanElement, UserAvatarProps>(({ login, src }, ref) => {
const firstLetter = login.charAt(0).toUpperCase();

return (
<Avatar>
<AvatarImage src={src} alt={login} />
<AvatarFallback ref={ref}>{firstLetter}</AvatarFallback>
</Avatar>
);
});

UserAvatar.displayName = "UserAvatar";
4 changes: 3 additions & 1 deletion packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-toast": "^1.1.4",
"class-variance-authority": "0.6.0",
"lucide-react": "0.241.0"
"lucide-react": "0.241.0",
"next-themes": "0.2.1",
"react-share": "4.4.1"
}
}

0 comments on commit d63c9ee

Please sign in to comment.