This commit is contained in:
2024-04-15 18:19:49 +05:00
commit e0498a7d25
32 changed files with 2705 additions and 0 deletions
+18
View File
@@ -0,0 +1,18 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}
+24
View File
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
+30
View File
@@ -0,0 +1,30 @@
# React + TypeScript + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
## Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
- Configure the top-level `parserOptions` property like this:
```js
export default {
// other rules...
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: ['./tsconfig.json', './tsconfig.node.json'],
tsconfigRootDir: __dirname,
},
}
```
- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
+13
View File
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
+34
View File
@@ -0,0 +1,34 @@
{
"name": "irth",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.22.3",
"react-zoom-pan-pinch": "^3.4.4",
"zustand": "^4.5.2"
},
"devDependencies": {
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
"@typescript-eslint/eslint-plugin": "^7.2.0",
"@typescript-eslint/parser": "^7.2.0",
"@vitejs/plugin-react": "^4.2.1",
"autoprefixer": "^10.4.19",
"eslint": "^8.57.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.6",
"postcss": "^8.4.38",
"tailwindcss": "^3.4.3",
"typescript": "^5.2.2",
"vite": "^5.2.0"
}
}
+6
View File
@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 13 MiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

+37
View File
@@ -0,0 +1,37 @@
import {
backgroundColors,
backgroundHoverColors,
textColors,
} from "../consts/buttonColors";
import { ButtonType } from "../types/button";
interface ButtonProps {
buttonType?: ButtonType;
icon?: React.ReactNode;
text?: string;
className?: string;
}
const Button = ({
className,
icon,
text,
buttonType = "primary",
}: ButtonProps) => {
const backgroundColor = backgroundColors[buttonType];
const backgroundHoverColor = backgroundHoverColors[buttonType];
const textColor = textColors[buttonType];
return (
<button
className={`min-w-[50px] py-3 px-6 transition-[background] duration-300 ease-in-out rounded-lg ${backgroundColor} ${backgroundHoverColor} ${textColor} ${
className ? className : ""
}`}
>
{icon && <div>{icon}</div>}
{text && <p>{text}</p>}
</button>
);
};
export default Button;
+25
View File
@@ -0,0 +1,25 @@
import Button from "../Button";
const Auth = () => {
const isAuth = false;
const username = "Name";
return (
<>
<div className="flex gap-4 py-2 pr-6 text-black col-span-2 justify-end">
{isAuth ? (
<>
<p>{username}</p>
<div className="rounded-full w-10 h-10 bg-[#E2E2DC] overflow-clip">
{/* <img src="" alt="avatar" className="bg-[#E2E2DC]" /> */}
</div>
</>
) : (
<Button buttonType="cta" text="Login" />
)}
</div>
</>
);
};
export default Auth;
+26
View File
@@ -0,0 +1,26 @@
import { Outlet } from "react-router-dom";
import Logo from "./Logo";
import Location from "./Location";
import Auth from "./Auth";
import Navbar from "./Navbar";
import useModal from "../../store/useModal";
const Header = () => {
const { modal } = useModal();
return (
<>
{modal}
<header className="bg-white w-full text-white grid grid-cols-6 text-sm">
<div className="flex gap-4 col-span-2">
<Logo />
<Location />
</div>
<Navbar />
<Auth />
</header>
<Outlet />
</>
);
};
export default Header;
+12
View File
@@ -0,0 +1,12 @@
import LocationIcon from "../icons/LocationIcon";
const Location = () => {
return (
<div className="text-[#73787C] flex gap-1 items-center">
<LocationIcon />
<p className="">Dubai</p>
</div>
);
};
export default Location;
+11
View File
@@ -0,0 +1,11 @@
import LogoIcon from "../icons/LogoIcon";
const Logo = () => {
return (
<div className="text-[#0D1922] py-4 px-6 border-r border-r-[#F3F3F2]">
<LogoIcon />
</div>
);
};
export default Logo;
+15
View File
@@ -0,0 +1,15 @@
const tabs = ["Masterplan", "Search", "Favorites", "Company"];
const Navbar = () => {
return (
<nav className="flex text-[#73787C] self-center col-span-2 justify-center">
{tabs.map((tab) => (
<button key={tab} className="px-4 py-[10px]">
{tab}
</button>
))}
</nav>
);
};
export default Navbar;
+19
View File
@@ -0,0 +1,19 @@
const LineIcon = () => {
return (
<svg
width="1"
height="18"
viewBox="0 0 1 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M0.5 1L0.499999 17"
stroke="currentColor"
strokeLinecap="round"
/>
</svg>
);
};
export default LineIcon;
+20
View File
@@ -0,0 +1,20 @@
const LocationIcon = () => {
return (
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4.16675 8.125C4.16675 7.38632 4.31763 6.65486 4.61078 5.97241C4.90394 5.28995 5.33362 4.66985 5.87529 4.14752C6.41697 3.6252 7.06003 3.21086 7.76776 2.92818C8.47549 2.6455 9.23404 2.5 10.0001 2.5C10.7661 2.5 11.5247 2.64549 12.2324 2.92818C12.9401 3.21086 13.5832 3.62519 14.1249 4.14752C14.6665 4.66985 15.0962 5.28995 15.3894 5.97241C15.6825 6.65486 15.8334 7.38631 15.8334 8.125C15.8334 12.2469 12.7012 15.3619 11.0271 16.7324C10.4211 17.2286 9.57906 17.2286 8.97304 16.7324C7.299 15.3619 4.16675 12.2469 4.16675 8.125ZM10.0001 11.6667C11.841 11.6667 13.3334 10.1743 13.3334 8.33333C13.3334 6.49238 11.841 5 10.0001 5C8.15913 5 6.66675 6.49238 6.66675 8.33333C6.66675 10.1743 8.15913 11.6667 10.0001 11.6667Z"
fill="currentColor"
/>
</svg>
);
};
export default LocationIcon;
+37
View File
@@ -0,0 +1,37 @@
const LogoIcon = () => {
return (
<svg
width="86"
height="24"
viewBox="0 0 86 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clip-path="url(#clip0_227_586)">
<path
d="M10.3982 0H0L2.49306 1.87928V22.1237L0 24H10.3982L7.90218 22.1237V1.87928L10.3982 0Z"
fill="currentColor"
/>
<path
d="M83.5038 1.87928L85.9999 0H75.6016L78.0947 1.87928V11.2486H68.4735V1.87928L70.9665 0H60.5713L63.0644 1.87928V22.1237L60.5713 24H70.9665L68.4735 22.1237V12.2033H78.0947V22.1237L75.6016 24H85.9999L83.5038 22.1237V1.87928Z"
fill="currentColor"
/>
<path
d="M57.6044 0H37.543L37.546 6.28536L40.633 0.954699H44.8931V22.1237L42.4001 24H52.7983L50.3023 22.1237V0.954699H54.5414L57.6254 6.28536V0.00602334L57.6044 0.0120467V0Z"
fill="currentColor"
/>
<path
d="M21.2855 0.903438H23.7486C23.7486 0.903438 28.1557 0.740808 28.3207 5.36673C28.4857 9.99266 26.9707 12.3387 22.4915 12.1219V13.0224H24.2856L29.6077 23.9999H37.8849L34.8308 21.6508L29.8837 13.4169C29.8837 13.4169 26.9197 13.0736 26.3376 12.7513C26.3166 12.7513 34.1978 12.4683 34.2008 5.76126C34.2008 5.74319 34.6568 -0.0090981 24.8406 0.00294858L13.3713 0.0180069L15.8644 1.89428V22.1207L13.3713 23.9999H23.7696L21.2765 22.1207L21.2915 0.900426L21.2855 0.903438Z"
fill="currentColor"
/>
</g>
<defs>
<clipPath id="clip0_227_586">
<rect width="86" height="24" fill="white" />
</clipPath>
</defs>
</svg>
);
};
export default LogoIcon;
@@ -0,0 +1,19 @@
const OpenFullscreenIcon = () => {
return (
<svg
width="49"
height="48"
viewBox="0 0 49 48"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11.9 29L6.5 24M6.5 24L11.9 19M6.5 24H18.2M30.8 24H42.5M42.5 24L37.1 29M42.5 24L37.1 19M29.5 36.6L24.5 42M24.5 42L19.5 36.6M24.5 42V30.3M24.5 17.7V6M24.5 6L29.5 11.4M24.5 6L19.5 11.4"
stroke="currentColor"
strokeWidth="3"
/>
</svg>
);
};
export default OpenFullscreenIcon;
+21
View File
@@ -0,0 +1,21 @@
const SearchPlusIcon = () => {
return (
<svg
width="49"
height="48"
viewBox="0 0 49 48"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M41.5 41L33.8137 33.3137M33.8137 33.3137C36.7091 30.4183 38.5 26.4183 38.5 22C38.5 13.1634 31.3366 6 22.5 6C13.6634 6 6.5 13.1634 6.5 22C6.5 30.8366 13.6634 38 22.5 38C26.9141 38 30.9108 36.2125 33.8056 33.3219L33.8137 33.3137ZM16.5 22H28.5M22.5 16V28"
stroke="currentColor"
strokeWidth="3"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
};
export default SearchPlusIcon;
+15
View File
@@ -0,0 +1,15 @@
import SearchPlusIcon from "../icons/SearchIcon";
import OpenFullscreenIcon from "../icons/OpenFullscreenIcon";
import LineIcon from "../icons/LineIcon";
const ZoomHint = () => {
return (
<div className="absolute z-10 m-auto top-1/2 left-1/2 flex bg-[#0D192266] p-4 text-white gap-4 items-center">
<SearchPlusIcon />
<LineIcon />
<OpenFullscreenIcon />
</div>
);
};
export default ZoomHint;
+21
View File
@@ -0,0 +1,21 @@
import { ButtonColor } from "../types/button";
const backgroundColors: ButtonColor = {
cta: "bg-[#00BED7]",
primary: "bg-[#ffffff]",
tertiary: "bg-[#0D192266]",
};
const backgroundHoverColors: ButtonColor = {
cta: "hover:bg-[#00A8BE]",
primary: "hover:bg-[#F3F3F2]",
tertiary: "hover:bg-[#0D192266]",
};
const textColors: ButtonColor = {
cta: "text-[#ffffff]",
primary: "text-[#0D1922]",
tertiary: "text-[#ffffff]",
};
export { textColors, backgroundColors, backgroundHoverColors };
+3
View File
@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
+22
View File
@@ -0,0 +1,22 @@
import ReactDOM from "react-dom/client";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import Header from "./components/header/Header";
import Main from "./pages/Main";
import "./index.css";
const router = createBrowserRouter([
{
path: "/",
element: <Header />,
children: [
{
path: "/",
element: <Main />,
},
],
},
]);
ReactDOM.createRoot(document.getElementById("root")!).render(
<RouterProvider router={router} />
);
+29
View File
@@ -0,0 +1,29 @@
import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch";
import useModal from "../store/useModal";
import { useEffect } from "react";
import ZoomHint from "../components/modals/ZoomHint";
const Main = () => {
const { setModal } = useModal();
useEffect(() => {
setModal(<ZoomHint />);
return () => {};
}, []);
return (
<div>
<TransformWrapper
alignmentAnimation={{ sizeX: 0, sizeY: 0 }}
zoomAnimation={{ size: 0 }}
>
<TransformComponent wrapperClass="h-[calc(100vh-60px)]">
<div>
<img src="images/Map.jpg" alt="Map" />
</div>
</TransformComponent>
</TransformWrapper>
</div>
);
};
export default Main;
+13
View File
@@ -0,0 +1,13 @@
import { create } from "zustand";
interface ModalStore {
modal: React.ReactNode | null;
setModal: (modal: React.ReactNode | null) => void;
}
const useModal = create<ModalStore>((set) => ({
modal: null,
setModal: (modal) => set(() => ({ modal: modal })),
}));
export default useModal;
+6
View File
@@ -0,0 +1,6 @@
type ButtonType = "primary" | "tertiary" | "cta";
type ButtonColor = {
[key in ButtonType]: string;
};
export type { ButtonColor, ButtonType };
+1
View File
@@ -0,0 +1 @@
/// <reference types="vite/client" />
+8
View File
@@ -0,0 +1,8 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {},
},
plugins: [],
};
+25
View File
@@ -0,0 +1,25 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}
+11
View File
@@ -0,0 +1,11 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"strict": true
},
"include": ["vite.config.ts"]
}
+7
View File
@@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
})
+2176
View File
File diff suppressed because it is too large Load Diff