একটি ডায়ালগ উপাদান নির্মাণ

কিভাবে রঙ-অভিযোজিত, প্রতিক্রিয়াশীল, এবং অ্যাক্সেসযোগ্য মিনি এবং মেগা মডেলগুলি <dialog> উপাদান দিয়ে তৈরি করা যায় তার একটি মৌলিক ওভারভিউ।

এই পোস্টে আমি <dialog> উপাদানের সাথে রঙ-অভিযোজিত, প্রতিক্রিয়াশীল, এবং অ্যাক্সেসযোগ্য মিনি এবং মেগা মডেলগুলি কীভাবে তৈরি করা যায় সে সম্পর্কে আমার চিন্তাভাবনা শেয়ার করতে চাই। ডেমো চেষ্টা করুন এবং উৎস দেখুন !

তাদের হালকা এবং অন্ধকার থিমে মেগা এবং মিনি ডায়ালগগুলির প্রদর্শনী৷

আপনি যদি ভিডিও পছন্দ করেন তবে এখানে এই পোস্টটির একটি YouTube সংস্করণ রয়েছে:

ওভারভিউ

<dialog> উপাদানটি ইন-পৃষ্ঠা প্রাসঙ্গিক তথ্য বা কর্মের জন্য দুর্দান্ত। বহু-পৃষ্ঠা অ্যাকশনের পরিবর্তে একই পৃষ্ঠার অ্যাকশন থেকে ব্যবহারকারীর অভিজ্ঞতা কখন উপকৃত হতে পারে তা বিবেচনা করুন: সম্ভবত ফর্মটি ছোট হওয়ার কারণে বা ব্যবহারকারীর কাছ থেকে শুধুমাত্র নিশ্চিত করা বা বাতিল করা প্রয়োজন।

<dialog> উপাদানটি সম্প্রতি ব্রাউজার জুড়ে স্থিতিশীল হয়েছে:

ব্রাউজার সমর্থন

  • ক্রোম: 37।
  • প্রান্ত: 79।
  • ফায়ারফক্স: 98।
  • সাফারি: 15.4.

উৎস

আমি দেখেছি যে উপাদানটিতে কয়েকটি জিনিস অনুপস্থিত ছিল, তাই এই GUI চ্যালেঞ্জে আমি বিকাশকারী অভিজ্ঞতার আইটেমগুলি যোগ করি যা আমি আশা করি: অতিরিক্ত ইভেন্ট, হালকা খারিজ, কাস্টম অ্যানিমেশন এবং একটি মিনি এবং মেগা টাইপ।

মার্কআপ

একটি <dialog> উপাদানের অপরিহার্য বিষয়গুলি বিনয়ী। উপাদানটি স্বয়ংক্রিয়ভাবে লুকানো হবে এবং আপনার বিষয়বস্তু ওভারলে করার জন্য শৈলী বিল্ট ইন থাকবে।

<dialog>
  …
</dialog>

আমরা এই বেসলাইন উন্নত করতে পারেন.

ঐতিহ্যগতভাবে, একটি ডায়ালগ উপাদান একটি মডেলের সাথে অনেক কিছু ভাগ করে এবং প্রায়শই নামগুলি বিনিময়যোগ্য। আমি এখানে ছোট ডায়ালগ পপআপ (মিনি), পাশাপাশি পুরো পৃষ্ঠার ডায়ালগ (মেগা) উভয়ের জন্য ডায়ালগ উপাদান ব্যবহার করার স্বাধীনতা নিয়েছি। আমি তাদের মেগা এবং মিনি নাম দিয়েছি, উভয় ডায়ালগই বিভিন্ন ব্যবহারের ক্ষেত্রে সামান্য অভিযোজিত। আমি আপনাকে টাইপ নির্দিষ্ট করার অনুমতি দেওয়ার জন্য একটি modal-mode বৈশিষ্ট্য যুক্ত করেছি:

<dialog id="MegaDialog" modal-mode="mega"></dialog>
<dialog id="MiniDialog" modal-mode="mini"></dialog>

হালকা এবং অন্ধকার উভয় থিমে মিনি এবং মেগা ডায়ালগের স্ক্রিনশট।

সবসময় নয়, তবে সাধারণত কিছু মিথস্ক্রিয়া তথ্য সংগ্রহ করতে ডায়ালগ উপাদান ব্যবহার করা হবে। সংলাপের উপাদানগুলির ভিতরে ফর্মগুলি একসাথে যেতে তৈরি করা হয় । একটি ফর্ম উপাদান আপনার ডায়ালগ বিষয়বস্তু মোড়ানো একটি ভাল ধারণা যাতে JavaScript ব্যবহারকারীর প্রবেশ করা ডেটা অ্যাক্সেস করতে পারে। উপরন্তু, method="dialog" ব্যবহার করে একটি ফর্মের ভিতরের বোতামগুলি জাভাস্ক্রিপ্ট এবং পাস ডেটা ছাড়াই একটি ডায়ালগ বন্ধ করতে পারে।

<dialog id="MegaDialog" modal-mode="mega">
  <form method="dialog">
    …
    <button value="cancel">Cancel</button>
    <button value="confirm">Confirm</button>
  </form>
</dialog>

মেগা ডায়ালগ

একটি মেগা ডায়ালগের ফর্মের ভিতরে তিনটি উপাদান থাকে: <header> , <article> , এবং <footer> । এগুলি শব্দার্থিক ধারক হিসাবে কাজ করে, সেইসাথে ডায়ালগ উপস্থাপনের জন্য শৈলী লক্ষ্যগুলি। শিরোনামটি মডেলটিকে শিরোনাম করে এবং একটি বন্ধ বোতাম অফার করে। নিবন্ধটি ফর্ম ইনপুট এবং তথ্যের জন্য। ফুটারটিতে অ্যাকশন বোতামগুলির একটি <menu> মেনু> রয়েছে।

<dialog id="MegaDialog" modal-mode="mega">
  <form method="dialog">
    <header>
      <h3>Dialog title</h3>
      <button onclick="this.closest('dialog').close('close')"></button>
    </header>
    <article>...</article>
    <footer>
      <menu>
        <button autofocus type="reset" onclick="this.closest('dialog').close('cancel')">Cancel</button>
        <button type="submit" value="confirm">Confirm</button>
      </menu>
    </footer>
  </form>
</dialog>

প্রথম মেনু বোতামটিতে autofocus এবং একটি onclick ইনলাইন ইভেন্ট হ্যান্ডলার রয়েছে। ডায়ালগ খোলা হলে autofocus অ্যাট্রিবিউটটি ফোকাস পাবে, এবং আমি এটিকে বাতিল বোতামে রাখা সর্বোত্তম অনুশীলন বলে মনে করি, নিশ্চিত বোতামে নয়। এটি নিশ্চিত করে যে নিশ্চিতকরণ ইচ্ছাকৃত এবং আকস্মিক নয়।

মিনি ডায়ালগ

মিনি ডায়ালগটি মেগা ডায়ালগের অনুরূপ, এটিতে একটি <header> উপাদান অনুপস্থিত। এটি এটিকে ছোট এবং আরও ইনলাইন হতে দেয়।

<dialog id="MiniDialog" modal-mode="mini">
  <form method="dialog">
    <article>
      <p>Are you sure you want to remove this user?</p>
    </article>
    <footer>
      <menu>
        <button autofocus type="reset" onclick="this.closest('dialog').close('cancel')">Cancel</button>
        <button type="submit" value="confirm">Confirm</button>
      </menu>
    </footer>
  </form>
</dialog>

ডায়ালগ উপাদানটি একটি সম্পূর্ণ ভিউপোর্ট উপাদানের জন্য একটি শক্তিশালী ভিত্তি প্রদান করে যা ডেটা এবং ব্যবহারকারীর মিথস্ক্রিয়া সংগ্রহ করতে পারে। এই প্রয়োজনীয় জিনিসগুলি আপনার সাইট বা অ্যাপে কিছু খুব আকর্ষণীয় এবং শক্তিশালী মিথস্ক্রিয়া তৈরি করতে পারে।

অ্যাক্সেসযোগ্যতা

ডায়ালগ উপাদানটির খুব ভাল অন্তর্নির্মিত অ্যাক্সেসযোগ্যতা রয়েছে। আমি সাধারণত করি এই বৈশিষ্ট্যগুলি যোগ করার পরিবর্তে, অনেকগুলি ইতিমধ্যেই রয়েছে।

ফোকাস পুনরুদ্ধার করা হচ্ছে

একটি sidenav কম্পোনেন্ট তৈরি করার ক্ষেত্রে আমরা যেমন হাতে করেছিলাম, এটা গুরুত্বপূর্ণ যে কোনো কিছু সঠিকভাবে খোলা এবং বন্ধ করা প্রাসঙ্গিক খোলা এবং বন্ধ বোতামগুলিতে ফোকাস রাখে। যখন সেই sidenav খোলে, ফোকাস বন্ধ বোতামে রাখা হয়। ক্লোজ বোতাম টিপলে, যে বোতামটি খুলেছে তাতে ফোকাস পুনরুদ্ধার করা হয়।

ডায়ালগ উপাদানের সাথে, এটি অন্তর্নির্মিত ডিফল্ট আচরণ:

দুর্ভাগ্যবশত, আপনি যদি ডায়ালগ ইন এবং আউট অ্যানিমেট করতে চান, এই কার্যকারিতা হারিয়ে যায়। জাভাস্ক্রিপ্ট বিভাগে আমি সেই কার্যকারিতা পুনরুদ্ধার করব।

ট্র্যাপিং ফোকাস

ডায়ালগ উপাদান নথিতে আপনার জন্য inert পরিচালনা করে। inert এর আগে, জাভাস্ক্রিপ্ট একটি উপাদান ছেড়ে ফোকাস দেখার জন্য ব্যবহার করা হত, যেখানে এটি বাধা দেয় এবং এটিকে ফিরিয়ে দেয়।

ব্রাউজার সমর্থন

  • ক্রোম: 102।
  • প্রান্ত: 102।
  • ফায়ারফক্স: 112।
  • সাফারি: 15.5।

উৎস

inert এর পরে, নথির যেকোনো অংশ এতটাই "হিমায়িত" হতে পারে যে সেগুলি আর ফোকাস লক্ষ্য নয় বা একটি মাউসের সাথে ইন্টারেক্টিভ নয়। ফোকাস আটকানোর পরিবর্তে, নথির একমাত্র ইন্টারেক্টিভ অংশে ফোকাস পরিচালিত হয়।

একটি উপাদান খুলুন এবং স্বয়ংক্রিয়ভাবে ফোকাস করুন

ডিফল্টরূপে, ডায়ালগ উপাদানটি ডায়ালগ মার্কআপের প্রথম ফোকাসযোগ্য উপাদানটিতে ফোকাস বরাদ্দ করবে। ব্যবহারকারীর জন্য ডিফল্ট করার জন্য এটি সেরা উপাদান না হলে, autofocus বৈশিষ্ট্যটি ব্যবহার করুন। পূর্বে বর্ণিত হিসাবে, আমি এটি বাতিল বোতামে রাখা ভাল অভ্যাস খুঁজে পেয়েছি এবং নিশ্চিত বোতামে নয়। এটি নিশ্চিত করে যে নিশ্চিতকরণ ইচ্ছাকৃত এবং আকস্মিক নয়।

এস্কেপ কী দিয়ে বন্ধ করা হচ্ছে

এই সম্ভাব্য বাধা সৃষ্টিকারী উপাদানটিকে বন্ধ করা সহজ করা গুরুত্বপূর্ণ। সৌভাগ্যবশত, ডায়ালগ উপাদানটি আপনার জন্য পালানোর কী পরিচালনা করবে, আপনাকে অর্কেস্ট্রেশনের বোঝা থেকে মুক্ত করবে।

শৈলী

ডায়ালগ উপাদান স্টাইল করার একটি সহজ পথ এবং একটি কঠিন পথ আছে। ডায়ালগের প্রদর্শন বৈশিষ্ট্য পরিবর্তন না করে এবং এর সীমাবদ্ধতার সাথে কাজ করে সহজ পথটি অর্জন করা হয়। আমি ডায়ালগ খোলা এবং বন্ধ করার জন্য কাস্টম অ্যানিমেশন প্রদান করার জন্য কঠিন পথে চলেছি, display সম্পত্তি গ্রহণ এবং আরও অনেক কিছু।

খোলা প্রপস সঙ্গে স্টাইলিং

অভিযোজিত রঙ এবং সামগ্রিক ডিজাইনের সামঞ্জস্যকে ত্বরান্বিত করতে, আমি নির্লজ্জভাবে আমার CSS ভেরিয়েবল লাইব্রেরি Open Props নিয়ে এসেছি। বিনামূল্যে প্রদত্ত ভেরিয়েবল ছাড়াও, আমি একটি নর্মালাইজ ফাইল এবং কিছু বোতামও ইম্পোর্ট করি, উভয়ই Open Props ঐচ্ছিক আমদানি হিসাবে প্রদান করে। এই আমদানিগুলি আমাকে ডায়ালগ এবং ডেমো কাস্টমাইজ করার উপর ফোকাস করতে সাহায্য করে যখন এটিকে সমর্থন করতে এবং এটিকে সুন্দর দেখাতে প্রচুর শৈলীর প্রয়োজন হয় না।

<dialog> উপাদান স্টাইল করা

প্রদর্শন সম্পত্তির মালিকানা

একটি ডায়ালগ উপাদানের ডিফল্ট প্রদর্শন এবং আড়াল আচরণ প্রদর্শন বৈশিষ্ট্যকে block থেকে none । দুর্ভাগ্যবশত এর মানে হল এটি ভিতরে এবং বাইরে অ্যানিমেটেড করা যাবে না, শুধুমাত্র ভিতরে। আমি ভিতরে এবং বাইরে উভয়ই অ্যানিমেট করতে চাই, এবং প্রথম পদক্ষেপটি হল আমার নিজস্ব প্রদর্শন সম্পত্তি সেট করা:

dialog {
  display: grid;
}

