ویرایشگر عکس


ظاهر برنامه

پارسا که با پنل ادمین دیجی‌کال‍ا کار می‌کرد، متوجه شد بعضی از عکس‌های محصولات وضعیت مناسبی ندارند و برای درست دیده شدن آن‌ها لازم است تا تغییراتی روی آن‌ها اعمال شود. پارسا که از قضا خیلی هم صبور نبود، برای این کار از رامین خواست که خیلی فوری در صفحه‌ی عکس محصولات، ابزاری برای او فراهم کند که بتواند با آن‌ها عکس کالا را بچرخاند، برگرداند (flip کند) و مقدار روشنایی آن را تنظیم کند.

از طرفی، پارسا از بچگی به عکس‌های هنری هم علاقه داشت. به همین دلیل، به رامین گفت که برایش چند فیلتر هم آماده کند که بتواند عکس‌های سایت را هنری کند!

رامین که خیلی قلب مهربانی داشت، درخواست پارسا را قبول کرد، اما چون باید در جلسه‌ی تیم فرانت‌اند شرکت می‌کرد، تنها فرصت کرد ظاهر صفحه‌ی عکس کالا را پیاده‌سازی کند. حال، از شما می‌خواهیم که تا قبل از تمام شدن جلسه‌ی رامین، با کامل کردن عملکرد این صفحه به او کمک کنید تا بتواند موارد درخواستی پارسا را به موقع به دستش برساند.

جزئیات پروژه🔗

پروژه‌ی اولیه را از این لینک دانلود کنید.

ساختار فایل‌های پروژه
image-editor
├── assets
│   ├── css
│   │   ├── editor.css
│   │   └── style.css
│   ├── font
│   │   └── Vazir.ttf
│   └── img
│       ├── favicon.ico
│       ├── flip.png
│       └── template.jpg
├── index.html
├── package.json
└── script.js
Plain text
  • برای پیاده‌سازی هر یک از ابزارهای خواسته شده، یک div خاص در نظر گرفته شده است که به‌صورت زیر هستند:

    • preview: برای چرخش، تغییرات روشنایی و اعمال فیلترها
    • preview-scale: برای بزرگ‌نمایی با موس
    • preview-flip: برای flip عمودی و افقی
  • در این سؤال، شما باید ۷ فیلتر را پیاده‌سازی کنید که در جدول زیر به آن‌ها اشاره شده است:

نوع فیلتر شدت فیلتر
grayscale 1
sepia 1
invert 1
blur 2px
saturate 2
contrast 2
hue-rotate 90deg

برای پیاده‌سازی آن‌ها، کافی است از فیلتر معادل آن‌ها در CSS به همراه مقدار ذکر شده برای آن استفاده کنید. برای مثال، فیلتر grayscale به صورت grayscale(1) خواهد بود.

  • با کلیک کردن روی گزینه‌ی none در لیست فیلترها، اگر فیلتری روی عکس اعمال شده باشد، باید حذف شود.
  • میزان چرخش عکس از ۰ تا ۳۶۰ درجه و میزان روشنایی عکس از ۰ تا ۲ می‌تواند تغییر کند.
  • هنگام چرخاندن تصویر، باید دقت شود که کادر مربعی تصویر (preview-container) نباید خالی شود. برای این کار لازم است به همراه چرخش مقداری بزرگ‌نمایی نیز انجام شود. این بزرگ‌نمایی به ازای هر θ\theta درجه چرخش، sin(θ)+cos(θ)|sin(\theta)| + |cos(\theta)| خواهد بود. تصویر زیر، این مورد را واضح‌تر بیان می‌کند:

