@kashi-ui/elements 0.25.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.
Files changed (70) hide show
  1. package/README.md +11 -1
  2. package/dist/appointment.d.ts +40 -0
  3. package/dist/appointment.js +175 -0
  4. package/dist/base.d.ts +408 -0
  5. package/dist/base.js +31 -0
  6. package/dist/basic.d.ts +43 -0
  7. package/dist/basic.js +222 -0
  8. package/dist/calendar.js +21 -20
  9. package/dist/cdn/appointment-CrIZi8Bp.js +1 -0
  10. package/dist/cdn/base-DTQ50VdG.js +1 -0
  11. package/dist/cdn/basic-CrEW2RKF.js +1 -0
  12. package/dist/cdn/calendar-CYRrdGIf.js +16 -0
  13. package/dist/cdn/charts-D6KM26Vp.js +1 -0
  14. package/dist/cdn/combobox-6EFI2qqN.js +1 -0
  15. package/dist/cdn/controls-BwebEIlf.js +1 -0
  16. package/dist/cdn/display-DFuMkcj5.js +1 -0
  17. package/dist/cdn/fields-DhLIhHQ8.js +1 -0
  18. package/dist/cdn/forms-BHivKDdC.js +1 -0
  19. package/dist/cdn/holidays-DgbIAdlt.js +1 -0
  20. package/dist/cdn/inputs-R7nEHfbV.js +1 -0
  21. package/dist/cdn/kashi-elements.js +1 -1
  22. package/dist/cdn/map-H3xiZd8Y.js +12 -0
  23. package/dist/cdn/{menu-DhrqyLgy.js → menu-BAUSubXk.js} +1 -1
  24. package/dist/cdn/nav-NK-v48B5.js +1 -0
  25. package/dist/cdn/overlay-CBv8grz3.js +1 -0
  26. package/dist/cdn/pickers-BHzNt_X8.js +1 -0
  27. package/dist/cdn/table-Bt9WVLoo.js +1 -0
  28. package/dist/cdn/text-D4a_hlwL.js +1 -0
  29. package/dist/cdn/time-DJ3p1wvd.js +1 -0
  30. package/dist/cdn/words-BdADG_9U.js +1 -0
  31. package/dist/cdn.d.ts +1 -0
  32. package/dist/cdn.js +4 -0
  33. package/dist/charts.js +27 -24
  34. package/dist/combobox.d.ts +6 -5
  35. package/dist/combobox.js +29 -20
  36. package/dist/controls.js +15 -15
  37. package/dist/display.js +17 -14
  38. package/dist/fields.js +3 -3
  39. package/dist/forms.d.ts +3 -3
  40. package/dist/forms.js +54 -47
  41. package/dist/index.d.ts +12 -2
  42. package/dist/index.js +7 -1
  43. package/dist/inputs.d.ts +3 -3
  44. package/dist/inputs.js +31 -30
  45. package/dist/map.js +16 -13
  46. package/dist/nav.js +18 -16
  47. package/dist/overlay.js +5 -5
  48. package/dist/pickers.d.ts +1 -1
  49. package/dist/pickers.js +39 -38
  50. package/dist/table.js +7 -7
  51. package/dist/toast.js +3 -3
  52. package/package.json +4 -4
  53. package/dist/cdn/base-Cmq7zeIG.js +0 -1
  54. package/dist/cdn/calendar-Dm3fq2bs.js +0 -16
  55. package/dist/cdn/charts-DNOWi4sJ.js +0 -1
  56. package/dist/cdn/combobox-ZGrZ5okg.js +0 -1
  57. package/dist/cdn/controls-K-OZcjg4.js +0 -1
  58. package/dist/cdn/digits-DE1YGT94.js +0 -1
  59. package/dist/cdn/display-C975tS7j.js +0 -1
  60. package/dist/cdn/fields-BUTaSN8m.js +0 -1
  61. package/dist/cdn/forms-C-effPKn.js +0 -1
  62. package/dist/cdn/inputs-BjOddAM7.js +0 -1
  63. package/dist/cdn/jalali-BiM5Kfep.js +0 -1
  64. package/dist/cdn/map-BHuusa4t.js +0 -12
  65. package/dist/cdn/nav-D_w5BV_K.js +0 -1
  66. package/dist/cdn/overlay-DX9un2xm.js +0 -1
  67. package/dist/cdn/pickers-BFLpUruK.js +0 -1
  68. package/dist/cdn/table-DPQfDMs-.js +0 -1
  69. package/dist/cdn/text-D1U32D6K.js +0 -1
  70. package/dist/cdn/words-9OdzG7y6.js +0 -1
