Skip to content

Commit b0181f4

Browse files
authored
Initial commit
0 parents  commit b0181f4

40 files changed

+9864
-0
lines changed

.eslintrc.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": ["next/core-web-vitals", "prettier"]
3+
}

.gitignore

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
.yarn/install-state.gz
8+
9+
# testing
10+
/coverage
11+
12+
# next.js
13+
/.next/
14+
/out/
15+
16+
# production
17+
/build
18+
19+
# misc
20+
.DS_Store
21+
*.pem
22+
23+
# debug
24+
npm-debug.log*
25+
yarn-debug.log*
26+
yarn-error.log*
27+
28+
# local env files
29+
.env*.local
30+
.env
31+
# vercel
32+
.vercel
33+
34+
# typescript
35+
*.tsbuildinfo
36+
next-env.d.ts

.prettierrc.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"plugins": [
3+
"prettier-plugin-organize-imports",
4+
"prettier-plugin-tailwindcss"
5+
],
6+
"tailwindFunctions": ["classNames"],
7+
"singleQuote": true,
8+
"trailingComma": "es5"
9+
}

.vscode/extensions.json

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"recommendations": [
3+
// Linting / Formatting
4+
"esbenp.prettier-vscode",
5+
"dbaeumer.vscode-eslint",
6+
"bradlc.vscode-tailwindcss"
7+
]
8+
}

.vscode/settings.json

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
// Formatting using Prettier by default for all languages
3+
"editor.defaultFormatter": "esbenp.prettier-vscode",
4+
"editor.formatOnSave": true,
5+
"editor.codeActionsOnSave": {
6+
"source.fixAll.eslint": "explicit"
7+
},
8+
// Formatting using Prettier for JavaScript, overrides VSCode default.
9+
"[javascript]": {
10+
"editor.defaultFormatter": "esbenp.prettier-vscode"
11+
},
12+
// Linting using ESLint.
13+
"eslint.validate": [
14+
"javascript",
15+
"javascriptreact",
16+
"typescript",
17+
"typescriptreact"
18+
],
19+
20+
// Enable file nesting.
21+
"explorer.fileNesting.enabled": true,
22+
"explorer.fileNesting.patterns": {
23+
"*.ts": "$(capture).test.ts, $(capture).test.tsx",
24+
"*.tsx": "$(capture).test.ts, $(capture).test.tsx"
25+
}
26+
}

README.md

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2+
3+
## Getting Started
4+
5+
First, run the development server:
6+
7+
```bash
8+
npm run dev
9+
# or
10+
yarn dev
11+
# or
12+
pnpm dev
13+
# or
14+
bun dev
15+
```
16+
17+
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18+
19+
You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
20+
21+
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
22+
23+
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
24+
25+
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
26+
27+
## Learn More
28+
29+
To learn more about Next.js, take a look at the following resources:
30+
31+
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
32+
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
33+
34+
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
35+
36+
## Deploy on Vercel
37+
38+
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
39+
40+
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.

components/Button.tsx

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
interface Props extends React.HTMLAttributes<HTMLButtonElement> {
2+
loading?: boolean;
3+
}
4+
5+
// const IS_BROWSER = typeof window !== 'undefined' && typeof document !== 'undefined'
6+
7+
export function Button(props: Props) {
8+
return (
9+
<button
10+
{...props}
11+
disabled={props.loading}
12+
type="button"
13+
className={`hover:(shadow-lg) focus:(shadow-lg outline-none) disabled:(opacity-50 cursor-not-allowed) mt-4 flex items-center gap-2 rounded-md bg-yellow-300 px-4 py-2 text-lg text-white shadow-md duration-300 ${
14+
props.className ?? ''
15+
}`}
16+
>
17+
{props.loading ? 'Loading...' : props.children}
18+
</button>
19+
);
20+
}

components/Dropdown.tsx

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { useEffect, useRef, useState } from 'react';
2+
3+
interface MenuItem {
4+
label: string;
5+
value: string;
6+
icon?: JSX.Element;
7+
}
8+
9+
interface DropdownButtonProps {
10+
icon?: JSX.Element;
11+
items: MenuItem[];
12+
onSelect: (item: MenuItem) => void;
13+
className?: string;
14+
style?: Record<string, string>;
15+
children?: JSX.Element | JSX.Element[];
16+
}
17+
18+
export default function Dropdown({
19+
icon,
20+
items,
21+
onSelect,
22+
className = '',
23+
style = {},
24+
}: DropdownButtonProps) {
25+
const [isOpen, setIsOpen] = useState(false);
26+
const [selectedItem, setSelectedItem] = useState<MenuItem | null>(null);
27+
const ref = useRef<HTMLDivElement>(null);
28+
29+
function handleItemClick(item: MenuItem) {
30+
setSelectedItem(item);
31+
onSelect(item);
32+
setIsOpen(false);
33+
}
34+
35+
function handleClickOutside(event: MouseEvent) {
36+
if (ref.current && !ref.current.contains(event.target as Node)) {
37+
setIsOpen(false);
38+
}
39+
}
40+
41+
useEffect(() => {
42+
document.addEventListener('mousedown', handleClickOutside);
43+
return () => {
44+
document.removeEventListener('mousedown', handleClickOutside);
45+
};
46+
}, []);
47+
48+
return (
49+
<div
50+
className={`inline-block transition ${className}`}
51+
style={style}
52+
onClick={() => setIsOpen(!isOpen)}
53+
ref={ref}
54+
>
55+
{icon}
56+
{isOpen && (
57+
<div className="absolute right-0 mt-2 w-32 origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5">
58+
<ul className="flex w-full flex-col py-1">
59+
{items.map((item) => (
60+
<div
61+
key={item.label}
62+
className="hover:(bg-gray-100 cursor-pointer) inline-flex w-full items-center gap-2 px-2 py-2 text-center text-sm text-gray-700 text-gray-900"
63+
onClick={() => handleItemClick(item)}
64+
>
65+
{item.icon && item.icon}
66+
<span>{item.label}</span>
67+
</div>
68+
))}
69+
</ul>
70+
</div>
71+
)}
72+
</div>
73+
);
74+
}

