@kashi-ui/react 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +24 -2
- package/dist/Alert.d.ts +18 -0
- package/dist/Alert.js +18 -0
- package/dist/Avatar.d.ts +29 -0
- package/dist/Avatar.js +32 -0
- package/dist/Badge.d.ts +13 -0
- package/dist/Badge.js +6 -0
- package/dist/Button.d.ts +22 -0
- package/dist/Button.js +10 -0
- package/dist/Card.d.ts +26 -0
- package/dist/Card.js +19 -0
- package/dist/Checkbox.d.ts +22 -0
- package/dist/Checkbox.js +26 -0
- package/dist/Field.d.ts +16 -0
- package/dist/Field.js +11 -0
- package/dist/Input.d.ts +25 -0
- package/dist/Input.js +14 -0
- package/dist/cx.d.ts +2 -0
- package/dist/cx.js +4 -0
- package/dist/icons.d.ts +5 -0
- package/dist/icons.js +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +9 -0
- package/dist/utils.d.ts +6 -0
- package/dist/utils.js +18 -0
- package/package.json +59 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kashi contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @kashi-ui/react
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
کامپوننتهای React [کاشی](https://github.com/Sepantapouya/Kashi)، دیزاینسیستم فارسی، دسترسپذیر و متنباز.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm i @kashi-ui/react @kashi-ui/css
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import "@kashi-ui/css";
|
|
11
|
+
import { Button, Input } from "@kashi-ui/react";
|
|
12
|
+
|
|
13
|
+
export function SignUp() {
|
|
14
|
+
return (
|
|
15
|
+
<form>
|
|
16
|
+
<Input label="ایمیل" type="email" required />
|
|
17
|
+
<Button variant="primary" type="submit">ثبتنام</Button>
|
|
18
|
+
</form>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
کامپوننتها: Button · Input · Textarea · Badge · Card · Alert · Avatar · AvatarGroup · Checkbox · Switch
|
|
24
|
+
|
|
25
|
+
نیازمند React ۱۸ یا بالاتر. مستندات کامل: [kashi.sepantapouya.com](https://kashi.sepantapouya.com) · کد: [GitHub](https://github.com/Sepantapouya/Kashi) · لایسنس MIT
|
package/dist/Alert.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type HTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
export type AlertTone = "neutral" | "brand" | "info" | "success" | "warning" | "error";
|
|
3
|
+
export interface AlertProps extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
|
|
4
|
+
/** @default "neutral" */
|
|
5
|
+
tone?: AlertTone;
|
|
6
|
+
title?: ReactNode;
|
|
7
|
+
/** آیکون سفارشی؛ false برای حذف */
|
|
8
|
+
icon?: ReactNode | false;
|
|
9
|
+
/** دکمهها یا لینکهای زیر متن */
|
|
10
|
+
actions?: ReactNode;
|
|
11
|
+
/** با دادن این تابع دکمهٔ بستن نمایش داده میشود */
|
|
12
|
+
onClose?: () => void;
|
|
13
|
+
/** @default "بستن" */
|
|
14
|
+
closeLabel?: string;
|
|
15
|
+
/** پسزمینهٔ رنگی کمرنگ */
|
|
16
|
+
tinted?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare const Alert: import("react").ForwardRefExoticComponent<AlertProps & import("react").RefAttributes<HTMLDivElement>>;
|
package/dist/Alert.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from "react";
|
|
3
|
+
import { cx } from "./cx.js";
|
|
4
|
+
import { CloseIcon, ErrorIcon, InfoIcon, SuccessIcon, WarningIcon } from "./icons.js";
|
|
5
|
+
const ICONS = {
|
|
6
|
+
neutral: InfoIcon,
|
|
7
|
+
brand: InfoIcon,
|
|
8
|
+
info: InfoIcon,
|
|
9
|
+
success: SuccessIcon,
|
|
10
|
+
warning: WarningIcon,
|
|
11
|
+
error: ErrorIcon,
|
|
12
|
+
};
|
|
13
|
+
export const Alert = forwardRef(function Alert({ tone = "neutral", title, icon, actions, onClose, closeLabel = "بستن", tinted = false, role, className, children, ...rest }, ref) {
|
|
14
|
+
const Icon = ICONS[tone];
|
|
15
|
+
return (_jsxs("div", { ref: ref,
|
|
16
|
+
// خطا و هشدار فوراً خوانده میشوند؛ بقیه مؤدبانه
|
|
17
|
+
role: role ?? (tone === "error" || tone === "warning" ? "alert" : "status"), className: cx("k-alert", tone !== "neutral" && `k-alert--${tone}`, tinted && "k-alert--tinted", className), ...rest, children: [icon !== false && _jsx("span", { className: "k-alert__icon", "aria-hidden": "true", children: icon ?? _jsx(Icon, {}) }), _jsxs("div", { className: "k-alert__content", children: [title && _jsx("p", { className: "k-alert__title", children: title }), children && _jsx("div", { className: "k-alert__description", children: children }), actions && _jsx("div", { className: "k-alert__actions", children: actions })] }), onClose && (_jsx("button", { type: "button", className: "k-btn k-btn--tertiary k-btn--icon k-btn--sm k-alert__close", "aria-label": closeLabel, onClick: onClose, children: _jsx(CloseIcon, {}) }))] }));
|
|
18
|
+
});
|
package/dist/Avatar.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type HTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
export type AvatarSize = "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
|
|
3
|
+
export type AvatarStatus = "online" | "offline" | "busy" | "away";
|
|
4
|
+
/** حروف اول دو کلمهٔ اول؛ با نیمفاصله تا حروف فارسی به هم نچسبند */
|
|
5
|
+
export declare function initials(name: string): string;
|
|
6
|
+
export interface AvatarProps extends HTMLAttributes<HTMLSpanElement> {
|
|
7
|
+
/** نام شخص — برای alt تصویر و حروف اول جایگزین */
|
|
8
|
+
name: string;
|
|
9
|
+
src?: string;
|
|
10
|
+
/** @default "md" */
|
|
11
|
+
size?: AvatarSize;
|
|
12
|
+
square?: boolean;
|
|
13
|
+
/** رنگ برند برای حالت حروف اول */
|
|
14
|
+
brand?: boolean;
|
|
15
|
+
status?: AvatarStatus;
|
|
16
|
+
/** برچسبهای وضعیت برای صفحهخوان (پیشفرض فارسی) */
|
|
17
|
+
statusLabels?: Partial<Record<AvatarStatus, string>>;
|
|
18
|
+
}
|
|
19
|
+
export declare const Avatar: import("react").ForwardRefExoticComponent<AvatarProps & import("react").RefAttributes<HTMLSpanElement>>;
|
|
20
|
+
export interface AvatarGroupProps extends HTMLAttributes<HTMLDivElement> {
|
|
21
|
+
/** حداکثر تعداد نمایش؛ بقیه به شکل «+۳» */
|
|
22
|
+
max?: number;
|
|
23
|
+
/** اندازهٔ آواتار «+n» @default "md" */
|
|
24
|
+
size?: AvatarSize;
|
|
25
|
+
/** @default (n) => `${n} نفر دیگر` */
|
|
26
|
+
moreLabel?: (count: number) => string;
|
|
27
|
+
children: ReactNode;
|
|
28
|
+
}
|
|
29
|
+
export declare function AvatarGroup({ max, size, moreLabel, className, children, ...rest }: AvatarGroupProps): import("react").JSX.Element;
|
package/dist/Avatar.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Children, forwardRef, useState } from "react";
|
|
3
|
+
import { cx } from "./cx.js";
|
|
4
|
+
import { toFaDigits } from "./utils.js";
|
|
5
|
+
const STATUS_LABEL = {
|
|
6
|
+
online: "آنلاین",
|
|
7
|
+
offline: "آفلاین",
|
|
8
|
+
busy: "مشغول",
|
|
9
|
+
away: "غایب",
|
|
10
|
+
};
|
|
11
|
+
/** حروف اول دو کلمهٔ اول؛ با نیمفاصله تا حروف فارسی به هم نچسبند */
|
|
12
|
+
export function initials(name) {
|
|
13
|
+
return name
|
|
14
|
+
.trim()
|
|
15
|
+
.split(/\s+/)
|
|
16
|
+
.slice(0, 2)
|
|
17
|
+
.map((w) => w[0])
|
|
18
|
+
.join("")
|
|
19
|
+
.toUpperCase();
|
|
20
|
+
}
|
|
21
|
+
export const Avatar = forwardRef(function Avatar({ name, src, size = "md", square = false, brand = false, status, statusLabels, className, ...rest }, ref) {
|
|
22
|
+
const [failed, setFailed] = useState(false);
|
|
23
|
+
const showImage = src && !failed;
|
|
24
|
+
const statusText = status ? (statusLabels?.[status] ?? STATUS_LABEL[status]) : undefined;
|
|
25
|
+
return (_jsxs("span", { ref: ref, role: "img", "aria-label": statusText ? `${name}، ${statusText}` : name, className: cx("k-avatar", size !== "md" && `k-avatar--${size}`, square && "k-avatar--square", brand && "k-avatar--brand", className), ...rest, children: [showImage ? (_jsx("img", { className: "k-avatar__image", src: src, alt: "", onError: () => setFailed(true) })) : (_jsx("span", { "aria-hidden": "true", children: initials(name) })), status && _jsx("span", { className: `k-avatar__status k-avatar__status--${status}`, "aria-hidden": "true" })] }));
|
|
26
|
+
});
|
|
27
|
+
export function AvatarGroup({ max, size = "md", moreLabel = (n) => `${toFaDigits(n)} نفر دیگر`, className, children, ...rest }) {
|
|
28
|
+
const items = Children.toArray(children);
|
|
29
|
+
const visible = max ? items.slice(0, max) : items;
|
|
30
|
+
const extra = items.length - visible.length;
|
|
31
|
+
return (_jsxs("div", { role: "group", className: cx("k-avatar-group", className), ...rest, children: [visible, extra > 0 && (_jsx("span", { role: "img", "aria-label": moreLabel(extra), className: cx("k-avatar", size !== "md" && `k-avatar--${size}`), children: _jsxs("span", { "aria-hidden": "true", children: ["+", toFaDigits(extra)] }) }))] }));
|
|
32
|
+
}
|
package/dist/Badge.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type HTMLAttributes } from "react";
|
|
2
|
+
export type BadgeTone = "neutral" | "brand" | "error" | "warning" | "success" | "info";
|
|
3
|
+
export interface BadgeProps extends HTMLAttributes<HTMLSpanElement> {
|
|
4
|
+
/** @default "neutral" */
|
|
5
|
+
tone?: BadgeTone;
|
|
6
|
+
/** @default "md" */
|
|
7
|
+
size?: "sm" | "md" | "lg";
|
|
8
|
+
/** نقطهٔ رنگی ابتدای متن */
|
|
9
|
+
dot?: boolean;
|
|
10
|
+
/** گوشهٔ کم بهجای گرد */
|
|
11
|
+
square?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare const Badge: import("react").ForwardRefExoticComponent<BadgeProps & import("react").RefAttributes<HTMLSpanElement>>;
|
package/dist/Badge.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from "react";
|
|
3
|
+
import { cx } from "./cx.js";
|
|
4
|
+
export const Badge = forwardRef(function Badge({ tone = "neutral", size = "md", dot = false, square = false, className, children, ...rest }, ref) {
|
|
5
|
+
return (_jsxs("span", { ref: ref, className: cx("k-badge", tone !== "neutral" && `k-badge--${tone}`, size !== "md" && `k-badge--${size}`, square && "k-badge--square", className), ...rest, children: [dot && _jsx("span", { className: "k-badge__dot", "aria-hidden": "true" }), children] }));
|
|
6
|
+
});
|
package/dist/Button.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type ButtonHTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
export type ButtonVariant = "primary" | "secondary" | "tertiary" | "link" | "danger" | "danger-secondary";
|
|
3
|
+
export type ButtonSize = "sm" | "md" | "lg" | "xl";
|
|
4
|
+
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
5
|
+
/** @default "secondary" */
|
|
6
|
+
variant?: ButtonVariant;
|
|
7
|
+
/** @default "md" */
|
|
8
|
+
size?: ButtonSize;
|
|
9
|
+
/** نمایش اسپینر؛ دکمه فوکوسپذیر میماند ولی کلیک نمیگیرد */
|
|
10
|
+
loading?: boolean;
|
|
11
|
+
/** متنی که صفحهخوان هنگام بارگذاری میخواند @default "در حال بارگذاری" */
|
|
12
|
+
loadingLabel?: string;
|
|
13
|
+
/** دکمهٔ تمامعرض */
|
|
14
|
+
block?: boolean;
|
|
15
|
+
/** دکمهٔ مربعی فقطآیکون — حتماً aria-label بدهید */
|
|
16
|
+
iconOnly?: boolean;
|
|
17
|
+
/** آیکون ابتدای متن (در RTL سمت راست) */
|
|
18
|
+
iconStart?: ReactNode;
|
|
19
|
+
/** آیکون انتهای متن (در RTL سمت چپ) */
|
|
20
|
+
iconEnd?: ReactNode;
|
|
21
|
+
}
|
|
22
|
+
export declare const Button: import("react").ForwardRefExoticComponent<ButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
|
package/dist/Button.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from "react";
|
|
3
|
+
import { cx } from "./cx.js";
|
|
4
|
+
import { isDev } from "./utils.js";
|
|
5
|
+
export const Button = forwardRef(function Button({ variant = "secondary", size = "md", loading = false, loadingLabel = "در حال بارگذاری", block = false, iconOnly = false, iconStart, iconEnd, disabled, type = "button", className, children, onClick, ...rest }, ref) {
|
|
6
|
+
if (isDev() && iconOnly && !rest["aria-label"] && !rest["aria-labelledby"]) {
|
|
7
|
+
console.warn("[kashi] <Button iconOnly> بدون aria-label برای صفحهخوانها بینام است.");
|
|
8
|
+
}
|
|
9
|
+
return (_jsxs("button", { ref: ref, type: type, disabled: disabled, "aria-disabled": loading || undefined, "aria-busy": loading || undefined, onClick: loading ? (e) => e.preventDefault() : onClick, className: cx("k-btn", `k-btn--${variant}`, size !== "md" && `k-btn--${size}`, block && "k-btn--block", iconOnly && "k-btn--icon", className), ...rest, children: [iconStart, children, iconEnd, loading && (_jsx("span", { className: "k-sr-only", role: "status", children: loadingLabel }))] }));
|
|
10
|
+
});
|
package/dist/Card.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type ElementType, type HTMLAttributes } from "react";
|
|
2
|
+
export interface CardProps extends HTMLAttributes<HTMLElement> {
|
|
3
|
+
/** @default "article" */
|
|
4
|
+
as?: ElementType;
|
|
5
|
+
/** بدون سایه */
|
|
6
|
+
flat?: boolean;
|
|
7
|
+
/** کارت قابلکلیک؛ یک لینک یا دکمه با کلاس k-card__link داخلش بگذارید */
|
|
8
|
+
interactive?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare const Card: import("react").ForwardRefExoticComponent<CardProps & import("react").RefAttributes<HTMLElement>>;
|
|
11
|
+
export declare const CardHeader: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLElement> & {
|
|
12
|
+
as?: ElementType;
|
|
13
|
+
} & import("react").RefAttributes<HTMLElement>>;
|
|
14
|
+
/** پیشفرض h3؛ با as سطح عنوان را با ساختار صفحه هماهنگ کنید */
|
|
15
|
+
export declare const CardTitle: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLElement> & {
|
|
16
|
+
as?: ElementType;
|
|
17
|
+
} & import("react").RefAttributes<HTMLElement>>;
|
|
18
|
+
export declare const CardDescription: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLElement> & {
|
|
19
|
+
as?: ElementType;
|
|
20
|
+
} & import("react").RefAttributes<HTMLElement>>;
|
|
21
|
+
export declare const CardBody: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLElement> & {
|
|
22
|
+
as?: ElementType;
|
|
23
|
+
} & import("react").RefAttributes<HTMLElement>>;
|
|
24
|
+
export declare const CardFooter: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLElement> & {
|
|
25
|
+
as?: ElementType;
|
|
26
|
+
} & import("react").RefAttributes<HTMLElement>>;
|
package/dist/Card.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from "react";
|
|
3
|
+
import { cx } from "./cx.js";
|
|
4
|
+
export const Card = forwardRef(function Card({ as: Tag = "article", flat = false, interactive = false, className, ...rest }, ref) {
|
|
5
|
+
return _jsx(Tag, { ref: ref, className: cx("k-card", flat && "k-card--flat", interactive && "k-card--interactive", className), ...rest });
|
|
6
|
+
});
|
|
7
|
+
const part = (tag, base, name) => {
|
|
8
|
+
const Part = forwardRef(function Part({ as: Tag = tag, className, ...rest }, ref) {
|
|
9
|
+
return _jsx(Tag, { ref: ref, className: cx(base, className), ...rest });
|
|
10
|
+
});
|
|
11
|
+
Part.displayName = name;
|
|
12
|
+
return Part;
|
|
13
|
+
};
|
|
14
|
+
export const CardHeader = part("header", "k-card__header", "CardHeader");
|
|
15
|
+
/** پیشفرض h3؛ با as سطح عنوان را با ساختار صفحه هماهنگ کنید */
|
|
16
|
+
export const CardTitle = part("h3", "k-card__title", "CardTitle");
|
|
17
|
+
export const CardDescription = part("p", "k-card__description", "CardDescription");
|
|
18
|
+
export const CardBody = part("div", "k-card__body", "CardBody");
|
|
19
|
+
export const CardFooter = part("footer", "k-card__footer", "CardFooter");
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type InputHTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
interface CheckOwnProps {
|
|
3
|
+
/** برچسب کنار کنترل */
|
|
4
|
+
label?: ReactNode;
|
|
5
|
+
/** توضیح زیر برچسب */
|
|
6
|
+
description?: ReactNode;
|
|
7
|
+
/** حالت خطا */
|
|
8
|
+
invalid?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface CheckboxProps extends CheckOwnProps, Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "size"> {
|
|
11
|
+
/** حالت نیمهانتخاب (مثلاً برای «انتخاب همه») */
|
|
12
|
+
indeterminate?: boolean;
|
|
13
|
+
/** @default "md" */
|
|
14
|
+
size?: "md" | "lg";
|
|
15
|
+
}
|
|
16
|
+
export declare const Checkbox: import("react").ForwardRefExoticComponent<CheckboxProps & import("react").RefAttributes<HTMLInputElement>>;
|
|
17
|
+
export interface SwitchProps extends CheckOwnProps, Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "size" | "role"> {
|
|
18
|
+
/** @default "md" */
|
|
19
|
+
size?: "sm" | "md" | "lg";
|
|
20
|
+
}
|
|
21
|
+
export declare const Switch: import("react").ForwardRefExoticComponent<SwitchProps & import("react").RefAttributes<HTMLInputElement>>;
|
|
22
|
+
export {};
|
package/dist/Checkbox.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useEffect, useId, useRef } from "react";
|
|
3
|
+
import { cx } from "./cx.js";
|
|
4
|
+
import { mergeRefs } from "./utils.js";
|
|
5
|
+
/** برچسب و توضیح مشترک بین Checkbox و Switch */
|
|
6
|
+
function CheckLabel({ id, label, description, className, children }) {
|
|
7
|
+
if (!label && !description)
|
|
8
|
+
return _jsx(_Fragment, { children: children });
|
|
9
|
+
return (_jsxs("label", { className: cx("k-check", className), htmlFor: id, children: [children, _jsxs("span", { className: "k-check__text", children: [label && _jsx("span", { className: "k-check__label", children: label }), description && _jsx("span", { className: "k-check__description", id: `${id}-desc`, children: description })] })] }));
|
|
10
|
+
}
|
|
11
|
+
export const Checkbox = forwardRef(function Checkbox({ label, description, invalid, indeterminate = false, size = "md", id, className, ...rest }, ref) {
|
|
12
|
+
const autoId = useId();
|
|
13
|
+
const inputId = id ?? autoId;
|
|
14
|
+
const inner = useRef(null);
|
|
15
|
+
// indeterminate فقط از طریق DOM قابلتنظیم است
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (inner.current)
|
|
18
|
+
inner.current.indeterminate = indeterminate;
|
|
19
|
+
}, [indeterminate]);
|
|
20
|
+
return (_jsx(CheckLabel, { id: inputId, label: label, description: description, className: className, children: _jsx("input", { ref: mergeRefs(ref, inner), type: "checkbox", id: inputId, className: cx("k-checkbox", size === "lg" && "k-checkbox--lg", !label && !description && className), "aria-invalid": invalid || undefined, "aria-describedby": description ? `${inputId}-desc` : undefined, "data-indeterminate": indeterminate || undefined, ...rest }) }));
|
|
21
|
+
});
|
|
22
|
+
export const Switch = forwardRef(function Switch({ label, description, invalid, size = "md", id, className, ...rest }, ref) {
|
|
23
|
+
const autoId = useId();
|
|
24
|
+
const inputId = id ?? autoId;
|
|
25
|
+
return (_jsx(CheckLabel, { id: inputId, label: label, description: description, className: className, children: _jsx("input", { ref: ref, type: "checkbox", role: "switch", id: inputId, className: cx("k-switch", size !== "md" && `k-switch--${size}`, !label && !description && className), "aria-invalid": invalid || undefined, "aria-describedby": description ? `${inputId}-desc` : undefined, ...rest }) }));
|
|
26
|
+
});
|
package/dist/Field.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
export interface FieldProps {
|
|
3
|
+
id: string;
|
|
4
|
+
label?: ReactNode;
|
|
5
|
+
hint?: ReactNode;
|
|
6
|
+
error?: ReactNode;
|
|
7
|
+
required?: boolean;
|
|
8
|
+
className?: string;
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
}
|
|
11
|
+
/** شناسهٔ متنهای راهنما و خطا برای aria-describedby */
|
|
12
|
+
export declare const hintId: (id: string) => string;
|
|
13
|
+
export declare const errorId: (id: string) => string;
|
|
14
|
+
export declare function describedBy(id: string, hint: unknown, error: unknown, extra?: string): string | undefined;
|
|
15
|
+
/** قالب مشترک برچسب + کنترل + راهنما/خطا */
|
|
16
|
+
export declare function Field({ id, label, hint, error, required, className, children }: FieldProps): import("react").JSX.Element;
|
package/dist/Field.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/** شناسهٔ متنهای راهنما و خطا برای aria-describedby */
|
|
3
|
+
export const hintId = (id) => `${id}-hint`;
|
|
4
|
+
export const errorId = (id) => `${id}-error`;
|
|
5
|
+
export function describedBy(id, hint, error, extra) {
|
|
6
|
+
return [extra, error ? errorId(id) : null, hint ? hintId(id) : null].filter(Boolean).join(" ") || undefined;
|
|
7
|
+
}
|
|
8
|
+
/** قالب مشترک برچسب + کنترل + راهنما/خطا */
|
|
9
|
+
export function Field({ id, label, hint, error, required, className, children }) {
|
|
10
|
+
return (_jsxs("div", { className: className ? `k-field ${className}` : "k-field", children: [label && (_jsxs("label", { className: "k-label", htmlFor: id, children: [label, required && _jsx("span", { className: "k-label__required", "aria-hidden": "true", children: "*" })] })), children, error && _jsx("p", { className: "k-hint k-hint--error", id: errorId(id), children: error }), hint && _jsx("p", { className: "k-hint", id: hintId(id), children: hint })] }));
|
|
11
|
+
}
|
package/dist/Input.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type InputHTMLAttributes, type ReactNode, type TextareaHTMLAttributes } from "react";
|
|
2
|
+
export type InputSize = "sm" | "md" | "lg";
|
|
3
|
+
interface FieldOwnProps {
|
|
4
|
+
/** برچسب بالای فیلد — اگر نمیدهید حتماً aria-label بدهید */
|
|
5
|
+
label?: ReactNode;
|
|
6
|
+
/** متن راهنمای زیر فیلد */
|
|
7
|
+
hint?: ReactNode;
|
|
8
|
+
/** پیام خطا؛ فیلد را aria-invalid میکند */
|
|
9
|
+
error?: ReactNode;
|
|
10
|
+
/** کلاس روی ظرف بیرونی (k-field) */
|
|
11
|
+
fieldClassName?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface InputProps extends FieldOwnProps, Omit<InputHTMLAttributes<HTMLInputElement>, "size"> {
|
|
14
|
+
/** @default "md" */
|
|
15
|
+
size?: InputSize;
|
|
16
|
+
/** آیکون یا متن ابتدای فیلد */
|
|
17
|
+
addonStart?: ReactNode;
|
|
18
|
+
/** آیکون یا متن انتهای فیلد */
|
|
19
|
+
addonEnd?: ReactNode;
|
|
20
|
+
}
|
|
21
|
+
export declare const Input: import("react").ForwardRefExoticComponent<InputProps & import("react").RefAttributes<HTMLInputElement>>;
|
|
22
|
+
export interface TextareaProps extends FieldOwnProps, TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
23
|
+
}
|
|
24
|
+
export declare const Textarea: import("react").ForwardRefExoticComponent<TextareaProps & import("react").RefAttributes<HTMLTextAreaElement>>;
|
|
25
|
+
export {};
|
package/dist/Input.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useId } from "react";
|
|
3
|
+
import { cx } from "./cx.js";
|
|
4
|
+
import { Field, describedBy } from "./Field.js";
|
|
5
|
+
export const Input = forwardRef(function Input({ label, hint, error, size = "md", addonStart, addonEnd, required, id, className, fieldClassName, "aria-describedby": ariaDescribedBy, ...rest }, ref) {
|
|
6
|
+
const autoId = useId();
|
|
7
|
+
const inputId = id ?? autoId;
|
|
8
|
+
return (_jsx(Field, { id: inputId, label: label, hint: hint, error: error, required: required, className: fieldClassName, children: _jsxs("div", { className: cx("k-input", size !== "md" && `k-input--${size}`, className), children: [addonStart && _jsx("span", { className: "k-input__addon", children: addonStart }), _jsx("input", { ref: ref, id: inputId, className: "k-input__control", required: required, "aria-invalid": error ? true : undefined, "aria-describedby": describedBy(inputId, hint, error, ariaDescribedBy), ...rest }), addonEnd && _jsx("span", { className: "k-input__addon", children: addonEnd })] }) }));
|
|
9
|
+
});
|
|
10
|
+
export const Textarea = forwardRef(function Textarea({ label, hint, error, required, id, className, fieldClassName, "aria-describedby": ariaDescribedBy, ...rest }, ref) {
|
|
11
|
+
const autoId = useId();
|
|
12
|
+
const inputId = id ?? autoId;
|
|
13
|
+
return (_jsx(Field, { id: inputId, label: label, hint: hint, error: error, required: required, className: fieldClassName, children: _jsx("div", { className: cx("k-input", "k-input--textarea", className), children: _jsx("textarea", { ref: ref, id: inputId, className: "k-input__control", required: required, "aria-invalid": error ? true : undefined, "aria-describedby": describedBy(inputId, hint, error, ariaDescribedBy), ...rest }) }) }));
|
|
14
|
+
});
|
package/dist/cx.d.ts
ADDED
package/dist/cx.js
ADDED
package/dist/icons.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const InfoIcon: () => import("react").JSX.Element;
|
|
2
|
+
export declare const SuccessIcon: () => import("react").JSX.Element;
|
|
3
|
+
export declare const WarningIcon: () => import("react").JSX.Element;
|
|
4
|
+
export declare const ErrorIcon: () => import("react").JSX.Element;
|
|
5
|
+
export declare const CloseIcon: () => import("react").JSX.Element;
|
package/dist/icons.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// آیکونهای داخلی کوچک — همه aria-hidden هستند
|
|
3
|
+
const base = {
|
|
4
|
+
viewBox: "0 0 20 20",
|
|
5
|
+
fill: "none",
|
|
6
|
+
stroke: "currentColor",
|
|
7
|
+
strokeWidth: 1.7,
|
|
8
|
+
strokeLinecap: "round",
|
|
9
|
+
strokeLinejoin: "round",
|
|
10
|
+
"aria-hidden": true,
|
|
11
|
+
};
|
|
12
|
+
export const InfoIcon = () => (_jsxs("svg", { ...base, children: [_jsx("circle", { cx: "10", cy: "10", r: "7.5" }), _jsx("path", { d: "M10 13.5V9.5M10 6.5h.01" })] }));
|
|
13
|
+
export const SuccessIcon = () => (_jsxs("svg", { ...base, children: [_jsx("circle", { cx: "10", cy: "10", r: "7.5" }), _jsx("path", { d: "m6.75 10 2.25 2.25 4.25-4.5" })] }));
|
|
14
|
+
export const WarningIcon = () => (_jsxs("svg", { ...base, children: [_jsx("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" }), _jsx("path", { d: "M10 7.5v3.25M10 13.5h.01" })] }));
|
|
15
|
+
export const ErrorIcon = () => (_jsxs("svg", { ...base, children: [_jsx("circle", { cx: "10", cy: "10", r: "7.5" }), _jsx("path", { d: "M10 6.5v4M10 13.5h.01" })] }));
|
|
16
|
+
export const CloseIcon = () => (_jsx("svg", { ...base, children: _jsx("path", { d: "m5 5 10 10M15 5 5 15" }) }));
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { Button } from "./Button.js";
|
|
2
|
+
export type { ButtonProps, ButtonVariant, ButtonSize } from "./Button.js";
|
|
3
|
+
export { Input, Textarea } from "./Input.js";
|
|
4
|
+
export type { InputProps, InputSize, TextareaProps } from "./Input.js";
|
|
5
|
+
export { Badge } from "./Badge.js";
|
|
6
|
+
export type { BadgeProps, BadgeTone } from "./Badge.js";
|
|
7
|
+
export { Card, CardHeader, CardTitle, CardDescription, CardBody, CardFooter } from "./Card.js";
|
|
8
|
+
export type { CardProps } from "./Card.js";
|
|
9
|
+
export { Alert } from "./Alert.js";
|
|
10
|
+
export type { AlertProps, AlertTone } from "./Alert.js";
|
|
11
|
+
export { Avatar, AvatarGroup, initials } from "./Avatar.js";
|
|
12
|
+
export type { AvatarProps, AvatarGroupProps, AvatarSize, AvatarStatus } from "./Avatar.js";
|
|
13
|
+
export { Checkbox, Switch } from "./Checkbox.js";
|
|
14
|
+
export type { CheckboxProps, SwitchProps } from "./Checkbox.js";
|
|
15
|
+
export { cx } from "./cx.js";
|
|
16
|
+
export { toFaDigits } from "./utils.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { Button } from "./Button.js";
|
|
2
|
+
export { Input, Textarea } from "./Input.js";
|
|
3
|
+
export { Badge } from "./Badge.js";
|
|
4
|
+
export { Card, CardHeader, CardTitle, CardDescription, CardBody, CardFooter } from "./Card.js";
|
|
5
|
+
export { Alert } from "./Alert.js";
|
|
6
|
+
export { Avatar, AvatarGroup, initials } from "./Avatar.js";
|
|
7
|
+
export { Checkbox, Switch } from "./Checkbox.js";
|
|
8
|
+
export { cx } from "./cx.js";
|
|
9
|
+
export { toFaDigits } from "./utils.js";
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Ref, RefCallback } from "react";
|
|
2
|
+
/** ارقام لاتین را به فارسی تبدیل میکند: 12 → ۱۲ */
|
|
3
|
+
export declare function toFaDigits(value: string | number): string;
|
|
4
|
+
/** چند ref را یکی میکند */
|
|
5
|
+
export declare function mergeRefs<T>(...refs: Array<Ref<T> | undefined>): RefCallback<T>;
|
|
6
|
+
export declare function isDev(): boolean;
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** ارقام لاتین را به فارسی تبدیل میکند: 12 → ۱۲ */
|
|
2
|
+
export function toFaDigits(value) {
|
|
3
|
+
return String(value).replace(/\d/g, (d) => "۰۱۲۳۴۵۶۷۸۹"[Number(d)]);
|
|
4
|
+
}
|
|
5
|
+
/** چند ref را یکی میکند */
|
|
6
|
+
export function mergeRefs(...refs) {
|
|
7
|
+
return (node) => {
|
|
8
|
+
for (const ref of refs) {
|
|
9
|
+
if (typeof ref === "function")
|
|
10
|
+
ref(node);
|
|
11
|
+
else if (ref)
|
|
12
|
+
ref.current = node;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export function isDev() {
|
|
17
|
+
return typeof process !== "undefined" && process.env.NODE_ENV !== "production";
|
|
18
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,61 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kashi-ui/react",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "کامپوننتهای React دیزاینسیستم کاشی",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./package.json": "./package.json"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"sideEffects": false,
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc -p tsconfig.json",
|
|
22
|
+
"test": "node --test test/*.test.mjs"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"@kashi-ui/css": "^0.2.0",
|
|
26
|
+
"react": ">=18"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/react": "^18.3.0",
|
|
30
|
+
"react": "^18.3.1",
|
|
31
|
+
"react-dom": "^18.3.1",
|
|
32
|
+
"typescript": "^5.6.0"
|
|
33
|
+
},
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/Sepantapouya/Kashi.git",
|
|
38
|
+
"directory": "packages/react"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://kashi.sepantapouya.com",
|
|
41
|
+
"bugs": "https://github.com/Sepantapouya/Kashi/issues",
|
|
42
|
+
"author": "Sepanta Pouya (https://sepantapouya.com)",
|
|
43
|
+
"keywords": [
|
|
44
|
+
"design-system",
|
|
45
|
+
"persian",
|
|
46
|
+
"farsi",
|
|
47
|
+
"rtl",
|
|
48
|
+
"accessibility",
|
|
49
|
+
"ui",
|
|
50
|
+
"css",
|
|
51
|
+
"react"
|
|
52
|
+
],
|
|
53
|
+
"publishConfig": {
|
|
54
|
+
"access": "public"
|
|
55
|
+
},
|
|
56
|
+
"peerDependenciesMeta": {
|
|
57
|
+
"@kashi-ui/css": {
|
|
58
|
+
"optional": false
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|