Skip to content

@doranjs/wc

Web Componentهای مستقل از framework (custom elementها) — دوران را در HTML ساده، یا با Vue، Svelte، Angular یا هر frameworkی به‌کار ببرید.

نصب (bundler)

ts
import '@doranjs/wc'; // elementها را به‌صورت خودکار register می‌کند
import '@doranjs/wc/styles.css'; // tokenها + استایل کامپوننت‌ها در یک فایل

از CDN (بدون build step)

html
<link rel="stylesheet" href="https://unpkg.com/@doranjs/wc/dist/styles.css" />
<script src="https://unpkg.com/@doranjs/wc/dist/doran.global.js"></script>

Elementها

Tagتوضیح
<doran-calendar>تقویم کامل ماه با انتخابگر ماه/سال/ساعت
<doran-datepicker>ورودی همراه با تقویم pop-over
<doran-rangepicker>انتخاب بازهٔ تاریخ با دو کلیک
<doran-nlp-input>ورودیِ زبان طبیعی با autocomplete + راهنما
html
<doran-calendar show-holidays value="1405/03/12" header-mode="dropdown"></doran-calendar>
<doran-datepicker with-time placeholder="تاریخ و ساعت"></doran-datepicker>
<doran-rangepicker show-holidays></doran-rangepicker>
<doran-nlp-input value="جمعه ساعت ۷ شب"></doran-nlp-input>

Attributeها

AttributeElementهاتوضیح
valuecalendar، datepicker، nlp-inputYYYY/MM/DD (یا متن خام برای nlp-input)
min / maxcalendar، datepickerکران‌های قابل انتخاب
localeهمهfa (پیش‌فرض) یا en
header-modecalendar، rangepickerdropdown (پیش‌فرض) یا separate
with-timecalendar، datepickerفعال‌سازی انتخابگر ساعت
show-holidayscalendar، datepicker، rangepickerنشانه‌گذاری تعطیلات رسمی
weekendscalendar، rangepickerاندیس روزهای هفته با کاما (6 = جمعه)
placeholderdatepicker، nlp-inputمتن placeholder
formatdatepicker، rangedatepicker، nlp-inputالگوی format برای نمایش؛ ارقامِ تایپ‌شده همین‌طور که وارد می‌شوند در این قالب mask می‌شوند و متن با همین الگو پارس می‌شود
footer-actionscalendar، datepicker، rangepickerاکشن‌های مرتب با کاما/فاصله؛ مقدار خالی فوتر را پنهان می‌کند
hide-footercalendar، datepicker، rangepickerمنسوخ؛ به‌جای آن footer-actions="" را استفاده کنید
icon-positiondatepickerleft (پیش‌فرض) یا right
text-aligndatepickerright (پیش‌فرض) یا left
input-widthdatepickerعرض CSS برای trigger، مثل 18rem
dropdown-widthdatepickerauto، trigger یا عرض CSS سفارشی
disableddatepickertrigger را غیرفعال می‌کند و popover باز را می‌بندد
editabledatepickerبا editable="false" تریگر به‌جای فیلد متنی یک دکمه می‌شود

footer-actions="today,clear" ترتیب دکمه‌ها را دقیقاً حفظ می‌کند. «امروز» تاریخ امروز را انتخاب و رویداد change را منتشر می‌کند؛ «پاک کردن» مقدار را خالی می‌کند و detail.date/detail.iso را null می‌فرستد. RangePicker فقط اکشن clear را می‌پذیرد و آن را به‌صورت پیش‌فرض در فوتر نشان می‌دهد. footer-actions="" کل فوتر (از جمله خلاصهٔ بازه) را پنهان می‌کند. متن دکمه‌ها از locale می‌آید: fa «امروز»/«پاک کردن» و en، Today/Clear را نشان می‌دهد.

عرض dropdown-width="auto" ذاتی است، trigger عرض popover را با trigger برابر می‌کند و هر مقدار دیگر مثل 24rem به‌عنوان عرض CSS سفارشی استفاده می‌شود. وقتی disabled حاضر باشد، trigger بومیِ datepicker غیرفعال است، با کلیک باز نمی‌شود و اگر popover باز باشد بسته می‌شود.