rotate and scale

  • از بین فیلترها تنها یک مورد در لحظه قابل اعمال است و با هر انتخاب فیلتر، قبلی جایگزین می‌شود.
  • باید امکان استفاده از فیلتر، تغییر مقدار روشنایی، flip کردن افقی و عمودی و چرخاندن به صورت هم‌زمان وجود داشته باشد.
  • با دوباره زدن روی دکمه‌های flip باید تصویر به حالت قبلی خود برگردد.
  • با بردن موس روی عکس، باید عکس با ضریب ۲ بزرگ‌نمایی شده و با حرکت موس در کادر تصویر، تصویر نیز جابه‌جا شود. در نهایت با خارج شدن موس از کادر، مقدار بزرگ‌نمایی به حالت قبلی خود بازگردد. برای جابه‌جا شدن تصویر، می‌بایست مقدار transform-origin را با توجه به موقعیت نشان‌گر موس روی تصویر (یا به طور دقیق‌تر، روی preview-scale) تغییر دهید. برای این کار باید ابتدا موقعیت موس در صفحه‌ی نمایش را با استفاده از pageX و pageY (که از ایونت e ورودی تابع handleMouseMove قابل دسترسی‌اند) به دست آورید. سپس با محاسبه‌ی فاصله‌ی preview-scale از سمت چپ و بالای صفحه نمایش به کمک offsetLeft و offsetTop، موقعیت موس روی preview-scale را محاسبه کنید.
  • هنگامی که موس روی تصویر حرکت می‌کند، سایر فیلترهایی که اعمال شده‌اند باید بدون تغییر باقی بمانند.
  • پس از چرخاندن یا flip کردن، نباید scale شدن و حرکت موس روی تصویر دچار مشکل شده یا کادر مربعی تصویر (preview-container) خالی شود.

نکات🔗

  • محاسبات ریاضی را تا حداکثر ۵ رقم اعشار انجام دهید. برای این کار، می‌توانید از تابع toFixed جاوااسکریپت استفاده کنید.
  • تصویر در ابعاد مربعی خواهد بود.
  • شما تنها مجاز به اعمال تغییرات در فایل script.js هستید.

آن‌چه باید آپلود کنید🔗

پس از پیاده‌سازی موارد خواسته‌شده، فایل script.js را آپلود کنید.

روتر جاوااسکریپتی


ظاهر برنامه

شکرستون یک شهرِ به شدت در حال رشد است و میزان خرید شهروندانش بسیار بالا رفته، اما از آن‌جایی که مشکلات زیرساختی زیادی درونش وجود دارد، روی تجربه‌ی کاربری خیلی از محصولاتش تأثیر گذاشته است. مهدی یک مهندس نرم‌افزار در دیجی‌کال‍ای شکرستون است و قصد دارد طی یه حرکت خفن، تجربه‌ی کاربری این سایت را بهتر کند!

یکی از مواردی که از نظر مهدی خیلی روی تجربه‌ی کاربری سایت تأثیرگذار است، refresh شدن صفحات پس از کلیک کردن روی لینک‌ها است. پس از کمی سرچ و مشورت، او متوجه شد که راه‌حل مشکلش، استفاده از کتاب‌خانه‌ی react-router است اما متأسفانه دیجی‌کال‍ا در شکرستون با جاوااسکریپت خام نوشته شده!

مهدی تصمیم گرفته که وارد میدان شود و با کمک شما، یک کتاب‌خانه‌ی client router بسازد. از آن‌جایی که تجربه‌ی کاربر برای مهدی خیلی مهم است، جابه‌جایی بین صفحات نباید باعث refresh شدن صفحه شود تا سرعت برنامه بالاتر رود. همچنین زمانی که کاربر روی دکمه‌های جلو و عقب مرورگر کلیک می‌کند، باید صفحه‌ی قبلی یا بعدی بدون تأخیر نمایش داده شود.

مهدی به تنهایی نمی‌تواند کل این کتاب‌خانه را پیاده‌سازی کند. او از شما خواسته که در پیاده‌سازی این کتاب‌خانه به او کمک کنید.

جزئیات پروژه🔗

پروژه‌ی اولیه را از این لینک دانلود کنید.

ساختار فایل‌های پروژه
client-router
├── assets
│   ├── css
│   │   └── style.css
│   ├── fonts
│   │   └── vazir.woff2
│   └── product_data.json
├── clientRouter.js
├── index.html
├── main.js
└── package.json
Plain text
راه‌اندازی پروژه

برای اجرای پروژه، باید Node.js و npm را از قبل نصب کرده باشید.

  • پروژه‌ی اولیه را دانلود و از حالت فشرده خارج کنید.
  • در پوشه‌ی client-router دستور npm install را برای نصب نیازمندی‌ها اجرا کنید.
  • در همین پوشه، دستور npm start را برای راه‌اندازی پروژه اجرا کنید.