উপরের সিএসএস স্নিপেটে যেমন দেখানো হয়েছে, ডিসপ্লে প্রপার্টির মান পরিবর্তন করে এবং তার মালিকানার মাধ্যমে, সঠিক ব্যবহারকারীর অভিজ্ঞতার সুবিধার্থে যথেষ্ট পরিমাণে স্টাইল পরিচালনা করা প্রয়োজন। প্রথমত, একটি ডায়ালগের ডিফল্ট অবস্থা বন্ধ করা হয়। আপনি এই অবস্থাটিকে দৃশ্যমানভাবে উপস্থাপন করতে পারেন এবং ডায়ালগটিকে নিম্নলিখিত শৈলীগুলির সাথে মিথস্ক্রিয়া গ্রহণ করা থেকে বিরত রাখতে পারেন:

dialog:not([open]) {
  pointer-events: none;
  opacity: 0;
}

এখন ডায়ালগটি অদৃশ্য এবং খোলা না থাকলে এর সাথে যোগাযোগ করা যাবে না। পরে আমি কিছু জাভাস্ক্রিপ্ট যোগ করব ডায়ালগে inert বৈশিষ্ট্য পরিচালনা করতে, নিশ্চিত করে যে কীবোর্ড এবং স্ক্রিন-রিডার ব্যবহারকারীরাও লুকানো ডায়ালগে পৌঁছাতে পারবেন না।

ডায়ালগটিকে একটি অভিযোজিত রঙের থিম দেওয়া

মেগা ডায়ালগ আলো এবং গাঢ় থিম দেখাচ্ছে, পৃষ্ঠের রং প্রদর্শন করছে।

color-scheme আপনার দস্তাবেজটিকে একটি ব্রাউজার-প্রদত্ত অভিযোজিত রঙের থিমে হালকা এবং অন্ধকার সিস্টেমের পছন্দগুলিতে বেছে নেয়, আমি ডায়ালগ উপাদানটিকে তার চেয়ে বেশি কাস্টমাইজ করতে চেয়েছিলাম। ওপেন প্রপস কয়েকটি পৃষ্ঠের রঙ সরবরাহ করে যা স্বয়ংক্রিয়ভাবে হালকা এবং অন্ধকার সিস্টেম পছন্দগুলির সাথে খাপ খায়, color-scheme ব্যবহারের অনুরূপ। এগুলি একটি ডিজাইনে স্তর তৈরি করার জন্য দুর্দান্ত এবং আমি স্তরের পৃষ্ঠের এই চেহারাটিকে দৃশ্যত সমর্থন করতে রঙ ব্যবহার করতে পছন্দ করি। পটভূমির রঙ var(--surface-1) ; সেই স্তরের উপরে বসতে, var(--surface-2) ব্যবহার করুন:

dialog {
  
  background: var(--surface-2);
  color: var(--text-1);
}

@media (prefers-color-scheme: dark) {
  dialog {
    border-block-start: var(--border-size-1) solid var(--surface-3);
  }
}

শিরোনাম এবং পাদচরণের মতো শিশু উপাদানগুলির জন্য পরে আরও অভিযোজিত রং যোগ করা হবে। আমি এগুলিকে একটি ডায়ালগ উপাদানের জন্য অতিরিক্ত বিবেচনা করি, কিন্তু একটি বাধ্যতামূলক এবং ভালভাবে ডিজাইন করা ডায়ালগ ডিজাইন তৈরি করার ক্ষেত্রে এটি সত্যিই গুরুত্বপূর্ণ৷

রেসপন্সিভ ডায়ালগ সাইজিং

ডায়ালগটি তার বিষয়বস্তুতে এর আকার অর্পণ করার জন্য ডিফল্ট, যা সাধারণত দুর্দান্ত। এখানে আমার লক্ষ্য হল max-inline-size একটি পঠনযোগ্য আকারে সীমাবদ্ধ করা ( --size-content-3 = 60ch ) বা ভিউপোর্ট প্রস্থের 90%। এটি নিশ্চিত করে যে ডায়ালগটি একটি মোবাইল ডিভাইসে প্রান্ত থেকে প্রান্তে যাবে না এবং একটি ডেস্কটপ স্ক্রিনে এত চওড়া হবে না যে এটি পড়া কঠিন। তারপর আমি একটি max-block-size যোগ করি যাতে ডায়ালগটি পৃষ্ঠার উচ্চতা অতিক্রম না করে। এর মানে হল যে ডায়ালগের স্ক্রোলযোগ্য এলাকাটি কোথায় তা আমাদের নির্দিষ্ট করতে হবে, যদি এটি একটি লম্বা ডায়ালগ উপাদান হয়।

dialog {
  
  max-inline-size: min(90vw, var(--size-content-3));
  max-block-size: min(80vh, 100%);
  max-block-size: min(80dvb, 100%);
  overflow: hidden;
}

লক্ষ্য করুন কিভাবে আমার max-block-size দুইবার আছে? প্রথমটি 80vh ব্যবহার করে, একটি শারীরিক ভিউপোর্ট ইউনিট। আমি আসলে যা চাই তা হল ডায়ালগটিকে আপেক্ষিক প্রবাহের মধ্যে রাখা, আন্তর্জাতিক ব্যবহারকারীদের জন্য, তাই আমি দ্বিতীয় ঘোষণায় যৌক্তিক, নতুন, এবং শুধুমাত্র আংশিকভাবে সমর্থিত dvb ইউনিট ব্যবহার করি যখন এটি আরও স্থিতিশীল হয়।

মেগা ডায়ালগ পজিশনিং

একটি ডায়ালগ উপাদানের অবস্থান নির্ধারণে সহায়তা করার জন্য, এটির দুটি অংশ ভেঙে ফেলা উচিত: পূর্ণ স্ক্রিন ব্যাকড্রপ এবং ডায়ালগ ধারক৷ ব্যাকড্রপকে অবশ্যই সবকিছু আবরণ করতে হবে, এই ডায়ালগটি সামনে রয়েছে এবং পিছনের বিষয়বস্তু অ্যাক্সেসযোগ্য নয় তা সমর্থন করার জন্য একটি ছায়া প্রভাব প্রদান করে৷ ডায়ালগ কন্টেইনারটি এই পটভূমিতে নিজেকে কেন্দ্রীভূত করতে এবং এর বিষয়বস্তুগুলির প্রয়োজনে যাই হোক না কেন আকার নিতে পারে৷

নিম্নলিখিত শৈলীগুলি উইন্ডোতে ডায়ালগ উপাদানটিকে ঠিক করে, প্রতিটি কোণে প্রসারিত করে, এবং বিষয়বস্তুকে কেন্দ্রীভূত করতে margin: auto ব্যবহার করে:

dialog {
  
  margin: auto;
  padding: 0;
  position: fixed;
  inset: 0;
  z-index: var(--layer-important);
}
মোবাইল মেগা ডায়ালগ শৈলী

ছোট ভিউপোর্টে, আমি এই পূর্ণ পৃষ্ঠার মেগা মডেলটিকে একটু ভিন্নভাবে স্টাইল করি। আমি নীচের মার্জিনটি 0 এ সেট করেছি, যা ডায়ালগ বিষয়বস্তুকে ভিউপোর্টের নীচে নিয়ে আসে। কয়েকটি স্টাইল সামঞ্জস্যের সাথে, আমি ব্যবহারকারীর থাম্বসের কাছাকাছি ডায়ালগটিকে একটি অ্যাকশনশিটে পরিণত করতে পারি:

@media (max-width: 768px) {
  dialog[modal-mode="mega"] {
    margin-block-end: 0;
    border-end-end-radius: 0;
    border-end-start-radius: 0;
  }
}

মার্জিন ব্যবধান ওভারলে করা ডেভটুলগুলির স্ক্রিনশট    খোলা থাকার সময় ডেস্কটপ এবং মোবাইল মেগা ডায়ালগ উভয়েই।

মিনি ডায়ালগ পজিশনিং

একটি বড় ভিউপোর্ট ব্যবহার করার সময় যেমন একটি ডেস্কটপ কম্পিউটারে, আমি মিনি ডায়ালগগুলিকে সেই উপাদানটির উপরে অবস্থান করতে বেছে নিয়েছি যা তাদের বলে। এটি করার জন্য আমি জাভাস্ক্রিপ্ট প্রয়োজন. আমি এখানে যে কৌশলটি ব্যবহার করি তা আপনি খুঁজে পেতে পারেন , কিন্তু আমি মনে করি এটি এই নিবন্ধের সুযোগের বাইরে। জাভাস্ক্রিপ্ট ছাড়া, মেগা ডায়ালগের মতোই স্ক্রিনের মাঝখানে মিনি ডায়ালগ দেখা যায়।

এটা পপ করা

অবশেষে, ডায়ালগে কিছু ফ্লেয়ার যোগ করুন যাতে এটি পৃষ্ঠার অনেক উপরে বসে থাকা একটি নরম পৃষ্ঠের মতো দেখায়। সংলাপের কোণগুলিকে বৃত্তাকার করে কোমলতা অর্জন করা হয়। গভীরতা ওপেন প্রপসের সাবধানে তৈরি করা শ্যাডো প্রপগুলির একটি দিয়ে অর্জন করা হয়:

dialog {
  
  border-radius: var(--radius-3);
  box-shadow: var(--shadow-6);
}

ব্যাকড্রপ সিউডো উপাদান কাস্টমাইজ করা হচ্ছে

আমি ব্যাকড্রপের সাথে খুব হালকাভাবে কাজ করতে বেছে নিয়েছি, শুধুমাত্র মেগা ডায়ালগে backdrop-filter সাথে একটি অস্পষ্ট প্রভাব যুক্ত করছি:

ব্রাউজার সমর্থন

  • ক্রোম: 76।
  • প্রান্ত: 79।
  • ফায়ারফক্স: 103।
  • সাফারি: 18।

উৎস

dialog[modal-mode="mega"]::backdrop {
  backdrop-filter: blur(25px);
}

আমি backdrop-filter এ একটি ট্রানজিশন করাও বেছে নিয়েছি, এই আশায় যে ব্রাউজারগুলি ভবিষ্যতে ব্যাকড্রপ উপাদানটি পরিবর্তন করার অনুমতি দেবে:

dialog::backdrop {
  transition: backdrop-filter .5s ease;
}

রঙিন অবতারের একটি অস্পষ্ট পটভূমিকে ওভারলে করা মেগা ডায়ালগের স্ক্রিনশট।

স্টাইলিং অতিরিক্ত

আমি এই বিভাগটিকে "অতিরিক্ত" বলি কারণ এটি সাধারণভাবে ডায়ালগ উপাদানের তুলনায় আমার ডায়ালগ উপাদান ডেমোর সাথে আরও বেশি কিছু করে।

স্ক্রোল কন্টেনমেন্ট

যখন ডায়ালগ দেখানো হয়, ব্যবহারকারী এখনও এটির পিছনে পৃষ্ঠাটি স্ক্রোল করতে সক্ষম হয়, যা আমি চাই না:

সাধারণত, overscroll-behavior আমার স্বাভাবিক সমাধান হবে, কিন্তু স্পেক অনুযায়ী , এটি ডায়ালগের উপর কোন প্রভাব ফেলে না কারণ এটি একটি স্ক্রোল পোর্ট নয়, অর্থাৎ এটি একটি স্ক্রলার নয় তাই প্রতিরোধ করার কিছু নেই। আমি এই নির্দেশিকা থেকে নতুন ইভেন্টগুলি দেখতে জাভাস্ক্রিপ্ট ব্যবহার করতে পারি, যেমন "বন্ধ" এবং "খোলা", এবং টগল overflow: hidden , বা আমি সব ব্রাউজারে :has() স্থিতিশীল হওয়ার জন্য অপেক্ষা করতে পারি:

ব্রাউজার সমর্থন

  • ক্রোম: 105।
  • প্রান্ত: 105।
  • ফায়ারফক্স: 121।
  • সাফারি: 15.4.

উৎস

html:has(dialog[open][modal-mode="mega"]) {
  overflow: hidden;
}

এখন যখন একটি মেগা ডায়ালগ খোলা থাকে, তখন html নথিতে overflow: hidden

<form> লেআউট

ব্যবহারকারীর কাছ থেকে মিথস্ক্রিয়া তথ্য সংগ্রহের জন্য একটি অত্যন্ত গুরুত্বপূর্ণ উপাদান হওয়ার পাশাপাশি, আমি শিরোনাম, পাদচরণ এবং নিবন্ধ উপাদানগুলিকে এখানে ব্যবহার করি। এই লেআউটের সাথে আমি নিবন্ধ শিশুটিকে একটি স্ক্রোলযোগ্য এলাকা হিসাবে স্পষ্ট করতে চাই। আমি grid-template-rows দিয়ে এটি অর্জন করি। নিবন্ধ উপাদান 1fr দেওয়া হয়েছে এবং ফর্ম নিজেই ডায়ালগ উপাদান হিসাবে একই সর্বোচ্চ উচ্চতা আছে. এই দৃঢ় উচ্চতা এবং দৃঢ় সারির আকার সেট করা যা নিবন্ধ উপাদানটিকে সীমাবদ্ধ হতে দেয় এবং যখন এটি ওভারফ্লো হয় তখন স্ক্রোল করে:

dialog > form {
  display: grid;
  grid-template-rows: auto 1fr auto;
  align-items: start;
  max-block-size: 80vh;
  max-block-size: 80dvb;
}

সারির উপর গ্রিড লেআউট তথ্য ওভারলে করা devtools এর স্ক্রিনশট।

ডায়ালগ <header> স্টাইল করা হচ্ছে

এই উপাদানটির ভূমিকা হল ডায়ালগ বিষয়বস্তুর জন্য একটি শিরোনাম প্রদান করা এবং ক্লোজ বোতাম খুঁজে পাওয়া সহজ অফার করা। এটিকে একটি পৃষ্ঠের রঙও দেওয়া হয়েছে যাতে এটি ডায়ালগ নিবন্ধের বিষয়বস্তুর পিছনে থাকে। এই প্রয়োজনীয়তাগুলি একটি ফ্লেক্সবক্স কন্টেইনারের দিকে নিয়ে যায়, উল্লম্বভাবে সারিবদ্ধ আইটেম যা তাদের প্রান্তে ফাঁকা থাকে এবং শিরোনাম এবং বন্ধ বোতামগুলিকে কিছু জায়গা দেওয়ার জন্য কিছু প্যাডিং এবং ফাঁক থাকে:

