@ranjbar.amir/persian-datetime-picker 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,234 @@
1
+ # Persian DateTime Picker (Vue 3)
2
+
3
+ یک Date & Time Picker شمسی (Jalali) مدرن، سبک و حرفه‌ای برای Vue 3
4
+ ساخته‌شده با Day.js + Jalaliday
5
+ طراحی‌شده با UI سطح Figma و پشتیبانی از Dark Mode
6
+
7
+ ---
8
+
9
+ ## ✨ امکانات
10
+
11
+ - تقویم کامل شمسی (Jalali)
12
+ - انتخاب تاریخ یا بازه تاریخ (Range)
13
+ - انتخاب ساعت و دقیقه (Time Picker)
14
+ - انتخاب سریع ماه و سال
15
+ - دکمه‌های میان‌بُر (امروز، فردا، هفته بعد، پاک کردن)
16
+ - پشتیبانی کامل از کیبورد
17
+ - انیمیشن‌های نرم (Fade + Slide)
18
+ - طراحی حرفه‌ای و قابل سفارشی‌سازی
19
+ - سازگار با Vue 3 و Nuxt 3
20
+ - بدون وابستگی‌های سنگین (Moment.js ندارد)
21
+ - پشتیبانی از TailwindCSS
22
+
23
+ ---
24
+
25
+ ## 📦 نصب
26
+
27
+ ```bash
28
+ npm install @ranjbar.amir/persian-datetime-picker
29
+ ```
30
+
31
+ یا
32
+
33
+ ```bash
34
+ yarn add @ranjbar.amir/persian-datetime-picker
35
+ ```
36
+
37
+ ---
38
+
39
+ ## 🚀 استفاده در Vue 3
40
+
41
+ ### 1) ایمپورت کامپوننت و استایل‌ها
42
+
43
+ ```vue
44
+ <script setup>
45
+ import { ref } from "vue"
46
+ import DatePicker from "@ranjbar.amir/persian-datetime-picker"
47
+ import "@ranjbar.amir/persian-datetime-picker/dist/style.css"
48
+
49
+ const date = ref("")
50
+ </script>
51
+
52
+ <template>
53
+ <DatePicker v-model="date" />
54
+ </template>
55
+ ```
56
+
57
+ ---
58
+
59
+ ## 🌗 فعال‌سازی Dark Mode
60
+
61
+ اگر پروژه شما Tailwind دارد:
62
+
63
+ ```html
64
+ <html class="dark">
65
+ ```
66
+
67
+ یا داینامیک:
68
+
69
+ ```js
70
+ document.documentElement.classList.toggle("dark")
71
+ ```
72
+
73
+ ---
74
+
75
+ ## 📅 انتخاب بازه تاریخ (Range Picker)
76
+
77
+ ```vue
78
+ <script setup>
79
+ import { ref } from "vue"
80
+ import DatePicker from "@ranjbar.amir/persian-datetime-picker"
81
+ import "@ranjbar.amir/persian-datetime-picker/dist/style.css"
82
+
83
+ const range = ref({ start: null, end: null })
84
+ </script>
85
+
86
+ <template>
87
+ <DatePicker v-model="range" range />
88
+ </template>
89
+ ```
90
+
91
+ ---
92
+
93
+ ## ⏱️ انتخاب ساعت (Time Picker)
94
+
95
+ ```vue
96
+ <DatePicker v-model="date" enableTime />
97
+ ```
98
+
99
+ خروجی:
100
+
101
+ ```
102
+ 1402/12/10 14:35
103
+ ```
104
+
105
+ ---
106
+
107
+ ## ⚡ دکمه‌های میان‌بُر (Shortcuts)
108
+
109
+ ```vue
110
+ <DatePicker v-model="date" shortcuts />
111
+ ```
112
+
113
+ میان‌بُرهای پیش‌فرض:
114
+
115
+ - امروز
116
+ - فردا
117
+ - هفته بعد
118
+ - پاک کردن
119
+
120
+ ---
121
+
122
+ ## 🗓️ انتخاب ماه و سال
123
+
124
+ ```vue
125
+ <DatePicker
126
+ v-model="date"
127
+ showMonthSelector
128
+ showYearSelector
129
+ />
130
+ ```
131
+
132
+ ---
133
+
134
+ ## 📘 API کامل
135
+
136
+ ### Props
137
+
138
+ | نام | نوع | پیش‌فرض | توضیح |
139
+ |-----|------|----------|--------|
140
+ | `modelValue` | String / Object | `""` | مقدار انتخاب‌شده |
141
+ | `range` | Boolean | `false` | فعال‌سازی انتخاب بازه |
142
+ | `enableTime` | Boolean | `false` | فعال‌سازی انتخاب ساعت |
143
+ | `shortcuts` | Boolean | `false` | نمایش دکمه‌های میان‌بُر |
144
+ | `showMonthSelector` | Boolean | `false` | انتخاب سریع ماه |
145
+ | `showYearSelector` | Boolean | `false` | انتخاب سریع سال |
146
+ | `placeholder` | String | `"انتخاب تاریخ"` | متن ورودی |
147
+ | `disabled` | Boolean | `false` | غیرفعال |
148
+ | `clearable` | Boolean | `false` | نمایش دکمه پاک‌کردن |
149
+
150
+ ---
151
+
152
+ ### Events
153
+
154
+ | رویداد | توضیح |
155
+ |--------|--------|
156
+ | `update:modelValue` | خروجی تاریخ یا بازه |
157
+ | `open` | باز شدن پنل |
158
+ | `close` | بسته شدن پنل |
159
+
160
+ ---
161
+
162
+ ## 🧩 خروجی Range
163
+
164
+ ```json
165
+ {
166
+ "start": "1402/12/01",
167
+ "end": "1402/12/10"
168
+ }
169
+ ```
170
+
171
+ ---
172
+
173
+ ## 🧱 استفاده در Nuxt 3
174
+
175
+ ### 1) ساخت پلاگین
176
+
177
+ `plugins/datetime-picker.client.ts`
178
+
179
+ ```ts
180
+ import DatePicker from "@ranjbar.amir/persian-datetime-picker"
181
+ import "@ranjbar.amir/persian-datetime-picker/dist/style.css"
182
+
183
+ export default defineNuxtPlugin((nuxtApp) => {
184
+ nuxtApp.vueApp.component("DatePicker", DatePicker)
185
+ })
186
+ ```
187
+
188
+ ### 2) استفاده
189
+
190
+ ```vue
191
+ <DatePicker v-model="date" />
192
+ ```
193
+
194
+ ---
195
+
196
+ ## 🎨 سفارشی‌سازی UI
197
+
198
+ تمام استایل‌ها در این فایل‌ها هستند:
199
+
200
+ ```
201
+ styles/base.css
202
+ styles/theme.css
203
+ styles/animations.css
204
+ ```
205
+
206
+ می‌توانید override کنید:
207
+
208
+ ```css
209
+ :root {
210
+ --dp-primary: #10b981; /* سبز */
211
+ }
212
+ ```
213
+
214
+ ---
215
+
216
+ ## 🛠️ توسعه‌دهندگان
217
+
218
+ ### Build
219
+
220
+ ```bash
221
+ npm run build
222
+ ```
223
+
224
+ ### انتشار در npm
225
+
226
+ ```bash
227
+ npm publish --access public
228
+ ```
229
+
230
+ ---
231
+
232
+ ## 📄 لایسنس
233
+
234
+ MIT — استفاده آزاد برای همه.
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue"),f=require("dayjs"),V=require("jalaliday"),Y={__name:"DayCell",props:{day:Object,isSelected:Boolean,isInRange:Boolean,isStart:Boolean,isEnd:Boolean},emits:["select","hover"],setup(t,{emit:i}){const l=i;return(a,o)=>(e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass(["dp-day",{selected:t.isSelected,range:t.isInRange,"range-start":t.isStart,"range-end":t.isEnd}]),onClick:o[0]||(o[0]=c=>l("select",t.day)),onMouseenter:o[1]||(o[1]=c=>l("hover",t.day))},e.toDisplayString(t.day.date()),35))}},x={class:"dp-grid"},E={__name:"Calendar",props:{current:Object,range:Object,hoverDate:Object},emits:["select"],setup(t,{emit:i}){const l=t,a=i,o=computed(()=>{const s=l.current.startOf("month").startOf("week"),r=l.current.endOf("month").endOf("week"),n=[];let p=s;for(;p.isBefore(r);)n.push(p),p=p.add(1,"day");return n});function c(s){return l.range?s.format("YYYY/MM/DD")===l.range.start||s.format("YYYY/MM/DD")===l.range.end:!1}function u(s){var r,n;return!((r=l.range)!=null&&r.start)||!((n=l.range)!=null&&n.end)?!1:s.isAfter(l.range.start)&&s.isBefore(l.range.end)}return(s,r)=>(e.openBlock(),e.createElementBlock("div",x,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(e.unref(o),n=>{var p,k;return e.openBlock(),e.createBlock(Y,{key:n.format("YYYY-MM-DD"),day:n,isSelected:c(n),isInRange:u(n),isStart:n.format("YYYY/MM/DD")===((p=t.range)==null?void 0:p.start),isEnd:n.format("YYYY/MM/DD")===((k=t.range)==null?void 0:k.end),onSelect:y=>a("select",n),onHover:y=>t.hoverDate=n},null,8,["day","isSelected","isInRange","isStart","isEnd","onSelect","onHover"])}),128))]))}},S={class:"grid grid-cols-3 gap-2 p-3"},M=["onClick"],w={__name:"MonthSelector",props:{current:Object},emits:["select"],setup(t,{emit:i}){const l=t,a=i,o=["فروردین","اردیبهشت","خرداد","تیر","مرداد","شهریور","مهر","آبان","آذر","دی","بهمن","اسفند"];function c(u){a("select",l.current.month(u))}return(u,s)=>(e.openBlock(),e.createElementBlock("div",S,[(e.openBlock(),e.createElementBlock(e.Fragment,null,e.renderList(o,(r,n)=>e.createElementVNode("button",{key:n,class:"dp-btn",onClick:p=>c(n)},e.toDisplayString(r),9,M)),64))]))}},$={class:"p-3"},C={class:"flex justify-between mb-3"},D={class:"grid grid-cols-3 gap-2"},N=["onClick"],_={__name:"YearSelector",props:{current:Object},emits:["select"],setup(t,{emit:i}){const l=t,a=i,o=e.ref(l.current.year());function c(s){o.value+=s}function u(s){a("select",l.current.year(s))}return(s,r)=>(e.openBlock(),e.createElementBlock("div",$,[e.createElementVNode("div",C,[e.createElementVNode("button",{class:"dp-btn",onClick:r[0]||(r[0]=n=>c(-10))},"«"),e.createElementVNode("span",null,e.toDisplayString(o.value),1),e.createElementVNode("button",{class:"dp-btn",onClick:r[1]||(r[1]=n=>c(10))},"»")]),e.createElementVNode("div",D,[(e.openBlock(),e.createElementBlock(e.Fragment,null,e.renderList(12,n=>e.createElementVNode("button",{key:n,class:"dp-btn",onClick:p=>u(o.value+n-6)},e.toDisplayString(o.value+n-6),9,N)),64))])]))}};function O(t){const i=[{label:"امروز",value:f()},{label:"فردا",value:f().add(1,"day")},{label:"هفته بعد",value:f().add(7,"day")},{label:"پاک کردن",value:null}];function l(a){t("update:modelValue",a.value?a.value.format("YYYY/MM/DD"):"")}return{shortcuts:i,applyShortcut:l}}const j={class:"flex gap-2 p-2 border-t dark:border-gray-700"},R=["onClick"],T={__name:"Shortcuts",emits:["apply"],setup(t,{emit:i}){const l=i,{shortcuts:a,applyShortcut:o}=O(c=>l("apply",c));return(c,u)=>(e.openBlock(),e.createElementBlock("div",j,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(e.unref(a),s=>(e.openBlock(),e.createElementBlock("button",{key:s.label,class:"dp-btn bg-gray-100 dark:bg-gray-700",onClick:r=>e.unref(o)(s)},e.toDisplayString(s.label),9,R))),128))]))}},I={class:"flex gap-2 p-2 border-t dark:border-gray-700"},P={__name:"TimePicker",props:{value:String},emits:["update"],setup(t,{emit:i}){const l=t,a=i,o=ref(0),c=ref(0);function u(){const s=f(l.value).hour(o.value).minute(c.value);a("update",s.format("YYYY/MM/DD HH:mm"))}return(s,r)=>(e.openBlock(),e.createElementBlock("div",I,[e.withDirectives(e.createElementVNode("input",{type:"number","onUpdate:modelValue":r[0]||(r[0]=n=>e.isRef(o)?o.value=n:null),min:"0",max:"23",class:"w-14 dp-btn",onInput:u},null,544),[[e.vModelText,e.unref(o)]]),e.withDirectives(e.createElementVNode("input",{type:"number","onUpdate:modelValue":r[1]||(r[1]=n=>e.isRef(c)?c.value=n:null),min:"0",max:"59",class:"w-14 dp-btn",onInput:u},null,544),[[e.vModelText,e.unref(c)]])]))}};function H(t,i){var s,r;const l=e.ref(((s=t.value)==null?void 0:s.start)||null),a=e.ref(((r=t.value)==null?void 0:r.end)||null),o=e.ref(null);function c(n){l.value?l.value&&!a.value?n>=l.value?a.value=n:l.value=n:(l.value=n,a.value=null):l.value=n,i("update:modelValue",{start:l.value,end:a.value})}function u(n){o.value=n}return e.watch(t,n=>{l.value=(n==null?void 0:n.start)||null,a.value=(n==null?void 0:n.end)||null}),{start:l,end:a,hovered:o,select:c,onHover:u}}function L(t,i,l){function a(o){if(t.value)switch(o.key){case"ArrowUp":t.value=t.value.subtract(1,"day");break;case"ArrowDown":t.value=t.value.add(1,"day");break;case"ArrowLeft":t.value=t.value.subtract(7,"day");break;case"ArrowRight":t.value=t.value.add(7,"day");break;case"Enter":i(t.value);break;case"Escape":l();break}}return{onKeydown:a}}const A={class:"relative inline-block text-right"},U={class:"flex items-center gap-1"},F=["placeholder","value","disabled"],q=["disabled"],K={key:0,class:"absolute z-50 mt-2 dp-panel w-80"},z={class:"dp-header"},G={class:"flex items-center gap-2"},J={class:"flex items-center gap-1"},Q={class:"px-2 pb-2"},W=e.defineComponent({__name:"DatePicker",props:{modelValue:{},range:{type:Boolean},enableTime:{type:Boolean},shortcuts:{type:Boolean},showMonthSelector:{type:Boolean},showYearSelector:{type:Boolean},placeholder:{},disabled:{type:Boolean},clearable:{type:Boolean}},emits:["update:modelValue","open","close"],setup(t,{emit:i}){f.extend(V);const l=t,a=i,o=e.ref(!1),c=e.ref("day"),u=e.ref(f().calendar("jalali")),s=e.ref(u.value),r=e.computed(()=>!!l.range),n=r.value?H(e.computed(()=>l.modelValue),(v,d)=>a(v,d)):null,p=e.computed(()=>{if(r.value){const v=l.modelValue,d=(v==null?void 0:v.start)||"",m=(v==null?void 0:v.end)||"";return!d&&!m?"":`${d||"—"} تا ${m||"—"}`}else return l.modelValue||""});function k(){l.disabled||o.value||(o.value=!0,a("open"))}function y(){o.value&&(o.value=!1,a("close"))}function b(v){if(r.value&&n)n.select(v);else{const d=l.enableTime?v.format("YYYY/MM/DD HH:mm"):v.format("YYYY/MM/DD");a("update:modelValue",d),y()}}function h(){l.disabled||a("update:modelValue",r.value?{start:null,end:null}:"")}const{onKeydown:g}=L(s,b,y);return e.onMounted(()=>{window.addEventListener("keydown",g)}),e.onUnmounted(()=>{window.removeEventListener("keydown",g)}),(v,d)=>(e.openBlock(),e.createElementBlock("div",A,[e.createElementVNode("div",U,[e.createElementVNode("input",{class:"border px-3 py-2 rounded w-56 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-60",placeholder:t.placeholder||(e.unref(n)?"انتخاب بازه تاریخ":"انتخاب تاریخ"),value:p.value,disabled:t.disabled,readonly:"",onClick:k},null,8,F),t.clearable&&p.value?(e.openBlock(),e.createElementBlock("button",{key:0,class:"text-xs px-2 py-1 rounded border bg-white hover:bg-gray-100 disabled:opacity-50",disabled:t.disabled,onClick:h}," پاک ",8,q)):e.createCommentVNode("",!0)]),e.createVNode(e.Transition,{name:"fade"},{default:e.withCtx(()=>[o.value?(e.openBlock(),e.createElementBlock("div",K,[e.createElementVNode("div",z,[e.createElementVNode("div",G,[t.showYearSelector?(e.openBlock(),e.createElementBlock("button",{key:0,class:"text-xs text-gray-500 hover:text-gray-800 dark:hover:text-gray-200",onClick:d[0]||(d[0]=m=>c.value="year")},e.toDisplayString(u.value.year()),1)):e.createCommentVNode("",!0),t.showMonthSelector?(e.openBlock(),e.createElementBlock("button",{key:1,class:"text-xs text-gray-500 hover:text-gray-800 dark:hover:text-gray-200",onClick:d[1]||(d[1]=m=>c.value="month")},e.toDisplayString(u.value.format("MMMM")),1)):e.createCommentVNode("",!0)]),e.createElementVNode("div",J,[e.createElementVNode("button",{class:"dp-btn text-xs",onClick:d[2]||(d[2]=m=>u.value=u.value.subtract(1,"month"))}," ‹ "),e.createElementVNode("button",{class:"dp-btn text-xs",onClick:d[3]||(d[3]=m=>u.value=u.value.add(1,"month"))}," › ")])]),e.createElementVNode("div",Q,[e.createVNode(e.Transition,{name:"slide-left",mode:"out-in"},{default:e.withCtx(()=>[c.value==="day"?(e.openBlock(),e.createBlock(E,{key:"day-"+u.value.format("YYYY-MM"),current:u.value,range:r.value?e.unref(n):null,hoverDate:s.value,onSelect:b},null,8,["current","range","hoverDate"])):e.createCommentVNode("",!0)]),_:1}),c.value==="month"?(e.openBlock(),e.createBlock(w,{key:0,current:u.value,onSelect:d[4]||(d[4]=m=>{u.value=m,c.value="day"})},null,8,["current"])):e.createCommentVNode("",!0),c.value==="year"?(e.openBlock(),e.createBlock(_,{key:1,current:u.value,onSelect:d[5]||(d[5]=m=>{u.value=m,c.value="day"})},null,8,["current"])):e.createCommentVNode("",!0)]),t.enableTime&&!r.value?(e.openBlock(),e.createBlock(P,{key:0,value:t.modelValue,onUpdate:d[6]||(d[6]=m=>a("update:modelValue",m))},null,8,["value"])):e.createCommentVNode("",!0),t.shortcuts&&!r.value?(e.openBlock(),e.createBlock(T,{key:1,onApply:d[7]||(d[7]=m=>a("update:modelValue",m))})):e.createCommentVNode("",!0)])):e.createCommentVNode("",!0)]),_:1})]))}}),X=(t,i)=>{const l=t.__vccOpts||t;for(const[a,o]of i)l[a]=o;return l},B=X(W,[["__scopeId","data-v-3a5e3cc6"]]);exports.DatePicker=B;exports.default=B;
@@ -0,0 +1,378 @@
1
+ import { openBlock as d, createElementBlock as m, normalizeClass as H, toDisplayString as k, Fragment as M, renderList as S, unref as h, createBlock as Y, createElementVNode as v, ref as g, withDirectives as V, isRef as B, vModelText as O, watch as A, defineComponent as P, computed as _, onMounted as T, onUnmounted as U, createCommentVNode as b, createVNode as j, Transition as E, withCtx as R } from "vue";
2
+ import x from "dayjs";
3
+ import L from "jalaliday";
4
+ const K = {
5
+ __name: "DayCell",
6
+ props: {
7
+ day: Object,
8
+ isSelected: Boolean,
9
+ isInRange: Boolean,
10
+ isStart: Boolean,
11
+ isEnd: Boolean
12
+ },
13
+ emits: ["select", "hover"],
14
+ setup(e, { emit: i }) {
15
+ const n = i;
16
+ return (l, a) => (d(), m("div", {
17
+ class: H(["dp-day", {
18
+ selected: e.isSelected,
19
+ range: e.isInRange,
20
+ "range-start": e.isStart,
21
+ "range-end": e.isEnd
22
+ }]),
23
+ onClick: a[0] || (a[0] = (s) => n("select", e.day)),
24
+ onMouseenter: a[1] || (a[1] = (s) => n("hover", e.day))
25
+ }, k(e.day.date()), 35));
26
+ }
27
+ }, N = { class: "dp-grid" }, z = {
28
+ __name: "Calendar",
29
+ props: {
30
+ current: Object,
31
+ range: Object,
32
+ hoverDate: Object
33
+ },
34
+ emits: ["select"],
35
+ setup(e, { emit: i }) {
36
+ const n = e, l = i, a = computed(() => {
37
+ const r = n.current.startOf("month").startOf("week"), o = n.current.endOf("month").endOf("week"), t = [];
38
+ let y = r;
39
+ for (; y.isBefore(o); )
40
+ t.push(y), y = y.add(1, "day");
41
+ return t;
42
+ });
43
+ function s(r) {
44
+ return n.range ? r.format("YYYY/MM/DD") === n.range.start || r.format("YYYY/MM/DD") === n.range.end : !1;
45
+ }
46
+ function u(r) {
47
+ var o, t;
48
+ return !((o = n.range) != null && o.start) || !((t = n.range) != null && t.end) ? !1 : r.isAfter(n.range.start) && r.isBefore(n.range.end);
49
+ }
50
+ return (r, o) => (d(), m("div", N, [
51
+ (d(!0), m(M, null, S(h(a), (t) => {
52
+ var y, $;
53
+ return d(), Y(K, {
54
+ key: t.format("YYYY-MM-DD"),
55
+ day: t,
56
+ isSelected: s(t),
57
+ isInRange: u(t),
58
+ isStart: t.format("YYYY/MM/DD") === ((y = e.range) == null ? void 0 : y.start),
59
+ isEnd: t.format("YYYY/MM/DD") === (($ = e.range) == null ? void 0 : $.end),
60
+ onSelect: (w) => l("select", t),
61
+ onHover: (w) => e.hoverDate = t
62
+ }, null, 8, ["day", "isSelected", "isInRange", "isStart", "isEnd", "onSelect", "onHover"]);
63
+ }), 128))
64
+ ]));
65
+ }
66
+ }, F = { class: "grid grid-cols-3 gap-2 p-3" }, q = ["onClick"], G = {
67
+ __name: "MonthSelector",
68
+ props: { current: Object },
69
+ emits: ["select"],
70
+ setup(e, { emit: i }) {
71
+ const n = e, l = i, a = [
72
+ "فروردین",
73
+ "اردیبهشت",
74
+ "خرداد",
75
+ "تیر",
76
+ "مرداد",
77
+ "شهریور",
78
+ "مهر",
79
+ "آبان",
80
+ "آذر",
81
+ "دی",
82
+ "بهمن",
83
+ "اسفند"
84
+ ];
85
+ function s(u) {
86
+ l("select", n.current.month(u));
87
+ }
88
+ return (u, r) => (d(), m("div", F, [
89
+ (d(), m(M, null, S(a, (o, t) => v("button", {
90
+ key: t,
91
+ class: "dp-btn",
92
+ onClick: (y) => s(t)
93
+ }, k(o), 9, q)), 64))
94
+ ]));
95
+ }
96
+ }, J = { class: "p-3" }, Q = { class: "flex justify-between mb-3" }, W = { class: "grid grid-cols-3 gap-2" }, X = ["onClick"], Z = {
97
+ __name: "YearSelector",
98
+ props: { current: Object },
99
+ emits: ["select"],
100
+ setup(e, { emit: i }) {
101
+ const n = e, l = i, a = g(n.current.year());
102
+ function s(r) {
103
+ a.value += r;
104
+ }
105
+ function u(r) {
106
+ l("select", n.current.year(r));
107
+ }
108
+ return (r, o) => (d(), m("div", J, [
109
+ v("div", Q, [
110
+ v("button", {
111
+ class: "dp-btn",
112
+ onClick: o[0] || (o[0] = (t) => s(-10))
113
+ }, "«"),
114
+ v("span", null, k(a.value), 1),
115
+ v("button", {
116
+ class: "dp-btn",
117
+ onClick: o[1] || (o[1] = (t) => s(10))
118
+ }, "»")
119
+ ]),
120
+ v("div", W, [
121
+ (d(), m(M, null, S(12, (t) => v("button", {
122
+ key: t,
123
+ class: "dp-btn",
124
+ onClick: (y) => u(a.value + t - 6)
125
+ }, k(a.value + t - 6), 9, X)), 64))
126
+ ])
127
+ ]));
128
+ }
129
+ };
130
+ function ee(e) {
131
+ const i = [
132
+ { label: "امروز", value: x() },
133
+ { label: "فردا", value: x().add(1, "day") },
134
+ { label: "هفته بعد", value: x().add(7, "day") },
135
+ { label: "پاک کردن", value: null }
136
+ ];
137
+ function n(l) {
138
+ e("update:modelValue", l.value ? l.value.format("YYYY/MM/DD") : "");
139
+ }
140
+ return { shortcuts: i, applyShortcut: n };
141
+ }
142
+ const te = { class: "flex gap-2 p-2 border-t dark:border-gray-700" }, ne = ["onClick"], ae = {
143
+ __name: "Shortcuts",
144
+ emits: ["apply"],
145
+ setup(e, { emit: i }) {
146
+ const n = i, { shortcuts: l, applyShortcut: a } = ee((s) => n("apply", s));
147
+ return (s, u) => (d(), m("div", te, [
148
+ (d(!0), m(M, null, S(h(l), (r) => (d(), m("button", {
149
+ key: r.label,
150
+ class: "dp-btn bg-gray-100 dark:bg-gray-700",
151
+ onClick: (o) => h(a)(r)
152
+ }, k(r.label), 9, ne))), 128))
153
+ ]));
154
+ }
155
+ }, le = { class: "flex gap-2 p-2 border-t dark:border-gray-700" }, oe = {
156
+ __name: "TimePicker",
157
+ props: { value: String },
158
+ emits: ["update"],
159
+ setup(e, { emit: i }) {
160
+ const n = e, l = i, a = ref(0), s = ref(0);
161
+ function u() {
162
+ const r = x(n.value).hour(a.value).minute(s.value);
163
+ l("update", r.format("YYYY/MM/DD HH:mm"));
164
+ }
165
+ return (r, o) => (d(), m("div", le, [
166
+ V(v("input", {
167
+ type: "number",
168
+ "onUpdate:modelValue": o[0] || (o[0] = (t) => B(a) ? a.value = t : null),
169
+ min: "0",
170
+ max: "23",
171
+ class: "w-14 dp-btn",
172
+ onInput: u
173
+ }, null, 544), [
174
+ [O, h(a)]
175
+ ]),
176
+ V(v("input", {
177
+ type: "number",
178
+ "onUpdate:modelValue": o[1] || (o[1] = (t) => B(s) ? s.value = t : null),
179
+ min: "0",
180
+ max: "59",
181
+ class: "w-14 dp-btn",
182
+ onInput: u
183
+ }, null, 544), [
184
+ [O, h(s)]
185
+ ])
186
+ ]));
187
+ }
188
+ };
189
+ function re(e, i) {
190
+ var r, o;
191
+ const n = g(((r = e.value) == null ? void 0 : r.start) || null), l = g(((o = e.value) == null ? void 0 : o.end) || null), a = g(null);
192
+ function s(t) {
193
+ n.value ? n.value && !l.value ? t >= n.value ? l.value = t : n.value = t : (n.value = t, l.value = null) : n.value = t, i("update:modelValue", { start: n.value, end: l.value });
194
+ }
195
+ function u(t) {
196
+ a.value = t;
197
+ }
198
+ return A(e, (t) => {
199
+ n.value = (t == null ? void 0 : t.start) || null, l.value = (t == null ? void 0 : t.end) || null;
200
+ }), { start: n, end: l, hovered: a, select: s, onHover: u };
201
+ }
202
+ function se(e, i, n) {
203
+ function l(a) {
204
+ if (e.value)
205
+ switch (a.key) {
206
+ case "ArrowUp":
207
+ e.value = e.value.subtract(1, "day");
208
+ break;
209
+ case "ArrowDown":
210
+ e.value = e.value.add(1, "day");
211
+ break;
212
+ case "ArrowLeft":
213
+ e.value = e.value.subtract(7, "day");
214
+ break;
215
+ case "ArrowRight":
216
+ e.value = e.value.add(7, "day");
217
+ break;
218
+ case "Enter":
219
+ i(e.value);
220
+ break;
221
+ case "Escape":
222
+ n();
223
+ break;
224
+ }
225
+ }
226
+ return { onKeydown: l };
227
+ }
228
+ const ue = { class: "relative inline-block text-right" }, ce = { class: "flex items-center gap-1" }, de = ["placeholder", "value", "disabled"], ie = ["disabled"], ve = {
229
+ key: 0,
230
+ class: "absolute z-50 mt-2 dp-panel w-80"
231
+ }, me = { class: "dp-header" }, pe = { class: "flex items-center gap-2" }, fe = { class: "flex items-center gap-1" }, ye = { class: "px-2 pb-2" }, be = /* @__PURE__ */ P({
232
+ __name: "DatePicker",
233
+ props: {
234
+ modelValue: {},
235
+ range: { type: Boolean },
236
+ enableTime: { type: Boolean },
237
+ shortcuts: { type: Boolean },
238
+ showMonthSelector: { type: Boolean },
239
+ showYearSelector: { type: Boolean },
240
+ placeholder: {},
241
+ disabled: { type: Boolean },
242
+ clearable: { type: Boolean }
243
+ },
244
+ emits: ["update:modelValue", "open", "close"],
245
+ setup(e, { emit: i }) {
246
+ x.extend(L);
247
+ const n = e, l = i, a = g(!1), s = g("day"), u = g(x().calendar("jalali")), r = g(u.value), o = _(() => !!n.range), t = o.value ? re(
248
+ _(() => n.modelValue),
249
+ (f, c) => l(f, c)
250
+ ) : null, y = _(() => {
251
+ if (o.value) {
252
+ const f = n.modelValue, c = (f == null ? void 0 : f.start) || "", p = (f == null ? void 0 : f.end) || "";
253
+ return !c && !p ? "" : `${c || "—"} تا ${p || "—"}`;
254
+ } else
255
+ return n.modelValue || "";
256
+ });
257
+ function $() {
258
+ n.disabled || a.value || (a.value = !0, l("open"));
259
+ }
260
+ function w() {
261
+ a.value && (a.value = !1, l("close"));
262
+ }
263
+ function C(f) {
264
+ if (o.value && t)
265
+ t.select(f);
266
+ else {
267
+ const c = n.enableTime ? f.format("YYYY/MM/DD HH:mm") : f.format("YYYY/MM/DD");
268
+ l("update:modelValue", c), w();
269
+ }
270
+ }
271
+ function I() {
272
+ n.disabled || l("update:modelValue", o.value ? { start: null, end: null } : "");
273
+ }
274
+ const { onKeydown: D } = se(r, C, w);
275
+ return T(() => {
276
+ window.addEventListener("keydown", D);
277
+ }), U(() => {
278
+ window.removeEventListener("keydown", D);
279
+ }), (f, c) => (d(), m("div", ue, [
280
+ v("div", ce, [
281
+ v("input", {
282
+ class: "border px-3 py-2 rounded w-56 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-60",
283
+ placeholder: e.placeholder || (h(t) ? "انتخاب بازه تاریخ" : "انتخاب تاریخ"),
284
+ value: y.value,
285
+ disabled: e.disabled,
286
+ readonly: "",
287
+ onClick: $
288
+ }, null, 8, de),
289
+ e.clearable && y.value ? (d(), m("button", {
290
+ key: 0,
291
+ class: "text-xs px-2 py-1 rounded border bg-white hover:bg-gray-100 disabled:opacity-50",
292
+ disabled: e.disabled,
293
+ onClick: I
294
+ }, " پاک ", 8, ie)) : b("", !0)
295
+ ]),
296
+ j(E, { name: "fade" }, {
297
+ default: R(() => [
298
+ a.value ? (d(), m("div", ve, [
299
+ v("div", me, [
300
+ v("div", pe, [
301
+ e.showYearSelector ? (d(), m("button", {
302
+ key: 0,
303
+ class: "text-xs text-gray-500 hover:text-gray-800 dark:hover:text-gray-200",
304
+ onClick: c[0] || (c[0] = (p) => s.value = "year")
305
+ }, k(u.value.year()), 1)) : b("", !0),
306
+ e.showMonthSelector ? (d(), m("button", {
307
+ key: 1,
308
+ class: "text-xs text-gray-500 hover:text-gray-800 dark:hover:text-gray-200",
309
+ onClick: c[1] || (c[1] = (p) => s.value = "month")
310
+ }, k(u.value.format("MMMM")), 1)) : b("", !0)
311
+ ]),
312
+ v("div", fe, [
313
+ v("button", {
314
+ class: "dp-btn text-xs",
315
+ onClick: c[2] || (c[2] = (p) => u.value = u.value.subtract(1, "month"))
316
+ }, " ‹ "),
317
+ v("button", {
318
+ class: "dp-btn text-xs",
319
+ onClick: c[3] || (c[3] = (p) => u.value = u.value.add(1, "month"))
320
+ }, " › ")
321
+ ])
322
+ ]),
323
+ v("div", ye, [
324
+ j(E, {
325
+ name: "slide-left",
326
+ mode: "out-in"
327
+ }, {
328
+ default: R(() => [
329
+ s.value === "day" ? (d(), Y(z, {
330
+ key: "day-" + u.value.format("YYYY-MM"),
331
+ current: u.value,
332
+ range: o.value ? h(t) : null,
333
+ hoverDate: r.value,
334
+ onSelect: C
335
+ }, null, 8, ["current", "range", "hoverDate"])) : b("", !0)
336
+ ]),
337
+ _: 1
338
+ }),
339
+ s.value === "month" ? (d(), Y(G, {
340
+ key: 0,
341
+ current: u.value,
342
+ onSelect: c[4] || (c[4] = (p) => {
343
+ u.value = p, s.value = "day";
344
+ })
345
+ }, null, 8, ["current"])) : b("", !0),
346
+ s.value === "year" ? (d(), Y(Z, {
347
+ key: 1,
348
+ current: u.value,
349
+ onSelect: c[5] || (c[5] = (p) => {
350
+ u.value = p, s.value = "day";
351
+ })
352
+ }, null, 8, ["current"])) : b("", !0)
353
+ ]),
354
+ e.enableTime && !o.value ? (d(), Y(oe, {
355
+ key: 0,
356
+ value: e.modelValue,
357
+ onUpdate: c[6] || (c[6] = (p) => l("update:modelValue", p))
358
+ }, null, 8, ["value"])) : b("", !0),
359
+ e.shortcuts && !o.value ? (d(), Y(ae, {
360
+ key: 1,
361
+ onApply: c[7] || (c[7] = (p) => l("update:modelValue", p))
362
+ })) : b("", !0)
363
+ ])) : b("", !0)
364
+ ]),
365
+ _: 1
366
+ })
367
+ ]));
368
+ }
369
+ }), ge = (e, i) => {
370
+ const n = e.__vccOpts || e;
371
+ for (const [l, a] of i)
372
+ n[l] = a;
373
+ return n;
374
+ }, xe = /* @__PURE__ */ ge(be, [["__scopeId", "data-v-3a5e3cc6"]]);
375
+ export {
376
+ xe as DatePicker,
377
+ xe as default
378
+ };
package/dist/style.css ADDED
@@ -0,0 +1 @@
1
+ [data-v-3a5e3cc6]:root{--dp-bg: #ffffff;--dp-bg-dark: #1f1f1f;--dp-text: #1a1a1a;--dp-text-dark: #e5e5e5;--dp-border: #e5e7eb;--dp-border-dark: #3f3f3f;--dp-primary: #3b82f6;--dp-primary-dark: #60a5fa;--dp-radius: 8px;--dp-shadow: 0 4px 16px rgba(0, 0, 0, .12);--dp-transition: .15s ease}.dp-panel[data-v-3a5e3cc6]{background:var(--dp-bg);color:var(--dp-text);border-radius:var(--dp-radius);box-shadow:var(--dp-shadow);border:1px solid var(--dp-border);transition:var(--dp-transition)}.dark .dp-panel[data-v-3a5e3cc6]{background:var(--dp-bg-dark);color:var(--dp-text-dark);border-color:var(--dp-border-dark)}.dp-header[data-v-3a5e3cc6]{display:flex;justify-content:space-between;align-items:center;padding:8px 12px;font-weight:600}.dp-grid[data-v-3a5e3cc6]{display:grid;grid-template-columns:repeat(7,1fr);gap:4px;padding:8px}.dp-btn[data-v-3a5e3cc6]{padding:6px 10px;border-radius:6px;cursor:pointer;transition:var(--dp-transition)}.dp-btn[data-v-3a5e3cc6]:hover{background:#0000000d}.dark .dp-btn[data-v-3a5e3cc6]:hover{background:#ffffff14}.dp-day[data-v-3a5e3cc6]{text-align:center;padding:8px 0;border-radius:6px;cursor:pointer;transition:var(--dp-transition)}.dp-day[data-v-3a5e3cc6]:hover{background:#0000000d}.dark .dp-day[data-v-3a5e3cc6]:hover{background:#ffffff14}.dp-day.selected[data-v-3a5e3cc6]{background:var(--dp-primary);color:#fff}.dp-day.range[data-v-3a5e3cc6]{background:#3b82f626}.dark .dp-day.range[data-v-3a5e3cc6]{background:#60a5fa40}.dp-day.range-start[data-v-3a5e3cc6],.dp-day.range-end[data-v-3a5e3cc6]{background:var(--dp-primary);color:#fff}.fade-enter-active[data-v-3a5e3cc6],.fade-leave-active[data-v-3a5e3cc6]{transition:opacity .15s ease}.fade-enter-from[data-v-3a5e3cc6],.fade-leave-to[data-v-3a5e3cc6]{opacity:0}.slide-left-enter-active[data-v-3a5e3cc6],.slide-left-leave-active[data-v-3a5e3cc6]{transition:transform .15s ease}.slide-left-enter-from[data-v-3a5e3cc6]{transform:translate(20px)}.slide-left-leave-to[data-v-3a5e3cc6]{transform:translate(-20px)}
package/index.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ declare module "@ranjbar.amir/persian-datetime-picker" {
2
+ import { DefineComponent } from "vue"
3
+
4
+ const DatePicker: DefineComponent<{}, {}, any>
5
+
6
+ export default DatePicker
7
+ export { DatePicker }
8
+ }
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@ranjbar.amir/persian-datetime-picker",
3
+ "version": "1.0.1",
4
+ "description": "Modern Persian (Jalali) DateTime Picker for Vue 3",
5
+ "main": "dist/index.cjs.js",
6
+ "module": "dist/index.es.js",
7
+ "types": "index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "index.d.ts"
11
+ ],
12
+ "scripts": {
13
+ "build": "vite build"
14
+ },
15
+ "peerDependencies": {
16
+ "vue": "^3.4.0"
17
+ },
18
+ "dependencies": {
19
+ "dayjs": "^1.11.10",
20
+ "jalaliday": "^2.0.0"
21
+ },
22
+ "devDependencies": {
23
+ "vite": "^5.0.0",
24
+ "vue": "^3.4.0",
25
+ "@vitejs/plugin-vue": "^5.0.0",
26
+ "typescript": "^5.0.0"
27
+ }
28
+ }