@kashi-ui/elements 0.0.0-stage → 0.24.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 CHANGED
@@ -1,3 +1,42 @@
1
- # Temporary Holding Version
1
+ # @kashi-ui/elements
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ وب کامپوننت‌های [کاشی](https://kashi.sepantapouya.com)، دیزاین‌سیستم فارسی و راست‌به‌چپ؛ برای HTML ساده، وردپرس، Vue، Svelte، Angular و هر فریم‌ورک دیگری. بدون وابستگی به React و بدون Shadow DOM، تا تم و CSS شما مستقیم اعمال شود.
4
+
5
+ ```html
6
+ <html lang="fa" dir="rtl">
7
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css">
8
+ <script type="module" src="https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js"></script>
9
+
10
+ <form action="/checkout" method="post">
11
+ <kashi-national-id name="national_id" required></kashi-national-id>
12
+ <kashi-mobile name="mobile"></kashi-mobile>
13
+ <kashi-date-picker label="تاریخ تحویل" name="delivery" holidays></kashi-date-picker>
14
+ <button class="k-btn k-btn--primary">ثبت سفارش</button>
15
+ </form>
16
+ ```
17
+
18
+ یا با npm:
19
+
20
+ ```bash
21
+ npm i @kashi-ui/elements @kashi-ui/css
22
+ ```
23
+
24
+ ```js
25
+ import "@kashi-ui/css";
26
+ import "@kashi-ui/elements";
27
+ ```
28
+
29
+ ## عنصرها
30
+
31
+ - **فیلدهای فارسی:** `kashi-national-id`، `kashi-legal-id`، `kashi-mobile`، `kashi-landline`، `kashi-card-number`، `kashi-iban`، `kashi-postal-code`، `kashi-price`
32
+ - **انتخاب:** `kashi-date-picker`، `kashi-calendar`، `kashi-otp`، `kashi-combobox`، `kashi-province-select`، `kashi-city-select`، `kashi-tag-input`، `kashi-number-input`، `kashi-rating`
33
+ - **نشانی و نقشه:** `kashi-address-form`، `kashi-iran-map`
34
+ - **داده:** `kashi-data-table`، `kashi-pagination`
35
+ - **نمایش:** `kashi-money`، `kashi-date`، `kashi-relative-time`، `kashi-countdown`
36
+ - **تعاملی:** `kashi-dialog`، `kashi-tabs`، `kashi-tooltip`، `kashi-dropdown-menu`، `kashi-popover`، `kashi-toaster` (و تابع `toast()`)، `kashi-copy-button`
37
+
38
+ هر فیلد با `name` مقدار خام را با ارقام انگلیسی در فرم ارسال می‌کند، مقدار نامعتبر جلوی ارسال فرم را می‌گیرد و با هر تغییر رویداد `kashi-change` می‌فرستد.
39
+
40
+ راهنمای کامل، نمونه‌های زنده و تنظیم Vue و Angular: [kashi.sepantapouya.com/docs/web-components](https://kashi.sepantapouya.com/docs/web-components/)
41
+
42
+ MIT
package/dist/base.d.ts ADDED
@@ -0,0 +1,47 @@
1
+ /** در Node.js (رندر سمت سرور) HTMLElement وجود ندارد؛ import کردن پکیج نباید خطا بدهد */
2
+ export declare const HTMLBase: typeof HTMLElement;
3
+ /** ثبت عنصر، فقط در مرورگر و فقط یک بار (اگر پکیج دو بار بارگذاری شود خطا نمی‌دهد) */
4
+ export declare function define(tag: string, ctor: CustomElementConstructor, display?: "block" | "inline" | "contents"): void;
5
+ /**
6
+ * فرزندان عنصر را پس از خوانده شدن کامل HTML در اختیار cb می‌گذارد.
7
+ * اگر اسکریپت بدون type="module" در <head> باشد، عنصر پیش از فرزندانش به صفحه وصل می‌شود.
8
+ */
9
+ export declare function afterParse(cb: () => void): void;
10
+ /** شناسهٔ یکتا برای اتصال برچسب، راهنما و خطا */
11
+ export declare const uid: (prefix: string) => string;
12
+ /** متن امن برای قرار دادن در HTML */
13
+ export declare const esc: (value: unknown) => string;
14
+ /** ویژگی بولی: حضورش یعنی true، مگر «false» */
15
+ export declare const bool: (el: Element, name: string, fallback?: boolean) => boolean;
16
+ /** رویداد سفارشی که بالا می‌رود و از مرز عناصر هم رد می‌شود */
17
+ export declare function emit<T>(el: Element, type: string, detail: T): void;
18
+ export declare const svg: (body: string, strokeWidth?: number, mirror?: boolean) => string;
19
+ export declare const ICONS: {
20
+ close: string;
21
+ info: string;
22
+ success: string;
23
+ warning: string;
24
+ error: string;
25
+ copy: string;
26
+ check: string;
27
+ calendar: string;
28
+ prev: string;
29
+ next: string;
30
+ };
31
+ export interface FieldOptions {
32
+ id: string;
33
+ label?: string | null;
34
+ required?: boolean;
35
+ optional?: string | null;
36
+ /** کادر کنترل، مثل <div class="k-input">…</div> */
37
+ control: string;
38
+ }
39
+ /** قالب مشترک برچسب + کنترل؛ ردیف راهنما و خطا را setMessages می‌سازد */
40
+ export declare function fieldHTML({ id, label, required, optional, control }: FieldOptions): string;
41
+ /**
42
+ * خطا و راهنمای زیر فیلد را به‌روز می‌کند و aria-invalid و aria-describedby کنترل را با آن‌ها همگام نگه می‌دارد.
43
+ * extra شناسه‌های دیگری است که کنترل را توصیف می‌کنند، مثل نام بانک.
44
+ */
45
+ export declare function setMessages(field: Element, control: HTMLElement, id: string, error: string | null, hint: string | null, extra?: string | null): void;
46
+ /** فیلد پنهان برای ارسال مقدار خام در فرم HTML معمولی */
47
+ export declare function syncHidden(host: Element, name: string | null, value: string): void;
package/dist/base.js ADDED
@@ -0,0 +1,124 @@
1
+ // پایهٔ مشترک وب کامپوننت‌ها: بدون Shadow DOM، تا کلاس‌های k-* از @kashi-ui/css و تم کاربر مستقیم اعمال شوند.
2
+ /** در Node.js (رندر سمت سرور) HTMLElement وجود ندارد؛ import کردن پکیج نباید خطا بدهد */
3
+ export const HTMLBase = typeof HTMLElement === "undefined" ? class {
4
+ } : HTMLElement;
5
+ const BLOCK_TAGS = [];
6
+ const CONTENTS_TAGS = [];
7
+ let style = null;
8
+ /** ثبت عنصر، فقط در مرورگر و فقط یک بار (اگر پکیج دو بار بارگذاری شود خطا نمی‌دهد) */
9
+ export function define(tag, ctor, display = "inline") {
10
+ if (typeof customElements === "undefined" || customElements.get(tag))
11
+ return;
12
+ if (display === "block")
13
+ BLOCK_TAGS.push(tag);
14
+ if (display === "contents")
15
+ CONTENTS_TAGS.push(tag);
16
+ customElements.define(tag, ctor);
17
+ if (typeof document === "undefined")
18
+ return;
19
+ if (!style) {
20
+ style = document.createElement("style");
21
+ style.dataset.kashiElements = "";
22
+ document.head.prepend(style);
23
+ }
24
+ // در Cascade Layer و با :where تا CSS خود کاربر همیشه برنده باشد
25
+ const rules = [
26
+ BLOCK_TAGS.length && `:where(${BLOCK_TAGS.join(",")}) { display: block; }`,
27
+ CONTENTS_TAGS.length && `:where(${CONTENTS_TAGS.join(",")}) { display: contents; }`,
28
+ ].filter(Boolean);
29
+ style.textContent = `@layer kashi.elements { ${rules.join(" ")} }`;
30
+ }
31
+ /**
32
+ * فرزندان عنصر را پس از خوانده شدن کامل HTML در اختیار cb می‌گذارد.
33
+ * اگر اسکریپت بدون type="module" در <head> باشد، عنصر پیش از فرزندانش به صفحه وصل می‌شود.
34
+ */
35
+ export function afterParse(cb) {
36
+ if (typeof document !== "undefined" && document.readyState === "loading")
37
+ document.addEventListener("DOMContentLoaded", cb, { once: true });
38
+ else
39
+ cb();
40
+ }
41
+ let counter = 0;
42
+ /** شناسهٔ یکتا برای اتصال برچسب، راهنما و خطا */
43
+ export const uid = (prefix) => `${prefix}-${(++counter).toString(36)}`;
44
+ const ENTITIES = { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" };
45
+ /** متن امن برای قرار دادن در HTML */
46
+ export const esc = (value) => String(value ?? "").replace(/[&<>"']/g, (c) => ENTITIES[c]);
47
+ /** ویژگی بولی: حضورش یعنی true، مگر «false» */
48
+ export const bool = (el, name, fallback = false) => {
49
+ const v = el.getAttribute(name);
50
+ return v === null ? fallback : v !== "false";
51
+ };
52
+ /** رویداد سفارشی که بالا می‌رود و از مرز عناصر هم رد می‌شود */
53
+ export function emit(el, type, detail) {
54
+ el.dispatchEvent(new CustomEvent(type, { detail, bubbles: true, composed: true }));
55
+ }
56
+ export const svg = (body, strokeWidth = 1.7, mirror = false) => `<svg${mirror ? ' data-k-mirror=""' : ""} viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="${strokeWidth}" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${body}</svg>`;
57
+ export const ICONS = {
58
+ close: svg('<path d="m5 5 10 10M15 5 5 15"/>'),
59
+ info: svg('<circle cx="10" cy="10" r="7.5"/><path d="M10 13.5V9.5M10 6.5h.01"/>'),
60
+ success: svg('<circle cx="10" cy="10" r="7.5"/><path d="m6.75 10 2.25 2.25 4.25-4.5"/>'),
61
+ warning: svg('<path d="M8.6 3.4 2.4 14a1.6 1.6 0 0 0 1.4 2.4h12.4a1.6 1.6 0 0 0 1.4-2.4L11.4 3.4a1.6 1.6 0 0 0-2.8 0Z"/><path d="M10 7.5v3.25M10 13.5h.01"/>'),
62
+ error: svg('<circle cx="10" cy="10" r="7.5"/><path d="M10 6.5v4M10 13.5h.01"/>'),
63
+ copy: svg('<rect x="7" y="7" width="10" height="10" rx="2"/><path d="M13 7V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h2"/>'),
64
+ check: svg('<path d="m5 10.5 3.2 3L15 7"/>', 2),
65
+ calendar: svg('<rect x="3" y="4.5" width="14" height="12.5" rx="2"/><path d="M3 8.5h14M7 3v3M13 3v3"/>'),
66
+ prev: svg('<path d="m12 5-5 5 5 5"/>', 1.7, true),
67
+ next: svg('<path d="m8 5 5 5-5 5"/>', 1.7, true),
68
+ };
69
+ /** قالب مشترک برچسب + کنترل؛ ردیف راهنما و خطا را setMessages می‌سازد */
70
+ export function fieldHTML({ id, label, required, optional, control }) {
71
+ const labelRow = label
72
+ ? `<div class="k-field__label-row"><label class="k-label" for="${id}">${esc(label)}${required ? '<span class="k-label__required" aria-hidden="true">*</span>' : ""}${optional ? `<span class="k-label__optional">${esc(optional)}</span>` : ""}</label></div>`
73
+ : "";
74
+ return `<div class="k-field">${labelRow}${control}</div>`;
75
+ }
76
+ /**
77
+ * خطا و راهنمای زیر فیلد را به‌روز می‌کند و aria-invalid و aria-describedby کنترل را با آن‌ها همگام نگه می‌دارد.
78
+ * extra شناسه‌های دیگری است که کنترل را توصیف می‌کنند، مثل نام بانک.
79
+ */
80
+ export function setMessages(field, control, id, error, hint, extra) {
81
+ field.querySelector(":scope > .k-field__hint-row")?.remove();
82
+ if (error)
83
+ field.setAttribute("data-invalid", "");
84
+ else
85
+ field.removeAttribute("data-invalid");
86
+ if (error || hint) {
87
+ const row = document.createElement("div");
88
+ row.className = "k-field__hint-row";
89
+ row.innerHTML = `<div class="k-field__hints">${error ? `<p class="k-hint k-hint--error" id="${id}-error">${esc(error)}</p>` : ""}${hint ? `<p class="k-hint" id="${id}-hint">${esc(hint)}</p>` : ""}</div>`;
90
+ field.append(row);
91
+ }
92
+ const by = [extra, error ? `${id}-error` : null, hint ? `${id}-hint` : null].filter(Boolean).join(" ");
93
+ if (by)
94
+ control.setAttribute("aria-describedby", by);
95
+ else
96
+ control.removeAttribute("aria-describedby");
97
+ if (error)
98
+ control.setAttribute("aria-invalid", "true");
99
+ else
100
+ control.removeAttribute("aria-invalid");
101
+ const box = control.closest(".k-input, .k-select, .k-otp");
102
+ if (box?.classList.contains("k-input")) {
103
+ if (error)
104
+ box.setAttribute("data-state", "invalid");
105
+ else if (box.getAttribute("data-state") === "invalid")
106
+ box.removeAttribute("data-state");
107
+ }
108
+ }
109
+ /** فیلد پنهان برای ارسال مقدار خام در فرم HTML معمولی */
110
+ export function syncHidden(host, name, value) {
111
+ let hidden = host.querySelector(":scope > input[type=hidden][data-kashi-value]");
112
+ if (!name) {
113
+ hidden?.remove();
114
+ return;
115
+ }
116
+ if (!hidden) {
117
+ hidden = document.createElement("input");
118
+ hidden.type = "hidden";
119
+ hidden.dataset.kashiValue = "";
120
+ host.append(hidden);
121
+ }
122
+ hidden.name = name;
123
+ hidden.value = value;
124
+ }
@@ -0,0 +1,73 @@
1
+ import { HTMLBase } from "./base.js";
2
+ /**
3
+ * تقویم شمسی با الگوی grid در WAI-ARIA: پیمایش با فلش‌ها (در RTL فلش چپ یعنی روز بعد)،
4
+ * PageUp/PageDown برای ماه و با Shift برای سال، Home/End برای ابتدا و انتهای هفته.
5
+ */
6
+ export declare class KashiCalendar extends HTMLBase {
7
+ static observedAttributes: string[];
8
+ /** غیرفعال کردن روزهای خاص، مثلاً (d) => d.getDay() === 5 */
9
+ isDateDisabled?: (date: Date) => boolean;
10
+ /** نام تعطیلی هر روز؛ اگر ویژگی holidays گذاشته شود، تعطیلات ثابت ایران و جمعه‌ها */
11
+ holidayOf?: (date: Date) => string | null | false | undefined;
12
+ private selected;
13
+ private valueSet;
14
+ private focused;
15
+ private refocus;
16
+ private rendered;
17
+ private key;
18
+ connectedCallback(): void;
19
+ attributeChangedCallback(name: string, old: string | null, next: string | null): void;
20
+ /** تاریخ انتخاب‌شده به شکل YYYY-MM-DD میلادی */
21
+ get value(): string;
22
+ set value(v: string);
23
+ get date(): Date | null;
24
+ set date(d: Date | null);
25
+ /** فوکوس روی روز انتخاب‌شده یا امروز */
26
+ focus(): void;
27
+ private get min();
28
+ private get max();
29
+ private clamp;
30
+ private disabled;
31
+ private holiday;
32
+ private go;
33
+ private pick;
34
+ private onClick;
35
+ private onSelect;
36
+ private onKey;
37
+ private render;
38
+ }
39
+ /**
40
+ * انتخاب تاریخ شمسی: تایپ مستقیم (۱۴۰۳/۰۵/۱۲) یا انتخاب از تقویم.
41
+ * با name، تاریخ میلادی به شکل YYYY-MM-DD در فرم ارسال می‌شود.
42
+ */
43
+ export declare class KashiDatePicker extends HTMLBase {
44
+ static observedAttributes: string[];
45
+ isDateDisabled?: (date: Date) => boolean;
46
+ private selected;
47
+ private valueSet;
48
+ private invalid;
49
+ private rendered;
50
+ private key;
51
+ private input;
52
+ private toggle;
53
+ private pop;
54
+ connectedCallback(): void;
55
+ disconnectedCallback(): void;
56
+ attributeChangedCallback(name: string, old: string | null, next: string | null): void;
57
+ get value(): string;
58
+ set value(v: string);
59
+ get date(): Date | null;
60
+ set date(d: Date | null);
61
+ focus(options?: FocusOptions): void;
62
+ private get digits();
63
+ private fmt;
64
+ private render;
65
+ private paint;
66
+ private commitText;
67
+ private commit;
68
+ /** باز کردن تقویم */
69
+ open(): void;
70
+ /** بستن تقویم؛ فوکوس به دکمهٔ تقویم برمی‌گردد */
71
+ close(returnFocus?: boolean): void;
72
+ private onOutside;
73
+ }