یکی از جذاب‌ترین مواردی که مهدی در react-router دیده بود، قابلیت route params است. مهدی نیز تصمیم گرفته این قابلیت را به کتاب‌خانه‌اش اضافه کند. این قابلیت به این‌صورت کار می‌کند که هنگام تعریف route ، اگر قبل از یکی از پارامترها : گذاشته شود، آن بخش از URL هنگام render به فانکشنش پاس داده می‌شود. مثال:

route:🔗

/product/:id/comments/:user
Plain text

render function:🔗

({id, user}) => { `<div>${id}-${user}</div>` }
JavaScript

پروژه شامل دو فایل main.js و clientRouter.js است.

فایل main.js شامل صفحات برنامه است. هر صفحه یک فانکشن است که route params را به‌عنوان ورودی دریافت کرده و یک رشته‌ی HTML را در جواب برمی‌گرداند.

در انتها نیز کتابخانه‌ی clientRouter صدا زده شده و صفحات به‌عنوان یک پارامتر به آن پاس داده می‌شوند.

شما باید فایل clientRouter.js را طوری پیاده‌سازی کنید که برنامه همانند ویدیوی موجود در ابتدای صورت سؤال کار کند.

فایل clientRouter.js شامل توابع زیر است:

  • تابع processRoutes: این تابع پس از هر تغییر در صفحه صدا زده می‌شود و وظیفه‌ی پردازش و مقایسه‌ی route ها را برعهده دارد.
  • تابع handleLinks: این تابع وظیفه‌ی اضافه کردن event listener ها را به لینک‌های موجود در صفحه برعهده دارد.
  • تابع handleRouteChange: این تابع وظیفه‌ی گوش کردن به تغییرات URL و اجرای توابع بالا بر حسب نیاز را برعهده دارد و از قبل پیاده‌سازی شده است.

نکات🔗

  • فایل clientRouter باید به گونه‌ای طراحی شود که تنها با اجرای تابع initializeRouter و پاس دادن ورودی های مناسب، برنامه به‌صورت یک SPA شروع به کار کند.
  • صفحات در قالب توابعی که یک رشته‌ی HTML برمی‌گردانند تعریف می‌شوند و route param ها به‌عنوان یک آبجکت به این توابع پاس داده می‌شوند.
  • اگر clientRouter نتوانست با هیچ‌کدام از صفحات مچ شود، باید صفحه ی 404 به کاربر نشان داده شود.
  • لینک‌هایی که توسط client router هندل می شوند، به جای استفاده از اتریبیوت href، از اتریبیوت data-href استفاده می‌کنند تا از لینک های معمولی متمایز شوند. نمونه:
    <a data-href="/products">test link</a>
    HTML
  • هنگام هندل کردن event های مربوط به لینک‌ها، به جای استفاده از event.target، از event.currentTarget استفاده کنید تا هنگام کلیک روی element های زیرمجموعه لینک شما، بتوانید به element والد دسترسی داشته باشید. جزئیات بیشتر
  • شما تنها مجاز به اعمال تغییرات در فایل clientRouter.js هستید.

آن‌چه باید آپلود کنید🔗

پس از پیاده‌سازی موارد خواسته‌شده، فایل clientRouter.js را آپلود کنید.

تطهیر کلاس‌ها


ظاهر برنامه

امین تازه در دیجی‌کال‍ا جذب شده و از آن‌جایی که خیلی به چالش های فنی علاقه‌مند است، بابک و فرزاد تصمیم گرفته‌اند از او بخواهند راه‌حلی برای یک مشکل پیدا کند. به امین در یافتن راه‌حل کمک کنید.

جزئیات پروژه🔗

پروژه‌ی اولیه را از این لینک دانلود کنید.

ساختار فایل‌های پروژه
purge-classnames
├── cypress
│   ├── fixtures
│   │   └── example.json
│   ├── integration
│   │   └── sample.spec.js
│   ├── plugins
│   │   └── index.js
│   └── support
│       ├── commands.js
│       └── index.js
├── cypress.json
├── index.html
├── package.json
├── script.js
└── styles.css
Plain text

امین باید تابعی با نام purgeClassNames پیاده‌سازی کنه که تعداد آرگومان‌هاش متغیر است. مثال:

purgeClassNames('m-5', 'd-block', 'mr-5')
purgeClassNames('p-3', 'oy-12')
JavaScript

این تابع باید کلاس‌های مشابه را بر اساس اولویت حذف کند، یعنی کلاس‌هایی که زودتر به تابع پاس داده شده‌اند اولویت کمتری دارند. مثلاً خروجی مثال بالا باید به‌صورت زیر باشد:

purgeClassNames('m-5', 'd-block', 'm-12') === 'd-block m-12' // true
JavaScript

در مثال فوق، کلاس m-5 با کلاس m-12 جایگزین می‌شود، زیرا m-12 دیرتر آمده و اولویت بیشتری دارد. کلاس d-block بدون هیچ تغییری به خروجی منتقل می‌شود.

توضیح کلاس‌ها🔗

کلاس margin🔗

  • کلاس m-n: مارجین از همه‌ی جهات به اندازه‌ی nn
  • کلاس ‌ml-n: مارجین از چپ
  • ‌ کلاس mr-n: مارجین از راست
  • ‌ کلاس mb-n: مارجین از پایین
  • ‌ کلاس mt-n: مارجین از بالا
  • ‌ کلاس my-n: مارجین از بالا و پایین
  • ‌ کلاس mx-n: مارجین از چپ و راست

در توضیحات بالا، nn می‌تواند هر عددی باشد. مثال:

m-10 mr-999 mx-11

چند نمونه تست کیس برای کلاس margin:🔗

ورودی ۱:

purgeClassNames('m-5', 'my-2')
JavaScript

خروجی ۱:

my-2 mx-5
Plain text

کلاس m-5 می‌تواند به my-5 mx-5 تجزیه شود. بنابراین my-2 جایگزین my-5 شده و خروجی برابر با my-2 mx-5 خواهد بود.

ورودی ۲:

purgeClassNames('mx-3', 'mr-1')
JavaScript

خروجی ۲:

mr-1 ml-3
Plain text

ورودی ۳:

purgeClassNames('m-3', 'mt-1')
JavaScript

خروجی ۳:

mt-1 mx-3 mb-3
Plain text

نکته: m-n می‌تواند به mx-n و my-n تجزیه شود. کلاس mx-n نیز می‌تواند به mr-n و ml-n تجزیه شود. به همین ترتیب، کلاس my-n نیز شامل mt-n و mb-n می‌شود. در این مثال‌ها، nn هر عددی می‌تواند باشد.

کلاس padding🔗

قواعد کلاس margin برای این کلاس هم وجود دارند. مثال:

pr-6 یا p-5 یا py-3 و...

کلاس display🔗

ساختار کلاس به صورت d-OPTION است که OPTION می‌تواند یکی از مقادیر زیر باشد:

  • flex
  • inline
  • inline-flex
  • block
  • inline-block

مثال:

d-block d-inline d-flex

یک نمونه تست کیس برای کلاس display:🔗

ورودی:

purgeClassNames('d-block', 'd-flex')
JavaScript

خروجی:

d-flex
Plain text

کلاس overflow🔗

ساختار کلاس overflow به‌صورت زیر است:

  • overflow-OPTION در تمامی جهات
  • overflow-y-OPTION درجهت محور y
  • overflow-x-OPTION در جهت محور x

در مثال‌های بالا، OPTION می‌تواند یکی از مقادیر زیر باشد:

  • hidden
  • visible
  • scroll
  • auto
  • none

چند نمونه تست کیس برای کلاس overflow:🔗

ورودی ۱:

purgeClassNames('overflow-auto', 'overflow-x-none')
JavaScript

خروجی ۱:

overflow-x-none overflow-y-auto
Plain text

ورودی ۲:

purgeClassNames('overflow-x-auto', 'overflow-y-auto')
JavaScript

خروجی ۲:

overflow-auto
Plain text

ورودی ۳:

purgeClassNames('overflow-x-visible', 'overflow-none')
JavaScript

خروجی ۳:

overflow-none
Plain text

نکته: کلاس overflow-OPTION می‌تواند به کلاس‌های overflow-x-OPTION و overflow-y-OPTION تجزیه شود.

نقطه شکست‌ها (Breakpoints)🔗

نقطه شکست‌ها اندازه های تعریف شده‌ای هستند که به ما این امکان را می‌دهند تا یک کلاس را فقط در یک اندازه‌ی مشخص اعمال کنیم.

هر کلاس می‌تواند فقط در یک breakpoint خاص فعال باشد، برای مثال: lg:mr-2 فقط در breakpoint lg فعال است. تمامی کلاس هایی که بالا معرفی شدند باید قابلیت استفاده با breakpoints را داشته باشند.

