@idds/js 1.0.38 → 1.0.39

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.
Files changed (3) hide show
  1. package/dist/index.iife.js +256 -138
  2. package/dist/index.js +256 -138
  3. package/package.json +1 -1
@@ -121,17 +121,24 @@ var InaUI = (() => {
121
121
  // src/js/components/stateful/datepicker.js
122
122
  function initDatepicker() {
123
123
  document.querySelectorAll(".ina-date-picker").forEach((datepicker) => {
124
+ if (datepicker.dataset.initialized === "true") return;
125
+ datepicker.dataset.initialized = "true";
126
+ const mode = datepicker.dataset.mode || "single";
127
+ const format = datepicker.dataset.format || "d MMMM yyyy";
124
128
  const trigger = datepicker.querySelector(".ina-date-picker__trigger");
125
129
  const triggerText = trigger.querySelector(".ina-date-picker__trigger-text");
126
130
  const panel = datepicker.querySelector(".ina-date-picker__panel");
131
+ panel.style.display = "none";
127
132
  let panelContent = panel.querySelector(".ina-date-picker__panel-content");
128
133
  if (!panelContent) {
129
134
  panelContent = document.createElement("div");
130
135
  panelContent.className = "ina-date-picker__panel-content";
131
136
  panel.appendChild(panelContent);
132
137
  }
133
- let currentDate = /* @__PURE__ */ new Date();
138
+ let viewDate = /* @__PURE__ */ new Date();
134
139
  let selectedDate = null;
140
+ let selectedDates = [];
141
+ let rangeDate = [null, null];
135
142
  let isOpen = false;
136
143
  const MONTHS_ID = [
137
144
  "Januari",
@@ -163,18 +170,67 @@ var InaUI = (() => {
163
170
  ];
164
171
  const DAYS_SHORT = ["Min", "Sen", "Sel", "Rab", "Kam", "Jum", "Sab"];
165
172
  function formatDate(date) {
173
+ if (!date) return "";
166
174
  const day = date.getDate().toString().padStart(2, "0");
167
175
  const monthIndex = date.getMonth();
168
176
  const year = date.getFullYear();
169
177
  return `${day} ${MONTHS_SHORT_ID[monthIndex]} ${year}`;
170
178
  }
171
- function createIcon(name, size = 16) {
172
- if (name === "chevron-left") {
173
- return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-left"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M15 6l-6 6l6 6" /></svg>`;
174
- }
175
- if (name === "chevron-right") {
176
- return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-right"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M9 6l6 6l-6 6" /></svg>`;
179
+ function updateTrigger() {
180
+ if (mode === "single") {
181
+ if (selectedDate) {
182
+ triggerText.textContent = formatDate(selectedDate);
183
+ triggerText.classList.add("ina-date-picker__trigger-text--value");
184
+ triggerText.classList.remove(
185
+ "ina-date-picker__trigger-text--placeholder"
186
+ );
187
+ } else {
188
+ triggerText.textContent = "Pilih Tanggal";
189
+ triggerText.classList.remove("ina-date-picker__trigger-text--value");
190
+ triggerText.classList.add(
191
+ "ina-date-picker__trigger-text--placeholder"
192
+ );
193
+ }
194
+ } else if (mode === "range") {
195
+ if (rangeDate[0] && rangeDate[1]) {
196
+ const start = formatDate(rangeDate[0]);
197
+ const end = formatDate(rangeDate[1]);
198
+ triggerText.textContent = `${start} - ${end}`;
199
+ triggerText.classList.add("ina-date-picker__trigger-text--value");
200
+ triggerText.classList.remove(
201
+ "ina-date-picker__trigger-text--placeholder"
202
+ );
203
+ } else if (rangeDate[0]) {
204
+ triggerText.textContent = `${formatDate(rangeDate[0])} - ...`;
205
+ triggerText.classList.add("ina-date-picker__trigger-text--value");
206
+ } else {
207
+ triggerText.textContent = "Pilih Rentang Tanggal";
208
+ triggerText.classList.remove("ina-date-picker__trigger-text--value");
209
+ triggerText.classList.add(
210
+ "ina-date-picker__trigger-text--placeholder"
211
+ );
212
+ }
213
+ } else if (mode === "multiple") {
214
+ if (selectedDates.length > 0) {
215
+ triggerText.textContent = `${selectedDates.length} Tanggal Terpilih`;
216
+ triggerText.classList.add("ina-date-picker__trigger-text--value");
217
+ triggerText.classList.remove(
218
+ "ina-date-picker__trigger-text--placeholder"
219
+ );
220
+ } else {
221
+ triggerText.textContent = "Pilih Beberapa Tanggal";
222
+ triggerText.classList.remove("ina-date-picker__trigger-text--value");
223
+ triggerText.classList.add(
224
+ "ina-date-picker__trigger-text--placeholder"
225
+ );
226
+ }
177
227
  }
228
+ }
229
+ function createIcon(name, size = 16) {
230
+ if (name === "chevron-left")
231
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 6l-6 6l6 6" /></svg>`;
232
+ if (name === "chevron-right")
233
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 6l6 6l-6 6" /></svg>`;
178
234
  return "";
179
235
  }
180
236
  function createMonthPicker(initialMonth, onChange) {
@@ -185,16 +241,10 @@ var InaUI = (() => {
185
241
  const pickerTrigger = document.createElement("button");
186
242
  pickerTrigger.type = "button";
187
243
  pickerTrigger.className = "ina-month-picker__trigger ina-month-picker__trigger--size-sm";
188
- const updateTriggerText = () => {
189
- pickerTrigger.innerHTML = `
190
- <span class="ina-month-picker__trigger-text">${MONTHS_SHORT_ID[currentMonthIdx]}</span>
191
- `;
244
+ const updateText = () => {
245
+ pickerTrigger.innerHTML = `<span class="ina-month-picker__trigger-text">${MONTHS_SHORT_ID[currentMonthIdx]}</span>`;
192
246
  };
193
- updateTriggerText();
194
- pickerTrigger.addEventListener("click", (e) => {
195
- e.stopPropagation();
196
- togglePicker(!isPickerOpen);
197
- });
247
+ updateText();
198
248
  const pickerPanel = document.createElement("div");
199
249
  pickerPanel.className = "ina-month-picker__panel";
200
250
  const grid = document.createElement("div");
@@ -209,21 +259,18 @@ var InaUI = (() => {
209
259
  btn.addEventListener("click", (e) => {
210
260
  e.stopPropagation();
211
261
  currentMonthIdx = idx;
212
- updateTriggerText();
213
- grid.querySelectorAll(".ina-month-picker__month-option").forEach((b, i) => {
214
- if (i === idx)
215
- b.classList.add("ina-month-picker__month-option--selected");
216
- else
217
- b.classList.remove("ina-month-picker__month-option--selected");
218
- });
262
+ updateText();
219
263
  togglePicker(false);
220
264
  onChange(idx);
221
265
  });
222
266
  grid.appendChild(btn);
223
267
  });
224
268
  pickerPanel.appendChild(grid);
225
- container.appendChild(pickerTrigger);
226
- container.appendChild(pickerPanel);
269
+ container.append(pickerTrigger, pickerPanel);
270
+ pickerTrigger.addEventListener("click", (e) => {
271
+ e.stopPropagation();
272
+ togglePicker(!isPickerOpen);
273
+ });
227
274
  function togglePicker(show) {
228
275
  isPickerOpen = show;
229
276
  if (show) {
@@ -241,13 +288,7 @@ var InaUI = (() => {
241
288
  element: container,
242
289
  setMonth: (m) => {
243
290
  currentMonthIdx = m;
244
- updateTriggerText();
245
- grid.querySelectorAll(".ina-month-picker__month-option").forEach((b, i) => {
246
- if (i === m)
247
- b.classList.add("ina-month-picker__month-option--selected");
248
- else
249
- b.classList.remove("ina-month-picker__month-option--selected");
250
- });
291
+ updateText();
251
292
  }
252
293
  };
253
294
  }
@@ -260,16 +301,10 @@ var InaUI = (() => {
260
301
  const pickerTrigger = document.createElement("button");
261
302
  pickerTrigger.type = "button";
262
303
  pickerTrigger.className = "ina-year-picker__trigger ina-year-picker__trigger--size-sm";
263
- const updateTriggerText = () => {
264
- pickerTrigger.innerHTML = `
265
- <span class="ina-year-picker__trigger-text">${currentYearVal}</span>
266
- `;
304
+ const updateText = () => {
305
+ pickerTrigger.innerHTML = `<span class="ina-year-picker__trigger-text">${currentYearVal}</span>`;
267
306
  };
268
- updateTriggerText();
269
- pickerTrigger.addEventListener("click", (e) => {
270
- e.stopPropagation();
271
- togglePicker(!isPickerOpen);
272
- });
307
+ updateText();
273
308
  const pickerPanel = document.createElement("div");
274
309
  pickerPanel.className = "ina-year-picker__panel";
275
310
  const header = document.createElement("div");
@@ -310,7 +345,7 @@ var InaUI = (() => {
310
345
  btn.addEventListener("click", (e) => {
311
346
  e.stopPropagation();
312
347
  currentYearVal = y;
313
- updateTriggerText();
348
+ updateText();
314
349
  togglePicker(false);
315
350
  onChange(y);
316
351
  });
@@ -319,16 +354,18 @@ var InaUI = (() => {
319
354
  }
320
355
  pickerPanel.append(header, grid);
321
356
  container.append(pickerTrigger, pickerPanel);
357
+ pickerTrigger.addEventListener("click", (e) => {
358
+ e.stopPropagation();
359
+ togglePicker(!isPickerOpen);
360
+ });
322
361
  function togglePicker(show) {
323
362
  isPickerOpen = show;
324
363
  if (show) {
325
364
  decadeStart = Math.floor(currentYearVal / 20) * 20;
326
365
  renderGrid();
327
366
  pickerPanel.classList.add("ina-year-picker__panel--open");
328
- pickerTrigger.setAttribute("aria-expanded", "true");
329
367
  } else {
330
368
  pickerPanel.classList.remove("ina-year-picker__panel--open");
331
- pickerTrigger.setAttribute("aria-expanded", "false");
332
369
  }
333
370
  }
334
371
  document.addEventListener("click", (e) => {
@@ -338,131 +375,212 @@ var InaUI = (() => {
338
375
  element: container,
339
376
  setYear: (y) => {
340
377
  currentYearVal = y;
341
- updateTriggerText();
378
+ updateText();
342
379
  }
343
380
  };
344
381
  }
345
- function renderCalendar() {
346
- panelContent.innerHTML = "";
347
- const year = currentDate.getFullYear();
348
- const month = currentDate.getMonth();
349
- const calendarContainer = document.createElement("div");
350
- calendarContainer.className = "ina-date-picker__calendar-container";
382
+ function renderCalendarGrid(baseDate, isNextMonth = false) {
383
+ const year = baseDate.getFullYear();
384
+ const month = baseDate.getMonth();
385
+ const container = document.createElement("div");
386
+ if (!isNextMonth) {
387
+ container.className = "ina-date-picker__calendar-container";
388
+ } else {
389
+ container.className = "ina-date-picker__calendar";
390
+ }
351
391
  const header = document.createElement("div");
352
- header.className = "ina-date-picker__calendar-header";
353
- const prevBtn = document.createElement("button");
354
- prevBtn.className = "ina-date-picker__nav-button";
355
- prevBtn.type = "button";
356
- prevBtn.innerHTML = createIcon("chevron-left", 20);
357
- prevBtn.addEventListener("click", (e) => {
358
- e.stopPropagation();
359
- currentDate.setMonth(month - 1);
360
- renderCalendar();
361
- });
362
- const nextBtn = document.createElement("button");
363
- nextBtn.className = "ina-date-picker__nav-button";
364
- nextBtn.type = "button";
365
- nextBtn.innerHTML = createIcon("chevron-right", 20);
366
- nextBtn.addEventListener("click", (e) => {
367
- e.stopPropagation();
368
- currentDate.setMonth(month + 1);
369
- renderCalendar();
370
- });
371
- const controls = document.createElement("div");
372
- controls.className = "ina-date-picker__header-controls";
373
- const monthContainer = document.createElement("div");
374
- monthContainer.className = "ina-date-picker__dropdown-container";
375
- const monthPicker = createMonthPicker(month, (newMonth) => {
376
- currentDate.setMonth(newMonth);
377
- renderCalendar();
378
- });
379
- monthContainer.appendChild(monthPicker.element);
380
- const yearContainer = document.createElement("div");
381
- yearContainer.className = "ina-date-picker__dropdown-container";
382
- const yearPicker = createYearPicker(year, (newYear) => {
383
- currentDate.setFullYear(newYear);
384
- renderCalendar();
385
- });
386
- yearContainer.appendChild(yearPicker.element);
387
- controls.append(monthContainer, yearContainer);
388
- header.append(prevBtn, controls, nextBtn);
392
+ header.className = isNextMonth ? "ina-date-picker__next-month-header" : "ina-date-picker__calendar-header";
393
+ if (!isNextMonth) {
394
+ const prevBtn = document.createElement("button");
395
+ prevBtn.type = "button";
396
+ prevBtn.className = "ina-date-picker__nav-button";
397
+ prevBtn.innerHTML = createIcon("chevron-left");
398
+ prevBtn.onclick = (e) => {
399
+ e.stopPropagation();
400
+ viewDate.setMonth(viewDate.getMonth() - 1);
401
+ render();
402
+ };
403
+ header.appendChild(prevBtn);
404
+ } else {
405
+ const spacer = document.createElement("div");
406
+ spacer.style.width = "32px";
407
+ header.appendChild(spacer);
408
+ }
409
+ if (!isNextMonth) {
410
+ const controls = document.createElement("div");
411
+ controls.className = "ina-date-picker__header-controls";
412
+ const monthCont = document.createElement("div");
413
+ monthCont.className = "ina-date-picker__dropdown-container";
414
+ const monthPicker = createMonthPicker(month, (m) => {
415
+ viewDate.setMonth(m);
416
+ render();
417
+ });
418
+ monthCont.appendChild(monthPicker.element);
419
+ const yearCont = document.createElement("div");
420
+ yearCont.className = "ina-date-picker__dropdown-container";
421
+ const yearPicker = createYearPicker(year, (y) => {
422
+ viewDate.setFullYear(y);
423
+ render();
424
+ });
425
+ yearCont.appendChild(yearPicker.element);
426
+ controls.append(monthCont, yearCont);
427
+ header.appendChild(controls);
428
+ } else {
429
+ const title = document.createElement("div");
430
+ title.className = "ina-date-picker__calendar-title";
431
+ title.textContent = `${MONTHS_ID[month]} ${year}`;
432
+ header.appendChild(title);
433
+ }
434
+ const showNextBtn = mode === "single" && !isNextMonth || isNextMonth;
435
+ if (showNextBtn) {
436
+ const nextBtn = document.createElement("button");
437
+ nextBtn.type = "button";
438
+ nextBtn.className = "ina-date-picker__nav-button";
439
+ nextBtn.innerHTML = createIcon("chevron-right");
440
+ nextBtn.onclick = (e) => {
441
+ e.stopPropagation();
442
+ viewDate.setMonth(viewDate.getMonth() + 1);
443
+ render();
444
+ };
445
+ header.appendChild(nextBtn);
446
+ } else if (!isNextMonth) {
447
+ const spacer = document.createElement("div");
448
+ spacer.style.width = "32px";
449
+ header.appendChild(spacer);
450
+ }
389
451
  const grid = document.createElement("div");
390
452
  grid.className = "ina-date-picker__calendar-grid";
391
- DAYS_SHORT.forEach((day) => {
453
+ DAYS_SHORT.forEach((d) => {
392
454
  const dh = document.createElement("div");
393
455
  dh.className = "ina-date-picker__day-header";
394
- dh.textContent = day;
456
+ dh.textContent = d;
395
457
  grid.appendChild(dh);
396
458
  });
397
459
  const firstDayOfMonth = new Date(year, month, 1).getDay();
398
460
  const daysInMonth = new Date(year, month + 1, 0).getDate();
399
461
  const daysInPrevMonth = new Date(year, month, 0).getDate();
462
+ const today = /* @__PURE__ */ new Date();
400
463
  for (let i = firstDayOfMonth - 1; i >= 0; i--) {
401
- const dayBtn = document.createElement("button");
402
- dayBtn.type = "button";
403
- dayBtn.className = "ina-date-picker__day ina-date-picker__day--other-month ina-date-picker__day--disabled";
404
- dayBtn.textContent = daysInPrevMonth - i;
405
- grid.appendChild(dayBtn);
464
+ const btn = document.createElement("button");
465
+ btn.type = "button";
466
+ btn.className = "ina-date-picker__day ina-date-picker__day--other-month ina-date-picker__day--disabled";
467
+ btn.textContent = daysInPrevMonth - i;
468
+ grid.appendChild(btn);
406
469
  }
407
- const today = /* @__PURE__ */ new Date();
408
470
  for (let i = 1; i <= daysInMonth; i++) {
409
- const dayBtn = document.createElement("button");
410
- dayBtn.type = "button";
411
- dayBtn.className = "ina-date-picker__day";
412
- dayBtn.textContent = i;
471
+ const date = new Date(year, month, i);
472
+ const btn = document.createElement("button");
473
+ btn.type = "button";
474
+ btn.className = "ina-date-picker__day";
475
+ btn.textContent = i;
413
476
  let isSelected = false;
414
- if (selectedDate && selectedDate.getDate() === i && selectedDate.getMonth() === month && selectedDate.getFullYear() === year) {
415
- dayBtn.classList.add("ina-date-picker__day--selected");
416
- isSelected = true;
417
- }
418
- if (today.getDate() === i && today.getMonth() === month && today.getFullYear() === year) {
419
- dayBtn.classList.add("ina-date-picker__day--today");
420
- }
421
- if (!isSelected) {
422
- dayBtn.classList.add("ina-date-picker__day--hover");
477
+ let isInRange = false;
478
+ if (mode === "single" && selectedDate) {
479
+ if (date.toDateString() === selectedDate.toDateString())
480
+ isSelected = true;
481
+ } else if (mode === "multiple") {
482
+ if (selectedDates.some((d) => d.toDateString() === date.toDateString()))
483
+ isSelected = true;
484
+ } else if (mode === "range") {
485
+ const [start, end] = rangeDate;
486
+ if (start && date.toDateString() === start.toDateString())
487
+ isSelected = true;
488
+ if (end && date.toDateString() === end.toDateString())
489
+ isSelected = true;
490
+ if (start && end && date > start && date < end) isInRange = true;
423
491
  }
424
- dayBtn.addEventListener("click", (e) => {
492
+ if (isSelected) btn.classList.add("ina-date-picker__day--selected");
493
+ if (isInRange) btn.classList.add("ina-date-picker__day--in-range");
494
+ if (date.toDateString() === today.toDateString())
495
+ btn.classList.add("ina-date-picker__day--today");
496
+ if (!isSelected && !isInRange)
497
+ btn.classList.add("ina-date-picker__day--hover");
498
+ btn.onclick = (e) => {
425
499
  e.stopPropagation();
426
- selectedDate = new Date(year, month, i);
427
- triggerText.textContent = formatDate(selectedDate);
428
- triggerText.classList.add("ina-date-picker__trigger-text--value");
429
- triggerText.classList.remove(
430
- "ina-date-picker__trigger-text--placeholder"
431
- );
500
+ if (mode === "single") {
501
+ selectedDate = date;
502
+ close();
503
+ } else if (mode === "multiple") {
504
+ const existsIdx = selectedDates.findIndex(
505
+ (d) => d.toDateString() === date.toDateString()
506
+ );
507
+ if (existsIdx >= 0) selectedDates.splice(existsIdx, 1);
508
+ else selectedDates.push(date);
509
+ render();
510
+ } else if (mode === "range") {
511
+ const [start, end] = rangeDate;
512
+ if (!start || start && end) {
513
+ rangeDate = [date, null];
514
+ } else {
515
+ if (date < start) {
516
+ rangeDate = [date, start];
517
+ } else {
518
+ rangeDate = [start, date];
519
+ }
520
+ close();
521
+ }
522
+ render();
523
+ }
524
+ updateTrigger();
432
525
  datepicker.dispatchEvent(
433
- new CustomEvent("date:changed", { detail: { selectedDate } })
526
+ new CustomEvent("date:changed", {
527
+ detail: {
528
+ selectedDate: mode === "single" ? selectedDate : mode === "multiple" ? selectedDates : rangeDate
529
+ }
530
+ })
434
531
  );
435
- toggle(false);
436
- });
437
- grid.appendChild(dayBtn);
532
+ };
533
+ grid.appendChild(btn);
438
534
  }
439
- const currentCells = grid.children.length - 7;
440
- const remaining = 42 - currentCells;
535
+ const usedCells = grid.children.length - 7;
536
+ const remaining = 42 - usedCells;
441
537
  for (let i = 1; i <= remaining; i++) {
442
- const dayBtn = document.createElement("button");
443
- dayBtn.type = "button";
444
- dayBtn.className = "ina-date-picker__day ina-date-picker__day--other-month ina-date-picker__day--disabled";
445
- dayBtn.textContent = i;
446
- grid.appendChild(dayBtn);
538
+ const btn = document.createElement("button");
539
+ btn.type = "button";
540
+ btn.className = "ina-date-picker__day ina-date-picker__day--other-month ina-date-picker__day--disabled";
541
+ btn.textContent = i;
542
+ grid.appendChild(btn);
447
543
  }
448
- calendarContainer.append(header, grid);
449
- panelContent.appendChild(calendarContainer);
544
+ container.append(header, grid);
545
+ return container;
450
546
  }
451
- function toggle(show) {
452
- isOpen = show;
453
- if (show) {
454
- panel.classList.add("ina-date-picker__panel--open");
455
- renderCalendar();
456
- } else {
457
- panel.classList.remove("ina-date-picker__panel--open");
547
+ function render() {
548
+ panelContent.innerHTML = "";
549
+ const cal1 = renderCalendarGrid(viewDate);
550
+ panelContent.appendChild(cal1);
551
+ if (mode === "range" || mode === "multiple") {
552
+ const nextMonthDate = new Date(
553
+ viewDate.getFullYear(),
554
+ viewDate.getMonth() + 1,
555
+ 1
556
+ );
557
+ const cal2 = renderCalendarGrid(nextMonthDate, true);
558
+ panelContent.appendChild(cal2);
458
559
  }
459
560
  }
561
+ function open() {
562
+ isOpen = true;
563
+ panel.classList.add("ina-date-picker__panel--open");
564
+ panel.style.display = "block";
565
+ render();
566
+ }
567
+ function close() {
568
+ isOpen = false;
569
+ panel.classList.remove("ina-date-picker__panel--open");
570
+ panel.style.display = "none";
571
+ }
572
+ function toggle() {
573
+ if (isOpen) close();
574
+ else open();
575
+ }
460
576
  trigger.addEventListener("click", (e) => {
461
577
  e.stopPropagation();
462
- toggle(!isOpen);
578
+ toggle();
463
579
  });
464
580
  document.addEventListener("click", (e) => {
465
- if (!datepicker.contains(e.target)) toggle(false);
581
+ if (!datepicker.contains(e.target)) {
582
+ close();
583
+ }
466
584
  });
467
585
  });
468
586
  }
package/dist/index.js CHANGED
@@ -109,17 +109,24 @@ function initAccordion(rootSelector = `.${PREFIX}-accordion`) {
109
109
  // src/js/components/stateful/datepicker.js
110
110
  function initDatepicker() {
111
111
  document.querySelectorAll(".ina-date-picker").forEach((datepicker) => {
112
+ if (datepicker.dataset.initialized === "true") return;
113
+ datepicker.dataset.initialized = "true";
114
+ const mode = datepicker.dataset.mode || "single";
115
+ const format = datepicker.dataset.format || "d MMMM yyyy";
112
116
  const trigger = datepicker.querySelector(".ina-date-picker__trigger");
113
117
  const triggerText = trigger.querySelector(".ina-date-picker__trigger-text");
114
118
  const panel = datepicker.querySelector(".ina-date-picker__panel");
119
+ panel.style.display = "none";
115
120
  let panelContent = panel.querySelector(".ina-date-picker__panel-content");
116
121
  if (!panelContent) {
117
122
  panelContent = document.createElement("div");
118
123
  panelContent.className = "ina-date-picker__panel-content";
119
124
  panel.appendChild(panelContent);
120
125
  }
121
- let currentDate = /* @__PURE__ */ new Date();
126
+ let viewDate = /* @__PURE__ */ new Date();
122
127
  let selectedDate = null;
128
+ let selectedDates = [];
129
+ let rangeDate = [null, null];
123
130
  let isOpen = false;
124
131
  const MONTHS_ID = [
125
132
  "Januari",
@@ -151,18 +158,67 @@ function initDatepicker() {
151
158
  ];
152
159
  const DAYS_SHORT = ["Min", "Sen", "Sel", "Rab", "Kam", "Jum", "Sab"];
153
160
  function formatDate(date) {
161
+ if (!date) return "";
154
162
  const day = date.getDate().toString().padStart(2, "0");
155
163
  const monthIndex = date.getMonth();
156
164
  const year = date.getFullYear();
157
165
  return `${day} ${MONTHS_SHORT_ID[monthIndex]} ${year}`;
158
166
  }
159
- function createIcon(name, size = 16) {
160
- if (name === "chevron-left") {
161
- return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-left"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M15 6l-6 6l6 6" /></svg>`;
162
- }
163
- if (name === "chevron-right") {
164
- return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-right"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M9 6l6 6l-6 6" /></svg>`;
167
+ function updateTrigger() {
168
+ if (mode === "single") {
169
+ if (selectedDate) {
170
+ triggerText.textContent = formatDate(selectedDate);
171
+ triggerText.classList.add("ina-date-picker__trigger-text--value");
172
+ triggerText.classList.remove(
173
+ "ina-date-picker__trigger-text--placeholder"
174
+ );
175
+ } else {
176
+ triggerText.textContent = "Pilih Tanggal";
177
+ triggerText.classList.remove("ina-date-picker__trigger-text--value");
178
+ triggerText.classList.add(
179
+ "ina-date-picker__trigger-text--placeholder"
180
+ );
181
+ }
182
+ } else if (mode === "range") {
183
+ if (rangeDate[0] && rangeDate[1]) {
184
+ const start = formatDate(rangeDate[0]);
185
+ const end = formatDate(rangeDate[1]);
186
+ triggerText.textContent = `${start} - ${end}`;
187
+ triggerText.classList.add("ina-date-picker__trigger-text--value");
188
+ triggerText.classList.remove(
189
+ "ina-date-picker__trigger-text--placeholder"
190
+ );
191
+ } else if (rangeDate[0]) {
192
+ triggerText.textContent = `${formatDate(rangeDate[0])} - ...`;
193
+ triggerText.classList.add("ina-date-picker__trigger-text--value");
194
+ } else {
195
+ triggerText.textContent = "Pilih Rentang Tanggal";
196
+ triggerText.classList.remove("ina-date-picker__trigger-text--value");
197
+ triggerText.classList.add(
198
+ "ina-date-picker__trigger-text--placeholder"
199
+ );
200
+ }
201
+ } else if (mode === "multiple") {
202
+ if (selectedDates.length > 0) {
203
+ triggerText.textContent = `${selectedDates.length} Tanggal Terpilih`;
204
+ triggerText.classList.add("ina-date-picker__trigger-text--value");
205
+ triggerText.classList.remove(
206
+ "ina-date-picker__trigger-text--placeholder"
207
+ );
208
+ } else {
209
+ triggerText.textContent = "Pilih Beberapa Tanggal";
210
+ triggerText.classList.remove("ina-date-picker__trigger-text--value");
211
+ triggerText.classList.add(
212
+ "ina-date-picker__trigger-text--placeholder"
213
+ );
214
+ }
165
215
  }
216
+ }
217
+ function createIcon(name, size = 16) {
218
+ if (name === "chevron-left")
219
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 6l-6 6l6 6" /></svg>`;
220
+ if (name === "chevron-right")
221
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 6l6 6l-6 6" /></svg>`;
166
222
  return "";
167
223
  }
168
224
  function createMonthPicker(initialMonth, onChange) {
@@ -173,16 +229,10 @@ function initDatepicker() {
173
229
  const pickerTrigger = document.createElement("button");
174
230
  pickerTrigger.type = "button";
175
231
  pickerTrigger.className = "ina-month-picker__trigger ina-month-picker__trigger--size-sm";
176
- const updateTriggerText = () => {
177
- pickerTrigger.innerHTML = `
178
- <span class="ina-month-picker__trigger-text">${MONTHS_SHORT_ID[currentMonthIdx]}</span>
179
- `;
232
+ const updateText = () => {
233
+ pickerTrigger.innerHTML = `<span class="ina-month-picker__trigger-text">${MONTHS_SHORT_ID[currentMonthIdx]}</span>`;
180
234
  };
181
- updateTriggerText();
182
- pickerTrigger.addEventListener("click", (e) => {
183
- e.stopPropagation();
184
- togglePicker(!isPickerOpen);
185
- });
235
+ updateText();
186
236
  const pickerPanel = document.createElement("div");
187
237
  pickerPanel.className = "ina-month-picker__panel";
188
238
  const grid = document.createElement("div");
@@ -197,21 +247,18 @@ function initDatepicker() {
197
247
  btn.addEventListener("click", (e) => {
198
248
  e.stopPropagation();
199
249
  currentMonthIdx = idx;
200
- updateTriggerText();
201
- grid.querySelectorAll(".ina-month-picker__month-option").forEach((b, i) => {
202
- if (i === idx)
203
- b.classList.add("ina-month-picker__month-option--selected");
204
- else
205
- b.classList.remove("ina-month-picker__month-option--selected");
206
- });
250
+ updateText();
207
251
  togglePicker(false);
208
252
  onChange(idx);
209
253
  });
210
254
  grid.appendChild(btn);
211
255
  });
212
256
  pickerPanel.appendChild(grid);
213
- container.appendChild(pickerTrigger);
214
- container.appendChild(pickerPanel);
257
+ container.append(pickerTrigger, pickerPanel);
258
+ pickerTrigger.addEventListener("click", (e) => {
259
+ e.stopPropagation();
260
+ togglePicker(!isPickerOpen);
261
+ });
215
262
  function togglePicker(show) {
216
263
  isPickerOpen = show;
217
264
  if (show) {
@@ -229,13 +276,7 @@ function initDatepicker() {
229
276
  element: container,
230
277
  setMonth: (m) => {
231
278
  currentMonthIdx = m;
232
- updateTriggerText();
233
- grid.querySelectorAll(".ina-month-picker__month-option").forEach((b, i) => {
234
- if (i === m)
235
- b.classList.add("ina-month-picker__month-option--selected");
236
- else
237
- b.classList.remove("ina-month-picker__month-option--selected");
238
- });
279
+ updateText();
239
280
  }
240
281
  };
241
282
  }
@@ -248,16 +289,10 @@ function initDatepicker() {
248
289
  const pickerTrigger = document.createElement("button");
249
290
  pickerTrigger.type = "button";
250
291
  pickerTrigger.className = "ina-year-picker__trigger ina-year-picker__trigger--size-sm";
251
- const updateTriggerText = () => {
252
- pickerTrigger.innerHTML = `
253
- <span class="ina-year-picker__trigger-text">${currentYearVal}</span>
254
- `;
292
+ const updateText = () => {
293
+ pickerTrigger.innerHTML = `<span class="ina-year-picker__trigger-text">${currentYearVal}</span>`;
255
294
  };
256
- updateTriggerText();
257
- pickerTrigger.addEventListener("click", (e) => {
258
- e.stopPropagation();
259
- togglePicker(!isPickerOpen);
260
- });
295
+ updateText();
261
296
  const pickerPanel = document.createElement("div");
262
297
  pickerPanel.className = "ina-year-picker__panel";
263
298
  const header = document.createElement("div");
@@ -298,7 +333,7 @@ function initDatepicker() {
298
333
  btn.addEventListener("click", (e) => {
299
334
  e.stopPropagation();
300
335
  currentYearVal = y;
301
- updateTriggerText();
336
+ updateText();
302
337
  togglePicker(false);
303
338
  onChange(y);
304
339
  });
@@ -307,16 +342,18 @@ function initDatepicker() {
307
342
  }
308
343
  pickerPanel.append(header, grid);
309
344
  container.append(pickerTrigger, pickerPanel);
345
+ pickerTrigger.addEventListener("click", (e) => {
346
+ e.stopPropagation();
347
+ togglePicker(!isPickerOpen);
348
+ });
310
349
  function togglePicker(show) {
311
350
  isPickerOpen = show;
312
351
  if (show) {
313
352
  decadeStart = Math.floor(currentYearVal / 20) * 20;
314
353
  renderGrid();
315
354
  pickerPanel.classList.add("ina-year-picker__panel--open");
316
- pickerTrigger.setAttribute("aria-expanded", "true");
317
355
  } else {
318
356
  pickerPanel.classList.remove("ina-year-picker__panel--open");
319
- pickerTrigger.setAttribute("aria-expanded", "false");
320
357
  }
321
358
  }
322
359
  document.addEventListener("click", (e) => {
@@ -326,131 +363,212 @@ function initDatepicker() {
326
363
  element: container,
327
364
  setYear: (y) => {
328
365
  currentYearVal = y;
329
- updateTriggerText();
366
+ updateText();
330
367
  }
331
368
  };
332
369
  }
333
- function renderCalendar() {
334
- panelContent.innerHTML = "";
335
- const year = currentDate.getFullYear();
336
- const month = currentDate.getMonth();
337
- const calendarContainer = document.createElement("div");
338
- calendarContainer.className = "ina-date-picker__calendar-container";
370
+ function renderCalendarGrid(baseDate, isNextMonth = false) {
371
+ const year = baseDate.getFullYear();
372
+ const month = baseDate.getMonth();
373
+ const container = document.createElement("div");
374
+ if (!isNextMonth) {
375
+ container.className = "ina-date-picker__calendar-container";
376
+ } else {
377
+ container.className = "ina-date-picker__calendar";
378
+ }
339
379
  const header = document.createElement("div");
340
- header.className = "ina-date-picker__calendar-header";
341
- const prevBtn = document.createElement("button");
342
- prevBtn.className = "ina-date-picker__nav-button";
343
- prevBtn.type = "button";
344
- prevBtn.innerHTML = createIcon("chevron-left", 20);
345
- prevBtn.addEventListener("click", (e) => {
346
- e.stopPropagation();
347
- currentDate.setMonth(month - 1);
348
- renderCalendar();
349
- });
350
- const nextBtn = document.createElement("button");
351
- nextBtn.className = "ina-date-picker__nav-button";
352
- nextBtn.type = "button";
353
- nextBtn.innerHTML = createIcon("chevron-right", 20);
354
- nextBtn.addEventListener("click", (e) => {
355
- e.stopPropagation();
356
- currentDate.setMonth(month + 1);
357
- renderCalendar();
358
- });
359
- const controls = document.createElement("div");
360
- controls.className = "ina-date-picker__header-controls";
361
- const monthContainer = document.createElement("div");
362
- monthContainer.className = "ina-date-picker__dropdown-container";
363
- const monthPicker = createMonthPicker(month, (newMonth) => {
364
- currentDate.setMonth(newMonth);
365
- renderCalendar();
366
- });
367
- monthContainer.appendChild(monthPicker.element);
368
- const yearContainer = document.createElement("div");
369
- yearContainer.className = "ina-date-picker__dropdown-container";
370
- const yearPicker = createYearPicker(year, (newYear) => {
371
- currentDate.setFullYear(newYear);
372
- renderCalendar();
373
- });
374
- yearContainer.appendChild(yearPicker.element);
375
- controls.append(monthContainer, yearContainer);
376
- header.append(prevBtn, controls, nextBtn);
380
+ header.className = isNextMonth ? "ina-date-picker__next-month-header" : "ina-date-picker__calendar-header";
381
+ if (!isNextMonth) {
382
+ const prevBtn = document.createElement("button");
383
+ prevBtn.type = "button";
384
+ prevBtn.className = "ina-date-picker__nav-button";
385
+ prevBtn.innerHTML = createIcon("chevron-left");
386
+ prevBtn.onclick = (e) => {
387
+ e.stopPropagation();
388
+ viewDate.setMonth(viewDate.getMonth() - 1);
389
+ render();
390
+ };
391
+ header.appendChild(prevBtn);
392
+ } else {
393
+ const spacer = document.createElement("div");
394
+ spacer.style.width = "32px";
395
+ header.appendChild(spacer);
396
+ }
397
+ if (!isNextMonth) {
398
+ const controls = document.createElement("div");
399
+ controls.className = "ina-date-picker__header-controls";
400
+ const monthCont = document.createElement("div");
401
+ monthCont.className = "ina-date-picker__dropdown-container";
402
+ const monthPicker = createMonthPicker(month, (m) => {
403
+ viewDate.setMonth(m);
404
+ render();
405
+ });
406
+ monthCont.appendChild(monthPicker.element);
407
+ const yearCont = document.createElement("div");
408
+ yearCont.className = "ina-date-picker__dropdown-container";
409
+ const yearPicker = createYearPicker(year, (y) => {
410
+ viewDate.setFullYear(y);
411
+ render();
412
+ });
413
+ yearCont.appendChild(yearPicker.element);
414
+ controls.append(monthCont, yearCont);
415
+ header.appendChild(controls);
416
+ } else {
417
+ const title = document.createElement("div");
418
+ title.className = "ina-date-picker__calendar-title";
419
+ title.textContent = `${MONTHS_ID[month]} ${year}`;
420
+ header.appendChild(title);
421
+ }
422
+ const showNextBtn = mode === "single" && !isNextMonth || isNextMonth;
423
+ if (showNextBtn) {
424
+ const nextBtn = document.createElement("button");
425
+ nextBtn.type = "button";
426
+ nextBtn.className = "ina-date-picker__nav-button";
427
+ nextBtn.innerHTML = createIcon("chevron-right");
428
+ nextBtn.onclick = (e) => {
429
+ e.stopPropagation();
430
+ viewDate.setMonth(viewDate.getMonth() + 1);
431
+ render();
432
+ };
433
+ header.appendChild(nextBtn);
434
+ } else if (!isNextMonth) {
435
+ const spacer = document.createElement("div");
436
+ spacer.style.width = "32px";
437
+ header.appendChild(spacer);
438
+ }
377
439
  const grid = document.createElement("div");
378
440
  grid.className = "ina-date-picker__calendar-grid";
379
- DAYS_SHORT.forEach((day) => {
441
+ DAYS_SHORT.forEach((d) => {
380
442
  const dh = document.createElement("div");
381
443
  dh.className = "ina-date-picker__day-header";
382
- dh.textContent = day;
444
+ dh.textContent = d;
383
445
  grid.appendChild(dh);
384
446
  });
385
447
  const firstDayOfMonth = new Date(year, month, 1).getDay();
386
448
  const daysInMonth = new Date(year, month + 1, 0).getDate();
387
449
  const daysInPrevMonth = new Date(year, month, 0).getDate();
450
+ const today = /* @__PURE__ */ new Date();
388
451
  for (let i = firstDayOfMonth - 1; i >= 0; i--) {
389
- const dayBtn = document.createElement("button");
390
- dayBtn.type = "button";
391
- dayBtn.className = "ina-date-picker__day ina-date-picker__day--other-month ina-date-picker__day--disabled";
392
- dayBtn.textContent = daysInPrevMonth - i;
393
- grid.appendChild(dayBtn);
452
+ const btn = document.createElement("button");
453
+ btn.type = "button";
454
+ btn.className = "ina-date-picker__day ina-date-picker__day--other-month ina-date-picker__day--disabled";
455
+ btn.textContent = daysInPrevMonth - i;
456
+ grid.appendChild(btn);
394
457
  }
395
- const today = /* @__PURE__ */ new Date();
396
458
  for (let i = 1; i <= daysInMonth; i++) {
397
- const dayBtn = document.createElement("button");
398
- dayBtn.type = "button";
399
- dayBtn.className = "ina-date-picker__day";
400
- dayBtn.textContent = i;
459
+ const date = new Date(year, month, i);
460
+ const btn = document.createElement("button");
461
+ btn.type = "button";
462
+ btn.className = "ina-date-picker__day";
463
+ btn.textContent = i;
401
464
  let isSelected = false;
402
- if (selectedDate && selectedDate.getDate() === i && selectedDate.getMonth() === month && selectedDate.getFullYear() === year) {
403
- dayBtn.classList.add("ina-date-picker__day--selected");
404
- isSelected = true;
405
- }
406
- if (today.getDate() === i && today.getMonth() === month && today.getFullYear() === year) {
407
- dayBtn.classList.add("ina-date-picker__day--today");
408
- }
409
- if (!isSelected) {
410
- dayBtn.classList.add("ina-date-picker__day--hover");
465
+ let isInRange = false;
466
+ if (mode === "single" && selectedDate) {
467
+ if (date.toDateString() === selectedDate.toDateString())
468
+ isSelected = true;
469
+ } else if (mode === "multiple") {
470
+ if (selectedDates.some((d) => d.toDateString() === date.toDateString()))
471
+ isSelected = true;
472
+ } else if (mode === "range") {
473
+ const [start, end] = rangeDate;
474
+ if (start && date.toDateString() === start.toDateString())
475
+ isSelected = true;
476
+ if (end && date.toDateString() === end.toDateString())
477
+ isSelected = true;
478
+ if (start && end && date > start && date < end) isInRange = true;
411
479
  }
412
- dayBtn.addEventListener("click", (e) => {
480
+ if (isSelected) btn.classList.add("ina-date-picker__day--selected");
481
+ if (isInRange) btn.classList.add("ina-date-picker__day--in-range");
482
+ if (date.toDateString() === today.toDateString())
483
+ btn.classList.add("ina-date-picker__day--today");
484
+ if (!isSelected && !isInRange)
485
+ btn.classList.add("ina-date-picker__day--hover");
486
+ btn.onclick = (e) => {
413
487
  e.stopPropagation();
414
- selectedDate = new Date(year, month, i);
415
- triggerText.textContent = formatDate(selectedDate);
416
- triggerText.classList.add("ina-date-picker__trigger-text--value");
417
- triggerText.classList.remove(
418
- "ina-date-picker__trigger-text--placeholder"
419
- );
488
+ if (mode === "single") {
489
+ selectedDate = date;
490
+ close();
491
+ } else if (mode === "multiple") {
492
+ const existsIdx = selectedDates.findIndex(
493
+ (d) => d.toDateString() === date.toDateString()
494
+ );
495
+ if (existsIdx >= 0) selectedDates.splice(existsIdx, 1);
496
+ else selectedDates.push(date);
497
+ render();
498
+ } else if (mode === "range") {
499
+ const [start, end] = rangeDate;
500
+ if (!start || start && end) {
501
+ rangeDate = [date, null];
502
+ } else {
503
+ if (date < start) {
504
+ rangeDate = [date, start];
505
+ } else {
506
+ rangeDate = [start, date];
507
+ }
508
+ close();
509
+ }
510
+ render();
511
+ }
512
+ updateTrigger();
420
513
  datepicker.dispatchEvent(
421
- new CustomEvent("date:changed", { detail: { selectedDate } })
514
+ new CustomEvent("date:changed", {
515
+ detail: {
516
+ selectedDate: mode === "single" ? selectedDate : mode === "multiple" ? selectedDates : rangeDate
517
+ }
518
+ })
422
519
  );
423
- toggle(false);
424
- });
425
- grid.appendChild(dayBtn);
520
+ };
521
+ grid.appendChild(btn);
426
522
  }
427
- const currentCells = grid.children.length - 7;
428
- const remaining = 42 - currentCells;
523
+ const usedCells = grid.children.length - 7;
524
+ const remaining = 42 - usedCells;
429
525
  for (let i = 1; i <= remaining; i++) {
430
- const dayBtn = document.createElement("button");
431
- dayBtn.type = "button";
432
- dayBtn.className = "ina-date-picker__day ina-date-picker__day--other-month ina-date-picker__day--disabled";
433
- dayBtn.textContent = i;
434
- grid.appendChild(dayBtn);
526
+ const btn = document.createElement("button");
527
+ btn.type = "button";
528
+ btn.className = "ina-date-picker__day ina-date-picker__day--other-month ina-date-picker__day--disabled";
529
+ btn.textContent = i;
530
+ grid.appendChild(btn);
435
531
  }
436
- calendarContainer.append(header, grid);
437
- panelContent.appendChild(calendarContainer);
532
+ container.append(header, grid);
533
+ return container;
438
534
  }
439
- function toggle(show) {
440
- isOpen = show;
441
- if (show) {
442
- panel.classList.add("ina-date-picker__panel--open");
443
- renderCalendar();
444
- } else {
445
- panel.classList.remove("ina-date-picker__panel--open");
535
+ function render() {
536
+ panelContent.innerHTML = "";
537
+ const cal1 = renderCalendarGrid(viewDate);
538
+ panelContent.appendChild(cal1);
539
+ if (mode === "range" || mode === "multiple") {
540
+ const nextMonthDate = new Date(
541
+ viewDate.getFullYear(),
542
+ viewDate.getMonth() + 1,
543
+ 1
544
+ );
545
+ const cal2 = renderCalendarGrid(nextMonthDate, true);
546
+ panelContent.appendChild(cal2);
446
547
  }
447
548
  }
549
+ function open() {
550
+ isOpen = true;
551
+ panel.classList.add("ina-date-picker__panel--open");
552
+ panel.style.display = "block";
553
+ render();
554
+ }
555
+ function close() {
556
+ isOpen = false;
557
+ panel.classList.remove("ina-date-picker__panel--open");
558
+ panel.style.display = "none";
559
+ }
560
+ function toggle() {
561
+ if (isOpen) close();
562
+ else open();
563
+ }
448
564
  trigger.addEventListener("click", (e) => {
449
565
  e.stopPropagation();
450
- toggle(!isOpen);
566
+ toggle();
451
567
  });
452
568
  document.addEventListener("click", (e) => {
453
- if (!datepicker.contains(e.target)) toggle(false);
569
+ if (!datepicker.contains(e.target)) {
570
+ close();
571
+ }
454
572
  });
455
573
  });
456
574
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idds/js",
3
- "version": "1.0.38",
3
+ "version": "1.0.39",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },