npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ershadsarabi/jalcal

v1.0.3

Published

تقویم شمسی سبکِ Vanilla JS با نمای ماه/روز و Drag & Drop رویدادها

Readme

# @ershadsarabi/jalcal

تقویم شمسی سبکِ **Vanilla JS** با نمای **ماه** و **روز**، پشتیبانی از **Drag & Drop**، و API کامل برای **CRUD رویدادها**.  
بدون وابستگی به jQuery. استایل‌ها و توابع namespaced هستند؛ CSS زیر `.jalcal` و توابع کمکی با پیشوند `jalcal_` استفاده می‌شوند.

---

## قابلیت‌ها

- تقویم جلالی دقیق (تبدیل گِرِگوری ⇄ جلالی داخلی و بدون وابستگی)
- نمای **ماه** با ۶ هفته ثابت و نمای **روز** با اسلات‌های ساعتی + بخش **تمام‌روزی**
- بارگذاری رویدادها از JSON، لینک رویداد اختیاری، رنگ دلخواه، وضعیت done/undone
- Drag & Drop بین روزها (در ماه) و بین ساعت‌ها/تمام‌روزی (در روز)
- انتخاب ماه/سال با پاپ‌آپ اختصاصی، ناوبری ماه/روز با دکمه‌های prev/next
- API رویدادها: **add/update/remove/eventDone/eventUndone**
- Hookها: **onCellClick**، **onEventClick**، **onEventDrag**، **onEventDrop**
- استایل تمیز با CSS Variables قابل‌سفارشی‌سازی

---

## نصب