@@ -0,0 +1 @@
1
+ import{A as e,C as t,E as n,T as r,a as i,b as a,c as o,d as s,f as c,g as l,i as u,l as d,m as f,n as p,o as m,p as h,s as g,t as _,u as v,v as y,w as b,y as x}from"./base-DTQ50VdG.js";import"./calendar-CYRrdGIf.js";const S=e=>e?a(e.trim())??t(e):null,C=(e,t=0)=>new Date(e.getFullYear(),e.getMonth(),e.getDate()+t),w=(e,t)=>!e&&!t||!!e&&!!t&&e.getTime()===t.getTime(),T=[[(e,t)=>e.last7(t(7)),e=>({start:C(e,-6),end:e})],[(e,t)=>e.last30(t(30)),e=>({start:C(e,-29),end:e})],[e=>e.thisMonth,e=>{let{jy:t,jm:r}=n(e);return{start:b(t,r,1),end:e}}],[e=>e.lastMonth,e=>{let{jy:t,jm:r}=n(e),[i,a]=r===1?[t-1,12]:[t,r-1];return{start:b(i,a,1),end:C(b(t,r,1),-1)}}]];var E=class extends _{constructor(){super(...arguments),this.current={start:null,end:null},this.draft={start:null,end:null},this.valueSet=!1,this.rendered=!1,this.key=y(`kashi-range`),this.pop=null,this.onOutside=e=>{this.contains(e.target)||this.close(!1)}}connectedCallback(){!this.rendered&&!this.valueSet&&(this.current={start:S(this.getAttribute(`start`)),end:S(this.getAttribute(`end`))}),this.render()}disconnectedCallback(){this.close(!1)}attributeChangedCallback(e,t,n){this.rendered&&t!==n&&((e===`start`||e===`end`)&&(this.current={...this.current,[e]:S(n)}),this.render())}get value(){return{...this.current}}set value(e){this.valueSet=!0,this.current={start:e?.start??null,end:e?.end??null},this.rendered&&this.render()}fmt(e){return x(e,{digits:g(this),locale:s()})}text(e){return e.start&&e.end?`${this.fmt(e.start)}${h().common.range}${this.fmt(e.end)}`:null}render(){this.close(!1);let e=this.key,t=this.getAttribute(`label`)??h().dateRange.label,n=this.text(this.current),i=`<div class="k-datepicker k-daterange"><div class="k-select"><button id="${e}" type="button" class="k-select__control k-daterange__trigger" aria-haspopup="dialog" aria-expanded="false"${n?``:` data-placeholder=""`}${this.hasAttribute(`disabled`)?` disabled`:``}>${p.calendar}<span>${d(n??this.getAttribute(`placeholder`)??h().dateRange.placeholder)}</span></button></div></div>`;this.querySelector(`:scope > .k-field`)?.remove(),this.insertAdjacentHTML(`afterbegin`,v({id:e,label:t,control:i})),this.trigger=this.querySelector(`.k-daterange__trigger`),this.trigger.addEventListener(`click`,()=>this.pop?this.close():this.open()),this.rendered=!0,l(this.querySelector(`:scope > .k-field`),this.trigger,e,this.getAttribute(`error`),this.getAttribute(`hint`));let a=this.getAttribute(`name`);if(this.querySelectorAll(`:scope > input[type=hidden]`).forEach(e=>e.remove()),a)for(let[e,t]of[[`start`,this.current.start],[`end`,this.current.end]])this.insertAdjacentHTML(`beforeend`,`<input type="hidden" name="${d(`${a}_${e}`)}" value="${t?r(t):``}">`)}open(){if(this.pop||this.hasAttribute(`disabled`))return;this.draft={...this.current};let e=this.querySelector(`.k-daterange`),t=document.createElement(`div`);t.id=`${this.key}-popover`,t.className=`k-datepicker__popover k-daterange__popover`,t.setAttribute(`role`,`dialog`),t.setAttribute(`aria-label`,this.getAttribute(`label`)??h().dateRange.placeholder);let n=this.getAttribute(`presets`)===`false`?[]:T;t.innerHTML=`${n.length?`<ul class="k-daterange__presets" role="list" aria-label="${d(h().dateRange.presets)}">${n.map(([e],t)=>`<li><button type="button" class="k-daterange__preset" data-preset="${t}" aria-pressed="false">${d(e(h().dateRange,e=>f(this,e)))}</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>${d(h().common.cancel)}</button><button type="button" class="k-btn k-btn--primary k-btn--sm" data-apply>${d(h().common.confirm)}</button></div></div>`;let i=document.createElement(`kashi-calendar`);for(let e of[`min`,`max`,`digits`,`holidays`]){let t=this.getAttribute(e);t!==null&&i.setAttribute(e,t)}i.range=this.draft,t.querySelector(`.k-daterange__main`).prepend(i),i.addEventListener(`kashi-change`,e=>{e.stopPropagation();let t=e.detail.date,n=this.draft;this.draft=!n.start||n.end?{start:t,end:null}:t<n.start?{start:t,end:n.start}:{start:n.start,end:t},i.range=this.draft,this.paintDraft()}),t.addEventListener(`click`,e=>{let t=e.target,a=t.closest(`[data-preset]`);if(a)this.draft=n[Number(a.dataset.preset)][1](C(/* @__PURE__ */ new Date)),i.range=this.draft,this.paintDraft();else if(t.closest(`[data-cancel]`))this.close();else if(t.closest(`[data-apply]`)&&this.draft.start&&this.draft.end){let e=!w(this.draft.start,this.current.start)||!w(this.draft.end,this.current.end);this.current={...this.draft},this.close(),this.render(),this.trigger.focus(),e&&o(this,`kashi-change`,{start:this.current.start,end:this.current.end,value:{start:r(this.current.start),end:r(this.current.end)}})}}),t.addEventListener(`keydown`,e=>{e.key===`Escape`&&(e.stopPropagation(),this.close())}),e.append(t),this.pop=t,this.trigger.setAttribute(`aria-expanded`,`true`),this.trigger.setAttribute(`aria-controls`,t.id),document.addEventListener(`pointerdown`,this.onOutside),this.paintDraft(),i.focus()}paintDraft(){let e=this.pop,t=this.draft;e.querySelector(`.k-daterange__summary`).textContent=this.text(t)??(t.start?h().dateRange.pickEnd:``),e.querySelector(`[data-apply]`).disabled=!t.start||!t.end;let n=C(/* @__PURE__ */ new Date);e.querySelectorAll(`[data-preset]`).forEach((e,r)=>{let i=T[r][1](n);e.setAttribute(`aria-pressed`,String(w(t.start,i.start)&&w(t.end,i.end)))})}close(e=!0){this.pop&&(this.pop.remove(),this.pop=null,document.removeEventListener(`pointerdown`,this.onOutside),this.trigger.setAttribute(`aria-expanded`,`false`),this.trigger.removeAttribute(`aria-controls`),e&&this.trigger.focus())}};E.observedAttributes=[`label`,`hint`,`error`,`start`,`end`,`min`,`max`,`holidays`,`digits`,`name`,`disabled`,`presets`,`placeholder`];const D=e=>`<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" data-k-mirror=""><path d="${e?`m8 5 5 5-5 5`:`m12 5-5 5 5 5`}"/></svg>`;var O=class extends _{constructor(){super(...arguments),this.selected=null,this.year=n(/* @__PURE__ */ new Date).jy,this.focusMonth=1,this.valueSet=!1,this.rendered=!1}connectedCallback(){if(!this.rendered){this.valueSet||(this.selected=this.parse(this.getAttribute(`value`)));let e=n(/* @__PURE__ */ new Date);this.year=this.selected?.year??e.jy,this.focusMonth=this.selected?.month??(this.year===e.jy?e.jm:1)}this.render()}attributeChangedCallback(e,t,n){this.rendered&&t!==n&&(e===`value`?this.value=n??``:this.render())}parse(e){let t=(e??``).trim().match(/^(\d{4})[-/](\d{1,2})$/);return t?{year:Number(t[1]),month:Number(t[2])}:null}get value(){return this.selected?`${this.selected.year}-${String(this.selected.month).padStart(2,`0`)}`:``}set value(e){this.valueSet=!0,this.selected=this.parse(e),this.selected&&([this.year,this.focusMonth]=[this.selected.year,this.selected.month]),this.rendered&&this.render()}out(e,t){let n=e*12+t,r=this.parse(this.getAttribute(`min`)),i=this.parse(this.getAttribute(`max`));return!!r&&n<r.year*12+r.month||!!i&&n>i.year*12+i.month}render(t=!1){let r=t=>g(this)===`latin`?String(t):e(String(t)),a=n(/* @__PURE__ */ new Date),s=this.parse(this.getAttribute(`min`)),c=this.parse(this.getAttribute(`max`)),l=this.year;this.innerHTML=`<div class="k-monthpicker" role="group" aria-label="${d(this.getAttribute(`label`)??h().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="${d(h().monthPicker.prevYear)}"${s&&l<=s.year?` disabled`:``}>${D(!1)}</button><span class="k-monthpicker__year" aria-live="polite">${r(l)}</span><button type="button" class="k-btn k-btn--tertiary k-btn--icon k-btn--sm" data-year="1" aria-label="${d(h().monthPicker.nextYear)}"${c&&l>=c.year?` disabled`:``}>${D(!0)}</button></div><div class="k-monthpicker__grid">${i().months.map((e,t)=>`<button type="button" data-month="${t+1}" class="k-monthpicker__month" tabindex="${this.focusMonth===t+1?0:-1}" aria-pressed="${this.selected?.year===l&&this.selected.month===t+1}" aria-label="${e} ${r(l)}"${a.jy===l&&a.jm===t+1?` aria-current="date"`:``}${this.out(l,t+1)?` disabled`:``}>${e}</button>`).join(``)}</div></div>`;let u=this.getAttribute(`name`);u&&this.insertAdjacentHTML(`beforeend`,`<input type="hidden" name="${d(u)}" value="${this.value}">`),this.rendered=!0,this.querySelectorAll(`[data-year]`).forEach(e=>e.addEventListener(`click`,()=>{this.year+=Number(e.dataset.year),this.render()}));let f=this.querySelector(`.k-monthpicker__grid`);f.addEventListener(`click`,e=>{let t=e.target.closest(`[data-month]`);t&&!t.disabled&&(this.selected={year:this.year,month:Number(t.dataset.month)},this.focusMonth=this.selected.month,this.render(!0),o(this,`kashi-change`,{value:this.value,year:this.selected.year,month:this.selected.month}))}),f.addEventListener(`keydown`,e=>{let t=getComputedStyle(f).direction===`rtl`,n={ArrowRight:t?-1:1,ArrowLeft:t?1:-1,ArrowDown:3,ArrowUp:-3,PageUp:-12,PageDown:12},r=this.year*12+this.focusMonth-1;if(e.key in n)r+=n[e.key];else if(e.key===`Home`)r=this.year*12;else if(e.key===`End`)r=this.year*12+11;else return;e.preventDefault(),this.year=Math.floor(r/12),this.focusMonth=r%12+1,this.render(!0)}),t&&this.querySelector(`[data-month="${this.focusMonth}"]`)?.focus()}};O.observedAttributes=[`value`,`min`,`max`,`digits`,`label`,`name`];function k(t){let n=h().fileUpload.units,r=t,i=0;for(;r>=1024&&i<n.length-1;)r/=1024,i++;let a=i===0?String(r):r.toFixed(+(r<10)).replace(/\.0$/,``);return`${c()===`fa`?e(a).replace(`.`,`٫`):a} ${n[i]}`}var A=class extends _{constructor(){super(...arguments),this.items=[],this.rendered=!1,this.key=y(`kashi-upload`)}connectedCallback(){this.rendered||this.render()}attributeChangedCallback(e,t,n){this.rendered&&t!==n&&this.render()}get files(){return this.items.filter(e=>e.status!==`error`).map(e=>e.file)}get multiple(){return u(this,`multiple`,!0)}render(){let e=this.key,t=this.getAttribute(`hint`),n=this.hasAttribute(`disabled`),r=this.getAttribute(`name`),i=this.getAttribute(`accept`);this.innerHTML=`<div class="k-dropzone"${n?` data-disabled=""`:``}><span class="k-dropzone__icon" aria-hidden="true"><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></span><p class="k-dropzone__title" style="margin: 0"><button type="button" class="k-dropzone__browse"${n?` disabled`:``}${t?` aria-describedby="${e}-hint"`:``}>${d(this.getAttribute(`label`)??h().fileUpload.browse)}</button> <span>${d(h().fileUpload.drop)}</span></p>${t?`<p class="k-dropzone__hint" id="${e}-hint" style="margin: 0">${d(t)}</p>`:``}<input type="file" tabindex="-1" aria-hidden="true"${i?` accept="${d(i)}"`:``}${this.multiple?` multiple`:``}${r?` name="${d(r)}"`:``}></div><ul class="k-file-list" role="list" hidden></ul><span class="k-sr-only" aria-live="polite"></span>`,this.rendered=!0,this.input=this.querySelector(`input[type=file]`);let a=this.querySelector(`.k-dropzone`);this.querySelector(`.k-dropzone__browse`).addEventListener(`click`,()=>this.input.click()),this.input.addEventListener(`change`,()=>{this.input.files?.length&&this.add([...this.input.files])}),a.addEventListener(`dragover`,e=>{e.preventDefault(),n||a.setAttribute(`data-dragging`,``)}),a.addEventListener(`dragleave`,e=>{a.contains(e.relatedTarget)||a.removeAttribute(`data-dragging`)}),a.addEventListener(`drop`,e=>{e.preventDefault(),a.removeAttribute(`data-dragging`),!n&&e.dataTransfer?.files.length&&this.add([...e.dataTransfer.files])}),this.querySelector(`.k-file-list`).addEventListener(`click`,e=>{let t=e.target.closest(`[data-remove]`);if(!t)return;let n=this.items.find(e=>e.id===t.dataset.remove);this.items=this.items.filter(e=>e.id!==t.dataset.remove),this.announce(h().common.removed(n?.file.name??``)),this.changed()}),this.paint()}accepts(e){let t=this.getAttribute(`accept`);return!t||t.split(`,`).map(e=>e.trim().toLowerCase()).some(t=>t.startsWith(`.`)?e.name.toLowerCase().endsWith(t):t.endsWith(`/*`)?e.type.startsWith(t.slice(0,-1)):e.type===t)}add(e){let t=this.multiple?e:e.slice(0,1),n=Number(this.getAttribute(`max-size`))||0,r=Number(this.getAttribute(`max-files`))||0,i=this.multiple?this.items.filter(e=>e.status!==`error`).length:0,a=t.map((e,t)=>{let a;this.accepts(e)?n&&e.size>n?a=h().fileUpload.tooBig(k(n)):r&&i+t>=r&&(a=h().fileUpload.tooMany(f(this,r))):a=h().fileUpload.wrongType;let o=!a&&!!this.uploader;return{id:y(`f`),file:e,status:a?`error`:o?`uploading`:`done`,progress:a||o?0:100,error:a}});this.items=this.multiple?[...this.items,...a]:a,this.announce(a.map(e=>`${e.file.name}: ${e.error??(this.uploader?h().fileUpload.uploading:h().fileUpload.done)}`).join(h().common.list)),this.changed();for(let e of a)e.status===`uploading`&&this.uploader&&this.uploader(e.file,t=>{e.progress=t,this.paint()}).then(()=>{e.status=`done`,e.progress=100,this.announce(`${e.file.name}: ${h().fileUpload.done}`),this.changed()},t=>{e.status=`error`,e.error=t instanceof Error?t.message:h().fileUpload.error,this.announce(`${e.file.name}: ${e.error}`),this.changed()})}announce(e){this.querySelector(`:scope > .k-sr-only`).textContent=e}changed(){try{let e=new DataTransfer;for(let t of this.files)e.items.add(t);this.input.files=e.files}catch{}this.paint(),o(this,`kashi-change`,{files:this.files})}paint(){let e=this.querySelector(`.k-file-list`);e.hidden=!this.items.length;let t=e=>(e.split(`.`).pop()??``).slice(0,4).toUpperCase();e.innerHTML=this.items.map(e=>`<li class="k-file" data-status="${e.status}"><span class="k-file__icon" aria-hidden="true">${d(t(e.file.name))}</span><span class="k-file__name" title="${d(e.file.name)}">${d(e.file.name)}</span><button type="button" class="k-btn k-btn--tertiary k-btn--icon k-btn--sm k-file__remove" data-remove="${e.id}" aria-label="${d(h().common.remove(e.file.name))}"><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></button><span class="k-file__meta">${d(e.error??`${k(e.file.size)}${e.status===`uploading`?` · ${h().common.percent(f(this,Math.round(e.progress)))}`:e.status===`done`?` · ${h().fileUpload.done}`:``}`)}</span>${e.status===`uploading`?`<div role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${Math.round(e.progress)}" aria-label="${d(h().fileUpload.uploadingFile(e.file.name))}" class="k-progress k-progress--sm"><div class="k-progress__bar" style="--k-value: ${e.progress}%"></div></div>`:``}</li>`).join(``)}};A.observedAttributes=[`accept`,`multiple`,`max-size`,`max-files`,`hint`,`name`,`disabled`,`label`];const j=/^#?([0-9a-f]{3}|[0-9a-f]{6})$/i,M=e=>{let t=e.trim().match(j);return t?`#${(t[1].length===3?[...t[1]].map(e=>e+e).join(``):t[1]).toLowerCase()}`:null},N=e=>[1,3,5].map(t=>parseInt(e.slice(t,t+2),16)),P=(e,t,n)=>`#${[e,t,n].map(e=>Math.round(Math.min(255,Math.max(0,e))).toString(16).padStart(2,`0`)).join(``)}`,F=e=>{let[t,n,r]=N(e).map(e=>e/255).map(e=>e<=.03928?e/12.92:((e+.055)/1.055)**2.4);return .2126*t+.7152*n+.0722*r},I=(e,t)=>{let[n,r]=[F(M(e)??e),F(M(t)??t)].sort((e,t)=>t-e);return(n+.05)/(r+.05)};function L(e){let[t,n,r]=N(e).map(e=>e/255),i=Math.max(t,n,r),a=i-Math.min(t,n,r),o=0;return a&&(o=i===t?(n-r)/a%6:i===n?(r-t)/a+2:(t-n)/a+4),{h:(o*60+360)%360,s:i?a/i:0,v:i}}function R({h:e,s:t,v:n}){let r=r=>{let i=(r+e/60)%6;return(n-n*t*Math.max(0,Math.min(i,4-i,1)))*255};return P(r(5),r(3),r(1))}const z=[[`firoozeh`,`#0b8077`],[`lajvard`,`#2740ed`],[`violet`,`#7c3aed`],[`anar`,`#e51d2a`],[`zafaran`,`#dd5e02`],[`peste`,`#518b25`],[`shab`,`#2a2f3d`]],B=()=>z.map(([e,t])=>[h().colorPicker.names[e],t]);var V=class extends _{constructor(){super(...arguments),this.current=`#0b8077`,this.hsv=L(`#0b8077`),this.valueSet=!1,this.rendered=!1,this.key=y(`kashi-color`),this.panel=null,this.onOutside=e=>{this.querySelector(`.k-color__anchor`)?.contains(e.target)||this.closePanel(!1)}}connectedCallback(){!this.rendered&&!this.valueSet&&this.setColor(M(this.getAttribute(`value`)??``)??`#0b8077`,!1),this.render()}disconnectedCallback(){document.removeEventListener(`pointerdown`,this.onOutside)}attributeChangedCallback(e,t,n){this.rendered&&t!==n&&(e===`value`?this.value=n??``:this.render())}get value(){return this.current}set value(e){this.valueSet=!0;let t=M(e??``);t&&this.setColor(t,!1),this.rendered&&this.paint()}setColor(e,t){if(this.current=e,!t){let t=L(e);this.hsv=t.s===0?{...t,h:this.hsv.h}:t}}swatches(){let e=this.getAttribute(`swatches`);return e===null?B():e.split(`,`).map(e=>e.trim().split(/\s+/)).filter(([e])=>M(e)).map(([e,...t])=>[t.join(` `)||e,M(e)])}render(){let e=this.key,t=this.swatches(),n=`<div class="k-color">${t.length?`<div class="k-color__swatches" role="radiogroup" aria-label="${d(h().colorPicker.swatches)}">${t.map(([t,n])=>`<label class="k-color__swatch" style="--_sw: ${n}; --_ink: ${I(n,`#ffffff`)>=3?`#fff`:`#000`}" title="${d(t)}"><input type="radio" class="k-sr-only" name="${e}-sw" value="${n}" aria-label="${d(t)}"><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></label>`).join(``)}</div>`:``}<div class="k-color__row"><span class="k-color__anchor"><button type="button" class="k-color__preview" aria-label="${d(h().colorPicker.custom)}" aria-haspopup="dialog" aria-expanded="false"></button></span><div class="k-input"><input id="${e}-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>`;this.querySelector(`:scope > .k-field`)?.remove(),this.insertAdjacentHTML(`afterbegin`,v({id:`${e}-hex`,label:this.getAttribute(`label`)??h().colorPicker.label,control:n})),this.rendered=!0,this.querySelector(`.k-color__swatches`)?.addEventListener(`change`,e=>this.pick(e.target.value));let r=this.querySelector(`#${e}-hex`);r.addEventListener(`input`,()=>{let e=M(r.value);this.paintMessages(!e),e&&this.pick(e,!1)}),r.addEventListener(`blur`,()=>{M(r.value)&&(r.value=this.current)}),this.querySelector(`.k-color__preview`).addEventListener(`click`,()=>this.panel?this.closePanel():this.openPanel()),this.paint()}pick(e,t=!0,n=!1){let r=e!==this.current;this.setColor(e,n),this.paint(t),r&&o(this,`kashi-change`,{value:e})}paint(e=!0){let t=this.current,n=I(t,`#ffffff`),r=I(t,`#000000`),i=n>=r?`#ffffff`:`#000000`,a=this.querySelector(`.k-color`);a.style.setProperty(`--_sw`,t),a.style.setProperty(`--_ink`,i),this.querySelectorAll(`.k-color__swatch input`).forEach(e=>e.checked=e.value===t);let o=this.querySelector(`#${this.key}-hex`);e&&(o.value=t);let s=Math.max(n,r),c=this.querySelector(`.k-color__contrast span:last-child`);c&&(c.textContent=h().colorPicker.contrast(i===`#ffffff`?`white`:`black`,f(this,(Math.round(s*10)/10).toFixed(1)).replace(`.`,h().common.decimal),s>=4.5));let l=this.getAttribute(`name`);this.querySelectorAll(`:scope > input[type=hidden]`).forEach(e=>e.remove()),l&&this.insertAdjacentHTML(`beforeend`,`<input type="hidden" name="${d(l)}" value="${t}">`),this.panel&&this.paintPanel(),this.paintMessages(!1)}paintMessages(e){let t=this.querySelector(`#${this.key}-hex`);l(this.querySelector(`:scope > .k-field`),t,`${this.key}-hex`,e?h().colorPicker.invalid:null,this.getAttribute(`hint`))}openPanel(){let e=this.querySelector(`.k-color__anchor`),t=document.createElement(`div`);t.className=`k-color__panel`,t.setAttribute(`role`,`dialog`),t.setAttribute(`aria-label`,h().colorPicker.custom),t.innerHTML=`<div class="k-color__area" role="slider" tabindex="0" aria-label="${d(h().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="${d(h().colorPicker.hue)}"></div><div class="k-color__codes" dir="ltr">${[`R`,`G`,`B`].map((e,t)=>`<label class="k-color__code"><input class="k-color__num" inputmode="numeric" data-ch="${t}" aria-label="${d([h().colorPicker.red,h().colorPicker.green,h().colorPicker.blue][t])}"><span aria-hidden="true">${e}</span></label>`).join(``)}</div>`,e.append(t),this.panel=t;let n=t.querySelector(`.k-color__area`),r=e=>{this.hsv=e,this.pick(R(e),!0,!0)},i=e=>{let t=n.getBoundingClientRect();r({...this.hsv,s:Math.min(1,Math.max(0,(e.clientX-t.left)/t.width)),v:1-Math.min(1,Math.max(0,(e.clientY-t.top)/t.height))})};n.addEventListener(`pointerdown`,e=>{n.setPointerCapture?.(e.pointerId),i(e)}),n.addEventListener(`pointermove`,e=>e.buttons===1&&i(e)),n.addEventListener(`keydown`,e=>{let t=e.shiftKey?.1:.01,n=this.hsv,i={ArrowLeft:{s:Math.max(0,n.s-t)},ArrowRight:{s:Math.min(1,n.s+t)},ArrowUp:{v:Math.min(1,n.v+t)},ArrowDown:{v:Math.max(0,n.v-t)}};i[e.key]&&(e.preventDefault(),r({...n,...i[e.key]}))}),t.querySelector(`.k-color__hue`).addEventListener(`input`,e=>r({...this.hsv,h:Number(e.target.value)})),t.querySelectorAll(`.k-color__num`).forEach(e=>e.addEventListener(`input`,()=>{let t=N(this.current);t[Number(e.dataset.ch)]=Math.min(255,Math.max(0,Number(e.value.replace(/\D/g,``))||0)),this.pick(P(...t))})),t.addEventListener(`keydown`,e=>{e.key===`Escape`&&(e.stopPropagation(),this.closePanel())}),this.querySelector(`.k-color__preview`).setAttribute(`aria-expanded`,`true`),document.addEventListener(`pointerdown`,this.onOutside),this.paintPanel(),n.focus({preventScroll:!0})}paintPanel(){let e=this.panel,{h:t,s:n,v:r}=this.hsv,i=e=>h().common.percent(f(this,Math.round(e*100))),a=e.querySelector(`.k-color__area`);a.style.setProperty(`--_hue`,`hsl(${t} 100% 50%)`),a.setAttribute(`aria-valuenow`,String(Math.round(n*100))),a.setAttribute(`aria-valuetext`,h().colorPicker.areaValue(i(n),i(r)));let o=e.querySelector(`.k-color__thumb`);o.style.left=`${n*100}%`,o.style.top=`${(1-r)*100}%`;let s=e.querySelector(`.k-color__hue`);s.value=String(Math.round(t)),s.setAttribute(`aria-valuetext`,h().colorPicker.degrees(f(this,Math.round(t))));let c=N(this.current);e.querySelectorAll(`.k-color__num`).forEach((e,t)=>{document.activeElement!==e&&(e.value=String(c[t]))})}closePanel(e=!0){if(!this.panel)return;this.panel.remove(),this.panel=null,document.removeEventListener(`pointerdown`,this.onOutside);let t=this.querySelector(`.k-color__preview`);t.setAttribute(`aria-expanded`,`false`),e&&t.focus()}};V.observedAttributes=[`label`,`hint`,`value`,`name`,`swatches`,`show-contrast`],m(`kashi-date-range-picker`,E,`block`),m(`kashi-month-picker`,O,`block`),m(`kashi-file-upload`,A,`block`),m(`kashi-color-picker`,V,`block`);export{V as KashiColorPicker,E as KashiDateRangePicker,A as KashiFileUpload,O as KashiMonthPicker,I as contrastRatio,k as formatBytes};
@@ -0,0 +1 @@
1
+ import{c as e,i as t,l as n,m as r,o as i,p as a,r as o,t as s,v as c}from"./base-DTQ50VdG.js";import{n as l}from"./text-D4a_hlwL.js";import{cellNumber as u}from"./controls-BwebEIlf.js";const d=e=>l(e,{digits:`latin`}).toLowerCase();var f=class extends s{constructor(){super(...arguments),this.cols=null,this.data=null,this.table=null,this.sort=null,this.query=``,this.key=c(`kashi-table`),this.getRowId=e=>String(e.id??``)}connectedCallback(){o(()=>this.isConnected&&this.setup())}attributeChangedCallback(e,t,n){this.table&&t!==n&&this.setup()}get columns(){return this.cols??[]}set columns(e){this.cols=e,this.isConnected&&this.data&&this.setup()}get rows(){return this.data??[]}set rows(e){this.data=e,this.isConnected&&this.cols&&this.setup()}get selected(){return[...this.querySelectorAll(`tbody tr[aria-selected="true"]`)].map(e=>e.dataset.id??``)}setup(){this.cols&&this.data&&this.buildFromData();let e=this.querySelector(`:scope table`);if(!e)return;let n=e.closest(`.k-table-wrap`)??this.wrap(e);n.className=`k-table-wrap${t(this,`sticky-header`)?` k-table-wrap--sticky`:``}`,e.classList.add(`k-table`),e.classList.toggle(`k-table--compact`,t(this,`compact`)),e.classList.toggle(`k-table--striped`,t(this,`striped`));let r=e.querySelector(`caption`);r&&(r.id||=`${this.key}-caption`,n.setAttribute(`aria-labelledby`,r.id)),this.table=e,this.decorateHeader(),this.decorateRows(),this.searchBox(),this.apply()}wrap(e){let t=document.createElement(`div`);return t.setAttribute(`role`,`region`),t.tabIndex=0,e.before(t),t.append(e),t}buildFromData(){let e=this.cols,t=this.getAttribute(`caption`)??``,r=(e,t)=>e.format?e.format(t[e.key],t):String(t[e.key]??``),i=`<table>${t?`<caption><span class="k-table__caption-title">${n(t)}</span></caption>`:``}<thead><tr>${e.map(e=>`<th scope="col"${e.sortable?` data-sort`:``}${e.numeric?` data-numeric`:``}>${n(e.header)}</th>`).join(``)}</tr></thead><tbody>${this.data.map(t=>`<tr data-id="${n(this.getRowId(t))}">${e.map(e=>`<td>${n(r(e,t))}</td>`).join(``)}</tr>`).join(``)}</tbody></table>`;this.querySelector(`:scope > .k-table-wrap, :scope > table`)?.remove(),this.insertAdjacentHTML(`beforeend`,i)}decorateHeader(){this.table;let n=this.headCells();n.forEach((t,n)=>{if(t.classList.contains(`k-table__check`)||(t.scope||=`col`,t.hasAttribute(`data-numeric`)&&t.classList.add(`k-table__num`),!t.hasAttribute(`data-sort`)||t.querySelector(`.k-table__sort`)))return;t.setAttribute(`aria-sort`,`none`);let r=document.createElement(`button`);r.type=`button`,r.className=`k-table__sort`,r.append(...t.childNodes),t.append(r);let i=this.dataIndex(n);r.addEventListener(`click`,()=>{this.sort=this.sort?.index===i&&this.sort.dir===`ascending`?{index:i,dir:`descending`}:{index:i,dir:`ascending`},this.apply(),e(this,`kashi-sort`,{column:i,direction:this.sort.dir})})});let r=n.map(e=>e.hasAttribute(`data-numeric`));for(let e of this.bodyRows())[...e.children].forEach((e,t)=>r[t]&&e.classList.add(`k-table__num`));let i=t(this,`selectable`),a=this.headRow(),o=a?.querySelector(`.k-table__check`);if(i&&a&&!o){let e=document.createElement(`th`);e.scope=`col`,e.className=`k-table__check`,e.innerHTML='<input type="checkbox" class="k-checkbox" aria-label="${esc(msg().table.selectAll)}">',a.prepend(e),e.querySelector(`input`).addEventListener(`change`,e=>{let t=e.target.checked;this.visibleRows().forEach(e=>this.setRow(e,t)),this.selectionChanged()})}else!i&&o&&o.remove()}headRow(){return this.table.querySelector(`:scope > thead > tr`)}headCells(){return[...this.headRow()?.children??[]]}bodyRows(){return[...this.table.querySelectorAll(`:scope > tbody > tr`)]}dataIndex(e){return this.headRow()?.querySelector(`.k-table__check`)?e-1:e}offset(){return+!!this.headRow()?.querySelector(`.k-table__check`)}decorateRows(){let e=t(this,`selectable`);this.bodyRows().forEach((t,r)=>{var i,o;(i=t.dataset).id||(i.id=String(r+1)),(o=t.dataset).order||(o.order=String(r));let s=t.querySelector(`.k-table__check`);if(e&&!s){let e=document.createElement(`td`);e.className=`k-table__check`;let r=t.children[0]?.textContent?.trim();e.innerHTML=`<input type="checkbox" class="k-checkbox" aria-label="${n(r?a().table.selectNamed(r):a().table.selectRow)}">`,t.prepend(e),t.setAttribute(`aria-selected`,`false`),e.querySelector(`input`).addEventListener(`change`,e=>{this.setRow(t,e.target.checked),this.selectionChanged()})}else!e&&s&&(s.remove(),t.removeAttribute(`aria-selected`))}),this.selectionChanged(!1)}setRow(e,t){e.setAttribute(`aria-selected`,String(t));let n=e.querySelector(`.k-table__check input`);n&&(n.checked=t)}selectionChanged(t=!0){let r=this.headRow()?.querySelector(`.k-table__check input`),i=this.visibleRows(),a=i.filter(e=>e.getAttribute(`aria-selected`)===`true`).length;r&&(r.checked=i.length>0&&a===i.length,r.indeterminate=a>0&&a<i.length);let o=this.getAttribute(`name`);if(this.querySelectorAll(`:scope > input[type=hidden]`).forEach(e=>e.remove()),o)for(let e of this.selected)this.insertAdjacentHTML(`beforeend`,`<input type="hidden" name="${n(o)}" value="${n(e)}">`);t&&e(this,`kashi-change`,{selected:this.selected})}visibleRows(){return this.bodyRows().filter(e=>!e.hidden&&!e.classList.contains(`k-table__empty`))}searchBox(){let e=this.querySelector(`:scope > .k-table-search`);if(!t(this,`searchable`))return e?.remove();if(e)return;let i=`${this.key}-search`,o=document.createElement(`div`);o.className=`k-table-search k-field`,o.style.marginBlockEnd=`var(--k-space-3)`,o.innerHTML=`<div class="k-input k-input--sm"><input id="${i}" class="k-input__control" type="search" placeholder="${n(a().table.search)}" aria-label="${n(a().table.search)}"></div><span class="k-sr-only" aria-live="polite"></span>`,this.prepend(o);let s=o.querySelector(`input`);s.addEventListener(`input`,()=>{this.query=s.value,this.apply(),o.querySelector(`.k-sr-only`).textContent=a().table.rows(r(this,this.visibleRows().length))})}apply(){let e=this.table.querySelector(`:scope > tbody`);if(!e)return;let t=this.offset(),r=this.bodyRows().filter(e=>!e.classList.contains(`k-table__empty`)),i=this.headCells();i.forEach((e,n)=>{e.hasAttribute(`aria-sort`)&&e.setAttribute(`aria-sort`,this.sort&&this.sort.index===n-t?this.sort.dir:`none`)});let o=r.sort((e,t)=>Number(e.dataset.order)-Number(t.dataset.order));if(this.sort){let{index:e,dir:n}=this.sort,r=i[e+t]?.hasAttribute(`data-numeric`),a=n=>{let i=n.children[e+t],a=i?.dataset.value??i?.textContent?.trim()??``;return r?u(a):a},s=n===`ascending`?1:-1;o=[...o].sort((e,t)=>{let n=a(e),r=a(t);return(typeof n==`number`&&typeof r==`number`?n-r:String(n).localeCompare(String(r),`fa`))*s})}let s=d(this.query.trim());for(let t of o)t.hidden=!!s&&!d(t.textContent??``).includes(s),e.append(t);if(e.querySelector(`.k-table__empty`)?.remove(),s&&!this.visibleRows().length){let t=i.length;e.insertAdjacentHTML(`beforeend`,`<tr class="k-table__empty"><td colspan="${t}"><p style="padding: 2rem; margin: 0; text-align: center">${n(a().common.noResults)}</p></td></tr>`)}this.selectionChanged(!1)}};f.observedAttributes=[`selectable`,`searchable`,`compact`,`striped`,`sticky-header`,`caption`,`name`],i(`kashi-data-table`,f,`block`);export{f as KashiDataTable};
@@ -0,0 +1 @@
1
+ import{A as e,k as t}from"./base-DTQ50VdG.js";function n(n,{digits:r}={}){let i=n.replace(/[يى]/g,`ی`).replace(/ك/g,`ک`).replace(/ة/g,`ه`).replace(/[ً-ٰٟ]/g,``).replace(/ـ/g,``).replace(/‌{2,}/g,`‌`).replace(/ *‌ */g,`‌`).replace(/[ \t]{2,}/g,` `).trim();return r===`fa`&&(i=e(t(i))),r===`latin`&&(i=t(i)),i}function r(t,{digits:n=!0}={}){let r={arabic:0,zwnj:0,punctuation:0,digits:0,spacing:0},i=(e,t,n,i)=>e.replace(t,(...e)=>(r[i]++,typeof n==`string`?n.replace(/\$(\d)/g,(t,n)=>e[Number(n)]??``):n(...e))),a=`[\\u0621-\\u063A\\u0641-\\u064A\\u067E\\u0686\\u0698\\u06A9\\u06AF\\u06CC]`,o=t;o=i(o,/[يى]/g,`ی`,`arabic`),o=i(o,/ك/g,`ک`,`arabic`),o=i(o,RegExp(`(^|[\\s(«])(ن?می) (?=${a})`,`g`),`$1$2‌`,`zwnj`),o=i(o,RegExp(`(${a}) (ها|های|هایی|هایم|هایت|هایش|تر|ترین)(?=$|[\\s.،؛؟!»)])`,`g`),`$1‌$2`,`zwnj`),o=i(o,RegExp(`(${a}\\s*),(?=\\s)`,`g`),`$1،`,`punctuation`),o=i(o,RegExp(`(${a}\\s*)\\?`,`g`),`$1؟`,`punctuation`),o=i(o,RegExp(`(${a}\\s*);`,`g`),`$1؛`,`punctuation`),o=i(o,/ +([،؛؟!.:])(?=\s|$)/g,`$1`,`spacing`),o=i(o,/ {2,}/g,` `,`spacing`),n&&(o=i(o,RegExp(`(${a}[\\s\\u200c]*)(\\d+)|(\\d+)(?=[\\s\\u200c]*${a})`,`g`),(t,n,r,i)=>n?n+e(r):e(i),`digits`));let s=Object.values(r).reduce((e,t)=>e+t,0);return{text:o,fixes:r,count:s}}export{n,r as t};
@@ -0,0 +1 @@
1
+ import{O as e}from"./base-DTQ50VdG.js";function t(t){let n=t.trim();if(!n)return null;let r=n.split(/[:٫.،,\s]/).map(e).filter(Boolean),i,a;if(r.length>=2)[i,a]=[Number(r[0]),Number(r[1])];else{let t=e(n);t.length<=2?[i,a]=[Number(t),0]:[i,a]=[Number(t.slice(0,-2)),Number(t.slice(-2))]}return i>=0&&i<24&&a>=0&&a<60?{hours:i,minutes:a}:null}function n({hours:e,minutes:t}){return`${String(e).padStart(2,`0`)}:${String(t).padStart(2,`0`)}`}export{t as n,n as t};
@@ -0,0 +1 @@
1
+ const e=[``,`یک`,`دو`,`سه`,`چهار`,`پنج`,`شش`,`هفت`,`هشت`,`نه`],t=[`ده`,`یازده`,`دوازده`,`سیزده`,`چهارده`,`پانزده`,`شانزده`,`هفده`,`هجده`,`نوزده`],n=[``,``,`بیست`,`سی`,`چهل`,`پنجاه`,`شصت`,`هفتاد`,`هشتاد`,`نود`],r=[``,`صد`,`دویست`,`سیصد`,`چهارصد`,`پانصد`,`ششصد`,`هفتصد`,`هشتصد`,`نهصد`],i=[``,`هزار`,`میلیون`,`میلیارد`,`تریلیون`];function a(i){let a=[],o=Math.floor(i/100),s=i%100;return o&&a.push(r[o]),s>=10&&s<20?a.push(t[s-10]):(s>=20&&a.push(n[Math.floor(s/10)]),s%10&&a.push(e[s%10])),a.join(` و `)}const o=[``,`one`,`two`,`three`,`four`,`five`,`six`,`seven`,`eight`,`nine`,`ten`,`eleven`,`twelve`,`thirteen`,`fourteen`,`fifteen`,`sixteen`,`seventeen`,`eighteen`,`nineteen`],s=[``,``,`twenty`,`thirty`,`forty`,`fifty`,`sixty`,`seventy`,`eighty`,`ninety`],c=[``,`thousand`,`million`,`billion`,`trillion`];function l(e){let t=Math.floor(e/100),n=e%100,r=n<20?o[n]:`${s[Math.floor(n/10)]}${n%10?`-${o[n%10]}`:``}`;return[t?`${o[t]} hundred`:``,r].filter(Boolean).join(` `)}function u(e,{locale:t=`fa`}={}){let n=typeof e==`bigint`?e:BigInt(Math.trunc(e)),r=t===`en`;if(n===0n)return r?`zero`:`صفر`;let o=n<0n;if(o&&(n=-n),n>=10n**15n)throw RangeError(`عدد بزرگ‌تر از حد پشتیبانی‌شده است`);let s=[];for(let e=0;n>0n;e++,n/=1000n){let t=Number(n%1000n);if(!t)continue;if(r){s.unshift([l(t),c[e]].filter(Boolean).join(` `));continue}let o=e===1&&t===1?``:a(t);s.unshift([o,i[e]].filter(Boolean).join(` `))}return r?(o?`minus `:``)+s.join(` `):(o?`منفی `:``)+s.join(` و `)}export{u as t};
package/dist/cdn.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { toast } from "./toast.js";
2
+ export { setLocale } from "./base.js";
2
3
  /** همهٔ عنصرها را همین حالا بارگذاری می‌کند؛ مثلاً پیش از ساختن عنصرها با JavaScript */
3
4
  export declare function loadAll(): Promise<unknown[]>;
package/dist/cdn.js CHANGED
@@ -1,6 +1,8 @@
1
1
  // نسخهٔ CDN با بارگذاری تنبل: هر بخش فقط وقتی یکی از تگ‌هایش در صفحه پیدا شود دانلود می‌شود.
2
2
  // صفحه‌ای که فقط فیلد کد ملی دارد، نقشه و نمودارها را دانلود نمی‌کند.
3
3
  export { toast } from "./toast.js";
4
+ // زبان پیش از بارگذاری عنصرها: <script type="module">import { setLocale } from ".../kashi-elements.js"; setLocale("en");</script>
5
+ export { setLocale } from "./base.js";
4
6
  const MODULES = [
5
7
  [["kashi-national-id", "kashi-legal-id", "kashi-mobile", "kashi-landline", "kashi-card-number", "kashi-iban", "kashi-postal-code", "kashi-price"], () => import("./inputs.js")],
6
8
  [["kashi-otp"], () => import("./fields.js")],
@@ -15,6 +17,8 @@ const MODULES = [
15
17
  [["kashi-multi-select", "kashi-slider", "kashi-chip-group", "kashi-password-strength", "kashi-persian-textarea", "kashi-keyboard-fix", "kashi-time-input", "kashi-plate-input", "kashi-inline-edit"], () => import("./forms.js")],
16
18
  [["kashi-date-range-picker", "kashi-month-picker", "kashi-file-upload", "kashi-color-picker"], () => import("./pickers.js")],
17
19
  [["kashi-bar-chart", "kashi-line-chart", "kashi-pie-chart", "kashi-bar-list", "kashi-proportion-bar", "kashi-sparkline", "kashi-activity-heatmap"], () => import("./charts.js")],
20
+ [["kashi-input", "kashi-textarea", "kashi-banner"], () => import("./basic.js")],
21
+ [["kashi-appointment-picker"], () => import("./appointment.js")],
18
22
  [["kashi-command-palette", "kashi-context-menu", "kashi-carousel", "kashi-tree-view", "kashi-tour", "kashi-splitter", "kashi-side-nav"], () => import("./nav.js")],
19
23
  ];
20
24
  const pending = new Set(MODULES);
package/dist/charts.js CHANGED
@@ -1,15 +1,18 @@
1
- import { JALALI_MONTHS, formatJalali, formatNumber, jalaliMonthLength, toGregorian, toISODate, toJalali } from "@kashi-ui/persian";
2
- import { HTMLBase, define, esc, uid } from "./base.js";
1
+ import { formatJalali, formatNumber as fmtNumber, jalaliMonthLength, toGregorian, toISODate, toJalali } from "@kashi-ui/persian";
2
+ import { calendarNames, define, esc, HTMLBase, lang, localeDigits, msg, uid } from "./base.js";
3
+ // عددها با ارقام زبان فعلی (setLocale)
4
+ const formatNumber = (n) => fmtNumber(n, { digits: localeDigits() });
5
+ const dec = (x) => formatNumber(Math.round(x * 10) / 10).replace(".", msg().common.decimal);
3
6
  /** عدد کوتاه برای محور: ۱٬۲۰۰٬۰۰۰ ← ۱٫۲ م */
4
7
  const compact = (n) => {
5
8
  const a = Math.abs(n);
6
- const fa = (x, u) => `${formatNumber(Math.round(x * 10) / 10).replace(".", "٫")}${u}`;
9
+ const C = msg().chart;
7
10
  if (a >= 1e9)
8
- return fa(n / 1e9, " میلیارد");
11
+ return dec(n / 1e9) + C.billion;
9
12
  if (a >= 1e6)
10
- return fa(n / 1e6, " م");
13
+ return dec(n / 1e6) + C.million;
11
14
  if (a >= 1e3)
12
- return fa(n / 1e3, " هزار");
15
+ return dec(n / 1e3) + C.thousand;
13
16
  return formatNumber(n);
14
17
  };
15
18
  /** گام‌های گرد محور: ۰، ۲۵، ۵۰… */
@@ -21,7 +24,7 @@ function niceTicks(max, count = 4) {
21
24
  const step = [1, 2, 2.5, 5, 10].map((m) => m * mag).find((s) => s >= raw);
22
25
  return Array.from({ length: Math.ceil(max / step) + 1 }, (_, i) => i * step);
23
26
  }
24
- const pctOf = (v, total) => `${formatNumber(Math.round((v / (total || 1)) * 1000) / 10).replace(".", "٫")}٪`;
27
+ const pctOf = (v, total) => msg().common.percent(dec((v / (total || 1)) * 100));
25
28
  const chartColor = (i) => `var(--k-chart-${i + 1})`;
26
29
  const json = (t, fallback) => {
27
30
  if (!t)
@@ -113,7 +116,7 @@ export class KashiBarChart extends Chart {
113
116
  const top = ticks.at(-1) || 1;
114
117
  const maxI = totals.indexOf(max);
115
118
  const labelMax = this.getAttribute("label-max") !== "false";
116
- const describe = (i) => (stacked ? `${cats[i]}: ${stacked.map((s) => `${s.name} ${f(s.values[i] ?? 0)}`).join("، ")}، جمع ${f(totals[i])}` : `${cats[i]}: ${f(totals[i])}`);
119
+ const describe = (i) => (stacked ? `${cats[i]}: ${stacked.map((s) => `${s.name} ${f(s.values[i] ?? 0)}`).join(msg().common.list)}${msg().common.list}${msg().common.total} ${f(totals[i])}` : `${cats[i]}: ${f(totals[i])}`);
117
120
  const height = this.getAttribute("height");
118
121
  this.innerHTML = `<figure class="k-chart k-chart--bar" aria-labelledby="${this.key}-t" style="margin: 0${height ? `; --k-chart-height: ${esc(height)}` : ""}"><figcaption class="k-chart__head"><p class="k-chart__title" id="${this.key}-t">${esc(this.heading)}</p>${stacked && stacked.length > 1 ? `<ul class="k-chart__legend" aria-hidden="true">${stacked.map((s, i) => `<li style="--_c: ${chartColor(i)}"><span class="k-chart__key k-chart__key--box"></span>${esc(s.name)}</li>`).join("")}</ul>` : ""}</figcaption><div class="k-chart__plot"><div class="k-bars__grid" aria-hidden="true">${ticks.map((t) => `<span data-v="${esc(compact(t))}"></span>`).join("")}</div><div class="k-bars" role="list" aria-label="${esc(this.heading)}">${cats.map((label, i) => `<div class="k-bars__col" role="listitem" tabindex="0" data-i="${i}" aria-label="${esc(describe(i))}" style="--_d: ${i * 40}ms">${labelMax && i === maxI ? `<span class="k-bars__value" aria-hidden="true">${esc(f(totals[i]))}</span>` : ""}${stacked ? `<span class="k-bars__stack" style="block-size: ${(totals[i] / top) * 100}%" aria-hidden="true">${stacked.map((s, si) => `<span class="k-bars__seg" style="--_c: ${chartColor(si)}; flex-grow: ${s.values[i] ?? 0}"></span>`).join("")}</span>` : `<span class="k-bars__bar" style="block-size: ${(totals[i] / top) * 100}%" aria-hidden="true"></span>`}<span class="k-bars__label" aria-hidden="true">${esc(label)}</span></div>`).join("")}</div></div></figure>`;
119
122
  const plot = this.querySelector(".k-chart__plot");
@@ -123,7 +126,7 @@ export class KashiBarChart extends Chart {
123
126
  col.setAttribute("data-active", "");
124
127
  const p = this.at(col.querySelector(".k-bars__bar, .k-bars__stack") ?? col, plot);
125
128
  this.tip(plot, p.x, p.y, cats[i], stacked
126
- ? [...stacked.map((s, si) => ({ name: s.name, value: f(s.values[i] ?? 0), color: chartColor(si) })), { name: "جمع", value: f(totals[i]) }]
129
+ ? [...stacked.map((s, si) => ({ name: s.name, value: f(s.values[i] ?? 0), color: chartColor(si) })), { name: msg().common.total, value: f(totals[i]) }]
127
130
  : [{ value: f(totals[i]) }]);
128
131
  };
129
132
  const hide = () => {
@@ -155,7 +158,7 @@ export class KashiBarList extends Chart {
155
158
  const d = data[Number(row.dataset.i)];
156
159
  row.setAttribute("data-active", "");
157
160
  const p = this.at(row.querySelector(".k-barlist__fill"), box);
158
- this.tip(box, p.x, p.y, d.label, [{ name: "مقدار", value: f(d.value) }, { name: "سهم از کل", value: pctOf(d.value, total) }]);
161
+ this.tip(box, p.x, p.y, d.label, [{ name: msg().chart.value, value: f(d.value) }, { name: msg().chart.share, value: pctOf(d.value, total) }]);
159
162
  });
160
163
  row.addEventListener("pointerleave", () => {
161
164
  row.removeAttribute("data-active");
@@ -182,7 +185,7 @@ export class KashiProportionBar extends Chart {
182
185
  const i = Number(seg.dataset.i);
183
186
  mark(i);
184
187
  const p = this.at(seg, box);
185
- this.tip(box, p.x, p.y, undefined, [{ name: parts[i].label, value: pctOf(parts[i].value, total), color: chartColor(i) }, { name: "تعداد", value: f(parts[i].value) }]);
188
+ this.tip(box, p.x, p.y, undefined, [{ name: parts[i].label, value: pctOf(parts[i].value, total), color: chartColor(i) }, { name: msg().chart.count, value: f(parts[i].value) }]);
186
189
  });
187
190
  seg.addEventListener("pointerleave", () => {
188
191
  mark(null);
@@ -199,12 +202,12 @@ export class KashiPieChart extends Chart {
199
202
  draw() {
200
203
  const f = this.format;
201
204
  const sorted = [...this.data].sort((a, b) => b.value - a.value);
202
- const parts = sorted.length > 5 ? [...sorted.slice(0, 4), { label: "سایر", value: sorted.slice(4).reduce((n, d) => n + d.value, 0) }] : sorted;
205
+ const parts = sorted.length > 5 ? [...sorted.slice(0, 4), { label: msg().common.other, value: sorted.slice(4).reduce((n, d) => n + d.value, 0) }] : sorted;
203
206
  const total = parts.reduce((n, d) => n + d.value, 0) || 1;
204
207
  const slot = (i) => (i < 4 ? chartColor(i) : "var(--k-text-quaternary)");
205
208
  const donut = this.getAttribute("variant") !== "pie";
206
209
  const size = Number(this.getAttribute("size")) || 176;
207
- const totalLabel = this.getAttribute("total-label") ?? "جمع";
210
+ const totalLabel = this.getAttribute("total-label") ?? msg().common.total;
208
211
  // هر بخش یک کمان با stroke-dasharray؛ برای pie، شعاع نصف و ضخامت برابر قطر
209
212
  const R = 50, r = donut ? 40 : 25, w = donut ? 16 : 50;
210
213
  const C = 2 * Math.PI * r;
@@ -232,7 +235,7 @@ export class KashiPieChart extends Chart {
232
235
  if (i === null)
233
236
  this.tip(plot, null);
234
237
  else
235
- this.tip(plot, mid(i).x, mid(i).y, undefined, [{ name: parts[i].label, value: pctOf(parts[i].value, total), color: slot(i) }, { name: "تعداد", value: f(parts[i].value) }]);
238
+ this.tip(plot, mid(i).x, mid(i).y, undefined, [{ name: parts[i].label, value: pctOf(parts[i].value, total), color: slot(i) }, { name: msg().chart.count, value: f(parts[i].value) }]);
236
239
  }
237
240
  };
238
241
  this.querySelectorAll(".k-pie__slice, .k-pie__legend li").forEach((el) => el.addEventListener("pointerenter", () => activate(Number(el.dataset.i))));
@@ -303,7 +306,7 @@ export class KashiLineChart extends Chart {
303
306
  const f = this.format;
304
307
  const labels = this.labels;
305
308
  const height = this.getAttribute("height");
306
- this.innerHTML = `<figure class="k-chart" aria-labelledby="${this.key}-t" style="margin: 0${height ? `; --k-chart-height: ${esc(height)}` : ""}"><figcaption class="k-chart__head"><p class="k-chart__title" id="${this.key}-t">${esc(this.heading)}</p>${shown.length > 1 ? `<ul class="k-chart__legend" aria-hidden="true">${shown.map((s, i) => `<li style="--_c: ${chartColor(i)}"><span class="k-chart__key"></span>${esc(s.name)}</li>`).join("")}</ul>` : ""}</figcaption><div class="k-chart__plot" tabindex="0" role="img" aria-label="${esc(`${this.heading}. با کلیدهای جهت بین نقطه‌ها حرکت کنید.`)}" aria-describedby="${this.key}-live"><svg class="k-chart__svg" aria-hidden="true" direction="ltr"></svg></div><span class="k-sr-only" aria-live="polite" id="${this.key}-live"></span><table class="k-sr-only"><caption>${esc(this.heading)}</caption><thead><tr><th scope="col">دوره</th>${shown.map((s) => `<th scope="col">${esc(s.name)}</th>`).join("")}</tr></thead><tbody>${labels.map((l, i) => `<tr><th scope="row">${esc(l)}</th>${shown.map((s) => `<td>${esc(f(s.values[i]))}</td>`).join("")}</tr>`).join("")}</tbody></table></figure>`;
309
+ this.innerHTML = `<figure class="k-chart" aria-labelledby="${this.key}-t" style="margin: 0${height ? `; --k-chart-height: ${esc(height)}` : ""}"><figcaption class="k-chart__head"><p class="k-chart__title" id="${this.key}-t">${esc(this.heading)}</p>${shown.length > 1 ? `<ul class="k-chart__legend" aria-hidden="true">${shown.map((s, i) => `<li style="--_c: ${chartColor(i)}"><span class="k-chart__key"></span>${esc(s.name)}</li>`).join("")}</ul>` : ""}</figcaption><div class="k-chart__plot" tabindex="0" role="img" aria-label="${esc(msg().chart.keyboard(this.heading))}" aria-describedby="${this.key}-live"><svg class="k-chart__svg" aria-hidden="true" direction="ltr"></svg></div><span class="k-sr-only" aria-live="polite" id="${this.key}-live"></span><table class="k-sr-only"><caption>${esc(this.heading)}</caption><thead><tr><th scope="col">${esc(msg().chart.period)}</th>${shown.map((s) => `<th scope="col">${esc(s.name)}</th>`).join("")}</tr></thead><tbody>${labels.map((l, i) => `<tr><th scope="row">${esc(l)}</th>${shown.map((s) => `<td>${esc(f(s.values[i]))}</td>`).join("")}</tr>`).join("")}</tbody></table></figure>`;
307
310
  const plot = this.querySelector(".k-chart__plot");
308
311
  plot.addEventListener("pointermove", (e) => this.pick(e.clientX));
309
312
  plot.addEventListener("pointerleave", () => this.setActive(null));
@@ -367,7 +370,7 @@ export class KashiLineChart extends Chart {
367
370
  this.tip(plot, null);
368
371
  else
369
372
  this.tip(plot, x(a), Math.min(...shown.map((s) => y(s.values[a]))) - 6, labels[a], shown.map((s, si) => ({ name: s.name, value: f(s.values[a]), color: chartColor(si) })));
370
- this.querySelector(`#${this.key}-live`).textContent = a === null ? "" : `${labels[a]}: ${shown.map((s) => `${s.name} ${f(s.values[a])}`).join("، ")}`;
373
+ this.querySelector(`#${this.key}-live`).textContent = a === null ? "" : `${labels[a]}: ${shown.map((s) => `${s.name} ${f(s.values[a])}`).join(msg().common.list)}`;
371
374
  }
372
375
  pick(clientX) {
373
376
  const { plot, labels, x } = this.geometry();
@@ -386,7 +389,6 @@ export class KashiLineChart extends Chart {
386
389
  }
387
390
  }
388
391
  KashiLineChart.observedAttributes = ["label", "labels", "series", "unit", "area", "height"];
389
- const WEEK = ["ش", "", "د", "", "چ", "", "ج"];
390
392
  /** نقشهٔ فعالیت سالانه با ماه‌های شمسی: data='{"2026-03-21": 3, …}'، year="1405"، unit="فعالیت" */
391
393
  export class KashiActivityHeatmap extends Chart {
392
394
  get data() { return this.read("data", {}); }
@@ -394,7 +396,8 @@ export class KashiActivityHeatmap extends Chart {
394
396
  draw() {
395
397
  const data = this.data;
396
398
  const year = Number(this.getAttribute("year")) || toJalali(new Date()).jy;
397
- const unit = this.getAttribute("unit") ?? "فعالیت";
399
+ const H = msg().heatmap;
400
+ const unit = this.getAttribute("unit") ?? H.unit;
398
401
  const days = [];
399
402
  for (let m = 1; m <= 12; m++)
400
403
  for (let d = 1; d <= jalaliMonthLength(year, m); d++) {
@@ -406,12 +409,12 @@ export class KashiActivityHeatmap extends Chart {
406
409
  // شنبه سطر اول
407
410
  const offset = (days[0].date.getDay() + 1) % 7;
408
411
  const col = (i) => Math.floor((i + offset) / 7);
409
- const months = JALALI_MONTHS.map((name, m) => ({ name, col: col(days.findIndex((d) => toJalali(d.date).jm === m + 1)) }));
412
+ const months = calendarNames().months.map((name, m) => ({ name, col: col(days.findIndex((d) => toJalali(d.date).jm === m + 1)) }));
410
413
  const total = days.reduce((s, d) => s + d.value, 0);
411
- const monthly = JALALI_MONTHS.map((name, m) => ({ name, sum: days.filter((d) => toJalali(d.date).jm === m + 1).reduce((s, d) => s + d.value, 0) }));
412
- const yearText = formatNumber(year).replace(/٬/g, "");
413
- const name = this.getAttribute("label") ?? `${unit} در سال ${yearText}`;
414
- this.innerHTML = `<figure class="k-heatmap" style="margin: 0"><div class="k-heatmap__scroll"><div role="img" aria-label="${esc(`${name}: ${formatNumber(total)} ${unit}`)}"><div class="k-heatmap__months" aria-hidden="true">${months.map((m) => `<span style="grid-column: ${m.col + 1}">${m.name}</span>`).join("")}</div><div class="k-heatmap__body" aria-hidden="true"><div class="k-heatmap__days">${WEEK.map((w) => `<span>${w}</span>`).join("")}</div><div class="k-heatmap__grid">${'<span class="k-heatmap__cell" data-empty=""></span>'.repeat(offset)}${days.map((d, i) => `<span class="k-heatmap__cell" data-l="${level(d.value)}" data-i="${i}"></span>`).join("")}</div></div></div></div><figcaption class="k-heatmap__foot"><span>${formatNumber(total)} ${esc(unit)} در ${yearText}</span><span class="k-heatmap__scale" aria-hidden="true">کمتر ${[0, 1, 2, 3, 4].map((l) => `<span class="k-heatmap__cell" data-l="${l}"></span>`).join("")} بیشتر</span></figcaption><table class="k-sr-only"><caption>${esc(name)}، به تفکیک ماه</caption><tbody>${monthly.map((m) => `<tr><th scope="row">${m.name}</th><td>${formatNumber(m.sum)}</td></tr>`).join("")}</tbody></table></figure>`;
414
+ const monthly = calendarNames().months.map((name, m) => ({ name, sum: days.filter((d) => toJalali(d.date).jm === m + 1).reduce((s, d) => s + d.value, 0) }));
415
+ const yearText = formatNumber(year).replace(/[٬,]/g, "");
416
+ const name = this.getAttribute("label") ?? H.inYear(unit, yearText);
417
+ this.innerHTML = `<figure class="k-heatmap" style="margin: 0"><div class="k-heatmap__scroll"><div role="img" aria-label="${esc(`${name}: ${formatNumber(total)} ${unit}`)}"><div class="k-heatmap__months" aria-hidden="true">${months.map((m) => `<span style="grid-column: ${m.col + 1}">${m.name}</span>`).join("")}</div><div class="k-heatmap__body" aria-hidden="true"><div class="k-heatmap__days">${H.weekdays.map((w) => `<span>${w}</span>`).join("")}</div><div class="k-heatmap__grid">${'<span class="k-heatmap__cell" data-empty=""></span>'.repeat(offset)}${days.map((d, i) => `<span class="k-heatmap__cell" data-l="${level(d.value)}" data-i="${i}"></span>`).join("")}</div></div></div></div><figcaption class="k-heatmap__foot"><span>${esc(H.total(formatNumber(total), unit, yearText))}</span><span class="k-heatmap__scale" aria-hidden="true">${esc(H.less)} ${[0, 1, 2, 3, 4].map((l) => `<span class="k-heatmap__cell" data-l="${l}"></span>`).join("")} ${esc(H.more)}</span></figcaption><table class="k-sr-only"><caption>${esc(H.byMonth(name))}</caption><tbody>${monthly.map((m) => `<tr><th scope="row">${m.name}</th><td>${formatNumber(m.sum)}</td></tr>`).join("")}</tbody></table></figure>`;
415
418
  const fig = this.querySelector("figure");
416
419
  this.querySelector(".k-heatmap__grid").addEventListener("pointerover", (e) => {
417
420
  const cell = e.target.closest("[data-i]");
@@ -421,7 +424,7 @@ export class KashiActivityHeatmap extends Chart {
421
424
  cell.setAttribute("data-active", "");
422
425
  const p = this.at(cell, fig);
423
426
  const d = days[Number(cell.dataset.i)];
424
- this.tip(fig, p.x, p.y - 2, formatJalali(d.date, { style: "full" }), [{ value: `${formatNumber(d.value)} ${unit}` }]);
427
+ this.tip(fig, p.x, p.y - 2, formatJalali(d.date, { style: "full", digits: localeDigits(), locale: lang() }), [{ value: `${formatNumber(d.value)} ${unit}` }]);
425
428
  });
426
429
  fig.addEventListener("pointerleave", () => this.tip(fig, null));
427
430
  }
@@ -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, toFaDigits } from "@kashi-ui/persian";
2
- import { HTMLBase, afterParse, define, emit, esc, fieldHTML, setMessages, syncHidden, uid } from "./base.js";
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="نمایش گزینه‌ها"${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>`;
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") ?? "نتیجه‌ای پیدا نشد.")}</li>`);
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">${toFaDigits(this.filtered.length - MAX_RENDER)} گزینهٔ دیگر؛ برای پیدا کردن تایپ کنید.</li>`);
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 ? `${toFaDigits(this.filtered.length)} نتیجه` : "";
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
- constructor() {
323
- super(...arguments);
324
- this.defaultLabel = "استان";
325
- this.defaultPlaceholder = "جستجوی استان یا شهر";
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 ? `مرکز: ${p.capital}` : undefined, keywords: [p.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 { HTMLBase, bool, define, emit, esc, fieldHTML, setMessages, uid } from "./base.js";
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 ? `حداکثر ${toFaDigits(max)} مورد` : !dup && next.includes(t) ? "این مورد قبلاً اضافه شده است." : this.validate?.(t);
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 = `${t} اضافه شد`;
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), `${t} حذف شد`);
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="حذف ${esc(t)}">${REMOVE}</button>`}</span>`).join("");
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.getAttribute("digits") === "latin" ? s : toFaDigits(s).replace(/,/g, "٬");
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="کاهش"${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="افزایش"${disabled ? " disabled" : ""}>${PLUS}</button></div></div>`;
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)} از ${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>`;
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)} از ${faNum(max)}">${STAR}</label>`).join("")}${valueHtml}</fieldset>`;
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.getAttribute("digits") === "latin" ? String(v) : toFaDigits(v));
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") ?? "صفحه‌بندی")}">${item(page - 1, `${chevron("m12 5-5 5 5 5")}قبلی`, "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: `صفحهٔ ${n(p)}` })}</li>`).join("")}</ul>${item(page + 1, `بعدی${chevron("m8 5 5 5-5 5")}`, "k-btn k-btn--secondary k-btn--sm", { disabled: page >= total })}</nav>`;
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)