@kashi-ui/elements 0.26.0 → 1.1.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.
Files changed (72) hide show
  1. package/README.md +9 -0
  2. package/dist/appointment.js +13 -12
  3. package/dist/base.d.ts +408 -0
  4. package/dist/base.js +29 -0
  5. package/dist/basic.js +8 -9
  6. package/dist/calendar.js +21 -20
  7. package/dist/cdn/appointment-CrIZi8Bp.js +1 -0
  8. package/dist/cdn/base-DTQ50VdG.js +1 -0
  9. package/dist/cdn/basic-CrEW2RKF.js +1 -0
  10. package/dist/cdn/calendar-CYRrdGIf.js +16 -0
  11. package/dist/cdn/charts-D6KM26Vp.js +1 -0
  12. package/dist/cdn/combobox-6EFI2qqN.js +1 -0
  13. package/dist/cdn/controls-BwebEIlf.js +1 -0
  14. package/dist/cdn/display-DFuMkcj5.js +1 -0
  15. package/dist/cdn/fields-DhLIhHQ8.js +1 -0
  16. package/dist/cdn/forms-BHivKDdC.js +1 -0
  17. package/dist/cdn/holidays-DgbIAdlt.js +1 -0
  18. package/dist/cdn/inputs-R7nEHfbV.js +1 -0
  19. package/dist/cdn/kashi-elements.js +1 -1
  20. package/dist/cdn/map-H3xiZd8Y.js +12 -0
  21. package/dist/cdn/{menu-Cga7QOfs.js → menu-BAUSubXk.js} +1 -1
  22. package/dist/cdn/nav-NK-v48B5.js +1 -0
  23. package/dist/cdn/overlay-CBv8grz3.js +1 -0
  24. package/dist/cdn/pickers-BHzNt_X8.js +1 -0
  25. package/dist/cdn/table-Bt9WVLoo.js +1 -0
  26. package/dist/cdn/text-D4a_hlwL.js +1 -0
  27. package/dist/cdn/time-DJ3p1wvd.js +1 -0
  28. package/dist/cdn/words-BdADG_9U.js +1 -0
  29. package/dist/cdn.d.ts +1 -0
  30. package/dist/cdn.js +2 -0
  31. package/dist/charts.js +27 -24
  32. package/dist/combobox.d.ts +6 -5
  33. package/dist/combobox.js +29 -20
  34. package/dist/controls.js +15 -15
  35. package/dist/display.js +17 -14
  36. package/dist/fields.js +3 -3
  37. package/dist/forms.d.ts +3 -3
  38. package/dist/forms.js +54 -47
  39. package/dist/index.d.ts +4 -0
  40. package/dist/index.js +3 -0
  41. package/dist/inputs.d.ts +3 -3
  42. package/dist/inputs.js +31 -30
  43. package/dist/map.js +16 -13
  44. package/dist/nav.js +18 -16
  45. package/dist/overlay.js +5 -5
  46. package/dist/pickers.d.ts +1 -1
  47. package/dist/pickers.js +39 -38
  48. package/dist/table.js +7 -7
  49. package/dist/toast.js +3 -3
  50. package/package.json +4 -4
  51. package/dist/cdn/appointment-DkMxtSWi.js +0 -1
  52. package/dist/cdn/base-DfzNTdQk.js +0 -1
  53. package/dist/cdn/basic-B5n1okZO.js +0 -1
  54. package/dist/cdn/calendar-oMlnK1_D.js +0 -16
  55. package/dist/cdn/charts-SHKq2S4I.js +0 -1
  56. package/dist/cdn/combobox-DiGy7lAB.js +0 -1
  57. package/dist/cdn/controls-C6zwNYWs.js +0 -1
  58. package/dist/cdn/digits-DE1YGT94.js +0 -1
  59. package/dist/cdn/display-DZFW9EEX.js +0 -1
  60. package/dist/cdn/fields-D2hmrvVR.js +0 -1
  61. package/dist/cdn/forms-CeItYHFx.js +0 -1
  62. package/dist/cdn/holidays-BwsAEOI1.js +0 -1
  63. package/dist/cdn/inputs-DuP7P3ka.js +0 -1
  64. package/dist/cdn/jalali-BiM5Kfep.js +0 -1
  65. package/dist/cdn/map-w5X2fTQI.js +0 -12
  66. package/dist/cdn/nav-CwPZ20z-.js +0 -1
  67. package/dist/cdn/overlay-BDzwsBN5.js +0 -1
  68. package/dist/cdn/pickers-BOpaZ-7q.js +0 -1
  69. package/dist/cdn/table-BdF43L5q.js +0 -1
  70. package/dist/cdn/text-D1U32D6K.js +0 -1
  71. package/dist/cdn/time-DXw4vOne.js +0 -1
  72. package/dist/cdn/words-9OdzG7y6.js +0 -1