لیست نقطه شکست های موجود:

  • xs
  • sm
  • md
  • lg

چند نمونه تست کیس برای breakpoints:🔗

ورودی ۱:

purgeClassNames('m-5', 'lg:m-2')
JavaScript

خروجی ۱:

m-5 lg:m-2
Plain text

همان‌طور که مشاهده می‌کنید، کلاس‌هایی که دارای breakpoint هستند، بر روی کلاس های معمولی اثری ندارند.

ورودی ۲:

purgeClassNames('sm:m-5', 'lg:m-2')
JavaScript

خروجی ۲:

sm:m-5 lg:m-2
Plain text

همان‌طور که مشاهده می‌کنید، breakpoint های متفاوت نیز بر روی یکدیگر تاثیر ندارند.

نکات🔗

  • ترتیب کلاس های خروجی داده شده توسط تابع purgeClassNames اهمیتی ندارد.
  • خروجی تابع purgeClassNames باید یک رشته شامل کلاس‌ها باشد که با استفاده از فاصله (space) از هم جدا شده‌اند.
  • تضمین می‌شود که فقط و فقط یک کلاس به ازای هر آرگومان به تابع داده خواهد شد.
  • دقت کنید که تعداد آرگومان های تابع متغیر خواهد بود.
  • حداقل یک آرگومان به تابع پاس داده خواهد شد.
  • اگر کلاسی به purgeClassNames داده شود که در لیست کلاس‌های بالا تعریف نشده بود، شما باید آن کلاس را بدون تغییر و به صورت مستقیم به خروجی ارسال کنید.
  • شما تنها مجاز به اعمال تغییرات در فایل script.js هستید.

آن‌چه باید آپلود کنید🔗

پس از پیاده‌سازی موارد خواسته‌شده، فایل script.js را آپلود کنید.

هوک فیلتر


در این سؤال، از کتاب‌خانه‌ی react-router-dom@6 استفاده شده است.


ظاهر برنامه

در دیجی‌کال‍ا تعداد زیادی داده وجود دارد که برخی اوقات لازم است آن‌ها را فیلتر کنیم. حال، می‌خوایم راهی پیدا کنیم که این فیلتر اعمال‌شده را به راحتی با بقیه به اشتراک بگذاریم. برای این کار، تصمیم گرفته‌ایم یک هوک بنویسیم که کارش هماهنگ کردن و اتصال state فرم فیلتر با آدرس صفحه است. بنابراین شما باید در کامل کردن این هوک (که آن را useFilter نامیده‌ایم) به ما کمک کنید.

جزئیات پروژه🔗

پروژه‌ی اولیه را از این لینک دانلود کنید.

ساختار فایل‌های پروژه
useFilter
├── public
│   ├── favicon.ico
│   └── index.html
├── src
│   ├── assets
│   │   ├── css
│   │   │   └── index.css
│   │   └── font
│   │       └── Vazir.ttf
│   ├── components
│   │   ├── chips
│   │   │   ├── chips.css
│   │   │   └── index.jsx
│   │   ├── formInput
│   │   │   ├── Checkbox
│   │   │   │   ├── Checkbox.css
│   │   │   │   └── index.jsx
│   │   │   ├── CheckboxGroup
│   │   │   │   ├── CheckboxGroup.css
│   │   │   │   └── index.jsx
│   │   │   ├── Dropdown
│   │   │   │   └── index.jsx
│   │   │   ├── RangeInput
│   │   │   │   ├── RangeInput.css
│   │   │   │   └── index.jsx
│   │   │   ├── TextInput
│   │   │   │   └── index.jsx
│   │   │   ├── Textarea
│   │   │   │   └── index.jsx
│   │   │   ├── FilterForm.jsx
│   │   │   ├── FormClear.jsx
│   │   │   └── FormItem.jsx
│   │   └── Location.jsx
│   ├── constants
│   │   └── FormType.js
│   ├── data
│   │   └── formData.js
│   ├── hooks
│   │   └── useFilter.js
│   ├── App.jsx
│   └── index.jsx
└── package.json
Plain text
راه‌اندازی پروژه

برای اجرای پروژه، باید Node.js و npm را از قبل نصب کرده باشید.

  • پروژه‌ی اولیه را دانلود و از حالت فشرده خارج کنید.
  • در پوشه‌ی useFilter، دستور npm install را برای نصب نیازمندی‌ها اجرا کنید.
  • در همین پوشه، دستور npm start را برای راه‌اندازی پروژه اجرا کنید.

