├── .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 | --------------------------------------------------------------------------------