@kashi-ui/elements 0.26.0 → 1.0.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 +9 -0
- package/dist/appointment.js +13 -12
- package/dist/base.d.ts +408 -0
- package/dist/base.js +29 -0
- package/dist/basic.js +8 -9
- package/dist/calendar.js +21 -20
- package/dist/cdn/appointment-CrIZi8Bp.js +1 -0
- package/dist/cdn/base-DTQ50VdG.js +1 -0
- package/dist/cdn/basic-CrEW2RKF.js +1 -0
- package/dist/cdn/calendar-CYRrdGIf.js +16 -0
- package/dist/cdn/charts-D6KM26Vp.js +1 -0
- package/dist/cdn/combobox-6EFI2qqN.js +1 -0
- package/dist/cdn/controls-BwebEIlf.js +1 -0
- package/dist/cdn/display-DFuMkcj5.js +1 -0
- package/dist/cdn/fields-DhLIhHQ8.js +1 -0
- package/dist/cdn/forms-BHivKDdC.js +1 -0
- package/dist/cdn/holidays-DgbIAdlt.js +1 -0
- package/dist/cdn/inputs-R7nEHfbV.js +1 -0
- package/dist/cdn/kashi-elements.js +1 -1
- package/dist/cdn/map-H3xiZd8Y.js +12 -0
- package/dist/cdn/{menu-Cga7QOfs.js → menu-BAUSubXk.js} +1 -1
- package/dist/cdn/nav-NK-v48B5.js +1 -0
- package/dist/cdn/overlay-CBv8grz3.js +1 -0
- package/dist/cdn/pickers-BHzNt_X8.js +1 -0
- package/dist/cdn/table-Bt9WVLoo.js +1 -0
- package/dist/cdn/text-D4a_hlwL.js +1 -0
- package/dist/cdn/time-DJ3p1wvd.js +1 -0
- package/dist/cdn/words-BdADG_9U.js +1 -0
- package/dist/cdn.d.ts +1 -0
- package/dist/cdn.js +2 -0
- package/dist/charts.js +27 -24
- package/dist/combobox.d.ts +6 -5
- package/dist/combobox.js +29 -20
- package/dist/controls.js +15 -15
- package/dist/display.js +17 -14
- package/dist/fields.js +3 -3
- package/dist/forms.d.ts +3 -3
- package/dist/forms.js +54 -47
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/inputs.d.ts +3 -3
- package/dist/inputs.js +31 -30
- package/dist/map.js +16 -13
- package/dist/nav.js +18 -16
- package/dist/overlay.js +5 -5
- package/dist/pickers.d.ts +1 -1
- package/dist/pickers.js +39 -38
- package/dist/table.js +7 -7
- package/dist/toast.js +3 -3
- package/package.json +4 -4
- package/dist/cdn/appointment-DkMxtSWi.js +0 -1
- package/dist/cdn/base-DfzNTdQk.js +0 -1
- package/dist/cdn/basic-B5n1okZO.js +0 -1
- package/dist/cdn/calendar-oMlnK1_D.js +0 -16
- package/dist/cdn/charts-SHKq2S4I.js +0 -1
- package/dist/cdn/combobox-DiGy7lAB.js +0 -1
- package/dist/cdn/controls-C6zwNYWs.js +0 -1
- package/dist/cdn/digits-DE1YGT94.js +0 -1
- package/dist/cdn/display-DZFW9EEX.js +0 -1
- package/dist/cdn/fields-D2hmrvVR.js +0 -1
- package/dist/cdn/forms-CeItYHFx.js +0 -1
- package/dist/cdn/holidays-BwsAEOI1.js +0 -1
- package/dist/cdn/inputs-DuP7P3ka.js +0 -1
- package/dist/cdn/jalali-BiM5Kfep.js +0 -1
- package/dist/cdn/map-w5X2fTQI.js +0 -12
- package/dist/cdn/nav-CwPZ20z-.js +0 -1
- package/dist/cdn/overlay-BDzwsBN5.js +0 -1
- package/dist/cdn/pickers-BOpaZ-7q.js +0 -1
- package/dist/cdn/table-BdF43L5q.js +0 -1
- package/dist/cdn/text-D1U32D6K.js +0 -1
- package/dist/cdn/time-DXw4vOne.js +0 -1
- package/dist/cdn/words-9OdzG7y6.js +0 -1
package/dist/combobox.d.ts
CHANGED
|
@@ -32,8 +32,8 @@ export declare class KashiCombobox extends HTMLBase {
|
|
|
32
32
|
private listbox;
|
|
33
33
|
private live;
|
|
34
34
|
/** برچسب پیشفرض وقتی label داده نشود */
|
|
35
|
-
protected defaultLabel: string | null;
|
|
36
|
-
protected defaultPlaceholder: string;
|
|
35
|
+
protected get defaultLabel(): string | null;
|
|
36
|
+
protected get defaultPlaceholder(): string;
|
|
37
37
|
connectedCallback(): void;
|
|
38
38
|
disconnectedCallback(): void;
|
|
39
39
|
attributeChangedCallback(name: string, old: string | null, next: string | null): void;
|
|
@@ -71,8 +71,8 @@ export declare class KashiCombobox extends HTMLBase {
|
|
|
71
71
|
/** انتخاب یکی از ۳۱ استان با جستجو؛ با نام مرکز استان هم پیدا میشود (مثلاً «مشهد») */
|
|
72
72
|
export declare class KashiProvinceSelect extends KashiCombobox {
|
|
73
73
|
static observedAttributes: string[];
|
|
74
|
-
protected defaultLabel: string;
|
|
75
|
-
protected defaultPlaceholder: string;
|
|
74
|
+
protected get defaultLabel(): string;
|
|
75
|
+
protected get defaultPlaceholder(): string;
|
|
76
76
|
protected source(): ComboboxOption[];
|
|
77
77
|
protected detail(): {
|
|
78
78
|
value: string;
|
|
@@ -85,7 +85,8 @@ export declare class KashiProvinceSelect extends KashiCombobox {
|
|
|
85
85
|
*/
|
|
86
86
|
export declare class KashiCitySelect extends KashiCombobox {
|
|
87
87
|
static observedAttributes: string[];
|
|
88
|
-
protected defaultLabel: string;
|
|
88
|
+
protected get defaultLabel(): string;
|
|
89
|
+
protected get defaultPlaceholder(): string;
|
|
89
90
|
private custom;
|
|
90
91
|
get province(): string | null;
|
|
91
92
|
set province(p: string | null);
|
package/dist/combobox.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { CITIES, PROVINCES, getCities, normalizePersian
|
|
2
|
-
import {
|
|
1
|
+
import { CITIES, PROVINCES, getCities, normalizePersian } from "@kashi-ui/persian";
|
|
2
|
+
import { afterParse, define, emit, esc, fieldHTML, HTMLBase, msg, num, setMessages, syncHidden, uid } from "./base.js";
|
|
3
3
|
/** متن یکدست برای جستجو: ي/ی، ك/ک، ارقام، حروف کوچک */
|
|
4
4
|
export const searchText = (s) => normalizePersian(s, { digits: "latin" }).toLowerCase();
|
|
5
5
|
/** بیشترین تعداد گزینهٔ رسمشده؛ برای فهرستهای بلند مثل ۱۲۰۰ شهر، بقیه با تایپ پیدا میشوند */
|
|
@@ -24,14 +24,18 @@ export class KashiCombobox extends HTMLBase {
|
|
|
24
24
|
this.rendered = false;
|
|
25
25
|
this.valueSet = false;
|
|
26
26
|
this.key = uid("kashi-combo");
|
|
27
|
-
/** برچسب پیشفرض وقتی label داده نشود */
|
|
28
|
-
this.defaultLabel = null;
|
|
29
|
-
this.defaultPlaceholder = "";
|
|
30
27
|
this.onOutside = (e) => {
|
|
31
28
|
if (!this.contains(e.target))
|
|
32
29
|
this.close();
|
|
33
30
|
};
|
|
34
31
|
}
|
|
32
|
+
/** برچسب پیشفرض وقتی label داده نشود */
|
|
33
|
+
get defaultLabel() {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
get defaultPlaceholder() {
|
|
37
|
+
return "";
|
|
38
|
+
}
|
|
35
39
|
connectedCallback() {
|
|
36
40
|
afterParse(() => {
|
|
37
41
|
if (!this.isConnected)
|
|
@@ -135,7 +139,7 @@ export class KashiCombobox extends HTMLBase {
|
|
|
135
139
|
const required = this.hasAttribute("required");
|
|
136
140
|
const disabled = this.hasAttribute("disabled");
|
|
137
141
|
const listId = `${id}-list`;
|
|
138
|
-
const control = `<div class="k-combobox"><div class="k-input"${disabled ? ' data-state="disabled"' : ""}><input id="${id}" class="k-input__control" type="text" role="combobox" aria-autocomplete="list" aria-expanded="false" aria-controls="${listId}" autocomplete="off" placeholder="${esc(this.getAttribute("placeholder") ?? this.defaultPlaceholder)}"${required ? " required" : ""}${disabled ? " disabled" : ""}${!label && ariaLabel ? ` aria-label="${esc(ariaLabel)}"` : ""}><button type="button" tabindex="-1" class="k-input__action k-combobox__toggle" aria-label="
|
|
142
|
+
const control = `<div class="k-combobox"><div class="k-input"${disabled ? ' data-state="disabled"' : ""}><input id="${id}" class="k-input__control" type="text" role="combobox" aria-autocomplete="list" aria-expanded="false" aria-controls="${listId}" autocomplete="off" placeholder="${esc(this.getAttribute("placeholder") ?? this.defaultPlaceholder)}"${required ? " required" : ""}${disabled ? " disabled" : ""}${!label && ariaLabel ? ` aria-label="${esc(ariaLabel)}"` : ""}><button type="button" tabindex="-1" class="k-input__action k-combobox__toggle" aria-label="${esc(msg().combobox.toggle)}"${disabled ? " disabled" : ""}>${CHEVRON}</button></div><ul id="${listId}" role="listbox" class="k-listbox" hidden style="inset-block-start: calc(100% + 6px)"${label ? ` aria-label="${esc(label)}"` : ` aria-labelledby="${id}"`}></ul><span class="k-sr-only" aria-live="polite"></span></div>`;
|
|
139
143
|
this.querySelector(":scope > .k-field")?.remove();
|
|
140
144
|
this.insertAdjacentHTML("afterbegin", fieldHTML({ id, label, required, control }));
|
|
141
145
|
this.input = this.querySelector("input.k-input__control");
|
|
@@ -249,11 +253,11 @@ export class KashiCombobox extends HTMLBase {
|
|
|
249
253
|
return `${header}<li id="${listId}-${i}" data-i="${i}" role="option" aria-selected="${on}"${o.disabled ? ' aria-disabled="true"' : ""}${i === this.active ? " data-active" : ""} class="k-option"><span class="k-option__body"><span>${mark(o.label)}</span>${o.description ? `<span class="k-option__desc">${esc(o.description)}</span>` : ""}</span>${on ? `<span class="k-option__check">${CHECK}</span>` : ""}</li>`;
|
|
250
254
|
});
|
|
251
255
|
if (!this.filtered.length)
|
|
252
|
-
items.push(`<li class="k-listbox__empty" role="presentation">${esc(this.getAttribute("empty-message") ??
|
|
256
|
+
items.push(`<li class="k-listbox__empty" role="presentation">${esc(this.getAttribute("empty-message") ?? msg().common.noResults)}</li>`);
|
|
253
257
|
if (this.filtered.length > MAX_RENDER)
|
|
254
|
-
items.push(`<li class="k-listbox__empty" role="presentation">${
|
|
258
|
+
items.push(`<li class="k-listbox__empty" role="presentation">${esc(msg().combobox.more(num(this, this.filtered.length - MAX_RENDER)))}</li>`);
|
|
255
259
|
this.listbox.innerHTML = items.join("");
|
|
256
|
-
this.live.textContent = this.typing ?
|
|
260
|
+
this.live.textContent = this.typing ? msg().common.results(num(this, this.filtered.length)) : "";
|
|
257
261
|
this.setActive(this.active);
|
|
258
262
|
}
|
|
259
263
|
setActive(i) {
|
|
@@ -319,14 +323,15 @@ export class KashiCombobox extends HTMLBase {
|
|
|
319
323
|
KashiCombobox.observedAttributes = ["label", "hint", "error", "placeholder", "required", "disabled", "name", "value", "empty-message", "aria-label"];
|
|
320
324
|
/** انتخاب یکی از ۳۱ استان با جستجو؛ با نام مرکز استان هم پیدا میشود (مثلاً «مشهد») */
|
|
321
325
|
export class KashiProvinceSelect extends KashiCombobox {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
+
get defaultLabel() {
|
|
327
|
+
return msg().province.label;
|
|
328
|
+
}
|
|
329
|
+
get defaultPlaceholder() {
|
|
330
|
+
return msg().province.search;
|
|
326
331
|
}
|
|
327
332
|
source() {
|
|
328
333
|
const capital = this.getAttribute("show-capital") !== "false";
|
|
329
|
-
return PROVINCES.map((p) => ({ value: p.name, label: p.name, description: capital ?
|
|
334
|
+
return PROVINCES.map((p) => ({ value: p.name, label: p.name, description: capital ? msg().province.capital(p.capital) : undefined, keywords: [p.capital] }));
|
|
330
335
|
}
|
|
331
336
|
detail() {
|
|
332
337
|
return { value: this.value, province: PROVINCES.find((p) => p.name === this.value) ?? null };
|
|
@@ -343,9 +348,14 @@ const loose = (s) => searchText(s).replace(/[\s]/g, "");
|
|
|
343
348
|
export class KashiCitySelect extends KashiCombobox {
|
|
344
349
|
constructor() {
|
|
345
350
|
super(...arguments);
|
|
346
|
-
this.defaultLabel = "شهر";
|
|
347
351
|
this.custom = null;
|
|
348
352
|
}
|
|
353
|
+
get defaultLabel() {
|
|
354
|
+
return msg().city.label;
|
|
355
|
+
}
|
|
356
|
+
get defaultPlaceholder() {
|
|
357
|
+
return this.placeholderText();
|
|
358
|
+
}
|
|
349
359
|
get province() {
|
|
350
360
|
return this.getAttribute("province") || null;
|
|
351
361
|
}
|
|
@@ -367,7 +377,7 @@ export class KashiCitySelect extends KashiCombobox {
|
|
|
367
377
|
let o = city ? (list.find((x) => x.label === city && (!p || x.value.startsWith(`${p}${SEP}`))) ?? null) : null;
|
|
368
378
|
if (city && !o) {
|
|
369
379
|
this.custom = city;
|
|
370
|
-
o = { value: `custom${SEP}${city}`, label: city, description:
|
|
380
|
+
o = { value: `custom${SEP}${city}`, label: city, description: msg().city.outside };
|
|
371
381
|
}
|
|
372
382
|
this.selected = o;
|
|
373
383
|
this.query = o?.label ?? "";
|
|
@@ -403,11 +413,10 @@ export class KashiCitySelect extends KashiCombobox {
|
|
|
403
413
|
}
|
|
404
414
|
}
|
|
405
415
|
connectedCallback() {
|
|
406
|
-
this.defaultPlaceholder = this.placeholderText();
|
|
407
416
|
super.connectedCallback();
|
|
408
417
|
}
|
|
409
418
|
placeholderText() {
|
|
410
|
-
return this.province ?
|
|
419
|
+
return this.province ? msg().city.search : msg().city.hint;
|
|
411
420
|
}
|
|
412
421
|
source(query) {
|
|
413
422
|
const p = this.province;
|
|
@@ -415,10 +424,10 @@ export class KashiCitySelect extends KashiCombobox {
|
|
|
415
424
|
? getCities(p).map((c) => ({ value: `${p}${SEP}${c}`, label: c }))
|
|
416
425
|
: Object.entries(CITIES).flatMap(([prov, cities]) => cities.map((c) => ({ value: `${prov}${SEP}${c}`, label: c, description: prov })));
|
|
417
426
|
if (this.custom)
|
|
418
|
-
list.unshift({ value: `custom${SEP}${this.custom}`, label: this.custom, description:
|
|
427
|
+
list.unshift({ value: `custom${SEP}${this.custom}`, label: this.custom, description: msg().city.outside });
|
|
419
428
|
const q = query.trim();
|
|
420
429
|
if (this.getAttribute("allow-custom") !== "false" && q && q !== this.custom && !list.some((o) => loose(o.label) === loose(q)))
|
|
421
|
-
list.push({ value: `custom${SEP}${q}`, label: q, description:
|
|
430
|
+
list.push({ value: `custom${SEP}${q}`, label: q, description: msg().city.custom });
|
|
422
431
|
return list;
|
|
423
432
|
}
|
|
424
433
|
accepts(o, q) {
|
package/dist/controls.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { formatNumber, normalizePersian, onlyDigits, toEnDigits, toFaDigits } from "@kashi-ui/persian";
|
|
2
|
-
import {
|
|
2
|
+
import { bool, define, digitsOf, emit, esc, fieldHTML, HTMLBase, msg, num, setMessages, uid } from "./base.js";
|
|
3
3
|
const REMOVE = '<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="m6 6 8 8M14 6l-8 8"/></svg>';
|
|
4
4
|
/** چند فیلد پنهان با یک name، برای ارسال چند مقدار در فرم HTML */
|
|
5
5
|
function syncHiddenList(host, name, values) {
|
|
@@ -60,7 +60,7 @@ export class KashiTagInput extends HTMLBase {
|
|
|
60
60
|
render() {
|
|
61
61
|
const id = this.key;
|
|
62
62
|
const ariaLabel = this.getAttribute("aria-label");
|
|
63
|
-
const label = this.hasAttribute("label") ? this.getAttribute("label") : ariaLabel ? null :
|
|
63
|
+
const label = this.hasAttribute("label") ? this.getAttribute("label") : ariaLabel ? null : msg().tagInput.label;
|
|
64
64
|
const control = `<div class="k-tag-input"><input id="${id}" class="k-tag-input__control"${this.hasAttribute("disabled") ? " disabled" : ""}${!label && ariaLabel ? ` aria-label="${esc(ariaLabel)}"` : ""}></div><span class="k-sr-only" aria-live="polite"></span>`;
|
|
65
65
|
this.querySelector(":scope > .k-field")?.remove();
|
|
66
66
|
this.insertAdjacentHTML("afterbegin", fieldHTML({ id, label, control }));
|
|
@@ -107,7 +107,7 @@ export class KashiTagInput extends HTMLBase {
|
|
|
107
107
|
const t = normalizePersian(r).trim();
|
|
108
108
|
if (!t)
|
|
109
109
|
continue;
|
|
110
|
-
const err = next.length >= max ?
|
|
110
|
+
const err = next.length >= max ? msg().common.max(num(this, max)) : !dup && next.includes(t) ? msg().tagInput.duplicate : this.validate?.(t);
|
|
111
111
|
if (err) {
|
|
112
112
|
this.localError = err;
|
|
113
113
|
this.commit(next, added);
|
|
@@ -115,7 +115,7 @@ export class KashiTagInput extends HTMLBase {
|
|
|
115
115
|
return;
|
|
116
116
|
}
|
|
117
117
|
next = [...next, t];
|
|
118
|
-
added =
|
|
118
|
+
added = msg().common.added(t);
|
|
119
119
|
}
|
|
120
120
|
this.localError = null;
|
|
121
121
|
this.input.value = "";
|
|
@@ -123,7 +123,7 @@ export class KashiTagInput extends HTMLBase {
|
|
|
123
123
|
}
|
|
124
124
|
removeAt(i) {
|
|
125
125
|
const t = this.tags[i];
|
|
126
|
-
this.commit(this.tags.filter((_, j) => j !== i),
|
|
126
|
+
this.commit(this.tags.filter((_, j) => j !== i), msg().common.removed(t));
|
|
127
127
|
this.input.focus();
|
|
128
128
|
}
|
|
129
129
|
commit(next, announce) {
|
|
@@ -140,7 +140,7 @@ export class KashiTagInput extends HTMLBase {
|
|
|
140
140
|
box.querySelectorAll(".k-badge").forEach((b) => b.remove());
|
|
141
141
|
const tone = this.getAttribute("tone") ?? "brand";
|
|
142
142
|
const off = this.hasAttribute("disabled");
|
|
143
|
-
const badges = this.tags.map((t, i) => `<span class="k-badge${tone !== "neutral" ? ` k-badge--${esc(tone)}` : ""}${off ? "" : " k-badge--removable"}">${esc(t)}${off ? "" : `<button type="button" class="k-badge__remove" data-i="${i}" aria-label="
|
|
143
|
+
const badges = this.tags.map((t, i) => `<span class="k-badge${tone !== "neutral" ? ` k-badge--${esc(tone)}` : ""}${off ? "" : " k-badge--removable"}">${esc(t)}${off ? "" : `<button type="button" class="k-badge__remove" data-i="${i}" aria-label="${esc(msg().common.remove(t))}">${REMOVE}</button>`}</span>`).join("");
|
|
144
144
|
this.input.insertAdjacentHTML("beforebegin", badges);
|
|
145
145
|
this.input.placeholder = this.tags.length ? "" : (this.getAttribute("placeholder") ?? "");
|
|
146
146
|
this.paintMessages();
|
|
@@ -205,15 +205,15 @@ export class KashiNumberInput extends HTMLBase {
|
|
|
205
205
|
if (n === null)
|
|
206
206
|
return "";
|
|
207
207
|
const s = this.hasAttribute("thousands") ? formatNumber(n, { digits: "latin" }) : String(n);
|
|
208
|
-
return this
|
|
208
|
+
return digitsOf(this) === "latin" ? s : toFaDigits(s).replace(/,/g, "٬");
|
|
209
209
|
}
|
|
210
210
|
render() {
|
|
211
211
|
const id = this.key;
|
|
212
212
|
const ariaLabel = this.getAttribute("aria-label");
|
|
213
|
-
const label = this.hasAttribute("label") ? this.getAttribute("label") : ariaLabel ? null :
|
|
213
|
+
const label = this.hasAttribute("label") ? this.getAttribute("label") : ariaLabel ? null : msg().numberInput.label;
|
|
214
214
|
const disabled = this.hasAttribute("disabled");
|
|
215
215
|
const unit = this.getAttribute("unit");
|
|
216
|
-
const control = `<div class="k-number"><div class="k-input"${disabled ? ' data-state="disabled"' : ""}><button type="button" class="k-number__step" data-step="-1" tabindex="-1" aria-label="
|
|
216
|
+
const control = `<div class="k-number"><div class="k-input"${disabled ? ' data-state="disabled"' : ""}><button type="button" class="k-number__step" data-step="-1" tabindex="-1" aria-label="${esc(msg().numberInput.decrement)}"${disabled ? " disabled" : ""}>${MINUS}</button><input id="${id}" class="k-input__control" type="text" inputmode="decimal" role="spinbutton" dir="ltr"${disabled ? " disabled" : ""}${!label && ariaLabel ? ` aria-label="${esc(ariaLabel)}"` : ""}>${unit ? `<span class="k-input__addon">${esc(unit)}</span>` : ""}<button type="button" class="k-number__step" data-step="1" tabindex="-1" aria-label="${esc(msg().numberInput.increment)}"${disabled ? " disabled" : ""}>${PLUS}</button></div></div>`;
|
|
217
217
|
this.querySelector(":scope > .k-field")?.remove();
|
|
218
218
|
this.insertAdjacentHTML("afterbegin", fieldHTML({ id, label, control }));
|
|
219
219
|
this.input = this.querySelector("input.k-input__control");
|
|
@@ -290,7 +290,6 @@ export class KashiNumberInput extends HTMLBase {
|
|
|
290
290
|
}
|
|
291
291
|
KashiNumberInput.observedAttributes = ["label", "hint", "error", "value", "min", "max", "step", "thousands", "digits", "unit", "disabled", "name", "aria-label"];
|
|
292
292
|
const STAR = '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="currentColor" d="M12 2.8l2.8 5.9 6.4.8-4.7 4.4 1.2 6.3L12 17.1l-5.7 3.1 1.2-6.3L2.8 9.5l6.4-.8z"/><path class="k-rating__half" fill="none" d="M12 2.8v14.3l-5.7 3.1 1.2-6.3L2.8 9.5l6.4-.8z"/></svg>';
|
|
293
|
-
const faNum = (n) => toFaDigits(String(n)).replace(".", "٫");
|
|
294
293
|
/** امتیاز ستارهای روی رادیوهای بومی؛ با readonly فقط نمایش (با نیمستاره برای امتیاز اعشاری) */
|
|
295
294
|
export class KashiRating extends HTMLBase {
|
|
296
295
|
constructor() {
|
|
@@ -322,19 +321,20 @@ export class KashiRating extends HTMLBase {
|
|
|
322
321
|
}
|
|
323
322
|
render() {
|
|
324
323
|
const max = Number(this.getAttribute("max")) || 5;
|
|
325
|
-
const label = this.getAttribute("label") ??
|
|
324
|
+
const label = this.getAttribute("label") ?? msg().rating.label;
|
|
325
|
+
const faNum = (x) => num(this, x).replace(".", msg().common.decimal);
|
|
326
326
|
const size = this.getAttribute("size");
|
|
327
327
|
const cls = `k-rating${size === "sm" || size === "lg" ? ` k-rating--${size}` : ""}`;
|
|
328
328
|
const c = this.current;
|
|
329
329
|
const stars = Array.from({ length: max }, (_, i) => i + 1);
|
|
330
330
|
const valueHtml = this.hasAttribute("show-value") ? `<span class="k-rating__value" aria-hidden="true">${faNum(c)}</span>` : "";
|
|
331
331
|
if (this.hasAttribute("readonly")) {
|
|
332
|
-
this.innerHTML = `<span class="${cls}" data-readonly="" role="img" aria-label="${esc(`${label}: ${faNum(c)
|
|
332
|
+
this.innerHTML = `<span class="${cls}" data-readonly="" role="img" aria-label="${esc(`${label}: ${msg().common.of(faNum(c), faNum(max))}`)}">${stars.map((n) => `<span class="k-rating__star"${n <= Math.floor(c) ? ' data-filled=""' : ""}${n === Math.ceil(c) && c % 1 >= 0.25 ? ' data-half=""' : ""}>${STAR}</span>`).join("")}${valueHtml}</span>`;
|
|
333
333
|
return;
|
|
334
334
|
}
|
|
335
335
|
const name = this.getAttribute("name") ?? this.key;
|
|
336
336
|
const off = this.hasAttribute("disabled");
|
|
337
|
-
this.innerHTML = `<fieldset class="${cls}"><legend class="k-sr-only">${esc(label)}</legend>${stars.map((n) => `<label class="k-rating__star"${n <= c ? ' data-filled=""' : ""}><input type="radio" class="k-sr-only" name="${esc(name)}" value="${n}"${c === n ? " checked" : ""}${off ? " disabled" : ""} aria-label="${faNum(n)
|
|
337
|
+
this.innerHTML = `<fieldset class="${cls}"><legend class="k-sr-only">${esc(label)}</legend>${stars.map((n) => `<label class="k-rating__star"${n <= c ? ' data-filled=""' : ""}><input type="radio" class="k-sr-only" name="${esc(name)}" value="${n}"${c === n ? " checked" : ""}${off ? " disabled" : ""} aria-label="${esc(msg().common.of(faNum(n), faNum(max)))}">${STAR}</label>`).join("")}${valueHtml}</fieldset>`;
|
|
338
338
|
const fs = this.querySelector("fieldset");
|
|
339
339
|
const fill = (upto) => fs.querySelectorAll(".k-rating__star").forEach((s, i) => s.toggleAttribute("data-filled", i + 1 <= upto));
|
|
340
340
|
fs.addEventListener("change", (e) => {
|
|
@@ -390,14 +390,14 @@ export class KashiPagination extends HTMLBase {
|
|
|
390
390
|
}
|
|
391
391
|
render() {
|
|
392
392
|
const page = Math.min(this.page, this.total), total = this.total;
|
|
393
|
-
const n = (v) => (this
|
|
393
|
+
const n = (v) => (digitsOf(this) === "latin" ? String(v) : toFaDigits(v));
|
|
394
394
|
const href = this.getAttribute("href");
|
|
395
395
|
const focused = this.contains(document.activeElement) ? document.activeElement.dataset.to : null;
|
|
396
396
|
const item = (to, body, cls, { current = false, disabled = false, aria = "" } = {}) => href && !disabled
|
|
397
397
|
? `<a class="${cls}" href="${esc(href.replace("{page}", String(to)))}" data-to="${to}"${current ? ' aria-current="page"' : ""}${aria ? ` aria-label="${esc(aria)}"` : ""}>${body}</a>`
|
|
398
398
|
: `<button type="button" class="${cls}" data-to="${to}"${disabled ? " disabled" : ""}${current ? ' aria-current="page"' : ""}${aria ? ` aria-label="${esc(aria)}"` : ""}>${body}</button>`;
|
|
399
399
|
const summary = this.getAttribute("summary");
|
|
400
|
-
this.innerHTML = `<nav class="k-pagination" aria-label="${esc(this.getAttribute("label") ??
|
|
400
|
+
this.innerHTML = `<nav class="k-pagination" aria-label="${esc(this.getAttribute("label") ?? msg().pagination.nav)}">${item(page - 1, `${chevron("m12 5-5 5 5 5")}${esc(msg().common.prev)}`, "k-btn k-btn--secondary k-btn--sm", { disabled: page <= 1 })}${summary ? `<span class="k-pagination__summary">${esc(summary)}</span>` : ""}<ul class="k-pagination__pages" role="list">${pageRange(page, total, Number(this.getAttribute("siblings") ?? 1)).map((p) => `<li>${p === null ? '<span class="k-pagination__ellipsis" aria-hidden="true">…</span>' : item(p, n(p), "k-pagination__page", { current: p === page, aria: msg().pagination.page(n(p)) })}</li>`).join("")}</ul>${item(page + 1, `${esc(msg().common.next)}${chevron("m8 5 5 5-5 5")}`, "k-btn k-btn--secondary k-btn--sm", { disabled: page >= total })}</nav>`;
|
|
401
401
|
this.querySelector("nav").addEventListener("click", (e) => {
|
|
402
402
|
const b = e.target.closest("button[data-to]");
|
|
403
403
|
if (!b || b.disabled)
|
package/dist/display.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { formatJalali, formatMoney, formatRelative, fromISODate, numberToWords, rialToToman, toFaDigits, tomanToRial } from "@kashi-ui/persian";
|
|
2
|
-
import {
|
|
2
|
+
import { define, digitsOf, emit, esc, HTMLBase, lang, msg } from "./base.js";
|
|
3
3
|
/** «2026-03-21» به نیمهشب محلی، و بقیهٔ قالبها (با ساعت) با Date */
|
|
4
4
|
const parseDateTime = (text) => {
|
|
5
5
|
if (!text)
|
|
@@ -24,19 +24,21 @@ export class KashiMoney extends Display {
|
|
|
24
24
|
const unit = this.getAttribute("unit") === "rial" ? "rial" : "toman";
|
|
25
25
|
const from = this.getAttribute("from") === "rial" ? "rial" : this.getAttribute("from") === "toman" ? "toman" : unit;
|
|
26
26
|
const compact = this.hasAttribute("compact");
|
|
27
|
-
const digits = this
|
|
27
|
+
const digits = digitsOf(this);
|
|
28
28
|
const signed = this.hasAttribute("signed");
|
|
29
29
|
const size = this.getAttribute("size") ?? "md";
|
|
30
30
|
const n = from === unit ? value : from === "rial" ? rialToToman(value) : tomanToRial(value);
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
const
|
|
31
|
+
const locale = lang(), M = msg();
|
|
32
|
+
const amount = formatMoney(n, { unit, compact, digits, locale, showUnit: false });
|
|
33
|
+
const full = formatMoney(n, { unit, compact, locale });
|
|
34
|
+
const unitName = unit === "toman" ? M.money.toman : M.money.rial;
|
|
35
|
+
const unitText = full.endsWith("همت") ? null : unitName;
|
|
34
36
|
const spoken = this.getAttribute("words") !== "false" && Number.isSafeInteger(Math.round(n))
|
|
35
|
-
? `${numberToWords(Math.round(n))} ${
|
|
37
|
+
? `${numberToWords(Math.round(n), { locale })} ${unitName}`
|
|
36
38
|
: null;
|
|
37
39
|
const tone = signed ? (n > 0 ? "positive" : n < 0 ? "negative" : null) : null;
|
|
38
40
|
const hide = spoken ? ' aria-hidden="true"' : "";
|
|
39
|
-
this.innerHTML = `<data value="${n}" class="k-money k-money--${esc(size)}"${tone ? ` data-tone="${tone}"` : ""}${spoken ? ` title="${esc(spoken)}"` : ""}><span class="k-money__amount"${hide}>${signed && n > 0 ? "+" : ""}${esc(amount)}</span>${unitText ? `<span class="k-money__unit"${hide}>${unitText}</span>` : ""}${spoken ? `<span class="k-sr-only">${n < 0 ? "" : signed && n > 0 ?
|
|
41
|
+
this.innerHTML = `<data value="${n}" class="k-money k-money--${esc(size)}"${tone ? ` data-tone="${tone}"` : ""}${spoken ? ` title="${esc(spoken)}"` : ""}><span class="k-money__amount"${hide}>${signed && n > 0 ? "+" : ""}${esc(amount)}</span>${unitText ? `<span class="k-money__unit"${hide}>${unitText}</span>` : ""}${spoken ? `<span class="k-sr-only">${n < 0 ? "" : signed && n > 0 ? M.money.positive : ""}${esc(spoken)}</span>` : ""}</data>`;
|
|
40
42
|
}
|
|
41
43
|
}
|
|
42
44
|
KashiMoney.observedAttributes = ["value", "unit", "from", "compact", "digits", "signed", "words", "size"];
|
|
@@ -50,8 +52,8 @@ export class KashiDate extends Display {
|
|
|
50
52
|
}
|
|
51
53
|
const f = this.getAttribute("format");
|
|
52
54
|
const style = f === "numeric" || f === "full" ? f : "long";
|
|
53
|
-
const digits = this
|
|
54
|
-
this.innerHTML = `<time datetime="${esc(this.getAttribute("datetime"))}">${esc(formatJalali(d, { style, digits }))}</time>`;
|
|
55
|
+
const digits = digitsOf(this);
|
|
56
|
+
this.innerHTML = `<time datetime="${esc(this.getAttribute("datetime"))}">${esc(formatJalali(d, { style, digits, locale: lang() }))}</time>`;
|
|
55
57
|
}
|
|
56
58
|
}
|
|
57
59
|
KashiDate.observedAttributes = ["datetime", "format", "digits"];
|
|
@@ -71,8 +73,8 @@ export class KashiRelativeTime extends Display {
|
|
|
71
73
|
this.textContent = "";
|
|
72
74
|
return;
|
|
73
75
|
}
|
|
74
|
-
const digits = this
|
|
75
|
-
this.innerHTML = `<time datetime="${d.toISOString()}" title="${esc(formatJalali(d, { style: "full", digits }))}">${esc(formatRelative(d, { digits }))}</time>`;
|
|
76
|
+
const digits = digitsOf(this);
|
|
77
|
+
this.innerHTML = `<time datetime="${d.toISOString()}" title="${esc(formatJalali(d, { style: "full", digits, locale: lang() }))}">${esc(formatRelative(d, { digits, locale: lang() }))}</time>`;
|
|
76
78
|
}
|
|
77
79
|
}
|
|
78
80
|
KashiRelativeTime.observedAttributes = ["datetime", "digits", "refresh"];
|
|
@@ -114,14 +116,15 @@ export class KashiCountdown extends HTMLBase {
|
|
|
114
116
|
render() {
|
|
115
117
|
const left = this.remaining;
|
|
116
118
|
const d = Math.floor(left / 86400), h = Math.floor((left % 86400) / 3600), m = Math.floor((left % 3600) / 60), s = left % 60;
|
|
117
|
-
const fa = this
|
|
119
|
+
const fa = digitsOf(this) === "fa";
|
|
118
120
|
const f = (x) => (fa ? toFaDigits(String(x)) : String(x));
|
|
119
|
-
const
|
|
121
|
+
const C = msg().countdown;
|
|
122
|
+
const spoken = [d && `${f(d)} ${C.days}`, h && `${f(h)} ${C.hours}`, m && `${f(m)} ${C.minutes}`, `${f(s)} ${C.seconds}`].filter(Boolean).join(msg().common.and);
|
|
120
123
|
const label = this.getAttribute("label");
|
|
121
124
|
const aria = esc(label ? `${label}: ${spoken}` : spoken);
|
|
122
125
|
const doneAttr = left === 0 ? ' data-done=""' : "";
|
|
123
126
|
if (this.getAttribute("variant") === "blocks") {
|
|
124
|
-
const units = [...(d ? [[d,
|
|
127
|
+
const units = [...(d ? [[d, C.days]] : []), [h, C.hours], [m, C.minutes], [s, C.seconds]];
|
|
125
128
|
this.innerHTML = `<div role="timer" aria-label="${aria}" class="k-countdown k-countdown--blocks"${doneAttr}>${units.map(([v, name]) => `<span class="k-countdown__unit" aria-hidden="true"><span class="k-countdown__num">${f(pad(v))}</span><span class="k-countdown__name">${name}</span></span>`).join("")}</div>`;
|
|
126
129
|
}
|
|
127
130
|
else {
|
package/dist/fields.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { onlyDigits, toFaDigits } from "@kashi-ui/persian";
|
|
2
|
-
import {
|
|
2
|
+
import { define, digitsOf, emit, esc, fieldHTML, HTMLBase, msg, setMessages, syncHidden, uid } from "./base.js";
|
|
3
3
|
/**
|
|
4
4
|
* کد تأیید یکبارمصرف: یک input واقعی (نه شش input جدا)، پس چسباندن، پاک کردن،
|
|
5
5
|
* صفحهخوان و پر شدن خودکار کد پیامکی (autocomplete="one-time-code") کار میکنند.
|
|
@@ -49,7 +49,7 @@ export class KashiOtp extends HTMLBase {
|
|
|
49
49
|
const length = this.length;
|
|
50
50
|
const group = Number(this.getAttribute("group-after")) || 0;
|
|
51
51
|
const ariaLabel = this.getAttribute("aria-label");
|
|
52
|
-
const label = this.hasAttribute("label") ? this.getAttribute("label") : ariaLabel ? null :
|
|
52
|
+
const label = this.hasAttribute("label") ? this.getAttribute("label") : ariaLabel ? null : msg().otp.label;
|
|
53
53
|
const slots = Array.from({ length }, (_, i) => `${group && i === group ? '<span class="k-otp__sep" aria-hidden="true"></span>' : ""}<span class="k-otp__slot" aria-hidden="true"></span>`).join("");
|
|
54
54
|
const control = `<div class="k-otp${this.getAttribute("size") === "sm" ? " k-otp--sm" : ""}"><input id="${id}" class="k-otp__input" type="text" inputmode="numeric" autocomplete="one-time-code" pattern="\\d*" maxlength="${length * 2}"${this.hasAttribute("disabled") ? " disabled" : ""}${!label && ariaLabel ? ` aria-label="${esc(ariaLabel)}"` : ""}>${slots}</div>`;
|
|
55
55
|
this.querySelector(":scope > .k-field")?.remove();
|
|
@@ -88,7 +88,7 @@ export class KashiOtp extends HTMLBase {
|
|
|
88
88
|
}
|
|
89
89
|
/** فقط خانهها؛ مقدار input را دست نمیزند تا تایپ کاربر پیش از رویداد input پاک نشود */
|
|
90
90
|
paintSlots() {
|
|
91
|
-
const fa = this
|
|
91
|
+
const fa = digitsOf(this) === "fa";
|
|
92
92
|
const focused = this.ownerDocument.activeElement === this.input;
|
|
93
93
|
const active = Math.min(this.caret, this.length - 1);
|
|
94
94
|
this.querySelectorAll(".k-otp__slot").forEach((slot, i) => {
|
package/dist/forms.d.ts
CHANGED
|
@@ -93,7 +93,7 @@ declare abstract class TextField extends HTMLBase {
|
|
|
93
93
|
protected valueSet: boolean;
|
|
94
94
|
protected text: string;
|
|
95
95
|
protected abstract multiline: boolean;
|
|
96
|
-
protected abstract defaultLabel: string;
|
|
96
|
+
protected abstract get defaultLabel(): string;
|
|
97
97
|
protected abstract hint(): string | null;
|
|
98
98
|
connectedCallback(): void;
|
|
99
99
|
attributeChangedCallback(name: string, old: string | null, next: string | null): void;
|
|
@@ -109,7 +109,7 @@ declare abstract class TextField extends HTMLBase {
|
|
|
109
109
|
export declare class KashiPersianTextarea extends TextField {
|
|
110
110
|
static observedAttributes: string[];
|
|
111
111
|
protected multiline: boolean;
|
|
112
|
-
protected defaultLabel: string;
|
|
112
|
+
protected get defaultLabel(): string;
|
|
113
113
|
protected render(): void;
|
|
114
114
|
private result;
|
|
115
115
|
protected hint(): string | null;
|
|
@@ -118,7 +118,7 @@ export declare class KashiPersianTextarea extends TextField {
|
|
|
118
118
|
export declare class KashiKeyboardFix extends TextField {
|
|
119
119
|
static observedAttributes: string[];
|
|
120
120
|
protected multiline: boolean;
|
|
121
|
-
protected defaultLabel: string;
|
|
121
|
+
protected get defaultLabel(): string;
|
|
122
122
|
protected render(): void;
|
|
123
123
|
protected hint(): string | null;
|
|
124
124
|
}
|