Вёрстка страницы 'Каталог Яхт'

This commit is contained in:
Sergey Bolshakov 2025-12-11 04:13:27 +03:00
parent 0e17e02248
commit 81d293434c
19 changed files with 899 additions and 30 deletions

98
package-lock.json generated
View File

@ -13,8 +13,10 @@
"@radix-ui/react-label": "^2.1.7", "@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-navigation-menu": "^1.2.14", "@radix-ui/react-navigation-menu": "^1.2.14",
"@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-radio-group": "^1.3.8",
"@radix-ui/react-select": "^2.2.6", "@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-separator": "^1.1.7", "@radix-ui/react-separator": "^1.1.7",
"@radix-ui/react-slider": "^1.3.6",
"@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-slot": "^1.2.3",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
@ -3234,6 +3236,69 @@
} }
} }
}, },
"node_modules/@radix-ui/react-radio-group": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.3.8.tgz",
"integrity": "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.3",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-direction": "1.1.1",
"@radix-ui/react-presence": "1.1.5",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-roving-focus": "1.1.11",
"@radix-ui/react-use-controllable-state": "1.2.2",
"@radix-ui/react-use-previous": "1.1.1",
"@radix-ui/react-use-size": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-roving-focus": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz",
"integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.3",
"@radix-ui/react-collection": "1.1.7",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-direction": "1.1.1",
"@radix-ui/react-id": "1.1.1",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-callback-ref": "1.1.1",
"@radix-ui/react-use-controllable-state": "1.2.2"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-select": { "node_modules/@radix-ui/react-select": {
"version": "2.2.6", "version": "2.2.6",
"resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz",
@ -3300,6 +3365,39 @@
} }
} }
}, },
"node_modules/@radix-ui/react-slider": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.3.6.tgz",
"integrity": "sha512-JPYb1GuM1bxfjMRlNLE+BcmBC8onfCi60Blk7OBqi2MLTFdS+8401U4uFjnwkOr49BLmXxLC6JHkvAsx5OJvHw==",
"license": "MIT",
"dependencies": {
"@radix-ui/number": "1.1.1",
"@radix-ui/primitive": "1.1.3",
"@radix-ui/react-collection": "1.1.7",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-direction": "1.1.1",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-controllable-state": "1.2.2",
"@radix-ui/react-use-layout-effect": "1.1.1",
"@radix-ui/react-use-previous": "1.1.1",
"@radix-ui/react-use-size": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-slot": { "node_modules/@radix-ui/react-slot": {
"version": "1.2.3", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",

View File

@ -14,8 +14,10 @@
"@radix-ui/react-label": "^2.1.7", "@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-navigation-menu": "^1.2.14", "@radix-ui/react-navigation-menu": "^1.2.14",
"@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-radio-group": "^1.3.8",
"@radix-ui/react-select": "^2.2.6", "@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-separator": "^1.1.7", "@radix-ui/react-separator": "^1.1.7",
"@radix-ui/react-slider": "^1.3.6",
"@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-slot": "^1.2.3",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",

View File

@ -0,0 +1,15 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_0_7570)">
<g clip-path="url(#clip1_0_7570)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.32572 4.33404C1.91697 4.53362 1.86175 4.55104 1.74999 4.83172C1.63267 5.12629 1.63244 5.126 3.20026 6.81716C3.98551 7.66418 4.62801 8.36673 4.62801 8.37838C4.62801 8.39 4.53026 8.70991 4.41085 9.08923L4.19365 9.77896L3.90631 9.92898C2.9553 10.4256 2.26173 11.2114 1.92877 12.1694C1.81522 12.4962 1.69821 13.115 1.74204 13.157C1.75464 13.1691 1.83083 13.1615 1.91133 13.14C2.13356 13.0809 2.50106 13.093 2.75127 13.1676C2.87468 13.2044 3.11047 13.3198 3.27527 13.4241C3.44007 13.5283 3.66356 13.6383 3.77193 13.6685C4.10074 13.7602 4.57565 13.7968 4.93245 13.758C5.44659 13.7022 5.61059 13.643 6.06757 13.3485C6.34818 13.1677 6.66088 13.0903 7.03153 13.11C7.39983 13.1296 7.66116 13.2231 7.96119 13.4429C8.54653 13.8715 9.93548 13.8802 10.4803 13.4587C10.9059 13.1294 11.494 13.0174 12.0012 13.1691C12.1451 13.2121 12.3313 13.2959 12.4151 13.3553C12.6649 13.5325 12.913 13.6459 13.1858 13.7075C13.6772 13.8184 14.6178 13.7652 14.7912 13.6167C14.8294 13.5839 15.4542 12.3655 16.1797 10.909C17.6096 8.03795 17.5907 8.08477 17.4218 7.82267C17.3796 7.75719 17.2811 7.67404 17.2029 7.63796C17.062 7.57299 17.0517 7.5738 16.1315 7.72232L15.2023 7.87227L13.8716 7.07235C13.1397 6.63241 12.4783 6.25732 12.4018 6.2388C12.3253 6.22033 11.3116 6.15561 10.1491 6.09503L8.03544 5.98485L6.49356 5.08089C5.07864 4.25133 4.93599 4.1761 4.76129 4.16726C4.65657 4.16196 4.01057 4.23701 3.32572 4.33404ZM9.52111 7.09868L12.1105 7.2345L12.8434 7.67218C13.2465 7.91291 13.5679 8.11789 13.5577 8.12772C13.5356 8.1489 5.45253 9.47892 5.43383 9.46443C5.41971 9.45351 5.97451 7.67185 6.04701 7.49521C6.1587 7.22321 6.51854 6.9693 6.79849 6.96499C6.87179 6.96385 8.09697 7.024 9.52111 7.09868ZM12.1441 10.0472C12.4055 10.1519 12.5357 10.4878 12.4074 10.7262C12.1447 11.214 11.4066 11.0562 11.4062 10.5121C11.4058 10.1315 11.7746 9.89911 12.1441 10.0472ZM9.70279 10.4298C9.8131 10.4906 9.95963 10.7358 9.95818 10.8571C9.95677 10.9767 9.81782 11.2299 9.72849 11.2758C9.33681 11.4768 8.94106 11.3051 8.90218 10.9173C8.85714 10.4675 9.29142 10.2031 9.70279 10.4298ZM7.18316 10.7733C7.52511 10.9924 7.51719 11.4371 7.1677 11.6415C7.00987 11.7338 6.82519 11.732 6.64793 11.6365C6.46759 11.5393 6.37963 11.3963 6.37963 11.2004C6.37963 10.7987 6.84049 10.5537 7.18316 10.7733ZM1.93601 14.1925C1.69863 14.3355 1.60625 14.5913 1.7078 14.8245C1.81697 15.075 2.43693 15.4479 3.05163 15.6327C4.22449 15.9853 5.85293 15.862 6.69275 15.3571L6.87263 15.2489L6.98641 15.3266C7.14573 15.4354 7.60031 15.6102 7.93994 15.6931C9.0548 15.9655 10.5432 15.8253 11.3228 15.3745C11.4891 15.2783 11.5525 15.2584 11.5964 15.2885C12.2271 15.7218 13.5283 15.9357 14.6243 15.7862C15.4879 15.6685 16.3827 15.2739 16.6504 14.8929C16.8835 14.5612 16.6232 14.1232 16.1928 14.1232C16.0535 14.1232 15.9853 14.1481 15.8462 14.2499C15.5821 14.4433 15.2387 14.5957 14.836 14.6982C14.5178 14.7792 14.3937 14.7911 13.8621 14.7911C13.3305 14.7911 13.2063 14.7792 12.8881 14.6982C12.5 14.5994 12.1991 14.4686 11.8687 14.2549C11.577 14.0664 11.414 14.0794 11.0893 14.3171C10.2113 14.9598 8.33101 14.9665 7.33925 14.3303C6.95404 14.0832 6.81235 14.0695 6.53194 14.2523C6.25207 14.4348 5.84089 14.6195 5.56105 14.6884C4.52124 14.9447 3.30676 14.7782 2.60311 14.2828C2.36295 14.1137 2.12103 14.0809 1.93601 14.1925Z" fill="#BEBEBE"/>
</g>
</g>
<defs>
<clipPath id="clip0_0_7570">
<rect width="20" height="20" fill="white"/>
</clipPath>
<clipPath id="clip1_0_7570">
<rect width="20" height="20" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -0,0 +1,15 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_0_7564)">
<g clip-path="url(#clip1_0_7564)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.76247 3.39857C8.37417 3.59311 8.35863 4.10823 8.73457 4.32429C8.8486 4.38984 8.92534 4.39466 9.86315 4.39476C10.9457 4.39491 10.9783 4.39013 11.1636 4.20309C11.3003 4.06519 11.3176 3.74896 11.1975 3.5844C11.0221 3.34409 10.9635 3.33301 9.87066 3.33301C8.99153 3.33301 8.88017 3.3396 8.76247 3.39857ZM5.95734 4.99537C5.63344 5.13398 5.56454 5.4328 5.75692 5.86453C6.03521 6.4891 6.18077 7.17611 6.18055 7.86375C6.18032 8.5623 6.05811 9.00479 5.74652 9.435C5.55241 9.70298 5.64673 10.0656 5.94792 10.209C6.06983 10.2671 6.27903 10.2706 9.07008 10.2621C12.0294 10.2531 12.0627 10.2522 12.1657 10.1797C12.2229 10.1394 12.3272 10.0279 12.3973 9.93193L12.5248 9.75746L14.546 9.73976C16.5136 9.72252 16.5698 9.72012 16.6707 9.64908C16.9511 9.45167 16.9993 9.25529 16.8534 8.90533C16.6998 8.53688 16.335 7.94729 16.0019 7.52901C14.9451 6.20203 13.5025 5.34703 11.762 5.01601L11.288 4.92585L8.70202 4.92663C6.23322 4.92737 6.10891 4.93049 5.95734 4.99537ZM11.2627 6.14416C11.4477 6.32613 11.663 6.72133 11.7561 7.04963C11.8506 7.38314 11.8497 8.35134 11.7545 8.69557C11.7168 8.83184 11.6576 8.99909 11.6227 9.06723L11.5595 9.19112L9.31652 9.20025C8.03867 9.20545 7.07353 9.19593 7.07353 9.17816C7.07353 9.16096 7.09981 9.07721 7.13195 8.99205C7.37231 8.35449 7.38369 7.4912 7.16378 6.57178C7.11253 6.35764 7.06072 6.13864 7.04863 6.08508L7.02659 5.98774H9.06516H11.1038L11.2627 6.14416ZM13.2058 6.56793C13.8678 6.88504 14.3781 7.27069 14.8074 7.77834C15.0161 8.02512 15.3711 8.56828 15.3711 8.64081C15.3711 8.66587 14.9783 8.67787 14.1602 8.67787H12.9494L12.9478 7.908C12.9463 7.18942 12.9398 7.11689 12.8496 6.81956C12.7964 6.64435 12.7404 6.47313 12.7251 6.43905C12.6792 6.33679 12.7794 6.36369 13.2058 6.56793ZM1.66699 10.261V10.7824L1.94708 11.0486L2.22717 11.3149L2.75472 12.6928C3.04491 13.4506 3.28632 14.0729 3.29128 14.0757C3.4044 14.1387 4.18287 14.3949 4.42996 14.4504C5.08062 14.5965 5.72174 14.5169 6.50036 14.1932C6.86582 14.0413 6.86853 14.0408 7.31757 14.0407L7.76811 14.0405L8.18111 14.2048C9.24894 14.6294 9.92874 14.6294 10.997 14.2049C11.4077 14.0417 11.4124 14.0407 11.8418 14.0409C12.27 14.041 12.2772 14.0424 12.7109 14.2087C13.5715 14.5385 14.1337 14.5994 14.8079 14.4357C15.004 14.3881 15.2659 14.3105 15.3898 14.2634L15.615 14.1776L16.5577 12.233L17.5003 10.2884L16.0133 10.2779C15.0135 10.2709 14.453 10.2806 14.3025 10.3077C14.0069 10.3609 13.7848 10.4726 13.0432 10.9411L12.4237 11.3325H9.02187L5.61999 11.3326L5.27202 10.838C4.90881 10.3217 4.64119 10.047 4.37803 9.92049C4.04564 9.76061 3.85532 9.73976 2.72915 9.73976H1.66699V10.261ZM2.48916 15.1649C2.16345 15.37 2.14645 15.772 2.45315 16.0153C2.60491 16.1357 3.27773 16.3802 3.79957 16.5045C4.90194 16.7672 5.90474 16.7066 6.98717 16.3119L7.33259 16.186L7.65582 16.3089C8.08248 16.4711 8.64634 16.5973 9.16668 16.6473C9.88868 16.7165 10.7924 16.5863 11.5264 16.3073L11.8538 16.1828L12.1875 16.3087C12.9494 16.5961 13.7868 16.7172 14.5244 16.6467C15.298 16.5727 16.4592 16.2338 16.7427 15.9991C17.1322 15.6767 16.902 15.0846 16.3871 15.0846C16.2997 15.0846 16.1027 15.1364 15.9217 15.207C15.143 15.5109 14.3526 15.6466 13.758 15.5784C13.3416 15.5307 12.7405 15.3771 12.3049 15.207C11.9658 15.0747 11.7517 15.0535 11.5835 15.1355C11.5261 15.1634 11.2853 15.2494 11.0483 15.3264C9.94327 15.6856 9.23486 15.6856 8.12983 15.3264C7.89288 15.2494 7.65202 15.1634 7.59462 15.1355C7.42641 15.0535 7.21237 15.0747 6.87322 15.207C6.43758 15.3771 5.83656 15.5307 5.42018 15.5784C4.8255 15.6466 4.03509 15.5109 3.25648 15.207C2.88012 15.0602 2.67351 15.0488 2.48916 15.1649Z" fill="#BEBEBE"/>
</g>
</g>
<defs>
<clipPath id="clip0_0_7564">
<rect width="20" height="20" fill="white"/>
</clipPath>
<clipPath id="clip1_0_7564">
<rect width="20" height="20" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -0,0 +1,15 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_0_7499)">
<g clip-path="url(#clip1_0_7499)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.06084 4.20472C4.79827 4.32378 4.65616 4.65081 4.7601 4.89686C4.78901 4.96534 5.00007 5.32835 5.22909 5.70354L5.64554 6.38569L5.19369 7.74321C4.94517 8.48986 4.74768 9.10601 4.75479 9.11239C4.76803 9.12433 13.0092 6.93226 13.0303 6.91121C13.0367 6.90479 12.4247 6.52824 11.6703 6.07441L10.2987 5.24926L8.42459 5.48101C7.36718 5.6118 6.53791 5.69871 6.52157 5.68049C6.50564 5.66276 6.3146 5.3538 6.09701 4.99391C5.86173 4.60472 5.65656 4.30763 5.59071 4.26077C5.45339 4.16298 5.20973 4.1372 5.06084 4.20472ZM16.0878 7.10365C15.7024 7.20007 12.865 7.89683 9.78245 8.65195C6.69985 9.40711 4.08761 10.0563 3.97743 10.0947C3.45855 10.2752 3.00111 10.6793 2.75859 11.1713C2.56055 11.5731 2.50144 11.8771 2.50063 12.4974L2.5 13.0044L2.78577 13.0047C3.20951 13.005 3.46091 13.0577 3.91578 13.2417C4.97275 13.6692 5.59709 13.6676 6.66117 13.2351C7.08713 13.0619 7.09425 13.0604 7.51475 13.0603C7.93643 13.0601 7.94104 13.0611 8.3444 13.2311C9.39322 13.6733 10.0596 13.6733 11.1099 13.2315C11.5124 13.0622 11.5188 13.0608 11.9395 13.0603C12.3609 13.0598 12.3663 13.0609 12.7876 13.2335C13.5752 13.5562 14.1297 13.6323 14.7375 13.5009L15.0411 13.4353L15.2056 13.1554C15.4655 12.7129 16.0687 11.4896 16.3253 10.8842C16.8126 9.73491 17.4483 7.89797 17.493 7.51032C17.5299 7.19104 17.2795 6.91677 16.958 6.92433C16.8647 6.92651 16.4731 7.00722 16.0878 7.10365ZM12.75 9.19734C13.1313 9.3889 13.1476 9.94045 12.7787 10.1654C12.5513 10.3041 12.2647 10.2441 12.0744 10.0179C11.7042 9.57795 12.2319 8.93702 12.75 9.19734ZM10.5529 9.76939C11.164 10.1198 10.673 11.0344 10.0362 10.7322C9.63433 10.5415 9.63525 9.95951 10.0378 9.76028C10.2314 9.66445 10.3743 9.66699 10.5529 9.76939ZM8.26434 10.2893C8.54587 10.4069 8.67607 10.7108 8.56718 10.996C8.48614 11.2081 8.33215 11.3176 8.08968 11.3355C7.49506 11.3792 7.28237 10.5554 7.82467 10.3092C8.00586 10.2269 8.10435 10.2224 8.26434 10.2893ZM6.15634 10.9069C6.45715 11.1211 6.45859 11.5806 6.15911 11.8036C6.1045 11.8442 5.98311 11.8813 5.87648 11.8898C5.72518 11.902 5.66556 11.8884 5.56386 11.8186C5.3698 11.6854 5.2962 11.563 5.2962 11.3732C5.2962 11.1078 5.45693 10.8807 5.68953 10.8175C5.81449 10.7835 6.04517 10.8277 6.15634 10.9069ZM2.75435 14.2312C2.43448 14.4449 2.41777 14.8636 2.71899 15.1171C2.86803 15.2425 3.5288 15.4971 4.0413 15.6267C5.12393 15.9003 6.10878 15.8372 7.17183 15.4261L7.51106 15.2949L7.8285 15.4229C8.24753 15.5918 8.80129 15.7234 9.31232 15.7754C10.0214 15.8476 10.909 15.7119 11.6298 15.4213L11.9514 15.2916L12.2791 15.4227C13.0273 15.7221 13.8497 15.8483 14.5741 15.7748C15.3339 15.6978 16.4743 15.3447 16.7527 15.1002C17.1353 14.7643 16.9092 14.1475 16.4034 14.1475C16.3176 14.1475 16.1241 14.2015 15.9464 14.2751C15.1817 14.5916 14.4054 14.7329 13.8214 14.6619C13.4125 14.6122 12.8222 14.4522 12.3944 14.2751C12.0613 14.1372 11.8511 14.1151 11.6859 14.2005C11.6295 14.2297 11.393 14.3192 11.1603 14.3994C10.075 14.7736 9.37928 14.7736 8.29403 14.3994C8.06132 14.3192 7.82478 14.2297 7.7684 14.2005C7.60321 14.1151 7.39299 14.1372 7.05992 14.2751C6.63208 14.4522 6.04181 14.6122 5.63289 14.6619C5.04886 14.7329 4.2726 14.5916 3.50793 14.2751C3.13831 14.1221 2.9354 14.1103 2.75435 14.2312Z" fill="#BEBEBE"/>
</g>
</g>
<defs>
<clipPath id="clip0_0_7499">
<rect width="20" height="20" fill="white"/>
</clipPath>
<clipPath id="clip1_0_7499">
<rect width="20" height="20" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -0,0 +1,15 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_0_7493)">
<g clip-path="url(#clip1_0_7493)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.1134 4.38259C10.1817 4.77494 9.96993 5.95625 10.71 6.63268C11.2484 7.1247 12.0617 7.1018 12.576 6.58011C12.6685 6.48619 12.7885 6.31904 12.8426 6.20864C12.9296 6.03093 12.9409 5.96651 12.9412 5.6478C12.9416 5.33204 12.9297 5.26236 12.8451 5.08181C12.717 4.80846 12.469 4.54978 12.2134 4.42288C12.0374 4.33552 11.9527 4.3177 11.6633 4.30733C11.3652 4.29663 11.2945 4.30632 11.1134 4.38259ZM8.68902 7.05164C8.2478 7.14764 7.86996 7.41065 7.23134 8.06634C6.81025 8.4987 6.65545 8.68818 6.39032 9.09573C5.91651 9.82404 5.61947 10.6621 5.54677 11.4759L5.52376 11.7333L4.7078 11.7513L3.89184 11.7693L3.75734 11.8783C3.54687 12.0488 3.48926 12.189 3.48926 12.5308C3.48926 13.4755 3.92641 14.4771 4.74831 15.4155C5.05813 15.7693 5.56917 16.2169 5.8553 16.3851L6.02789 16.4865H8.70292H11.378L12.4507 14.0739C13.0408 12.747 13.618 11.4466 13.7335 11.1842C13.849 10.9218 13.95 10.7071 13.958 10.7071C13.966 10.7071 14.005 10.7244 14.0447 10.7457C14.1495 10.8018 14.4851 10.7929 14.6038 10.7309C14.7202 10.6701 14.8751 10.5122 14.9378 10.3905C15.0423 10.1875 14.9635 9.79805 14.785 9.63564C14.7416 9.59611 13.6076 9.01193 12.265 8.33749C9.69291 7.04545 9.57347 6.99475 9.11756 7.00098C9.00262 7.00256 8.80979 7.02536 8.68902 7.05164ZM11.6614 9.55383L12.7393 10.0931L12.3708 10.9132L12.0022 11.7333L10.4963 11.7427L8.99034 11.7521L9.01562 11.5807C9.1015 10.998 9.52259 10.2111 10.1354 9.48794C10.2925 9.30263 10.4453 9.12093 10.475 9.08417C10.5047 9.0474 10.5412 9.01672 10.5562 9.01597C10.5712 9.01525 11.0686 9.25726 11.6614 9.55383ZM14.9451 11.8144C14.8565 11.9808 12.8876 16.4381 12.8876 16.4722C12.8876 16.4998 13.1266 16.5075 13.6888 16.4981C14.6134 16.4826 14.5741 16.4941 15.0727 16.0925C16.2872 15.1143 17.0698 13.6603 17.023 12.4686C17.0117 12.1813 17.0012 12.1424 16.9017 12.0195C16.6948 11.764 16.6411 11.7513 15.7611 11.7513C15.0527 11.7513 14.9755 11.7573 14.9451 11.8144Z" fill="#BEBEBE"/>
</g>
</g>
<defs>
<clipPath id="clip0_0_7493">
<rect width="20" height="20" fill="white"/>
</clipPath>
<clipPath id="clip1_0_7493">
<rect width="20" height="20" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,15 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_0_7514)">
<g clip-path="url(#clip1_0_7514)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.64813 3.38145C9.43689 3.44128 9.20283 3.57193 9.03366 3.72446L8.90796 3.83783H8.18606C7.3798 3.83783 7.31028 3.85405 7.16653 4.0759C7.04902 4.25727 7.06972 4.39857 7.32729 5.17292L7.56207 5.87869H9.99992H12.4378L12.6725 5.17292C12.9301 4.39857 12.9508 4.25727 12.8333 4.0759C12.6896 3.85409 12.6199 3.83783 11.8144 3.83783H11.0932L10.9421 3.70544C10.5662 3.37614 10.0888 3.25658 9.64813 3.38145ZM7.09384 7.4009C6.13707 8.41975 5.46287 9.39254 4.91889 10.539C4.01705 12.4398 3.92108 14.2513 4.66012 15.4242C5.03297 16.0159 5.64863 16.4783 6.23914 16.61C6.57553 16.6851 13.4243 16.6851 13.7607 16.61C14.3512 16.4783 14.9669 16.0159 15.3397 15.4242C16.0798 14.2496 15.9841 12.4478 15.0802 10.5387C14.5416 9.40114 13.8801 8.44638 12.906 7.40056L12.4701 6.93248L10.0016 6.93285L7.53308 6.93319L7.09384 7.4009ZM10.4843 9.1229V9.54384L10.5816 9.56435C10.8908 9.62949 11.3056 9.95586 11.4597 10.2551C11.5414 10.4137 11.6232 10.7562 11.5883 10.793C11.579 10.8028 11.394 10.8109 11.1771 10.8109H10.7829L10.7624 10.7026C10.7353 10.5599 10.5029 10.3275 10.3341 10.2742C10.1734 10.2235 9.86492 10.2205 9.70225 10.2681C9.44693 10.3428 9.27015 10.5874 9.29985 10.8249C9.33282 11.0884 9.56272 11.2178 10.2744 11.3735C11.2651 11.5902 11.6219 11.8914 11.6691 12.5508C11.7195 13.2558 11.3551 13.7804 10.669 13.9905L10.4843 14.0471V14.4529V14.8586H9.99992H9.51558V14.4529V14.0471L9.32992 13.9906C8.77257 13.8212 8.40583 13.423 8.33569 12.9113L8.31354 12.7497L8.72888 12.7502L9.14425 12.7507L9.22498 12.9253C9.29001 13.066 9.34015 13.1203 9.48306 13.2049C9.64241 13.2992 9.69324 13.311 9.98355 13.3214C10.2857 13.3322 10.3182 13.3267 10.4842 13.2369C10.7715 13.0816 10.8793 12.8254 10.7599 12.582C10.6701 12.399 10.4919 12.3217 9.7739 12.1537C8.93253 11.957 8.61974 11.7374 8.47728 11.2432C8.34112 10.771 8.4653 10.2445 8.78636 9.93276C8.93951 9.78405 9.24022 9.60972 9.41064 9.57088L9.51558 9.54693V9.12447V8.70197H9.99992H10.4843V9.1229Z" fill="#BEBEBE"/>
</g>
</g>
<defs>
<clipPath id="clip0_0_7514">
<rect width="20" height="20" fill="white"/>
</clipPath>
<clipPath id="clip1_0_7514">
<rect width="20" height="20" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,16 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_0_7507)">
<g clip-path="url(#clip1_0_7507)">
<path d="M9.99968 16.6663C13.6816 16.6663 16.6663 13.6816 16.6663 9.99968C16.6663 6.31778 13.6816 3.33301 9.99968 3.33301C6.31778 3.33301 3.33301 6.31778 3.33301 9.99968C3.33301 13.6816 6.31778 16.6663 9.99968 16.6663Z" fill="#BEBEBE"/>
<path d="M7.65898 11.6419L8.65351 11.5375C8.70572 12.0347 9.43144 12.3868 10.2419 12.3017C11.0525 12.2166 11.6893 11.7215 11.6371 11.2242C11.5839 10.7178 11.0474 10.5879 9.92785 10.4587C8.84803 10.3253 7.50334 10.1593 7.36896 8.87925C7.28244 8.05508 7.93996 7.27848 8.92476 6.93769L8.81939 5.93395L10.3111 5.77734L10.4165 6.78108C11.4506 6.90993 12.2551 7.53305 12.3416 8.35723L11.3471 8.46163C11.2949 7.96437 10.5691 7.61228 9.75863 7.69737C8.94808 7.78246 8.3113 8.27758 8.3635 8.77485C8.41667 9.28132 8.95316 9.41121 10.0727 9.54039C11.1525 9.67375 12.4972 9.83982 12.6316 11.1198C12.7181 11.944 12.0606 12.7206 11.0758 13.0614L11.1812 14.0651L9.68943 14.2217L9.58406 13.218C8.54995 13.0892 7.7455 12.466 7.65898 11.6419Z" fill="white"/>
</g>
</g>
<defs>
<clipPath id="clip0_0_7507">
<rect width="20" height="20" fill="white"/>
</clipPath>
<clipPath id="clip1_0_7507">
<rect width="20" height="20" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,263 @@
"use client";
import { useState } from "react";
import { Slider } from "@/components/ui/slider";
import { Checkbox } from "@/components/ui/checkbox";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Label } from "@/components/ui/label";
import { Button } from "@/components/ui/button";
import { DatePicker } from "@/components/ui/date-picker";
import { GuestPicker } from "@/components/form/guest-picker";
import Icon from "@/components/ui/icon";
export default function CatalogSidebar() {
const [lengthRange, setLengthRange] = useState([7, 50]);
const [priceRange, setPriceRange] = useState([3000, 200000]);
const [yearRange, setYearRange] = useState([1991, 2025]);
const [adults, setAdults] = useState<number>(0);
const [children, setChildren] = useState<number>(0);
const [paymentType, setPaymentType] = useState("all");
const [quickBooking, setQuickBooking] = useState(false);
const [hasToilet, setHasToilet] = useState(false);
const [vesselType, setVesselType] = useState("");
const formatPrice = (value: number) => {
return new Intl.NumberFormat("ru-RU").format(value) + " Р";
};
const handleReset = () => {
setLengthRange([7, 50]);
setPriceRange([3000, 200000]);
setYearRange([1991, 2025]);
setAdults(0);
setChildren(0);
setPaymentType("all");
setQuickBooking(false);
setHasToilet(false);
setVesselType("");
};
const activeFiltersCount = [
lengthRange[0] !== 7 || lengthRange[1] !== 50,
priceRange[0] !== 3000 || priceRange[1] !== 200000,
yearRange[0] !== 1991 || yearRange[1] !== 2025,
adults !== 0 || children !== 0,
paymentType !== "all",
quickBooking,
hasToilet,
vesselType !== "",
].filter(Boolean).length;
return (
<aside className="w-full md:w-[260px] bg-white p-6 rounded-[16px]">
<div className="flex items-center justify-between mb-4">
<h2 className="text-md font-bold text-[#333333]">Искать</h2>
<button
onClick={handleReset}
className="text-xs text-[#333333] hover:text-gray-900"
>
Сбросить все{" "}
<span className="text-[#999999]">
({activeFiltersCount})
</span>
</button>
</div>
<div className="space-y-4">
{/* Тип судна */}
<div>
<Label className="text-md font-bold text-[#333333] mb-4 block">
Тип судна
</Label>
<input
type="text"
placeholder="Искать"
value={vesselType}
onChange={(e) => setVesselType(e.target.value)}
className="w-full h-12 px-3 border border-gray-300 rounded-full text-sm focus:outline-none focus:ring-2 focus:ring-[#008299]"
/>
</div>
{/* Длина катеров/яхт */}
<div>
<Label className="text-md font-bold text-[#333333] mb-5 block">
Длина катеров/яхт
</Label>
<div className="px-0">
<Slider
value={lengthRange}
onValueChange={setLengthRange}
min={7}
max={50}
step={1}
className="mb-2 [&_[role=slider]]:bg-[#008299] [&_[role=slider]]:border-[#008299] [&>div>div]:bg-[#008299]"
/>
<div className="flex justify-between text-xs text-[#333333]">
<div className="flex items-center gap-1">
<Icon name="lengthMin" size={20} />
{lengthRange[0]} m
</div>
<div className="flex items-center gap-1">
{lengthRange[1]} m
<Icon name="lengthMax" size={20} />
</div>
</div>
</div>
</div>
{/* Дата */}
<div>
<DatePicker variant="small" placeholder="Дата" />
</div>
{/* Гостей */}
<div>
<GuestPicker
variant="small"
adults={adults}
childrenCount={children}
onChange={(adults, children) => {
setAdults(adults);
setChildren(children);
}}
/>
</div>
{/* Быстрая бронь */}
<div className="flex items-center space-x-2">
<Checkbox
id="quick-booking"
variant="large"
checked={quickBooking}
onCheckedChange={(checked) =>
setQuickBooking(checked === true)
}
/>
<Label
htmlFor="quick-booking"
className="text-sm font-medium text-[#333333] cursor-pointer flex items-center gap-1"
>
Быстрая бронь
</Label>
</div>
{/* Цена за 1 час */}
<div>
<Label className="text-md font-bold text-[#333333] mb-4 block">
Цена за 1 час
</Label>
<div>
<Slider
value={priceRange}
onValueChange={setPriceRange}
min={3000}
max={200000}
step={1000}
className="mb-2 [&_[role=slider]]:bg-[#008299] [&_[role=slider]]:border-[#008299] [&>div>div]:bg-[#008299]"
/>
<div className="flex justify-between text-xs text-[#333333]">
<div className="flex items-center gap-1">
<Icon name="priceMin" size={20} />
{formatPrice(priceRange[0])}
</div>
<div className="flex items-center gap-1">
{formatPrice(priceRange[1])}
<Icon name="priceMax" size={20} />
</div>
</div>
</div>
</div>
{/* Тип оплаты */}
<div>
<Label className="text-md font-bold text-[#333333] mb-4 block">
Тип оплаты
</Label>
<RadioGroup
value={paymentType}
onValueChange={setPaymentType}
className="space-y-2"
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="all" id="all" />
<Label
htmlFor="all"
className="text-sm font-normal text-black cursor-pointer"
>
Все типы оплаты
</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="partial" id="partial" />
<Label
htmlFor="partial"
className="text-sm font-normal text-black cursor-pointer"
>
Частичная оплата
</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="full" id="full" />
<Label
htmlFor="full"
className="text-sm font-normal text-black cursor-pointer"
>
Полная оплата
</Label>
</div>
</RadioGroup>
</div>
{/* Год */}
<div>
<Label className="text-md font-bold text-[#333333] mb-4 block">
Год
</Label>
<div>
<Slider
value={yearRange}
onValueChange={setYearRange}
min={1991}
max={2025}
step={1}
className="mb-2 [&_[role=slider]]:bg-[#008299] [&_[role=slider]]:border-[#008299] [&>div>div]:bg-[#008299]"
/>
<div className="flex justify-between text-xs text-[#333333]">
<div className="text-s flex items-center gap-1">
<Icon name="boatYearMin" size={20} />
{yearRange[0]}
</div>
<div className="flex items-center gap-1">
{yearRange[1]}
<Icon name="boatYearMax" size={20} />
</div>
</div>
</div>
</div>
{/* Гальюн */}
<div className="flex items-center space-x-2">
<Checkbox
id="toilet"
variant="large"
checked={hasToilet}
onCheckedChange={(checked) =>
setHasToilet(checked === true)
}
/>
<Label
htmlFor="toilet"
className="text-sm font-medium text-[#333333] cursor-pointer"
>
Гальюн
</Label>
</div>
{/* Кнопка Применить */}
<Button className="w-full bg-[#008299] hover:bg-[#006d7f] text-white font-bold h-12 mt-2">
Применить
</Button>
</div>
</aside>
);
}