با editable="false" تریگر به دکمه تبدیل می‌شود: کل فیلد تقویم را باز می‌کند و تاریخ فقط از جدول انتخاب می‌شود. روی صفحه‌های لمسی معمولاً انتخاب بهتری است، چون فیلد متنی کیبورد صفحه‌کلید را روی تقویم بالا می‌آورد و رسیدن به پیکر یعنی زدن روی آیکن. این attribute به‌جای «حاضر بودن»، به‌صورت رشته خوانده می‌شود تا :editable="false" و [editable]="false" از Vue، Svelte و Angular همان معنایی را بدهند که می‌نویسند. برخلاف readonly که یک <input> واقعی نگه می‌دارد و فقط متن تازه را نمی‌پذیرد، اینجا اصلاً فیلد متنی وجود ندارد.

روی اشاره‌گر لمسی، datepicker با باز شدن تقویم مکان‌نما را رها می‌کند تا کیبورد پیش از جای‌گیری پنل بسته شود، و بعد از انتخاب تاریخ فوکوس را پس نمی‌گیرد — وگرنه کیبورد روی تقویم می‌افتد و با بسته‌شدن وسط لمس، پنل را از زیر انگشت جابه‌جا می‌کند. پنل به‌جای window.innerHeight با visual viewport اندازه‌گیری می‌شود و در طول هر حرکتی که روی خودش شروع شود بی‌حرکت می‌ماند.

زیر ۶۴۰ پیکسل تقویم به‌جای چسبیدن به تریگر به‌صورت شیت پایین‌صفحه نمایش داده می‌شود — یعنی mode="auto"، که پیش‌فرضِ <doran-datepicker> و <doran-rangedatepicker> هر دو است. با mode="popover" همه‌جا چسبیده می‌ماند و با mode="sheet" همیشه شیت است. شیت تمام‌عرض است، صفحهٔ پشتش را تیره می‌کند و داخل خودش اسکرول می‌شود؛ درون آن اندازهٔ روزها به هدف لمسی ۴۴ پیکسل می‌رسد، ماه‌های انتخابگر بازه روی هم می‌آیند و میان‌برهایش به نوار افقی تبدیل می‌شوند. با --doran-sheet-bg، --doran-sheet-backdrop، --doran-sheet-radius، --doran-sheet-padding، --doran-sheet-content-width و --doran-day-size-touch تنظیمش کنید.

<doran-rangedatepicker> با فوکوس باز می‌شود، پس روی دستگاه لمسی نمی‌تواند مثل <doran-datepicker> مکان‌نما را رها کند — همان چیزی که بازش کرده است. به‌جایش تا وقتی روی اشاره‌گر لمسی به‌صورت شیت نمایش داده می‌شود فیلدهایش readonly می‌شوند؛ تنها سیگنالی که مرورگرها برای «فوکوس بگیر ولی کیبورد بالا نیاور» رعایت می‌کنند. وگرنه کیبورد روی همان شیتی می‌افتد که تازه باز شده. تایپ در بقیهٔ جاها سر جایش است، از جمله پنجرهٔ باریکِ دسکتاپ. اگر تایپ روی موبایل مهم‌تر است، mode="popover" بدهید.

format علاوه بر نمایش، تایپ را هم هدایت می‌کند. ارقام همین‌طور که وارد می‌شوند داخل الگو می‌ریزند — 14020512 بدون زدن هیچ جداکننده‌ای ۱۴۰۲/۰۵/۱۲ می‌شود — و فیلدها مثل یک ورودی تاریخ نیتیو جلو می‌روند: 95 ماه نیست، پس 9 می‌شود ماه 09 و 5 روز را شروع می‌کند. جداکننده‌ای که خودتان تایپ کنید فیلد را زودتر می‌بندد و 1402-1-2 را ماه ۱ و روز ۲ نگه می‌دارد. متن هم با همان الگو پارس می‌شود، پس format="MM-DD-YYYY" ورودی 05-12-1402 را می‌پذیرد. الگوهایی که از tokenهای متنی ساخته شده‌اند (MMMM، dddd) قابل ماسک‌شدن نیستند و آزاد می‌مانند تا روی blur مرتب شوند.