components/Footer.tsx

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { IconBrandGithub } from '@tabler/icons-react';
2+
3+
export default function Footer() {
4+
const menus = [
5+
{
6+
title: 'Documentation',
7+
children: [
8+
{ name: 'Getting Started', href: '#' },
9+
{ name: 'Guide', href: '#' },
10+
{ name: 'API', href: '#' },
11+
{ name: 'Showcase', href: '#' },
12+
{ name: 'Pricing', href: '#' },
13+
],
14+
},
15+
{
16+
title: 'Community',
17+
children: [
18+
{ name: 'Forum', href: '#' },
19+
{ name: 'Discord', href: '#' },
20+
],
21+
},
22+
];
23+
24+
return (
25+
<div className="flex w-full max-w-screen-lg flex-col gap-8 bg-white px-8 py-8 text-sm md:flex-row md:gap-16">
26+
<div className="flex-1">
27+
<a href="https://github.com/gofenix" target="_blank">
28+
<img width="200" height="32" src="logo.png" alt="Made by Fenix" />
29+
</a>
30+
<a href="https://github.com/leeduckgo" target="_blank">
31+
<img width="200" height="32" src="logo_ldg.png" alt="Made by LeeDuckGo" />
32+
</a>
33+
</div>
34+
35+
{menus.map((item) => (
36+
<div className="mb-4" key={item.title}>
37+
<div className="font-bold">{item.title}</div>
38+
<ul className="mt-2">
39+
{item.children.map((child) => (
40+
<li className="mt-2" key={child.name}>
41+
<a
42+
className="text-gray-500 hover:text-gray-700"
43+
href={child.href}
44+
>
45+
{child.name}
46+
</a>
47+
</li>
48+
))}
49+
</ul>
50+
</div>
51+
))}
52+
53+
<div className="space-y-2 text-gray-500">
54+
<div className="text-xs">
55+
Copyright © 2024 gofenix&leeduckgo
56+
<br />
57+
All right reserved.
58+
</div>
59+
60+
<a
61+
href="https://github.com/NonceGeek/bodhi-rss"
62+
className="inline-block hover:text-black"
63+
aria-label="GitHub"
64+
>
65+
<IconBrandGithub aria-hidden="true" />
66+
</a>
67+
</div>
68+
</div>
69+
);
70+
}

components/Header.tsx

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import {
2+
IconBrandGithub,
3+
IconBrandTwitter,
4+
IconLink,
5+
} from '@tabler/icons-react';
6+
import Link from 'next/link';
7+
8+
export default function Header() {
9+
return (
10+
<div className="flex w-full max-w-screen-lg flex-col items-center gap-4 bg-white py-6 md:flex-row">
11+
<div className="flex flex-1 items-center">
12+
<IconLink color="#ffdb1e" />
13+
<div className="ml-1 text-xl font-bold">Bodhi RSS</div>
14+
</div>
15+
<div className="w-full">
16+
<nav className="flex justify-center space-x-4">
17+
<Link href="/" className="hover:text-cyan-600">RSS Generator</Link>
18+
<Link href="https://github.com/NonceGeek/bodhi-rss/discussions/categories/awesome-rss" className="hover:text-cyan-600">Awesome RSS Feeds</Link>
19+
<Link href="/link-maker" className="hover:text-cyan-600">Link Maker</Link>
20+
</nav>
21+
</div>
22+
<div className="flex items-center">
23+
<a
24+
href="https://bodhi.wtf/space/5/15370?action=buy"
25+
target="_blank"
26+
className="rounded border hover:bg-cyan-200"
27+
>
28+
<img width="180" height="18" src="donation.png" alt="Made by Fenix" />
29+
</a>
30+
{/* <a href="https://bodhi.wtf/13714" target="_blank">
31+
<img width="180" height="18" src="logo.png" alt="Made by Fenix" />
32+
</a> */}
33+
<a
34+
href={'https://github.com/NonceGeek/bodhi-rss'}
35+
target="_blank"
36+
className="p-1"
37+
>
38+
<IconBrandGithub />
39+
</a>
40+
<a href={'https://twitter.com/0xleeduckgo'} target="_blank" className="p-1">
41+
<IconBrandTwitter />
42+
</a>
43+
</div>
44+
</div>
45+
);
46+
}

components/Input.tsx

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export default function Input(props: React.HTMLAttributes<HTMLInputElement>) {
2+
return (
3+
<input
4+
{...props}
5+
className={`focus:(outline-none border-yellow-400) disabled:(opacity-50 cursor-not-allowed) mt-4 w-full rounded-md border-2 border-yellow-300 p-2 text-center text-lg duration-300 ${
6+
props.className ?? ''
7+
}`}
8+
/>
9+
);
10+
}

0 commit comments

Comments
 (0)