dialog > form > header {
  display: flex;
  gap: var(--size-3);
  justify-content: space-between;
  align-items: flex-start;
  background: var(--surface-2);
  padding-block: var(--size-3);
  padding-inline: var(--size-5);
}

@media (prefers-color-scheme: dark) {
  dialog > form > header {
    background: var(--surface-1);
  }
}

ডায়ালগ হেডারে ফ্লেক্সবক্স লেআউট তথ্য ওভারলে করা Chrome Devtools-এর স্ক্রিনশট।

শিরোনাম বন্ধ বোতাম স্টাইলিং

যেহেতু ডেমোটি ওপেন প্রপস বোতামগুলি ব্যবহার করছে, তাই বন্ধ বোতামটি একটি বৃত্তাকার আইকন কেন্দ্রিক বোতামে কাস্টমাইজ করা হয়েছে:

dialog > form > header > button {
  border-radius: var(--radius-round);
  padding: .75ch;
  aspect-ratio: 1;
  flex-shrink: 0;
  place-items: center;
  stroke: currentColor;
  stroke-width: 3px;
}

হেডার ক্লোজ বোতামের জন্য Chrome Devtools ওভারলে সাইজিং এবং প্যাডিং তথ্যের স্ক্রিনশট।

ডায়ালগ <article> স্টাইল করা

এই ডায়ালগে নিবন্ধ উপাদানটির একটি বিশেষ ভূমিকা রয়েছে: এটি একটি লম্বা বা দীর্ঘ ডায়ালগের ক্ষেত্রে স্ক্রোল করার উদ্দেশ্যে একটি স্থান।

এটি সম্পন্ন করার জন্য, প্যারেন্ট ফর্ম উপাদানটি নিজের জন্য কিছু সর্বোচ্চ নির্ধারণ করেছে যা এই নিবন্ধের উপাদানটি খুব লম্বা হলে পৌঁছাতে বাধা প্রদান করে। overflow-y: auto তাই স্ক্রলবারগুলি শুধুমাত্র প্রয়োজন হলেই দেখানো হয়, এর মধ্যে overscroll-behavior: contain , এবং বাকিগুলি কাস্টম উপস্থাপনা শৈলী হবে:

dialog > form > article {
  overflow-y: auto; 
  max-block-size: 100%; /* safari */
  overscroll-behavior-y: contain;
  display: grid;
  justify-items: flex-start;
  gap: var(--size-3);
  box-shadow: var(--shadow-2);
  z-index: var(--layer-1);
  padding-inline: var(--size-5);
  padding-block: var(--size-3);
}

@media (prefers-color-scheme: light) {
  dialog > form > article {
    background: var(--surface-1);
  }
}

ফুটারের ভূমিকা হল অ্যাকশন বোতামের মেনু ধারণ করা। ফ্লেক্সবক্স পাদচরণ ইনলাইন অক্ষের শেষে বিষয়বস্তু সারিবদ্ধ করতে ব্যবহৃত হয়, তারপর বোতামগুলিকে কিছু জায়গা দেওয়ার জন্য কিছু ব্যবধান।

dialog > form > footer {
  background: var(--surface-2);
  display: flex;
  flex-wrap: wrap;
  gap: var(--size-3);
  justify-content: space-between;
  align-items: flex-start;
  padding-inline: var(--size-5);
  padding-block: var(--size-3);
}

@media (prefers-color-scheme: dark) {
  dialog > form > footer {
    background: var(--surface-1);
  }
}

ফুটার এলিমেন্টে ফ্লেক্সবক্স লেআউট তথ্য ওভারলে করা Chrome Devtools-এর স্ক্রিনশট।

menu উপাদানটি ডায়ালগের জন্য অ্যাকশন বোতামগুলি ধারণ করতে ব্যবহৃত হয়। এটি বোতামগুলির মধ্যে স্থান প্রদানের জন্য gap সহ একটি মোড়ানো ফ্লেক্সবক্স লেআউট ব্যবহার করে। মেনু উপাদানগুলিতে প্যাডিং থাকে যেমন <ul> । আমি সেই স্টাইলটিও সরিয়ে ফেলি যেহেতু আমার এটির প্রয়োজন নেই।

dialog > form > footer > menu {
  display: flex;
  flex-wrap: wrap;
  gap: var(--size-3);
  padding-inline-start: 0;
}

dialog > form > footer > menu:only-child {
  margin-inline-start: auto;
}

ফুটার মেনু উপাদানগুলিতে ফ্লেক্সবক্স তথ্য ওভারলে করা Chrome Devtools-এর স্ক্রিনশট৷

অ্যানিমেশন

ডায়ালগ উপাদানগুলি প্রায়শই অ্যানিমেটেড হয় কারণ তারা উইন্ডোতে প্রবেশ করে এবং প্রস্থান করে। এই প্রবেশদ্বার এবং প্রস্থানের জন্য ডায়ালগগুলিকে কিছু সহায়ক গতি প্রদান করা ব্যবহারকারীদের প্রবাহে নিজেদেরকে অভিমুখী করতে সহায়তা করে৷

সাধারণত ডায়ালগ উপাদান শুধুমাত্র অ্যানিমেটেড হতে পারে, বাইরে নয়। এর কারণ হল ব্রাউজার উপাদানটির display বৈশিষ্ট্য টগল করে। এর আগে, গাইডটি ডিসপ্লেকে গ্রিডে সেট করে এবং কখনই এটিকে সেট করে না। এটি ভিতরে এবং বাইরে অ্যানিমেট করার ক্ষমতা আনলক করে।

ওপেন প্রপস ব্যবহারের জন্য অনেক কীফ্রেম অ্যানিমেশনের সাথে আসে, যা অর্কেস্ট্রেশনকে সহজ এবং সুস্পষ্ট করে তোলে। আমি যে অ্যানিমেশন লক্ষ্য এবং স্তরযুক্ত পদ্ধতি গ্রহণ করেছি তা এখানে রয়েছে:

  1. হ্রাসকৃত গতি হল ডিফল্ট রূপান্তর, একটি সাধারণ অস্বচ্ছতা ফেইড ইন এবং আউট।
  2. গতি ঠিক থাকলে, স্লাইড এবং স্কেল অ্যানিমেশন যোগ করা হয়।
  3. মেগা ডায়ালগের জন্য প্রতিক্রিয়াশীল মোবাইল লেআউটটি স্লাইড আউট করার জন্য সামঞ্জস্য করা হয়েছে।

একটি নিরাপদ এবং অর্থপূর্ণ ডিফল্ট রূপান্তর

ওপেন প্রপস কীফ্রেমের সাথে ফেইড ইন এবং আউট করার জন্য আসে, আমি সম্ভাব্য আপগ্রেড হিসাবে কীফ্রেম অ্যানিমেশনগুলির সাথে ডিফল্ট হিসাবে রূপান্তরের এই স্তরযুক্ত পদ্ধতিকে পছন্দ করি। এর আগে আমরা ডায়ালগের দৃশ্যমানতা অস্বচ্ছতার সাথে স্টাইল করেছি, [open] অ্যাট্রিবিউটের উপর নির্ভর করে 1 বা 0 অর্কেস্ট্রেট করে। 0% এবং 100% এর মধ্যে স্থানান্তর করতে, ব্রাউজারকে বলুন কতক্ষণ এবং আপনি কী ধরনের সহজ করতে চান:

dialog {
  transition: opacity .5s var(--ease-3);
}

ট্রানজিশনে গতি যোগ করা হচ্ছে

ব্যবহারকারীর গতির সাথে ঠিক থাকলে, মেগা এবং মিনি ডায়ালগ উভয়ই তাদের প্রবেশদ্বার হিসাবে স্লাইড করা উচিত এবং তাদের প্রস্থান হিসাবে স্কেল আউট করা উচিত। আপনি prefers-reduced-motion মিডিয়া ক্যোয়ারী এবং কয়েকটি ওপেন প্রপস দিয়ে এটি অর্জন করতে পারেন:

@media (prefers-reduced-motion: no-preference) {
  dialog {
    animation: var(--animation-scale-down) forwards;
    animation-timing-function: var(--ease-squish-3);
  }

  dialog[open] {
    animation: var(--animation-slide-in-up) forwards;
  }
}

মোবাইলের জন্য প্রস্থান অ্যানিমেশন মানিয়ে নেওয়া

এর আগে স্টাইলিং বিভাগে, মেগা ডায়ালগ শৈলী মোবাইল ডিভাইসগুলির জন্য একটি অ্যাকশন শীটের মতো হওয়ার জন্য অভিযোজিত হয়েছে, যেন একটি ছোট কাগজের টুকরো স্ক্রিনের নিচ থেকে উপরে উঠে গেছে এবং এখনও নীচে সংযুক্ত রয়েছে। স্কেল আউট এক্সিট অ্যানিমেশন এই নতুন ডিজাইনের সাথে ভালভাবে মানানসই নয়, এবং আমরা কয়েকটি মিডিয়া প্রশ্ন এবং কিছু ওপেন প্রপসের সাথে এটি মানিয়ে নিতে পারি:

@media (prefers-reduced-motion: no-preference) and @media (max-width: 768px) {
  dialog[modal-mode="mega"] {
    animation: var(--animation-slide-out-down) forwards;
    animation-timing-function: var(--ease-squish-2);
  }
}

জাভাস্ক্রিপ্ট

জাভাস্ক্রিপ্টের সাথে যোগ করার জন্য বেশ কয়েকটি জিনিস রয়েছে:

// dialog.js
export default async function (dialog) {
  // add light dismiss
  // add closing and closed events
  // add opening and opened events
  // add removed event
  // removing loading attribute
}

এই সংযোজনগুলি হালকা বরখাস্তের আকাঙ্ক্ষা থেকে উদ্ভূত হয় (সংলাপের পটভূমিতে ক্লিক করা), অ্যানিমেশন, এবং ফর্ম ডেটা পাওয়ার জন্য আরও ভাল সময়ের জন্য কিছু অতিরিক্ত ইভেন্ট।

হালকা খারিজ যোগ করা হচ্ছে

এই কাজটি সহজবোধ্য এবং একটি ডায়ালগ উপাদানের একটি দুর্দান্ত সংযোজন যা অ্যানিমেটেড হচ্ছে না। ডায়ালগ এলিমেন্টে ক্লিক দেখে এবং কি ক্লিক করা হয়েছে তা মূল্যায়ন করতে ইভেন্ট বুদবুদ ব্যবহার করে ইন্টারঅ্যাকশনটি অর্জন করা হয়, এবং এটি শুধুমাত্র close() যদি এটি শীর্ষ-সবথেকে উপাদান হয়:

export default async function (dialog) {
  dialog.addEventListener('click', lightDismiss)
}

const lightDismiss = ({target:dialog}) => {
  if (dialog.nodeName === 'DIALOG')
    dialog.close('dismiss')
}

dialog.close('dismiss') লক্ষ্য করুন। ঘটনা বলা হয় এবং একটি স্ট্রিং প্রদান করা হয়. এই স্ট্রিংটি অন্যান্য জাভাস্ক্রিপ্ট দ্বারা পুনরুদ্ধার করা যেতে পারে কিভাবে ডায়ালগটি বন্ধ করা হয়েছিল সে সম্পর্কে অন্তর্দৃষ্টি পেতে। ব্যবহারকারীর মিথস্ক্রিয়া সম্পর্কে আমার অ্যাপ্লিকেশনের প্রসঙ্গ সরবরাহ করার জন্য, আপনি দেখতে পাবেন যে আমি যখনই বিভিন্ন বোতাম থেকে ফাংশনটিতে কল করি তখন আমি ঘনিষ্ঠ স্ট্রিংগুলি সরবরাহ করেছি।

ক্লোজিং এবং ক্লোজড ইভেন্ট যোগ করা হচ্ছে

ডায়ালগ উপাদানটি একটি ক্লোজ ইভেন্টের সাথে আসে: ডায়ালগ close() ফাংশনটি কল করা হলে এটি অবিলম্বে নির্গত হয়। যেহেতু আমরা এই উপাদানটিকে অ্যানিমেট করছি, তাই অ্যানিমেশনের আগে এবং পরে ইভেন্টগুলি থাকা ভাল, ডেটা নেওয়ার জন্য বা ডায়ালগ ফর্মটি রিসেট করার জন্য পরিবর্তনের জন্য৷ আমি এখানে এটি ব্যবহার করি বন্ধ ডায়ালগে inert বৈশিষ্ট্যের সংযোজন পরিচালনা করতে, এবং ডেমোতে আমি এগুলি ব্যবহার করি অবতার তালিকা পরিবর্তন করতে যদি ব্যবহারকারী একটি নতুন চিত্র জমা দেয়।

এটি অর্জন করতে, closing এবং closed নামে দুটি নতুন ইভেন্ট তৈরি করুন। তারপর ডায়ালগে অন্তর্নির্মিত ক্লোজ ইভেন্টের জন্য শুনুন। এখান থেকে, ডায়ালগটিকে inert করতে সেট করুন এবং closing ইভেন্টটি প্রেরণ করুন। পরবর্তী কাজটি হল অ্যানিমেশন এবং ট্রানজিশনগুলি ডায়ালগে চলা শেষ হওয়ার জন্য অপেক্ষা করা, তারপরে closed ইভেন্টটি প্রেরণ করা।

const dialogClosingEvent = new Event('closing')
const dialogClosedEvent  = new Event('closed')

export default async function (dialog) {
  
  dialog.addEventListener('close', dialogClose)
}

const dialogClose = async ({target:dialog}) => {
  dialog.setAttribute('inert', '')
  dialog.dispatchEvent(dialogClosingEvent)

  await animationsComplete(dialog)

  dialog.dispatchEvent(dialogClosedEvent)
}

const animationsComplete = element =>
  Promise.allSettled(
    element.getAnimations().map(animation => 
      animation.finished))

animationsComplete ফাংশন, যা একটি টোস্ট উপাদান তৈরিতেও ব্যবহৃত হয়, অ্যানিমেশন এবং ট্রানজিশন প্রতিশ্রুতির সমাপ্তির উপর ভিত্তি করে একটি প্রতিশ্রুতি প্রদান করে। এই কারণে dialogClose একটি অ্যাসিঙ্ক ফাংশন ; এটি তখন প্রতিশ্রুতি ফিরে পাওয়ার await করতে পারে এবং বন্ধ ইভেন্টে আত্মবিশ্বাসের সাথে এগিয়ে যেতে পারে।

খোলার এবং খোলা ঘটনা যোগ করা হচ্ছে

এই ইভেন্টগুলি যোগ করা ততটা সহজ নয় কারণ অন্তর্নির্মিত ডায়ালগ উপাদানটি বন্ধের মতো একটি খোলা ইভেন্ট প্রদান করে না। ডায়ালগের বৈশিষ্ট্য পরিবর্তনের অন্তর্দৃষ্টি প্রদান করতে আমি একটি MutationObserver ব্যবহার করি। এই পর্যবেক্ষক-এ, আমি ওপেন অ্যাট্রিবিউটের পরিবর্তনগুলি দেখব এবং সেই অনুযায়ী কাস্টম ইভেন্টগুলি পরিচালনা করব।

আমরা যেভাবে ক্লোজিং এবং ক্লোজড ইভেন্টগুলি শুরু করেছি, একইভাবে opening এবং opened নামে দুটি নতুন ইভেন্ট তৈরি করুন। যেখানে আমরা আগে ডায়ালগ বন্ধ ইভেন্টের জন্য শুনেছিলাম, এইবার ডায়ালগের বৈশিষ্ট্যগুলি দেখার জন্য একটি তৈরি মিউটেশন পর্যবেক্ষক ব্যবহার করুন৷


const dialogOpeningEvent = new Event('opening')
const dialogOpenedEvent  = new Event('opened')

export default async function (dialog) {
  
  dialogAttrObserver.observe(dialog, { 
    attributes: true,
  })
}

const dialogAttrObserver = new MutationObserver((mutations, observer) => {
  mutations.forEach(async mutation => {
    if (mutation.attributeName === 'open') {
      const dialog = mutation.target

      const isOpen = dialog.hasAttribute('open')
      if (!isOpen) return

      dialog.removeAttribute('inert')

      // set focus
      const focusTarget = dialog.querySelector('[autofocus]')
      focusTarget
        ? focusTarget.focus()
        : dialog.querySelector('button').focus()

      dialog.dispatchEvent(dialogOpeningEvent)
      await animationsComplete(dialog)
      dialog.dispatchEvent(dialogOpenedEvent)
    }
  })
})

মিউটেশন পর্যবেক্ষক কলব্যাক ফাংশন কল করা হবে যখন ডায়ালগ বৈশিষ্ট্যগুলি পরিবর্তন করা হয়, একটি অ্যারে হিসাবে পরিবর্তনের তালিকা প্রদান করে। বৈশিষ্ট্যের পরিবর্তনের উপর পুনরাবৃত্তি করুন, attributeName খোলার জন্য খুঁজছেন। এর পরে, উপাদানটির বৈশিষ্ট্য আছে কিনা তা পরীক্ষা করুন: এটি ডায়ালগটি খোলা হয়েছে কিনা তা জানায়। যদি এটি খোলা হয়ে থাকে, inert বৈশিষ্ট্যটি সরান, autofocus অনুরোধকারী একটি উপাদান বা ডায়ালগে পাওয়া প্রথম button উপাদানটিতে ফোকাস সেট করুন। শেষ, সমাপ্তি এবং বন্ধ ইভেন্টের অনুরূপ, এখনই উদ্বোধনী ইভেন্টটি প্রেরণ করুন, অ্যানিমেশনগুলি শেষ হওয়ার জন্য অপেক্ষা করুন, তারপর খোলা ইভেন্টটি প্রেরণ করুন৷

একটি সরানো ইভেন্ট যোগ করা হচ্ছে৷

একক পৃষ্ঠার অ্যাপ্লিকেশনগুলিতে, ডায়ালগগুলি প্রায়শই রুট বা অন্যান্য অ্যাপ্লিকেশনের প্রয়োজন এবং অবস্থার উপর ভিত্তি করে যুক্ত এবং সরানো হয়। একটি ডায়ালগ সরানো হলে ইভেন্ট বা ডেটা পরিষ্কার করতে এটি কার্যকর হতে পারে।

আপনি অন্য মিউটেশন পর্যবেক্ষকের সাথে এটি অর্জন করতে পারেন। এবার, ডায়ালগ এলিমেন্টে অ্যাট্রিবিউটগুলি পর্যবেক্ষণ করার পরিবর্তে, আমরা বডি এলিমেন্টের বাচ্চাদের পর্যবেক্ষণ করব এবং ডায়ালগ এলিমেন্টগুলি সরানো হচ্ছে কিনা তা দেখব।


const dialogRemovedEvent = new Event('removed')

export default async function (dialog) {
  
  dialogDeleteObserver.observe(document.body, {
    attributes: false,
    subtree: false,
    childList: true,
  })
}

const dialogDeleteObserver = new MutationObserver((mutations, observer) => {
  mutations.forEach(mutation => {
    mutation.removedNodes.forEach(removedNode => {
      if (removedNode.nodeName === 'DIALOG') {
        removedNode.removeEventListener('click', lightDismiss)
        removedNode.removeEventListener('close', dialogClose)
        removedNode.dispatchEvent(dialogRemovedEvent)
      }
    })
  })
})

মিউটেশন পর্যবেক্ষক কলব্যাক বলা হয় যখনই নথির মূল অংশ থেকে শিশুদের যোগ করা বা সরানো হয়। নির্দিষ্ট মিউটেশনগুলি দেখা হচ্ছে removedNodes জন্য যেগুলির একটি ডায়ালগের nodeName রয়েছে৷ একটি ডায়ালগ মুছে ফেলা হলে, মেমরি খালি করার জন্য ক্লিক এবং বন্ধ ইভেন্টগুলি সরানো হয় এবং কাস্টম সরানো ইভেন্টটি প্রেরণ করা হয়।

লোডিং অ্যাট্রিবিউট অপসারণ করা হচ্ছে

পৃষ্ঠায় বা পৃষ্ঠা লোড করার সময় ডায়ালগ অ্যানিমেশনটিকে তার প্রস্থান অ্যানিমেশন চালানো থেকে বিরত রাখতে, ডায়ালগে একটি লোডিং বৈশিষ্ট্য যুক্ত করা হয়েছে। নিম্নলিখিত স্ক্রিপ্টটি ডায়ালগ অ্যানিমেশনগুলি চলা শেষ হওয়ার জন্য অপেক্ষা করে, তারপর বৈশিষ্ট্যটি সরিয়ে দেয়। এখন ডায়ালগটি ভিতরে এবং বাইরে অ্যানিমেট করার জন্য বিনামূল্যে, এবং আমরা কার্যকরভাবে একটি অন্যথায় বিভ্রান্তিকর অ্যানিমেশন লুকিয়ে রেখেছি।

export default async function (dialog) {
  
  await animationsComplete(dialog)
  dialog.removeAttribute('loading')
}

এখানে পৃষ্ঠা লোডের কীফ্রেম অ্যানিমেশন প্রতিরোধের সমস্যা সম্পর্কে আরও জানুন।

সব একসাথে

এখানে dialog.js সম্পূর্ণরূপে রয়েছে, এখন আমরা প্রতিটি বিভাগকে পৃথকভাবে ব্যাখ্যা করেছি:

// custom events to be added to <dialog>
const dialogClosingEvent = new Event('closing')
const dialogClosedEvent  = new Event('closed')
const dialogOpeningEvent = new Event('opening')
const dialogOpenedEvent  = new Event('opened')
const dialogRemovedEvent = new Event('removed')

// track opening
const dialogAttrObserver = new MutationObserver((mutations, observer) => {
  mutations.forEach(async mutation => {
    if (mutation.attributeName === 'open') {
      const dialog = mutation.target

      const isOpen = dialog.hasAttribute('open')
      if (!isOpen) return

      dialog.removeAttribute('inert')

      // set focus
      const focusTarget = dialog.querySelector('[autofocus]')
      focusTarget
        ? focusTarget.focus()
        : dialog.querySelector('button').focus()

      dialog.dispatchEvent(dialogOpeningEvent)
      await animationsComplete(dialog)
      dialog.dispatchEvent(dialogOpenedEvent)
    }
  })
})

// track deletion
const dialogDeleteObserver = new MutationObserver((mutations, observer) => {
  mutations.forEach(mutation => {
    mutation.removedNodes.forEach(removedNode => {
      if (removedNode.nodeName === 'DIALOG') {
        removedNode.removeEventListener('click', lightDismiss)
        removedNode.removeEventListener('close', dialogClose)
        removedNode.dispatchEvent(dialogRemovedEvent)
      }
    })
  })
})

// wait for all dialog animations to complete their promises
const animationsComplete = element =>
  Promise.allSettled(
    element.getAnimations().map(animation => 
      animation.finished))

// click outside the dialog handler
const lightDismiss = ({target:dialog}) => {
  if (dialog.nodeName === 'DIALOG')
    dialog.close('dismiss')
}

const dialogClose = async ({target:dialog}) => {
  dialog.setAttribute('inert', '')
  dialog.dispatchEvent(dialogClosingEvent)

  await animationsComplete(dialog)

  dialog.dispatchEvent(dialogClosedEvent)
}

// page load dialogs setup
export default async function (dialog) {
  dialog.addEventListener('click', lightDismiss)
  dialog.addEventListener('close', dialogClose)

  dialogAttrObserver.observe(dialog, { 
    attributes: true,
  })

  dialogDeleteObserver.observe(document.body, {
    attributes: false,
    subtree: false,
    childList: true,
  })

  // remove loading attribute
  // prevent page load @keyframes playing
  await animationsComplete(dialog)
  dialog.removeAttribute('loading')
}

dialog.js মডিউল ব্যবহার করে

মডিউল থেকে রপ্তানিকৃত ফাংশনটি এই নতুন ইভেন্ট এবং কার্যকারিতা যোগ করতে চায় এমন একটি ডায়ালগ উপাদানকে কল করা এবং পাস করার প্রত্যাশা করে:

import GuiDialog from './dialog.js'

const MegaDialog = document.querySelector('#MegaDialog')
const MiniDialog = document.querySelector('#MiniDialog')

GuiDialog(MegaDialog)
GuiDialog(MiniDialog)

ঠিক তেমনি, দুটি ডায়ালগ হালকা বরখাস্ত, অ্যানিমেশন লোডিং ফিক্স এবং আরও ইভেন্টের সাথে কাজ করার জন্য আপগ্রেড করা হয়েছে।

নতুন কাস্টম ঘটনা শোনা

প্রতিটি আপগ্রেড করা ডায়ালগ উপাদান এখন পাঁচটি নতুন ইভেন্ট শুনতে পারে, যেমন:

MegaDialog.addEventListener('closing', dialogClosing)
MegaDialog.addEventListener('closed', dialogClosed)

MegaDialog.addEventListener('opening', dialogOpening)
MegaDialog.addEventListener('opened', dialogOpened)

MegaDialog.addEventListener('removed', dialogRemoved)

এই ঘটনাগুলি পরিচালনা করার দুটি উদাহরণ এখানে রয়েছে:

const dialogOpening = ({target:dialog}) => {
  console.log('Dialog opening', dialog)
}

const dialogClosed = ({target:dialog}) => {
  console.log('Dialog closed', dialog)
  console.info('Dialog user action:', dialog.returnValue)

  if (dialog.returnValue === 'confirm') {
    // do stuff with the form values
    const dialogFormData = new FormData(dialog.querySelector('form'))
    console.info('Dialog form data', Object.fromEntries(dialogFormData.entries()))

    // then reset the form
    dialog.querySelector('form')?.reset()
  }
}

আমি ডায়ালগ উপাদান দিয়ে যে ডেমো তৈরি করেছি, আমি সেই বন্ধ ইভেন্ট এবং ফর্ম ডেটা তালিকায় একটি নতুন অবতার উপাদান যোগ করতে ব্যবহার করি। টাইমিং ভাল যে ডায়ালগটি তার প্রস্থান অ্যানিমেশন সম্পূর্ণ করেছে, এবং তারপর কিছু স্ক্রিপ্ট নতুন অবতারে অ্যানিমেট হয়েছে। নতুন ইভেন্টের জন্য ধন্যবাদ, ব্যবহারকারীর অভিজ্ঞতা আরও মসৃণ হতে পারে।

dialog.returnValue লক্ষ্য করুন : এতে পাস করা ক্লোজ স্ট্রিং থাকে যখন ডায়ালগ close() ইভেন্ট কল করা হয়। dialogClosed ইভেন্টে ডায়ালগটি বন্ধ, বাতিল বা নিশ্চিত করা হয়েছে কিনা তা জানার জন্য এটি গুরুত্বপূর্ণ। এটি নিশ্চিত হলে, স্ক্রিপ্টটি ফর্মের মানগুলি ধরে নেয় এবং ফর্মটি পুনরায় সেট করে৷ রিসেটটি কার্যকর যাতে ডায়ালগটি আবার দেখানো হয়, এটি ফাঁকা থাকে এবং একটি নতুন জমা দেওয়ার জন্য প্রস্তুত থাকে।

উপসংহার

এখন যেহেতু আপনি জানেন যে আমি কীভাবে এটি করেছি, আপনি কেমন হবে‽ 🙂৷

আসুন আমাদের পদ্ধতির বৈচিত্র্য আনুন এবং ওয়েবে তৈরি করার সমস্ত উপায় শিখি।

একটি ডেমো তৈরি করুন, আমাকে লিঙ্কগুলি টুইট করুন এবং আমি নীচের সম্প্রদায়ের রিমিক্স বিভাগে এটি যুক্ত করব!

কমিউনিটি রিমিক্স

