@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.
- 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/map.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EMPTY_ADDRESS, PROVINCES, formatNumber, getCities } from "@kashi-ui/persian";
|
|
2
|
-
import {
|
|
2
|
+
import { afterParse, bool, define, digitsOf, emit, esc, HTMLBase, msg, uid } from "./base.js";
|
|
3
3
|
import { IRAN_PROVINCES, IRAN_VIEWBOX } from "./iranMapData.js";
|
|
4
4
|
// فرم نشانی از این عنصرها ساخته شده؛ با import همین فایل بهتنهایی هم ثبت شوند
|
|
5
5
|
import "./combobox.js";
|
|
@@ -83,7 +83,7 @@ export class KashiIranMap extends HTMLBase {
|
|
|
83
83
|
return this.multiple || bool(this, "selectable") || this.hasAttribute("value") || this.hasAttribute("name");
|
|
84
84
|
}
|
|
85
85
|
fmt(n) {
|
|
86
|
-
return formatNumber(n);
|
|
86
|
+
return formatNumber(n, { digits: digitsOf(this) });
|
|
87
87
|
}
|
|
88
88
|
level(n) {
|
|
89
89
|
const data = this.values;
|
|
@@ -115,7 +115,7 @@ export class KashiIranMap extends HTMLBase {
|
|
|
115
115
|
return typeof v === "number" ? `${n}: ${this.fmt(v)}${unit ? ` ${unit}` : ""}` : n;
|
|
116
116
|
}
|
|
117
117
|
render() {
|
|
118
|
-
const label = this.getAttribute("label") ??
|
|
118
|
+
const label = this.getAttribute("label") ?? msg().iranMap.label;
|
|
119
119
|
const sel = this.selectable;
|
|
120
120
|
const multi = this.multiple;
|
|
121
121
|
const data = this.values;
|
|
@@ -133,7 +133,7 @@ export class KashiIranMap extends HTMLBase {
|
|
|
133
133
|
? `<table class="k-sr-only"><caption>${esc(label)}</caption><tbody>${NAMES.filter((n) => typeof data[n] === "number").sort((a, b) => data[b] - data[a]).map((n) => `<tr><th scope="row">${esc(n)}</th><td>${this.fmt(data[n])}${unit ? ` ${esc(unit)}` : ""}</td></tr>`).join("")}</tbody></table>`
|
|
134
134
|
: "";
|
|
135
135
|
// نسبت ابعاد با aspect-ratio، نه padding درصدی: در چیدمان flex ستونی ارتفاع padding درصدی صفر حساب میشود
|
|
136
|
-
this.innerHTML = `<figure class="k-iran-map${sel ? " k-iran-map--selectable" : ""}"><div class="k-iran-map__canvas" style="block-size: auto; aspect-ratio: ${VW} / ${VH}"><svg viewBox="${IRAN_VIEWBOX}" class="k-iran-map__svg" role="${sel ? (multi ? "group" : "radiogroup") : "img"}" aria-label="${esc(sel ? label : `${label}${data ?
|
|
136
|
+
this.innerHTML = `<figure class="k-iran-map${sel ? " k-iran-map--selectable" : ""}"><div class="k-iran-map__canvas" style="block-size: auto; aspect-ratio: ${VW} / ${VH}"><svg viewBox="${IRAN_VIEWBOX}" class="k-iran-map__svg" role="${sel ? (multi ? "group" : "radiogroup") : "img"}" aria-label="${esc(sel ? label : `${label}${data ? msg().iranMap.tableNote : ""}`)}">${paths}</svg><div class="k-chart__tip" aria-hidden="true" style="display: none"></div></div>${legend}${table}</figure>`;
|
|
137
137
|
const svg = this.querySelector("svg");
|
|
138
138
|
const fig = this.querySelector("figure");
|
|
139
139
|
svg.addEventListener("pointerover", (e) => {
|
|
@@ -235,7 +235,7 @@ export class KashiIranMap extends HTMLBase {
|
|
|
235
235
|
const w = this.querySelector("figure").clientWidth || VW;
|
|
236
236
|
const v = this.values?.[n];
|
|
237
237
|
const unit = this.getAttribute("unit");
|
|
238
|
-
const row = typeof v === "number" ? `${this.fmt(v)}${unit ? ` ${unit}` : ""}` :
|
|
238
|
+
const row = typeof v === "number" ? `${this.fmt(v)}${unit ? ` ${unit}` : ""}` : msg().province.capital(CAPITAL[n]);
|
|
239
239
|
box.innerHTML = `<span class="k-chart__tip-title">${esc(n)}</span><span class="k-chart__tip-row"><strong>${esc(row)}</strong></span>`;
|
|
240
240
|
// داخل ظرف بماند و راهنمای استانهای شمالی بالای نقشه بیرون نزند
|
|
241
241
|
box.style.left = `${Math.min(Math.max((p.x / VW) * w, 80), w - 80)}px`;
|
|
@@ -244,7 +244,10 @@ export class KashiIranMap extends HTMLBase {
|
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
246
|
KashiIranMap.observedAttributes = ["data", "value", "label", "unit", "steps", "scale", "legend", "selectable", "multiple", "name"];
|
|
247
|
-
const LABELS =
|
|
247
|
+
const LABELS = () => {
|
|
248
|
+
const A = msg().address;
|
|
249
|
+
return { province: A.province, city: A.city, street: A.street, plaque: A.plaque, unit: A.unit, postalCode: A.postalCode };
|
|
250
|
+
};
|
|
248
251
|
/**
|
|
249
252
|
* فرم نشانی ایرانی: استان، شهرِ همان استان، نشانی، پلاک، واحد و کد پستی دهرقمی؛ با map نقشهٔ ایران برای انتخاب استان.
|
|
250
253
|
* با انتخاب شهر بدون استان، استان خودکار پر میشود و با عوض شدن استان، شهرِ نامربوط پاک میشود.
|
|
@@ -291,7 +294,7 @@ export class KashiAddressForm extends HTMLBase {
|
|
|
291
294
|
}
|
|
292
295
|
/** خطای هر فیلد، مثلاً از اعتبارسنجی سرور: { plaque: "پلاک را بنویسید." } */
|
|
293
296
|
set errors(e) {
|
|
294
|
-
for (const f of Object.keys(LABELS)) {
|
|
297
|
+
for (const f of Object.keys(LABELS())) {
|
|
295
298
|
const el = this.querySelector(`[data-field="${f}"]`);
|
|
296
299
|
if (!el)
|
|
297
300
|
continue;
|
|
@@ -306,7 +309,7 @@ export class KashiAddressForm extends HTMLBase {
|
|
|
306
309
|
}
|
|
307
310
|
}
|
|
308
311
|
label(f) {
|
|
309
|
-
return this.getAttribute(`label-${f === "postalCode" ? "postal-code" : f}`) ?? LABELS[f];
|
|
312
|
+
return this.getAttribute(`label-${f === "postalCode" ? "postal-code" : f}`) ?? LABELS()[f];
|
|
310
313
|
}
|
|
311
314
|
render() {
|
|
312
315
|
const a = this.address;
|
|
@@ -317,18 +320,18 @@ export class KashiAddressForm extends HTMLBase {
|
|
|
317
320
|
const map = bool(this, "map");
|
|
318
321
|
const k = this.key;
|
|
319
322
|
const reqMark = req ? '<span class="k-label__required" aria-hidden="true">*</span>' : "";
|
|
320
|
-
const legend = this.getAttribute("legend") ??
|
|
323
|
+
const legend = this.getAttribute("legend") ?? msg().address.legend;
|
|
321
324
|
this.innerHTML = `<fieldset class="k-address${map ? " k-address--map" : ""}"${this.hasAttribute("disabled") ? " disabled" : ""}>
|
|
322
325
|
${legend ? `<legend class="k-address__legend">${esc(legend)}</legend>` : ""}
|
|
323
326
|
<div class="k-address__fields">
|
|
324
|
-
<kashi-province-select class="k-address__province" data-field="province" label="${esc(this.label("province"))}" show-capital="false" placeholder="
|
|
327
|
+
<kashi-province-select class="k-address__province" data-field="province" label="${esc(this.label("province"))}" show-capital="false" placeholder="${esc(msg().address.provinceSearch)}"${n("province")}${r}></kashi-province-select>
|
|
325
328
|
<kashi-city-select class="k-address__city" data-field="city" label="${esc(this.label("city"))}"${n("city")}${r}></kashi-city-select>
|
|
326
|
-
<div class="k-field k-address__street" data-field="street"><div class="k-field__label-row"><label class="k-label" for="${k}-street">${esc(this.label("street"))}${reqMark}</label></div><div class="k-input k-input--textarea k-input--autoresize"><textarea id="${k}-street" class="k-input__control" rows="2" autocomplete="street-address" placeholder="
|
|
329
|
+
<div class="k-field k-address__street" data-field="street"><div class="k-field__label-row"><label class="k-label" for="${k}-street">${esc(this.label("street"))}${reqMark}</label></div><div class="k-input k-input--textarea k-input--autoresize"><textarea id="${k}-street" class="k-input__control" rows="2" autocomplete="street-address" placeholder="${esc(msg().address.streetHint)}"${n("street")}${r}></textarea></div></div>
|
|
327
330
|
<div class="k-field k-address__plaque" data-field="plaque"><div class="k-field__label-row"><label class="k-label" for="${k}-plaque">${esc(this.label("plaque"))}${reqMark}</label></div><div class="k-input"><input id="${k}-plaque" class="k-input__control" inputmode="numeric" autocomplete="address-line2"${n("plaque")}${r}></div></div>
|
|
328
|
-
<div class="k-field k-address__unit" data-field="unit"><div class="k-field__label-row"><label class="k-label" for="${k}-unit">${esc(this.label("unit"))}${req ?
|
|
331
|
+
<div class="k-field k-address__unit" data-field="unit"><div class="k-field__label-row"><label class="k-label" for="${k}-unit">${esc(this.label("unit"))}${req ? `<span class="k-label__optional">${esc(msg().common.optional)}</span>` : ""}</label></div><div class="k-input"><input id="${k}-unit" class="k-input__control" inputmode="numeric" autocomplete="address-line3"${n("unit")}></div></div>
|
|
329
332
|
<kashi-postal-code class="k-address__postal" data-field="postalCode" label="${esc(this.label("postalCode"))}"${n("postalCode")}${r}></kashi-postal-code>
|
|
330
333
|
</div>
|
|
331
|
-
${map ? `<kashi-iran-map class="k-address__map" selectable label="${esc(
|
|
334
|
+
${map ? `<kashi-iran-map class="k-address__map" selectable label="${esc(msg().iranMap.onMap(this.label("province")))}"></kashi-iran-map>` : ""}
|
|
332
335
|
</fieldset>`;
|
|
333
336
|
this.built = true;
|
|
334
337
|
const province = this.querySelector("kashi-province-select");
|
package/dist/nav.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { normalizePersian
|
|
2
|
-
import {
|
|
1
|
+
import { normalizePersian } from "@kashi-ui/persian";
|
|
2
|
+
import { afterParse, bool, define, emit, esc, HTMLBase, msg, num, uid } from "./base.js";
|
|
3
3
|
const search = (s) => normalizePersian(s, { digits: "latin" }).toLowerCase();
|
|
4
4
|
const items = (menu) => [...menu.querySelectorAll('[role^="menuitem"]:not([aria-disabled="true"])')];
|
|
5
5
|
const SEARCH = '<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" aria-hidden="true"><circle cx="9" cy="9" r="5.5"/><path d="m13.5 13.5 3 3"/></svg>';
|
|
@@ -68,9 +68,10 @@ export class KashiCommandPalette extends HTMLBase {
|
|
|
68
68
|
build() {
|
|
69
69
|
const d = document.createElement("dialog");
|
|
70
70
|
d.className = "k-dialog k-command";
|
|
71
|
-
const
|
|
71
|
+
const M = msg();
|
|
72
|
+
const title = this.getAttribute("label") ?? M.command.title;
|
|
72
73
|
d.setAttribute("aria-label", title);
|
|
73
|
-
d.innerHTML = `<div class="k-input"><span class="k-input__addon">${SEARCH}</span><input class="k-input__control" type="text" role="combobox" aria-expanded="true" aria-controls="${this.key}-list" aria-autocomplete="list" aria-label="${esc(title)}" placeholder="${esc(this.getAttribute("placeholder") ??
|
|
74
|
+
d.innerHTML = `<div class="k-input"><span class="k-input__addon">${SEARCH}</span><input class="k-input__control" type="text" role="combobox" aria-expanded="true" aria-controls="${this.key}-list" aria-autocomplete="list" aria-label="${esc(title)}" placeholder="${esc(this.getAttribute("placeholder") ?? M.command.placeholder)}" autocomplete="off"></div><ul id="${this.key}-list" role="listbox" aria-label="${esc(title)}" class="k-listbox k-listbox--inline k-command__list"></ul><div class="k-command__footer" aria-hidden="true"><span><kbd class="k-kbd">↑</kbd><kbd class="k-kbd">↓</kbd>${esc(M.command.navigate)}</span><span><kbd class="k-kbd">Enter</kbd>${esc(M.command.select)}</span><span><kbd class="k-kbd">Esc</kbd>${esc(M.common.close)}</span></div><span class="k-sr-only" aria-live="polite"></span>`;
|
|
74
75
|
this.append(d);
|
|
75
76
|
this.dialog = d;
|
|
76
77
|
const input = d.querySelector("input");
|
|
@@ -152,14 +153,14 @@ export class KashiCommandPalette extends HTMLBase {
|
|
|
152
153
|
last = it.group;
|
|
153
154
|
return `${header}<li id="${this.key}-list-${i}" data-i="${i}" role="option" aria-selected="${i === this.active}"${i === this.active ? " data-active" : ""} class="k-option"><span class="k-option__body"><span>${mark(it.label)}</span>${it.description ? `<span class="k-option__desc">${esc(it.description)}</span>` : ""}</span>${it.shortcut ? `<span class="k-menu__shortcut" aria-hidden="true">${esc(it.shortcut)}</span>` : ""}</li>`;
|
|
154
155
|
}).join("")
|
|
155
|
-
:
|
|
156
|
+
: `<li role="presentation" class="k-listbox__empty">${esc(msg().command.empty)}</li>`;
|
|
156
157
|
const input = d.querySelector("input");
|
|
157
158
|
if (list.length)
|
|
158
159
|
input.setAttribute("aria-activedescendant", `${this.key}-list-${this.active}`);
|
|
159
160
|
else
|
|
160
161
|
input.removeAttribute("aria-activedescendant");
|
|
161
162
|
d.querySelector(`#${this.key}-list-${this.active}`)?.scrollIntoView?.({ block: "nearest" });
|
|
162
|
-
d.querySelector(":scope > .k-sr-only").textContent = this.query ?
|
|
163
|
+
d.querySelector(":scope > .k-sr-only").textContent = this.query ? msg().common.results(num(this, list.length)) : "";
|
|
163
164
|
}
|
|
164
165
|
}
|
|
165
166
|
KashiCommandPalette.observedAttributes = ["placeholder", "label", "shortcut"];
|
|
@@ -192,7 +193,7 @@ export class KashiContextMenu extends HTMLBase {
|
|
|
192
193
|
this.setAttribute("aria-describedby", `${this.key}-hint`);
|
|
193
194
|
const menu = document.createElement("ul");
|
|
194
195
|
menu.setAttribute("role", "menu");
|
|
195
|
-
menu.setAttribute("aria-label", this.getAttribute("label") ??
|
|
196
|
+
menu.setAttribute("aria-label", this.getAttribute("label") ?? msg().contextMenu.label);
|
|
196
197
|
menu.className = "k-menu k-menu--context";
|
|
197
198
|
menu.hidden = true;
|
|
198
199
|
for (const el of [...(source?.children ?? [])]) {
|
|
@@ -221,7 +222,7 @@ export class KashiContextMenu extends HTMLBase {
|
|
|
221
222
|
menu.append(li);
|
|
222
223
|
}
|
|
223
224
|
source?.remove();
|
|
224
|
-
this.insertAdjacentHTML("beforeend", `<span id="${this.key}-hint" class="k-sr-only"
|
|
225
|
+
this.insertAdjacentHTML("beforeend", `<span id="${this.key}-hint" class="k-sr-only">${esc(msg().contextMenu.hint)}</span>`);
|
|
225
226
|
this.append(menu);
|
|
226
227
|
this.menu = menu;
|
|
227
228
|
this.addEventListener("contextmenu", (e) => {
|
|
@@ -333,11 +334,12 @@ export class KashiCarousel extends HTMLBase {
|
|
|
333
334
|
return Math.max(1, this.slides.length - this.perView + 1);
|
|
334
335
|
}
|
|
335
336
|
build() {
|
|
336
|
-
const
|
|
337
|
+
const M = msg();
|
|
338
|
+
const label = this.getAttribute("label") ?? M.carousel.slider;
|
|
337
339
|
const children = [...this.children];
|
|
338
340
|
const section = document.createElement("section");
|
|
339
341
|
section.className = "k-carousel";
|
|
340
|
-
section.setAttribute("aria-roledescription",
|
|
342
|
+
section.setAttribute("aria-roledescription", M.carousel.slider);
|
|
341
343
|
section.setAttribute("aria-label", label);
|
|
342
344
|
section.style.setProperty("--k-carousel-per-view", String(this.perView));
|
|
343
345
|
const track = document.createElement("div");
|
|
@@ -348,8 +350,8 @@ export class KashiCarousel extends HTMLBase {
|
|
|
348
350
|
const s = document.createElement("div");
|
|
349
351
|
s.className = "k-carousel__slide";
|
|
350
352
|
s.setAttribute("role", "group");
|
|
351
|
-
s.setAttribute("aria-roledescription",
|
|
352
|
-
s.setAttribute("aria-label",
|
|
353
|
+
s.setAttribute("aria-roledescription", M.carousel.slide);
|
|
354
|
+
s.setAttribute("aria-label", M.carousel.slideOf(num(this, i + 1), num(this, children.length)));
|
|
353
355
|
s.id = `${this.key}-s${i}`;
|
|
354
356
|
s.append(c);
|
|
355
357
|
track.append(s);
|
|
@@ -359,7 +361,7 @@ export class KashiCarousel extends HTMLBase {
|
|
|
359
361
|
this.track = track;
|
|
360
362
|
if (this.pages > 1) {
|
|
361
363
|
const dots = this.getAttribute("dots") !== "false";
|
|
362
|
-
section.insertAdjacentHTML("beforeend", `<div class="k-carousel__bar">${dots ? `<ul class="k-carousel__dots" aria-label="
|
|
364
|
+
section.insertAdjacentHTML("beforeend", `<div class="k-carousel__bar">${dots ? `<ul class="k-carousel__dots" aria-label="${esc(M.carousel.slides(label))}">${Array.from({ length: this.pages }, (_, i) => `<li><button type="button" class="k-carousel__dot" data-go="${i}" aria-label="${esc(M.carousel.slideOf(num(this, i + 1), num(this, this.pages)))}" aria-controls="${this.key}-s${i}"></button></li>`).join("")}</ul>` : "<span></span>"}<div class="k-carousel__nav"><button type="button" class="k-btn k-btn--secondary k-btn--icon k-btn--sm" data-step="-1" aria-label="${esc(M.common.prev)}">${ARROW(false)}</button><button type="button" class="k-btn k-btn--secondary k-btn--icon k-btn--sm" data-step="1" aria-label="${esc(M.common.next)}">${ARROW(true)}</button></div></div>`);
|
|
363
365
|
section.querySelector(".k-carousel__bar").addEventListener("click", (e) => {
|
|
364
366
|
const b = e.target.closest("button");
|
|
365
367
|
if (!b)
|
|
@@ -701,7 +703,7 @@ export class KashiTour extends HTMLBase {
|
|
|
701
703
|
});
|
|
702
704
|
document.body.append(this.card);
|
|
703
705
|
}
|
|
704
|
-
this.card.innerHTML = `<span class="k-tour__step"
|
|
706
|
+
this.card.innerHTML = `<span class="k-tour__step">${esc(msg().tour.step(num(this, this.i + 1), num(this, n)))}</span><p class="k-tour__title" id="${this.key}-t">${esc(s.title)}</p><p class="k-tour__body" id="${this.key}-b">${esc(s.content)}</p><div class="k-tour__actions"><button type="button" class="k-btn k-btn--link k-tour__skip" data-act="skip">${esc(msg().tour.skip)}</button><span class="k-tour__progress" aria-hidden="true">${this.steps.map((_, k) => `<span${k <= this.i ? ' data-on=""' : ""}></span>`).join("")}</span>${this.i > 0 ? '<button type="button" class="k-btn k-btn--secondary k-btn--sm" data-act="prev">${esc(msg().common.prev)}</button>' : ""}<button type="button" class="k-btn k-btn--primary k-btn--sm" data-act="${last ? "done" : "next"}">${esc(last ? msg().tour.done : msg().common.next)}</button></div>`;
|
|
705
707
|
const el = document.querySelector(s.target);
|
|
706
708
|
const reduce = typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
707
709
|
el?.scrollIntoView?.({ block: "center", behavior: reduce ? "auto" : "smooth" });
|
|
@@ -819,7 +821,7 @@ export class KashiSplitter extends HTMLBase {
|
|
|
819
821
|
handle.setAttribute("aria-orientation", this.vertical ? "horizontal" : "vertical");
|
|
820
822
|
handle.setAttribute("aria-valuemin", String(this.num("min", 15)));
|
|
821
823
|
handle.setAttribute("aria-valuemax", String(this.num("max", 85)));
|
|
822
|
-
handle.setAttribute("aria-label", this.getAttribute("label") ??
|
|
824
|
+
handle.setAttribute("aria-label", this.getAttribute("label") ?? msg().splitter.label);
|
|
823
825
|
this.paint();
|
|
824
826
|
}
|
|
825
827
|
set(p, fire = true) {
|
|
@@ -838,7 +840,7 @@ export class KashiSplitter extends HTMLBase {
|
|
|
838
840
|
pane.style.blockSize = this.vertical ? `${this.current}%` : "";
|
|
839
841
|
const handle = box.querySelector(".k-splitter__handle");
|
|
840
842
|
handle.setAttribute("aria-valuenow", String(this.current));
|
|
841
|
-
handle.setAttribute("aria-valuetext",
|
|
843
|
+
handle.setAttribute("aria-valuetext", msg().common.percent(num(this, Math.round(this.current))));
|
|
842
844
|
}
|
|
843
845
|
}
|
|
844
846
|
KashiSplitter.observedAttributes = ["size", "min", "max", "orientation", "label", "height"];
|
package/dist/overlay.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { afterParse, bool, define, emit, esc, HTMLBase, ICONS, msg, uid } from "./base.js";
|
|
2
2
|
/**
|
|
3
3
|
* پنجرهٔ گفتگو روی <dialog> بومی با showModal: فوکوس داخل پنجره میماند، Esc میبندد،
|
|
4
4
|
* بقیهٔ صفحه از دسترس صفحهخوان خارج میشود و پس از بستن، فوکوس به دکمهٔ بازکننده برمیگردد.
|
|
@@ -100,7 +100,7 @@ export class KashiDialog extends HTMLBase {
|
|
|
100
100
|
d.setAttribute("aria-describedby", desc.id);
|
|
101
101
|
else
|
|
102
102
|
d.removeAttribute("aria-describedby");
|
|
103
|
-
d.querySelector(".k-dialog__close").setAttribute("aria-label", this.getAttribute("close-label") ??
|
|
103
|
+
d.querySelector(".k-dialog__close").setAttribute("aria-label", this.getAttribute("close-label") ?? msg().common.close);
|
|
104
104
|
}
|
|
105
105
|
sync() {
|
|
106
106
|
const d = this.dialog;
|
|
@@ -387,9 +387,9 @@ export class KashiCopyButton extends HTMLBase {
|
|
|
387
387
|
}, Number(this.getAttribute("timeout")) || 2000);
|
|
388
388
|
}
|
|
389
389
|
render() {
|
|
390
|
-
const copy = this.getAttribute("label") ??
|
|
391
|
-
const copied = this.getAttribute("copied-label") ??
|
|
392
|
-
const text = this.state === "copied" ? copied : this.state === "failed" ?
|
|
390
|
+
const copy = this.getAttribute("label") ?? msg().copy.copy;
|
|
391
|
+
const copied = this.getAttribute("copied-label") ?? msg().copy.copied;
|
|
392
|
+
const text = this.state === "copied" ? copied : this.state === "failed" ? msg().copy.failed : copy;
|
|
393
393
|
const iconOnly = bool(this, "icon-only");
|
|
394
394
|
const icon = this.state === "copied" ? ICONS.check : ICONS.copy;
|
|
395
395
|
const variant = this.getAttribute("variant") ?? "secondary";
|
package/dist/pickers.d.ts
CHANGED
|
@@ -48,7 +48,7 @@ export declare class KashiMonthPicker extends HTMLBase {
|
|
|
48
48
|
private out;
|
|
49
49
|
private render;
|
|
50
50
|
}
|
|
51
|
-
/** حجم فایل به
|
|
51
|
+
/** حجم فایل به زبان فعلی: «۱٫۲ مگابایت» یا «1.2 MB» */
|
|
52
52
|
export declare function formatBytes(bytes: number): string;
|
|
53
53
|
/**
|
|
54
54
|
* بارگذاری فایل با کشیدن و رها کردن، بررسی نوع و حجم و فهرست فایلها.
|
package/dist/pickers.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { formatJalali, fromISODate, parseJalali, toFaDigits, toGregorian, toISODate, toJalali } from "@kashi-ui/persian";
|
|
2
|
+
import { bool, calendarNames, define, digitsOf, emit, esc, fieldHTML, HTMLBase, ICONS, lang, localeDigits, msg, num, setMessages, uid } from "./base.js";
|
|
3
3
|
import "./calendar.js";
|
|
4
4
|
const parseDate = (t) => (t ? (fromISODate(t.trim()) ?? parseJalali(t)) : null);
|
|
5
5
|
const day = (d, add = 0) => new Date(d.getFullYear(), d.getMonth(), d.getDate() + add);
|
|
6
6
|
const same = (a, b) => (!a && !b) || (!!a && !!b && a.getTime() === b.getTime());
|
|
7
7
|
const PRESETS = [
|
|
8
|
-
[
|
|
9
|
-
[
|
|
10
|
-
[
|
|
8
|
+
[(m, n) => m.last7(n(7)), (t) => ({ start: day(t, -6), end: t })],
|
|
9
|
+
[(m, n) => m.last30(n(30)), (t) => ({ start: day(t, -29), end: t })],
|
|
10
|
+
[(m) => m.thisMonth, (t) => {
|
|
11
11
|
const { jy, jm } = toJalali(t);
|
|
12
12
|
return { start: toGregorian(jy, jm, 1), end: t };
|
|
13
13
|
}],
|
|
14
|
-
[
|
|
14
|
+
[(m) => m.lastMonth, (t) => {
|
|
15
15
|
const { jy, jm } = toJalali(t);
|
|
16
16
|
const [py, pm] = jm === 1 ? [jy - 1, 12] : [jy, jm - 1];
|
|
17
17
|
return { start: toGregorian(py, pm, 1), end: day(toGregorian(jy, jm, 1), -1) };
|
|
@@ -61,17 +61,17 @@ export class KashiDateRangePicker extends HTMLBase {
|
|
|
61
61
|
this.render();
|
|
62
62
|
}
|
|
63
63
|
fmt(d) {
|
|
64
|
-
return formatJalali(d, { digits: this
|
|
64
|
+
return formatJalali(d, { digits: digitsOf(this), locale: lang() });
|
|
65
65
|
}
|
|
66
66
|
text(r) {
|
|
67
|
-
return r.start && r.end ? `${this.fmt(r.start)}
|
|
67
|
+
return r.start && r.end ? `${this.fmt(r.start)}${msg().common.range}${this.fmt(r.end)}` : null;
|
|
68
68
|
}
|
|
69
69
|
render() {
|
|
70
70
|
this.close(false);
|
|
71
71
|
const id = this.key;
|
|
72
|
-
const label = this.getAttribute("label") ??
|
|
72
|
+
const label = this.getAttribute("label") ?? msg().dateRange.label;
|
|
73
73
|
const text = this.text(this.current);
|
|
74
|
-
const control = `<div class="k-datepicker k-daterange"><div class="k-select"><button id="${id}" type="button" class="k-select__control k-daterange__trigger" aria-haspopup="dialog" aria-expanded="false"${text ? "" : ' data-placeholder=""'}${this.hasAttribute("disabled") ? " disabled" : ""}>${ICONS.calendar}<span>${esc(text ?? this.getAttribute("placeholder") ??
|
|
74
|
+
const control = `<div class="k-datepicker k-daterange"><div class="k-select"><button id="${id}" type="button" class="k-select__control k-daterange__trigger" aria-haspopup="dialog" aria-expanded="false"${text ? "" : ' data-placeholder=""'}${this.hasAttribute("disabled") ? " disabled" : ""}>${ICONS.calendar}<span>${esc(text ?? this.getAttribute("placeholder") ?? msg().dateRange.placeholder)}</span></button></div></div>`;
|
|
75
75
|
this.querySelector(":scope > .k-field")?.remove();
|
|
76
76
|
this.insertAdjacentHTML("afterbegin", fieldHTML({ id, label, control }));
|
|
77
77
|
this.trigger = this.querySelector(".k-daterange__trigger");
|
|
@@ -94,9 +94,9 @@ export class KashiDateRangePicker extends HTMLBase {
|
|
|
94
94
|
pop.id = `${this.key}-popover`;
|
|
95
95
|
pop.className = "k-datepicker__popover k-daterange__popover";
|
|
96
96
|
pop.setAttribute("role", "dialog");
|
|
97
|
-
pop.setAttribute("aria-label", this.getAttribute("label") ??
|
|
97
|
+
pop.setAttribute("aria-label", this.getAttribute("label") ?? msg().dateRange.placeholder);
|
|
98
98
|
const presets = this.getAttribute("presets") === "false" ? [] : PRESETS;
|
|
99
|
-
pop.innerHTML = `${presets.length ? `<ul class="k-daterange__presets" role="list" aria-label="
|
|
99
|
+
pop.innerHTML = `${presets.length ? `<ul class="k-daterange__presets" role="list" aria-label="${esc(msg().dateRange.presets)}">${presets.map(([l], i) => `<li><button type="button" class="k-daterange__preset" data-preset="${i}" aria-pressed="false">${esc(l(msg().dateRange, (v) => num(this, v)))}</button></li>`).join("")}</ul>` : ""}<div class="k-daterange__main"><div class="k-daterange__footer"><span class="k-daterange__summary" aria-live="polite"></span><button type="button" class="k-btn k-btn--secondary k-btn--sm" data-cancel>${esc(msg().common.cancel)}</button><button type="button" class="k-btn k-btn--primary k-btn--sm" data-apply>${esc(msg().common.confirm)}</button></div></div>`;
|
|
100
100
|
const cal = document.createElement("kashi-calendar");
|
|
101
101
|
for (const a of ["min", "max", "digits", "holidays"]) {
|
|
102
102
|
const v = this.getAttribute(a);
|
|
@@ -150,7 +150,7 @@ export class KashiDateRangePicker extends HTMLBase {
|
|
|
150
150
|
paintDraft() {
|
|
151
151
|
const pop = this.pop;
|
|
152
152
|
const r = this.draft;
|
|
153
|
-
pop.querySelector(".k-daterange__summary").textContent = this.text(r) ?? (r.start ?
|
|
153
|
+
pop.querySelector(".k-daterange__summary").textContent = this.text(r) ?? (r.start ? msg().dateRange.pickEnd : "");
|
|
154
154
|
pop.querySelector("[data-apply]").disabled = !r.start || !r.end;
|
|
155
155
|
const today = day(new Date());
|
|
156
156
|
pop.querySelectorAll("[data-preset]").forEach((b, i) => {
|
|
@@ -222,11 +222,11 @@ export class KashiMonthPicker extends HTMLBase {
|
|
|
222
222
|
return (!!min && k < min.year * 12 + min.month) || (!!max && k > max.year * 12 + max.month);
|
|
223
223
|
}
|
|
224
224
|
render(focus = false) {
|
|
225
|
-
const f = (n) => (this
|
|
225
|
+
const f = (n) => (digitsOf(this) === "latin" ? String(n) : toFaDigits(String(n)));
|
|
226
226
|
const t = toJalali(new Date());
|
|
227
227
|
const min = this.parse(this.getAttribute("min")), max = this.parse(this.getAttribute("max"));
|
|
228
228
|
const y = this.year;
|
|
229
|
-
this.innerHTML = `<div class="k-monthpicker" role="group" aria-label="${esc(this.getAttribute("label") ??
|
|
229
|
+
this.innerHTML = `<div class="k-monthpicker" role="group" aria-label="${esc(this.getAttribute("label") ?? msg().monthPicker.label)}"><div class="k-monthpicker__header"><button type="button" class="k-btn k-btn--tertiary k-btn--icon k-btn--sm" data-year="-1" aria-label="${esc(msg().monthPicker.prevYear)}"${min && y <= min.year ? " disabled" : ""}>${ARROW(false)}</button><span class="k-monthpicker__year" aria-live="polite">${f(y)}</span><button type="button" class="k-btn k-btn--tertiary k-btn--icon k-btn--sm" data-year="1" aria-label="${esc(msg().monthPicker.nextYear)}"${max && y >= max.year ? " disabled" : ""}>${ARROW(true)}</button></div><div class="k-monthpicker__grid">${calendarNames().months.map((name, i) => `<button type="button" data-month="${i + 1}" class="k-monthpicker__month" tabindex="${this.focusMonth === i + 1 ? 0 : -1}" aria-pressed="${this.selected?.year === y && this.selected.month === i + 1}" aria-label="${name} ${f(y)}"${t.jy === y && t.jm === i + 1 ? ' aria-current="date"' : ""}${this.out(y, i + 1) ? " disabled" : ""}>${name}</button>`).join("")}</div></div>`;
|
|
230
230
|
const name = this.getAttribute("name");
|
|
231
231
|
if (name)
|
|
232
232
|
this.insertAdjacentHTML("beforeend", `<input type="hidden" name="${esc(name)}" value="${this.value}">`);
|
|
@@ -267,16 +267,16 @@ export class KashiMonthPicker extends HTMLBase {
|
|
|
267
267
|
}
|
|
268
268
|
}
|
|
269
269
|
KashiMonthPicker.observedAttributes = ["value", "min", "max", "digits", "label", "name"];
|
|
270
|
-
/** حجم فایل به
|
|
270
|
+
/** حجم فایل به زبان فعلی: «۱٫۲ مگابایت» یا «1.2 MB» */
|
|
271
271
|
export function formatBytes(bytes) {
|
|
272
|
-
const units =
|
|
272
|
+
const units = msg().fileUpload.units;
|
|
273
273
|
let v = bytes, i = 0;
|
|
274
274
|
while (v >= 1024 && i < units.length - 1) {
|
|
275
275
|
v /= 1024;
|
|
276
276
|
i++;
|
|
277
277
|
}
|
|
278
278
|
const n = i === 0 ? String(v) : v.toFixed(v < 10 ? 1 : 0).replace(/\.0$/, "");
|
|
279
|
-
return `${toFaDigits(n).replace(".", "٫")} ${units[i]}`;
|
|
279
|
+
return `${localeDigits() === "fa" ? toFaDigits(n).replace(".", "٫") : n} ${units[i]}`;
|
|
280
280
|
}
|
|
281
281
|
const UPLOAD = '<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="M10 13V3.5m-4 4 4-4 4 4M4 13v2.5A1.5 1.5 0 0 0 5.5 17h9a1.5 1.5 0 0 0 1.5-1.5V13"/></svg>';
|
|
282
282
|
const TRASH = '<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="M3.5 5.5h13M8 5.5V4h4v1.5M5 5.5l.8 10.2a1.5 1.5 0 0 0 1.5 1.3h5.4a1.5 1.5 0 0 0 1.5-1.3L15 5.5"/></svg>';
|
|
@@ -312,7 +312,7 @@ export class KashiFileUpload extends HTMLBase {
|
|
|
312
312
|
const off = this.hasAttribute("disabled");
|
|
313
313
|
const name = this.getAttribute("name");
|
|
314
314
|
const accept = this.getAttribute("accept");
|
|
315
|
-
this.innerHTML = `<div class="k-dropzone"${off ? ' data-disabled=""' : ""}><span class="k-dropzone__icon" aria-hidden="true">${UPLOAD}</span><p class="k-dropzone__title" style="margin: 0"><button type="button" class="k-dropzone__browse"${off ? " disabled" : ""}${hint ? ` aria-describedby="${k}-hint"` : ""}>${esc(this.getAttribute("label") ??
|
|
315
|
+
this.innerHTML = `<div class="k-dropzone"${off ? ' data-disabled=""' : ""}><span class="k-dropzone__icon" aria-hidden="true">${UPLOAD}</span><p class="k-dropzone__title" style="margin: 0"><button type="button" class="k-dropzone__browse"${off ? " disabled" : ""}${hint ? ` aria-describedby="${k}-hint"` : ""}>${esc(this.getAttribute("label") ?? msg().fileUpload.browse)}</button> <span>${esc(msg().fileUpload.drop)}</span></p>${hint ? `<p class="k-dropzone__hint" id="${k}-hint" style="margin: 0">${esc(hint)}</p>` : ""}<input type="file" tabindex="-1" aria-hidden="true"${accept ? ` accept="${esc(accept)}"` : ""}${this.multiple ? " multiple" : ""}${name ? ` name="${esc(name)}"` : ""}></div><ul class="k-file-list" role="list" hidden></ul><span class="k-sr-only" aria-live="polite"></span>`;
|
|
316
316
|
this.rendered = true;
|
|
317
317
|
this.input = this.querySelector("input[type=file]");
|
|
318
318
|
const zone = this.querySelector(".k-dropzone");
|
|
@@ -342,7 +342,7 @@ export class KashiFileUpload extends HTMLBase {
|
|
|
342
342
|
return;
|
|
343
343
|
const it = this.items.find((x) => x.id === b.dataset.remove);
|
|
344
344
|
this.items = this.items.filter((x) => x.id !== b.dataset.remove);
|
|
345
|
-
this.announce(
|
|
345
|
+
this.announce(msg().common.removed(it?.file.name ?? ""));
|
|
346
346
|
this.changed();
|
|
347
347
|
});
|
|
348
348
|
this.paint();
|
|
@@ -361,16 +361,16 @@ export class KashiFileUpload extends HTMLBase {
|
|
|
361
361
|
const added = list.map((file, i) => {
|
|
362
362
|
let error;
|
|
363
363
|
if (!this.accepts(file))
|
|
364
|
-
error =
|
|
364
|
+
error = msg().fileUpload.wrongType;
|
|
365
365
|
else if (maxSize && file.size > maxSize)
|
|
366
|
-
error =
|
|
366
|
+
error = msg().fileUpload.tooBig(formatBytes(maxSize));
|
|
367
367
|
else if (maxFiles && kept + i >= maxFiles)
|
|
368
|
-
error =
|
|
368
|
+
error = msg().fileUpload.tooMany(num(this, maxFiles));
|
|
369
369
|
const up = !error && !!this.uploader;
|
|
370
370
|
return { id: uid("f"), file, status: error ? "error" : up ? "uploading" : "done", progress: error ? 0 : up ? 0 : 100, error };
|
|
371
371
|
});
|
|
372
372
|
this.items = this.multiple ? [...this.items, ...added] : added;
|
|
373
|
-
this.announce(added.map((a) => `${a.file.name}: ${a.error ?? (this.uploader ?
|
|
373
|
+
this.announce(added.map((a) => `${a.file.name}: ${a.error ?? (this.uploader ? msg().fileUpload.uploading : msg().fileUpload.done)}`).join(msg().common.list));
|
|
374
374
|
this.changed();
|
|
375
375
|
for (const it of added) {
|
|
376
376
|
if (it.status !== "uploading" || !this.uploader)
|
|
@@ -381,11 +381,11 @@ export class KashiFileUpload extends HTMLBase {
|
|
|
381
381
|
}).then(() => {
|
|
382
382
|
it.status = "done";
|
|
383
383
|
it.progress = 100;
|
|
384
|
-
this.announce(`${it.file.name}:
|
|
384
|
+
this.announce(`${it.file.name}: ${msg().fileUpload.done}`);
|
|
385
385
|
this.changed();
|
|
386
386
|
}, (e) => {
|
|
387
387
|
it.status = "error";
|
|
388
|
-
it.error = e instanceof Error ? e.message :
|
|
388
|
+
it.error = e instanceof Error ? e.message : msg().fileUpload.error;
|
|
389
389
|
this.announce(`${it.file.name}: ${it.error}`);
|
|
390
390
|
this.changed();
|
|
391
391
|
});
|
|
@@ -410,7 +410,7 @@ export class KashiFileUpload extends HTMLBase {
|
|
|
410
410
|
const list = this.querySelector(".k-file-list");
|
|
411
411
|
list.hidden = !this.items.length;
|
|
412
412
|
const ext = (n) => (n.split(".").pop() ?? "").slice(0, 4).toUpperCase();
|
|
413
|
-
list.innerHTML = this.items.map((it) => `<li class="k-file" data-status="${it.status}"><span class="k-file__icon" aria-hidden="true">${esc(ext(it.file.name))}</span><span class="k-file__name" title="${esc(it.file.name)}">${esc(it.file.name)}</span><button type="button" class="k-btn k-btn--tertiary k-btn--icon k-btn--sm k-file__remove" data-remove="${it.id}" aria-label="
|
|
413
|
+
list.innerHTML = this.items.map((it) => `<li class="k-file" data-status="${it.status}"><span class="k-file__icon" aria-hidden="true">${esc(ext(it.file.name))}</span><span class="k-file__name" title="${esc(it.file.name)}">${esc(it.file.name)}</span><button type="button" class="k-btn k-btn--tertiary k-btn--icon k-btn--sm k-file__remove" data-remove="${it.id}" aria-label="${esc(msg().common.remove(it.file.name))}">${TRASH}</button><span class="k-file__meta">${esc(it.error ?? `${formatBytes(it.file.size)}${it.status === "uploading" ? ` · ${msg().common.percent(num(this, Math.round(it.progress)))}` : it.status === "done" ? ` · ${msg().fileUpload.done}` : ""}`)}</span>${it.status === "uploading" ? `<div role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${Math.round(it.progress)}" aria-label="${esc(msg().fileUpload.uploadingFile(it.file.name))}" class="k-progress k-progress--sm"><div class="k-progress__bar" style="--k-value: ${it.progress}%"></div></div>` : ""}</li>`).join("");
|
|
414
414
|
}
|
|
415
415
|
}
|
|
416
416
|
KashiFileUpload.observedAttributes = ["accept", "multiple", "max-size", "max-files", "hint", "name", "disabled", "label"];
|
|
@@ -447,7 +447,8 @@ function hsvToHex({ h, s, v }) {
|
|
|
447
447
|
};
|
|
448
448
|
return toHex(f(5), f(3), f(1));
|
|
449
449
|
}
|
|
450
|
-
const
|
|
450
|
+
const SWATCH_KEYS = [["firoozeh", "#0b8077"], ["lajvard", "#2740ed"], ["violet", "#7c3aed"], ["anar", "#e51d2a"], ["zafaran", "#dd5e02"], ["peste", "#518b25"], ["shab", "#2a2f3d"]];
|
|
451
|
+
const SWATCHES = () => SWATCH_KEYS.map(([k, hex]) => [msg().colorPicker.names[k], hex]);
|
|
451
452
|
const SW_CHECK = '<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m5 10.5 3.2 3L15 7"/></svg>';
|
|
452
453
|
/**
|
|
453
454
|
* انتخاب رنگ: رنگهای پیشنهادی، پنل اشباع و رنگمایه، کد HEX و RGB، و کنتراست با متن سفید یا مشکی.
|
|
@@ -505,15 +506,15 @@ export class KashiColorPicker extends HTMLBase {
|
|
|
505
506
|
swatches() {
|
|
506
507
|
const attr = this.getAttribute("swatches");
|
|
507
508
|
if (attr === null)
|
|
508
|
-
return SWATCHES;
|
|
509
|
+
return SWATCHES();
|
|
509
510
|
return attr.split(",").map((s) => s.trim().split(/\s+/)).filter(([h]) => norm(h)).map(([h, ...n]) => [n.join(" ") || h, norm(h)]);
|
|
510
511
|
}
|
|
511
512
|
render() {
|
|
512
513
|
const k = this.key;
|
|
513
514
|
const sw = this.swatches();
|
|
514
|
-
const control = `<div class="k-color">${sw.length ? `<div class="k-color__swatches" role="radiogroup" aria-label="
|
|
515
|
+
const control = `<div class="k-color">${sw.length ? `<div class="k-color__swatches" role="radiogroup" aria-label="${esc(msg().colorPicker.swatches)}">${sw.map(([n, h]) => `<label class="k-color__swatch" style="--_sw: ${h}; --_ink: ${contrastRatio(h, "#ffffff") >= 3 ? "#fff" : "#000"}" title="${esc(n)}"><input type="radio" class="k-sr-only" name="${k}-sw" value="${h}" aria-label="${esc(n)}">${SW_CHECK}</label>`).join("")}</div>` : ""}<div class="k-color__row"><span class="k-color__anchor"><button type="button" class="k-color__preview" aria-label="${esc(msg().colorPicker.custom)}" aria-haspopup="dialog" aria-expanded="false"></button></span><div class="k-input"><input id="${k}-hex" class="k-input__control" dir="ltr" spellcheck="false" autocomplete="off"></div></div>${this.getAttribute("show-contrast") === "false" ? "" : '<p class="k-color__contrast" aria-live="polite"><span class="k-color__sample">${esc(msg().colorPicker.sample)}</span><span></span></p>'}</div>`;
|
|
515
516
|
this.querySelector(":scope > .k-field")?.remove();
|
|
516
|
-
this.insertAdjacentHTML("afterbegin", fieldHTML({ id: `${k}-hex`, label: this.getAttribute("label") ??
|
|
517
|
+
this.insertAdjacentHTML("afterbegin", fieldHTML({ id: `${k}-hex`, label: this.getAttribute("label") ?? msg().colorPicker.label, control }));
|
|
517
518
|
this.rendered = true;
|
|
518
519
|
this.querySelector(".k-color__swatches")?.addEventListener("change", (e) => this.pick(e.target.value));
|
|
519
520
|
const hex = this.querySelector(`#${k}-hex`);
|
|
@@ -551,7 +552,7 @@ export class KashiColorPicker extends HTMLBase {
|
|
|
551
552
|
const best = Math.max(white, black);
|
|
552
553
|
const out = this.querySelector(".k-color__contrast span:last-child");
|
|
553
554
|
if (out)
|
|
554
|
-
out.textContent =
|
|
555
|
+
out.textContent = msg().colorPicker.contrast(ink === "#ffffff" ? "white" : "black", num(this, (Math.round(best * 10) / 10).toFixed(1)).replace(".", msg().common.decimal), best >= 4.5);
|
|
555
556
|
const name = this.getAttribute("name");
|
|
556
557
|
this.querySelectorAll(":scope > input[type=hidden]").forEach((el) => el.remove());
|
|
557
558
|
if (name)
|
|
@@ -562,15 +563,15 @@ export class KashiColorPicker extends HTMLBase {
|
|
|
562
563
|
}
|
|
563
564
|
paintMessages(invalid) {
|
|
564
565
|
const hex = this.querySelector(`#${this.key}-hex`);
|
|
565
|
-
setMessages(this.querySelector(":scope > .k-field"), hex, `${this.key}-hex`, invalid ?
|
|
566
|
+
setMessages(this.querySelector(":scope > .k-field"), hex, `${this.key}-hex`, invalid ? msg().colorPicker.invalid : null, this.getAttribute("hint"));
|
|
566
567
|
}
|
|
567
568
|
openPanel() {
|
|
568
569
|
const anchor = this.querySelector(".k-color__anchor");
|
|
569
570
|
const p = document.createElement("div");
|
|
570
571
|
p.className = "k-color__panel";
|
|
571
572
|
p.setAttribute("role", "dialog");
|
|
572
|
-
p.setAttribute("aria-label",
|
|
573
|
-
p.innerHTML = `<div class="k-color__area" role="slider" tabindex="0" aria-label="
|
|
573
|
+
p.setAttribute("aria-label", msg().colorPicker.custom);
|
|
574
|
+
p.innerHTML = `<div class="k-color__area" role="slider" tabindex="0" aria-label="${esc(msg().colorPicker.area)}"><span class="k-color__thumb"></span></div><div class="k-color__tools"><span class="k-color__current" aria-hidden="true"></span><input type="range" class="k-color__hue" min="0" max="359" aria-label="${esc(msg().colorPicker.hue)}"></div><div class="k-color__codes" dir="ltr">${["R", "G", "B"].map((ch, i) => `<label class="k-color__code"><input class="k-color__num" inputmode="numeric" data-ch="${i}" aria-label="${esc([msg().colorPicker.red, msg().colorPicker.green, msg().colorPicker.blue][i])}"><span aria-hidden="true">${ch}</span></label>`).join("")}</div>`;
|
|
574
575
|
anchor.append(p);
|
|
575
576
|
this.panel = p;
|
|
576
577
|
const area = p.querySelector(".k-color__area");
|
|
@@ -619,17 +620,17 @@ export class KashiColorPicker extends HTMLBase {
|
|
|
619
620
|
paintPanel() {
|
|
620
621
|
const p = this.panel;
|
|
621
622
|
const { h, s, v } = this.hsv;
|
|
622
|
-
const pct = (n) =>
|
|
623
|
+
const pct = (n) => msg().common.percent(num(this, Math.round(n * 100)));
|
|
623
624
|
const area = p.querySelector(".k-color__area");
|
|
624
625
|
area.style.setProperty("--_hue", `hsl(${h} 100% 50%)`);
|
|
625
626
|
area.setAttribute("aria-valuenow", String(Math.round(s * 100)));
|
|
626
|
-
area.setAttribute("aria-valuetext",
|
|
627
|
+
area.setAttribute("aria-valuetext", msg().colorPicker.areaValue(pct(s), pct(v)));
|
|
627
628
|
const thumb = p.querySelector(".k-color__thumb");
|
|
628
629
|
thumb.style.left = `${s * 100}%`;
|
|
629
630
|
thumb.style.top = `${(1 - v) * 100}%`;
|
|
630
631
|
const hue = p.querySelector(".k-color__hue");
|
|
631
632
|
hue.value = String(Math.round(h));
|
|
632
|
-
hue.setAttribute("aria-valuetext",
|
|
633
|
+
hue.setAttribute("aria-valuetext", msg().colorPicker.degrees(num(this, Math.round(h))));
|
|
633
634
|
const rgb = rgbOf(this.current);
|
|
634
635
|
p.querySelectorAll(".k-color__num").forEach((el, i) => {
|
|
635
636
|
if (document.activeElement !== el)
|
package/dist/table.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { normalizePersian
|
|
2
|
-
import {
|
|
1
|
+
import { normalizePersian } from "@kashi-ui/persian";
|
|
2
|
+
import { afterParse, bool, define, emit, esc, HTMLBase, msg, num, uid } from "./base.js";
|
|
3
3
|
import { cellNumber } from "./controls.js";
|
|
4
4
|
const search = (s) => normalizePersian(s, { digits: "latin" }).toLowerCase();
|
|
5
5
|
/**
|
|
@@ -123,7 +123,7 @@ export class KashiDataTable extends HTMLBase {
|
|
|
123
123
|
const th = document.createElement("th");
|
|
124
124
|
th.scope = "col";
|
|
125
125
|
th.className = "k-table__check";
|
|
126
|
-
th.innerHTML = '<input type="checkbox" class="k-checkbox" aria-label="
|
|
126
|
+
th.innerHTML = '<input type="checkbox" class="k-checkbox" aria-label="${esc(msg().table.selectAll)}">';
|
|
127
127
|
head.prepend(th);
|
|
128
128
|
th.querySelector("input").addEventListener("change", (e) => {
|
|
129
129
|
const on = e.target.checked;
|
|
@@ -162,7 +162,7 @@ export class KashiDataTable extends HTMLBase {
|
|
|
162
162
|
const td = document.createElement("td");
|
|
163
163
|
td.className = "k-table__check";
|
|
164
164
|
const label = tr.children[0]?.textContent?.trim();
|
|
165
|
-
td.innerHTML = `<input type="checkbox" class="k-checkbox" aria-label="${esc(label ?
|
|
165
|
+
td.innerHTML = `<input type="checkbox" class="k-checkbox" aria-label="${esc(label ? msg().table.selectNamed(label) : msg().table.selectRow)}">`;
|
|
166
166
|
tr.prepend(td);
|
|
167
167
|
tr.setAttribute("aria-selected", "false");
|
|
168
168
|
td.querySelector("input").addEventListener("change", (e) => {
|
|
@@ -212,13 +212,13 @@ export class KashiDataTable extends HTMLBase {
|
|
|
212
212
|
const box = document.createElement("div");
|
|
213
213
|
box.className = "k-table-search k-field";
|
|
214
214
|
box.style.marginBlockEnd = "var(--k-space-3)";
|
|
215
|
-
box.innerHTML = `<div class="k-input k-input--sm"><input id="${id}" class="k-input__control" type="search" placeholder="
|
|
215
|
+
box.innerHTML = `<div class="k-input k-input--sm"><input id="${id}" class="k-input__control" type="search" placeholder="${esc(msg().table.search)}" aria-label="${esc(msg().table.search)}"></div><span class="k-sr-only" aria-live="polite"></span>`;
|
|
216
216
|
this.prepend(box);
|
|
217
217
|
const input = box.querySelector("input");
|
|
218
218
|
input.addEventListener("input", () => {
|
|
219
219
|
this.query = input.value;
|
|
220
220
|
this.apply();
|
|
221
|
-
box.querySelector(".k-sr-only").textContent =
|
|
221
|
+
box.querySelector(".k-sr-only").textContent = msg().table.rows(num(this, this.visibleRows().length));
|
|
222
222
|
});
|
|
223
223
|
}
|
|
224
224
|
/** مرتبسازی و جستجو را روی ردیفها اعمال میکند */
|
|
@@ -258,7 +258,7 @@ export class KashiDataTable extends HTMLBase {
|
|
|
258
258
|
body.querySelector(".k-table__empty")?.remove();
|
|
259
259
|
if (q && !this.visibleRows().length) {
|
|
260
260
|
const cols = ths.length;
|
|
261
|
-
body.insertAdjacentHTML("beforeend", `<tr class="k-table__empty"><td colspan="${cols}"><p style="padding: 2rem; margin: 0; text-align: center"
|
|
261
|
+
body.insertAdjacentHTML("beforeend", `<tr class="k-table__empty"><td colspan="${cols}"><p style="padding: 2rem; margin: 0; text-align: center">${esc(msg().common.noResults)}</p></td></tr>`);
|
|
262
262
|
}
|
|
263
263
|
this.selectionChanged(false);
|
|
264
264
|
}
|