package/README.md CHANGED
@@ -26,6 +26,15 @@ import "@kashi-ui/css";
26
26
  import "@kashi-ui/elements";
27
27
  ```
28
28
 
29
+ ## زبان انگلیسی
30
+
31
+ ```ts
32
+ import { setLocale } from "@kashi-ui/elements";
33
+ setLocale("en"); // پیش از ساخته شدن عنصرها
34
+ ```
35
+
36
+ راهنما: https://kashi.sepantapouya.com/docs/i18n/
37
+
29
38
  ## عنصرها
30
39
 
31
40
  - **فیلدهای فارسی:** `kashi-national-id`، `kashi-legal-id`، `kashi-mobile`، `kashi-landline`، `kashi-card-number`، `kashi-iban`، `kashi-postal-code`، `kashi-price`
@@ -1,6 +1,6 @@
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]];
1
+ import { formatJalali, formatTime, fromISODate, getIranHoliday, parseTime, toFaDigits, toISODate, toJalali } from "@kashi-ui/persian";
2
+ import { calendarNames, define, digitsOf, emit, esc, HTMLBase, lang, msg, uid } from "./base.js";
3
+ const PERIODS = [["morning", 12], ["afternoon", 17], ["evening", 24]];
4
4
  const day = (d, add = 0) => new Date(d.getFullYear(), d.getMonth(), d.getDate() + add);
5
5
  const same = (a, b) => a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
6
6
  const minutes = (t) => {
@@ -70,7 +70,7 @@ export class KashiAppointmentPicker extends HTMLBase {
70
70
  const closedAttr = this.getAttribute("closed") !== "false";
71
71
  return Array.from({ length: count }, (_, i) => {
72
72
  const d = day(start, i);
73
- const closed = this.isClosed ? this.isClosed(d) : closedAttr && !!getIranHoliday(d);
73
+ const closed = this.isClosed ? this.isClosed(d) : closedAttr && !!getIranHoliday(d, { locale: lang() });
74
74
  let slots = [];
75
75
  if (!closed) {
76
76
  if (this.getSlots)
@@ -92,7 +92,7 @@ export class KashiAppointmentPicker extends HTMLBase {
92
92
  }
93
93
  render(focus = null) {
94
94
  const now = new Date();
95
- const fa = this.getAttribute("digits") !== "latin";
95
+ const fa = digitsOf(this) === "fa";
96
96
  const n = (v) => (fa ? toFaDigits(String(v)) : String(v));
97
97
  const digits = fa ? "fa" : "latin";
98
98
  const list = this.list();
@@ -101,27 +101,28 @@ export class KashiAppointmentPicker extends HTMLBase {
101
101
  this.active = i >= 0 ? i : Math.max(0, list.findIndex((x) => x.free > 0));
102
102
  }
103
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]);
104
+ const M = msg(), A = M.appointment, { months, weekdays } = calendarNames(), locale = lang();
105
+ const label = this.getAttribute("label") ?? A.label;
106
+ const wd = (d) => (same(d, now) ? M.common.today : same(d, day(now, 1)) ? M.common.tomorrow : weekdays[(d.getDay() + 1) % 7]);
106
107
  const daysHtml = list.map((x, i) => {
107
108
  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>`;