সম্পদ

,

কিভাবে রঙ-অভিযোজিত, প্রতিক্রিয়াশীল, এবং অ্যাক্সেসযোগ্য মিনি এবং মেগা মডেলগুলি <dialog> উপাদান দিয়ে তৈরি করা যায় তার একটি মৌলিক ওভারভিউ।

এই পোস্টে আমি <dialog> উপাদানের সাথে রঙ-অভিযোজিত, প্রতিক্রিয়াশীল, এবং অ্যাক্সেসযোগ্য মিনি এবং মেগা মডেলগুলি কীভাবে তৈরি করা যায় সে সম্পর্কে আমার চিন্তাভাবনা শেয়ার করতে চাই। ডেমো চেষ্টা করুন এবং উৎস দেখুন !

তাদের হালকা এবং অন্ধকার থিমে মেগা এবং মিনি ডায়ালগগুলির প্রদর্শনী৷

আপনি যদি ভিডিও পছন্দ করেন তবে এখানে এই পোস্টটির একটি YouTube সংস্করণ রয়েছে:

ওভারভিউ

<dialog> উপাদানটি ইন-পৃষ্ঠা প্রাসঙ্গিক তথ্য বা কর্মের জন্য দুর্দান্ত। বহু-পৃষ্ঠা অ্যাকশনের পরিবর্তে একই পৃষ্ঠার অ্যাকশন থেকে ব্যবহারকারীর অভিজ্ঞতা কখন উপকৃত হতে পারে তা বিবেচনা করুন: সম্ভবত ফর্মটি ছোট হওয়ার কারণে বা ব্যবহারকারীর কাছ থেকে শুধুমাত্র নিশ্চিত করা বা বাতিল করা প্রয়োজন।

<dialog> উপাদানটি সম্প্রতি ব্রাউজার জুড়ে স্থিতিশীল হয়েছে:

ব্রাউজার সমর্থন

  • ক্রোম: 37।
  • প্রান্ত: 79।
  • ফায়ারফক্স: 98।
  • সাফারি: 15.4.

উৎস

আমি দেখেছি যে উপাদানটিতে কয়েকটি জিনিস অনুপস্থিত ছিল, তাই এই GUI চ্যালেঞ্জে আমি বিকাশকারী অভিজ্ঞতার আইটেমগুলি যোগ করি যা আমি আশা করি: অতিরিক্ত ইভেন্ট, হালকা খারিজ, কাস্টম অ্যানিমেশন এবং একটি মিনি এবং মেগা টাইপ।

মার্কআপ

একটি <dialog> উপাদানের অপরিহার্য বিষয়গুলি বিনয়ী। উপাদানটি স্বয়ংক্রিয়ভাবে লুকানো হবে এবং আপনার বিষয়বস্তু ওভারলে করার জন্য শৈলী বিল্ট ইন থাকবে।

<dialog>
  …
</dialog>

আমরা এই বেসলাইন উন্নত করতে পারেন.

ঐতিহ্যগতভাবে, একটি ডায়ালগ উপাদান একটি মডেলের সাথে অনেক কিছু ভাগ করে এবং প্রায়শই নামগুলি বিনিময়যোগ্য। আমি এখানে ছোট ডায়ালগ পপআপ (মিনি), পাশাপাশি পুরো পৃষ্ঠার ডায়ালগ (মেগা) উভয়ের জন্য ডায়ালগ উপাদান ব্যবহার করার স্বাধীনতা নিয়েছি। আমি তাদের মেগা এবং মিনি নাম দিয়েছি, উভয় ডায়ালগই বিভিন্ন ব্যবহারের ক্ষেত্রে সামান্য অভিযোজিত। আমি আপনাকে টাইপ নির্দিষ্ট করার অনুমতি দেওয়ার জন্য একটি modal-mode বৈশিষ্ট্য যুক্ত করেছি:

<dialog id="MegaDialog" modal-mode="mega"></dialog>
<dialog id="MiniDialog" modal-mode="mini"></dialog>

হালকা এবং অন্ধকার উভয় থিমে মিনি এবং মেগা ডায়ালগের স্ক্রিনশট।

সবসময় নয়, তবে সাধারণত কিছু মিথস্ক্রিয়া তথ্য সংগ্রহ করতে ডায়ালগ উপাদান ব্যবহার করা হবে। সংলাপের উপাদানগুলির ভিতরে ফর্মগুলি একসাথে যেতে তৈরি করা হয় । একটি ফর্ম উপাদান আপনার ডায়ালগ বিষয়বস্তু মোড়ানো একটি ভাল ধারণা যাতে JavaScript ব্যবহারকারীর প্রবেশ করা ডেটা অ্যাক্সেস করতে পারে। উপরন্তু, method="dialog" ব্যবহার করে একটি ফর্মের ভিতরের বোতামগুলি জাভাস্ক্রিপ্ট এবং পাস ডেটা ছাড়াই একটি ডায়ালগ বন্ধ করতে পারে।

<dialog id="MegaDialog" modal-mode="mega">
  <form method="dialog">
    …
    <button value="cancel">Cancel</button>
    <button value="confirm">Confirm</button>
  </form>
</dialog>

মেগা ডায়ালগ

একটি মেগা ডায়ালগের ফর্মের ভিতরে তিনটি উপাদান থাকে: <header> , <article> , এবং <footer> । এগুলি শব্দার্থিক ধারক হিসাবে কাজ করে, সেইসাথে ডায়ালগ উপস্থাপনের জন্য শৈলী লক্ষ্যগুলি। শিরোনামটি মডেলটিকে শিরোনাম করে এবং একটি বন্ধ বোতাম অফার করে। নিবন্ধটি ফর্ম ইনপুট এবং তথ্যের জন্য। ফুটারটিতে অ্যাকশন বোতামগুলির একটি <menu> মেনু> রয়েছে।

<dialog id="MegaDialog" modal-mode="mega">
  <form method="dialog">
    <header>
      <h3>Dialog title</h3>
      <button onclick="this.closest('dialog').close('close')"></button>
    </header>
    <article>...</article>
    <footer>
      <menu>
        <button autofocus type="reset" onclick="this.closest('dialog').close('cancel')">Cancel</button>
        <button type="submit" value="confirm">Confirm</button>
      </menu>
    </footer>
  </form>
</dialog>

প্রথম মেনু বোতামটিতে autofocus এবং একটি onclick ইনলাইন ইভেন্ট হ্যান্ডলার রয়েছে। ডায়ালগ খোলা হলে autofocus অ্যাট্রিবিউটটি ফোকাস পাবে, এবং আমি এটিকে বাতিল বোতামে রাখা সর্বোত্তম অনুশীলন বলে মনে করি, নিশ্চিত বোতামে নয়। এটি নিশ্চিত করে যে নিশ্চিতকরণ ইচ্ছাকৃত এবং আকস্মিক নয়।

মিনি ডায়ালগ

মিনি ডায়ালগটি মেগা ডায়ালগের অনুরূপ, এটিতে একটি <header> উপাদান অনুপস্থিত। এটি এটিকে ছোট এবং আরও ইনলাইন হতে দেয়।

<dialog id="MiniDialog" modal-mode="mini">
  <form method="dialog">
    <article>
      <p>Are you sure you want to remove this user?</p>
    </article>
    <footer>
      <menu>
        <button autofocus type="reset" onclick="this.closest('dialog').close('cancel')">Cancel</button>
        <button type="submit" value="confirm">Confirm</button>
      </menu>
    </footer>
  </form>
</dialog>

ডায়ালগ উপাদানটি একটি সম্পূর্ণ ভিউপোর্ট উপাদানের জন্য একটি শক্তিশালী ভিত্তি প্রদান করে যা ডেটা এবং ব্যবহারকারীর মিথস্ক্রিয়া সংগ্রহ করতে পারে। এই প্রয়োজনীয় জিনিসগুলি আপনার সাইট বা অ্যাপে কিছু খুব আকর্ষণীয় এবং শক্তিশালী মিথস্ক্রিয়া তৈরি করতে পারে।

অ্যাক্সেসযোগ্যতা

ডায়ালগ উপাদানটির খুব ভাল অন্তর্নির্মিত অ্যাক্সেসযোগ্যতা রয়েছে। আমি সাধারণত করি এই বৈশিষ্ট্যগুলি যোগ করার পরিবর্তে, অনেকগুলি ইতিমধ্যেই রয়েছে।

ফোকাস পুনরুদ্ধার করা হচ্ছে

একটি sidenav কম্পোনেন্ট তৈরি করার ক্ষেত্রে আমরা যেমন হাতে করেছিলাম, এটা গুরুত্বপূর্ণ যে কোনো কিছু সঠিকভাবে খোলা এবং বন্ধ করা প্রাসঙ্গিক খোলা এবং বন্ধ বোতামগুলিতে ফোকাস রাখে। যখন সেই sidenav খোলে, ফোকাস বন্ধ বোতামে রাখা হয়। ক্লোজ বোতাম টিপলে, যে বোতামটি খুলেছে তাতে ফোকাস পুনরুদ্ধার করা হয়।

ডায়ালগ উপাদানের সাথে, এটি অন্তর্নির্মিত ডিফল্ট আচরণ:

দুর্ভাগ্যবশত, আপনি যদি ডায়ালগ ইন এবং আউট অ্যানিমেট করতে চান, এই কার্যকারিতা হারিয়ে যায়। জাভাস্ক্রিপ্ট বিভাগে আমি সেই কার্যকারিতা পুনরুদ্ধার করব।

ট্র্যাপিং ফোকাস

ডায়ালগ উপাদান নথিতে আপনার জন্য inert পরিচালনা করে। inert এর আগে, জাভাস্ক্রিপ্ট একটি উপাদান ছেড়ে ফোকাস দেখার জন্য ব্যবহার করা হত, যেখানে এটি বাধা দেয় এবং এটিকে ফিরিয়ে দেয়।

ব্রাউজার সমর্থন

  • ক্রোম: 102।
  • প্রান্ত: 102।
  • ফায়ারফক্স: 112।
  • সাফারি: 15.5।

উৎস

inert এর পরে, নথির যেকোনো অংশ এতটাই "হিমায়িত" হতে পারে যে সেগুলি আর ফোকাস লক্ষ্য নয় বা একটি মাউসের সাথে ইন্টারেক্টিভ নয়। ফোকাস আটকানোর পরিবর্তে, নথির একমাত্র ইন্টারেক্টিভ অংশে ফোকাস পরিচালিত হয়।

একটি উপাদান খুলুন এবং স্বয়ংক্রিয়ভাবে ফোকাস করুন

ডিফল্টরূপে, ডায়ালগ উপাদানটি ডায়ালগ মার্কআপের প্রথম ফোকাসযোগ্য উপাদানটিতে ফোকাস বরাদ্দ করবে। ব্যবহারকারীর জন্য ডিফল্ট করার জন্য এটি সেরা উপাদান না হলে, autofocus বৈশিষ্ট্যটি ব্যবহার করুন। পূর্বে বর্ণিত হিসাবে, আমি এটি বাতিল বোতামে রাখা ভাল অভ্যাস খুঁজে পেয়েছি এবং নিশ্চিত বোতামে নয়। এটি নিশ্চিত করে যে নিশ্চিতকরণ ইচ্ছাকৃত এবং আকস্মিক নয়।

এস্কেপ কী দিয়ে বন্ধ করা হচ্ছে

এই সম্ভাব্য বাধা সৃষ্টিকারী উপাদানটিকে বন্ধ করা সহজ করা গুরুত্বপূর্ণ। সৌভাগ্যবশত, ডায়ালগ উপাদানটি আপনার জন্য পালানোর কী পরিচালনা করবে, আপনাকে অর্কেস্ট্রেশনের বোঝা থেকে মুক্ত করবে।

শৈলী

ডায়ালগ উপাদান স্টাইল করার একটি সহজ পথ এবং একটি কঠিন পথ আছে। ডায়ালগের প্রদর্শন বৈশিষ্ট্য পরিবর্তন না করে এবং এর সীমাবদ্ধতার সাথে কাজ করে সহজ পথটি অর্জন করা হয়। আমি ডায়ালগ খোলা এবং বন্ধ করার জন্য কাস্টম অ্যানিমেশন প্রদান করার জন্য কঠিন পথে চলেছি, display সম্পত্তি গ্রহণ এবং আরও অনেক কিছু।

খোলা প্রপস সঙ্গে স্টাইলিং

অভিযোজিত রঙ এবং সামগ্রিক ডিজাইনের সামঞ্জস্যকে ত্বরান্বিত করতে, আমি নির্লজ্জভাবে আমার CSS ভেরিয়েবল লাইব্রেরি Open Props নিয়ে এসেছি। বিনামূল্যে প্রদত্ত ভেরিয়েবল ছাড়াও, আমি একটি নর্মালাইজ ফাইল এবং কিছু বোতামও ইম্পোর্ট করি, উভয়ই Open Props ঐচ্ছিক আমদানি হিসাবে প্রদান করে। এই আমদানিগুলি আমাকে ডায়ালগ এবং ডেমো কাস্টমাইজ করার উপর ফোকাস করতে সাহায্য করে যখন এটিকে সমর্থন করতে এবং এটিকে সুন্দর দেখাতে প্রচুর শৈলীর প্রয়োজন হয় না।

<dialog> উপাদান স্টাইল করা

প্রদর্শন সম্পত্তির মালিকানা

একটি ডায়ালগ উপাদানের ডিফল্ট প্রদর্শন এবং আড়াল আচরণ প্রদর্শন বৈশিষ্ট্যকে block থেকে none । দুর্ভাগ্যবশত এর মানে হল এটি ভিতরে এবং বাইরে অ্যানিমেটেড করা যাবে না, শুধুমাত্র ভিতরে। আমি ভিতরে এবং বাইরে উভয়ই অ্যানিমেট করতে চাই, এবং প্রথম পদক্ষেপটি হল আমার নিজস্ব প্রদর্শন সম্পত্তি সেট করা:

dialog {
  display: grid;
}