داده‌ی لازم برای ساخت فرم به‌صورت یک آرایه به هوک useFilter پاس داده می‌شود. هر عضو این آرایه معادل یک المان (input element) از فرم است. آبجکت زیر شامل تمامی prop ها است که به المان‌ها داده می‌شود. بعضی از این prop ها مربوط به نوع خاصی از المان‌ها هستند. برای مثال، options فقط مربوط به dropdown و checkbox group است.

{
  "type": "text"|"number"|"dropdown"|"textarea"|"checkbox"|"checkbox-group",
  "name": string,
  "label": string,
  "children"?: string[],
  "parent"?: string,
  "options": [{ value: string|number, title: string|number }],
}
JSON

فیلد options🔗

همان‌طور که گفته شد، این فیلد مختص dropdown و checkbox group است.

فیلد parent و children🔗

بعضی از المان‌های فرم ممکن است دارای وابستگی به المان دیگری باشند. این وابستگی در فرزند با فیلدی به نام parent که یک رشته شامل نام پدر است، نمایش داده می‌شود و در المان پدر با فیلدی به نام children که شامل آرایه‌ای از رشته‌ها شامل نام (فیلد name) فرزندان است.

اتصال (binding) دوطرفه🔗

هوک خواسته‌شده باید یک اتصال (binding) دوطرفه بین پارامترهای جستجوی آدرس (query params) و فیلدهای فرم داشته باشد، به این معنی که با هر تغییر در المان‌های فرم، بلافاصله آدرس به روزرسانی شود و برعکس. این هوک باید یک آبجکت شامل کلیدهای زیر برگرداند:

{
  "filterState": {
    "name": value
  }
  "setFilterState": (filterState)=>void
  "onChange": (e, name, type)=>void
  "onClear": (name)=>void
  "onClearAll": ()=>void
}
JSON

آرایه‌ای که فرم از روی آن ساخته می‌شود (متغیر formData) به‌عنوان ورودی به هوک داده می‌شود.

متغیر filterState و تابع setFilterState🔗

این دو مورد، عملکردی معادل متغیر و تابع از هوک معروف useState را دارند. متغیر یک آبجکت است که هر کلید آن باید برابر با نام (فیلد name) از المان فرم باشد و مقدار آن هم مقدار آن المان است (فیلد value).

تابع onChange🔗

این تابع هنگام تغییر هر المان فرم صدا زده می‌شود.

تابع onClear🔗

در پایین صفحه، دکمه‌هایی برای پاک کردن مقادیر المان‌های فرم‌ وجود دارد. با کلیک کردن بر روی این دکمه‌ها، باید مقدار المان مورد نظر از فرم پاک شود و هم‌زمان این مقدار از آدرس هم پاک شود. این تابع به عنوان ورودی نام (فیلد name) المان موردنظر را دریافت می‌کند.

نکته: در صورتی که مقدار المان پدر پاک شود و یا تغییر کند، مقدار المان فرزند حتماً باید ریست شود، یعنی هم از آدرس پاک شود و هم مقدار input مربوطه از فرم خالی شود.

تابع onClearAll🔗

در صورتی که روی دکمه‌ی پاک کردن همه کلیک شود، مسلماً باید تمامی مقادیر المان‌ها پاک شود و آدرس به حالت / برود.

تجزیه و ترکیب آدرس (url parser & url stringify)🔗

همان‌طور که می‌دانید، در حالت کلی پارامترهای جستجوی آدرس (query parameter) به‌صورت زیر ساخته می‌شوند:

  ?name1=value1&name2=value2&name3=v1,v2,v3
Plain text

اما به دلایلی نیاز داریم از فرمت متفاوتی برای ساخت این آدرس استفاده کنیم؛ به این‌صورت که برای پشت سر هم قرار دادن پارامترها به جای کارکتر& از کارکتر+و برای جدا کردن key و value به‌جای کارکتر= از ~ استفاده می‌کنیم. هم‌چنین در مواردی که یک آرایه از مقادیر داریم (این اتفاق فقط در المان checkbox group رخ می‌دهد)، برای جدا کردن آن‌ها از هم به جای , از کارکتر -- استفاده می‌کنیم (ترتیب مقادیر مهم نیست). برای مثال، این پارامترهای رایج آدرس:

