Skip to content
This repository has been archived by the owner on Mar 30, 2024. It is now read-only.

Commit

Permalink
Merge pull request #23 from paveg/share-button
Browse files Browse the repository at this point in the history
Add share button
  • Loading branch information
paveg authored Mar 22, 2024
2 parents 55427bd + 0f0be01 commit 6f15bd9
Show file tree
Hide file tree
Showing 10 changed files with 958 additions and 33 deletions.
19 changes: 16 additions & 3 deletions app/posts/[...slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { TableOfContents } from '@/components/table-of-contents';
import { cfg } from '@/utils/constants';
import { CategoryBadge } from '@/components/category-badge';
import { CustomImage } from '@/components/custom-image';
import { ShareButtons } from '@/components/share-buttons';
import { Separator } from '@/components/ui/separator';

interface PostProps {
params: {
Expand Down Expand Up @@ -136,7 +138,7 @@ export default async function PostPage({ params }: PostProps) {
</span>
)}
</div>
<div className="flex flex-row gap-2 text-sm">
<div className="flex flex-row items-center gap-2 text-sm">
<div className="flex-1">
<span className="text-slate-700 dark:text-slate-200">
<CategoryBadge badgeString={post.category} />
Expand All @@ -150,13 +152,18 @@ export default async function PostPage({ params }: PostProps) {
</span>
</div>
)}
<ShareButtons
title={post.title}
url={fullUrl}
hashtags={post.category ? [post.category] : []}
/>
</div>
<hr className="my-4" />
<Separator className="my-4" />
<div
className="lg:grid lg:grid-cols-8 lg:gap-x-4"
style={{ gridTemplateRows: 'auto 1fr' }}
>
<div className="py-4 lg:col-span-7">
<div className="py-2 lg:col-span-7">
<Mdx code={post.body.code} />
</div>
<aside>
Expand All @@ -165,6 +172,12 @@ export default async function PostPage({ params }: PostProps) {
</div>
</aside>
</div>
<Separator className="mb-2 mt-10" />
<ShareButtons
title={post.title}
url={fullUrl}
hashtags={post.category ? [post.category] : []}
/>
</article>
);
}
37 changes: 15 additions & 22 deletions components/category-badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,28 @@ type Props = {
};

type CategoryTypes =
| 'Technology'
| 'Programming'
| 'Productivity'
| 'Lifestyle'
| 'Gadgets'
| 'Other';
| 'technology'
| 'programming'
| 'productivity'
| 'lifestyle'
| 'gadgets'
| 'other';
const ALL_CATEGORIES = [
'Technology',
'Programming',
'Productivity',
'Lifestyle',
'Gadgets',
'Other',
'technology',
'programming',
'productivity',
'lifestyle',
'gadgets',
'other',
];

function isSuit(value: string) {
return ALL_CATEGORIES.includes(value);
}

const toPascalCase = (str: string) => {
return [str]
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
.join('');
};

export const CategoryBadge = ({ badgeString }: Props) => {
const bs = badgeString ? toPascalCase(badgeString) : 'Other';
if (!isSuit(bs)) {
return <Badge variant="secondary">Other</Badge>;
if (!badgeString || !isSuit(badgeString)) {
return <Badge variant="secondary">other</Badge>;
}
return <Badge variant="secondary">{bs}</Badge>;
return <Badge variant="secondary">{badgeString}</Badge>;
};
40 changes: 40 additions & 0 deletions components/share-buttons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'use client';

import { Share2Icon, TwitterLogoIcon } from '@radix-ui/react-icons';
import { TwitterShareButton } from 'react-share';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from './ui/dropdown-menu';
import { Button } from '@/components/ui/button';

type Props = {
title: string;
url: string;
hashtags: string[];
};
export const ShareButtons = ({ url, title, hashtags }: Props) => {
return (
<div className="flex justify-end gap-2">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon">
<Share2Icon className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>
<TwitterShareButton title={title} url={url} hashtags={hashtags}>
<div className="flex items-center gap-4">
<TwitterLogoIcon className="h-4 w-4" />
<span>Twitter</span>
</div>
</TwitterShareButton>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
);
};
205 changes: 205 additions & 0 deletions components/ui/dropdown-menu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
'use client';

import * as React from 'react';
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
import {
CheckIcon,
ChevronRightIcon,
DotFilledIcon,
} from '@radix-ui/react-icons';

import { cn } from '@/lib/utils';

const DropdownMenu = DropdownMenuPrimitive.Root;

const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;

const DropdownMenuGroup = DropdownMenuPrimitive.Group;

const DropdownMenuPortal = DropdownMenuPrimitive.Portal;

const DropdownMenuSub = DropdownMenuPrimitive.Sub;

const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;

const DropdownMenuSubTrigger = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
inset?: boolean;
}
>(({ className, inset, children, ...props }, ref) => (
<DropdownMenuPrimitive.SubTrigger
ref={ref}
className={cn(
'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent',
inset && 'pl-8',
className
)}
{...props}
>
{children}
<ChevronRightIcon className="ml-auto h-4 w-4" />
</DropdownMenuPrimitive.SubTrigger>
));
DropdownMenuSubTrigger.displayName =
DropdownMenuPrimitive.SubTrigger.displayName;

const DropdownMenuSubContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
>(({ className, ...props }, ref) => (
<DropdownMenuPrimitive.SubContent
ref={ref}
className={cn(
'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
className
)}
{...props}
/>
));
DropdownMenuSubContent.displayName =
DropdownMenuPrimitive.SubContent.displayName;

const DropdownMenuContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md',
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
className
)}
{...props}
/>
</DropdownMenuPrimitive.Portal>
));
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;

const DropdownMenuItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean;
}
>(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Item
ref={ref}
className={cn(
'relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
inset && 'pl-8',
className
)}
{...props}
/>
));
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;

const DropdownMenuCheckboxItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
>(({ className, children, checked, ...props }, ref) => (
<DropdownMenuPrimitive.CheckboxItem
ref={ref}
className={cn(
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
className
)}
checked={checked}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<CheckIcon className="h-4 w-4" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.CheckboxItem>
));
DropdownMenuCheckboxItem.displayName =
DropdownMenuPrimitive.CheckboxItem.displayName;

const DropdownMenuRadioItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
>(({ className, children, ...props }, ref) => (
<DropdownMenuPrimitive.RadioItem
ref={ref}
className={cn(
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
className
)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<DotFilledIcon className="h-4 w-4 fill-current" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.RadioItem>
));
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;

const DropdownMenuLabel = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
inset?: boolean;
}
>(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Label
ref={ref}
className={cn(
'px-2 py-1.5 text-sm font-semibold',
inset && 'pl-8',
className
)}
{...props}
/>
));
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;

const DropdownMenuSeparator = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
>(({ className, ...props }, ref) => (
<DropdownMenuPrimitive.Separator
ref={ref}
className={cn('-mx-1 my-1 h-px bg-muted', className)}
{...props}
/>
));
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;

const DropdownMenuShortcut = ({
className,
...props
}: React.HTMLAttributes<HTMLSpanElement>) => {
return (
<span
className={cn('ml-auto text-xs tracking-widest opacity-60', className)}
{...props}
/>
);
};
DropdownMenuShortcut.displayName = 'DropdownMenuShortcut';

export {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuCheckboxItem,
DropdownMenuRadioItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuGroup,
DropdownMenuPortal,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuRadioGroup,
};
31 changes: 31 additions & 0 deletions components/ui/separator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
'use client';

import * as React from 'react';
import * as SeparatorPrimitive from '@radix-ui/react-separator';

import { cn } from '@/lib/utils';

const Separator = React.forwardRef<
React.ElementRef<typeof SeparatorPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
>(
(
{ className, orientation = 'horizontal', decorative = true, ...props },
ref
) => (
<SeparatorPrimitive.Root
ref={ref}
decorative={decorative}
orientation={orientation}
className={cn(
'shrink-0 bg-border',
orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
className
)}
{...props}
/>
)
);
Separator.displayName = SeparatorPrimitive.Root.displayName;

export { Separator };
2 changes: 1 addition & 1 deletion content/posts/replacement-blog.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Moved to a new blog
description: I replaced my blog with a new one because my usecase isn't matched with Astro.
category: Lifestyle
category: lifestyle
publishedDate: 2024-03-20
heroImage: /blog-post-1.jpg
isPublished: true
Expand Down
2 changes: 1 addition & 1 deletion content/posts/review-monitors-u4025qw-dell.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: 5K2K U4025QW from DELL
publishedDate: 2024-03-22
lastUpdatedDate:
description: 5K2KモニターのU4025QWを購入したので、レビューとインプレッションを記載します
category: Gadgets
category: gadgets
heroImage: https://funailog.imgix.net/u4025qw-1.jpeg?auto=format,compress,enhance
isPublished: true
---
Expand Down
Loading

0 comments on commit 6f15bd9

Please sign in to comment.