263
src/app/catalog/page.tsx Normal file
View File

@ -0,0 +1,263 @@
"use client";
import { Card, CardContent, CardHeader } from "@/components/ui/card";
import Image from "next/image";
import Icon from "@/components/ui/icon";
import CatalogSidebar from "@/app/catalog/components/CatalogSidebar";
import Link from "next/link";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
const yachts = [
{
name: "Яхта",
length: "12 метров",
price: "от 12 500 ₽ / час",
feet: "7 Футов",
img: "/images/yachts/yacht1.jpg",
quickBooking: true,
},
{
name: "Яхта",
length: "12 метров",
price: "от 13 200 ₽ / час",
feet: "7 Футов",
img: "/images/yachts/yacht2.jpg",
badge: "По запросу",
},
{
name: "Яхта",
length: "8 метров",
price: "от 7 000 ₽ / час",
feet: "7 Футов",
img: "/images/yachts/yacht3.jpg",
badge: "По запросу",
},
{
name: "Яхта",
length: "13 метров",
price: "от 12 000 ₽ / час",
feet: "7 Футов",
img: "/images/yachts/yacht4.jpg",
badge: "По запросу",
},
{
name: "Яхта",
length: "13 метров",
price: "от 30 000 ₽ / час",
feet: "7 Футов",
img: "/images/yachts/yacht5.jpg",
badge: "По запросу",
},
{
name: "Яхта",
length: "10 метров",
price: "от 8 500 ₽ / час",
feet: "7 Футов",
img: "/images/yachts/yacht6.jpg",
badge: "По запросу",
},
{
name: "Яхта",
length: "8 метров",
price: "от 6 000 ₽ / час",
feet: "7 Футов",
img: "/images/yachts/yacht1.jpg",
badge: "По запросу",
},
{
name: "Яхта",
length: "10 метров",
price: "от 6 960 ₽ / час",
feet: "7 Футов",
img: "/images/yachts/yacht2.jpg",
badge: "По запросу",
},
{
name: "Яхта",
length: "10 метров",
price: "от 6 600 ₽ / час",
feet: "7 Футов",
img: "/images/yachts/yacht3.jpg",
badge: "По запросу",
},
{
name: "Яхта",
length: "13 метров",
price: "от 18 000 ₽ / час",
feet: "7 Футов",
img: "/images/yachts/yacht4.jpg",
badge: "По запросу",
},
{
name: "Яхта",
length: "10 метров",
price: "от 7 200 ₽ / час",
feet: "7 Футов",
img: "/images/yachts/yacht5.jpg",
badge: "По запросу",
},
{
name: "Яхта",
length: "11 метров",
price: "от 7 200 ₽ / час",
feet: "7 Футов",
img: "/images/yachts/yacht6.jpg",
badge: "По запросу",
},
];
export default function CatalogPage() {
return (
<main className="bg-[#f4f4f4] min-h-screen">
<div className="container max-w-6xl mx-auto px-4 py-6">
{/* Breadcrumbs */}
<div className="mb-4 text-sm text-[#999999] flex items-center gap-[16px]">
<Link href="/">
<span className="cursor-pointer hover:text-[#333333] transition-colors">
Аренда яхты
</span>
</Link>
<span>&gt;</span>
<span className="text-[#333333]">Каталог яхт</span>
</div>
<div className="flex flex-col lg:flex-row gap-[74px]">
{/* Sidebar */}
<div className="w-full lg:w-[260px] flex-shrink-0">
<CatalogSidebar />
</div>
{/* Main Content */}
<div className="flex-1">
{/* Header */}
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-end mb-6 gap-4">
<div>
<h1 className="text-2xl md:text-3xl font-bold text-[#333333] mb-4">
Аренда яхт
</h1>
<p className="text-lg text-[#333333]">
Доступно яхт:{" "}
<span className="text-[#b2b2b2]">
{yachts.length}
</span>
</p>
</div>
<div className="flex items-center gap-1 w-full sm:w-auto">
<div className="text-base text-[#999999]">Сортировка:</div>
<Select defaultValue="default">
<SelectTrigger
className="w-full"
variant="ghost"
>
<SelectValue placeholder="Сортировка" />
</SelectTrigger>
<SelectContent>
<SelectItem value="default">
По умолчанию
</SelectItem>
<SelectItem value="price-asc">
Цена: по возрастанию
</SelectItem>
<SelectItem value="price-desc">
Цена: по убыванию
</SelectItem>
<SelectItem value="length-asc">
Длина: по возрастанию
</SelectItem>
<SelectItem value="length-desc">
Длина: по убыванию
</SelectItem>
</SelectContent>
</Select>
</div>
</div>
{/* Yacht Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{yachts.map((yacht, idx) => (
<div key={idx} className="relative">
<Card className="overflow-hidden bg-white text-gray-900 border border-gray-200">
<CardHeader className="p-0 relative">
<div className="relative">
{/* Quick Booking Badge */}
{yacht.quickBooking && (
<div className="absolute top-3 left-3 z-10">
<div className="bg-[#008299] text-white px-3 py-1 rounded-lg text-sm font-medium">
Быстрая бронь
</div>
</div>
)}
<Image
src={yacht.img}
alt={yacht.name}
width={400}
height={250}
className="w-full h-48 object-cover"
/>
{/* Badge Overlay */}
{yacht.badge && (
<div className="absolute top-3 left-3">
<div className="flex items-center justify-center bg-black/40 text-white px-3 py-1 rounded-lg text-sm gap-1">
<Icon
size={16}
name="restart"
/>
<span>
{yacht.badge}
</span>
</div>
</div>
)}
</div>
</CardHeader>
<CardContent className="p-4">
<div className="flex justify-between gap-4">
{/* Левая колонка - название и длина */}
<div className="space-y-2">
<h3 className="font-bold text-lg">
{yacht.name}
</h3>
<div className="flex items-center gap-1 text-sm text-gray-600">
<Icon
size={16}
name="width"
/>
<span>
{yacht.length}
</span>
</div>
</div>
{/* Правая колонка - цена и футы */}
<div className="space-y-2 text-right">
<p className="text-lg font-bold text-black">
{yacht.price}
</p>
<div className="flex items-center gap-1 text-sm text-gray-600 justify-end">
<Icon
size={16}
name="anchor"
/>
<span>
{yacht.feet}
</span>
</div>
</div>
</div>
</CardContent>
</Card>
</div>
))}
</div>
</div>
</div>
</div>
</main>
);
}