Eventها

همهٔ elementها یک change CustomEventِ bubbling منتشر می‌کنند:

js
document.querySelector('doran-calendar').addEventListener('change', (e) => {
  console.log(e.detail.date); // DoranDate یا null پس از Clear
  console.log(e.detail.value); // رشتهٔ format‌شده
});

document.querySelector('doran-rangepicker').addEventListener('change', (e) => {
  console.log(e.detail.start, e.detail.end);
});

document.querySelector('doran-nlp-input').addEventListener('resolve', (e) => {
  console.log(e.detail.result); // ParseResult | null
});

ویجت روزها

روزها را نشانه‌گذاری کنید — نرخ بلیت، شمار صندلی، وضعیت ظرفیت. این‌ها به‌جای attribute، پراپرتیِ جاوااسکریپت‌اند، چون نگاشتِ روزها و تابعِ شرط به رشته تبدیل نمی‌شوند.

js
const picker = document.querySelector('doran-datepicker');

picker.dayData = {
  '1404-5-12': { text: '۱٬۲۰۰٬۰۰۰', tone: 'low' },
  '1404-5-14': { disabled: true, disabledReason: 'ظرفیت تکمیل' },
};

picker.disabledDates = (day) => day.dayOfWeek === 6;

روی <doran-calendar>، <doran-datepicker> و <doran-rangepicker> در دسترس است. کلیدها جلالیِ YYYY-M-D هستند؛ شکل‌های صفرداده و با ارقام فارسی به همان روز می‌رسند.

tone به data-tone تبدیل می‌شود: low/positive و high/negative از پیش استایل دارند و هر مقدار دیگری برای CSS خودتان عبور می‌کند.

روزِ بسته به‌جای ویژگی disabled مقدار aria-disabled می‌گیرد، پس قابل فوکوس می‌ماند و disabledReason آن — هم tooltip و هم بخشی از نامِ دسترس‌پذیر روز — واقعاً شنیده می‌شود.

اسلات‌ها

نواحی legend، aside و footer فرزندانِ light-DOM را می‌پذیرند، پس قالب‌های Vue، Svelte و Angular بدون هیچ پشتیبانیِ اضافه‌ای پرشان می‌کنند:

html
<doran-datepicker>
  <div slot="legend">ارزان‌ترین نرخ مشخص شده</div>
  <div slot="footer">قیمت‌ها به تومان است</div>
</doran-datepicker>

<doran-datepicker> هم dayData، هم disabledDates و هم فرزندانِ اسلاتش را به تقویمِ پاپ‌اور می‌فرستد. <doran-rangepicker> نوار کناری‌اش را بین اسلاتِ aside و میان‌برهای آماده تقسیم می‌کند.

Theming

این elementها همان class nameها و CSS variableهای کامپوننت‌های React را به‌کار می‌برند، پس مجموعهٔ کامل tokenها اعمال می‌شود. هر instance را با inline style جداگانه override کنید:

html
<doran-calendar style="--doran-day-selected-bg: #e11d48; --doran-calendar-radius: 22px">
</doran-calendar>

انتخاب بازه با ورودی

html
<doran-rangedatepicker presets months="2"></doran-rangedatepicker>

یک تریگر با دو فیلد که هم تایپ می‌شوند و هم از جدول پر، با حفظ ترتیب دو سر. dayData و disabledDates را به‌صورت پراپرتی می‌گیرد و همان اسلات‌های legend/aside/footer را دارد. <doran-rangepicker> همان نسخهٔ inline است.

انتخاب زمان

hour-step و minute-step تعیین می‌کنند یک فشار فلش هر واحد را چقدر جابه‌جا کند و پیش‌فرضِ هر دو 1 است. هر فیلد را می‌شود تایپ هم کرد، و readonly تایپ را می‌بندد بدون اینکه فلش‌ها را از کار بیندازد.

Released under the MIT License.