উপরের সিএসএস স্নিপেটে যেমন দেখানো হয়েছে, ডিসপ্লে প্রপার্টির মান পরিবর্তন করে এবং তার মালিকানার মাধ্যমে, সঠিক ব্যবহারকারীর অভিজ্ঞতার সুবিধার্থে যথেষ্ট পরিমাণে স্টাইল পরিচালনা করা প্রয়োজন। প্রথমত, একটি ডায়ালগের ডিফল্ট অবস্থা বন্ধ করা হয়। আপনি এই অবস্থাটিকে দৃশ্যমানভাবে উপস্থাপন করতে পারেন এবং ডায়ালগটিকে নিম্নলিখিত শৈলীগুলির সাথে মিথস্ক্রিয়া গ্রহণ করা থেকে বিরত রাখতে পারেন:

dialog:not([open]) {
  pointer-events: none;
  opacity: 0;
}

এখন ডায়ালগটি অদৃশ্য এবং খোলা না থাকলে এর সাথে যোগাযোগ করা যাবে না। পরে আমি কিছু জাভাস্ক্রিপ্ট যোগ করব ডায়ালগে inert বৈশিষ্ট্য পরিচালনা করতে, নিশ্চিত করে যে কীবোর্ড এবং স্ক্রিন-রিডার ব্যবহারকারীরাও লুকানো ডায়ালগে পৌঁছাতে পারবেন না।

ডায়ালগটিকে একটি অভিযোজিত রঙের থিম দেওয়া

মেগা ডায়ালগ আলো এবং গাঢ় থিম দেখাচ্ছে, পৃষ্ঠের রং প্রদর্শন করছে।

color-scheme আপনার দস্তাবেজটিকে একটি ব্রাউজার-প্রদত্ত অভিযোজিত রঙের থিমে হালকা এবং অন্ধকার সিস্টেমের পছন্দগুলিতে বেছে নেয়, আমি ডায়ালগ উপাদানটিকে তার চেয়ে বেশি কাস্টমাইজ করতে চেয়েছিলাম। ওপেন প্রপস কয়েকটি পৃষ্ঠের রঙ সরবরাহ করে যা স্বয়ংক্রিয়ভাবে হালকা এবং অন্ধকার সিস্টেম পছন্দগুলির সাথে খাপ খায়, color-scheme ব্যবহারের অনুরূপ। এগুলি একটি ডিজাইনে স্তর তৈরি করার জন্য দুর্দান্ত এবং আমি স্তরের পৃষ্ঠের এই চেহারাটিকে দৃশ্যত সমর্থন করতে রঙ ব্যবহার করতে পছন্দ করি। পটভূমির রঙ var(--surface-1) ; সেই স্তরের উপরে বসতে, var(--surface-2) ব্যবহার করুন:

dialog {
  
  background: var(--surface-2);
  color: var(--text-1);
}

@media (prefers-color-scheme: dark) {
  dialog {
    border-block-start: var(--border-size-1) solid var(--surface-3);
  }
}

শিরোনাম এবং পাদচরণের মতো শিশু উপাদানগুলির জন্য পরে আরও অভিযোজিত রং যোগ করা হবে। আমি এগুলিকে একটি ডায়ালগ উপাদানের জন্য অতিরিক্ত বিবেচনা করি, কিন্তু একটি বাধ্যতামূলক এবং ভালভাবে ডিজাইন করা ডায়ালগ ডিজাইন তৈরি করার ক্ষেত্রে এটি সত্যিই গুরুত্বপূর্ণ৷

রেসপন্সিভ ডায়ালগ সাইজিং

ডায়ালগটি তার বিষয়বস্তুতে এর আকার অর্পণ করার জন্য ডিফল্ট, যা সাধারণত দুর্দান্ত। এখানে আমার লক্ষ্য হল max-inline-size একটি পঠনযোগ্য আকারে সীমাবদ্ধ করা ( --size-content-3 = 60ch ) বা ভিউপোর্ট প্রস্থের 90%। এটি নিশ্চিত করে যে ডায়ালগটি একটি মোবাইল ডিভাইসে প্রান্ত থেকে প্রান্তে যাবে না এবং একটি ডেস্কটপ স্ক্রিনে এত চওড়া হবে না যে এটি পড়া কঠিন। তারপর আমি একটি max-block-size যোগ করি যাতে ডায়ালগটি পৃষ্ঠার উচ্চতা অতিক্রম না করে। এর মানে হল যে ডায়ালগের স্ক্রোলযোগ্য এলাকাটি কোথায় তা আমাদের নির্দিষ্ট করতে হবে, যদি এটি একটি লম্বা ডায়ালগ উপাদান হয়।

dialog {
  
  max-inline-size: min(90vw, var(--size-content-3));
  max-block-size: min(80vh, 100%);
  max-block-size: min(80dvb, 100%);
  overflow: hidden;
}

লক্ষ্য করুন কিভাবে আমার max-block-size দুইবার আছে? প্রথমটি 80vh ব্যবহার করে, একটি শারীরিক ভিউপোর্ট ইউনিট। আমি আসলে যা চাই তা হল ডায়ালগটিকে আপেক্ষিক প্রবাহের মধ্যে রাখা, আন্তর্জাতিক ব্যবহারকারীদের জন্য, তাই আমি দ্বিতীয় ঘোষণায় যৌক্তিক, নতুন, এবং শুধুমাত্র আংশিকভাবে সমর্থিত dvb ইউনিট ব্যবহার করি যখন এটি আরও স্থিতিশীল হয়।

মেগা ডায়ালগ পজিশনিং

একটি ডায়ালগ উপাদানের অবস্থান নির্ধারণে সহায়তা করার জন্য, এটির দুটি অংশ ভেঙে ফেলা উচিত: পূর্ণ স্ক্রিন ব্যাকড্রপ এবং ডায়ালগ ধারক৷ ব্যাকড্রপকে অবশ্যই সবকিছু আবরণ করতে হবে, এই ডায়ালগটি সামনে রয়েছে এবং পিছনের বিষয়বস্তু অ্যাক্সেসযোগ্য নয় তা সমর্থন করার জন্য একটি ছায়া প্রভাব প্রদান করে৷ ডায়ালগ কন্টেইনারটি এই পটভূমিতে নিজেকে কেন্দ্রীভূত করতে এবং এর বিষয়বস্তুগুলির প্রয়োজনে যাই হোক না কেন আকার নিতে পারে৷

নিম্নলিখিত শৈলীগুলি উইন্ডোতে ডায়ালগ উপাদানটিকে ঠিক করে, প্রতিটি কোণে প্রসারিত করে, এবং বিষয়বস্তুকে কেন্দ্রীভূত করতে margin: auto ব্যবহার করে:

dialog {
  
  margin: auto;
  padding: 0;
  position: fixed;
  inset: 0;
  z-index: var(--layer-important);
}
মোবাইল মেগা ডায়ালগ শৈলী

ছোট ভিউপোর্টে, আমি এই পূর্ণ পৃষ্ঠার মেগা মডেলটিকে একটু ভিন্নভাবে স্টাইল করি। আমি নীচের মার্জিনটি 0 এ সেট করেছি, যা ডায়ালগ বিষয়বস্তুকে ভিউপোর্টের নীচে নিয়ে আসে। কয়েকটি স্টাইল সামঞ্জস্যের সাথে, আমি ব্যবহারকারীর থাম্বসের কাছাকাছি ডায়ালগটিকে একটি অ্যাকশনশিটে পরিণত করতে পারি:

@media (max-width: 768px) {
  dialog[modal-mode="mega"] {
    margin-block-end: 0;
    border-end-end-radius: 0;
    border-end-start-radius: 0;
  }
}

মার্জিন ব্যবধান ওভারলে করা ডেভটুলগুলির স্ক্রিনশট    খোলা থাকার সময় ডেস্কটপ এবং মোবাইল মেগা ডায়ালগ উভয়েই।

মিনি ডায়ালগ পজিশনিং

একটি বড় ভিউপোর্ট ব্যবহার করার সময় যেমন একটি ডেস্কটপ কম্পিউটারে, আমি মিনি ডায়ালগগুলিকে সেই উপাদানটির উপরে অবস্থান করতে বেছে নিয়েছি যা তাদের বলে। এটি করার জন্য আমি জাভাস্ক্রিপ্ট প্রয়োজন. আমি এখানে যে কৌশলটি ব্যবহার করি তা আপনি খুঁজে পেতে পারেন , কিন্তু আমি মনে করি এটি এই নিবন্ধের সুযোগের বাইরে। জাভাস্ক্রিপ্ট ছাড়া, মেগা ডায়ালগের মতোই স্ক্রিনের মাঝখানে মিনি ডায়ালগ দেখা যায়।

এটা পপ করা

অবশেষে, ডায়ালগে কিছু ফ্লেয়ার যোগ করুন যাতে এটি পৃষ্ঠার অনেক উপরে বসে থাকা একটি নরম পৃষ্ঠের মতো দেখায়। সংলাপের কোণগুলিকে বৃত্তাকার করে কোমলতা অর্জন করা হয়। গভীরতা ওপেন প্রপসের সাবধানে তৈরি করা শ্যাডো প্রপগুলির একটি দিয়ে অর্জন করা হয়:

dialog {
  
  border-radius: var(--radius-3);
  box-shadow: var(--shadow-6);
}

ব্যাকড্রপ সিউডো উপাদান কাস্টমাইজ করা হচ্ছে

আমি ব্যাকড্রপের সাথে খুব হালকাভাবে কাজ করতে বেছে নিয়েছি, শুধুমাত্র মেগা ডায়ালগে backdrop-filter সাথে একটি অস্পষ্ট প্রভাব যুক্ত করছি:

ব্রাউজার সমর্থন

  • ক্রোম: 76।
  • প্রান্ত: 79।
  • ফায়ারফক্স: 103।
  • সাফারি: 18।

উৎস

dialog[modal-mode="mega"]::backdrop {
  backdrop-filter: blur(25px);
}

আমি backdrop-filter এ একটি ট্রানজিশন করাও বেছে নিয়েছি, এই আশায় যে ব্রাউজারগুলি ভবিষ্যতে ব্যাকড্রপ উপাদানটি পরিবর্তন করার অনুমতি দেবে:

dialog::backdrop {
  transition: backdrop-filter .5s ease;
}

রঙিন অবতারের একটি অস্পষ্ট পটভূমিকে ওভারলে করা মেগা ডায়ালগের স্ক্রিনশট।

স্টাইলিং অতিরিক্ত

আমি এই বিভাগটিকে "অতিরিক্ত" বলি কারণ এটি সাধারণভাবে ডায়ালগ উপাদানের তুলনায় আমার ডায়ালগ উপাদান ডেমোর সাথে আরও বেশি কিছু করে।

স্ক্রোল কন্টেনমেন্ট

যখন ডায়ালগ দেখানো হয়, ব্যবহারকারী এখনও এটির পিছনে পৃষ্ঠাটি স্ক্রোল করতে সক্ষম হয়, যা আমি চাই না:

সাধারণত, overscroll-behavior আমার স্বাভাবিক সমাধান হবে, কিন্তু স্পেক অনুযায়ী , এটি ডায়ালগের উপর কোন প্রভাব ফেলে না কারণ এটি একটি স্ক্রোল পোর্ট নয়, অর্থাৎ এটি একটি স্ক্রলার নয় তাই প্রতিরোধ করার কিছু নেই। আমি এই নির্দেশিকা থেকে নতুন ইভেন্টগুলি দেখতে জাভাস্ক্রিপ্ট ব্যবহার করতে পারি, যেমন "বন্ধ" এবং "খোলা", এবং টগল overflow: hidden , বা আমি সব ব্রাউজারে :has() স্থিতিশীল হওয়ার জন্য অপেক্ষা করতে পারি:

ব্রাউজার সমর্থন

  • ক্রোম: 105।
  • প্রান্ত: 105।
  • ফায়ারফক্স: 121।
  • সাফারি: 15.4.

উৎস

html:has(dialog[open][modal-mode="mega"]) {
  overflow: hidden;
}

এখন যখন একটি মেগা ডায়ালগ খোলা থাকে, তখন html নথিতে overflow: hidden

<form> লেআউট

ব্যবহারকারীর কাছ থেকে মিথস্ক্রিয়া তথ্য সংগ্রহের জন্য একটি অত্যন্ত গুরুত্বপূর্ণ উপাদান হওয়ার পাশাপাশি, আমি শিরোনাম, পাদচরণ এবং নিবন্ধ উপাদানগুলিকে এখানে ব্যবহার করি। এই লেআউটের সাথে আমি নিবন্ধ শিশুটিকে একটি স্ক্রোলযোগ্য এলাকা হিসাবে স্পষ্ট করতে চাই। আমি grid-template-rows দিয়ে এটি অর্জন করি। নিবন্ধ উপাদান 1fr দেওয়া হয়েছে এবং ফর্ম নিজেই ডায়ালগ উপাদান হিসাবে একই সর্বোচ্চ উচ্চতা আছে. এই দৃঢ় উচ্চতা এবং দৃঢ় সারির আকার সেট করা যা নিবন্ধ উপাদানটিকে সীমাবদ্ধ হতে দেয় এবং যখন এটি ওভারফ্লো হয় তখন স্ক্রোল করে:

dialog > form {
  display: grid;
  grid-template-rows: auto 1fr auto;
  align-items: start;
  max-block-size: 80vh;
  max-block-size: 80dvb;
}

সারির উপর গ্রিড লেআউট তথ্য ওভারলে করা devtools এর স্ক্রিনশট।

ডায়ালগ <header> স্টাইল করা হচ্ছে

এই উপাদানটির ভূমিকা হল ডায়ালগ বিষয়বস্তুর জন্য একটি শিরোনাম প্রদান করা এবং ক্লোজ বোতাম খুঁজে পাওয়া সহজ অফার করা। এটিকে একটি পৃষ্ঠের রঙও দেওয়া হয়েছে যাতে এটি ডায়ালগ নিবন্ধের বিষয়বস্তুর পিছনে থাকে। এই প্রয়োজনীয়তাগুলি একটি ফ্লেক্সবক্স কন্টেইনারের দিকে নিয়ে যায়, উল্লম্বভাবে সারিবদ্ধ আইটেম যা তাদের প্রান্তে ফাঁকা থাকে এবং শিরোনাম এবং বন্ধ বোতামগুলিকে কিছু জায়গা দেওয়ার জন্য কিছু প্যাডিং এবং ফাঁক থাকে:

dialog > form > header {
  display: flex;
  gap: var(--size-3);
  justify-content: space-between;
  align-items: flex-start;
  background: var(--surface-2);
  padding-block: var(--size-3);
  padding-inline: var(--size-5);
}

@media (prefers-color-scheme: dark) {
  dialog > form > header {
    background: var(--surface-1);
  }
}

ডায়ালগ হেডারে ফ্লেক্সবক্স লেআউট তথ্য ওভারলে করা Chrome Devtools-এর স্ক্রিনশট।

শিরোনাম বন্ধ বোতাম স্টাইলিং

যেহেতু ডেমোটি ওপেন প্রপস বোতামগুলি ব্যবহার করছে, তাই বন্ধ বোতামটি একটি বৃত্তাকার আইকন কেন্দ্রিক বোতামে কাস্টমাইজ করা হয়েছে:

dialog > form > header > button {
  border-radius: var(--radius-round);
  padding: .75ch;
  aspect-ratio: 1;
  flex-shrink: 0;
  place-items: center;
  stroke: currentColor;
  stroke-width: 3px;
}

হেডার ক্লোজ বোতামের জন্য Chrome Devtools ওভারলে সাইজিং এবং প্যাডিং তথ্যের স্ক্রিনশট।

ডায়ালগ <article> স্টাইল করা

এই ডায়ালগে নিবন্ধ উপাদানটির একটি বিশেষ ভূমিকা রয়েছে: এটি একটি লম্বা বা দীর্ঘ ডায়ালগের ক্ষেত্রে স্ক্রোল করার উদ্দেশ্যে একটি স্থান।

এটি সম্পন্ন করার জন্য, প্যারেন্ট ফর্ম উপাদানটি নিজের জন্য কিছু সর্বোচ্চ নির্ধারণ করেছে যা এই নিবন্ধের উপাদানটি খুব লম্বা হলে পৌঁছাতে বাধা প্রদান করে। overflow-y: auto তাই স্ক্রলবারগুলি শুধুমাত্র প্রয়োজন হলেই দেখানো হয়, এর মধ্যে overscroll-behavior: contain , এবং বাকিগুলি কাস্টম উপস্থাপনা শৈলী হবে:

dialog > form > article {
  overflow-y: auto; 
  max-block-size: 100%; /* safari */
  overscroll-behavior-y: contain;
  display: grid;
  justify-items: flex-start;
  gap: var(--size-3);
  box-shadow: var(--shadow-2);
  z-index: var(--layer-1);
  padding-inline: var(--size-5);
  padding-block: var(--size-3);
}

@media (prefers-color-scheme: light) {
  dialog > form > article {
    background: var(--surface-1);
  }
}

ফুটারের ভূমিকা হল অ্যাকশন বোতামের মেনু ধারণ করা। ফ্লেক্সবক্স পাদচরণ ইনলাইন অক্ষের শেষে বিষয়বস্তু সারিবদ্ধ করতে ব্যবহৃত হয়, তারপর বোতামগুলিকে কিছু জায়গা দেওয়ার জন্য কিছু ব্যবধান।

dialog > form > footer {
  background: var(--surface-2);
  display: flex;
  flex-wrap: wrap;
  gap: var(--size-3);
  justify-content: space-between;
  align-items: flex-start;
  padding-inline: var(--size-5);
  padding-block: var(--size-3);
}

@media (prefers-color-scheme: dark) {
  dialog > form > footer {
    background: var(--surface-1);
  }
}

ফুটার এলিমেন্টে ফ্লেক্সবক্স লেআউট তথ্য ওভারলে করা Chrome Devtools-এর স্ক্রিনশট।

menu উপাদানটি ডায়ালগের জন্য অ্যাকশন বোতামগুলি ধারণ করতে ব্যবহৃত হয়। এটি বোতামগুলির মধ্যে স্থান প্রদানের জন্য gap সহ একটি মোড়ানো ফ্লেক্সবক্স লেআউট ব্যবহার করে। মেনু উপাদানগুলিতে প্যাডিং থাকে যেমন <ul> । আমি সেই স্টাইলটিও সরিয়ে ফেলি যেহেতু আমার এটির প্রয়োজন নেই।

dialog > form > footer > menu {
  display: flex;
  flex-wrap: wrap;
  gap: var(--size-3);
  padding-inline-start: 0;
}

dialog > form > footer > menu:only-child {
  margin-inline-start: auto;
}

ফুটার মেনু উপাদানগুলিতে ফ্লেক্সবক্স তথ্য ওভারলে করা Chrome Devtools-এর স্ক্রিনশট৷

অ্যানিমেশন

ডায়ালগ উপাদানগুলি প্রায়শই অ্যানিমেটেড হয় কারণ তারা উইন্ডোতে প্রবেশ করে এবং প্রস্থান করে। এই প্রবেশদ্বার এবং প্রস্থানের জন্য ডায়ালগগুলিকে কিছু সহায়ক গতি প্রদান করা ব্যবহারকারীদের প্রবাহে নিজেদেরকে অভিমুখী করতে সহায়তা করে৷

সাধারণত ডায়ালগ উপাদান শুধুমাত্র অ্যানিমেটেড হতে পারে, বাইরে নয়। এর কারণ হল ব্রাউজার উপাদানটির display বৈশিষ্ট্য টগল করে। এর আগে, গাইডটি ডিসপ্লেকে গ্রিডে সেট করে এবং কখনই এটিকে সেট করে না। এটি ভিতরে এবং বাইরে অ্যানিমেট করার ক্ষমতা আনলক করে।

ওপেন প্রপস ব্যবহারের জন্য অনেক কীফ্রেম অ্যানিমেশনের সাথে আসে, যা অর্কেস্ট্রেশনকে সহজ এবং সুস্পষ্ট করে তোলে। আমি যে অ্যানিমেশন লক্ষ্য এবং স্তরযুক্ত পদ্ধতি গ্রহণ করেছি তা এখানে রয়েছে:

  1. হ্রাসকৃত গতি হল ডিফল্ট রূপান্তর, একটি সাধারণ অস্বচ্ছতা ফেইড ইন এবং আউট।
  2. গতি ঠিক থাকলে, স্লাইড এবং স্কেল অ্যানিমেশন যোগ করা হয়।
  3. মেগা ডায়ালগের জন্য প্রতিক্রিয়াশীল মোবাইল লেআউটটি স্লাইড আউট করার জন্য সামঞ্জস্য করা হয়েছে।

একটি নিরাপদ এবং অর্থপূর্ণ ডিফল্ট রূপান্তর

ওপেন প্রপস কীফ্রেমের সাথে ফেইড ইন এবং আউট করার জন্য আসে, আমি সম্ভাব্য আপগ্রেড হিসাবে কীফ্রেম অ্যানিমেশনগুলির সাথে ডিফল্ট হিসাবে রূপান্তরের এই স্তরযুক্ত পদ্ধতিকে পছন্দ করি। এর আগে আমরা ডায়ালগের দৃশ্যমানতা অস্বচ্ছতার সাথে স্টাইল করেছি, [open] অ্যাট্রিবিউটের উপর নির্ভর করে 1 বা 0 অর্কেস্ট্রেট করে। 0% এবং 100% এর মধ্যে স্থানান্তর করতে, ব্রাউজারকে বলুন কতক্ষণ এবং আপনি কী ধরনের সহজ করতে চান:

dialog {
  transition: opacity .5s var(--ease-3);
}

ট্রানজিশনে গতি যোগ করা হচ্ছে

ব্যবহারকারীর গতির সাথে ঠিক থাকলে, মেগা এবং মিনি ডায়ালগ উভয়ই তাদের প্রবেশদ্বার হিসাবে স্লাইড করা উচিত এবং তাদের প্রস্থান হিসাবে স্কেল আউট করা উচিত। আপনি prefers-reduced-motion মিডিয়া ক্যোয়ারী এবং কয়েকটি ওপেন প্রপস দিয়ে এটি অর্জন করতে পারেন:

@media (prefers-reduced-motion: no-preference) {
  dialog {
    animation: var(--animation-scale-down) forwards;
    animation-timing-function: var(--ease-squish-3);
  }

  dialog[open] {
    animation: var(--animation-slide-in-up) forwards;
  }
}

মোবাইলের জন্য প্রস্থান অ্যানিমেশন মানিয়ে নেওয়া

এর আগে স্টাইলিং বিভাগে, মেগা ডায়ালগ শৈলী মোবাইল ডিভাইসগুলির জন্য একটি অ্যাকশন শীটের মতো হওয়ার জন্য অভিযোজিত হয়েছে, যেন একটি ছোট কাগজের টুকরো স্ক্রিনের নিচ থেকে উপরে উঠে গেছে এবং এখনও নীচে সংযুক্ত রয়েছে। স্কেল আউট এক্সিট অ্যানিমেশন এই নতুন ডিজাইনের সাথে ভালভাবে মানানসই নয়, এবং আমরা কয়েকটি মিডিয়া প্রশ্ন এবং কিছু ওপেন প্রপসের সাথে এটি মানিয়ে নিতে পারি:

@media (prefers-reduced-motion: no-preference) and @media (max-width: 768px) {
  dialog[modal-mode="mega"] {
    animation: var(--animation-slide-out-down) forwards;
    animation-timing-function: var(--ease-squish-2);
  }
}

জাভাস্ক্রিপ্ট

জাভাস্ক্রিপ্টের সাথে যোগ করার জন্য বেশ কয়েকটি জিনিস রয়েছে:

// dialog.js
export default async function (dialog) {
  // add light dismiss
  // add closing and closed events
  // add opening and opened events
  // add removed event
  // removing loading attribute
}

এই সংযোজনগুলি হালকা বরখাস্তের আকাঙ্ক্ষা থেকে উদ্ভূত হয় (সংলাপের পটভূমিতে ক্লিক করা), অ্যানিমেশন, এবং ফর্ম ডেটা পাওয়ার জন্য আরও ভাল সময়ের জন্য কিছু অতিরিক্ত ইভেন্ট।

হালকা খারিজ যোগ করা হচ্ছে

এই কাজটি সহজবোধ্য এবং একটি ডায়ালগ উপাদানের একটি দুর্দান্ত সংযোজন যা অ্যানিমেটেড হচ্ছে না। ডায়ালগ এলিমেন্টে ক্লিক দেখে এবং কি ক্লিক করা হয়েছে তা মূল্যায়ন করতে ইভেন্ট বুদবুদ ব্যবহার করে ইন্টারঅ্যাকশনটি অর্জন করা হয়, এবং এটি শুধুমাত্র close() যদি এটি শীর্ষ-সবথেকে উপাদান হয়:

export default async function (dialog) {
  dialog.addEventListener('click', lightDismiss)
}

const lightDismiss = ({target:dialog}) => {
  if (dialog.nodeName === 'DIALOG')
    dialog.close('dismiss')
}

dialog.close('dismiss') লক্ষ্য করুন। ঘটনা বলা হয় এবং একটি স্ট্রিং প্রদান করা হয়. এই স্ট্রিংটি অন্যান্য জাভাস্ক্রিপ্ট দ্বারা পুনরুদ্ধার করা যেতে পারে কিভাবে ডায়ালগটি বন্ধ করা হয়েছিল সে সম্পর্কে অন্তর্দৃষ্টি পেতে। ব্যবহারকারীর মিথস্ক্রিয়া সম্পর্কে আমার অ্যাপ্লিকেশনের প্রসঙ্গ সরবরাহ করার জন্য, আপনি দেখতে পাবেন যে আমি যখনই বিভিন্ন বোতাম থেকে ফাংশনটিতে কল করি তখন আমি ঘনিষ্ঠ স্ট্রিংগুলি সরবরাহ করেছি।

ক্লোজিং এবং ক্লোজড ইভেন্ট যোগ করা হচ্ছে

ডায়ালগ উপাদানটি একটি ক্লোজ ইভেন্টের সাথে আসে: ডায়ালগ close() ফাংশনটি কল করা হলে এটি অবিলম্বে নির্গত হয়। যেহেতু আমরা এই উপাদানটিকে অ্যানিমেট করছি, তাই অ্যানিমেশনের আগে এবং পরে ইভেন্টগুলি থাকা ভাল, ডেটা নেওয়ার জন্য বা ডায়ালগ ফর্মটি রিসেট করার জন্য পরিবর্তনের জন্য৷ আমি এখানে এটি ব্যবহার করি বন্ধ ডায়ালগে inert বৈশিষ্ট্যের সংযোজন পরিচালনা করতে, এবং ডেমোতে আমি এগুলি ব্যবহার করি অবতার তালিকা পরিবর্তন করতে যদি ব্যবহারকারী একটি নতুন চিত্র জমা দেয়।

এটি অর্জন করতে, closing এবং closed নামে দুটি নতুন ইভেন্ট তৈরি করুন। তারপর ডায়ালগে অন্তর্নির্মিত ক্লোজ ইভেন্টের জন্য শুনুন। এখান থেকে, ডায়ালগটিকে inert করতে সেট করুন এবং closing ইভেন্টটি প্রেরণ করুন। পরবর্তী কাজটি হল অ্যানিমেশন এবং ট্রানজিশনগুলি ডায়ালগে চলা শেষ হওয়ার জন্য অপেক্ষা করা, তারপরে closed ইভেন্টটি প্রেরণ করা।

const dialogClosingEvent = new Event('closing')
const dialogClosedEvent  = new Event('closed')

export default async function (dialog) {
  
  dialog.addEventListener('close', dialogClose)
}

const dialogClose = async ({target:dialog}) => {
  dialog.setAttribute('inert', '')
  dialog.dispatchEvent(dialogClosingEvent)

  await animationsComplete(dialog)

  dialog.dispatchEvent(dialogClosedEvent)
}

const animationsComplete = element =>
  Promise.allSettled(
    element.getAnimations().map(animation => 
      animation.finished))

