refactor/feat: ipod nano 7g-style now playing component, discontinue fontawesome, content updates
- redesign NowPlaying widget with iPod Nano 7th generation-inspired UI - content updates (home, about) - general code cleanup - bump deps, clean up - update README for self hosting - remove old workflows
This commit is contained in:
parent
db86ce3277
commit
d613b58dd6
17 changed files with 466 additions and 460 deletions
|
@ -1,24 +0,0 @@
|
|||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faArrowLeft } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
interface BackButtonProps {
|
||||
href: string;
|
||||
label?: string;
|
||||
}
|
||||
|
||||
const BackButton: React.FC<BackButtonProps> = ({ href, label = 'Back' }) => {
|
||||
return (
|
||||
<Link
|
||||
href={href}
|
||||
className="inline-flex items-center px-4 py-2 mt-4 text-white bg-gray-800 rounded-sm shadow-md transition-all duration-300 ease-in-out hover:bg-gray-700 hover:shadow-lg hover:-translate-y-0.5 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-gray-500"
|
||||
aria-label={`Go back to ${label}`}
|
||||
>
|
||||
<FontAwesomeIcon icon={faArrowLeft} className="mr-2" />
|
||||
{label}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
export default BackButton;
|
|
@ -1,30 +1,40 @@
|
|||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
interface ButtonProps {
|
||||
href: string
|
||||
label: string
|
||||
icon?: React.ElementType
|
||||
target?: string
|
||||
variant?: "primary" | "rounded"
|
||||
className?: string
|
||||
icon?: React.ReactNode
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
const Button: React.FC<ButtonProps> = ({ href, label, icon, target, className }) => {
|
||||
return (
|
||||
<Link
|
||||
href={href}
|
||||
className={className ? (
|
||||
cn("inline-flex items-center bg-gray-800 text-white font-bold py-2 px-4 rounded-sm shadow-md transition-all duration-300 ease-in-out hover:bg-gray-700 hover:shadow-lg hover:-translate-y-0.5 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-gray-500", className)
|
||||
) : (
|
||||
"inline-flex items-center bg-gray-800 text-white font-bold py-2 px-4 rounded-sm shadow-md transition-all duration-300 ease-in-out hover:bg-gray-700 hover:shadow-lg hover:-translate-y-0.5 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-gray-500"
|
||||
)}
|
||||
target={target}
|
||||
>
|
||||
{icon && React.createElement(icon, { size: 20, className: "mr-2" })}
|
||||
{label}
|
||||
</Link>
|
||||
)
|
||||
const Button: React.FC<ButtonProps> = ({ href, target, variant, className, icon, children }) => {
|
||||
if (!variant || variant === "primary") {
|
||||
return (
|
||||
<Link
|
||||
href={href}
|
||||
className={`inline-flex items-center bg-gray-800 text-white font-bold py-2 px-4 rounded-sm shadow-md transition-all duration-300 ease-in-out hover:bg-gray-700 hover:shadow-lg hover:-translate-y-0.5 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 gap-2 ${className}`}
|
||||
target={target}
|
||||
>
|
||||
{icon}
|
||||
{children}
|
||||
</Link>
|
||||
)
|
||||
} else if (variant === "rounded") {
|
||||
return (
|
||||
<Link
|
||||
href={href}
|
||||
target={target}
|
||||
rel={target === "_blank" ? "noopener noreferrer" : undefined}
|
||||
className={`bg-gray-700 text-white px-4 py-2 rounded-full hover:bg-gray-600 transition-colors inline-flex items-center justify-center gap-2 whitespace-nowrap ${className}`}
|
||||
>
|
||||
{icon}
|
||||
{children}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Button
|
|
@ -1,26 +0,0 @@
|
|||
import { IconDefinition } from '@fortawesome/fontawesome-svg-core'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import Link from 'next/link';
|
||||
|
||||
interface ContactButtonProps {
|
||||
href: string;
|
||||
icon: IconDefinition;
|
||||
label: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function ContactButton({ href, icon, label, className }: ContactButtonProps) {
|
||||
return (
|
||||
<Link
|
||||
href={href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={`bg-gray-700 text-white px-4 py-2 rounded-full hover:bg-gray-600 transition-colors inline-flex items-center ${className}`}
|
||||
>
|
||||
<FontAwesomeIcon icon={icon} className="text-xl mr-2" />
|
||||
{label}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
export default ContactButton;
|
37
components/objects/MusicText.tsx
Normal file
37
components/objects/MusicText.tsx
Normal file
|
@ -0,0 +1,37 @@
|
|||
"use client"
|
||||
|
||||
import type React from "react"
|
||||
|
||||
interface ScrollTxtProps {
|
||||
text: string
|
||||
className?: string
|
||||
type?: 'artist' | 'track' | 'release'
|
||||
}
|
||||
|
||||
const ScrollTxt: React.FC<ScrollTxtProps> = ({ text, className = "", type }) => {
|
||||
const getTypeClass = (type?: string) => {
|
||||
switch(type) {
|
||||
case 'artist':
|
||||
return 'text-white text-xs opacity-90 font-medium text-[8px]'
|
||||
case 'track':
|
||||
return 'text-white text-xs font-bold'
|
||||
case 'release':
|
||||
return 'text-white text-xs opacity-90 font-medium text-[8px]'
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
const textClass = getTypeClass(type)
|
||||
|
||||
return (
|
||||
<div className={`overflow-hidden ${className}`}>
|
||||
<div className="whitespace-nowrap inline-block">
|
||||
<span className={textClass}>{text}</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ScrollTxt
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
"use client"
|
||||
|
||||
import type React from "react"
|
||||
import { useEffect, useRef, useState } from "react"
|
||||
|
||||
interface ScrollTxtProps {
|
||||
text: string
|
||||
className?: string
|
||||
}
|
||||
|
||||
const ScrollTxt: React.FC<ScrollTxtProps> = ({ text, className = "" }) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
const textRef = useRef<HTMLDivElement>(null)
|
||||
const [shouldScroll, setShouldScroll] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (containerRef.current && textRef.current) {
|
||||
const containerWidth = containerRef.current.offsetWidth
|
||||
const textWidth = textRef.current.offsetWidth
|
||||
setShouldScroll(textWidth > containerWidth)
|
||||
}
|
||||
}, []) // Updated dependency array
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className={`overflow-hidden ${className}`}>
|
||||
<div
|
||||
ref={textRef}
|
||||
className={`whitespace-nowrap inline-block ${shouldScroll ? "animate-marquee hover:pause" : ""}`}
|
||||
>
|
||||
{shouldScroll ? (
|
||||
<>
|
||||
<span>{text}</span>
|
||||
<span className="mx-4">•</span>
|
||||
<span>{text}</span>
|
||||
<span className="mx-4">•</span>
|
||||
<span>{text}</span>
|
||||
</>
|
||||
) : (
|
||||
text
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ScrollTxt
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue