├── .eslintignore ├── .eslintrc.cjs ├── .gitignore ├── .husky └── pre-commit ├── .prettierignore ├── .prettierrc ├── README.md ├── app ├── favicon.ico ├── globals.css ├── layout.tsx └── page.tsx ├── components.json ├── components ├── dropdown │ ├── countries.tsx │ └── states.tsx └── ui │ ├── button.tsx │ ├── command.tsx │ ├── dialog.tsx │ ├── popover.tsx │ └── scroll-area.tsx ├── data ├── countries.json └── states.json ├── lib ├── store │ └── dropdown.ts ├── types.ts └── utils.ts ├── next.config.js ├── package.json ├── postcss.config.js ├── public ├── next.svg └── vercel.svg ├── tailwind.config.ts ├── tsconfig.json ├── widgets └── country-state.tsx └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | .next 2 | next-env.d.ts 3 | node_modules 4 | yarn.lock 5 | package-lock.json 6 | public 7 | .vscode 8 | README.md 9 | tailwind.config.js 10 | 11 | ``` -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | es2021: true, 6 | }, 7 | extends: [ 8 | "plugin:react/recommended", 9 | "standard-with-typescript", 10 | "prettier", 11 | "airbnb", 12 | "airbnb-typescript", 13 | "airbnb/hooks", 14 | "plugin:@typescript-eslint/recommended", 15 | "plugin:@typescript-eslint/recommended-requiring-type-checking", 16 | "plugin:prettier/recommended", 17 | "plugin:@next/next/recommended", 18 | "eslint:recommended", 19 | ], 20 | globals: { 21 | Atomics: "readonly", 22 | SharedArrayBuffer: "readonly", 23 | }, 24 | parser: "@typescript-eslint/parser", 25 | overrides: [], 26 | parserOptions: { 27 | ecmaFeatures: { 28 | jsx: true, 29 | }, 30 | ecmaVersion: "latest", 31 | sourceType: "module", 32 | project: "./tsconfig.json", 33 | }, 34 | plugins: ["react", "@typescript-eslint", "prettier"], 35 | rules: { 36 | quotes: "off", 37 | "@typescript-eslint/quotes": [0], 38 | "react/function-component-definition": [2, { namedComponents: "arrow-function" }], 39 | "react/jsx-props-no-spreading": "off", 40 | "@typescript-eslint/explicit-function-return-type": "off", 41 | "@typescript-eslint/no-unsafe-member-access": "off", 42 | "@typescript-eslint/no-unsafe-return": "off", 43 | "@typescript-eslint/no-unsafe-call": "off", 44 | "jsx-a11y/anchor-is-valid": "off", 45 | "@typescript-eslint/no-unsafe-assignment": "off", 46 | "@typescript-eslint/ban-ts-comment": "off", 47 | "no-undef": "off", 48 | "@next/next/no-img-element": "off", 49 | "no-nested-ternary": "off", 50 | // Debugging 51 | "@typescript-eslint/no-explicit-any": "off", 52 | "no-console": "off", 53 | "import/extensions": "off", 54 | "@typescript-eslint/promise-function-async": "off", 55 | "@typescript-eslint/no-misused-promises": "off", 56 | "@typescript-eslint/strict-boolean-expressions": "off", 57 | "no-underscore-dangle": "off", 58 | "@typescript-eslint/restrict-plus-operands": "off", 59 | "no-plusplus": "off", 60 | "react/button-has-type": "off", 61 | "@typescript-eslint/no-floating-promises": "off", 62 | "no-mixed-spaces-and-tabs": "off", 63 | "import/prefer-default-export": "off", 64 | "react/require-default-props": "off", 65 | "@typescript-eslint/naming-convention": "off", 66 | "import/no-extraneous-dependencies": "off", 67 | "@typescript-eslint/no-unused-vars": ["error"], 68 | }, 69 | settings: { 70 | react: { 71 | version: "18.2.0", 72 | }, 73 | "import/resolver": { 74 | node: { 75 | extensions: [".js", ".jsx", ".ts", ".tsx"], 76 | }, 77 | }, 78 | }, 79 | }; 80 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 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 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | echo '🏗️👷 Styling, testing and building your project before committing' 5 | 6 | # Check Prettier standards 7 | yarn format || 8 | ( 9 | echo '🤢🤮🤢🤮 Its FOKING RAW - Your styling looks disgusting. 🤢🤮🤢🤮 10 | Prettier Check Failed. Run npm run format, add changes and try commit again.'; 11 | false; 12 | ) 13 | 14 | yarn check-format || 15 | ( 16 | echo '🤢🤮🤢🤮 Its FOKING RAW - Your styling looks disgusting. 🤢🤮🤢🤮 17 | Prettier Check Failed. Run npm run format, add changes and try commit again.'; 18 | false; 19 | ) 20 | 21 | # Check ESLint Standards 22 | yarn check-lint || 23 | ( 24 | echo '😤🏀👋😤 Get that weak shit out of here! 😤🏀👋😤 25 | ESLint Check Failed. Make the required changes listed above, add changes and try to commit again.' 26 | false; 27 | ) 28 | 29 | # If everything passes... Now we can commit 30 | echo '🤔🤔🤔🤔... Alright.... Code looks good to me... Trying to build now. 🤔🤔🤔🤔' 31 | 32 | # Check tsconfig standards 33 | yarn check-types || 34 | ( 35 | echo '🤡😂❌🤡 Failed Type check. 🤡😂❌🤡 36 | Are you seriously trying to write that? Make the changes required above.' 37 | false; 38 | ) 39 | 40 | yarn build || 41 | ( 42 | echo '❌👷🔨❌ Better call Bob... Because your build failed ❌👷🔨❌ 43 | Next build failed: View the errors above to see why. 44 | ' 45 | false; 46 | ) 47 | 48 | # If everything passes... Now we can commit 49 | echo '✅✅✅✅ You win this time... I am committing this now. ✅✅✅✅' 50 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .next 2 | next-env.d.ts 3 | node_modules 4 | yarn.lock 5 | package-lock.json 6 | public 7 | .vscode 8 | README.md 9 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "endOfLine": "lf", 3 | "htmlWhitespaceSensitivity": "css", 4 | "insertPragma": false, 5 | "jsxSingleQuote": false, 6 | "printWidth": 120, 7 | "proseWrap": "always", 8 | "quoteProps": "preserve", 9 | "requirePragma": false, 10 | "semi": true, 11 | "singleQuote": false, 12 | "tabWidth": 4, 13 | "trailingComma": "all", 14 | "useTabs": false, 15 | "arrowParens": "always", 16 | "bracketSpacing": true, 17 | "plugins": ["prettier-plugin-tailwindcss"] 18 | } 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with 2 | [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 3 | 4 | ## Getting Started 5 | 6 | First, run the development server: 7 | 8 | ```bash 9 | npm run dev 10 | # or 11 | yarn dev 12 | # or 13 | pnpm dev 14 | # or 15 | bun dev 16 | ``` 17 | 18 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 19 | 20 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. 21 | 22 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and 23 | load Inter, a custom Google Font. 24 | 25 | ## Learn More 26 | 27 | To learn more about Next.js, take a look at the following resources: 28 | 29 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 30 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 31 | 32 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions 33 | are welcome! 34 | 35 | ## Deploy on Vercel 36 | 37 | The easiest way to deploy your Next.js app is to use the 38 | [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) 39 | from the creators of Next.js. 40 | 41 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 42 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jayprecode/country-state-dropdown/e9cbc6a2dbc485f9b6110a636b42322715310de8/app/favicon.ico -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- */ 2 | /* External Dependency */ 3 | /* -------------------------------------------------------------------------- */ 4 | 5 | import React from "react"; 6 | import type { Metadata } from "next"; 7 | import { Inter } from "next/font/google"; 8 | 9 | /* -------------------------------------------------------------------------- */ 10 | /* Internal Dependency */ 11 | /* -------------------------------------------------------------------------- */ 12 | 13 | import "./globals.css"; 14 | 15 | const inter = Inter({ subsets: ["latin"] }); 16 | 17 | export const metadata: Metadata = { 18 | title: "Country Dropdown", 19 | description: "A Country Dropdown", 20 | }; 21 | 22 | const RootLayout = ({ children }: { children: React.ReactNode }) => { 23 | return ( 24 | 25 | {children} 26 | 27 | ); 28 | }; 29 | 30 | export default RootLayout; 31 | -------------------------------------------------------------------------------- /app/page.tsx: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- */ 2 | /* External Dependency */ 3 | /* -------------------------------------------------------------------------- */ 4 | import React from "react"; 5 | 6 | /* -------------------------------------------------------------------------- */ 7 | /* Internal Dependency */ 8 | /* -------------------------------------------------------------------------- */ 9 | 10 | import CountryState from "@/widgets/country-state"; 11 | 12 | const Home = () => { 13 | return ( 14 |
15 | 16 |
17 | ); 18 | }; 19 | 20 | export default Home; 21 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": false, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": false 11 | }, 12 | "aliases": { 13 | "components": "@/components", 14 | "utils": "@/lib/utils" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /components/dropdown/countries.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | /* -------------------------------------------------------------------------- */ 4 | /* External Dependency */ 5 | /* -------------------------------------------------------------------------- */ 6 | 7 | import React from "react"; 8 | import { Check, ChevronsUpDown } from "lucide-react"; 9 | 10 | /* -------------------------------------------------------------------------- */ 11 | /* Internal Dependency */ 12 | /* -------------------------------------------------------------------------- */ 13 | 14 | import { Button } from "@/components/ui/button"; 15 | import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"; 16 | import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem } from "@/components/ui/command"; 17 | import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; 18 | 19 | import { cn, lowerCase } from "@/lib/utils"; 20 | import countries from "@/data/countries.json"; 21 | 22 | import { type CountryProps } from "@/lib/types"; 23 | import { useDropdownStore } from "@/lib/store/dropdown"; 24 | 25 | interface CountryDropdownProps { 26 | disabled?: boolean; 27 | } 28 | 29 | const CountryDropdown = ({ disabled }: CountryDropdownProps) => { 30 | const { countryValue, setCountryValue, openCountryDropdown, setOpenCountryDropdown } = useDropdownStore(); 31 | const C = countries as CountryProps[]; 32 | 33 | return ( 34 | 35 | 36 | 59 | 60 | 61 | 62 | 63 | No country found. 64 | 65 | 66 | {C.map((country) => ( 67 | { 71 | setCountryValue(currentValue === lowerCase(country.name) ? currentValue : ""); 72 | setOpenCountryDropdown(false); 73 | }} 74 | className="flex cursor-pointer items-center justify-between text-xs hover:!bg-[#27272a] hover:!text-white" 75 | > 76 |
77 | {country.emoji} 78 | {country.name} 79 |
80 | 86 |
87 | ))} 88 | 89 |
90 |
91 |
92 |
93 |
94 | ); 95 | }; 96 | 97 | export default CountryDropdown; 98 | -------------------------------------------------------------------------------- /components/dropdown/states.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | /* -------------------------------------------------------------------------- */ 4 | /* External Dependency */ 5 | /* -------------------------------------------------------------------------- */ 6 | 7 | import React from "react"; 8 | import { Check, ChevronsUpDown } from "lucide-react"; 9 | 10 | /* -------------------------------------------------------------------------- */ 11 | /* Internal Dependency */ 12 | /* -------------------------------------------------------------------------- */ 13 | 14 | import { Button } from "@/components/ui/button"; 15 | import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"; 16 | import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem } from "@/components/ui/command"; 17 | import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; 18 | 19 | import { cn, lowerCase, sentenceCase } from "@/lib/utils"; 20 | import states from "@/data/states.json"; 21 | import { useDropdownStore } from "@/lib/store/dropdown"; 22 | 23 | import { type StateProps } from "@/lib/types"; 24 | 25 | const StateDropdown = () => { 26 | const { countryValue, stateValue, openStateDropdown, setOpenStateDropdown, setStateValue } = useDropdownStore(); 27 | 28 | const SD = states as StateProps[]; 29 | const S = SD.filter((state) => state.country_name === sentenceCase(countryValue)); 30 | 31 | return ( 32 | 33 | 34 | 50 | 51 | 52 | 53 | 54 | No state found. 55 | 56 | 57 | {S.map((state) => ( 58 | { 62 | setStateValue(currentValue === lowerCase(state.name) ? currentValue : ""); 63 | setOpenStateDropdown(false); 64 | }} 65 | className="flex cursor-pointer items-center justify-between text-xs hover:!bg-[#27272a] hover:!text-white" 66 | > 67 |
68 | {state.name} 69 |
70 | 76 |
77 | ))} 78 | 79 |
80 |
81 |
82 |
83 |
84 | ); 85 | }; 86 | 87 | export default StateDropdown; 88 | -------------------------------------------------------------------------------- /components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Slot } from "@radix-ui/react-slot"; 3 | import { cva, type VariantProps } from "class-variance-authority"; 4 | 5 | import { cn } from "@/lib/utils"; 6 | 7 | const buttonVariants = cva( 8 | "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 dark:ring-offset-slate-950 dark:focus-visible:ring-slate-300", 9 | { 10 | variants: { 11 | variant: { 12 | default: 13 | "bg-slate-900 text-slate-50 hover:bg-slate-900/90 dark:bg-slate-50 dark:text-slate-900 dark:hover:bg-slate-50/90", 14 | destructive: 15 | "bg-red-500 text-slate-50 hover:bg-red-500/90 dark:bg-red-900 dark:text-slate-50 dark:hover:bg-red-900/90", 16 | outline: 17 | "border border-slate-200 bg-white hover:bg-slate-100 hover:text-slate-900 dark:border-slate-800 dark:bg-slate-950 dark:hover:bg-slate-800 dark:hover:text-slate-50", 18 | secondary: 19 | "bg-slate-100 text-slate-900 hover:bg-slate-100/80 dark:bg-slate-800 dark:text-slate-50 dark:hover:bg-slate-800/80", 20 | ghost: "hover:bg-slate-100 hover:text-slate-900 dark:hover:bg-slate-800 dark:hover:text-slate-50", 21 | link: "text-slate-900 underline-offset-4 hover:underline dark:text-slate-50", 22 | }, 23 | size: { 24 | default: "h-10 px-4 py-2", 25 | sm: "h-9 rounded-md px-3", 26 | lg: "h-11 rounded-md px-8", 27 | icon: "h-10 w-10", 28 | }, 29 | }, 30 | defaultVariants: { 31 | variant: "default", 32 | size: "default", 33 | }, 34 | }, 35 | ); 36 | 37 | export interface ButtonProps 38 | extends React.ButtonHTMLAttributes, 39 | VariantProps { 40 | asChild?: boolean; 41 | } 42 | 43 | const Button = React.forwardRef( 44 | ({ className, variant, size, asChild = false, ...props }, ref) => { 45 | const Comp = asChild ? Slot : "button"; 46 | return ; 47 | }, 48 | ); 49 | Button.displayName = "Button"; 50 | 51 | export { Button, buttonVariants }; 52 | -------------------------------------------------------------------------------- /components/ui/command.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { type DialogProps } from "@radix-ui/react-dialog"; 3 | import { Command as CommandPrimitive } from "cmdk"; 4 | import { Search } from "lucide-react"; 5 | 6 | import { cn } from "@/lib/utils"; 7 | import { Dialog, DialogContent } from "@/components/ui/dialog"; 8 | 9 | const Command = React.forwardRef< 10 | React.ElementRef, 11 | React.ComponentPropsWithoutRef & { className?: string } 12 | >(({ className, ...props }, ref) => ( 13 | 21 | )); 22 | Command.displayName = CommandPrimitive.displayName; 23 | 24 | interface CommandDialogProps extends DialogProps {} 25 | 26 | const CommandDialog = ({ children, ...props }: CommandDialogProps) => { 27 | return ( 28 | 29 | 30 | 31 | {children} 32 | 33 | 34 | 35 | ); 36 | }; 37 | 38 | const CommandInput = React.forwardRef< 39 | React.ElementRef, 40 | React.ComponentPropsWithoutRef & { className?: string } 41 | >(({ className, ...props }, ref) => ( 42 | // eslint-disable-next-line react/no-unknown-property 43 |
44 | 45 | 53 |
54 | )); 55 | 56 | CommandInput.displayName = CommandPrimitive.Input.displayName; 57 | 58 | const CommandList = React.forwardRef< 59 | React.ElementRef, 60 | React.ComponentPropsWithoutRef & { className?: string } 61 | >(({ className, ...props }, ref) => ( 62 | 67 | )); 68 | 69 | CommandList.displayName = CommandPrimitive.List.displayName; 70 | 71 | const CommandEmpty = React.forwardRef< 72 | React.ElementRef, 73 | React.ComponentPropsWithoutRef 74 | >((props, ref) => ); 75 | 76 | CommandEmpty.displayName = CommandPrimitive.Empty.displayName; 77 | 78 | const CommandGroup = React.forwardRef< 79 | React.ElementRef, 80 | React.ComponentPropsWithoutRef & { className?: string } 81 | >(({ className, ...props }, ref) => ( 82 | 90 | )); 91 | 92 | CommandGroup.displayName = CommandPrimitive.Group.displayName; 93 | 94 | const CommandSeparator = React.forwardRef< 95 | React.ElementRef, 96 | React.ComponentPropsWithoutRef & { className?: string } 97 | >(({ className, ...props }, ref) => ( 98 | 99 | )); 100 | CommandSeparator.displayName = CommandPrimitive.Separator.displayName; 101 | 102 | const CommandItem = React.forwardRef< 103 | React.ElementRef, 104 | React.ComponentPropsWithoutRef & { className?: string } 105 | >(({ className, ...props }, ref) => ( 106 | 114 | )); 115 | 116 | CommandItem.displayName = CommandPrimitive.Item.displayName; 117 | 118 | const CommandShortcut = ({ className, ...props }: React.HTMLAttributes & { className?: string }) => { 119 | return ; 120 | }; 121 | CommandShortcut.displayName = "CommandShortcut"; 122 | 123 | export { 124 | Command, 125 | CommandDialog, 126 | CommandInput, 127 | CommandList, 128 | CommandEmpty, 129 | CommandGroup, 130 | CommandItem, 131 | CommandShortcut, 132 | CommandSeparator, 133 | }; 134 | -------------------------------------------------------------------------------- /components/ui/dialog.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import * as DialogPrimitive from "@radix-ui/react-dialog"; 3 | import { X } from "lucide-react"; 4 | 5 | import { cn } from "@/lib/utils"; 6 | 7 | const Dialog = DialogPrimitive.Root; 8 | 9 | const DialogTrigger = DialogPrimitive.Trigger; 10 | 11 | const DialogPortal = DialogPrimitive.Portal; 12 | 13 | const DialogClose = DialogPrimitive.Close; 14 | 15 | const DialogOverlay = React.forwardRef< 16 | React.ElementRef, 17 | React.ComponentPropsWithoutRef & { className?: string } 18 | >(({ className, ...props }, ref) => ( 19 | 27 | )); 28 | DialogOverlay.displayName = DialogPrimitive.Overlay.displayName; 29 | 30 | const DialogContent = React.forwardRef< 31 | React.ElementRef, 32 | React.ComponentPropsWithoutRef & { className?: string } 33 | >(({ className, children, ...props }, ref) => ( 34 | 35 | 36 | 44 | {children} 45 | 46 | 47 | Close 48 | 49 | 50 | 51 | )); 52 | DialogContent.displayName = DialogPrimitive.Content.displayName; 53 | 54 | const DialogHeader = ({ className, ...props }: React.HTMLAttributes & { className?: string }) => ( 55 |
56 | ); 57 | DialogHeader.displayName = "DialogHeader"; 58 | 59 | const DialogFooter = ({ className, ...props }: React.HTMLAttributes & { className?: string }) => ( 60 |
61 | ); 62 | DialogFooter.displayName = "DialogFooter"; 63 | 64 | const DialogTitle = React.forwardRef< 65 | React.ElementRef, 66 | React.ComponentPropsWithoutRef & { className?: string } 67 | >(({ className, ...props }, ref) => ( 68 | 73 | )); 74 | DialogTitle.displayName = DialogPrimitive.Title.displayName; 75 | 76 | const DialogDescription = React.forwardRef< 77 | React.ElementRef, 78 | React.ComponentPropsWithoutRef & { className?: string } 79 | >(({ className, ...props }, ref) => ( 80 | 81 | )); 82 | DialogDescription.displayName = DialogPrimitive.Description.displayName; 83 | 84 | export { 85 | Dialog, 86 | DialogPortal, 87 | DialogOverlay, 88 | DialogClose, 89 | DialogTrigger, 90 | DialogContent, 91 | DialogHeader, 92 | DialogFooter, 93 | DialogTitle, 94 | DialogDescription, 95 | }; 96 | -------------------------------------------------------------------------------- /components/ui/popover.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import * as PopoverPrimitive from "@radix-ui/react-popover"; 3 | 4 | import { cn } from "@/lib/utils"; 5 | 6 | const Popover = PopoverPrimitive.Root; 7 | 8 | const PopoverTrigger = PopoverPrimitive.Trigger; 9 | 10 | const PopoverContent = React.forwardRef< 11 | React.ElementRef, 12 | React.ComponentPropsWithoutRef & { 13 | className?: string; 14 | align?: "start" | "center" | "end"; 15 | sideOffset?: number; 16 | } 17 | >(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( 18 | 19 | 29 | 30 | )); 31 | PopoverContent.displayName = PopoverPrimitive.Content.displayName; 32 | 33 | export { Popover, PopoverTrigger, PopoverContent }; 34 | -------------------------------------------------------------------------------- /components/ui/scroll-area.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"; 3 | 4 | import { cn } from "@/lib/utils"; 5 | 6 | const ScrollBar = React.forwardRef< 7 | React.ElementRef, 8 | React.ComponentPropsWithoutRef & { 9 | className?: string; 10 | orientation?: "vertical" | "horizontal"; 11 | } 12 | >(({ className, orientation = "vertical", ...props }, ref) => ( 13 | 24 | 25 | 26 | )); 27 | ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName; 28 | 29 | const ScrollArea = React.forwardRef< 30 | React.ElementRef, 31 | React.ComponentPropsWithoutRef & { className?: string } 32 | >(({ className, children, ...props }, ref) => ( 33 | 34 | 35 | {children} 36 | 37 | 38 | 39 | 40 | )); 41 | ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName; 42 | 43 | export { ScrollArea, ScrollBar }; 44 | -------------------------------------------------------------------------------- /lib/store/dropdown.ts: -------------------------------------------------------------------------------- 1 | import { create } from "zustand"; 2 | 3 | interface DropdownStateProps { 4 | countryValue: string; 5 | setCountryValue: (countries: string) => void; 6 | openCountryDropdown: boolean; 7 | setOpenCountryDropdown: (openCountry: boolean) => void; 8 | stateValue: string; 9 | setStateValue: (state: string) => void; 10 | openStateDropdown: boolean; 11 | setOpenStateDropdown: (openState: boolean) => void; 12 | } 13 | 14 | export const useDropdownStore = create((set) => ({ 15 | countryValue: "", 16 | setCountryValue: (country: string) => { 17 | set({ countryValue: country }); 18 | }, 19 | openCountryDropdown: false, 20 | setOpenCountryDropdown: (openCountry: boolean) => { 21 | set({ openCountryDropdown: openCountry }); 22 | }, 23 | stateValue: "", 24 | setStateValue: (state: string) => { 25 | set({ stateValue: state }); 26 | }, 27 | openStateDropdown: false, 28 | setOpenStateDropdown: (openState: boolean) => { 29 | set({ openStateDropdown: openState }); 30 | }, 31 | })); 32 | -------------------------------------------------------------------------------- /lib/types.ts: -------------------------------------------------------------------------------- 1 | export interface CountryProps { 2 | id: number; 3 | name: string; 4 | iso3: string; 5 | iso2: string; 6 | numeric_code: string; 7 | phone_code: string; 8 | capital: string; 9 | currency: string; 10 | currency_name: string; 11 | currency_symbol: string; 12 | tld: string; 13 | native: string; 14 | region: string; 15 | region_id: string; 16 | subregion: string; 17 | subregion_id: string; 18 | nationality: string; 19 | timezones: Timezone[]; 20 | translations: Record; 21 | latitude: string; 22 | longitude: string; 23 | emoji: string; 24 | emojiU: string; 25 | } 26 | 27 | interface Timezone { 28 | zoneName: string; 29 | gmtOffset: number; 30 | gmtOffsetName: string; 31 | abbreviation: string; 32 | tzName: string; 33 | } 34 | 35 | export interface StateProps { 36 | id: number; 37 | name: string; 38 | country_id: number; 39 | country_code: string; 40 | country_name: string; 41 | state_code: string; 42 | type: string | null; 43 | latitude: string; 44 | longitude: string; 45 | } 46 | -------------------------------------------------------------------------------- /lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | 8 | // Function to capitalize the first letter 9 | export const capitalizeFirstLetter = (string: string): string => { 10 | return string.charAt(0).toUpperCase() + string.slice(1); 11 | }; 12 | 13 | // Function to lowercase the first letter 14 | export const lowercaseFirstLetter = (string: string): string => { 15 | return string.charAt(0).toLowerCase() + string.slice(1); 16 | }; 17 | 18 | export const sentenceCase = (str: string): string => { 19 | return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase(); 20 | }; 21 | 22 | export const lowerCase = (str: string): string => { 23 | return str.charAt(0).toLowerCase() + str.slice(1).toLowerCase(); 24 | }; 25 | 26 | export const titleCase = (str: string): string => { 27 | return str 28 | .toLowerCase() 29 | .split(" ") 30 | .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) 31 | .join(" "); 32 | }; 33 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | module.exports = nextConfig; 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "country-dropdown", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "start": "NODE_OPTIONS=\"--max-old-space-size=4096\" next start", 8 | "build": "next build", 9 | "compile": "next experimental-compile", 10 | "generate": "next experimental-generate", 11 | "build-export": "next build && next export", 12 | "check-types": "tsc --pretty --noEmit", 13 | "check-format": "prettier --check .", 14 | "format": "next lint --fix && prettier '**/*.{js,jsx,json,md,yml,yaml,ts,tsx}' --write --ignore-path .gitignore", 15 | "check-lint": "eslint . --ext jsx --ext js --ext ts --ext tsx", 16 | "fix": "eslint --fix .", 17 | "check-packages": "yarn clean && yarn compile && yarn test && yarn lint", 18 | "test-all": "yarn check-format && yarn check-lint && yarn check-types && yarn build", 19 | "prepare": "husky install" 20 | }, 21 | "dependencies": { 22 | "@radix-ui/react-dialog": "^1.0.5", 23 | "@radix-ui/react-popover": "^1.0.7", 24 | "@radix-ui/react-scroll-area": "^1.0.5", 25 | "@radix-ui/react-slot": "^1.0.2", 26 | "class-variance-authority": "^0.7.0", 27 | "clsx": "^2.0.0", 28 | "cmdk": "^0.2.0", 29 | "lucide-react": "^0.298.0", 30 | "next": "14.0.4", 31 | "react": "^18", 32 | "react-dom": "^18", 33 | "tailwind-merge": "^2.1.0", 34 | "tailwindcss-animate": "^1.0.7", 35 | "zustand": "^4.4.7" 36 | }, 37 | "devDependencies": { 38 | "@types/node": "^20", 39 | "@types/react": "^18", 40 | "@types/react-dom": "^18", 41 | "autoprefixer": "^10.0.1", 42 | "eslint": "^8", 43 | "eslint-config-next": "14.0.4", 44 | "eslint-config-airbnb": "^19.0.4", 45 | "eslint-config-airbnb-typescript": "^17.1.0", 46 | "eslint-config-prettier": "^9.0.0", 47 | "@next/eslint-plugin-next": "^14.0.0", 48 | "eslint-config-standard-with-typescript": "^39.1.1", 49 | "eslint-plugin-import": "^2.29.0", 50 | "eslint-plugin-jsx-a11y": "^6.7.1", 51 | "eslint-plugin-n": "^16.2.0", 52 | "eslint-plugin-prettier": "^5.0.1", 53 | "eslint-plugin-promise": "^6.1.1", 54 | "eslint-plugin-react": "^7.33.2", 55 | "eslint-plugin-react-hooks": "^4.6.0", 56 | "@typescript-eslint/eslint-plugin": "^6.9.0", 57 | "@typescript-eslint/parser": "^6.9.0", 58 | "husky": "^8.0.0", 59 | "lint-staged": "^15.0.2", 60 | "prettier": "3.0.3", 61 | "prettier-plugin-tailwindcss": "^0.5.6", 62 | "postcss": "^8", 63 | "tailwindcss": "^3.3.0", 64 | "typescript": "^5" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | const config: Config = { 4 | content: [ 5 | "./pages/**/*.{js,ts,jsx,tsx,mdx}", 6 | "./components/**/*.{js,ts,jsx,tsx,mdx}", 7 | "./app/**/*.{js,ts,jsx,tsx,mdx}", 8 | ], 9 | theme: { 10 | container: { 11 | center: true, 12 | padding: "2rem", 13 | screens: { 14 | "2xl": "1400px", 15 | }, 16 | }, 17 | extend: { 18 | backgroundImage: { 19 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", 20 | "gradient-conic": "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", 21 | }, 22 | keyframes: { 23 | "accordion-down": { 24 | from: { height: "0" }, 25 | to: { height: "var(--radix-accordion-content-height)" }, 26 | }, 27 | "accordion-up": { 28 | from: { height: "var(--radix-accordion-content-height)" }, 29 | to: { height: "0" }, 30 | }, 31 | }, 32 | animation: { 33 | "accordion-down": "accordion-down 0.2s ease-out", 34 | "accordion-up": "accordion-up 0.2s ease-out", 35 | }, 36 | }, 37 | }, 38 | plugins: [], 39 | }; 40 | export default config; 41 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "removeComments": true, 7 | "preserveConstEnums": true, 8 | "strict": true, 9 | "alwaysStrict": true, 10 | "strictNullChecks": true, 11 | "noUncheckedIndexedAccess": true, 12 | 13 | "noImplicitAny": true, 14 | "noImplicitReturns": true, 15 | "noImplicitThis": true, 16 | "noUnusedLocals": true, 17 | "noUnusedParameters": true, 18 | "allowUnreachableCode": false, 19 | "noFallthroughCasesInSwitch": true, 20 | 21 | "target": "es2017", 22 | "outDir": "out", 23 | "declaration": true, 24 | "sourceMap": true, 25 | 26 | "esModuleInterop": true, 27 | "allowSyntheticDefaultImports": true, 28 | "allowJs": true, 29 | "checkJs": true, 30 | "skipLibCheck": true, 31 | "forceConsistentCasingInFileNames": true, 32 | 33 | "jsx": "preserve", 34 | "noEmit": true, 35 | "isolatedModules": true, 36 | "incremental": true, 37 | 38 | "baseUrl": ".", 39 | "allowUnusedLabels": false, 40 | "pretty": true, 41 | "resolveJsonModule": true, 42 | "paths": { 43 | "@/*": ["./*"] 44 | }, 45 | "plugins": [ 46 | { 47 | "name": "next" 48 | } 49 | ] 50 | }, 51 | "include": [ 52 | "next-env.d.ts", 53 | "next.config.js", 54 | "**/*.ts", 55 | "**/*.tsx", 56 | ".eslintrc.cjs", 57 | ".next/types/**/*.ts", 58 | "postcss.config.js", 59 | "tailwind.config.ts", 60 | "lint-staged.config.ts", 61 | "app/(root)/quests/edit/layout" 62 | ], 63 | "exclude": ["node_modules", "dirty"] 64 | } 65 | -------------------------------------------------------------------------------- /widgets/country-state.tsx: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- */ 2 | /* External Dependency */ 3 | /* -------------------------------------------------------------------------- */ 4 | 5 | import React from "react"; 6 | 7 | /* -------------------------------------------------------------------------- */ 8 | /* Internal Dependency */ 9 | /* -------------------------------------------------------------------------- */ 10 | 11 | import CountryDropdown from "@/components/dropdown/countries"; 12 | import StateDropdown from "@/components/dropdown/states"; 13 | 14 | const CountryState = () => { 15 | return ( 16 |
17 | 18 | 19 |
20 | ); 21 | }; 22 | 23 | export default CountryState; 24 | --------------------------------------------------------------------------------