?name1=value1&name2=v1,v2,v3
Plain text

به این صورت تبدیل می‌شوند:

 ?name1~value1+name2~v1--v2--v3
Plain text

و در صورت تجزیه (parse) این پارامترها به آبجکت زیر می‌رسیم:

{
    "name1": "value1",
    "name2": ["v1", "v2", "v3"]
}
JSON

نکات🔗

  • فیلدی که مقداری ندارد نباید در پارامترهای جستجو (query params) آدرس ظاهر شود.
  • در صورتی که روی دکمه‌های پاک کردن (clear) پایین صفحه کلیک شود، باید پارامتر موردنظر از آدرس مربوطه پاک شود و همزمان مقدار input متصل به آن فیلد پاک شود.
  • در صورتی که یک فیلد دارای children پاک شود، در این‌صورت باید تمامی فرزندانش نیز از آدرس پاک شوند.
  • فیلدهای فیلتر بایستی مقداردهی اولیه شوند، به‌طوری که در زمان بارگذاری صفحه، در صورتی که آدرس دارای پارامترهای جستجو (query params) باشد، باید بلافاصله این مقادیر با فرم هماهنگ شوند و فیلدهای فرم با مقادیر آدرس پر شوند.
  • شما تنها مجاز به اعمال تغییرات در فایل hooks/useFilter.js هستید.
  • تنها مجاز به استفاده از کتاب‌خانه‌های ذکر شده در صورت سوال هستید.

آن‌چه باید آپلود کنید🔗

پس از پیاده‌سازی موارد خواسته‌شده، فایل useFilter.js را آپلود کنید.

شمس‌الله در متاورس


ظاهر برنامه

با trend شدن زمین‌های متاورس و خرید آن‌ها توسط مردم، شمس‌الله تصمیم گرفته که به فروش زمین‌های متاورسی روی بیاورد. او که جدیداً با وب‌سوکت آشنا شده است، می‌خواهد این پروژه را با وب‌سوکت پیاده‌سازی کند. برای این کار، او سرور این پروژه را ساخته و از شما می‌خواهد که در قسمت فرانت‌اند پروژه به او کمک کنید.

جزئیات پروژه🔗

پروژه‌ی اولیه را از این لینک دانلود کنید.

ساختار فایل‌های پروژه
metaverse-lands
├── public
│   ├── favicon.ico
│   └── index.html
├── src
│   ├── components
│   │   └── Block.jsx
│   ├── data
│   │   └── index.js
│   ├── server
│   │   ├── data
│   │   │   ├── MapData.js
│   │   │   └── OwnerData.js
│   │   └── index.js
│   ├── styles
│   │   └── styles.css
│   ├── App.jsx
│   ├── index.js
│   └── setupTests.js
└── package.json
Plain text
راه‌اندازی پروژه

برای اجرای پروژه، باید Node.js و npm را از قبل نصب کرده باشید.

  • پروژه‌ی اولیه را دانلود و از حالت فشرده خارج کنید.
  • در پوشه‌ی metaverse-lands، دستور npm install را برای نصب نیازمندی‌ها اجرا کنید.
  • در همین پوشه، دستور npm start را برای راه‌اندازی پروژه اجرا کنید.
راه‌اندازی سرور
  • ابتدا پروژه‌ی اولیه را دانلود و از حالت فشرده خارج کنید.
  • در پوشه‌ی metaverse-lands، دستور npm install را برای نصب نیازمندی‌ها اجرا کنید.
  • در همین پوشه، دستور npm run server را برای راه‌اندازی سرور پروژه اجرا کنید.

در این پروژه، ارتباط شما با سرور بر بستر وب‌سوکت است و شما تمام داده‌ها را از این طریق سرور دریافت می‌کنید و از همین طریق برای سرور می‌فرستید. در همان لحظه که به وب‌سوکت وصل شوید، شما دیتای مپ یا نقشه و همچنین دیتای دارندگان هر نقطه از نقشه را دریافت می‌کنید.

ساختار داده‌‌ی ارسالی سرور به‌شکل زیر است:

{
  "type": "MAP" | "OWNER",
  "result": "Array",
}
JSON