View File

@ -1,5 +1,6 @@
"use client"; "use client";
import { useState } from "react";
import Image from "next/image"; import Image from "next/image";
import { Card, CardContent } from "@/components/ui/card"; import { Card, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
@ -8,6 +9,8 @@ import Icon from "@/components/ui/icon";
import { GuestPicker } from "@/components/form/guest-picker"; import { GuestPicker } from "@/components/form/guest-picker";
export default function Hero() { export default function Hero() {
const [adults, setAdults] = useState<number>(0);
const [children, setChildren] = useState<number>(0);
return ( return (
<section className="relative h-[600px] rounded-[24px] mx-[16px] overflow-hidden flex text-white"> <section className="relative h-[600px] rounded-[24px] mx-[16px] overflow-hidden flex text-white">
<Image <Image
@ -62,7 +65,14 @@ export default function Hero() {
{/* Количество гостей */} {/* Количество гостей */}
<div className="flex-1"> <div className="flex-1">
<GuestPicker /> <GuestPicker
adults={adults}
childrenCount={children}
onChange={(adults, children) => {
setAdults(adults);
setChildren(children);
}}
/>
</div> </div>
{/* Кнопка поиска */} {/* Кнопка поиска */}

View File

@ -2,6 +2,7 @@ import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import Image from "next/image"; import Image from "next/image";
import Icon from "@/components/ui/icon"; import Icon from "@/components/ui/icon";
import Link from "next/link";
import FeaturedYacht from "./FeaturedYacht"; import FeaturedYacht from "./FeaturedYacht";
const yachts = [ const yachts = [
@ -174,12 +175,14 @@ export default function YachtGrid() {
{/* Call to Action Button */} {/* Call to Action Button */}
<div className="text-center"> <div className="text-center">
<Button <Link href="/catalog">
size="lg" <Button
className="bg-white text-gray-900 hover:bg-gray-100 px-8 py-3 h-[56px] w-[336px] text-lg font-bold" size="lg"
> className="bg-white text-gray-900 hover:bg-gray-100 px-8 py-3 h-[56px] w-[336px] text-lg font-bold"
Каталог яхт >
</Button> Каталог яхт
</Button>
</Link>
</div> </div>
</div> </div>
</section> </section>

View File

@ -12,29 +12,47 @@ import { Counter } from "../ui/counter";
import { ChevronUp, ChevronDown } from "lucide-react"; import { ChevronUp, ChevronDown } from "lucide-react";
interface GuestPickerProps { interface GuestPickerProps {
adults?: number;
childrenCount?: number;
onChange?: (adults: number, children: number) => void;
onApply?: (adults: number, children: number) => void; onApply?: (adults: number, children: number) => void;
className?: string; className?: string;
showIcon?: boolean; showIcon?: boolean;
variant?: "default" | "small";
placeholder?: string;
} }
export const GuestPicker: React.FC<GuestPickerProps> = ({ onApply, showIcon = true }) => { export const GuestPicker: React.FC<GuestPickerProps> = ({
const [adults, setAdults] = useState<number>(0); adults = 0,
const [children, setChildren] = useState<number>(0); childrenCount = 0,
onChange,
showIcon = true,
variant = "default",
placeholder = "Сколько гостей?"
}) => {
const [isOpen, setIsOpen] = useState<boolean>(false); const [isOpen, setIsOpen] = useState<boolean>(false);
const handleAdultsChange = (value: number) => {
onChange?.(value, childrenCount);
};
const handleChildrenChange = (value: number) => {
onChange?.(adults, value);
};
const handleApply = () => { const handleApply = () => {
onApply?.(adults, children); onChange?.(adults, childrenCount);
setIsOpen(false); setIsOpen(false);
}; };
const getDisplayText = () => { const getDisplayText = () => {
const total = adults + children; const total = adults + childrenCount;
if (total === 0) return "Сколько гостей?"; if (total === 0) return placeholder;
if (children === 0) if (childrenCount === 0)
return `${adults} ${adults === 1 ? "взрослый" : "взрослых"}`; return `${adults} ${adults === 1 ? "взрослый" : "взрослых"}`;
return `${adults} ${ return `${adults} ${
adults === 1 ? "взрослый" : "взрослых" adults === 1 ? "взрослый" : "взрослых"
}, ${children} ${children === 1 ? "ребенок" : "детей"}`; }, ${childrenCount} ${childrenCount === 1 ? "ребенок" : "детей"}`;
}; };
return ( return (
@ -46,7 +64,7 @@ export const GuestPicker: React.FC<GuestPickerProps> = ({ onApply, showIcon = tr
<PopoverTrigger asChild> <PopoverTrigger asChild>
<Button <Button
variant="outline" variant="outline"
className="h-[64px] px-4 w-full justify-between" className={`${variant === "small" ? "h-[48px]" : "h-[64px]"} px-4 w-full justify-between`}
> >
<div className="flex items-center"> <div className="flex items-center">
{showIcon && ( {showIcon && (
@ -69,7 +87,7 @@ export const GuestPicker: React.FC<GuestPickerProps> = ({ onApply, showIcon = tr
<Counter <Counter
label="Взрослые" label="Взрослые"
value={adults} value={adults}
onChange={setAdults} onChange={handleAdultsChange}
min={0} min={0}
max={10} max={10}
/> />
@ -77,8 +95,8 @@ export const GuestPicker: React.FC<GuestPickerProps> = ({ onApply, showIcon = tr
<div className="mb-4"> <div className="mb-4">
<Counter <Counter
label="Дети" label="Дети"
value={children} value={childrenCount}
onChange={setChildren} onChange={handleChildrenChange}
min={0} min={0}
max={10} max={10}
/> />

View File

@ -6,14 +6,21 @@ import { Check } from "lucide-react"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
interface CheckboxProps
extends React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root> {
variant?: "default" | "large"
}
const Checkbox = React.forwardRef< const Checkbox = React.forwardRef<
React.ElementRef<typeof CheckboxPrimitive.Root>, React.ElementRef<typeof CheckboxPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root> CheckboxProps
>(({ className, ...props }, ref) => ( >(({ className, variant = "default", ...props }, ref) => (
<CheckboxPrimitive.Root <CheckboxPrimitive.Root
ref={ref} ref={ref}
className={cn( className={cn(
"grid place-content-center peer h-5 w-5 shrink-0 rounded-[3px] border border-gray-400 shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-[#008299] data-[state=checked]:text-primary-foreground", "grid place-content-center peer shrink-0 border shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-[#008299] data-[state=checked]:text-primary-foreground",
variant === "default" && "h-5 w-5 rounded-[3px] border-gray-400",
variant === "large" && "h-6 w-6 rounded border-2 border-[#333333]",
className className
)} )}
{...props} {...props}
@ -21,7 +28,7 @@ const Checkbox = React.forwardRef<
<CheckboxPrimitive.Indicator <CheckboxPrimitive.Indicator
className={cn("grid place-content-center text-current")} className={cn("grid place-content-center text-current")}
> >
<Check className="h-5 w-5" /> <Check className={variant === "large" ? "h-6 w-6" : "h-5 w-5"} />
</CheckboxPrimitive.Indicator> </CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root> </CheckboxPrimitive.Root>
)) ))

View File

@ -16,9 +16,11 @@ import Icon from "./icon";
interface DatePickerProps { interface DatePickerProps {
showIcon?: boolean; showIcon?: boolean;
variant?: "default" | "small";
placeholder?: string;
} }
export function DatePicker({ showIcon = true }: DatePickerProps) { export function DatePicker({ showIcon = true, variant = "default", placeholder = "Выберите дату и время" }: DatePickerProps) {
const [date, setDate] = React.useState<Date>(); const [date, setDate] = React.useState<Date>();
const [departureTime, setDepartureTime] = React.useState("12:00"); const [departureTime, setDepartureTime] = React.useState("12:00");
const [arrivalTime, setArrivalTime] = React.useState("13:00"); const [arrivalTime, setArrivalTime] = React.useState("13:00");
@ -29,13 +31,15 @@ export function DatePicker({ showIcon = true }: DatePickerProps) {
setOpen(false); setOpen(false);
}; };
const heightClass = variant === "small" ? "h-[48px]" : "h-[64px]";
return ( return (
<Popover open={open} onOpenChange={setOpen}> <Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild> <PopoverTrigger asChild>
<Button <Button
variant="outline" variant="outline"
data-empty={!date} data-empty={!date}
className="w-full h-[64px] justify-between text-left font-normal" className={`w-full ${heightClass} justify-between text-left font-normal`}
> >
<div className="flex items-center"> <div className="flex items-center">
{showIcon && ( {showIcon && (
@ -51,7 +55,7 @@ export function DatePicker({ showIcon = true }: DatePickerProps) {
{ locale: ru } { locale: ru }
) )
) : ( ) : (
<span>Выберите дату и время</span> <span>{placeholder}</span>
)} )}
</div> </div>
{open ? ( {open ? (

View File

@ -16,6 +16,12 @@ import VkIcon from "../../../public/images/icons/vk.svg";
import DzenIcon from "../../../public/images/icons/dzen.svg"; import DzenIcon from "../../../public/images/icons/dzen.svg";
import TgIcon from "../../../public/images/icons/tg.svg"; import TgIcon from "../../../public/images/icons/tg.svg";
import AdIcon from "../../../public/images/icons/ad.svg"; import AdIcon from "../../../public/images/icons/ad.svg";
import LengthMinIcon from "../../../public/images/icons/length-min.svg";
import LengthMaxIcon from "../../../public/images/icons/length-max.svg";
import PriceMinIcon from "../../../public/images/icons/price-min.svg";
import PriceMaxIcon from "../../../public/images/icons/price-max.svg";
import BoatYearMinIcon from "../../../public/images/icons/boat-year-min.svg";
import BoatYearMaxIcon from "../../../public/images/icons/boat-year-max.svg";
// Объект с иконками для удобного доступа // Объект с иконками для удобного доступа
const icons = { const icons = {
@ -33,6 +39,12 @@ const icons = {
dzen: DzenIcon, dzen: DzenIcon,
tg: TgIcon, tg: TgIcon,
ad: AdIcon, ad: AdIcon,
lengthMin: LengthMinIcon,
lengthMax: LengthMaxIcon,
priceMin: PriceMinIcon,
priceMax: PriceMaxIcon,
boatYearMin: BoatYearMinIcon,
boatYearMax: BoatYearMaxIcon,
}; };
export type IconName = export type IconName =
@ -49,7 +61,13 @@ export type IconName =
| "vk" | "vk"
| "dzen" | "dzen"
| "tg" | "tg"
| "ad"; | "ad"
| "lengthMin"
| "lengthMax"
| "priceMin"
| "priceMax"
| "boatYearMin"
| "boatYearMax";
export interface IconProps export interface IconProps
extends Omit<SVGProps<SVGSVGElement>, "name" | "preserveAspectRatio"> { extends Omit<SVGProps<SVGSVGElement>, "name" | "preserveAspectRatio"> {

View File

@ -0,0 +1,45 @@
"use client"
import * as React from "react"
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
import { Circle } from "lucide-react"
import { cn } from "@/lib/utils"
const RadioGroup = React.forwardRef<
React.ElementRef<typeof RadioGroupPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>
>(({ className, ...props }, ref) => {
return (
<RadioGroupPrimitive.Root
className={cn("grid gap-2", className)}
{...props}
ref={ref}
/>
)
})
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName
const RadioGroupItem = React.forwardRef<
React.ElementRef<typeof RadioGroupPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>
>(({ className, ...props }, ref) => {
return (
<RadioGroupPrimitive.Item
ref={ref}
className={cn(
"aspect-square h-6 w-6 rounded-full border-2 shadow focus:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
className
)}
style={{ borderColor: "#008299" }}
{...props}
>
<RadioGroupPrimitive.Indicator className="flex items-center justify-center">
<Circle className="h-3.5 w-3.5" style={{ fill: "#008299", stroke: "none" }} />
</RadioGroupPrimitive.Indicator>
</RadioGroupPrimitive.Item>
)
})
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName
export { RadioGroup, RadioGroupItem }

View File

@ -12,14 +12,23 @@ const SelectGroup = SelectPrimitive.Group
const SelectValue = SelectPrimitive.Value const SelectValue = SelectPrimitive.Value
interface SelectTriggerProps
extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> {
variant?: "default" | "ghost"
}
const SelectTrigger = React.forwardRef< const SelectTrigger = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Trigger>, React.ElementRef<typeof SelectPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> SelectTriggerProps
>(({ className, children, ...props }, ref) => ( >(({ className, children, variant = "default", ...props }, ref) => (
<SelectPrimitive.Trigger <SelectPrimitive.Trigger
ref={ref} ref={ref}
className={cn( className={cn(
"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-full border border-input bg-transparent px-3 py-2 text-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1", "flex h-9 w-full items-center justify-between whitespace-nowrap rounded-full bg-transparent px-3 py-2 text-sm ring-offset-background focus:outline-none disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
variant === "default" &&
"border border-input data-[placeholder]:text-muted-foreground focus:ring-1 focus:ring-ring",
variant === "ghost" &&
"border-0 text-[#008299] data-[placeholder]:text-[#008299] px-0 text-base",
className className
)} )}
{...props} {...props}

View File

@ -0,0 +1,38 @@
"use client"
import * as React from "react"
import * as SliderPrimitive from "@radix-ui/react-slider"
import { cn } from "@/lib/utils"
const Slider = React.forwardRef<
React.ElementRef<typeof SliderPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
>(({ className, ...props }, ref) => {
const value = props.value || props.defaultValue || [0]
const thumbCount = Array.isArray(value) ? value.length : 1
return (
<SliderPrimitive.Root
ref={ref}
className={cn(
"relative flex w-full touch-none select-none items-center",
className
)}
{...props}
>
<SliderPrimitive.Track className="relative h-0.5 w-full grow overflow-hidden rounded-full bg-primary/20">
<SliderPrimitive.Range className="absolute h-full bg-brand" />
</SliderPrimitive.Track>
{Array.from({ length: thumbCount }).map((_, index) => (
<SliderPrimitive.Thumb
key={index}
className="block h-2 w-2 rounded-full border border-primary/50 bg-background shadow transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50"
/>
))}
</SliderPrimitive.Root>
)
})
Slider.displayName = SliderPrimitive.Root.displayName
export { Slider }