109
+ const status = x.closed ? A.closed : x.free ? A.freeLong(n(x.free)) : A.none;
110
+ 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, locale })}${M.common.list}${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">${months[jm - 1]}</span><span class="k-appt__free" aria-hidden="true">${x.closed ? A.closed : x.free ? A.free(n(x.free)) : A.full}</span></button>`;
110
111
  }).join("");
111
112
  const selDay = !!this.selected && same(this.selected.date, cur.date);
112
113
  const focusTime = selDay ? this.selected.time : cur.slots.find((s) => s.available)?.time;
113
114
  const slotsHtml = cur.closed || !cur.free
114
- ? `<p class="k-appt__empty">${cur.closed ? "این روز تعطیل است." : "نوبت خالی برای این روز نمانده است؛ روز دیگری را انتخاب کنید."}</p>`
115
+ ? `<p class="k-appt__empty">${cur.closed ? A.closedDay : A.noSlots}</p>`
115
116
  : PERIODS.map(([title, until], k) => {
116
117
  const slots = cur.slots.filter((s) => minutes(s.time) / 60 < until && minutes(s.time) / 60 >= (k ? PERIODS[k - 1][1] : 0));
117
118
  if (!slots.length)
118
119
  return "";
119
120
  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
+ return `<div class="k-appt__group"><p class="k-appt__period" id="${gid}">${A[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="${esc(A.time(n(s.time)))}${s.available ? "" : esc(A.booked)}">${n(s.time)}</button>`).join("")}</div></div>`;
121
122
  }).join("");
122
123
  const name = this.getAttribute("name");
123
124
  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.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="${esc(A.day)}">${daysHtml}</div><div class="k-appt__slots">${slotsHtml}</div><p class="k-appt__summary" aria-live="polite">${sel ? `${CLOCK}<span>${esc(A.yours)} <strong>${esc(A.at(formatJalali(sel.date, { style: "full", digits, locale }), 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
126
  this.rendered = true;
126
127
  const days = this.querySelector(".k-appt__days");
127
128
  const times = this.querySelector(".k-appt__slots");
package/dist/base.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { type KashiLocale, type KashiMessagesInput } from "@kashi-ui/persian";
1
2
  /** در Node.js (رندر سمت سرور) HTMLElement وجود ندارد؛ import کردن پکیج نباید خطا بدهد */
2
3
  export declare const HTMLBase: typeof HTMLElement;
3
4
  /** ثبت عنصر، فقط در مرورگر و فقط یک بار (اگر پکیج دو بار بارگذاری شود خطا نمی‌دهد) */
@@ -7,6 +8,412 @@ export declare function define(tag: string, ctor: CustomElementConstructor, disp
7
8
  * اگر اسکریپت بدون type="module" در <head> باشد، عنصر پیش از فرزندانش به صفحه وصل می‌شود.
8
9
  */
9
10
  export declare function afterParse(cb: () => void): void;
11
+ type Digits = "fa" | "latin";
12
+ /**
13
+ * زبان متن‌های داخلی همهٔ عنصرها: setLocale("en") برچسب دکمه‌ها، متن صفحه‌خوان، پیام‌های خطا، نام ماه‌ها و ارقام را انگلیسی می‌کند.
14
+ * پیش از ساخته شدن عنصرها صدا بزنید (بالای صفحه، بعد از import). با messages هر متنی را عوض کنید.
15
+ */
16
+ export declare function setLocale(locale: KashiLocale, { messages, digits }?: {
17
+ messages?: KashiMessagesInput;
18
+ digits?: Digits;
19
+ }): void;
20
+ /** ارقام پیش‌فرض زبان فعلی */
21
+ export declare const localeDigits: () => Digits;
22
+ /** فرهنگ متن‌های فعلی */
23
+ export declare const msg: () => {
24
+ common: {
25
+ close: string;
26
+ loading: string;
27
+ optional: string;
28
+ remove: (item: string) => string;
29
+ removeShort: string;
30
+ clear: string;
31
+ clearAll: string;
32
+ cancel: string;
33
+ confirm: string;
34
+ save: string;
35
+ edit: string;
36
+ prev: string;
37
+ next: string;
38
+ today: string;
39
+ tomorrow: string;
40
+ noResults: string;
41
+ searching: string;
42
+ results: (n: string) => string;
43
+ added: (item: string) => string;
44
+ removed: (item: string) => string;
45
+ selected: (item: string) => string;
46
+ max: (n: string) => string;
47
+ total: string;
48
+ other: string;
49
+ of: (a: string, b: string) => string;
50
+ and: string;
51
+ list: string;
52
+ range: string;
53
+ percent: (n: string) => string;
54
+ decimal: string;
55
+ };
56
+ money: {
57
+ toman: string;
58
+ rial: string;
59
+ positive: string;
60
+ };
61
+ alert: {
62
+ close: string;
63
+ };
64
+ banner: {
65
+ label: string;
66
+ close: string;
67
+ };
68
+ avatar: {
69
+ online: string;
70
+ offline: string;
71
+ busy: string;
72
+ away: string;
73
+ more: (n: string) => string;
74
+ };
75
+ bankCard: {
76
+ card: string;
77
+ cardOf: (bank: string) => string;
78
+ bank: string;
79
+ endingIn: (last4: string) => string;
80
+ expiry: string;
81
+ };
82
+ breadcrumb: {
83
+ label: string;
84
+ expand: string;
85
+ };
86
+ calendar: {
87
+ label: string;
88
+ prevMonth: string;
89
+ nextMonth: string;
90
+ month: string;
91
+ year: string;
92
+ holiday: string;
93
+ };
94
+ carousel: {
95
+ slider: string;
96
+ slide: string;
97
+ slideOf: (n: string, total: string) => string;
98
+ slides: (label: string) => string;
99
+ };
100
+ chart: {
101
+ period: string;
102
+ value: string;
103
+ share: string;
104
+ count: string;
105
+ keyboard: (title: string) => string;
106
+ thousand: string;
107
+ million: string;
108
+ billion: string;
109
+ };
110
+ city: {
111
+ label: string;
112
+ outside: string;
113
+ custom: string;
114
+ search: string;
115
+ hint: string;
116
+ };
117
+ province: {
118
+ label: string;
119
+ search: string;
120
+ capital: (c: string) => string;
121
+ };
122
+ colorPicker: {
123
+ label: string;
124
+ custom: string;
125
+ area: string;
126
+ areaValue: (s: string, v: string) => string;
127
+ eyedropper: string;
128
+ hue: string;
129
+ degrees: (n: string) => string;
130
+ red: string;
131
+ green: string;
132
+ blue: string;
133
+ invalid: string;
134
+ swatches: string;
135
+ sample: string;
136
+ contrast: (ink: "white" | "black", ratio: string, pass: boolean) => string;
137
+ names: {
138
+ firoozeh: string;
139
+ lajvard: string;
140
+ violet: string;
141
+ anar: string;
142
+ zafaran: string;
143
+ peste: string;
144
+ shab: string;
145
+ };
146
+ };
147
+ combobox: {
148
+ toggle: string;
149
+ more: (n: string) => string;
150
+ };
151
+ otp: {
152
+ label: string;
153
+ };
154
+ command: {
155
+ placeholder: string;
156
+ empty: string;
157
+ title: string;
158
+ navigate: string;
159
+ select: string;
160
+ };
161
+ contextMenu: {
162
+ label: string;
163
+ hint: string;
164
+ };
165
+ copy: {
166
+ copy: string;
167
+ copied: string;
168
+ failed: string;
169
+ };
170
+ countdown: {
171
+ days: string;
172
+ hours: string;
173
+ minutes: string;
174
+ seconds: string;
175
+ left: string;
176
+ };
177
+ table: {
178
+ selectAll: string;
179
+ selectRow: string;
180
+ selectNamed: (row: string) => string;
181
+ empty: string;
182
+ sort: (col: string) => string;
183
+ search: string;
184
+ rows: (n: string) => string;
185
+ };
186
+ datePicker: {
187
+ label: string;
188
+ invalid: string;
189
+ open: string;
190
+ };
191
+ dateRange: {
192
+ label: string;
193
+ placeholder: string;
194
+ presets: string;
195
+ pickEnd: string;
196
+ last7: (n: string) => string;
197
+ last30: (n: string) => string;
198
+ thisMonth: string;
199
+ lastMonth: string;
200
+ };
201
+ monthPicker: {
202
+ label: string;
203
+ prevYear: string;
204
+ nextYear: string;
205
+ };
206
+ fileUpload: {
207
+ units: string[];
208
+ browse: string;
209
+ drop: string;
210
+ tooBig: (max: string) => string;
211
+ wrongType: string;
212
+ tooMany: (n: string) => string;
213
+ done: string;
214
+ uploading: string;
215
+ uploadingFile: (name: string) => string;
216
+ error: string;
217
+ };
218
+ input: {
219
+ clear: string;
220
+ show: string;
221
+ hide: string;
222
+ checking: string;
223
+ };
224
+ invalid: {
225
+ card: string;
226
+ iban: string;
227
+ nationalId: string;
228
+ legalId: string;
229
+ mobile: string;
230
+ landline: string;
231
+ postal: string;
232
+ plate: string;
233
+ time: string;
234
+ timeRange: (a: string, b: string) => string;
235
+ priceMin: (n: string) => string;
236
+ priceMax: (n: string) => string;
237
+ priceRange: (a: string, b: string) => string;
238
+ };
239
+ inlineEdit: {
240
+ empty: string;
241
+ failed: string;
242
+ };
243
+ iranMap: {
244
+ label: string;
245
+ tableNote: string;
246
+ onMap: (label: string) => string;
247
+ };
248
+ keyboardFix: {
249
+ before: string;
250
+ after: string;
251
+ };
252
+ meter: {
253
+ ok: string;
254
+ warning: string;
255
+ critical: string;
256
+ };
257
+ multiSelect: {
258
+ label: string;
259
+ selected: (n: string) => string;
260
+ clearedAll: string;
261
+ };
262
+ numberInput: {
263
+ label: string;
264
+ increment: string;
265
+ decrement: string;
266
+ };
267
+ pagination: {
268
+ nav: string;
269
+ page: (n: string) => string;
270
+ perPage: string;
271
+ jump: string;
272
+ jumpRange: (jump: string, a: string, b: string) => string;
273
+ };
274
+ password: {
275
+ title: string;
276
+ strength: (level: string) => string;
277
+ levels: string[];
278
+ persian: string;
279
+ length: (n: string) => string;
280
+ cases: string;
281
+ digit: string;
282
+ symbol: string;
283
+ met: string;
284
+ unmet: string;
285
+ };
286
+ receipt: {
287
+ success: string;
288
+ failed: string;
289
+ pending: string;
290
+ merchant: string;
291
+ tracking: string;
292
+ copyTracking: string;
293
+ reference: string;
294
+ card: string;
295
+ date: string;
296
+ at: (time: string) => string;
297
+ };
298
+ persianInput: {
299
+ text: string;
300
+ "national-id": string;
301
+ "legal-id": string;
302
+ mobile: string;
303
+ landline: string;
304
+ card: string;
305
+ iban: string;
306
+ price: string;
307
+ postal: string;
308
+ time: string;
309
+ plate: string;
310
+ };
311
+ textFix: {
312
+ fix: string;
313
+ clean: string;
314
+ count: (n: string, summary: string) => string;
315
+ arabic: string;
316
+ zwnj: string;
317
+ punctuation: string;
318
+ digits: string;
319
+ spacing: string;
320
+ };
321
+ plate: {
322
+ label: string;
323
+ first: string;
324
+ letter: string;
325
+ middle: string;
326
+ region: string;
327
+ iran: string;
328
+ };
329
+ rating: {
330
+ label: string;
331
+ };
332
+ sideNav: {
333
+ label: string;
334
+ };
335
+ slider: {
336
+ from: string;
337
+ to: string;
338
+ };
339
+ splitter: {
340
+ label: string;
341
+ };
342
+ stat: {
343
+ up: string;
344
+ down: string;
345
+ flat: string;
346
+ };
347
+ stepper: {
348
+ label: string;
349
+ complete: string;
350
+ current: string;
351
+ };
352
+ tagInput: {
353
+ label: string;
354
+ duplicate: string;
355
+ };
356
+ toast: {
357
+ region: string;
358
+ close: string;
359
+ };
360
+ tour: {
361
+ done: string;
362
+ skip: string;
363
+ step: (n: string, total: string) => string;
364
+ };
365
+ heatmap: {
366
+ unit: string;
367
+ weekdays: string[];
368
+ inYear: (unit: string, year: string) => string;
369
+ total: (n: string, unit: string, year: string) => string;
370
+ less: string;
371
+ more: string;
372
+ byMonth: (name: string) => string;
373
+ };
374
+ appointment: {
375
+ label: string;
376
+ day: string;
377
+ morning: string;
378
+ afternoon: string;
379
+ evening: string;
380
+ closed: string;
381
+ full: string;
382
+ free: (n: string) => string;
383
+ freeLong: (n: string) => string;
384
+ none: string;
385
+ closedDay: string;
386
+ noSlots: string;
387
+ time: (t: string) => string;
388
+ booked: string;
389
+ yours: string;
390
+ at: (date: string, time: string) => string;
391
+ };
392
+ address: {
393
+ legend: string;
394
+ province: string;
395
+ city: string;
396
+ street: string;
397
+ plaque: string;
398
+ unit: string;
399
+ postalCode: string;
400
+ provinceSearch: string;
401
+ streetHint: string;
402
+ };
403
+ };
404
+ /** زبان فعلی */
405
+ export declare const lang: () => KashiLocale;
406
+ /** ارقام عنصر: ویژگی digits، وگرنه پیش‌فرض زبان */
407
+ export declare const digitsOf: (el: Element) => Digits;
408
+ /** عدد با ارقام همان عنصر */
409
+ export declare const num: (el: Element, v: number | string) => string;
410
+ /** نام ماه‌ها و روزهای شمسی به زبان فعلی */
411
+ export declare const calendarNames: () => {
412
+ months: readonly ["Farvardin", "Ordibehesht", "Khordad", "Tir", "Mordad", "Shahrivar", "Mehr", "Aban", "Azar", "Dey", "Bahman", "Esfand"] | readonly ["فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور", "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند"];
413
+ weekdays: readonly ["Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday"] | readonly ["شنبه", "یکشنبه", "دوشنبه", "سه‌شنبه", "چهارشنبه", "پنجشنبه", "جمعه"];
414
+ weekdaysShort: readonly ["Sa", "Su", "Mo", "Tu", "We", "Th", "Fr"] | readonly ["ش", "ی", "د", "س", "چ", "پ", "ج"];
415
+ friday: string;
416
+ };
10
417
  /** شناسهٔ یکتا برای اتصال برچسب، راهنما و خطا */
11
418
  export declare const uid: (prefix: string) => string;
12
419
  /** متن امن برای قرار دادن در HTML */
@@ -45,3 +452,4 @@ export declare function fieldHTML({ id, label, required, optional, control }: Fi
45
452
  export declare function setMessages(field: Element, control: HTMLElement, id: string, error: string | null, hint: string | null, extra?: string | null): void;
46
453
  /** فیلد پنهان برای ارسال مقدار خام در فرم HTML معمولی */
47
454
  export declare function syncHidden(host: Element, name: string | null, value: string): void;
455
+ export {};
package/dist/base.js CHANGED
@@ -1,4 +1,5 @@
1
1
  // پایهٔ مشترک وب کامپوننت‌ها: بدون Shadow DOM، تا کلاس‌های k-* از @kashi-ui/css و تم کاربر مستقیم اعمال شوند.
2
+ import { JALALI_MONTHS, JALALI_MONTHS_EN, WEEKDAYS, WEEKDAYS_EN, WEEKDAYS_SHORT, WEEKDAYS_SHORT_EN, faMessages, getMessages, toFaDigits, } from "@kashi-ui/persian";
2
3
  /** در Node.js (رندر سمت سرور) HTMLElement وجود ندارد؛ import کردن پکیج نباید خطا بدهد */
3
4
  export const HTMLBase = typeof HTMLElement === "undefined" ? class {
4
5
  } : HTMLElement;
@@ -64,6 +65,34 @@ export function afterParse(cb) {
64
65
  else
65
66
  cb();
66
67
  }
68
+ const LOCALE = { locale: "fa", messages: faMessages, digits: "fa" };
69
+ /**
70
+ * زبان متن‌های داخلی همهٔ عنصرها: setLocale("en") برچسب دکمه‌ها، متن صفحه‌خوان، پیام‌های خطا، نام ماه‌ها و ارقام را انگلیسی می‌کند.
71
+ * پیش از ساخته شدن عنصرها صدا بزنید (بالای صفحه، بعد از import). با messages هر متنی را عوض کنید.
72
+ */
73
+ export function setLocale(locale, { messages, digits } = {}) {
74
+ LOCALE.locale = locale;
75
+ LOCALE.messages = getMessages(locale, messages);
76
+ LOCALE.digits = digits ?? (locale === "en" ? "latin" : "fa");
77
+ }
78
+ /** ارقام پیش‌فرض زبان فعلی */
79
+ export const localeDigits = () => LOCALE.digits;
80
+ /** فرهنگ متن‌های فعلی */
81
+ export const msg = () => LOCALE.messages;
82
+ /** زبان فعلی */
83
+ export const lang = () => LOCALE.locale;
84
+ /** ارقام عنصر: ویژگی digits، وگرنه پیش‌فرض زبان */
85
+ export const digitsOf = (el) => {
86
+ const a = el.getAttribute("digits");
87
+ return a === "latin" || a === "fa" ? a : LOCALE.digits;
88
+ };
89
+ /** عدد با ارقام همان عنصر */
90
+ export const num = (el, v) => (digitsOf(el) === "fa" ? toFaDigits(String(v)) : String(v));
91
+ /** نام ماه‌ها و روزهای شمسی به زبان فعلی */
92
+ export const calendarNames = () => {
93
+ const en = LOCALE.locale === "en";
94
+ return { months: en ? JALALI_MONTHS_EN : JALALI_MONTHS, weekdays: en ? WEEKDAYS_EN : WEEKDAYS, weekdaysShort: en ? WEEKDAYS_SHORT_EN : WEEKDAYS_SHORT, friday: en ? "Friday" : "جمعه" };
95
+ };
67
96
  let counter = 0;
68
97
  /** شناسهٔ یکتا برای اتصال برچسب، راهنما و خطا */
69
98
  export const uid = (prefix) => `${prefix}-${(++counter).toString(36)}`;
package/dist/basic.js CHANGED
@@ -1,5 +1,4 @@
1
- import { toFaDigits } from "@kashi-ui/persian";
2
- import { HTMLBase, ICONS, afterParse, bool, define, emit, esc, fieldHTML, setMessages, uid } from "./base.js";
1
+ import { afterParse, bool, define, emit, esc, fieldHTML, HTMLBase, ICONS, msg, num, setMessages, uid } from "./base.js";
3
2
  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
3
  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
4
  /** ویژگی‌هایی که مستقیم روی input یا textarea داخلی می‌روند */
@@ -62,7 +61,7 @@ export class KashiInput extends HTMLBase {
62
61
  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
62
  const optional = this.getAttribute("optional");
64
63
  this.querySelector(":scope > .k-field")?.remove();
65
- this.insertAdjacentHTML("afterbegin", fieldHTML({ id, label, required: this.hasAttribute("required"), optional: optional === null ? null : optional || "اختیاری", control: box }));
64
+ this.insertAdjacentHTML("afterbegin", fieldHTML({ id, label, required: this.hasAttribute("required"), optional: optional === null ? null : optional || msg().common.optional, control: box }));
66
65
  this.control = this.querySelector(".k-input__control");
67
66
  this.control.value = this.text;
68
67
  this.control.addEventListener("input", () => {
@@ -97,9 +96,9 @@ export class KashiInput extends HTMLBase {
97
96
  const end = this.querySelector(".k-input__end");
98
97
  const password = !this.multiline && this.getAttribute("type") === "password" && this.getAttribute("revealable") !== "false";
99
98
  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>` : "",
99
+ bool(this, "loading") ? '<span class="k-input__addon"><span class="k-spinner k-spinner--sm" role="status" aria-label="${esc(msg().input.checking)}"></span></span>' : "",
100
+ bool(this, "clearable") && len > 0 && !off ? `<button type="button" class="k-input__action" data-act="clear" aria-label="${esc(msg().input.clear)}">${X}</button>` : "",
101
+ password ? `<button type="button" class="k-input__action" data-act="reveal" aria-label="${esc(this.revealed ? msg().input.hide : msg().input.show)}" aria-pressed="${this.revealed}">${EYE(this.revealed)}</button>` : "",
103
102
  ].join("");
104
103
  end.style.display = end.innerHTML ? "contents" : "none";
105
104
  const field = this.querySelector(":scope > .k-field");
@@ -114,7 +113,7 @@ export class KashiInput extends HTMLBase {
114
113
  row.innerHTML = '<div class="k-field__hints"></div>';
115
114
  field.append(row);
116
115
  }
117
- row.insertAdjacentHTML("beforeend", `<span class="k-counter" id="${countId}"${max && len > max ? ' data-over=""' : ""}>${max ? `${toFaDigits(len)} / ${toFaDigits(max)}` : toFaDigits(len)}</span>`);
116
+ row.insertAdjacentHTML("beforeend", `<span class="k-counter" id="${countId}"${max && len > max ? ' data-over=""' : ""}>${max ? `${num(this, len)} / ${num(this, max)}` : num(this, len)}</span>`);
118
117
  }
119
118
  }
120
119
  }
@@ -195,10 +194,10 @@ export class KashiBanner extends HTMLBase {
195
194
  const box = this.box;
196
195
  const tone = this.getAttribute("tone");
197
196
  box.className = `k-banner${tone && tone !== "neutral" ? ` k-banner--${tone}` : ""}${bool(this, "flush") ? " k-banner--flush" : ""}`;
198
- box.setAttribute("aria-label", this.getAttribute("label") ?? "اطلاعیه");
197
+ box.setAttribute("aria-label", this.getAttribute("label") ?? msg().banner.label);
199
198
  box.querySelector(".k-banner__close")?.remove();
200
199
  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>`);
200
+ 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") ?? msg().banner.close)}">${ICONS.close}</button>`);
202
201
  box.querySelector(".k-banner__close").addEventListener("click", () => this.close());
203
202
  }
204
203
  }