animationsComplete ফাংশন, যা একটি টোস্ট উপাদান তৈরিতেও ব্যবহৃত হয়, অ্যানিমেশন এবং ট্রানজিশন প্রতিশ্রুতির সমাপ্তির উপর ভিত্তি করে একটি প্রতিশ্রুতি প্রদান করে। এই কারণে dialogClose একটি অ্যাসিঙ্ক ফাংশন ; এটি তখন প্রতিশ্রুতি ফিরে পাওয়ার await করতে পারে এবং বন্ধ ইভেন্টে আত্মবিশ্বাসের সাথে এগিয়ে যেতে পারে।

খোলার এবং খোলা ঘটনা যোগ করা হচ্ছে

এই ইভেন্টগুলি যোগ করা ততটা সহজ নয় কারণ অন্তর্নির্মিত ডায়ালগ উপাদানটি বন্ধের মতো একটি খোলা ইভেন্ট প্রদান করে না। ডায়ালগের বৈশিষ্ট্য পরিবর্তনের অন্তর্দৃষ্টি প্রদান করতে আমি একটি MutationObserver ব্যবহার করি। এই পর্যবেক্ষক-এ, আমি ওপেন অ্যাট্রিবিউটের পরিবর্তনগুলি দেখব এবং সেই অনুযায়ী কাস্টম ইভেন্টগুলি পরিচালনা করব।

আমরা যেভাবে ক্লোজিং এবং ক্লোজড ইভেন্টগুলি শুরু করেছি, একইভাবে opening এবং opened নামে দুটি নতুন ইভেন্ট তৈরি করুন। যেখানে আমরা আগে ডায়ালগ বন্ধ ইভেন্টের জন্য শুনেছিলাম, এইবার ডায়ালগের বৈশিষ্ট্যগুলি দেখার জন্য একটি তৈরি মিউটেশন পর্যবেক্ষক ব্যবহার করুন৷


const dialogOpeningEvent = new Event('opening')
const dialogOpenedEvent  = new Event('opened')

export default async function (dialog) {
  
  dialogAttrObserver.observe(dialog, { 
    attributes: true,
  })
}

const dialogAttrObserver = new MutationObserver((mutations, observer) => {
  mutations.forEach(async mutation => {
    if (mutation.attributeName === 'open') {
      const dialog = mutation.target

      const isOpen = dialog.hasAttribute('open')
      if (!isOpen) return

      dialog.removeAttribute('inert')

      // set focus
      const focusTarget = dialog.querySelector('[autofocus]')
      focusTarget
        ? focusTarget.focus()
        : dialog.querySelector('button').focus()

      dialog.dispatchEvent(dialogOpeningEvent)
      await animationsComplete(dialog)
      dialog.dispatchEvent(dialogOpenedEvent)
    }
  })
})

মিউটেশন পর্যবেক্ষক কলব্যাক ফাংশন কল করা হবে যখন ডায়ালগ বৈশিষ্ট্যগুলি পরিবর্তন করা হয়, একটি অ্যারে হিসাবে পরিবর্তনের তালিকা প্রদান করে। বৈশিষ্ট্যের পরিবর্তনের উপর পুনরাবৃত্তি করুন, attributeName খোলার জন্য খুঁজছেন। এর পরে, উপাদানটির বৈশিষ্ট্য আছে কিনা তা পরীক্ষা করুন: এটি ডায়ালগটি খোলা হয়েছে কিনা তা জানায়। যদি এটি খোলা হয়ে থাকে, inert বৈশিষ্ট্যটি সরান, autofocus অনুরোধকারী একটি উপাদান বা ডায়ালগে পাওয়া প্রথম button উপাদানটিতে ফোকাস সেট করুন। শেষ, সমাপ্তি এবং বন্ধ ইভেন্টের অনুরূপ, এখনই উদ্বোধনী ইভেন্টটি প্রেরণ করুন, অ্যানিমেশনগুলি শেষ হওয়ার জন্য অপেক্ষা করুন, তারপর খোলা ইভেন্টটি প্রেরণ করুন৷

একটি সরানো ইভেন্ট যোগ করা হচ্ছে৷

একক পৃষ্ঠার অ্যাপ্লিকেশনগুলিতে, ডায়ালগগুলি প্রায়শই রুট বা অন্যান্য অ্যাপ্লিকেশনের প্রয়োজন এবং অবস্থার উপর ভিত্তি করে যুক্ত এবং সরানো হয়। একটি ডায়ালগ সরানো হলে ইভেন্ট বা ডেটা পরিষ্কার করতে এটি কার্যকর হতে পারে।

আপনি অন্য মিউটেশন পর্যবেক্ষকের সাথে এটি অর্জন করতে পারেন। এবার, ডায়ালগ এলিমেন্টে অ্যাট্রিবিউটগুলি পর্যবেক্ষণ করার পরিবর্তে, আমরা বডি এলিমেন্টের বাচ্চাদের পর্যবেক্ষণ করব এবং ডায়ালগ এলিমেন্টগুলি সরানো হচ্ছে কিনা তা দেখব।


const dialogRemovedEvent = new Event('removed')

export default async function (dialog) {
  
  dialogDeleteObserver.observe(document.body, {
    attributes: false,
    subtree: false,
    childList: true,
  })
}

const dialogDeleteObserver = new MutationObserver((mutations, observer) => {
  mutations.forEach(mutation => {
    mutation.removedNodes.forEach(removedNode => {
      if (removedNode.nodeName === 'DIALOG') {
        removedNode.removeEventListener('click', lightDismiss)
        removedNode.removeEventListener('close', dialogClose)
        removedNode.dispatchEvent(dialogRemovedEvent)
      }
    })
  })
})

মিউটেশন পর্যবেক্ষক কলব্যাক বলা হয় যখনই নথির মূল অংশ থেকে শিশুদের যোগ করা বা সরানো হয়। নির্দিষ্ট মিউটেশনগুলি দেখা হচ্ছে removedNodes জন্য যেগুলির একটি ডায়ালগের nodeName রয়েছে৷ একটি ডায়ালগ মুছে ফেলা হলে, মেমরি খালি করার জন্য ক্লিক এবং বন্ধ ইভেন্টগুলি সরানো হয় এবং কাস্টম সরানো ইভেন্টটি প্রেরণ করা হয়।

লোডিং অ্যাট্রিবিউট অপসারণ করা হচ্ছে

পৃষ্ঠায় বা পৃষ্ঠা লোড করার সময় ডায়ালগ অ্যানিমেশনটিকে তার প্রস্থান অ্যানিমেশন চালানো থেকে বিরত রাখতে, ডায়ালগে একটি লোডিং বৈশিষ্ট্য যুক্ত করা হয়েছে। নিম্নলিখিত স্ক্রিপ্টটি ডায়ালগ অ্যানিমেশনগুলি চলা শেষ হওয়ার জন্য অপেক্ষা করে, তারপর বৈশিষ্ট্যটি সরিয়ে দেয়। এখন ডায়ালগটি ভিতরে এবং বাইরে অ্যানিমেট করার জন্য বিনামূল্যে, এবং আমরা কার্যকরভাবে একটি অন্যথায় বিভ্রান্তিকর অ্যানিমেশন লুকিয়ে রেখেছি।

export default async function (dialog) {
  
  await animationsComplete(dialog)
  dialog.removeAttribute('loading')
}

এখানে পৃষ্ঠা লোডের কীফ্রেম অ্যানিমেশন প্রতিরোধের সমস্যা সম্পর্কে আরও জানুন।

সব একসাথে

এখানে dialog.js সম্পূর্ণরূপে রয়েছে, এখন আমরা প্রতিটি বিভাগকে পৃথকভাবে ব্যাখ্যা করেছি:

// custom events to be added to <dialog>
const dialogClosingEvent = new Event('closing')
const dialogClosedEvent  = new Event('closed')
const dialogOpeningEvent = new Event('opening')
const dialogOpenedEvent  = new Event('opened')
const dialogRemovedEvent = new Event('removed')

// track opening
const dialogAttrObserver = new MutationObserver((mutations, observer) => {
  mutations.forEach(async mutation => {
    if (mutation.attributeName === 'open') {
      const dialog = mutation.target

      const isOpen = dialog.hasAttribute('open')
      if (!isOpen) return

      dialog.removeAttribute('inert')

      // set focus
      const focusTarget = dialog.querySelector('[autofocus]')
      focusTarget
        ? focusTarget.focus()
        : dialog.querySelector('button').focus()

      dialog.dispatchEvent(dialogOpeningEvent)
      await animationsComplete(dialog)
      dialog.dispatchEvent(dialogOpenedEvent)
    }
  })
})

// track deletion
const dialogDeleteObserver = new MutationObserver((mutations, observer) => {
  mutations.forEach(mutation => {
    mutation.removedNodes.forEach(removedNode => {
      if (removedNode.nodeName === 'DIALOG') {
        removedNode.removeEventListener('click', lightDismiss)
        removedNode.removeEventListener('close', dialogClose)
        removedNode.dispatchEvent(dialogRemovedEvent)
      }
    })
  })
})

// wait for all dialog animations to complete their promises
const animationsComplete = element =>
  Promise.allSettled(
    element.getAnimations().map(animation => 
      animation.finished))

// click outside the dialog handler
const lightDismiss = ({target:dialog}) => {
  if (dialog.nodeName === 'DIALOG')
    dialog.close('dismiss')
}

const dialogClose = async ({target:dialog}) => {
  dialog.setAttribute('inert', '')
  dialog.dispatchEvent(dialogClosingEvent)

  await animationsComplete(dialog)

  dialog.dispatchEvent(dialogClosedEvent)
}

// page load dialogs setup
export default async function (dialog) {
  dialog.addEventListener('click', lightDismiss)
  dialog.addEventListener('close', dialogClose)

  dialogAttrObserver.observe(dialog, { 
    attributes: true,
  })

  dialogDeleteObserver.observe(document.body, {
    attributes: false,
    subtree: false,
    childList: true,
  })

  // remove loading attribute
  // prevent page load @keyframes playing
  await animationsComplete(dialog)
  dialog.removeAttribute('loading')
}

dialog.js মডিউল ব্যবহার করে

মডিউল থেকে রপ্তানিকৃত ফাংশনটি এই নতুন ইভেন্ট এবং কার্যকারিতা যোগ করতে চায় এমন একটি ডায়ালগ উপাদানকে কল করা এবং পাস করার প্রত্যাশা করে:

import GuiDialog from './dialog.js'

const MegaDialog = document.querySelector('#MegaDialog')
const MiniDialog = document.querySelector('#MiniDialog')

GuiDialog(MegaDialog)
GuiDialog(MiniDialog)

ঠিক তেমনি, দুটি ডায়ালগ হালকা বরখাস্ত, অ্যানিমেশন লোডিং ফিক্স এবং আরও ইভেন্টের সাথে কাজ করার জন্য আপগ্রেড করা হয়েছে।

নতুন কাস্টম ঘটনা শোনা

প্রতিটি আপগ্রেড করা ডায়ালগ উপাদান এখন পাঁচটি নতুন ইভেন্ট শুনতে পারে, যেমন:

MegaDialog.addEventListener('closing', dialogClosing)
MegaDialog.addEventListener('closed', dialogClosed)

MegaDialog.addEventListener('opening', dialogOpening)
MegaDialog.addEventListener('opened', dialogOpened)

MegaDialog.addEventListener('removed', dialogRemoved)

এই ঘটনাগুলি পরিচালনা করার দুটি উদাহরণ এখানে রয়েছে:

const dialogOpening = ({target:dialog}) => {
  console.log('Dialog opening', dialog)
}

const dialogClosed = ({target:dialog}) => {
  console.log('Dialog closed', dialog)
  console.info('Dialog user action:', dialog.returnValue)

  if (dialog.returnValue === 'confirm') {
    // do stuff with the form values
    const dialogFormData = new FormData(dialog.querySelector('form'))
    console.info('Dialog form data', Object.fromEntries(dialogFormData.entries()))

    // then reset the form
    dialog.querySelector('form')?.reset()
  }
}

আমি ডায়ালগ উপাদান দিয়ে যে ডেমো তৈরি করেছি, আমি সেই বন্ধ ইভেন্ট এবং ফর্ম ডেটা তালিকায় একটি নতুন অবতার উপাদান যোগ করতে ব্যবহার করি। টাইমিং ভাল যে ডায়ালগটি তার প্রস্থান অ্যানিমেশন সম্পূর্ণ করেছে, এবং তারপর কিছু স্ক্রিপ্ট নতুন অবতারে অ্যানিমেট হয়েছে। নতুন ইভেন্টের জন্য ধন্যবাদ, ব্যবহারকারীর অভিজ্ঞতা আরও মসৃণ হতে পারে।

dialog.returnValue লক্ষ্য করুন : এতে পাস করা ক্লোজ স্ট্রিং থাকে যখন ডায়ালগ close() ইভেন্ট কল করা হয়। dialogClosed ইভেন্টে ডায়ালগটি বন্ধ, বাতিল বা নিশ্চিত করা হয়েছে কিনা তা জানার জন্য এটি গুরুত্বপূর্ণ। এটি নিশ্চিত হলে, স্ক্রিপ্টটি ফর্মের মানগুলি ধরে নেয় এবং ফর্মটি পুনরায় সেট করে৷ রিসেটটি কার্যকর যাতে ডায়ালগটি আবার দেখানো হয়, এটি ফাঁকা থাকে এবং একটি নতুন জমা দেওয়ার জন্য প্রস্তুত থাকে।

উপসংহার

এখন যেহেতু আপনি জানেন যে আমি কীভাবে এটি করেছি, আপনি কেমন হবে‽ 🙂৷

আসুন আমাদের পদ্ধতির বৈচিত্র্য আনুন এবং ওয়েবে তৈরি করার সমস্ত উপায় শিখি।

একটি ডেমো তৈরি করুন, আমাকে লিঙ্কগুলি টুইট করুন এবং আমি নীচের সম্প্রদায়ের রিমিক্স বিভাগে এটি যুক্ত করব!

কমিউনিটি রিমিক্স

সম্পদ