@kashi-ui/elements 0.25.0 → 0.26.0
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 +2 -1
- package/dist/appointment.d.ts +40 -0
- package/dist/appointment.js +174 -0
- package/dist/base.js +2 -0
- package/dist/basic.d.ts +43 -0
- package/dist/basic.js +223 -0
- package/dist/cdn/appointment-DkMxtSWi.js +1 -0
- package/dist/cdn/base-DfzNTdQk.js +1 -0
- package/dist/cdn/basic-B5n1okZO.js +1 -0
- package/dist/cdn/calendar-oMlnK1_D.js +16 -0
- package/dist/cdn/{charts-DNOWi4sJ.js → charts-SHKq2S4I.js} +1 -1
- package/dist/cdn/{combobox-ZGrZ5okg.js → combobox-DiGy7lAB.js} +1 -1
- package/dist/cdn/{controls-K-OZcjg4.js → controls-C6zwNYWs.js} +1 -1
- package/dist/cdn/{display-C975tS7j.js → display-DZFW9EEX.js} +1 -1
- package/dist/cdn/{fields-BUTaSN8m.js → fields-D2hmrvVR.js} +1 -1
- package/dist/cdn/forms-CeItYHFx.js +1 -0
- package/dist/cdn/holidays-BwsAEOI1.js +1 -0
- package/dist/cdn/{inputs-BjOddAM7.js → inputs-DuP7P3ka.js} +1 -1
- package/dist/cdn/kashi-elements.js +1 -1
- package/dist/cdn/{map-BHuusa4t.js → map-w5X2fTQI.js} +1 -1
- package/dist/cdn/{menu-DhrqyLgy.js → menu-Cga7QOfs.js} +1 -1
- package/dist/cdn/{nav-D_w5BV_K.js → nav-CwPZ20z-.js} +1 -1
- package/dist/cdn/{overlay-DX9un2xm.js → overlay-BDzwsBN5.js} +1 -1
- package/dist/cdn/{pickers-BFLpUruK.js → pickers-BOpaZ-7q.js} +1 -1
- package/dist/cdn/{table-DPQfDMs-.js → table-BdF43L5q.js} +1 -1
- package/dist/cdn/time-DXw4vOne.js +1 -0
- package/dist/cdn.js +2 -0
- package/dist/index.d.ts +8 -2
- package/dist/index.js +4 -1
- package/package.json +4 -4
- package/dist/cdn/base-Cmq7zeIG.js +0 -1
- package/dist/cdn/calendar-Dm3fq2bs.js +0 -16
- package/dist/cdn/forms-C-effPKn.js +0 -1
package/README.md
CHANGED
|
@@ -29,8 +29,9 @@ import "@kashi-ui/elements";
|
|
|
29
29
|
## عنصرها
|
|
30
30
|
|
|
31
31
|
- **فیلدهای فارسی:** `kashi-national-id`، `kashi-legal-id`، `kashi-mobile`، `kashi-landline`، `kashi-card-number`، `kashi-iban`، `kashi-postal-code`، `kashi-price`
|
|
32
|
+
- **فیلد متنی و اطلاعیه:** `kashi-input` (پاک کردن، نمایش رمز، شمارنده)، `kashi-textarea`، `kashi-banner` (با دکمهٔ بستن)
|
|
32
33
|
- **فیلدهای دیگر:** `kashi-time-input`، `kashi-plate-input`، `kashi-persian-textarea`، `kashi-keyboard-fix`، `kashi-password-strength`، `kashi-inline-edit`
|
|
33
|
-
- **انتخاب:** `kashi-date-picker`، `kashi-calendar`، `kashi-date-range-picker`، `kashi-month-picker`، `kashi-otp`، `kashi-combobox`، `kashi-multi-select`، `kashi-province-select`، `kashi-city-select`، `kashi-tag-input`، `kashi-chip-group`، `kashi-number-input`، `kashi-slider`، `kashi-rating`، `kashi-color-picker`، `kashi-file-upload`
|
|
34
|
+
- **انتخاب:** `kashi-date-picker`، `kashi-calendar`، `kashi-date-range-picker`، `kashi-month-picker`، `kashi-appointment-picker`، `kashi-otp`، `kashi-combobox`، `kashi-multi-select`، `kashi-province-select`، `kashi-city-select`، `kashi-tag-input`، `kashi-chip-group`، `kashi-number-input`، `kashi-slider`، `kashi-rating`، `kashi-color-picker`، `kashi-file-upload`
|
|
34
35
|
- **نشانی و نقشه:** `kashi-address-form`، `kashi-iran-map`
|
|
35
36
|
- **داده و نمودار:** `kashi-data-table`، `kashi-pagination`، `kashi-bar-chart`، `kashi-line-chart`، `kashi-pie-chart`، `kashi-bar-list`، `kashi-proportion-bar`، `kashi-sparkline`، `kashi-activity-heatmap`
|
|
36
37
|
- **نمایش:** `kashi-money`، `kashi-date`، `kashi-relative-time`، `kashi-countdown`
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { HTMLBase } from "./base.js";
|
|
2
|
+
export interface AppointmentSlot {
|
|
3
|
+
time: string;
|
|
4
|
+
available?: boolean;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* انتخاب نوبت: روزهای شمسی در یک ردیف (با تعداد نوبت خالی) و ساعتهای همان روز در گروههای صبح، بعدازظهر و عصر.
|
|
8
|
+
* ساعت کاری با from، to و step؛ نوبتهای پر با booked (JSON) یا همهٔ ساعتها با element.getSlots.
|
|
9
|
+
* با name دو فیلد name_date (YYYY-MM-DD) و name_time در فرم ارسال میشوند.
|
|
10
|
+
*/
|
|
11
|
+
export declare class KashiAppointmentPicker extends HTMLBase {
|
|
12
|
+
static observedAttributes: string[];
|
|
13
|
+
/** ساعتهای هر روز بهطور کامل: (date) => ["09:00", { time: "09:30", available: false }] */
|
|
14
|
+
getSlots?: (date: Date) => (string | AppointmentSlot)[];
|
|
15
|
+
/** روزهای تعطیل دلخواه؛ پیشفرض جمعهها و تعطیلات رسمی ثابت */
|
|
16
|
+
isClosed?: (date: Date) => boolean;
|
|
17
|
+
private selected;
|
|
18
|
+
private active;
|
|
19
|
+
private valueSet;
|
|
20
|
+
private rendered;
|
|
21
|
+
private timer;
|
|
22
|
+
private key;
|
|
23
|
+
connectedCallback(): void;
|
|
24
|
+
disconnectedCallback(): void;
|
|
25
|
+
attributeChangedCallback(name: string, old: string | null, next: string | null): void;
|
|
26
|
+
/** «2026-11-04 10:30» */
|
|
27
|
+
private parse;
|
|
28
|
+
/** نوبت انتخابشده: { date, time } */
|
|
29
|
+
get value(): {
|
|
30
|
+
date: Date;
|
|
31
|
+
time: string;
|
|
32
|
+
} | null;
|
|
33
|
+
set value(v: {
|
|
34
|
+
date: Date;
|
|
35
|
+
time: string;
|
|
36
|
+
} | null);
|
|
37
|
+
private list;
|
|
38
|
+
private render;
|
|
39
|
+
private roving;
|
|
40
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { JALALI_MONTHS, WEEKDAYS, formatJalali, formatTime, fromISODate, getIranHoliday, parseTime, toFaDigits, toISODate, toJalali } from "@kashi-ui/persian";
|
|
2
|
+
import { HTMLBase, define, emit, esc, uid } from "./base.js";
|
|
3
|
+
const PERIODS = [["صبح", 12], ["بعدازظهر", 17], ["عصر", 24]];
|
|
4
|
+
const day = (d, add = 0) => new Date(d.getFullYear(), d.getMonth(), d.getDate() + add);
|
|
5
|
+
const same = (a, b) => a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
|
|
6
|
+
const minutes = (t) => {
|
|
7
|
+
const p = parseTime(t);
|
|
8
|
+
return p ? p.hours * 60 + p.minutes : 0;
|
|
9
|
+
};
|
|
10
|
+
const CLOCK = '<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="10" cy="10" r="7"/><path d="M10 6v4l2.5 2"/></svg>';
|
|
11
|
+
/**
|
|
12
|
+
* انتخاب نوبت: روزهای شمسی در یک ردیف (با تعداد نوبت خالی) و ساعتهای همان روز در گروههای صبح، بعدازظهر و عصر.
|
|
13
|
+
* ساعت کاری با from، to و step؛ نوبتهای پر با booked (JSON) یا همهٔ ساعتها با element.getSlots.
|
|
14
|
+
* با name دو فیلد name_date (YYYY-MM-DD) و name_time در فرم ارسال میشوند.
|
|
15
|
+
*/
|
|
16
|
+
export class KashiAppointmentPicker extends HTMLBase {
|
|
17
|
+
constructor() {
|
|
18
|
+
super(...arguments);
|
|
19
|
+
this.selected = null;
|
|
20
|
+
this.active = -1;
|
|
21
|
+
this.valueSet = false;
|
|
22
|
+
this.rendered = false;
|
|
23
|
+
this.key = uid("kashi-appt");
|
|
24
|
+
}
|
|
25
|
+
connectedCallback() {
|
|
26
|
+
if (!this.rendered && !this.valueSet)
|
|
27
|
+
this.selected = this.parse(this.getAttribute("value"));
|
|
28
|
+
this.render();
|
|
29
|
+
// ساعتهای گذشتهٔ امروز هر دقیقه پر میشوند
|
|
30
|
+
clearInterval(this.timer);
|
|
31
|
+
this.timer = setInterval(() => !this.contains(document.activeElement) && this.render(), 60000);
|
|
32
|
+
}
|
|
33
|
+
disconnectedCallback() {
|
|
34
|
+
clearInterval(this.timer);
|
|
35
|
+
}
|
|
36
|
+
attributeChangedCallback(name, old, next) {
|
|
37
|
+
if (!this.rendered || old === next)
|
|
38
|
+
return;
|
|
39
|
+
if (name === "value")
|
|
40
|
+
this.selected = this.parse(next);
|
|
41
|
+
this.render();
|
|
42
|
+
}
|
|
43
|
+
/** «2026-11-04 10:30» */
|
|
44
|
+
parse(v) {
|
|
45
|
+
const m = (v ?? "").trim().match(/^(\d{4}-\d{2}-\d{2})[ T](\d{1,2}:\d{2})$/);
|
|
46
|
+
const d = m ? fromISODate(m[1]) : null;
|
|
47
|
+
return d && m ? { date: d, time: formatTime(parseTime(m[2])) } : null;
|
|
48
|
+
}
|
|
49
|
+
/** نوبت انتخابشده: { date, time } */
|
|
50
|
+
get value() {
|
|
51
|
+
return this.selected ? { ...this.selected } : null;
|
|
52
|
+
}
|
|
53
|
+
set value(v) {
|
|
54
|
+
this.valueSet = true;
|
|
55
|
+
this.selected = v ? { date: day(v.date), time: v.time } : null;
|
|
56
|
+
if (this.rendered)
|
|
57
|
+
this.render();
|
|
58
|
+
}
|
|
59
|
+
list() {
|
|
60
|
+
const now = new Date();
|
|
61
|
+
const start = fromISODate(this.getAttribute("start") ?? "") ?? now;
|
|
62
|
+
const count = Math.max(1, Number(this.getAttribute("days")) || 7);
|
|
63
|
+
let booked = {};
|
|
64
|
+
try {
|
|
65
|
+
booked = JSON.parse(this.getAttribute("booked") ?? "{}");
|
|
66
|
+
}
|
|
67
|
+
catch { /* قالب نادرست نادیده گرفته میشود */ }
|
|
68
|
+
const from = this.getAttribute("from") ?? "09:00", to = this.getAttribute("to") ?? "17:00";
|
|
69
|
+
const step = Number(this.getAttribute("step")) || 30;
|
|
70
|
+
const closedAttr = this.getAttribute("closed") !== "false";
|
|
71
|
+
return Array.from({ length: count }, (_, i) => {
|
|
72
|
+
const d = day(start, i);
|
|
73
|
+
const closed = this.isClosed ? this.isClosed(d) : closedAttr && !!getIranHoliday(d);
|
|
74
|
+
let slots = [];
|
|
75
|
+
if (!closed) {
|
|
76
|
+
if (this.getSlots)
|
|
77
|
+
slots = this.getSlots(d).map((s) => (typeof s === "string" ? { time: s, available: true } : { time: s.time, available: s.available ?? true }));
|
|
78
|
+
else {
|
|
79
|
+
const taken = new Set(booked[toISODate(d)] ?? []);
|
|
80
|
+
for (let m = minutes(from); m + step <= minutes(to); m += step) {
|
|
81
|
+
const time = formatTime({ hours: Math.floor(m / 60), minutes: m % 60 });
|
|
82
|
+
slots.push({ time, available: !taken.has(time) });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (same(d, now)) {
|
|
86
|
+
const nm = now.getHours() * 60 + now.getMinutes();
|
|
87
|
+
slots = slots.map((s) => (minutes(s.time) <= nm ? { ...s, available: false } : s));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return { date: d, closed, slots, free: slots.filter((s) => s.available).length };
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
render(focus = null) {
|
|
94
|
+
const now = new Date();
|
|
95
|
+
const fa = this.getAttribute("digits") !== "latin";
|
|
96
|
+
const n = (v) => (fa ? toFaDigits(String(v)) : String(v));
|
|
97
|
+
const digits = fa ? "fa" : "latin";
|
|
98
|
+
const list = this.list();
|
|
99
|
+
if (this.active < 0 || this.active >= list.length) {
|
|
100
|
+
const i = this.selected ? list.findIndex((x) => same(x.date, this.selected.date)) : -1;
|
|
101
|
+
this.active = i >= 0 ? i : Math.max(0, list.findIndex((x) => x.free > 0));
|
|
102
|
+
}
|
|
103
|
+
const cur = list[this.active];
|
|
104
|
+
const label = this.getAttribute("label") ?? "انتخاب نوبت";
|
|
105
|
+
const wd = (d) => (same(d, now) ? "امروز" : same(d, day(now, 1)) ? "فردا" : WEEKDAYS[(d.getDay() + 1) % 7]);
|
|
106
|
+
const daysHtml = list.map((x, i) => {
|
|
107
|
+
const { jm, jd } = toJalali(x.date);
|
|
108
|
+
const status = x.closed ? "تعطیل" : x.free ? `${n(x.free)} نوبت خالی` : "نوبت خالی ندارد";
|
|
109
|
+
return `<button type="button" role="radio" data-i="${i}" class="k-appt__day" aria-checked="${i === this.active}" tabindex="${i === this.active ? 0 : -1}"${x.closed ? " disabled" : ""} aria-label="${esc(`${formatJalali(x.date, { style: "full", digits })}، ${status}`)}"><span class="k-appt__weekday" aria-hidden="true">${wd(x.date)}</span><span class="k-appt__date" aria-hidden="true">${n(jd)}</span><span class="k-appt__month" aria-hidden="true">${JALALI_MONTHS[jm - 1]}</span><span class="k-appt__free" aria-hidden="true">${x.closed ? "تعطیل" : x.free ? `${n(x.free)} نوبت` : "پر"}</span></button>`;
|
|
110
|
+
}).join("");
|
|
111
|
+
const selDay = !!this.selected && same(this.selected.date, cur.date);
|
|
112
|
+
const focusTime = selDay ? this.selected.time : cur.slots.find((s) => s.available)?.time;
|
|
113
|
+
const slotsHtml = cur.closed || !cur.free
|
|
114
|
+
? `<p class="k-appt__empty">${cur.closed ? "این روز تعطیل است." : "نوبت خالی برای این روز نمانده است؛ روز دیگری را انتخاب کنید."}</p>`
|
|
115
|
+
: PERIODS.map(([title, until], k) => {
|
|
116
|
+
const slots = cur.slots.filter((s) => minutes(s.time) / 60 < until && minutes(s.time) / 60 >= (k ? PERIODS[k - 1][1] : 0));
|
|
117
|
+
if (!slots.length)
|
|
118
|
+
return "";
|
|
119
|
+
const gid = `${this.key}-p${k}`;
|
|
120
|
+
return `<div class="k-appt__group"><p class="k-appt__period" id="${gid}">${title}</p><div class="k-appt__times" role="radiogroup" aria-labelledby="${gid}">${slots.map((s) => `<button type="button" role="radio" class="k-appt__time" data-time="${s.time}" aria-checked="${selDay && this.selected.time === s.time}" tabindex="${s.time === focusTime ? 0 : -1}"${s.available ? "" : " disabled"} aria-label="ساعت ${n(s.time)}${s.available ? "" : "، پر"}">${n(s.time)}</button>`).join("")}</div></div>`;
|
|
121
|
+
}).join("");
|
|
122
|
+
const name = this.getAttribute("name");
|
|
123
|
+
const sel = this.selected;
|
|
124
|
+
this.innerHTML = `<div class="k-appt" role="group" aria-labelledby="${this.key}-label"><p class="k-label" id="${this.key}-label" style="margin: 0">${esc(label)}</p><div class="k-appt__days" role="radiogroup" aria-label="روز">${daysHtml}</div><div class="k-appt__slots">${slotsHtml}</div><p class="k-appt__summary" aria-live="polite">${sel ? `${CLOCK}<span>نوبت شما: <strong>${formatJalali(sel.date, { style: "full", digits })}، ساعت ${n(sel.time)}</strong></span>` : ""}</p>${name ? `<input type="hidden" name="${esc(`${name}_date`)}" value="${sel ? toISODate(sel.date) : ""}"><input type="hidden" name="${esc(`${name}_time`)}" value="${sel?.time ?? ""}">` : ""}</div>`;
|
|
125
|
+
this.rendered = true;
|
|
126
|
+
const days = this.querySelector(".k-appt__days");
|
|
127
|
+
const times = this.querySelector(".k-appt__slots");
|
|
128
|
+
days.addEventListener("click", (e) => {
|
|
129
|
+
const b = e.target.closest(".k-appt__day");
|
|
130
|
+
if (!b || b.disabled)
|
|
131
|
+
return;
|
|
132
|
+
this.active = Number(b.dataset.i);
|
|
133
|
+
this.render("day");
|
|
134
|
+
});
|
|
135
|
+
times.addEventListener("click", (e) => {
|
|
136
|
+
const b = e.target.closest(".k-appt__time");
|
|
137
|
+
if (!b || b.disabled)
|
|
138
|
+
return;
|
|
139
|
+
this.selected = { date: cur.date, time: b.dataset.time };
|
|
140
|
+
this.render("time");
|
|
141
|
+
emit(this, "kashi-change", { date: toISODate(cur.date), time: this.selected.time, value: this.value });
|
|
142
|
+
});
|
|
143
|
+
days.addEventListener("keydown", (e) => this.roving(e, days, (b) => {
|
|
144
|
+
this.active = Number(b.dataset.i);
|
|
145
|
+
this.render("day");
|
|
146
|
+
}));
|
|
147
|
+
times.addEventListener("keydown", (e) => this.roving(e, times));
|
|
148
|
+
if (focus === "day")
|
|
149
|
+
days.querySelector('[tabindex="0"]')?.focus();
|
|
150
|
+
if (focus === "time")
|
|
151
|
+
times.querySelector('[aria-checked="true"]')?.focus();
|
|
152
|
+
if (focus === "day")
|
|
153
|
+
days.querySelector('[tabindex="0"]')?.scrollIntoView?.({ block: "nearest", inline: "nearest" });
|
|
154
|
+
}
|
|
155
|
+
// فوکوس گردشی؛ در RTL فلش چپ یعنی بعدی
|
|
156
|
+
roving(e, box, onMove) {
|
|
157
|
+
const items = [...box.querySelectorAll('[role="radio"]:not(:disabled)')];
|
|
158
|
+
const i = items.indexOf(document.activeElement);
|
|
159
|
+
if (i < 0)
|
|
160
|
+
return;
|
|
161
|
+
const rtl = getComputedStyle(box).direction === "rtl";
|
|
162
|
+
const step = { ArrowLeft: rtl ? 1 : -1, ArrowRight: rtl ? -1 : 1, ArrowDown: 1, ArrowUp: -1 }[e.key];
|
|
163
|
+
const to = step ? (i + step + items.length) % items.length : e.key === "Home" ? 0 : e.key === "End" ? items.length - 1 : -1;
|
|
164
|
+
if (to < 0)
|
|
165
|
+
return;
|
|
166
|
+
e.preventDefault();
|
|
167
|
+
if (onMove)
|
|
168
|
+
onMove(items[to]);
|
|
169
|
+
else
|
|
170
|
+
items[to].focus();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
KashiAppointmentPicker.observedAttributes = ["start", "days", "from", "to", "step", "booked", "closed", "label", "name", "digits", "value"];
|
|
174
|
+
define("kashi-appointment-picker", KashiAppointmentPicker, "block");
|
package/dist/base.js
CHANGED
|
@@ -26,6 +26,8 @@ export function define(tag, ctor, display = "inline") {
|
|
|
26
26
|
const rules = [
|
|
27
27
|
BLOCK_TAGS.length && `:where(${BLOCK_TAGS.join(",")}) { display: block; }`,
|
|
28
28
|
CONTENTS_TAGS.length && `:where(${CONTENTS_TAGS.join(",")}) { display: contents; }`,
|
|
29
|
+
// display بالا قاعدهٔ hidden مرورگر را از کار میاندازد؛ عنصر پنهانشده باید پنهان بماند
|
|
30
|
+
`:where(${[...BLOCK_TAGS, ...CONTENTS_TAGS].join(",")})[hidden] { display: none; }`,
|
|
29
31
|
].filter(Boolean);
|
|
30
32
|
style.textContent = `@layer kashi.elements { ${rules.join(" ")} }`;
|
|
31
33
|
}
|
package/dist/basic.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { HTMLBase } from "./base.js";
|
|
2
|
+
/**
|
|
3
|
+
* فیلد متنی کاشی با برچسب، راهنما و خطا؛ با clearable دکمهٔ پاک کردن، با type="password" دکمهٔ نمایش رمز،
|
|
4
|
+
* با show-count شمارندهٔ نویسه («۱۲ / ۱۰۰» با maxlength) و با loading اسپینر. input واقعی داخل عنصر است و با name مستقیم در فرم ارسال میشود.
|
|
5
|
+
*/
|
|
6
|
+
export declare class KashiInput extends HTMLBase {
|
|
7
|
+
static observedAttributes: string[];
|
|
8
|
+
protected multiline: boolean;
|
|
9
|
+
protected key: string;
|
|
10
|
+
protected control: HTMLInputElement | HTMLTextAreaElement;
|
|
11
|
+
private text;
|
|
12
|
+
private revealed;
|
|
13
|
+
private rendered;
|
|
14
|
+
private valueSet;
|
|
15
|
+
connectedCallback(): void;
|
|
16
|
+
attributeChangedCallback(name: string, old: string | null, next: string | null): void;
|
|
17
|
+
get value(): string;
|
|
18
|
+
set value(v: string);
|
|
19
|
+
focus(options?: FocusOptions): void;
|
|
20
|
+
protected render(): void;
|
|
21
|
+
/** دکمههای انتهای فیلد، شمارنده و پیامها */
|
|
22
|
+
private paint;
|
|
23
|
+
}
|
|
24
|
+
/** متن چندخطی کاشی؛ همان ویژگیهای kashi-input بهاضافهٔ rows و autoresize (ارتفاع خودکار) */
|
|
25
|
+
export declare class KashiTextarea extends KashiInput {
|
|
26
|
+
static observedAttributes: string[];
|
|
27
|
+
protected multiline: boolean;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* نوار اطلاعیهٔ بالای صفحه با tone (brand، warning یا error) و دکمهٔ بستن (dismissible).
|
|
31
|
+
* عنصرهای slot="actions" کنار متن و slot="icon" پیش از آن میآیند؛ با remember="کلید" بستن در همین مرورگر یادش میماند.
|
|
32
|
+
*/
|
|
33
|
+
export declare class KashiBanner extends HTMLBase {
|
|
34
|
+
static observedAttributes: string[];
|
|
35
|
+
private box;
|
|
36
|
+
connectedCallback(): void;
|
|
37
|
+
attributeChangedCallback(): void;
|
|
38
|
+
private read;
|
|
39
|
+
private build;
|
|
40
|
+
private decorate;
|
|
41
|
+
/** بستن اطلاعیه؛ با preventDefault روی رویداد kashi-close جلوی بستن گرفته میشود */
|
|
42
|
+
close(): void;
|
|
43
|
+
}
|
package/dist/basic.js
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { toFaDigits } from "@kashi-ui/persian";
|
|
2
|
+
import { HTMLBase, ICONS, afterParse, bool, define, emit, esc, fieldHTML, setMessages, uid } from "./base.js";
|
|
3
|
+
const EYE = (off) => `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M1.8 10S4.8 4.5 10 4.5 18.2 10 18.2 10 15.2 15.5 10 15.5 1.8 10 1.8 10Z"/><circle cx="10" cy="10" r="2.5"/>${off ? '<path d="m3 3 14 14"/>' : ""}</svg>`;
|
|
4
|
+
const X = '<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" aria-hidden="true"><path d="m6 6 8 8M14 6l-8 8"/></svg>';
|
|
5
|
+
/** ویژگیهایی که مستقیم روی input یا textarea داخلی میروند */
|
|
6
|
+
const PASS = ["name", "placeholder", "autocomplete", "inputmode", "maxlength", "minlength", "pattern", "min", "max", "step", "dir", "spellcheck"];
|
|
7
|
+
/**
|
|
8
|
+
* فیلد متنی کاشی با برچسب، راهنما و خطا؛ با clearable دکمهٔ پاک کردن، با type="password" دکمهٔ نمایش رمز،
|
|
9
|
+
* با show-count شمارندهٔ نویسه («۱۲ / ۱۰۰» با maxlength) و با loading اسپینر. input واقعی داخل عنصر است و با name مستقیم در فرم ارسال میشود.
|
|
10
|
+
*/
|
|
11
|
+
export class KashiInput extends HTMLBase {
|
|
12
|
+
constructor() {
|
|
13
|
+
super(...arguments);
|
|
14
|
+
this.multiline = false;
|
|
15
|
+
this.key = uid("kashi-input");
|
|
16
|
+
this.text = "";
|
|
17
|
+
this.revealed = false;
|
|
18
|
+
this.rendered = false;
|
|
19
|
+
this.valueSet = false;
|
|
20
|
+
}
|
|
21
|
+
connectedCallback() {
|
|
22
|
+
if (!this.rendered && !this.valueSet)
|
|
23
|
+
this.text = this.getAttribute("value") ?? "";
|
|
24
|
+
this.render();
|
|
25
|
+
}
|
|
26
|
+
attributeChangedCallback(name, old, next) {
|
|
27
|
+
if (!this.rendered || old === next)
|
|
28
|
+
return;
|
|
29
|
+
if (name === "value")
|
|
30
|
+
this.value = next ?? "";
|
|
31
|
+
else {
|
|
32
|
+
this.text = this.control.value;
|
|
33
|
+
this.render();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
get value() {
|
|
37
|
+
return this.rendered ? this.control.value : this.text;
|
|
38
|
+
}
|
|
39
|
+
set value(v) {
|
|
40
|
+
this.valueSet = true;
|
|
41
|
+
this.text = String(v ?? "");
|
|
42
|
+
if (this.rendered) {
|
|
43
|
+
this.control.value = this.text;
|
|
44
|
+
this.paint();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
focus(options) {
|
|
48
|
+
this.control?.focus(options);
|
|
49
|
+
}
|
|
50
|
+
render() {
|
|
51
|
+
const id = this.key;
|
|
52
|
+
const ariaLabel = this.getAttribute("aria-label");
|
|
53
|
+
const label = this.getAttribute("label");
|
|
54
|
+
const type = this.getAttribute("type") ?? "text";
|
|
55
|
+
const size = this.getAttribute("size");
|
|
56
|
+
const disabled = this.hasAttribute("disabled");
|
|
57
|
+
const attrs = PASS.map((a) => (this.hasAttribute(a) ? ` ${a}="${esc(this.getAttribute(a))}"` : "")).join("");
|
|
58
|
+
const flags = `${this.hasAttribute("required") ? " required" : ""}${disabled ? " disabled" : ""}${this.hasAttribute("readonly") ? " readonly" : ""}${!label && ariaLabel ? ` aria-label="${esc(ariaLabel)}"` : ""}`;
|
|
59
|
+
const control = this.multiline
|
|
60
|
+
? `<textarea id="${id}" class="k-input__control" rows="${Number(this.getAttribute("rows")) || 4}"${attrs}${flags}></textarea>`
|
|
61
|
+
: `<input id="${id}" class="k-input__control" type="${esc(type === "password" && this.revealed ? "text" : type)}"${attrs}${flags}>`;
|
|
62
|
+
const box = `<div class="k-input${this.multiline ? " k-input--textarea" : ""}${bool(this, "autoresize") ? " k-input--autoresize" : ""}${size === "sm" || size === "lg" ? ` k-input--${size}` : ""}" data-size="${size ?? "md"}"${disabled ? ' data-state="disabled"' : ""}>${control}<span class="k-input__end"></span></div>`;
|
|
63
|
+
const optional = this.getAttribute("optional");
|
|
64
|
+
this.querySelector(":scope > .k-field")?.remove();
|
|
65
|
+
this.insertAdjacentHTML("afterbegin", fieldHTML({ id, label, required: this.hasAttribute("required"), optional: optional === null ? null : optional || "اختیاری", control: box }));
|
|
66
|
+
this.control = this.querySelector(".k-input__control");
|
|
67
|
+
this.control.value = this.text;
|
|
68
|
+
this.control.addEventListener("input", () => {
|
|
69
|
+
this.paint();
|
|
70
|
+
emit(this, "kashi-change", { value: this.control.value });
|
|
71
|
+
});
|
|
72
|
+
this.querySelector(".k-input__end").addEventListener("click", (e) => {
|
|
73
|
+
const b = e.target.closest("button");
|
|
74
|
+
if (!b)
|
|
75
|
+
return;
|
|
76
|
+
if (b.dataset.act === "clear") {
|
|
77
|
+
this.control.value = "";
|
|
78
|
+
this.control.dispatchEvent(new Event("input", { bubbles: true }));
|
|
79
|
+
this.control.focus();
|
|
80
|
+
emit(this, "kashi-clear", {});
|
|
81
|
+
}
|
|
82
|
+
else if (b.dataset.act === "reveal") {
|
|
83
|
+
this.revealed = !this.revealed;
|
|
84
|
+
this.control.type = this.revealed ? "text" : "password";
|
|
85
|
+
this.paint();
|
|
86
|
+
b.focus();
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
this.rendered = true;
|
|
90
|
+
this.paint();
|
|
91
|
+
}
|
|
92
|
+
/** دکمههای انتهای فیلد، شمارنده و پیامها */
|
|
93
|
+
paint() {
|
|
94
|
+
const el = this.control;
|
|
95
|
+
const len = el.value.length;
|
|
96
|
+
const off = this.hasAttribute("disabled") || this.hasAttribute("readonly");
|
|
97
|
+
const end = this.querySelector(".k-input__end");
|
|
98
|
+
const password = !this.multiline && this.getAttribute("type") === "password" && this.getAttribute("revealable") !== "false";
|
|
99
|
+
end.innerHTML = [
|
|
100
|
+
bool(this, "loading") ? '<span class="k-input__addon"><span class="k-spinner k-spinner--sm" role="status" aria-label="در حال بررسی"></span></span>' : "",
|
|
101
|
+
bool(this, "clearable") && len > 0 && !off ? `<button type="button" class="k-input__action" data-act="clear" aria-label="پاک کردن">${X}</button>` : "",
|
|
102
|
+
password ? `<button type="button" class="k-input__action" data-act="reveal" aria-label="${this.revealed ? "پنهان کردن رمز" : "نمایش رمز"}" aria-pressed="${this.revealed}">${EYE(this.revealed)}</button>` : "",
|
|
103
|
+
].join("");
|
|
104
|
+
end.style.display = end.innerHTML ? "contents" : "none";
|
|
105
|
+
const field = this.querySelector(":scope > .k-field");
|
|
106
|
+
const countId = `${this.key}-count`;
|
|
107
|
+
setMessages(field, el, this.key, this.getAttribute("error"), this.getAttribute("hint"), bool(this, "show-count") ? countId : null);
|
|
108
|
+
if (bool(this, "show-count")) {
|
|
109
|
+
const max = Number(this.getAttribute("maxlength")) || 0;
|
|
110
|
+
let row = field.querySelector(":scope > .k-field__hint-row");
|
|
111
|
+
if (!row) {
|
|
112
|
+
row = document.createElement("div");
|
|
113
|
+
row.className = "k-field__hint-row";
|
|
114
|
+
row.innerHTML = '<div class="k-field__hints"></div>';
|
|
115
|
+
field.append(row);
|
|
116
|
+
}
|
|
117
|
+
row.insertAdjacentHTML("beforeend", `<span class="k-counter" id="${countId}"${max && len > max ? ' data-over=""' : ""}>${max ? `${toFaDigits(len)} / ${toFaDigits(max)}` : toFaDigits(len)}</span>`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
KashiInput.observedAttributes = ["label", "hint", "error", "type", "required", "disabled", "readonly", "clearable", "revealable", "show-count", "loading", "size", "optional", "aria-label", "value", ...PASS];
|
|
122
|
+
/** متن چندخطی کاشی؛ همان ویژگیهای kashi-input بهاضافهٔ rows و autoresize (ارتفاع خودکار) */
|
|
123
|
+
export class KashiTextarea extends KashiInput {
|
|
124
|
+
constructor() {
|
|
125
|
+
super(...arguments);
|
|
126
|
+
this.multiline = true;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
KashiTextarea.observedAttributes = [...KashiInput.observedAttributes, "rows", "autoresize"];
|
|
130
|
+
/**
|
|
131
|
+
* نوار اطلاعیهٔ بالای صفحه با tone (brand، warning یا error) و دکمهٔ بستن (dismissible).
|
|
132
|
+
* عنصرهای slot="actions" کنار متن و slot="icon" پیش از آن میآیند؛ با remember="کلید" بستن در همین مرورگر یادش میماند.
|
|
133
|
+
*/
|
|
134
|
+
export class KashiBanner extends HTMLBase {
|
|
135
|
+
constructor() {
|
|
136
|
+
super(...arguments);
|
|
137
|
+
this.box = null;
|
|
138
|
+
}
|
|
139
|
+
connectedCallback() {
|
|
140
|
+
afterParse(() => {
|
|
141
|
+
if (!this.isConnected || this.box)
|
|
142
|
+
return;
|
|
143
|
+
const key = this.getAttribute("remember");
|
|
144
|
+
if (key && this.read(key)) {
|
|
145
|
+
this.hidden = true;
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
this.build();
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
attributeChangedCallback() {
|
|
152
|
+
if (this.box)
|
|
153
|
+
this.decorate();
|
|
154
|
+
}
|
|
155
|
+
read(key) {
|
|
156
|
+
try {
|
|
157
|
+
return localStorage.getItem(`kashi-banner:${key}`) === "closed";
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
build() {
|
|
164
|
+
const icon = this.querySelector(":scope > [slot='icon']");
|
|
165
|
+
const actions = [...this.querySelectorAll(":scope > [slot='actions']")];
|
|
166
|
+
const rest = [...this.childNodes].filter((n) => n !== icon && !actions.includes(n));
|
|
167
|
+
const box = document.createElement("div");
|
|
168
|
+
box.setAttribute("role", "region");
|
|
169
|
+
if (icon) {
|
|
170
|
+
icon.removeAttribute("slot");
|
|
171
|
+
const s = document.createElement("span");
|
|
172
|
+
s.className = "k-banner__icon";
|
|
173
|
+
s.setAttribute("aria-hidden", "true");
|
|
174
|
+
s.append(icon);
|
|
175
|
+
box.append(s);
|
|
176
|
+
}
|
|
177
|
+
const text = document.createElement("p");
|
|
178
|
+
text.className = "k-banner__text";
|
|
179
|
+
text.append(...rest);
|
|
180
|
+
box.append(text);
|
|
181
|
+
if (actions.length) {
|
|
182
|
+
const a = document.createElement("div");
|
|
183
|
+
a.className = "k-banner__actions";
|
|
184
|
+
for (const el of actions) {
|
|
185
|
+
el.removeAttribute("slot");
|
|
186
|
+
a.append(el);
|
|
187
|
+
}
|
|
188
|
+
box.append(a);
|
|
189
|
+
}
|
|
190
|
+
this.replaceChildren(box);
|
|
191
|
+
this.box = box;
|
|
192
|
+
this.decorate();
|
|
193
|
+
}
|
|
194
|
+
decorate() {
|
|
195
|
+
const box = this.box;
|
|
196
|
+
const tone = this.getAttribute("tone");
|
|
197
|
+
box.className = `k-banner${tone && tone !== "neutral" ? ` k-banner--${tone}` : ""}${bool(this, "flush") ? " k-banner--flush" : ""}`;
|
|
198
|
+
box.setAttribute("aria-label", this.getAttribute("label") ?? "اطلاعیه");
|
|
199
|
+
box.querySelector(".k-banner__close")?.remove();
|
|
200
|
+
if (bool(this, "dismissible")) {
|
|
201
|
+
box.insertAdjacentHTML("beforeend", `<button type="button" class="k-btn k-btn--tertiary k-btn--icon k-btn--sm k-banner__close" aria-label="${esc(this.getAttribute("close-label") ?? "بستن اطلاعیه")}">${ICONS.close}</button>`);
|
|
202
|
+
box.querySelector(".k-banner__close").addEventListener("click", () => this.close());
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/** بستن اطلاعیه؛ با preventDefault روی رویداد kashi-close جلوی بستن گرفته میشود */
|
|
206
|
+
close() {
|
|
207
|
+
const ev = new CustomEvent("kashi-close", { bubbles: true, cancelable: true, detail: {} });
|
|
208
|
+
if (!this.dispatchEvent(ev))
|
|
209
|
+
return;
|
|
210
|
+
this.hidden = true;
|
|
211
|
+
const key = this.getAttribute("remember");
|
|
212
|
+
if (key) {
|
|
213
|
+
try {
|
|
214
|
+
localStorage.setItem(`kashi-banner:${key}`, "closed");
|
|
215
|
+
}
|
|
216
|
+
catch { /* حالت خصوصی */ }
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
KashiBanner.observedAttributes = ["tone", "flush", "label", "dismissible", "close-label"];
|
|
221
|
+
define("kashi-input", KashiInput, "block");
|
|
222
|
+
define("kashi-textarea", KashiTextarea, "block");
|
|
223
|
+
define("kashi-banner", KashiBanner, "block");
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as e,d as t,o as n,s as r,t as i}from"./base-DfzNTdQk.js";import{i as a}from"./digits-DE1YGT94.js";import{a as o,d as s,i as c,n as l,t as u,u as d}from"./jalali-BiM5Kfep.js";import{n as f,t as p}from"./time-DXw4vOne.js";import{t as m}from"./holidays-BwsAEOI1.js";const h=[[`صبح`,12],[`بعدازظهر`,17],[`عصر`,24]],g=(e,t=0)=>new Date(e.getFullYear(),e.getMonth(),e.getDate()+t),_=(e,t)=>e.getFullYear()===t.getFullYear()&&e.getMonth()===t.getMonth()&&e.getDate()===t.getDate(),v=e=>{let t=f(e);return t?t.hours*60+t.minutes:0};var y=class extends i{constructor(){super(...arguments),this.selected=null,this.active=-1,this.valueSet=!1,this.rendered=!1,this.key=t(`kashi-appt`)}connectedCallback(){!this.rendered&&!this.valueSet&&(this.selected=this.parse(this.getAttribute(`value`))),this.render(),clearInterval(this.timer),this.timer=setInterval(()=>!this.contains(document.activeElement)&&this.render(),6e4)}disconnectedCallback(){clearInterval(this.timer)}attributeChangedCallback(e,t,n){this.rendered&&t!==n&&(e===`value`&&(this.selected=this.parse(n)),this.render())}parse(e){let t=(e??``).trim().match(/^(\d{4}-\d{2}-\d{2})[ T](\d{1,2}:\d{2})$/),n=t?o(t[1]):null;return n&&t?{date:n,time:p(f(t[2]))}:null}get value(){return this.selected?{...this.selected}:null}set value(e){this.valueSet=!0,this.selected=e?{date:g(e.date),time:e.time}:null,this.rendered&&this.render()}list(){let e=/* @__PURE__ */ new Date,t=o(this.getAttribute(`start`)??``)??e,n=Math.max(1,Number(this.getAttribute(`days`))||7),r={};try{r=JSON.parse(this.getAttribute(`booked`)??`{}`)}catch{}let i=this.getAttribute(`from`)??`09:00`,a=this.getAttribute(`to`)??`17:00`,s=Number(this.getAttribute(`step`))||30,c=this.getAttribute(`closed`)!==`false`;return Array.from({length:n},(n,o)=>{let l=g(t,o),u=this.isClosed?this.isClosed(l):c&&!!m(l),f=[];if(!u){if(this.getSlots)f=this.getSlots(l).map(e=>typeof e==`string`?{time:e,available:!0}:{time:e.time,available:e.available??!0});else{let e=new Set(r[d(l)]??[]);for(let t=v(i);t+s<=v(a);t+=s){let n=p({hours:Math.floor(t/60),minutes:t%60});f.push({time:n,available:!e.has(n)})}}if(_(l,e)){let t=e.getHours()*60+e.getMinutes();f=f.map(e=>v(e.time)<=t?{...e,available:!1}:e)}}return{date:l,closed:u,slots:f,free:f.filter(e=>e.available).length}})}render(e=null){let t=/* @__PURE__ */ new Date,i=this.getAttribute(`digits`)!==`latin`,o=e=>i?a(String(e)):String(e),f=i?`fa`:`latin`,p=this.list();if(this.active<0||this.active>=p.length){let e=this.selected?p.findIndex(e=>_(e.date,this.selected.date)):-1;this.active=e>=0?e:Math.max(0,p.findIndex(e=>e.free>0))}let m=p[this.active],y=this.getAttribute(`label`)??`انتخاب نوبت`,b=e=>_(e,t)?`امروز`:_(e,g(t,1))?`فردا`:l[(e.getDay()+1)%7],x=p.map((e,t)=>{let{jm:n,jd:i}=s(e.date),a=e.closed?`تعطیل`:e.free?`${o(e.free)} نوبت خالی`:`نوبت خالی ندارد`;return`<button type="button" role="radio" data-i="${t}" class="k-appt__day" aria-checked="${t===this.active}" tabindex="${t===this.active?0:-1}"${e.closed?` disabled`:``} aria-label="${r(`${c(e.date,{style:`full`,digits:f})}، ${a}`)}"><span class="k-appt__weekday" aria-hidden="true">${b(e.date)}</span><span class="k-appt__date" aria-hidden="true">${o(i)}</span><span class="k-appt__month" aria-hidden="true">${u[n-1]}</span><span class="k-appt__free" aria-hidden="true">${e.closed?`تعطیل`:e.free?`${o(e.free)} نوبت`:`پر`}</span></button>`}).join(``),S=!!this.selected&&_(this.selected.date,m.date),C=S?this.selected.time:m.slots.find(e=>e.available)?.time,w=m.closed||!m.free?`<p class="k-appt__empty">${m.closed?`این روز تعطیل است.`:`نوبت خالی برای این روز نمانده است؛ روز دیگری را انتخاب کنید.`}</p>`:h.map(([e,t],n)=>{let r=m.slots.filter(e=>v(e.time)/60<t&&v(e.time)/60>=(n?h[n-1][1]:0));if(!r.length)return``;let i=`${this.key}-p${n}`;return`<div class="k-appt__group"><p class="k-appt__period" id="${i}">${e}</p><div class="k-appt__times" role="radiogroup" aria-labelledby="${i}">${r.map(e=>`<button type="button" role="radio" class="k-appt__time" data-time="${e.time}" aria-checked="${S&&this.selected.time===e.time}" tabindex="${e.time===C?0:-1}"${e.available?``:` disabled`} aria-label="ساعت ${o(e.time)}${e.available?``:`، پر`}">${o(e.time)}</button>`).join(``)}</div></div>`}).join(``),T=this.getAttribute(`name`),E=this.selected;this.innerHTML=`<div class="k-appt" role="group" aria-labelledby="${this.key}-label"><p class="k-label" id="${this.key}-label" style="margin: 0">${r(y)}</p><div class="k-appt__days" role="radiogroup" aria-label="روز">${x}</div><div class="k-appt__slots">${w}</div><p class="k-appt__summary" aria-live="polite">${E?`<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="10" cy="10" r="7"/><path d="M10 6v4l2.5 2"/></svg><span>نوبت شما: <strong>${c(E.date,{style:`full`,digits:f})}، ساعت ${o(E.time)}</strong></span>`:``}</p>${T?`<input type="hidden" name="${r(`${T}_date`)}" value="${E?d(E.date):``}"><input type="hidden" name="${r(`${T}_time`)}" value="${E?.time??``}">`:``}</div>`,this.rendered=!0;let D=this.querySelector(`.k-appt__days`),O=this.querySelector(`.k-appt__slots`);D.addEventListener(`click`,e=>{let t=e.target.closest(`.k-appt__day`);t&&!t.disabled&&(this.active=Number(t.dataset.i),this.render(`day`))}),O.addEventListener(`click`,e=>{let t=e.target.closest(`.k-appt__time`);t&&!t.disabled&&(this.selected={date:m.date,time:t.dataset.time},this.render(`time`),n(this,`kashi-change`,{date:d(m.date),time:this.selected.time,value:this.value}))}),D.addEventListener(`keydown`,e=>this.roving(e,D,e=>{this.active=Number(e.dataset.i),this.render(`day`)})),O.addEventListener(`keydown`,e=>this.roving(e,O)),e===`day`&&D.querySelector(`[tabindex="0"]`)?.focus(),e===`time`&&O.querySelector(`[aria-checked="true"]`)?.focus(),e===`day`&&D.querySelector(`[tabindex="0"]`)?.scrollIntoView?.({block:`nearest`,inline:`nearest`})}roving(e,t,n){let r=[...t.querySelectorAll(`[role="radio"]:not(:disabled)`)],i=r.indexOf(document.activeElement);if(i<0)return;let a=getComputedStyle(t).direction===`rtl`,o={ArrowLeft:a?1:-1,ArrowRight:a?-1:1,ArrowDown:1,ArrowUp:-1}[e.key],s=o?(i+o+r.length)%r.length:e.key===`Home`?0:e.key===`End`?r.length-1:-1;s<0||(e.preventDefault(),n?n(r[s]):r[s].focus())}};y.observedAttributes=[`start`,`days`,`from`,`to`,`step`,`booked`,`closed`,`label`,`name`,`digits`,`value`],e(`kashi-appointment-picker`,y,`block`);export{y as KashiAppointmentPicker};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=typeof HTMLElement>`u`?class{}:HTMLElement,t=[],n=[];let r=null;function i(e,i,o=`inline`){if(typeof customElements>`u`||customElements.get(e)||(o===`block`&&t.push(e),o===`contents`&&n.push(e),a(i),customElements.define(e,i),typeof document>`u`))return;r||(r=document.createElement(`style`),r.dataset.kashiElements=``,document.head.prepend(r));let s=[t.length&&`:where(${t.join(`,`)}) { display: block; }`,n.length&&`:where(${n.join(`,`)}) { display: contents; }`,`:where(${[...t,...n].join(`,`)})[hidden] { display: none; }`].filter(Boolean);r.textContent=`@layer kashi.elements { ${s.join(` `)} }`}function a(t){let n=/* @__PURE__ */ new Set;for(let r=t.prototype;r&&r!==e.prototype;r=Object.getPrototypeOf(r))for(let[e,t]of Object.entries(Object.getOwnPropertyDescriptors(r)))t.set&&n.add(e);let r=t.prototype.connectedCallback;t.prototype.connectedCallback=function(){for(let e of n){if(!Object.prototype.hasOwnProperty.call(this,e))continue;let t=this[e];delete this[e],this[e]=t}r?.call(this)}}function o(e){typeof document<`u`&&document.readyState===`loading`?document.addEventListener(`DOMContentLoaded`,e,{once:!0}):e()}let s=0;const c=e=>`${e}-${(++s).toString(36)}`,l={"&":`&`,"<":`<`,">":`>`,'"':`"`,"'":`'`},u=e=>String(e??``).replace(/[&<>"']/g,e=>l[e]),d=(e,t,n=!1)=>{let r=e.getAttribute(t);return r===null?n:r!==`false`};function f(e,t,n){e.dispatchEvent(new CustomEvent(t,{detail:n,bubbles:!0,composed:!0}))}const p=(e,t=1.7,n=!1)=>`<svg${n?` data-k-mirror=""`:``} viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="${t}" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${e}</svg>`,m={close:p(`<path d="m5 5 10 10M15 5 5 15"/>`),info:p(`<circle cx="10" cy="10" r="7.5"/><path d="M10 13.5V9.5M10 6.5h.01"/>`),success:p(`<circle cx="10" cy="10" r="7.5"/><path d="m6.75 10 2.25 2.25 4.25-4.5"/>`),warning:p(`<path d="M8.6 3.4 2.4 14a1.6 1.6 0 0 0 1.4 2.4h12.4a1.6 1.6 0 0 0 1.4-2.4L11.4 3.4a1.6 1.6 0 0 0-2.8 0Z"/><path d="M10 7.5v3.25M10 13.5h.01"/>`),error:p(`<circle cx="10" cy="10" r="7.5"/><path d="M10 6.5v4M10 13.5h.01"/>`),copy:p(`<rect x="7" y="7" width="10" height="10" rx="2"/><path d="M13 7V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h2"/>`),check:p(`<path d="m5 10.5 3.2 3L15 7"/>`,2),calendar:p(`<rect x="3" y="4.5" width="14" height="12.5" rx="2"/><path d="M3 8.5h14M7 3v3M13 3v3"/>`),prev:p(`<path d="m12 5-5 5 5 5"/>`,1.7,!0),next:p(`<path d="m8 5 5 5-5 5"/>`,1.7,!0)};function h({id:e,label:t,required:n,optional:r,control:i}){return`<div class="k-field">${t?`<div class="k-field__label-row"><label class="k-label" for="${e}">${u(t)}${n?`<span class="k-label__required" aria-hidden="true">*</span>`:``}${r?`<span class="k-label__optional">${u(r)}</span>`:``}</label></div>`:``}${i}</div>`}function g(e,t,n,r,i,a){if(e.querySelector(`:scope > .k-field__hint-row`)?.remove(),r?e.setAttribute(`data-invalid`,``):e.removeAttribute(`data-invalid`),r||i){let t=document.createElement(`div`);t.className=`k-field__hint-row`,t.innerHTML=`<div class="k-field__hints">${r?`<p class="k-hint k-hint--error" id="${n}-error">${u(r)}</p>`:``}${i?`<p class="k-hint" id="${n}-hint">${u(i)}</p>`:``}</div>`,e.append(t)}let o=[a,r?`${n}-error`:null,i?`${n}-hint`:null].filter(Boolean).join(` `);o?t.setAttribute(`aria-describedby`,o):t.removeAttribute(`aria-describedby`),r?t.setAttribute(`aria-invalid`,`true`):t.removeAttribute(`aria-invalid`);let s=t.closest(`.k-input, .k-select, .k-otp`);s?.classList.contains(`k-input`)&&(r?s.setAttribute(`data-state`,`invalid`):s.getAttribute(`data-state`)===`invalid`&&s.removeAttribute(`data-state`))}function _(e,t,n){let r=e.querySelector(`:scope > input[type=hidden][data-kashi-value]`);t?(r||(r=document.createElement(`input`),r.type=`hidden`,r.dataset.kashiValue=``,e.append(r)),r.name=t,r.value=n):r?.remove()}export{i as a,h as c,c as d,d as i,g as l,m as n,f as o,o as r,u as s,e as t,_ as u};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as e,c as t,d as n,i as r,l as i,n as a,o,r as s,s as c,t as l}from"./base-DfzNTdQk.js";import{i as u}from"./digits-DE1YGT94.js";const d=e=>`<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M1.8 10S4.8 4.5 10 4.5 18.2 10 18.2 10 15.2 15.5 10 15.5 1.8 10 1.8 10Z"/><circle cx="10" cy="10" r="2.5"/>${e?`<path d="m3 3 14 14"/>`:``}</svg>`,f=[`name`,`placeholder`,`autocomplete`,`inputmode`,`maxlength`,`minlength`,`pattern`,`min`,`max`,`step`,`dir`,`spellcheck`];var p=class extends l{constructor(){super(...arguments),this.multiline=!1,this.key=n(`kashi-input`),this.text=``,this.revealed=!1,this.rendered=!1,this.valueSet=!1}connectedCallback(){!this.rendered&&!this.valueSet&&(this.text=this.getAttribute(`value`)??``),this.render()}attributeChangedCallback(e,t,n){this.rendered&&t!==n&&(e===`value`?this.value=n??``:(this.text=this.control.value,this.render()))}get value(){return this.rendered?this.control.value:this.text}set value(e){this.valueSet=!0,this.text=String(e??``),this.rendered&&(this.control.value=this.text,this.paint())}focus(e){this.control?.focus(e)}render(){let e=this.key,n=this.getAttribute(`aria-label`),i=this.getAttribute(`label`),a=this.getAttribute(`type`)??`text`,s=this.getAttribute(`size`),l=this.hasAttribute(`disabled`),u=f.map(e=>this.hasAttribute(e)?` ${e}="${c(this.getAttribute(e))}"`:``).join(``),d=`${this.hasAttribute(`required`)?` required`:``}${l?` disabled`:``}${this.hasAttribute(`readonly`)?` readonly`:``}${!i&&n?` aria-label="${c(n)}"`:``}`,p=this.multiline?`<textarea id="${e}" class="k-input__control" rows="${Number(this.getAttribute(`rows`))||4}"${u}${d}></textarea>`:`<input id="${e}" class="k-input__control" type="${c(a===`password`&&this.revealed?`text`:a)}"${u}${d}>`,m=`<div class="k-input${this.multiline?` k-input--textarea`:``}${r(this,`autoresize`)?` k-input--autoresize`:``}${s===`sm`||s===`lg`?` k-input--${s}`:``}" data-size="${s??`md`}"${l?` data-state="disabled"`:``}>${p}<span class="k-input__end"></span></div>`,h=this.getAttribute(`optional`);this.querySelector(`:scope > .k-field`)?.remove(),this.insertAdjacentHTML(`afterbegin`,t({id:e,label:i,required:this.hasAttribute(`required`),optional:h===null?null:h||`اختیاری`,control:m})),this.control=this.querySelector(`.k-input__control`),this.control.value=this.text,this.control.addEventListener(`input`,()=>{this.paint(),o(this,`kashi-change`,{value:this.control.value})}),this.querySelector(`.k-input__end`).addEventListener(`click`,e=>{let t=e.target.closest(`button`);t&&(t.dataset.act===`clear`?(this.control.value=``,this.control.dispatchEvent(new Event(`input`,{bubbles:!0})),this.control.focus(),o(this,`kashi-clear`,{})):t.dataset.act===`reveal`&&(this.revealed=!this.revealed,this.control.type=this.revealed?`text`:`password`,this.paint(),t.focus()))}),this.rendered=!0,this.paint()}paint(){let e=this.control,t=e.value.length,n=this.hasAttribute(`disabled`)||this.hasAttribute(`readonly`),a=this.querySelector(`.k-input__end`),o=!this.multiline&&this.getAttribute(`type`)===`password`&&this.getAttribute(`revealable`)!==`false`;a.innerHTML=[r(this,`loading`)?`<span class="k-input__addon"><span class="k-spinner k-spinner--sm" role="status" aria-label="در حال بررسی"></span></span>`:``,r(this,`clearable`)&&t>0&&!n?`<button type="button" class="k-input__action" data-act="clear" aria-label="پاک کردن"><svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" aria-hidden="true"><path d="m6 6 8 8M14 6l-8 8"/></svg></button>`:``,o?`<button type="button" class="k-input__action" data-act="reveal" aria-label="${this.revealed?`پنهان کردن رمز`:`نمایش رمز`}" aria-pressed="${this.revealed}">${d(this.revealed)}</button>`:``].join(``),a.style.display=a.innerHTML?`contents`:`none`;let s=this.querySelector(`:scope > .k-field`),c=`${this.key}-count`;if(i(s,e,this.key,this.getAttribute(`error`),this.getAttribute(`hint`),r(this,`show-count`)?c:null),r(this,`show-count`)){let e=Number(this.getAttribute(`maxlength`))||0,n=s.querySelector(`:scope > .k-field__hint-row`);n||(n=document.createElement(`div`),n.className=`k-field__hint-row`,n.innerHTML=`<div class="k-field__hints"></div>`,s.append(n)),n.insertAdjacentHTML(`beforeend`,`<span class="k-counter" id="${c}"${e&&t>e?` data-over=""`:``}>${e?`${u(t)} / ${u(e)}`:u(t)}</span>`)}}};p.observedAttributes=[`label`,`hint`,`error`,`type`,`required`,`disabled`,`readonly`,`clearable`,`revealable`,`show-count`,`loading`,`size`,`optional`,`aria-label`,`value`,...f];var m=class extends p{constructor(){super(...arguments),this.multiline=!0}};m.observedAttributes=[...p.observedAttributes,`rows`,`autoresize`];var h=class extends l{constructor(){super(...arguments),this.box=null}connectedCallback(){s(()=>{if(!this.isConnected||this.box)return;let e=this.getAttribute(`remember`);e&&this.read(e)?this.hidden=!0:this.build()})}attributeChangedCallback(){this.box&&this.decorate()}read(e){try{return localStorage.getItem(`kashi-banner:${e}`)===`closed`}catch{return!1}}build(){let e=this.querySelector(`:scope > [slot='icon']`),t=[...this.querySelectorAll(`:scope > [slot='actions']`)],n=[...this.childNodes].filter(n=>n!==e&&!t.includes(n)),r=document.createElement(`div`);if(r.setAttribute(`role`,`region`),e){e.removeAttribute(`slot`);let t=document.createElement(`span`);t.className=`k-banner__icon`,t.setAttribute(`aria-hidden`,`true`),t.append(e),r.append(t)}let i=document.createElement(`p`);if(i.className=`k-banner__text`,i.append(...n),r.append(i),t.length){let e=document.createElement(`div`);e.className=`k-banner__actions`;for(let n of t)n.removeAttribute(`slot`),e.append(n);r.append(e)}this.replaceChildren(r),this.box=r,this.decorate()}decorate(){let e=this.box,t=this.getAttribute(`tone`);e.className=`k-banner${t&&t!==`neutral`?` k-banner--${t}`:``}${r(this,`flush`)?` k-banner--flush`:``}`,e.setAttribute(`aria-label`,this.getAttribute(`label`)??`اطلاعیه`),e.querySelector(`.k-banner__close`)?.remove(),r(this,`dismissible`)&&(e.insertAdjacentHTML(`beforeend`,`<button type="button" class="k-btn k-btn--tertiary k-btn--icon k-btn--sm k-banner__close" aria-label="${c(this.getAttribute(`close-label`)??`بستن اطلاعیه`)}">${a.close}</button>`),e.querySelector(`.k-banner__close`).addEventListener(`click`,()=>this.close()))}close(){let e=new CustomEvent(`kashi-close`,{bubbles:!0,cancelable:!0,detail:{}});if(!this.dispatchEvent(e))return;this.hidden=!0;let t=this.getAttribute(`remember`);if(t)try{localStorage.setItem(`kashi-banner:${t}`,`closed`)}catch{}}};h.observedAttributes=[`tone`,`flush`,`label`,`dismissible`,`close-label`],e(`kashi-input`,p,`block`),e(`kashi-textarea`,m,`block`),e(`kashi-banner`,h,`block`);export{h as KashiBanner,p as KashiInput,m as KashiTextarea};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import{a as e,c as t,d as n,i as r,l as i,n as a,o,s,t as c,u as l}from"./base-DfzNTdQk.js";import{i as u}from"./digits-DE1YGT94.js";import{a as d,c as f,d as p,i as m,l as h,n as g,o as _,r as v,s as y,t as b,u as x}from"./jalali-BiM5Kfep.js";import{t as S}from"./holidays-BwsAEOI1.js";const C=(e,t)=>!!e&&!!t&&e.getFullYear()===t.getFullYear()&&e.getMonth()===t.getMonth()&&e.getDate()===t.getDate(),w=e=>new Date(e.getFullYear(),e.getMonth(),e.getDate()),T=(e,t)=>new Date(e.getFullYear(),e.getMonth(),e.getDate()+t),E=e=>`${e.getFullYear()}-${e.getMonth()+1}-${e.getDate()}`;function D(e,t){let{jy:n,jm:r,jd:i}=p(e),a=n*12+(r-1)+t,o=Math.floor(a/12),s=a-o*12+1;return h(o,s,Math.min(i,_(o,s)))}const O=e=>e?d(e.trim())??f(e):null;var k=class extends c{constructor(){super(...arguments),this.rangeValue=null,this.hover=null,this.selected=null,this.valueSet=!1,this.focused=w(/* @__PURE__ */ new Date),this.refocus=!1,this.rendered=!1,this.key=n(`kashi-cal`),this.onClick=e=>{let t=e.target.closest(`button`);if(!t||!this.contains(t))return;let n=t.dataset.nav;if(n)this.go(D(this.focused,n===`prev`?-1:1),!1);else if(t.dataset.today!==void 0){let e=/* @__PURE__ */ new Date;this.pick(new Date(e.getFullYear(),e.getMonth(),e.getDate()))}else if(t.dataset.day){let[e,n,r]=t.dataset.day.split(`-`).map(Number);this.pick(new Date(e,n-1,r))}},this.onSelect=e=>{let t=e.target;if(!t.matches?.(`.k-calendar__select`))return;e.stopPropagation();let{jy:n,jm:r,jd:i}=p(this.focused),a=t.dataset.part===`year`?Number(t.value):n,o=t.dataset.part===`month`?Number(t.value):r;this.go(h(a,o,Math.min(i,_(a,o))),!1),this.querySelector(`.k-calendar__select[data-part="${t.dataset.part}"]`)?.focus()},this.onKey=e=>{if(!e.target.classList?.contains(`k-calendar__day`))return;let t=getComputedStyle(this).direction===`rtl`,n=this.focused,r=y(n),i={ArrowLeft:()=>T(n,t?1:-1),ArrowRight:()=>T(n,t?-1:1),ArrowUp:()=>T(n,-7),ArrowDown:()=>T(n,7),Home:()=>T(n,-r),End:()=>T(n,6-r),PageUp:()=>D(n,e.shiftKey?-12:-1),PageDown:()=>D(n,e.shiftKey?12:1)};i[e.key]&&(e.preventDefault(),this.go(i[e.key]()))}}get range(){return this.rangeValue}set range(e){this.rangeValue=e,e?.start&&(this.focused=this.clamp(w(e.start))),this.rendered&&this.render()}connectedCallback(){if(!this.rendered){this.valueSet||(this.selected=O(this.getAttribute(`value`))),this.focused=this.clamp(w(this.selected??/* @__PURE__ */ new Date)),this.addEventListener(`click`,this.onClick),this.addEventListener(`keydown`,this.onKey),this.addEventListener(`change`,this.onSelect);let e=e=>{let t=e.target.closest?.(`.k-calendar__day`)?.dataset.day;if(!this.rangeValue?.start||this.rangeValue.end||!t)return;let[n,r,i]=t.split(`-`).map(Number);this.hover=new Date(n,r-1,i),this.paintRange()};this.addEventListener(`pointerover`,e),this.addEventListener(`focusin`,e)}this.rendered=!0,this.render()}attributeChangedCallback(e,t,n){this.rendered&&t!==n&&(e===`value`&&(this.selected=O(n),this.selected&&(this.focused=w(this.selected))),this.render())}get value(){return this.selected?x(this.selected):``}set value(e){this.valueSet=!0,this.selected=O(e??``),this.selected&&(this.focused=w(this.selected)),this.rendered&&this.render()}get date(){return this.selected}set date(e){this.value=e?x(e):``}focus(){this.querySelector(`.k-calendar__day[tabindex="0"]`)?.focus()}get min(){return O(this.getAttribute(`min`))}get max(){return O(this.getAttribute(`max`))}clamp(e){let t=this.min,n=this.max;return t&&e<t?w(t):n&&e>n?w(n):e}disabled(e){let t=this.min,n=this.max;return!!t&&e<t||!!n&&e>n||!!this.isDateDisabled?.(e)}holiday(e){return this.holidayOf?this.holidayOf(e)||null:r(this,`holidays`)?S(e):null}go(e,t=!0){this.focused=this.clamp(e),this.refocus=t,this.render()}pick(e){this.disabled(e)||(this.selected=e,this.focused=e,this.refocus=!0,this.render(),l(this,this.getAttribute(`name`),this.value),o(this,`kashi-change`,{value:this.value,date:e}))}paintRange(){let e=this.rangeValue;if(!e)return;let t=e.start?w(e.start):null,n=e.end?w(e.end):null,r=t&&!n&&this.hover?this.hover:null,[i,a]=t&&(n||r)?[t,n??r].sort((e,t)=>+e-t):[t,t];this.querySelectorAll(`td[data-date]`).forEach(e=>{let[t,r,o]=e.dataset.date.split(`-`).map(Number),s=new Date(t,r-1,o),c=!!i&&!!a&&s>=i&&s<=a;e.setAttribute(`aria-selected`,String(c)),e.toggleAttribute(`data-in-range`,c&&!!n),e.toggleAttribute(`data-preview`,c&&!n),e.toggleAttribute(`data-range-start`,!!i&&C(s,i)),e.toggleAttribute(`data-range-end`,!!a&&C(s,a))})}render(){let e=this.getAttribute(`digits`)===`latin`?`latin`:`fa`,t=t=>e===`fa`?u(t):String(t),n=w(/* @__PURE__ */ new Date),{jy:r,jm:i}=p(this.focused),o=h(r,i,1),c=_(r,i),d=[...Array(y(o)).fill(null),...Array.from({length:c},(e,t)=>T(o,t))];for(;d.length%7;)d.push(null);let f=this.min,x=this.max,S=f?p(f).jy:p(n).jy-100,D=x?p(x).jy:p(n).jy+20,O=Array.from({length:Math.max(0,D-S+1)},(e,t)=>S+t);O.includes(r)||O.push(r),O.sort((e,t)=>e-t);let k=!!f&&o<=w(f),A=!!x&&h(r,i,c)>=w(x),j=[];for(let r=0;r<d.length;r+=7)j.push(`<tr>${d.slice(r,r+7).map(r=>{if(!r)return`<td role="gridcell"></td>`;let i=this.holiday(r),a=m(r,{style:`full`,digits:e})+(i?`، تعطیل${i===`جمعه`?``:`: ${i}`}`:``);return`<td role="gridcell" data-date="${E(r)}" aria-selected="${C(r,this.selected)}"><button type="button" class="k-calendar__day" data-day="${E(r)}" tabindex="${C(r,this.focused)?0:-1}" aria-label="${s(a)}"${i&&i!==`جمعه`?` title="${s(i)}"`:``}${i?` data-holiday=""`:``}${C(r,n)?` aria-current="date"`:``}${this.disabled(r)?` aria-disabled="true"`:``}>${t(p(r).jd)}</button></td>`}).join(``)}</tr>`);let M=this.getAttribute(`today-label`);this.innerHTML=`<div class="k-calendar" role="group" aria-label="${s(this.getAttribute(`label`)??`تقویم`)}">
|
|
2
|
+
<div class="k-calendar__header">
|
|
3
|
+
<button type="button" class="k-btn k-btn--tertiary k-btn--icon k-btn--sm" data-nav="prev" aria-label="ماه قبل"${k?` disabled`:``}>${a.prev}</button>
|
|
4
|
+
<div class="k-calendar__selects">
|
|
5
|
+
<select class="k-calendar__select" data-part="month" aria-label="ماه">${b.map((e,t)=>`<option value="${t+1}"${t+1===i?` selected`:``}>${e}</option>`).join(``)}</select>
|
|
6
|
+
<select class="k-calendar__select" data-part="year" aria-label="سال">${O.map(e=>`<option value="${e}"${e===r?` selected`:``}>${t(e)}</option>`).join(``)}</select>
|
|
7
|
+
</div>
|
|
8
|
+
<button type="button" class="k-btn k-btn--tertiary k-btn--icon k-btn--sm" data-nav="next" aria-label="ماه بعد"${A?` disabled`:``}>${a.next}</button>
|
|
9
|
+
</div>
|
|
10
|
+
<h2 class="k-sr-only" id="${this.key}-title" aria-live="polite">${b[i-1]} ${t(r)}</h2>
|
|
11
|
+
<table class="k-calendar__grid" role="grid" aria-labelledby="${this.key}-title">
|
|
12
|
+
<thead><tr>${v.map((e,t)=>`<th scope="col" abbr="${g[t]}">${e}</th>`).join(``)}</tr></thead>
|
|
13
|
+
<tbody>${j.join(``)}</tbody>
|
|
14
|
+
</table>
|
|
15
|
+
${M?`<div class="k-calendar__footer"><button type="button" class="k-btn k-btn--link" data-today>${s(M)}</button></div>`:``}
|
|
16
|
+
</div>`,l(this,this.getAttribute(`name`),this.value),this.paintRange(),this.refocus&&(this.refocus=!1,this.querySelector(`[data-day="${E(this.focused)}"]`)?.focus())}};k.observedAttributes=[`value`,`min`,`max`,`digits`,`holidays`,`label`,`today-label`,`name`];var A=class extends c{constructor(){super(...arguments),this.selected=null,this.valueSet=!1,this.invalid=!1,this.rendered=!1,this.key=n(`kashi-date`),this.pop=null,this.onOutside=e=>{this.contains(e.target)||this.close(!1)}}connectedCallback(){!this.rendered&&!this.valueSet&&(this.selected=O(this.getAttribute(`value`))),this.render()}disconnectedCallback(){this.close(!1)}attributeChangedCallback(e,t,n){this.rendered&&t!==n&&(e===`value`?(this.selected=O(n),this.invalid=!1,this.paint()):this.render())}get value(){return this.selected?x(this.selected):``}set value(e){this.valueSet=!0,this.selected=O(e??``),this.invalid=!1,this.rendered&&this.paint()}get date(){return this.selected}set date(e){this.value=e?x(e):``}focus(e){this.input?.focus(e)}get digits(){return this.getAttribute(`digits`)===`latin`?`latin`:`fa`}fmt(e){return e?m(e,{digits:this.digits}):``}render(){let e=this.key,n=this.getAttribute(`aria-label`),r=this.hasAttribute(`label`)?this.getAttribute(`label`):n?null:`تاریخ`,i=this.hasAttribute(`required`),o=this.hasAttribute(`disabled`),c=this.getAttribute(`placeholder`)??(this.digits===`fa`?`۱۴۰۳/۰۱/۰۱`:`1403/01/01`);this.close(!1),this.querySelector(`:scope > .k-datepicker`)?.remove();let l=`<div class="k-input"${o?` data-state="disabled"`:``}><input id="${e}" class="k-input__control" type="text" dir="ltr" autocomplete="off" placeholder="${s(c)}"${i?` required`:``}${o?` disabled`:``}${!r&&n?` aria-label="${s(n)}"`:``}><span class="k-input__addon"><button type="button" class="k-btn k-btn--tertiary k-btn--icon k-btn--sm k-datepicker__toggle" aria-haspopup="dialog" aria-expanded="false"${o?` disabled`:``}>${a.calendar}</button></span></div>`;this.insertAdjacentHTML(`afterbegin`,`<div class="k-datepicker">${t({id:e,label:r,required:i,control:l})}</div>`),this.input=this.querySelector(`input.k-input__control`),this.toggle=this.querySelector(`.k-datepicker__toggle`),this.input.addEventListener(`blur`,()=>this.commitText()),this.input.addEventListener(`keydown`,e=>{e.key===`Enter`&&this.commitText()}),this.toggle.addEventListener(`click`,()=>this.pop?this.close():this.open()),this.rendered=!0,this.paint()}paint(){let e=this.fmt(this.selected);!this.invalid&&this.input.value!==e&&(this.input.value=e);let t=this.getAttribute(`open-label`)??`انتخاب تاریخ از تقویم`;this.toggle.setAttribute(`aria-label`,this.selected?`${t}، ${m(this.selected,{style:`full`,digits:this.digits})}`:t);let n=this.getAttribute(`invalid-message`)??`این تاریخ معتبر نیست؛ آن را به شکل ۱۴۰۳/۰۵/۱۲ وارد کنید.`;this.input.setCustomValidity(this.invalid?n:``),i(this.querySelector(`.k-field`),this.input,this.key,this.getAttribute(`error`)||(this.invalid?n:null),this.getAttribute(`hint`)),l(this,this.getAttribute(`name`),this.value)}commitText(){let e=this.input.value.trim();if(!e)return this.commit(null);let t=f(e),n=O(this.getAttribute(`min`)),r=O(this.getAttribute(`max`));t&&!(n&&t<n)&&!(r&&t>r)&&!this.isDateDisabled?.(t)?this.commit(t):(this.invalid=!0,this.paint())}commit(e){let t=!C(e,this.selected)||e===null!=(this.selected===null);this.selected=e,this.invalid=!1,this.input.value=this.fmt(e),this.paint(),t&&o(this,`kashi-change`,{value:this.value,date:e})}open(){if(this.pop||this.hasAttribute(`disabled`))return;let e=this.querySelector(`.k-datepicker`),t=this.querySelector(`.k-input`),n=document.createElement(`div`);n.className=`k-datepicker__popover`,n.id=`${this.key}-calendar`,n.setAttribute(`role`,`dialog`),n.setAttribute(`aria-label`,this.getAttribute(`open-label`)??`انتخاب تاریخ از تقویم`),n.style.insetBlockStart=`${t.offsetTop+t.offsetHeight+6}px`;let r=document.createElement(`kashi-calendar`);for(let e of[`min`,`max`,`digits`,`holidays`]){let t=this.getAttribute(e);t!==null&&r.setAttribute(e,t)}this.selected&&r.setAttribute(`value`,x(this.selected)),r.setAttribute(`today-label`,this.getAttribute(`today-label`)??`امروز`),r.isDateDisabled=this.isDateDisabled,r.addEventListener(`kashi-change`,e=>{e.stopPropagation(),this.commit(e.detail.date),this.close()}),n.addEventListener(`keydown`,e=>{e.key===`Escape`&&(e.stopPropagation(),this.close())}),n.addEventListener(`focusout`,e=>{let t=e.relatedTarget;t&&!this.contains(t)&&this.close(!1)}),n.append(r),e.append(n),this.pop=n,this.toggle.setAttribute(`aria-expanded`,`true`),this.toggle.setAttribute(`aria-controls`,n.id),document.addEventListener(`pointerdown`,this.onOutside),r.focus()}close(e=!0){this.pop&&(this.pop.remove(),this.pop=null,document.removeEventListener(`pointerdown`,this.onOutside),this.toggle.setAttribute(`aria-expanded`,`false`),this.toggle.removeAttribute(`aria-controls`),e&&this.toggle.focus())}};A.observedAttributes=[`label`,`hint`,`error`,`required`,`disabled`,`value`,`min`,`max`,`digits`,`holidays`,`name`,`placeholder`,`invalid-message`,`aria-label`],e(`kashi-calendar`,k,`block`),e(`kashi-date-picker`,A,`block`);export{k as KashiCalendar,A as KashiDatePicker};
|