Eduard Makarov
9 months ago
commit
9d04f2e7a8
47 changed files with 4713 additions and 0 deletions
@ -0,0 +1,18 @@
@@ -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 }, |
||||
], |
||||
}, |
||||
} |
@ -0,0 +1,24 @@
@@ -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? |
@ -0,0 +1,30 @@
@@ -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 |
@ -0,0 +1,13 @@
@@ -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="main.tsx"></script> |
||||
</body> |
||||
</html> |
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
import React from "react"; |
||||
import ReactDOM from "react-dom/client"; |
||||
import App from "./src/App.tsx"; |
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render( |
||||
<React.StrictMode> |
||||
<App /> |
||||
</React.StrictMode> |
||||
); |
@ -0,0 +1,34 @@
@@ -0,0 +1,34 @@
|
||||
{ |
||||
"name": "almont", |
||||
"private": true, |
||||
"version": "0.0.0", |
||||
"type": "module", |
||||
"scripts": { |
||||
"dev": "vite --open --port 3000", |
||||
"build": "tsc && vite build", |
||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", |
||||
"preview": "vite preview" |
||||
}, |
||||
"dependencies": { |
||||
"localforage": "^1.10.0", |
||||
"match-sorter": "^6.3.4", |
||||
"react": "^18.2.0", |
||||
"react-dom": "^18.2.0", |
||||
"react-icons": "^5.0.1", |
||||
"react-router-dom": "^6.22.2", |
||||
"react-select": "^5.8.0", |
||||
"sort-by": "^1.2.0" |
||||
}, |
||||
"devDependencies": { |
||||
"@types/react": "^18.2.56", |
||||
"@types/react-dom": "^18.2.19", |
||||
"@typescript-eslint/eslint-plugin": "^7.0.2", |
||||
"@typescript-eslint/parser": "^7.0.2", |
||||
"@vitejs/plugin-react": "^4.2.1", |
||||
"eslint": "^8.56.0", |
||||
"eslint-plugin-react-hooks": "^4.6.0", |
||||
"eslint-plugin-react-refresh": "^0.4.5", |
||||
"typescript": "^5.2.2", |
||||
"vite": "^5.1.4" |
||||
} |
||||
} |
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
import "@/assets/styles/index.css"; |
||||
import { Navigation } from "./navigation"; |
||||
|
||||
function App() { |
||||
return <Navigation />; |
||||
} |
||||
|
||||
export default App; |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,150 @@
@@ -0,0 +1,150 @@
|
||||
@font-face { |
||||
font-family: "Roboto"; |
||||
src: url("/src/assets/fonts/Roboto-Bold.ttf"); |
||||
font-weight: 900; |
||||
font-style: normal; |
||||
} |
||||
|
||||
@font-face { |
||||
font-family: "Roboto"; |
||||
src: url("/src/assets/fonts/Roboto-Bold.ttf"); |
||||
font-weight: 700; |
||||
font-style: normal; |
||||
} |
||||
|
||||
@font-face { |
||||
font-family: "Roboto"; |
||||
src: url("/src/assets/fonts/Roboto-Medium.ttf"); |
||||
font-weight: 500; |
||||
font-style: normal; |
||||
} |
||||
|
||||
@font-face { |
||||
font-family: "Roboto"; |
||||
src: url("/src/assets/fonts/Roboto-Regular.ttf"); |
||||
font-weight: 400; |
||||
font-style: normal; |
||||
} |
||||
|
||||
@font-face { |
||||
font-family: "Roboto"; |
||||
src: url("/src/assets/fonts/Roboto-Light.ttf"); |
||||
font-weight: 300; |
||||
font-style: normal; |
||||
} |
||||
|
||||
|
||||
#root { |
||||
max-width: 1280px; |
||||
margin: 0 auto; |
||||
} |
||||
|
||||
* { |
||||
box-sizing: border-box; |
||||
font-family: "Roboto", sans-serif !important; |
||||
outline: none !important; |
||||
} |
||||
|
||||
html, |
||||
body, |
||||
div, |
||||
span, |
||||
object, |
||||
iframe, |
||||
h1, |
||||
h2, |
||||
h3, |
||||
h4, |
||||
h5, |
||||
h6, |
||||
p, |
||||
blockquote, |
||||
pre, |
||||
abbr, |
||||
address, |
||||
cite, |
||||
code, |
||||
del, |
||||
dfn, |
||||
em, |
||||
img, |
||||
ins, |
||||
kbd, |
||||
q, |
||||
samp, |
||||
small, |
||||
strong, |
||||
sub, |
||||
sup, |
||||
var, |
||||
b, |
||||
i, |
||||
dl, |
||||
dt, |
||||
dd, |
||||
ol, |
||||
ul, |
||||
li, |
||||
fieldset, |
||||
form, |
||||
label, |
||||
legend, |
||||
table, |
||||
caption, |
||||
tbody, |
||||
tfoot, |
||||
thead, |
||||
tr, |
||||
th, |
||||
td, |
||||
article, |
||||
aside, |
||||
canvas, |
||||
details, |
||||
figcaption, |
||||
figure, |
||||
footer, |
||||
header, |
||||
hgroup, |
||||
menu, |
||||
nav, |
||||
section, |
||||
summary, |
||||
time, |
||||
mark, |
||||
audio, |
||||
video { |
||||
margin: 0; |
||||
padding: 0; |
||||
border: 0; |
||||
outline: 0; |
||||
font-size: 100%; |
||||
vertical-align: baseline; |
||||
background: transparent; |
||||
list-style: none; |
||||
list-style: none; |
||||
|
||||
} |
||||
|
||||
body { |
||||
line-height: 1; |
||||
height: 100vh; |
||||
} |
||||
|
||||
p { |
||||
color: #000000; |
||||
} |
||||
|
||||
button{ |
||||
border: none; |
||||
background-color: transparent; |
||||
margin:0; |
||||
padding:0; |
||||
} |
||||
|
||||
|
||||
a:link, |
||||
a:visited { |
||||
color: inherit; |
||||
text-decoration: none; |
||||
} |
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
import { Colors } from "../typing/colors.interfaces"; |
||||
|
||||
export const colors: Colors = { |
||||
primatyText: "#000000D9", |
||||
lightBlue: "#EAF3FF", |
||||
secondaryText: "#6E6E6E", |
||||
whiteText: "#FFFFFF", |
||||
primary: "#2D3A5F", |
||||
disabledColor: "gray", |
||||
errorColor: "red", |
||||
lightYellow: "#FFFFE7", |
||||
}; |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
export { PrimaryButton } from "./primary-button"; |
@ -0,0 +1,57 @@
@@ -0,0 +1,57 @@
|
||||
import { CSSProperties, useMemo } from "react"; |
||||
import styles from "./style.module.css"; |
||||
|
||||
interface Props { |
||||
onClick: () => void; |
||||
label: string; |
||||
disabled?: boolean; |
||||
isLoading?: boolean; |
||||
leftIcon?: JSX.Element | null; |
||||
style?: CSSProperties; |
||||
labelStyle?: CSSProperties; |
||||
} |
||||
|
||||
export const PrimaryButton = ({ |
||||
onClick, |
||||
label, |
||||
style, |
||||
labelStyle, |
||||
disabled, |
||||
isLoading, |
||||
leftIcon = null, |
||||
}: Props) => { |
||||
const getButtonClass = () => { |
||||
switch (true) { |
||||
case disabled: |
||||
return `${styles.disabled} ${styles.button_container}`; |
||||
default: |
||||
return styles.button_container; |
||||
} |
||||
}; |
||||
|
||||
const content = useMemo(() => { |
||||
if (isLoading) { |
||||
return <div className={styles.loader}></div>; |
||||
} else { |
||||
return ( |
||||
<div className={styles.content}> |
||||
<>{leftIcon}</> |
||||
<p className={styles.button_label} style={labelStyle}> |
||||
{label} |
||||
</p> |
||||
</div> |
||||
); |
||||
} |
||||
}, [isLoading, label, labelStyle]); |
||||
|
||||
return ( |
||||
<button |
||||
disabled={disabled} |
||||
onClick={onClick} |
||||
style={style} |
||||
className={getButtonClass()} |
||||
> |
||||
{content} |
||||
</button> |
||||
); |
||||
}; |
@ -0,0 +1,54 @@
@@ -0,0 +1,54 @@
|
||||
.button_container { |
||||
position: relative; |
||||
width:300px; |
||||
height: 40px; |
||||
cursor: pointer; |
||||
transition: background-color 0.5s; |
||||
background-color: #2D3A5F; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
} |
||||
|
||||
.button_label{ |
||||
color: #FFFF; |
||||
font-size: 16px; |
||||
|
||||
} |
||||
.disabled{ |
||||
pointer-events: none; |
||||
background-color: #00000052; |
||||
|
||||
} |
||||
.button_container:hover { |
||||
opacity: 0.9; |
||||
} |
||||
|
||||
.button_container:active { |
||||
opacity: 0.8; |
||||
} |
||||
|
||||
.content{ |
||||
display: flex; |
||||
gap: 8px; |
||||
} |
||||
|
||||
.loader { |
||||
position: absolute; |
||||
transform: translate(-50%, -50%); |
||||
border: 2px solid #e1dddd; |
||||
border-top: 2px solid #88a2eb; |
||||
border-radius: 50%; |
||||
width: 20px; |
||||
height: 20px; |
||||
animation: spin 1s linear infinite; |
||||
} |
||||
|
||||
@keyframes spin { |
||||
0% { |
||||
transform: rotate(0deg); |
||||
} |
||||
100% { |
||||
transform: rotate(360deg); |
||||
} |
||||
} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
export * from "./button-primary"; |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
export * from "./text-field"; |
||||
export * from "./select-field"; |
@ -0,0 +1,49 @@
@@ -0,0 +1,49 @@
|
||||
import React from "react"; |
||||
|
||||
interface Option { |
||||
value: string; |
||||
label: string; |
||||
options?: unknown; |
||||
} |
||||
|
||||
interface Props { |
||||
isMulti: boolean; |
||||
onClick: () => void; |
||||
selecedOptions: Option[]; |
||||
} |
||||
export const SelectHeaderAtom = ({ |
||||
isMulti, |
||||
onClick, |
||||
selecedOptions, |
||||
}: Props) => { |
||||
return ( |
||||
<div |
||||
className="select-header" |
||||
onClick={onClick} |
||||
style={{ |
||||
width: 200, |
||||
border: "1px solid", |
||||
display: "flex", |
||||
flexDirection: "row", |
||||
justifyContent: "space-between", |
||||
alignItems: "center", |
||||
padding: 10, |
||||
}} |
||||
> |
||||
{isMulti && selectedOptions.length > 0 |
||||
? selectedOptions.map((opt) => ( |
||||
<div style={{ display: "flex" }}> |
||||
<p>{opt.label}</p> |
||||
<FiTrash2 |
||||
style={{ width: 15, height: 15, cursor: "pointer" }} |
||||
onClick={() => removeTag(opt)} |
||||
/> |
||||
</div> |
||||
)) |
||||
: selectedOptions.length > 0 |
||||
? selectedOptions[0].label |
||||
: "Select..."} |
||||
{isOpen ? <FiChevronUp /> : <FiChevronDown />} |
||||
</div> |
||||
); |
||||
}; |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
export { SelectField } from "./select-field"; |
@ -0,0 +1,103 @@
@@ -0,0 +1,103 @@
|
||||
import React from "react"; |
||||
// import styles from "./style.module.css";
|
||||
import { useState } from "react"; |
||||
import { FiChevronUp, FiChevronDown, FiTrash2 } from "react-icons/fi"; // Іконки стрілок
|
||||
|
||||
const options: Option[] = [ |
||||
{ value: "blues", label: "Blues" }, |
||||
{ value: "rock", label: "Rock" }, |
||||
{ value: "jazz", label: "Jazz" }, |
||||
{ value: "orchestra", label: "Orchestra" }, |
||||
]; |
||||
|
||||
interface Option { |
||||
value: string; |
||||
label: string; |
||||
options?: unknown; |
||||
} |
||||
|
||||
interface Props { |
||||
isMulti: boolean; |
||||
error?: boolean; |
||||
} |
||||
|
||||
export const SelectField = ({ isMulti }: Props) => { |
||||
const [isOpen, setIsOpen] = useState(false); |
||||
const [selectedOptions, setSelectedOptions] = useState<Option[]>([]); |
||||
|
||||
const toggleSelect = () => { |
||||
setIsOpen((prevState) => !prevState); |
||||
}; |
||||
|
||||
const handleOptionSelect = (option: Option) => { |
||||
if (isMulti) { |
||||
setSelectedOptions((prevOptions) => { |
||||
const exists = prevOptions.some((item) => item.value === option.value); |
||||
if (exists) { |
||||
return prevOptions.filter((item) => item.value !== option.value); |
||||
} else { |
||||
return [...prevOptions, option]; |
||||
} |
||||
}); |
||||
} else { |
||||
setSelectedOptions([option]); |
||||
setIsOpen(false); |
||||
} |
||||
}; |
||||
|
||||
const removeTag = (option: Option) => { |
||||
setSelectedOptions((prev) => { |
||||
return prev.filter((it) => it.value !== option.value); |
||||
}); |
||||
}; |
||||
|
||||
return ( |
||||
<div className="custom-select"> |
||||
<div |
||||
className="select-header" |
||||
onClick={toggleSelect} |
||||
style={{ |
||||
width: 200, |
||||
border: "1px solid", |
||||
display: "flex", |
||||
flexDirection: "row", |
||||
justifyContent: "space-between", |
||||
alignItems: "center", |
||||
padding: 10, |
||||
}} |
||||
> |
||||
{isMulti && selectedOptions.length > 0 |
||||
? selectedOptions.map((opt) => ( |
||||
<div style={{ display: "flex" }}> |
||||
<p>{opt.label}</p> |
||||
<FiTrash2 |
||||
style={{ width: 15, height: 15, cursor: "pointer" }} |
||||
onClick={() => removeTag(opt)} |
||||
/> |
||||
</div> |
||||
)) |
||||
: selectedOptions.length > 0 |
||||
? selectedOptions[0].label |
||||
: "Select..."} |
||||
{isOpen ? <FiChevronUp /> : <FiChevronDown />} |
||||
</div> |
||||
{isOpen && ( |
||||
<ul className="options-list"> |
||||
{options.map((option) => ( |
||||
<li |
||||
key={option.value} |
||||
onClick={() => handleOptionSelect(option)} |
||||
className={ |
||||
selectedOptions.some((item) => item.value === option.value) |
||||
? "selected" |
||||
: "" |
||||
} |
||||
> |
||||
{option.label} |
||||
</li> |
||||
))} |
||||
</ul> |
||||
)} |
||||
</div> |
||||
); |
||||
}; |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
export { TextField } from "./text-field.control"; |
@ -0,0 +1,32 @@
@@ -0,0 +1,32 @@
|
||||
.text_field { |
||||
max-width: 200px; |
||||
height: 40px; |
||||
padding: 5px 10px; |
||||
align-items: center; |
||||
border: 1px solid #D9D9D9; |
||||
border-radius: 2px; |
||||
} |
||||
|
||||
.text_field.focused { |
||||
border-color: #bdbdbd; |
||||
box-shadow: 0 0 0.4rem 0.2rem rgba(209, 209, 209, 0.162); |
||||
} |
||||
|
||||
|
||||
|
||||
.label{ |
||||
font-size: 14px; |
||||
color: #000000D9; |
||||
margin-bottom: 10px; |
||||
} |
||||
.error_field{border-color: red;} |
||||
|
||||
.error_focused { |
||||
border-color: red; |
||||
box-shadow: 0 0 0.4rem 0.2rem rgba(255, 0, 0, 0.135); |
||||
} |
||||
.error{ |
||||
color: red; |
||||
font-size: 10px; |
||||
margin-top: 3px |
||||
} |
@ -0,0 +1,69 @@
@@ -0,0 +1,69 @@
|
||||
import { CSSProperties, InputHTMLAttributes, useMemo, useState } from "react"; |
||||
import styles from "./styles.module.css"; |
||||
interface Props extends InputHTMLAttributes<HTMLInputElement> { |
||||
value: string; |
||||
onChangeField: (val: string) => void; |
||||
label?: string; |
||||
error?: string; |
||||
styleContainer?: CSSProperties; |
||||
inputStyle?: CSSProperties; |
||||
} |
||||
export const TextField = ({ |
||||
value, |
||||
onChangeField, |
||||
error, |
||||
label, |
||||
styleContainer, |
||||
inputStyle, |
||||
...props |
||||
}: Props) => { |
||||
const [focused, setFocused] = useState(false); |
||||
|
||||
const handleFocus = () => { |
||||
setFocused(true); |
||||
}; |
||||
|
||||
const handleBlur = () => { |
||||
setFocused(false); |
||||
}; |
||||
const labelField = useMemo(() => { |
||||
if (!label) { |
||||
return null; |
||||
} |
||||
return <p className={styles.label}>{label}</p>; |
||||
}, [label]); |
||||
|
||||
const getClassNames = () => { |
||||
let classNames = styles.text_field; |
||||
switch (true) { |
||||
case focused && !!error: |
||||
classNames += ` ${styles.error_focused}`; |
||||
break; |
||||
case !!error && !focused: |
||||
classNames += ` ${styles.error_field}`; |
||||
break; |
||||
case focused: |
||||
classNames += ` ${styles.focused}`; |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
return classNames; |
||||
}; |
||||
|
||||
return ( |
||||
<div style={styleContainer}> |
||||
<>{labelField}</> |
||||
<input |
||||
style={inputStyle} |
||||
value={value} |
||||
onChange={(e) => onChangeField(e.target.value)} |
||||
className={getClassNames()} |
||||
onFocus={handleFocus} |
||||
onBlur={handleBlur} |
||||
{...props} |
||||
/> |
||||
{error ? <p className={styles.error}>{error}</p> : null} |
||||
</div> |
||||
); |
||||
}; |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
export * from "./buttons"; |
||||
export * from "./form-controls"; |
@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
|
||||
export interface Colors { |
||||
primatyText: string; |
||||
secondaryText: string; |
||||
primary: string; |
||||
disabledColor: string; |
||||
errorColor: string; |
||||
whiteText: string; |
||||
lightBlue: string; |
||||
lightYellow: string; |
||||
} |
@ -0,0 +1,70 @@
@@ -0,0 +1,70 @@
|
||||
import React, { CSSProperties } from "react"; |
||||
import { PrimaryButton } from "../../../theme/components"; |
||||
import { colors } from "../../../theme/colors"; |
||||
|
||||
const IconsPlus = (props: { style?: CSSProperties; colorIcon?: string }) => ( |
||||
<div style={props?.style}> |
||||
<svg |
||||
fill="none" |
||||
aria-hidden="true" |
||||
className="w-6 h-6 text-gray-800 dark:text-white" |
||||
viewBox="0 0 24 24" |
||||
> |
||||
<path |
||||
stroke={props?.colorIcon || "currentColor"} |
||||
strokeLinecap="round" |
||||
strokeLinejoin="round" |
||||
strokeWidth={2} |
||||
d="M12 7.8v8.4M7.8 12h8.4m4.8 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" |
||||
/> |
||||
</svg> |
||||
</div> |
||||
); |
||||
|
||||
export const ButtonsPage = () => { |
||||
return ( |
||||
<div style={{ display: "flex", flexDirection: "column", gap: 20 }}> |
||||
<PrimaryButton |
||||
onClick={() => console.log("hello 1")} |
||||
label="Login" |
||||
isLoading={true} |
||||
/> |
||||
<PrimaryButton onClick={() => console.log("hello 1")} label="Login" /> |
||||
|
||||
<PrimaryButton |
||||
disabled={true} |
||||
onClick={() => console.log("hello 2")} |
||||
label="Disaled button" |
||||
/> |
||||
|
||||
<PrimaryButton |
||||
onClick={() => console.log("hello 2")} |
||||
label="Outline button" |
||||
style={{ backgroundColor: "transparent", border: "1px solid" }} |
||||
labelStyle={{ color: colors.primatyText }} |
||||
/> |
||||
|
||||
<PrimaryButton |
||||
onClick={() => console.log("hello 3")} |
||||
label="Text button" |
||||
labelStyle={{ color: colors.primatyText, fontWeight: "500" }} |
||||
style={{ backgroundColor: "transparent" }} |
||||
/> |
||||
|
||||
<PrimaryButton |
||||
onClick={() => console.log("hello 3")} |
||||
label="Text button with icon" |
||||
labelStyle={{ color: colors.primatyText, fontWeight: "500" }} |
||||
style={{ backgroundColor: "transparent" }} |
||||
leftIcon={<IconsPlus style={{ height: 20, width: 20 }} />} |
||||
/> |
||||
<PrimaryButton |
||||
onClick={() => console.log("hello 1")} |
||||
label="Primary with icon" |
||||
leftIcon={ |
||||
<IconsPlus style={{ height: 20, width: 20 }} colorIcon="white" /> |
||||
} |
||||
/> |
||||
</div> |
||||
); |
||||
}; |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
export { ButtonsPage } from "./buttons.page"; |
@ -0,0 +1,54 @@
@@ -0,0 +1,54 @@
|
||||
import React, { useEffect, useState } from "react"; |
||||
import { TextField, SelectField } from "../../../theme/components"; |
||||
|
||||
export const FormsPage = () => { |
||||
const [value, setValue] = useState(""); |
||||
const [error, setError] = useState("Error message"); |
||||
console.log("value", value); |
||||
useEffect(() => { |
||||
if (value.length > 0) { |
||||
setError(""); |
||||
} else { |
||||
setError("Error message"); |
||||
} |
||||
}, [value]); |
||||
|
||||
return ( |
||||
<div> |
||||
<h4 style={{ marginBottom: 10 }}>Text fields</h4> |
||||
|
||||
<div style={{ display: "flex", gap: 20 }}> |
||||
<TextField |
||||
label="Username" |
||||
placeholder="Enter name" |
||||
value={value} |
||||
onChangeField={setValue} |
||||
error={error} |
||||
/> |
||||
|
||||
<TextField |
||||
label="Numeric" |
||||
placeholder="Only number" |
||||
type="number" |
||||
value={value} |
||||
onChangeField={setValue} |
||||
/> |
||||
|
||||
<TextField |
||||
label="Password" |
||||
placeholder="Enter password" |
||||
type="password" |
||||
value={value} |
||||
onChangeField={setValue} |
||||
error={error} |
||||
/> |
||||
</div> |
||||
|
||||
<h4 style={{ margin: "10px 0px" }}>Select field</h4> |
||||
<div style={{ display: "flex", gap: 20 }}> |
||||
<SelectField isMulti /> |
||||
<SelectField isMulti={false} /> |
||||
</div> |
||||
</div> |
||||
); |
||||
}; |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
export { FormsPage } from "./forms.page"; |
@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
|
||||
export { UIKitPage } from "./root.page"; |
||||
export { ButtonsPage } from "./buttons"; |
||||
export { FormsPage } from "./forms"; |
@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
|
||||
import { Outlet, useNavigate } from "react-router-dom"; |
||||
import { PrimaryButton } from "../../theme/components"; |
||||
|
||||
export const UIKitPage = () => { |
||||
const nav = useNavigate(); |
||||
const navigateTo = (urlPage: string) => nav(urlPage); |
||||
return ( |
||||
<> |
||||
<div style={{ display: "flex", gap: 20, padding: "30px 0px" }}> |
||||
<PrimaryButton |
||||
onClick={() => navigateTo("buttons")} |
||||
label="Buttons page" |
||||
/> |
||||
<PrimaryButton |
||||
onClick={() => navigateTo("forms")} |
||||
label="Forms control page" |
||||
/> |
||||
</div> |
||||
|
||||
<Outlet /> |
||||
</> |
||||
); |
||||
}; |
@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
|
||||
export const createStyleSheet = <K extends string>( |
||||
styles: Record<K, React.CSSProperties> |
||||
) => styles; |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
export * from "./use-routes.hook"; |
@ -0,0 +1,24 @@
@@ -0,0 +1,24 @@
|
||||
import { Route } from "react-router-dom"; |
||||
import { AppRoute } from "../typing/interfaces"; |
||||
|
||||
interface IProps { |
||||
routes: AppRoute[]; |
||||
} |
||||
export const useRoutes = ({ routes }: IProps) => { |
||||
return routes.map((route) => { |
||||
if (route.children) { |
||||
return ( |
||||
<Route key={route.path} element={route.element} path={route.path}> |
||||
{route.children.map((childRoute) => ( |
||||
<Route |
||||
key={childRoute.path} |
||||
element={childRoute.element} |
||||
path={childRoute.path} |
||||
/> |
||||
))} |
||||
</Route> |
||||
); |
||||
} |
||||
return <Route key={route.path} element={route.element} path={route.path} />; |
||||
}); |
||||
}; |
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
import { BrowserRouter, Routes } from "react-router-dom"; |
||||
import { routesConfig } from "./routes-config"; |
||||
import { useRoutes } from "./hooks/use-routes.hook"; |
||||
|
||||
export const Navigation = () => { |
||||
const routes = useRoutes({ routes: routesConfig }); |
||||
return ( |
||||
<BrowserRouter> |
||||
<Routes>{routes}</Routes> |
||||
</BrowserRouter> |
||||
); |
||||
}; |
@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
|
||||
import { UIKitPage, FormsPage, ButtonsPage } from "../core/ui-kit/pages"; |
||||
import { AppRoute } from "./typing/interfaces"; |
||||
|
||||
export const routesConfig: AppRoute[] = [ |
||||
{ |
||||
title: "UI Kit", |
||||
path: "/", |
||||
element: <UIKitPage />, |
||||
children: [ |
||||
{ |
||||
title: "Buttons UI", |
||||
path: "buttons", |
||||
element: <ButtonsPage />, |
||||
}, |
||||
{ |
||||
title: "Forms UI", |
||||
path: "forms", |
||||
element: <FormsPage />, |
||||
}, |
||||
], |
||||
}, |
||||
]; |
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
interface RouteElement { |
||||
title: string; |
||||
path: string; |
||||
element?: JSX.Element; |
||||
} |
||||
|
||||
export interface AppRoute extends RouteElement { |
||||
children?: RouteElement[]; |
||||
} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
@ -0,0 +1,34 @@
@@ -0,0 +1,34 @@
|
||||
{ |
||||
"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", |
||||
"main.tsx" |
||||
], |
||||
"references": [ |
||||
{ |
||||
"path": "./tsconfig.node.json" |
||||
} |
||||
] |
||||
} |
@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
|
||||
{ |
||||
"compilerOptions": { |
||||
"composite": true, |
||||
"skipLibCheck": true, |
||||
"module": "ESNext", |
||||
"moduleResolution": "bundler", |
||||
"allowSyntheticDefaultImports": true, |
||||
"strict": true |
||||
}, |
||||
"include": ["vite.config.ts"] |
||||
} |
Loading…
Reference in new issue