This commit is contained in:
2025-02-11 17:48:42 +05:00
parent a7e30e6663
commit eb047cafa9
37 changed files with 337 additions and 253 deletions
+7 -7
View File
@@ -111,15 +111,15 @@ html {
}
@utility line1 {
@apply lg:text-[clamp(96px,4.444vw,128px)] md:max-lg:text-[clamp(92px,92px+(100vw-768px)/672*8,100px)] sm:max-md:text-[clamp(40px,40px+(100vw-360px)/408*16,56px)] text-[40px] leading-[85%];
@apply 2xl:text-[128px] lg:max-2xl:text-[clamp(96px,4.444vw,128px)] md:max-lg:text-[clamp(92px,92px+(100vw-768px)/672*8,100px)] sm:max-md:text-[clamp(40px,40px+(100vw-360px)/408*16,56px)] text-[40px] leading-[85%];
}
@utility line2 {
@apply lg:text-[clamp(64px,4.444vw,88px)] md:max-lg:text-[clamp(40px,40px+(100vw-768px)/672*24,64px)] sm:max-md:text-[clamp(32px,32px+(100vw-360px)/408*8,40px)] text-[32px] leading-[95%];
@apply 2xl:text-[88px] lg:max-2xl:text-[clamp(64px,4.444vw,88px)] md:max-lg:text-[clamp(40px,40px+(100vw-768px)/672*24,64px)] sm:max-md:text-[clamp(32px,32px+(100vw-360px)/408*8,40px)] text-[32px] leading-[95%];
}
@utility heading1 {
@apply lg:text-[clamp(24px,24px+(100vw-1440px)/480*4,32px)] md:max-lg:text-2xl text-xl leading-[1.167];
@apply 2xl:text-[2.083vw] lg:max-2xl:text-[clamp(24px,24px+(100vw-1440px)/480*4,32px)] md:max-lg:text-2xl text-xl leading-[1.167];
}
@utility heading2 {
@@ -127,19 +127,19 @@ html {
}
@utility accent {
@apply 2xl:text-[2.604vw] lg:max-2xl:text-[clamp(32px,32px+(100vw-1440px)/480*8,40px)] text-[clamp(24px,32px+(100vw-360px)/1080*8,32px)] lg:leading-[1.1] leading-none;
@apply 2xl:text-[2.604vw] lg:max-2xl:text-[clamp(32px,32px+(100vw-1440px)/480*8,40px)] text-[clamp(16px,16px+(100vw-360px)/1080*8,32px)] lg:leading-[1.1] leading-none;
}
@utility text1 {
@apply 2xl:text-[1.172vw] lg:max-2xl:text-[clamp(14px,14px+(100vw-1440px)/480*4,18px)] text-sm leading-[1.35];
@apply 2xl:text-2xl lg:max-2xl:text-[clamp(14px,14px+(100vw-1440px)/480*4,18px)] text-sm leading-[1.35];
}
@utility text2 {
@apply 2xl:text-[0.911vw] lg:max-2xl:text-[clamp(12px,12px+(100vw-1440px)/480*2,14px)] text-xs leading-[1.35];
@apply 2xl:text-xl lg:max-2xl:text-[clamp(12px,12px+(100vw-1440px)/480*2,14px)] text-xs leading-[1.35];
}
@utility btnl {
@apply lg:text-[1.25vw] md:max-lg:text-[18px] sm:max-md:text-[clamp(16px,16px+(100vw-360px)/1560*2,18px)] text-base leading-none;
@apply md:text-2xl sm:max-md:text-[clamp(16px,16px+(100vw-360px)/1560*2,18px)] text-base leading-none;
}
@utility btnm {
+4 -5
View File
@@ -11,7 +11,7 @@ import { SyntheticEvent } from 'react';
import EditIcon from '../../public/icons/edit.svg';
import TrashIcon from '../../public/icons/trash.svg';
import { DeleteItemModal } from './DeleteItemModal';
import { ArticleFormModal } from './modals/ArticleFormModal';
import { ArticleContentFormModal } from './modals/ArticleContentFormModal';
import { CompanyFormModal } from './modals/CompanyFormModal';
import { ProjectFormModal } from './modals/ProjectFormModal';
@@ -38,12 +38,11 @@ export function ItemActions({
<CompanyFormModal action="edit" defaultValues={company} />,
'editCompanyForm'
);
} else {
} else
setModal(
<ArticleFormModal action="edit" defaultValues={item} />,
'editArticleForm'
<ArticleContentFormModal {...item} />,
'articleContentFormModal'
);
}
}
function handleDelete(e: SyntheticEvent) {
+2 -2
View File
@@ -83,7 +83,7 @@ export function FeedbackForm() {
currentPhoneCodeAndCountry={[phoneCode, country]}
onClick={setPhoneCodeAndCountry}
/>
<div className="border-l border-[#3D425C]" />
<div className="border-l border-[#37393B]" />
<ReactInputMask
type="tel"
id={'tel'}
@@ -100,7 +100,7 @@ export function FeedbackForm() {
type="text"
placeholder="E-mail*"
{...register('email')}
className="bg-transparent border-b border-[#3D425C] focus:border-white py-4 rounded-none outline-none transition-all w-full placeholder:btnl placeholder:font-medium placeholder:select-none"
className="bg-transparent border-b border-[#37393B] focus:border-white py-4 rounded-none outline-none transition-all w-full placeholder:btnl placeholder:font-medium placeholder:select-none"
/>
<div className="sm:flex items-stretch gap-3">
<Button
+6 -13
View File
@@ -1,6 +1,7 @@
import { Icon } from '@/ui/Icon';
import Link from 'next/link';
import { PropsWithChildren } from 'react';
import ArrowMoreIcon from '../../../public/icons/arrow_more.svg';
export function Footer() {
return (
@@ -11,13 +12,9 @@ export function Footer() {
className="p-6 flex flex-col justify-between max-sm:gap-y-10 bg-[linear-gradient(to_top_left,#7A7A7A50,transparent)] rounded-2xl flex-1"
>
<p className="text-[#7A7A7A] text1 font-medium">Позвонить</p>
<div className="lg:line2 sm:heading1 line2 flex items-center font-medium">
<div className="lg:line2 sm:max-lg:heading1 line2 flex items-center font-medium">
8 800 770 00 67
<Icon
name="arrow_more"
color="white"
svgProp={{ className: 'lg:w-20 lg:h-20 sm:w-8 sm:h-8 w-9 h-9' }}
/>
<ArrowMoreIcon className="text-white lg:w-20 lg:h-20 md:max-lg:w-8 md:max-lg:h-8 w-9 h-9" />
</div>
</Link>
<Link
@@ -25,17 +22,13 @@ export function Footer() {
className="p-6 flex flex-col justify-between max-sm:gap-y-10 bg-[linear-gradient(to_top_left,#7A7A7A50,transparent)] rounded-2xl flex-1"
>
<p className="text-[#7A7A7A] text1 font-medium">Написать</p>
<div className="lg:line2 sm:heading1 line2 flex items-center font-medium">
<div className="lg:line2 sm:max-lg:heading1 line2 flex items-center font-medium">
info@graff.tech
<Icon
name="arrow_more"
color="white"
svgProp={{ className: 'lg:w-20 lg:h-20 sm:w-8 sm:h-8 w-9 h-9' }}
/>
<ArrowMoreIcon className="text-white lg:w-20 lg:h-20 md:max-lg:w-8 md:max-lg:h-8 w-9 h-9" />
</div>
</Link>
<div className="gap-y-2 justify-stretch sm:gap-x-2 gap-x-1 sm:flex-col flex">
<ContactLink href="https://t.me/GRAFFinteractive">
<ContactLink href="https://t.me/graffestate">
<Icon
name="tg"
svgProp={{
+13 -9
View File
@@ -75,28 +75,28 @@ export function Header() {
animate={{
width:
burgerOpened && (isXs || isSm)
? 340
? 320
: productsOpened && !isXs && !isSm
? isLg
? 'calc(992 / 1440 * 100vw)'
? '68.889vw'
: 'calc(100vw - 32px)'
: 'auto',
}}
className="fixed lg:top-5 top-4 p-1 rounded-[20px] bg-[#37393B99] backdrop-blur-2xl flex gap-1 z-[12]"
className="fixed lg:top-5 top-4 p-1 rounded-[20px] bg-[#37393B99] backdrop-blur-2xl flex gap-1 z-12"
>
{((isLg && scroll < -logoRef.current?.clientHeight!) || !isLg) && (
<Link
href={'/'}
className="aspect-square p-4 alg:hidden hover:bg-[#37393B99] rounded-xl content-center"
className="aspect-square p-3 hover:bg-[#37393B99] rounded-2xl content-center"
>
<ClassNameWrapper className={'w-4 h-4'}>
<ClassNameWrapper className={'w-6 h-6'}>
<LogoIcon />
</ClassNameWrapper>
</Link>
)}
<div ref={productsBtnRef} className="max-md:hidden">
<button
className="px-6 py-4 font-medium btnm hover:bg-[#37393B99] rounded-2xl active:bg-white active:text-black"
className="px-6 py-4 font-medium btnm hover:bg-[#37393B99] rounded-2xl active:bg-white active:text-black cursor-pointer outline-none"
onClick={() => {
setProductsOpened((prev) => !prev);
}}
@@ -157,7 +157,7 @@ export function Header() {
initial={{ opacity: 0 }}
animate={{ opacity: 100 }}
exit={{ opacity: 0 }}
className="absolute w-full p-4 pt-2 top-16 rounded-2xl bg-[#232425] md:hidden space-y-6"
className="absolute p-4 pt-2 top-16 rounded-2xl bg-[#232425] md:hidden space-y-6 xs:max-h-[calc(100dvh-100px)] h-fit"
>
<div className="px-2 -mx-4 space-y-1">
<HeaderLink
@@ -165,7 +165,11 @@ export function Header() {
text={'О нас'}
className="accent"
/>
<HeaderLink href={'/'} text={'Блог'} className="accent" />
<HeaderLink
href={'/blog'}
text={'Блог'}
className="accent"
/>
<HeaderLink
href={'/projects'}
text={'Проекты'}
@@ -187,7 +191,7 @@ export function Header() {
</Link>
</div>
</motion.div>
<div className="fixed w-screen h-screen bg-[#0F101199] backdrop-blur-lg -top-4 -left-[calc((100vw-100%)/2)] -z-[1]" />
<div className="absolute w-screen h-screen bg-[#0F101199] backdrop-blur-lg -top-4 -left-[calc((100vw-100%)/2)] -z-[1]" />
</>
)}
</AnimatePresence>
+4 -8
View File
@@ -3,7 +3,7 @@ import { ProductItem } from '@/ui/ProductItem';
export function Products() {
return (
<div className="grid md:grid-cols-2 grid-cols-3 md:grid-rows-6 grid-rows-2 gap-1 lg:gap-2 rounded-2xl max-h-[calc(100dvh-100px)]">
<div className="grid md:grid-cols-2 grid-cols-3 md:grid-rows-6 grid-rows-2 gap-1 lg:gap-2 rounded-2xl max-h-[calc(100dvh-100px)] lg:w-[68.889vw] lg:h-[38.889vw] md:max-lg:w-[95.703vw] md:h-[72.917vw] h-full w-full">
{products.map((product, index) => (
<ProductItem
href={'/' + product.title.toLowerCase()}
@@ -11,14 +11,10 @@ export function Products() {
{...product}
className={
index < 2
? `md:aspect-[361.5/263] md:col-start-1 md:row-span-3`
? 'max-md:aspect-[100/114] md:col-start-1 md:row-span-3'
: index === 4
? `col-span-2 md:col-span-1 md:col-start-2 md:row-start-${
index * 2 - 3
} md:row-end-${index * 2 - 1}`
: `max-md:aspect-[100/114] md:col-span-1 md:col-start-2 md:row-start-${
index * 2 - 3
} md:row-end-${index * 2 - 1}`
? 'col-span-2 md:col-span-1 md:col-start-2 md:row-start-5 md:row-end-7'
: 'max-md:aspect-[100/114] md:col-span-1 md:col-start-2 md:nth-3:row-start-1 md:nth-3:row-end-3 md:nth-4:row-start-3 md:nth-4:row-end-5'
}
/>
))}
+2 -2
View File
@@ -49,9 +49,9 @@ export function SelectPhoneCode({
/>
<p className="btnl font-medium">{currentPhoneCode}</p>
{open ? (
<ChevronUpIcon className="max-sm:w-4 sm:max-lg:w-5 flex-1 text-white" />
<ChevronUpIcon className="max-sm:w-4 sm:max-lg:w-5 text-white" />
) : (
<ChevronDownIcon className="max-sm:w-4 sm:max-lg:w-5 flex-1 text-white" />
<ChevronDownIcon className="max-sm:w-4 sm:max-lg:w-5 text-white" />
)}
</button>
{open && (
@@ -1,33 +1,51 @@
import { BundledEditor } from '@/lib/BundledEditor';
import { useRef } from 'react';
import { Control, Controller } from 'react-hook-form';
import { Dispatch, SetStateAction, useRef } from 'react';
import { Control, FieldArrayWithId, useController } from 'react-hook-form';
import { Editor } from 'tinymce';
import { IArticleInput } from '../modals/ArticleFormModal';
export function ArticleContentEditor({
control,
index,
setEditing,
item,
}: {
index: number;
control: Control<IArticleInput, any>;
setEditing: Dispatch<SetStateAction<boolean>>;
item: FieldArrayWithId<IArticleInput, 'blocks', 'id'> & { content: string };
}) {
const ref = useRef<Editor | null>(null);
const {
field: { onChange, value },
} = useController({
control,
name: `blocks.${index}.content`,
defaultValue: item.content,
});
return (
<div className="w-full">
<Controller
control={control}
name={`blocks.${index}.content`}
render={({ field: { onChange, value } }) => (
<BundledEditor
onEditorChange={onChange}
value={value}
onInit={(_, editor) => {
ref.current = editor;
}}
/>
)}
<div className="w-full space-y-4">
<BundledEditor
id={item.id}
onEditorChange={onChange}
value={value}
onInit={(_, editor) => {
ref.current = editor;
}}
init={{
content_style:
'body {color: #fff; background: #14161f; font-size:16px;}',
}}
/>
<button
onClick={() => {
setEditing(false);
}}
>
Сохранить
</button>
</div>
);
}
@@ -1,11 +1,12 @@
import { Reorder } from 'framer-motion';
import parse from 'html-react-parser';
import { useRef, useState } from 'react';
import { useEffect, useRef, useState } from 'react';
import {
Control,
FieldArrayWithId,
UseFieldArrayInsert,
UseFieldArrayRemove,
useFormContext,
} from 'react-hook-form';
import { BlockActions } from '../BlockActions';
import { IArticleInput } from '../modals/ArticleFormModal';
@@ -13,7 +14,7 @@ import { ArticleContentEditor } from './ArticleContentEditor';
export interface IArticleContentInputProps {
index: number;
item: FieldArrayWithId<IArticleInput, 'blocks', 'id'>;
item: FieldArrayWithId<IArticleInput, 'blocks', 'id'> & { content: string };
control: Control<IArticleInput, any>;
insert: UseFieldArrayInsert<IArticleInput, 'blocks'>;
remove: UseFieldArrayRemove;
@@ -30,6 +31,19 @@ export function ArticleContentInput({
const [editing, setEditing] = useState(false);
const [content, setContent] = useState('');
const { watch } = useFormContext<IArticleInput>();
useEffect(() => {
const { unsubscribe } = watch(({ blocks }) => {
if (!blocks || !blocks.length || blocks[index]?.type !== 'Content')
return;
setContent(blocks[index].content ?? '');
});
return unsubscribe;
}, [index, watch]);
return (
<Reorder.Item
as="div"
@@ -38,10 +52,15 @@ export function ArticleContentInput({
onDoubleClick={() => ref.current && ref.current.click()}
>
{editing ? (
<ArticleContentEditor control={control} index={index} />
<ArticleContentEditor
control={control}
index={index}
item={item}
setEditing={setEditing}
/>
) : (
<div className="border-[#3D425C] border p-4 rounded-3xl no-tailwindcss-base">
{item.type === 'Content' && parse(item.content)}
{item.type === 'Content' && parse(content)}
</div>
)}
<BlockActions
@@ -1,3 +1,4 @@
import { useArticleMutation } from '@/hooks/useArticleMutation';
import { useModalStore } from '@/stores/useModalStore';
import { IArticle } from '@/types/IArticle';
import { Button } from '@/ui/Button';
@@ -37,16 +38,27 @@ export function ArticleContentFormModal({
},
});
const { control } = form;
const { control, getValues } = form;
const { append, fields, swap, insert, remove } = useFieldArray({
control,
name: 'blocks',
});
const { mutateAsync: save } = useArticleMutation({ action: 'edit', id });
async function handleSave(drafted: boolean) {
await save({
...getValues(),
blocks: JSON.stringify(getValues('blocks')),
drafted,
});
setModal(null, '');
}
return (
<>
<ArticleFormActions disabled={false} handleSave={() => {}} />
<ArticleFormActions disabled={false} handleSave={handleSave} />
<div className="relative space-y-4 bg-[#232425] rounded-[28px] top-5 w-[calc(954/1440*100vw)] h-[calc(100vh-40px)] z-1 overflow-y-auto">
<div className="absolute top-3 right-4">
<Button
@@ -96,48 +108,50 @@ export function ArticleContentFormModal({
</div>
</div>
<FormProvider {...form}>
<Reorder.Group
axis="y"
values={fields}
onReorder={reorderFields(fields, swap)}
className="pt-10 px-[75px] space-y-5"
>
{fields.map((item, index) =>
item.type === 'Content' ? (
<ArticleContentInput
key={item.id}
control={control}
index={index}
item={item}
insert={insert}
remove={remove}
/>
) : item.type === 'Quote' ? (
<ArticleQuoteInput
key={item.id}
index={index}
item={item}
control={control}
remove={remove}
/>
) : item.type === 'Slider' ? (
<ArticleSliderInput
key={item.id}
control={control}
index={index}
item={item}
remove={remove}
/>
) : (
<ArticleVideoUploader
key={item.id}
item={item}
index={index}
remove={remove}
/>
)
)}
</Reorder.Group>
<form>
<Reorder.Group
axis="y"
values={fields}
onReorder={reorderFields(fields, swap)}
className="pt-10 px-[75px] space-y-5"
>
{fields.map((item, index) =>
item.type === 'Content' ? (
<ArticleContentInput
key={item.id}
control={control}
index={index}
item={item}
insert={insert}
remove={remove}
/>
) : item.type === 'Quote' ? (
<ArticleQuoteInput
key={item.id}
index={index}
item={item}
control={control}
remove={remove}
/>
) : item.type === 'Slider' ? (
<ArticleSliderInput
key={item.id}
control={control}
index={index}
item={item}
remove={remove}
/>
) : (
<ArticleVideoUploader
key={item.id}
item={item}
index={index}
remove={remove}
/>
)
)}
</Reorder.Group>
</form>
</FormProvider>
</div>
<div className="fixed bottom-5 right-20 z-10 space-y-2">
+2 -2
View File
@@ -72,7 +72,7 @@ export function ArticleFormModal<TAction extends 'create' | 'edit'>({
handleSave={handleSave}
disabled={!title || !tags || !cardImage || !posterImage}
/>
<div className="relative pb-10 space-y-4 bg-[#232425] rounded-[28px] top-5 w-[calc(954/1440*100vw)] pl-[75px] pr-[55px] overflow-y-auto z-[2] max-h-[calc(100vh-40px)]">
<div className="relative pb-10 space-y-4 bg-[#232425] rounded-[28px] top-5 w-[66.25vw] pl-[75px] pr-[55px] overflow-y-auto z-[2] max-h-[calc(100vh-40px)]">
<FormModalHeader
submitHandler={handleSubmit(onSubmit)}
disabled={!title || !tags || !cardImage || !posterImage}
@@ -93,7 +93,7 @@ export function ArticleFormModal<TAction extends 'create' | 'edit'>({
/>
<div className="gap-y-4 flex flex-col">
<label htmlFor="devices" className="w-fit">
Устройства
Категории
</label>
<div className="flex flex-wrap gap-2">
{['Недвижимость', 'Награды', 'Выставки'].map((tag) => (
+69 -28
View File
@@ -1,7 +1,7 @@
import { stories } from '@/consts/stories';
import { useMediaQueries } from '@/hooks/useMediaQueries';
import { useModalStore } from '@/stores/useModalStore';
import { createRef, RefObject, useEffect, useState } from 'react';
import { createRef, RefObject, useEffect, useRef, useState } from 'react';
import { useSwipeable } from 'react-swipeable';
import CloseIcon from '../../../public/icons/close.svg';
@@ -14,6 +14,8 @@ export function StoriesModal({ startIndex = 0 }: { startIndex?: number }) {
const { isLg, isMd } = useMediaQueries();
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
setVideoRefs(stories.map(createRef<HTMLVideoElement>));
}, []);
@@ -41,7 +43,7 @@ export function StoriesModal({ startIndex = 0 }: { startIndex?: number }) {
if (currentIndex === videoRefs.length - 1) setModal(null, '');
else setCurrentIndex((prev) => prev + 1);
}
}, [currentIndex, currentProgress, setModal, videoRefs]);
}, [currentProgress, setModal, videoRefs]);
useEffect(() => {
if (
@@ -50,15 +52,37 @@ export function StoriesModal({ startIndex = 0 }: { startIndex?: number }) {
!videoRefs[currentIndex].current
)
return;
videoRefs.forEach(({ current: video }, index) => {
if (index === currentIndex) video?.play();
else {
if (index === currentIndex) {
video!.playbackRate = 10;
video?.play();
} else {
video!.currentTime = 0;
video?.pause();
}
});
}, [currentIndex, videoRefs]);
useEffect(() => {
const slider = ref.current;
if (!slider || isLg || isMd) return;
function handleTouch(e: TouchEvent) {
if (e.touches[0].clientX > slider!.clientWidth / 2)
setCurrentIndex((prev) => Math.min(videoRefs.length - 1, prev + 1));
else setCurrentIndex((prev) => Math.max(0, prev - 1));
}
slider.addEventListener('touchstart', handleTouch);
return () => slider.removeEventListener('touchstart', handleTouch);
}, []);
useEffect(() => {
console.log('currentIndex', currentIndex);
}, [currentIndex]);
const handlers = useSwipeable({
trackMouse: true,
preventScrollOnSwipe: true,
@@ -71,34 +95,32 @@ export function StoriesModal({ startIndex = 0 }: { startIndex?: number }) {
return (
<>
<button
className="rounded-2xl p-4 bg-[#37393B99] absolute top-5 right-5 z-1 cursor-pointer"
className="rounded-2xl p-4 bg-[#37393B99] absolute top-5 right-5 z-10 cursor-pointer"
onClick={() => setModal(null, '')}
>
<CloseIcon className="w-4 h-4 text-white" />
</button>
<div
{...handlers}
className="overflow-hidden z-1 m-auto lg:max-w-[83.958vw] lg:max-h-[76.433vh] md:max-lg:max-w-[157.422vw] md:max-lg:max-h-[67.669vh] max-w-screen max-h-screen"
className="overflow-hidden z-1 md:m-auto lg:max-w-[83.958vw] lg:max-h-[76.433vh] md:max-lg:max-w-[157.422vw] md:max-lg:max-h-[67.669vh] max-w-screen max-h-dvh max-md:top-0"
>
<div
className="flex md:gap-6 items-center h-full relative transition-transform"
style={{
transform: isLg
? `translateX(calc(${1 - currentIndex}*26.25vw))`
: isMd
? `translateX(calc(${1 / 2 - currentIndex}*49.219vw))`
: undefined,
left: isLg || isMd ? 24 * (1 - currentIndex) : 0,
}}
>
<div ref={ref} className="flex md:gap-6 items-center h-full relative">
{!!videoRefs.length &&
stories.map(({ id, video, title, poster }, index) => (
<div
style={{
transform: isLg
? `translateX(calc(${1 - currentIndex}*26.25vw))`
: isMd
? `translateX(calc(${1 / 2 - currentIndex}*49.219vw))`
: `translateX(${-currentIndex * 100}%)`,
left: isLg || isMd ? 24 * (1 - currentIndex) : 0,
}}
key={id}
className={`select-none relative flex items-end overflow-hidden md:rounded-xl cursor-pointer p-4 ${
className={`select-none relative flex items-end overflow-hidden md:rounded-xl cursor-pointer transition-transform p-4 ${
index === currentIndex
? 'lg:min-w-[28.125vw] lg:h-[76.433vh] md:max-lg:min-w-[52.734vw] md:max-lg:h-[67.669vh] min-w-screen h-screen'
: 'lg:min-w-[26.25vw] lg:h-[71.338vh] md:max-lg:min-w-[49.219vw] md:max-lg:h-[63.158vh] min-w-screen h-screen'
? 'lg:min-w-[28.125vw] lg:h-[76.433vh] md:max-lg:min-w-[52.734vw] md:max-lg:h-[67.669vh] min-w-screen h-dvh'
: 'lg:min-w-[26.25vw] lg:h-[71.338vh] md:max-lg:min-w-[49.219vw] md:max-lg:h-[63.158vh] min-w-screen h-dvh'
}`}
onClick={() => setCurrentIndex(index)}
>
@@ -106,22 +128,22 @@ export function StoriesModal({ startIndex = 0 }: { startIndex?: number }) {
ref={videoRefs[index]}
src={video}
poster={poster}
className="absolute left-0 top-0 w-full h-full object-cover object-center md:rounded-xl"
playsInline
className="absolute left-0 top-0 w-full h-full object-cover object-center"
/>
<div
className={`absolute bottom-0 left-1/2 -translate-x-1/2 w-full h-full transition-colors ${
className={`absolute bottom-0 left-1/2 -translate-x-1/2 w-full h-full md:rounded-xl transition-colors ${
index === currentIndex
? 'lg:bg-[radial-gradient(37.292vw_23.036vh_at_bottom,#6078F2,#C868F5,transparent)] md:max-lg:bg-[radial-gradient(69.922vw_20.395vh_at_bottom,#6078F2,#C868F5,transparent)]'
: 'bg-[#0F101199]'
? 'lg:bg-[radial-gradient(37.292vw_23.036vh_at_bottom,#6078F2,#C868F5,transparent)] md:max-lg:bg-[radial-gradient(69.922vw_20.395vh_at_bottom,#6078F2,#C868F5,transparent)] bg-[radial-gradient(149.167vw_33.906vh_at_bottom,#6078F2,#C868F5,transparent)]'
: 'md:bg-[#0F101199]'
}`}
/>
{index === currentIndex && (
<div className="space-y-5 z-1">
{currentIndex === index && (
<div className="space-y-5 z-1 max-md:hidden">
<p className="heading1 font-medium">{title}</p>
<div className="bg-white/30 relative w-full h-1">
<div className="bg-white/30 w-full h-1 rounded-[34px]">
<div
className="h-1 bg-white absolute transition-[width]"
className="h-1 bg-white transition-[width] rounded-[34px]"
style={{ width: currentProgress * 100 + '%' }}
/>
</div>
@@ -129,6 +151,25 @@ export function StoriesModal({ startIndex = 0 }: { startIndex?: number }) {
)}
</div>
))}
<div className="md:hidden absolute space-y-6 left-2.5 right-2.5 bottom-4 z-1 w-full">
<p className="heading1 font-medium">
{stories[currentIndex].title}
</p>
<div className="flex gap-1">
{stories.map(({ id }, index) => (
<div key={id} className="bg-white/40 flex-1 rounded-[34px] h-1">
<div
className="bg-white rounded-[34px] h-1 transition-all"
style={
currentIndex === index
? { width: currentProgress * 100 + '%' }
: { width: currentIndex > index ? '100%' : '0%' }
}
/>
</div>
))}
</div>
</div>
</div>
</div>
</>
@@ -1,6 +1,7 @@
'use client';
import { useGetArticleById } from '@/queries/getArticleById';
import parse from 'html-react-parser';
import Image from 'next/image';
export function ArticleSyncPage({ articleId }: { articleId: string }) {
@@ -35,7 +36,11 @@ export function ArticleSyncPage({ articleId }: { articleId: string }) {
</div>
</div>
</div>
<div className="py-10 px-[75px]"></div>
<div className="py-10 px-[75px]">
{article.blocks.map(
(block) => block.type === 'Content' && parse(block.content)
)}
</div>
</div>
);
}
@@ -21,7 +21,7 @@ export function ArticlesPageActions({ tags }: { tags: string[] }) {
modal={<ArticleFormModal action="create" />}
>
<Button
className="px-3 py-2 outline-none btns"
className="px-3 py-2 outline-none btns flex items-center text-nowrap"
rounded="xl"
icon={<AddIcon className="w-4 h-4 text-white" />}
>
@@ -31,7 +31,7 @@ export function ArticlesPageActions({ tags }: { tags: string[] }) {
{auth && (
<Button
color="secondary"
className={`active:bg-white active:text-black px-3 transition-colors py-2 btns ${
className={`active:bg-white active:text-black px-3 transition-colors text-nowrap py-2 btns ${
show ? 'bg-white text-black' : 'bg-[#37393B99]'
}`}
rounded="xl"
+5 -10
View File
@@ -1,31 +1,26 @@
'use client';
import { awards } from '@/consts/awards';
import { useScroll } from '@/hooks/useScroll';
import { Title } from '@/ui/Title';
import { useInView } from 'framer-motion';
import Image from 'next/image';
import { useRef } from 'react';
export function Awards() {
const ref = useRef<HTMLDivElement>(null);
const target = useRef<HTMLDivElement>(null);
const scroll = useScroll(ref);
const inView = useInView(target, { margin: '100% 0% -50% 0%' });
return (
<div
className="lg:mt-40 mt-[100px] sm:space-y-3 space-y-10 relative"
ref={ref}
ref={target}
>
<Title className="lg:absolute">Награды</Title>
<div
className="lg:flex sm:grid-cols-2 sm:gap-3 grid w-full h-full gap-2 transition-all duration-500"
style={{
minHeight:
scroll > 400
? `calc(740 / 1440 * 100vw)`
: scroll < 0
? 'calc(640 / 1440 * 100vw)'
: `calc((740 - 120 * ${1 - scroll / 400}) / 1440 * 100vw)`,
minHeight: !inView ? '50vw' : '44.444vw',
}}
>
{awards.map((award, index) => (
@@ -14,7 +14,7 @@ import { StatsColumn } from './StatColumn';
export function Calculator() {
const [selectedRegion, setSelectedRegion] = useState<Region>();
const [consultations, setConsultations] = useState<number>(10);
const [consultations, setConsultations] = useState<number>(60);
const [implementationPeriod, setImplementationPeriod] = useState<number>(
null!
);
@@ -215,9 +215,9 @@ export function Calculator() {
</div>
</div>
</div>
<div className="md:flex grid-cols-2 gap-x-3 hidden">
<div className="rounded-2xl bg-[linear-gradient(to_top,#7A7A7A40,#7A7A7A30)] p-7 w-1/2 relative overflow-hidden">
<p className="text1 font-medium max-w-[60%] mb-11">
<div className="md:flex gap-x-3 hidden">
<div className="rounded-2xl bg-[linear-gradient(to_top,#7A7A7A40,#7A7A7A30)] p-7 w-1/2 relative overflow-hidden aspect-[514/180] flex flex-col justify-between">
<p className="text1 font-medium max-w-[60%]">
Срок реализации объекта
</p>
<p className="font-medium">
@@ -269,9 +269,9 @@ export function Calculator() {
)}
</AnimatePresence>
</div>
<div className="rounded-2xl bg-[linear-gradient(to_top,#7A7A7A40,#7A7A7A30)] p-7 w-1/2 relative overflow-hidden">
<div className="rounded-2xl bg-[linear-gradient(to_top,#7A7A7A40,#7A7A7A30)] p-7 w-1/2 relative overflow-hidden aspect-[514/180]">
<div className="flex flex-col justify-between h-full">
<p className="text1 mb-11 font-medium">Ежемесячный доход</p>
<p className="text1 font-medium">Ежемесячный доход</p>
<p className="font-medium">
<span className="line2">
{calculated ? monthlyIncome : oldMonthlyIncome}
@@ -46,7 +46,7 @@ export function ConsultationRange({
start,
root.current!.clientWidth - 48
),
(root.current!.clientWidth - 48) / 35
((root.current!.clientWidth - 48) / 35) * 6
);
el.style.left = `${dx}px`;
setOffset(dx);
@@ -63,7 +63,7 @@ export function ConsultationRange({
<div className="md:space-y-3 md:max-lg:flex-1 space-y-2">
<p className="font-medium text-[#7A7A7A] btnl">Консультаций в месяц</p>
<div
className="lg:py-7 py-5 px-8 bg-[#37393B99] rounded-2xl relative lg:w-[360px] w-full flex justify-end"
className="lg:py-[27px] py-5 px-8 bg-[#37393B99] rounded-2xl relative lg:w-[25vw] w-full flex justify-end"
ref={root}
>
<div
@@ -35,7 +35,7 @@ export function RegionSelector({
</p>
<div
ref={root}
className="px-8 lg:py-6 py-4 bg-[#37393B99] rounded-2xl flex items-center justify-between lg:w-[360px]"
className="px-8 lg:py-6 py-4 bg-[#37393B99] rounded-2xl flex items-center justify-between lg:w-[25vw]"
onClick={() => setOpened((prev) => !prev)}
>
<p className="lg:btnl btnl font-medium">{chosen.name}</p>
@@ -26,8 +26,8 @@ export function Clients() {
className="lg:space-y-16 md:max-lg:space-y-12 space-y-10 lg:mt-40 mt-[100px]"
ref={ref}
>
<div className="flex">
<Title className="max-w-2/3 mx-auto text-center">
<div className="flex justify-between">
<Title className="lg:max-w-2/3a mx-autotext-center">
<span className="text-gradient">
{companies !== undefined && getCompaniesCount(companies.length)}
</span>{' '}
+3 -3
View File
@@ -41,7 +41,7 @@ export function Slider({
<p className="btnl font-medium opacity-60">
Последнее в{city.startsWith('В') ? 'о' : ''} {prepositionCity(city)}
</p>
<div className="flex gap-4">
<div className="flex gap-1">
{companies.map(({ id, mapIcon, title }, index) => (
<button
onClick={() => setCurrent(index)}
@@ -52,8 +52,8 @@ export function Slider({
>
<Image
src={process.env.NEXT_PUBLIC_S3_BUCKET + mapIcon!}
width={48}
height={48}
width={56}
height={56}
alt={title}
/>
</button>
+13 -8
View File
@@ -1,5 +1,7 @@
'use client';
import { StoriesModal } from '@/components/modals/StoriesModal';
import { useModalStore } from '@/stores/useModalStore';
import { Title } from '@/ui/Title';
import { motion, useInView } from 'framer-motion';
import Image from 'next/image';
@@ -25,14 +27,14 @@ export function Motivation() {
margin: '100% 0px -85% 0px',
});
const { setModal } = useModalStore();
return (
<div className="lg:space-y-[4.444vw] md:space-y-[6.25vw] space-y-[11.111vw]">
<Title headerLevel={1} className="sm:max-lg:text-5xla text-center">
Помогаем девелоперам
<br />
продавать недвижимость проще и&nbsp;
<Title headerLevel={1} className="text-center">
Помогаем девелоперам продавать&nbsp;недвижимость проще и&nbsp;
<span className="relative">
<span className="text-[#37393B]">быстрее </span>
<span className="text-[#37393B]">быстрее&nbsp;</span>
<span className="absolute top-[55%] -left-[2.5%] 2xl:h-1.5 h-1 w-full bg-white" />
</span>
дороже
@@ -55,8 +57,11 @@ export function Motivation() {
className="rounded-2xl h-full w-full object-cover"
/>
</div>
<div className="max-md:space-y-[4.444vw]a max-md:flex flex-col justify-between lg:w-[28.611vw] md:max-lg:w-[47.135vw] lg:h-[15.694vw] md:max-lg:h-[29.427vw] w-[46.111vw] h-[61.111vw] lg:p-[1.667vw] p-4 lg:col-start-2 lg:row-start-1 row-start-2 bg-[radial-gradient(ellipse_at_bottom_right,#7A7A7A50,transparent)] rounded-2xl relative">
<p className="font-medium heading2 lg:w-[10.278vw] md:max-lg:w-[19.271vw]">
<button
onClick={() => setModal(<StoriesModal />, 'stories')}
className="flex flex-col justify-between lg:w-[28.611vw] md:max-lg:w-[47.135vw] lg:h-[15.694vw] md:max-lg:h-[29.427vw] w-[46.111vw] h-[61.111vw] lg:p-[1.667vw] p-4 lg:col-start-2 lg:row-start-1 row-start-2 bg-[radial-gradient(ellipse_at_bottom_right,#7A7A7A50,transparent)] rounded-2xl relative cursor-pointer"
>
<p className="font-medium heading2 lg:w-[10.278vw] md:max-lg:w-[19.271vw] self-start text-left">
Интеграция в&nbsp;офисы продаж
</p>
<div className="lg:w-[11.667vw] md:max-lg:w-[21.875vw] w-[37.222vw] aspect-square md:absolute relative lg:right-[1.667vw] lg:bottom-[0.903vw] md:max-lg:right-[3.125vw] md:max-lg:bottom-[1.693vw]">
@@ -102,7 +107,7 @@ export function Motivation() {
className="absolute top-0 text-white rounded-full"
/> */}
</div>
</div>
</button>
<div className="relative lg:w-[28.611vw] lg:h-[19.028vw] md:max-lg:w-[47.135vw] md:max-lg:h-[29.427vw] overflow-hidden w-[46.111vw] h-[61.111vw] bg-[radial-gradient(ellipse_at_bottom_right,#7A7A7A50,transparent)] col-start-2 row-start-2 lg:p-[1.667vw] p-4 rounded-2xl flex flex-col lg:gap-[1.944vw] md:max-lg:gap-[3.125vw] gap-[3.333vw]">
<p className="heading2 font-medium">Удаленная демонстрация</p>
<div className="md:relative absolute lg:w-[19.583vw] md:max-lg:w-[40.885vw] w-[73.333vw] self-center max-md:left-4 max-md:-bottom-0.5">
@@ -22,22 +22,22 @@ export function IntegrationCRM({ scroll }: { scroll: MotionValue<number> }) {
<div className="space-y-15 max-w-9/10">
<div className="space-y-4">
<div className="flex">
<div className="w-14 h-14 rounded-lg bg-[#B5F54E] flex items-center text-black font-medium justify-center -rotate-[4deg] text1 shadow-[0_2px_10px_0_#00000033] z-[2]">
<div className="w-16 h-16 rounded-lg bg-[#B5F54E] flex items-center text-black font-medium justify-center -rotate-[4deg] text1 shadow-[0_2px_10px_0_#00000033] z-[2]">
2K
</div>
<div className="bg-[#37393B] rounded-lg right-2 relative shadow-[0_2px_10px_0_#00000033] z-[1]">
<Image
src={'/img/pages/home/presentation/flat1.png'}
width={56}
height={56}
width={64}
height={64}
alt="rooms"
/>
</div>
<div className="bg-[#37393B] rounded-lg -rotate-[4deg] right-4 relative shadow-[0_2px_10px_0_#00000033]">
<Image
src={'/img/pages/home/presentation/flat2.png'}
width={48}
height={48}
width={64}
height={64}
alt="rooms"
/>
</div>
@@ -51,15 +51,15 @@ export function IntegrationCRM({ scroll }: { scroll: MotionValue<number> }) {
<div className="flex">
<Image
src={'/img/pages/home/presentation/vova.png'}
width={56}
height={56}
width={64}
height={64}
alt="vovka"
className="rounded-lg -rotate-[4deg] shadow-[0_2px_10px_0_#00000033] z-[2]"
/>
<div className="rounded-lg bg-[#37393B] flex justify-center items-center relative right-2 w-14 h-14 shadow-[0_2px_10px_0_#00000033] z-[1]">
<div className="rounded-lg bg-[#37393B] flex justify-center items-center relative right-2 w-16 h-16 shadow-[0_2px_10px_0_#00000033] z-[1]">
<MailIcon className="text-white w-8 h-8" />
</div>
<div className="rounded-lg bg-[#37393B] flex justify-center items-center relative right-4 -rotate-[4deg] w-14 h-14 shadow-[0_2px_10px_0_#00000033]">
<div className="rounded-lg bg-[#37393B] flex justify-center items-center relative right-4 -rotate-[4deg] w-16 h-16 shadow-[0_2px_10px_0_#00000033]">
<PhoneIcon className="w-8 h-8 text-white" />
</div>
</div>
@@ -19,7 +19,7 @@ export function PresentationDesktop() {
<Title>
Интерактивная презентация{' '}
<span className="text-gradient">улучшает опыт выбора недвижимости</span>{' '}
и увеличивают темпы продаж квартир в жилом комплексе
и&nbsp;увеличивают темпы продаж квартир в&nbsp;жилом комплексе
</Title>
<div className="h-[480vh]" ref={target}>
<div className="flex gap-x-3 items-stretch h-[480vh] max-h-[80vh] overflow-hidden w-full sticky duration-1000 top-24">
@@ -32,7 +32,7 @@ export function PresentationMini() {
return (
<div className="mt-25 lg:hidden">
<div className="md:space-y-12 md:top-30 xs:-top-10 -top-40 sticky space-y-5">
<div className="md:space-y-12 md:top-0 xs:-top-10 -top-40 sticky space-y-5">
<Title>
Интерактивная презентация{' '}
<span className="text-gradient">
@@ -1,7 +1,8 @@
import { search } from '@/consts/presentation/search';
import { useMediaQueries } from '@/hooks/useMediaQueries';
import { Icon } from '@/ui/Icon';
import { motion, MotionValue, useTransform } from 'framer-motion';
import HeartIcon from '../../../../../public/icons/hearth.svg';
import LocationIcon from '../../../../../public/icons/location.svg';
export function SearchAndSelect({ scroll }: { scroll: MotionValue<number> }) {
const opacity = useTransform(scroll, [0, 1 / 6], [1, 0]);
@@ -17,30 +18,18 @@ export function SearchAndSelect({ scroll }: { scroll: MotionValue<number> }) {
>
<div className="md:p-6 md:rounded-2xl md:bg-radial-[at_100%_100%] from-[#7A7A7A66] md:backdrop-blur-[500px] xs:max-md:space-y-4 space-y-2 md:flex flex-col justify-between md:flex-1">
<p className="heading2 font-medium">Выбор квартиры на генплане</p>
<div className="xs:max-md:space-y-4 space-y-2">
<div className="space-y-4">
<div className="flex items-center gap-6">
<div className="bg-[#FF4517] rounded-[9px] p-[4.5px]">
<Icon
name="hearth"
color="#232425"
svgProp={{
className: 'max-md:w-[22.5px] max-md:w-[22.5px] w-5 h-5',
}}
/>
<div className="bg-[#FF4517] rounded-[9px] p-[0.556vw]">
<HeartIcon className="text-[#232425] w-[1.389vw] h-[1.389vw]" />
</div>
<p className="text1 md:max-w-[70%]">
Эмоциональное вовлечение пользователя в выбор квартиры
</p>
</div>
<div className="flex items-center gap-6">
<div className="bg-[#B5F54E] rounded-[9px] p-[4.5px]">
<Icon
name="location"
color="#232425"
svgProp={{
className: 'max-md:w-[22.5px] max-md:w-[22.5px] w-5 h-5',
}}
/>
<div className="bg-[#B5F54E] rounded-[9px] p-[0.556vw]">
<LocationIcon className="text-[#232425] w-[1.389vw] h-[1.389vw]" />
</div>
<p className="text1 md:max-w-[70%]">
Удобство выбора расположения и видовых характеристик
@@ -37,13 +37,15 @@ export function ReviewTab({
}}
ref={ref}
>
<Image
src={image}
width={96}
height={96}
alt={title}
className="rounded-xl aspect-square"
/>
<div className="aspect-square">
<Image
src={image}
width={96}
height={96}
alt={title}
className="rounded-xl aspect-square"
/>
</div>
{active && (
<motion.div
key={index}
@@ -21,7 +21,7 @@ export function Reviews() {
const [currentHovered, setCurrentHovered] = useState<number>();
const isInView = useInView(ref, { margin: '100% 0px -85% 0px' });
const isInView = useInView(ref, { margin: '100% 0px -75% 0px' });
return (
<div className="max-lg:space-y-2">
@@ -34,10 +34,8 @@ export function Reviews() {
}`}
>
<VideoPlayer src={reviewsData[tab].src} showMutingBtn ref={videoRef}>
<div className="lg:space-y-6 space-y-4 lg:max-w-[35.208vw] absolute lg:left-6 lg:top-6 md:max-lg:top-4 left-4 bottom-4 transition-opacity z-[5]">
<p className="max-w-[507px] text2 opacity-80">
{reviewsData[tab].author}
</p>
<div className="lg:space-y-6 space-y-4 absolute lg:left-6 lg:top-6 md:max-lg:top-4 left-4 bottom-4 transition-opacity z-[5] max-w-[40vw]">
<p className="text2 opacity-80">{reviewsData[tab].author}</p>
<p className="accent font-medium">{reviewsData[tab].text}</p>
</div>
<div className="absolute flex flex-nowrap bottom-6 left-6 gap-2 z-[6] max-lg:hidden">
@@ -26,12 +26,13 @@ export function Streaming() {
<div className="lg:mt-[140px] mt-[100px] lg:space-y-[4.444vw] md:max-lg:space-y-[6.25vw] space-y-[11.111vw]">
<Title className="max-md:hidden">
Уникальная технология
<span className="text-gradient"> удаленной демонстрации</span> дает
возможность презентовать объект покупателю из&nbsp;любой точки мира
<span className="text-gradient"> удаленной демонстрации</span>{' '}
дает&nbsp;возможность презентовать объект покупателю из&nbsp;любой точки
мира
</Title>
<Title headerLevel={2} className="md:hidden text-center">
<span className="text-gradient">Удаленная демонстрация </span>
презентуйте объект покупателю из любой точки мира
презентуйте объект покупателю из&nbsp;любой точки мира
</Title>
<div
className="lg:grid md:max-lg:flex grid-cols-4 gap-3 px-5 md:-mx-5 md:max-lg:overflow-auto [scrollbar-width:none] relative max-md:aspect-[340/344] transform-3d items-stretch"
@@ -68,7 +69,8 @@ export function Streaming() {
Расскажем и покажем как это работает на&nbsp;созвоне
</p>
<Button
className="btnl group-hover:scale-105 px-6 py-4 transition-transform rounded-lg"
rounded="2xl"
className="btnm font-medium group-hover:scale-105 px-6 py-[17px] transition-transform"
onClick={() => {}}
>
Оставить заявку
@@ -82,8 +84,9 @@ export function Streaming() {
loop
autoPlay
muted
className="lg:apect-[1400/640] md:max-lg:aspect-[736/480] max-md:aspect-[340/600]"
>
<p className="absolute text-[32px] font-medium bottom-6 left-6 lg:max-w-[40%] md:max-lg:max-w-[80%] accent">
<p className="absolute font-medium bottom-6 left-6 lg:max-w-[40%] md:max-lg:max-w-[80%] accent">
Graff.estate stream доступен на&nbsp;любых устройствах, для&nbsp;
демонстрации нужен только интернет
</p>
@@ -37,12 +37,12 @@ export function StreamingProject({
}}
/>
<div className="lg:self-end space-y-3 font-medium z-1">
<p className="heading1">{title}</p>
<p className="heading1 font-medium">{title}</p>
<div className="flex flex-wrap gap-1">
{company && (
<div className="px-2 py-1.5 flex gap-1 items-center rounded-2xl bg-[#37393B99] btns">
<div
className="w-2 h-2 rounded-full"
className="w-2 h-2 rounded-full m-1"
style={{ backgroundColor: company.color }}
/>
{company.title}
@@ -53,26 +53,23 @@ export function StreamingProject({
</p>
</div>
</div>
<div className="absolute bottom-4 right-4 lg:hidden">
<Button
rounded="xl"
className="px-3 py-2"
icon={<ArrowMoreIcon className="w-4 h-4 text-white" />}
>
Смотреть
</Button>
</div>
<Link
href={href}
className="hidden lg:group-hover:block absolute w-full h-full left-0 bottom-0 md:max-lg:rounded-2xl rounded-xl font-medium backdrop-blur-[3px] content-center text-center z-2"
className="hidden lg:group-hover:block absolute w-full h-full left-0 bottom-0 md:max-lg:rounded-2xl rounded-xl font-medium backdrop-blur-[3px] content-center text-center"
>
<p className="btnl flex justify-center gap-2">
Начать демонстрацию <ArrowMoreIcon className="text-white w-4 h-4" />
</p>
</Link>
<Link
className="lg:group-hover:block hidden bottom-4 right-4 absolute z-2"
href={'/'}
>
<Button
icon={<ArrowMoreIcon className="text-white w-4 h-4" />}
className="btns px-3 py-2"
rounded="xl"
>
Смотреть
</Button>
</Link>
</div>
);
}
@@ -1,7 +1,7 @@
'use client';
import { IProject } from '@/types/IProject';
import Link from 'next/link';
import { motion } from 'framer-motion';
import { usePathname } from 'next/navigation';
import ArrowMoreIcon from '../../../../public/icons/arrow_more.svg';
import { AwardsCard } from './AwardsCard';
@@ -25,13 +25,21 @@ export function ProjectsSection({ projects }: { projects: IProject[] }) {
<AwardsCard className="sm:col-start-2 sm:row-start-2 row-start-3" />
)}
{pathname === '/' && (
<Link
<motion.a
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
viewport={{ once: true, margin: '-100px' }}
transition={{
duration: 1,
ease: [0.58, 0.12, 0.27, 0.98],
delay: 0.2,
}}
href={'/projects'}
className="rounded-xl bg-[#232425] aspect-square self-center p-5 opacity-60 hover:opacity-100 transition-opacity flex items-center justify-center btnl font-medium gap-2"
>
Смотреть все
<ArrowMoreIcon className="w-5 h-5 text-white" />
</Link>
</motion.a>
)}
</div>
);
@@ -78,7 +78,7 @@ export function TagsFilters({
)}
<button
onClick={handleApplyClick}
className="bg-gradient rounded-2xl btnm flex items-center gap-2 pl-6 py-4 pr-4 font-medium z-[11] -mb-5 mt-5"
className="bg-gradient rounded-2xl btnm flex items-center gap-2 pl-6 py-4 pr-4 font-medium z-[11] -mb-5 mt-5 cursor-pointer"
>
Применить
<CheckIcon className="text-white w-4 h-4" />
@@ -88,7 +88,7 @@ export function TagsFilters({
) : (
<button
onClick={() => setOpen(true)}
className="gap-x-2 rounded-2xl flex items-center pl-6 p-4 bg-[#37393B99] backdrop-blur-sm"
className="gap-x-2 rounded-2xl flex items-center pl-6 p-4 bg-[#37393B99] backdrop-blur-sm cursor-pointer"
>
<p className="btnm font-medium">Фильтры</p>
<FilterIcon className="text-white w-4 h-4" />
+1 -1
View File
@@ -15,7 +15,7 @@ export function useArticleMutation({
action === 'create'
? await api.post('articles', { json }).json<IArticle>()
: await api.put(`articles/${id}`, { json }).json<IArticle>(),
onSuccess: async (article) => {
onSuccess: async () => {
await queryClient.invalidateQueries({ queryKey: ['articles'] });
},
});
+2 -2
View File
@@ -31,8 +31,8 @@ export function Button({
if (type !== 'submit') e.preventDefault();
onClick?.();
}}
className={`group cursor-pointer relative px-6 py-2 rounded-${
rounded ?? 'full'
className={`group cursor-pointer relative px-6 py-2${
rounded ? ' rounded-' + rounded : ''
} min-w-fit ${
(color === 'primary'
? 'bg-gradient-to-r from-[#798FFF] to-[#D375FF]'
+1 -3
View File
@@ -27,10 +27,8 @@ export function Option<T extends keyof FieldValues>({
<>
<label
htmlFor={text}
className={`cursor-pointer transition-colors rounded-xl font-medium text-nowrap select-none ${
className={`cursor-pointer transition-colors rounded-xl font-medium text-nowrap select-none px-6 py-[17px] btnm ${
checked ? 'bg-white text-black' : 'bg-[#37393B99]'
} ${
fieldName === 'products' ? 'px-6 py-[17px] btnm' : 'px-3 py-2 btns'
}`}
>
{text}
+4 -4
View File
@@ -58,9 +58,9 @@ export function ProductItem({
<div className="relative">
<Image
src={image}
className="rounded-lg !relative min-w-[calc(224/768*100vw)]"
className="rounded-lg !relative min-w-[29.167vw]"
fill
sizes="calc(224/768*100vw)"
sizes="29.167vw"
alt={text}
/>
</div>
@@ -74,9 +74,9 @@ export function ProductItem({
<Image
src={image}
alt={text}
className="rounded-lg !relative min-w-[calc(312/1440*100vw)]"
className="rounded-lg !relative min-w-[21.667vw]"
fill
sizes="calc(312/1440*100vw)"
sizes="21.667vw"
/>
</div>
</motion.div>
+1 -1
View File
@@ -11,7 +11,7 @@ export function SlideBadge({
}) {
return (
<div
className={`flex md:gap-3 gap-1 lg:items-center max-md:flex-col md:max-lg:justify-between md:bg-[#37393B99] rounded-2xl lg:p-1 md:p-2 lg:pr-10 md:backdrop-blur-[20px]${
className={`flex md:gap-3 gap-1 lg:items-center max-md:flex-col md:max-lg:justify-between md:bg-[#37393B99] rounded-2xl lg:p-1 md:max-lg:p-2 lg:pr-10 md:backdrop-blur-[20px]${
className ? ' ' + className : ''
}`}
>
+2 -2
View File
@@ -18,7 +18,7 @@ export function StoriesButton() {
'stories'
)
}
className="p-0.5 cursor-pointer outline-none rounded-full aspect-square relative first:translate-x-2 last:-translate-x-2 [background:linear-gradient(#2a2c34,#2a2c34)_padding-box,linear-gradient(45deg,#FF52E5,#F6D242)_border-box]"
className="p-px cursor-pointer outline-none bg-gradient rounded-full aspect-square relative first:translate-x-2 last:-translate-x-2"
key={id}
>
<Image
@@ -26,7 +26,7 @@ export function StoriesButton() {
width={52}
height={52}
alt={title}
className="rounded-full object-cover object-center aspect-square"
className="rounded-full object-cover object-center aspect-square border-[2px] border-[#0F1011]"
/>
</button>
))}