├── .eslintrc.cjs
├── .gitignore
├── README.md
├── index.html
├── package.json
├── postcss.config.js
├── public
└── vite.svg
├── src
├── App.jsx
├── components
│ ├── button-groups
│ │ └── index.jsx
│ └── button
│ │ └── index.jsx
├── hooks
│ └── use-media.jsx
├── index.css
└── main.jsx
├── tailwind.config.js
└── vite.config.js
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: { browser: true, es2020: true },
4 | extends: [
5 | 'eslint:recommended',
6 | 'plugin:react/recommended',
7 | 'plugin:react/jsx-runtime',
8 | 'plugin:react-hooks/recommended',
9 | ],
10 | ignorePatterns: ['dist', '.eslintrc.cjs'],
11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
12 | settings: { react: { version: '18.2' } },
13 | plugins: ['react-refresh'],
14 | rules: {
15 | 'react-refresh/only-export-components': [
16 | 'warn',
17 | { allowConstantExport: true },
18 | ],
19 | },
20 | }
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 | bun.lockb
10 |
11 | node_modules
12 | dist
13 | dist-ssr
14 | *.local
15 |
16 | # Editor directories and files
17 | .vscode/*
18 | !.vscode/extensions.json
19 | .idea
20 | .DS_Store
21 | *.suo
22 | *.ntvs*
23 | *.njsproj
24 | *.sln
25 | *.sw?
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Responsive Button
2 |
3 | Youtube kanalimda birlikte hazirladigimiz responsive button ornegimiz, genelde admin panellerinde yan yana olan butonlarin mobilde nasil yonetilecegine dair bir yaklasim.
4 |
5 | Videoyu izlemek icin:
6 | https://www.youtube.com/watch?v=qNkaHPG37F8
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-responsive-button-groups",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "classnames": "^2.5.1",
14 | "react": "^18.2.0",
15 | "react-dom": "^18.2.0",
16 | "react-spring-bottom-sheet": "^3.4.1"
17 | },
18 | "devDependencies": {
19 | "@types/react": "^18.2.43",
20 | "@types/react-dom": "^18.2.17",
21 | "@vitejs/plugin-react": "^4.2.1",
22 | "autoprefixer": "^10.4.17",
23 | "eslint": "^8.55.0",
24 | "eslint-plugin-react": "^7.33.2",
25 | "eslint-plugin-react-hooks": "^4.6.0",
26 | "eslint-plugin-react-refresh": "^0.4.5",
27 | "postcss": "^8.4.34",
28 | "tailwindcss": "^3.4.1",
29 | "vite": "^5.0.8"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/App.jsx:
--------------------------------------------------------------------------------
1 | import ButtonGroups from "./components/button-groups/index.jsx";
2 |
3 | function App() {
4 |
5 | return (
6 |
7 |
8 |
9 | Hoşgeldin
10 |
39 |
40 |
41 | {/**/}
42 | {/**/}
43 | {/**/}
44 | {/**/}
45 | {/**/}
46 |
47 | )
48 | }
49 |
50 | export default App
51 |
--------------------------------------------------------------------------------
/src/components/button-groups/index.jsx:
--------------------------------------------------------------------------------
1 | import {useState} from "react";
2 | import Button from "../button/index.jsx";
3 | import useMedia from "../../hooks/use-media.jsx";
4 | import {BottomSheet} from 'react-spring-bottom-sheet'
5 | import 'react-spring-bottom-sheet/dist/style.css'
6 |
7 | export default function ButtonGroups({buttons, autoClose = false}) {
8 |
9 | const [open, setOpen] = useState(false)
10 | const isMobile = useMedia(500)
11 |
12 | const prepareButtons = (
13 | buttons.map((button, index) => {
14 |
15 | const attributes = {...button.attributes}
16 |
17 | if (autoClose) {
18 | attributes.onClick = (e) => {
19 | setOpen(false)
20 | button?.attributes?.onClick?.(e)
21 | }
22 | }
23 |
24 | return (
25 |
28 | )
29 | })
30 | )
31 |
32 | if (isMobile) {
33 | return (
34 | <>
35 |
46 | setOpen(false)}
48 | open={open}
49 | >
50 |
53 |
54 | >
55 | )
56 | }
57 |
58 | return (
59 |
62 | )
63 | }
--------------------------------------------------------------------------------
/src/components/button/index.jsx:
--------------------------------------------------------------------------------
1 | import classNames from "classnames";
2 |
3 | export default function Button({ children, size = 'medium', variant = 'default', ...props }) {
4 | return (
5 |
22 | )
23 | }
--------------------------------------------------------------------------------
/src/hooks/use-media.jsx:
--------------------------------------------------------------------------------
1 | import {useEffect, useState} from "react";
2 |
3 | export default function useMedia(width = 1024 ) {
4 |
5 | const [status, setStatus] = useState(window.matchMedia(`(max-width: ${width}px)`).matches)
6 |
7 | useEffect(() => {
8 | const matchMedia = window.matchMedia(`(max-width: ${width}px)`)
9 |
10 | const onChangeHandle = e => {
11 | setStatus(e.matches)
12 | }
13 |
14 | matchMedia.addEventListener('change', onChangeHandle)
15 |
16 | // cleanup
17 | return () => {
18 | matchMedia.removeEventListener('changge', onChangeHandle)
19 | }
20 |
21 | })
22 |
23 | return status
24 |
25 | }
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
--------------------------------------------------------------------------------
/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.jsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')).render(
7 |
8 | )
9 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | export default {
3 | content: [
4 | "./index.html",
5 | "./src/**/*.{js,ts,jsx,tsx}",
6 | ],
7 | theme: {
8 | extend: {},
9 | },
10 | plugins: [],
11 | }
12 |
13 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------