که type می‌تواند یکی از دو مقدار MAP و OWNER را داشته باشد.

  • تایپ MAP مشخص‌کننده‌ی داده‌های نقشه اصلی است.
  • تایپ OWNER مشخص‌کننده‌ی داده‌های دارندگان هر نقطه از نقشه است.

داده‌های MAP به‌شکل زیر هستند:

{
  "id": "string",
  "type": "LAND" | "WALL",
  "x": "number",
  "y": "number",
}
JSON

بدین ترتیب مختصات تمام نقاط نقشه را سرور برای شما می‌فرستد. دقت کنید داده‌های نقشه مرتب نیست و تمام مختصات در یک آرایه قرار دارد. همچنین اگر ‍type یک نقطه برابر LAND بود، بدین معنا است که آن نقطه زمین است و اگر برابر WALL بود، بدین معنا است که آن نقطه دیوار است. در صورتی که یک نقطه LAND بود، شما باید آن نقطه را به رنگ ‍‍DEFAULT_COLORS.LAND و اگر WALL بود، باید به رنگ DEFAULT_COLORS.WALL در بیاورید.

داده‌های OWNER به‌شکل زیر است:

{
  "compony": "string",
  "color": "string",
  "startCord": {
    "x": "number",
    "y": "number"
  },
  "endCord": {
    "x": "number",
    "y": "number"
  }
}
JSON

در داده‌های بالا، ‍startCord‍ نشان‌دهنده‌ی شروع مختصات نقاطی است که تحت مالکیت فردی با رنگ اتخصاصی color است. همچنین endCord نشان‌دهنده‌ی پایان مختصات است. تمام نقاط بین startCord‍ و endCord تحت مالکیت این فرد یا شرکت است و رنگ این نقط باید به رنگ ‍‍color باشد. با این فرض این نقاط همیشه به شکل مربع یا مستطیل هستند. شما باید بر اساس این داده‌ها، نقشه را ترسیم کنید. پس از آن، در هر لحظه ممکن است سرور داده‌های دارندگان جدید را برای شما بفرستد. شما باید بر اساس داده‌های جدید، نقشه را به‌روزرسانی کنید.

هم‌چنین کاربر فعلی می‌تواند با انتخاب هر نقطه از نقشه، آن نقطه را برای خود خریداری کند.

برای این کار، شما باید ساختار داده‌ای مانند ساختار داده‌ی زیر را برای سرور تحت سوکت بفرستید:

{
  "x": "number",
  "y": "number"
}
JSON

سرور پس دریافت این نقاط، داده‌های مالک این نقطه را برای شما می‌فرستد و شما باید مانند قبل، نقشه را به‌روزرسانی کنید.

نکات🔗

  • در ابتدا باید شما به سوکت سرور وصل شوید. در صورتی که به سوکت وصل نباشید، دیتایی دریافت نمی‌کنید.
  • پس دریافت داده‌های نقشه و دارندگان هر نقطه باید نقشه را بسازید. برای این منظور به کامنت داخل فایل App.jsx توجه کنید.
  • شما باید برای نقطه از نقشه از کامپوننت Block استفاده کنید.
  • رنگ هر نقطه از نقشه باید متناسب با نوع آن نقطه باشد؛ یعنی در صورتی که زمین و دیوار بود، باید از رنگ‌های داده‌شده و در صورتی که تحت مالکیت بود، از رنگ ارسالی سرور استفاده کنید.
  • نقطات زمین ممکن است به مالکیت فرد دیگری در بیاید.
  • هر فرد می‌توانند چندین نقطه داشته باشد، اما داده‌های داخل هر ابجکت دیتای OWNER پیوسته است.
  • دیتای مربوط به نقشه فقط در لحظه اتصال به سوکت ارسال می‌شود.
  • برای مشاهده‌ی بهتر دیتا، می‌توانید پوشه server را بررسی کنید.
  • سوکت در پورت 3001 لوکال هاست اجرا می‌شود. (‍‍‍‍"ws://localhost:3001")
  • برای اطلاعات بیشتر در مورد وب‌سوکت می‌‌توانید از لینک استفاده کنید.
  • تمام داده‌های ثابت در فایل data/index.js قرار داده شده است.
  • شما تنها مجاز به اعمال تغییرات در فایل App.jsx هستید.

آن‌چه باید آپلود کنید🔗

پس از پیاده‌سازی موارد خواسته‌شده، فایل App.jsx را آپلود کنید.