feat: implement social share buttons
This commit is contained in:
parent
46e009f888
commit
1881032ed2
@ -1,59 +1,70 @@
|
||||
import type { FC } from 'react'
|
||||
import { Link } from 'react-router'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { LinkIcon } from '@heroicons/react/20/solid'
|
||||
import { useState } from 'react'
|
||||
import {
|
||||
FacebookShareButton,
|
||||
LinkedinShareButton,
|
||||
TwitterShareButton,
|
||||
InstapaperShareButton,
|
||||
} from 'react-share'
|
||||
|
||||
import { FacebookIcon } from '~/components/icons/facebook'
|
||||
import { InstagramIcon } from '~/components/icons/instagram'
|
||||
import { LinkIcon } from '~/components/icons/link-icon'
|
||||
import { LinkedinIcon } from '~/components/icons/linkedin'
|
||||
import { XIcon } from '~/components/icons/x'
|
||||
|
||||
type SocialMediaProperties = {
|
||||
className?: string
|
||||
slug?: string
|
||||
type SocialShareButtonsProperties = {
|
||||
url: string
|
||||
title: string
|
||||
}
|
||||
|
||||
const dataSocialMedia = [
|
||||
{
|
||||
type: 'link',
|
||||
url: 'post-id/',
|
||||
icon: LinkIcon,
|
||||
},
|
||||
{
|
||||
type: 'facebook',
|
||||
url: 'https://facebook.com/',
|
||||
icon: FacebookIcon,
|
||||
},
|
||||
{
|
||||
type: 'linkedin',
|
||||
url: 'https://linkedin.com/',
|
||||
icon: LinkedinIcon,
|
||||
},
|
||||
{
|
||||
type: 'x',
|
||||
url: 'https://x.com/',
|
||||
icon: XIcon,
|
||||
},
|
||||
{
|
||||
type: 'instagram',
|
||||
url: 'https://instagram.com/',
|
||||
icon: InstagramIcon,
|
||||
},
|
||||
]
|
||||
export const SocialShareButtons = ({
|
||||
url,
|
||||
title,
|
||||
}: SocialShareButtonsProperties) => {
|
||||
const [showPopup, setShowPopup] = useState(false)
|
||||
|
||||
export const IconsSocial: FC<SocialMediaProperties> = ({ className }) => {
|
||||
const handleCopyLink = () => {
|
||||
navigator.clipboard.writeText(url)
|
||||
setShowPopup(true)
|
||||
setTimeout(() => setShowPopup(false), 2000)
|
||||
}
|
||||
return (
|
||||
<div className={twMerge('flex gap-2', className)}>
|
||||
{dataSocialMedia.map(({ url, icon: Icon }, index) => (
|
||||
<Link
|
||||
key={index}
|
||||
to={url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
<div className="flex items-center space-x-2">
|
||||
{showPopup && (
|
||||
<div className="absolute top-0 rounded-lg border-2 border-gray-400 bg-white p-2 shadow-lg">
|
||||
Link berhasil disalin!
|
||||
</div>
|
||||
)}
|
||||
<button onClick={handleCopyLink}>
|
||||
<LinkIcon className="h-8 w-8 rounded-full bg-gray-400 p-2 sm:h-10 sm:w-10" />
|
||||
</button>
|
||||
<FacebookShareButton
|
||||
url={url}
|
||||
title={title}
|
||||
>
|
||||
<Icon className="h-8 w-8 rounded-full bg-gray-400 p-2 sm:h-10 sm:w-10" />
|
||||
</Link>
|
||||
))}
|
||||
<FacebookIcon className="h-8 w-8 rounded-full bg-gray-400 p-2 sm:h-10 sm:w-10" />
|
||||
</FacebookShareButton>
|
||||
|
||||
<LinkedinShareButton
|
||||
url={url}
|
||||
title={title}
|
||||
>
|
||||
<LinkedinIcon className="h-8 w-8 rounded-full bg-gray-400 p-2 sm:h-10 sm:w-10" />
|
||||
</LinkedinShareButton>
|
||||
|
||||
<TwitterShareButton
|
||||
url={url}
|
||||
title={title}
|
||||
>
|
||||
<XIcon className="h-8 w-8 rounded-full bg-gray-400 p-2 sm:h-10 sm:w-10" />
|
||||
</TwitterShareButton>
|
||||
|
||||
<InstapaperShareButton
|
||||
url={url}
|
||||
title={title}
|
||||
>
|
||||
<InstagramIcon className="h-8 w-8 rounded-full bg-gray-400 p-2 sm:h-10 sm:w-10" />
|
||||
</InstapaperShareButton>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ import { useRouteLoaderData } from 'react-router'
|
||||
import type { TTagResponse } from '~/apis/common/get-tags'
|
||||
import { Card } from '~/components/ui/card'
|
||||
import { CarouselSection } from '~/components/ui/carousel-section'
|
||||
import { IconsSocial } from '~/components/ui/social-share'
|
||||
import { SocialShareButtons } from '~/components/ui/social-share'
|
||||
import { BERITA } from '~/data/contents'
|
||||
import type { loader } from '~/routes/_news.detail.$slug'
|
||||
import { formatDate } from '~/utils/formatter'
|
||||
@ -13,10 +13,10 @@ export const NewsDetailPage = () => {
|
||||
const loaderData = useRouteLoaderData<typeof loader>(
|
||||
'routes/_news.detail.$slug',
|
||||
)
|
||||
|
||||
const currentUrl = globalThis.location
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const { newsDetailData }: any = loaderData
|
||||
const { title, content, featured_image, slug, author, live_at, tags } =
|
||||
const { title, content, featured_image, author, live_at, tags } =
|
||||
newsDetailData
|
||||
|
||||
return (
|
||||
@ -40,7 +40,11 @@ export const NewsDetailPage = () => {
|
||||
<p className="text-sm">{formatDate(live_at)} . 5 min read </p>
|
||||
</div>
|
||||
</div>
|
||||
<IconsSocial className="flex-row" />
|
||||
{/* <IconsSocial className="flex-row" /> */}
|
||||
<SocialShareButtons
|
||||
url={`${currentUrl}`}
|
||||
title={title}
|
||||
/>
|
||||
</div>
|
||||
{/* end next planing create component for this section */}
|
||||
<div className="w-full bg-amber-200">
|
||||
@ -59,9 +63,10 @@ export const NewsDetailPage = () => {
|
||||
<div className="items-end justify-between border-b-gray-300 py-4 sm:flex">
|
||||
<div className="flex flex-col max-sm:mb-3">
|
||||
<p className="mb-2">Share this post</p>
|
||||
<IconsSocial
|
||||
className="a"
|
||||
slug={slug}
|
||||
|
||||
<SocialShareButtons
|
||||
url={`${currentUrl}`}
|
||||
title={title}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-end gap-2">
|
||||
|
||||
@ -43,6 +43,7 @@
|
||||
"react-dom": "^19.0.0",
|
||||
"react-hook-form": "^7.54.2",
|
||||
"react-router": "^7.1.3",
|
||||
"react-share": "^5.2.2",
|
||||
"remix-hook-form": "^6.1.3",
|
||||
"tailwind-merge": "^3.0.1",
|
||||
"xior": "^0.6.3",
|
||||
|
||||
25
pnpm-lock.yaml
generated
25
pnpm-lock.yaml
generated
@ -95,6 +95,9 @@ importers:
|
||||
react-router:
|
||||
specifier: ^7.1.3
|
||||
version: 7.1.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
react-share:
|
||||
specifier: ^5.2.2
|
||||
version: 5.2.2(react@19.0.0)
|
||||
remix-hook-form:
|
||||
specifier: ^6.1.3
|
||||
version: 6.1.3(react-dom@19.0.0(react@19.0.0))(react-hook-form@7.54.2(react@19.0.0))(react-router@7.1.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)
|
||||
@ -3130,6 +3133,9 @@ packages:
|
||||
jsonfile@6.1.0:
|
||||
resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
|
||||
|
||||
jsonp@0.2.1:
|
||||
resolution: {integrity: sha512-pfog5gdDxPdV4eP7Kg87M8/bHgshlZ5pybl+yKxAnCZ5O7lCIn7Ixydj03wOlnDQesky2BPyA91SQ+5Y/mNwzw==}
|
||||
|
||||
jsonparse@1.3.1:
|
||||
resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==}
|
||||
engines: {'0': node >= 0.2.0}
|
||||
@ -3945,6 +3951,11 @@ packages:
|
||||
react-dom:
|
||||
optional: true
|
||||
|
||||
react-share@5.2.2:
|
||||
resolution: {integrity: sha512-z0nbOX6X6vHHWAvXduNkYeJUKTKNpKM5Xpmc5a2BxjJhUWl+sE7AsSEMmYEUj2DuDjZr5m7KFIGF0sQPKcUN6w==}
|
||||
peerDependencies:
|
||||
react: ^17 || ^18 || ^19
|
||||
|
||||
react-simple-animate@3.5.3:
|
||||
resolution: {integrity: sha512-Ob+SmB5J1tXDEZyOe2Hf950K4M8VaWBBmQ3cS2BUnTORqHjhK0iKG8fB+bo47ZL15t8d3g/Y0roiqH05UBjG7A==}
|
||||
peerDependencies:
|
||||
@ -7894,6 +7905,12 @@ snapshots:
|
||||
optionalDependencies:
|
||||
graceful-fs: 4.2.11
|
||||
|
||||
jsonp@0.2.1:
|
||||
dependencies:
|
||||
debug: 2.6.9
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
jsonparse@1.3.1: {}
|
||||
|
||||
jsx-ast-utils@3.3.5:
|
||||
@ -8683,6 +8700,14 @@ snapshots:
|
||||
optionalDependencies:
|
||||
react-dom: 19.0.0(react@19.0.0)
|
||||
|
||||
react-share@5.2.2(react@19.0.0):
|
||||
dependencies:
|
||||
classnames: 2.5.1
|
||||
jsonp: 0.2.1
|
||||
react: 19.0.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
react-simple-animate@3.5.3(react-dom@19.0.0(react@19.0.0)):
|
||||
dependencies:
|
||||
react-dom: 19.0.0(react@19.0.0)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user