```bash
npm i @ershadsarabi/jalcal

استفاده سریع (ESM)

<div id="calendar" style="height:600px;"></div>
<script type="module">
  import jalcal from '@ershadsarabi/jalcal';
  import '@ershadsarabi/jalcal/style.css';

  const events = [
    { id:1, title:'جلسه', start:'1404/08/23 09:00', end:'1404/08/23 11:00', allDay:false, status:false, color:'#1976d2' },
    { id:2, title:'آموزش', start:'1404/08/24 14:00', end:'1404/08/24 15:30', allDay:false, status:true,  color:'#43a047' },
    { id:3, title:'تعطیل', start:'1404/08/25',       end:'1404/08/25',       allDay:true,  status:false, color:'#e53935' }
  ];

  const cal = jalcal('#calendar', {
    events,
    onCellClick: ({ jy, jm, jd, hour, allDay, events, cell }) => {},
    onEventClick: ({ id, event, jy, jm, jd, events }) => {},
    onEventDrag: ({ id, event, from }) => {},
    onEventDrop: ({ id, event, from, to }) => {}
  });

  // ناوبری
  // cal.today(); cal.next(); cal.prev();
  // تغییر نما
  // cal.setView('day'); cal.setView('month');
</script>

استفاده از CDN (UMD)

<link rel="stylesheet" href="https://unpkg.com/@ershadsarabi/jalcal/dist/jalcal.css">
<div id="calendar" style="height:600px"></div>
<script src="https://unpkg.com/@ershadsarabi/jalcal/dist/jalcal.umd.js"></script>
<script>
  // در UMD آبجکت گلوبال Jalcal در دسترس است
  const cal = Jalcal('#calendar', {
    events: []
  });
</script>

ساختار داده رویداد

type JalcalEvent = {
  id: string | number;          // الزامی و یکتا
  title?: string;                // عنوان رویداد
  description?: string;          // توضیحات (در UI پایه استفاده نمی‌شود)
  start: string;                 // "YYYY/MM/DD HH:MM" یا "YYYY/MM/DD"
  end?: string;                  // اختیاری با همان فرمت start
  allDay?: boolean;              // true = تمام‌روزی
  status?: boolean;              // true = done (نمایش خط‌خورده و کم‌رنگ)
  color?: string;                // مثل "#1976d2" برای استایل
  eventLink?: string;            // لینک جزئیات (target=_blank)
  // سایر فیلدهای اختیاری آزاد
};
  • اگر start بدون زمان باشد یا allDay = true، رویداد تمام‌روزی رندر می‌شود.
  • end اختیاری است. در جابه‌جایی روز، تاریخ end هم با اختلاف روزی جابه‌جا می‌شود.
  • ترتیب نمایش: رویدادهای تمام‌روزی در ابتدا، سپس رویدادهای زمانی بر اساس HH:MM.

گزینه‌های سازنده

type Options = {
  events?: JalcalEvent[];   // پیش‌فرض []
  onCellClick?: (args) => any | false;
  onEventClick?: (args) => any | false;
  onEventDrag?: (args) => any | false;
  onEventDrop?: (args) => any | false;
};

امضای Hookها

onCellClick: (args: {
  jy: number; jm: number; jd: number;
  hour?: number | null;    // در نمای روز مقداردهی می‌شود
  allDay?: boolean;        // در نمای روز برای بخش تمام‌روزی true می‌شود
  events: JalcalEvent[];   // رویدادهای همان روز
  cell: HTMLElement;       // عنصر کلیک‌شده
}) => any | false;

onEventClick: (args: {
  id: string|number;
  event: JalcalEvent;
  jy?: number; jm?: number; jd?: number;
  events?: JalcalEvent[];  // رویدادهای آن روز
}) => any | false;

onEventDrag: (args: {
  id: string|number;
  event: JalcalEvent;
  from: { jy:number; jm:number; jd:number; hh?:number|null; mm?:number|null };
}) => any | false;

onEventDrop: (args: {
  id: string|number;
  event: JalcalEvent;
  from: { jy:number; jm:number; jd:number; hh?:number|null; mm?:number|null };
  to:   { jy:number; jm:number; jd:number; hh?:number|null }; // در نمای روز شامل ساعت مقصد
  monthContext: { jy:number; jm:number }; // ماهی که نمای ماه بر اساس آن رندر شده
}) => any | false;
  • برگرداندن false از هر Hook مربوط، عملیات پیش‌فرض را متوقف می‌کند (مثلاً Drop را کنسل می‌کند).

متدهای API

const api = jalcal('#calendar', options);

// رندر
api.renderMonth(jy, jm);
api.renderDay(jy, jm, jd);

// نما
api.setView('month' | 'day');
api.getView(); // 'month' | 'day'

// ناوبری
api.today(); // جهش به امروز (ماه یا روز)
api.next();  // ماه بعد یا روز بعد
api.prev();  // ماه قبل یا روز قبل

// CRUD
api.addEvent(event);
api.updateEvent(partialEventWithId);
api.removeEvent(id);
api.eventDone(id);
api.eventUndone(id);

// داده
api.getEvents(); // آرایه کپی‌شده

مثال: افزودن/ویرایش/حذف

const cal = jalcal('#calendar', { events: [] });

// افزودن
cal.addEvent({
  id: 101,
  title: 'Daily Standup',
  start: '1404/08/27 09:30',
  end:   '1404/08/27 10:00',
  allDay: false,
  color: '#1976d2'
});

// ویرایش
cal.updateEvent({
  id: 101,
  title: 'Daily Scrum',
  color: '#1565c0'
});

// انجام‌شده
cal.eventDone(101);
// بازگردانی
cal.eventUndone(101);

// حذف
cal.removeEvent(101);

مثال: کنترل Drag & Drop سفارشی

const cal = jalcal('#calendar', {
  events: [...],
  onEventDrop: ({ id, event, from, to }) => {
    // اعتبارسنجی
    const isWeekend = (jy,jm,jd) => {
      const w = jalcal_weekdaySat0_J(jy,jm,jd); // 0=شنبه ... 6=جمعه
      return w === 6; // جمعه
    };
    if (isWeekend(to.jy, to.jm, to.jd)) {
      alert('جابجایی به جمعه مجاز نیست');
      return false; // لغو Drop
    }

    // ذخیره سمت سرور (async). اگر می‌خواهی بلوکه کنی، خودت state را برگردان.
    // این کتابخانه drop را اعمال می‌کند و UI را رندر می‌کند.
  }
});

مثال: سوییچ برنامه‌ریزی‌شده بین نمای روز/ماه

// دکمه‌های سفارشی خودت
document.querySelector('#btn-month').onclick = () => {
  cal.setView('month');
};

document.querySelector('#btn-day').onclick = () => {
  // پیش‌فرض: "روز جاری" باید نمایش داده شود
  const now = new Date();
  const j = jalcal_toJalaali(now.getUTCFullYear(), now.getUTCMonth()+1, now.getUTCDate());
  cal.renderDay(j.jy, j.jm, j.jd);
};

رفتار پیش‌فرض دکمه‌های نمای روز

  • در سربرگ تقویم، روی دکمه «روز» کلیک شود، به‌صورت پیش‌فرض روز جاری نمایش داده می‌شود.
  • عنوان نمای روز: «نام روز، YYYY/MM/DD» به فارسی. مثال: سه‌شنبه، ۱۴۰۴/۰۸/۲۳.

پاپ‌آپ انتخاب ماه/سال

  • کلیک روی عنوان ماه/سال در سربرگ → پاپ‌آپ انتخاب ماه همان سال.
  • دکمه‌های «‹ ›» برای پیمایش سال. دکمه وسط سال با استایل قابل خواندن.
  • دکمه سال → پاپ‌آپ انتخاب سال (شبکه ۵×۵، سال مرکز در وسط).
  • انتخاب سال → پاپ‌آپ سال بسته می‌شود، پاپ‌آپ ماه همان سال تنظیم و بازمی‌ماند تا ماه انتخاب شود.

سفارشی‌سازی ظاهر با CSS Variables

.jalcal{
  --border:#e5e7eb;
  --muted:#9aa0a6;
  --bg:#f8f9fa;
  --bg2:#f3f4f6;
  --primary:#1a73e8;  /* رنگ اصلی دکمه‌ها و اِلِمان‌ها */
  --text:#111827;
  --today-bg:#FFF6DF; /* پس‌زمینه روز جاری */
}
  • برای تمام‌صفحه: والد را 100% ارتفاع بده. مثال:

    html,body,#app,#calendar { height:100%; }

یکپارچه‌سازی با فریم‌ورک‌ها

React (ESM)

import { useEffect, useRef } from 'react';
import jalcal from '@ershadsarabi/jalcal';
import '@ershadsarabi/jalcal/style.css';

export default function Calendar() {
  const ref = useRef(null);
  useEffect(() => {
    if (!ref.current) return;
    const api = jalcal(ref.current, { events: [] });
    return () => { /* در صورت نیاز: پاکسازی خاصی لازم ندارد */ };
  }, []);
  return <div ref={ref} style={{height:'600px'}} />;
}

Next.js

  • Import سمت کلاینت. SSR برای DOM لازم نیست. از dynamic import با ssr:false استفاده کن.
// app/components/Calendar.tsx
'use client';
import { useEffect, useRef } from 'react';
import jalcal from '@ershadsarabi/jalcal';
import '@ershadsarabi/jalcal/style.css';

export default function Calendar() {
  const el = useRef<HTMLDivElement>(null);
  useEffect(() => {
    if (!el.current) return;
    jalcal(el.current, { events: [] });
  }, []);
  return <div ref={el} style={{height:'600px'}} />;
}

نکات فنی

  • فرمت تاریخ ورودی: "YYYY/MM/DD" یا "YYYY/MM/DD HH:MM". ارقام فارسی نیز پشتیبانی می‌شود.
  • Drag & Drop از HTML5 API استفاده می‌کند. اگر Hookها داده را رد نکنند، تقویم خودش state داخلی را به‌روز و رندر می‌کند.
  • تمام توابع کمکی و الگوریتم جلالی داخل پکیج پیاده‌سازی شده‌اند. وابستگی خارجی وجود ندارد.
  • RTL به‌صورت پیش‌فرض. LTR نیز با تغییر direction قابل استفاده است.
  • مرورگرهای مدرن پشتیبانی می‌شوند. IE پشتیبانی نمی‌شود.

توسعه و بیلد

  • سورس: src/jalcal.js و src/jalcal.css

  • خروجی بیلد:

    • ESM: dist/index.mjs
    • CJS: dist/index.cjs
    • UMD: dist/jalcal.umd.js
    • CSS: dist/jalcal.css

اسکریپت‌ها:

npm run build

انتشار:

npm login
npm version patch   # یا minor/major
npm publish --access public

License

MIT


نویسنده

Ershad Sarabi


سایت

ershad.id.ir