8.4 מנטין קומפוננטות הרצאה
מנטין קומפוננטות - Mantine Components¶
בשיעור זה נכיר את מגוון הקומפוננטות שמנטין מציעה: קלטים, הצגת נתונים, שכבות-על, ניווט ומשוב. נלמד את ה-API של כל קומפוננטה ונראה דוגמאות מעשיות.
קומפוננטות קלט - Inputs¶
TextInput - שדה טקסט¶
import { TextInput } from "@mantine/core";
function TextInputExamples() {
return (
<>
<TextInput label="שם מלא" placeholder="הכנס שם" />
<TextInput
label="אימייל"
placeholder="your@email.com"
description="לא נשתף את האימייל שלך"
required
/>
<TextInput
label="שם משתמש"
error="שם המשתמש כבר תפוס"
/>
<TextInput
label="חיפוש"
leftSection="Q"
rightSection="X"
/>
<TextInput
label="כתובת"
disabled
value="רח' הרצל 1"
/>
{/* גדלים */}
<TextInput size="xs" label="קטן מאוד" />
<TextInput size="sm" label="קטן" />
<TextInput size="md" label="רגיל" />
<TextInput size="lg" label="גדול" />
{/* variants */}
<TextInput variant="default" label="ברירת מחדל" />
<TextInput variant="filled" label="מלא" />
<TextInput variant="unstyled" label="ללא עיצוב" />
</>
);
}
NumberInput - שדה מספרי¶
import { NumberInput } from "@mantine/core";
function NumberInputExamples() {
return (
<>
<NumberInput label="כמות" placeholder="הכנס מספר" />
<NumberInput
label="מחיר"
prefix="$"
suffix=" USD"
thousandSeparator=","
decimalScale={2}
min={0}
max={10000}
/>
<NumberInput
label="גיל"
min={0}
max={120}
step={1}
clampBehavior="strict"
/>
{/* עם כפתורי +/- */}
<NumberInput
label="כמות"
defaultValue={1}
min={1}
max={99}
stepHoldDelay={500}
stepHoldInterval={100}
/>
{/* ללא כפתורי +/- */}
<NumberInput
label="טלפון"
hideControls
placeholder="050-1234567"
/>
</>
);
}
Select ו-MultiSelect¶
import { Select, MultiSelect } from "@mantine/core";
function SelectExamples() {
return (
<>
{/* Select בסיסי */}
<Select
label="עיר"
placeholder="בחר עיר"
data={["תל אביב", "ירושלים", "חיפה", "באר שבע"]}
/>
{/* עם ערך ותווית */}
<Select
label="מחלקה"
data={[
{ value: "dev", label: "פיתוח" },
{ value: "design", label: "עיצוב" },
{ value: "marketing", label: "שיווק" },
]}
/>
{/* עם חיפוש */}
<Select
label="מדינה"
placeholder="חפש מדינה..."
searchable
data={["ישראל", "ארצות הברית", "בריטניה", "גרמניה", "צרפת"]}
nothingFoundMessage="לא נמצא"
/>
{/* עם קבוצות */}
<Select
label="שפה"
data={[
{ group: "פרונטאנד", items: ["JavaScript", "TypeScript", "HTML"] },
{ group: "באקאנד", items: ["Python", "Java", "Go"] },
]}
/>
{/* ניתן לנקות */}
<Select label="סטטוס" clearable data={["פעיל", "לא פעיל", "מושהה"]} />
{/* בחירה מרובה */}
<MultiSelect
label="תגיות"
placeholder="בחר תגיות"
data={["ריאקט", "TypeScript", "Mantine", "Tailwind", "CSS"]}
searchable
clearable
/>
</>
);
}
קומפוננטות בחירה נוספות¶
import { Checkbox, Radio, Switch, Textarea } from "@mantine/core";
function MoreInputs() {
return (
<>
{/* Checkbox */}
<Checkbox label="אני מסכים לתנאי השימוש" />
<Checkbox label="מסומן כברירת מחדל" defaultChecked />
<Checkbox label="מושבת" disabled />
<Checkbox
label="עם תיאור"
description="תיאור נוסף מתחת ל-Checkbox"
/>
{/* קבוצת Checkbox */}
<Checkbox.Group
label="בחר שפות"
description="סמן את השפות שאתה יודע"
defaultValue={["react"]}
>
<Checkbox value="react" label="ריאקט" />
<Checkbox value="vue" label="Vue" />
<Checkbox value="angular" label="Angular" />
<Checkbox value="svelte" label="Svelte" />
</Checkbox.Group>
{/* Radio */}
<Radio.Group
name="plan"
label="בחר תוכנית"
defaultValue="pro"
>
<Radio value="free" label="חינם" />
<Radio value="pro" label="פרו - 49 ש\"ח לחודש" />
<Radio value="enterprise" label="ארגוני - צור קשר" />
</Radio.Group>
{/* Switch */}
<Switch label="מצב כהה" />
<Switch label="התראות" defaultChecked />
<Switch label="פעיל" size="lg" onLabel="כן" offLabel="לא" />
{/* Textarea */}
<Textarea
label="הודעה"
placeholder="כתוב את ההודעה שלך..."
minRows={3}
maxRows={6}
autosize
/>
</>
);
}
הצגת נתונים - Data Display¶
Table - טבלה¶
import { Table } from "@mantine/core";
const employees = [
{ name: "יוסי כהן", role: "מפתח", department: "פיתוח", status: "פעיל" },
{ name: "דנה לוי", role: "מעצבת", department: "עיצוב", status: "פעיל" },
{ name: "אבי מזרחי", role: "QA", department: "בדיקות", status: "חופשה" },
];
function TableExample() {
return (
<Table striped highlightOnHover withTableBorder withColumnBorders>
<Table.Thead>
<Table.Tr>
<Table.Th>שם</Table.Th>
<Table.Th>תפקיד</Table.Th>
<Table.Th>מחלקה</Table.Th>
<Table.Th>סטטוס</Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>
{employees.map((emp) => (
<Table.Tr key={emp.name}>
<Table.Td>{emp.name}</Table.Td>
<Table.Td>{emp.role}</Table.Td>
<Table.Td>{emp.department}</Table.Td>
<Table.Td>{emp.status}</Table.Td>
</Table.Tr>
))}
</Table.Tbody>
</Table>
);
}
- striped - שורות מפוספסות
- highlightOnHover - הדגשה בעכבר
- withTableBorder - גבול חיצוני
- withColumnBorders - גבולות בין עמודות
Badge - תגית¶
import { Badge, Group } from "@mantine/core";
function BadgeExamples() {
return (
<Group>
<Badge>ברירת מחדל</Badge>
<Badge color="red">דחוף</Badge>
<Badge color="green" variant="light">פעיל</Badge>
<Badge color="orange" variant="outline">ממתין</Badge>
<Badge color="gray" variant="dot">טיוטה</Badge>
<Badge size="lg" radius="sm">גדול</Badge>
<Badge leftSection="!">חדש</Badge>
</Group>
);
}
Card - כרטיסייה¶
import { Card, Image, Text, Badge, Button, Group } from "@mantine/core";
function ProductCard() {
return (
<Card shadow="sm" padding="lg" radius="md" withBorder maw={340}>
<Card.Section>
<Image
src="https://placehold.co/340x200"
height={200}
alt="מוצר"
/>
</Card.Section>
<Group justify="space-between" mt="md" mb="xs">
<Text fw={500}>אוזניות אלחוטיות</Text>
<Badge color="pink" variant="light">
מבצע
</Badge>
</Group>
<Text size="sm" c="dimmed">
אוזניות בלוטות' איכותיות עם ביטול רעשים אקטיבי וסוללה של 30 שעות
</Text>
<Button variant="light" color="blue" fullWidth mt="md" radius="md">
הוסף לעגלה
</Button>
</Card>
);
}
- Card.Section מתפרס על כל רוחב הכרטיסייה, כולל מעבר ל-padding
Avatar - תמונת פרופיל¶
import { Avatar, Group } from "@mantine/core";
function AvatarExamples() {
return (
<Group>
{/* עם תמונה */}
<Avatar src="https://placehold.co/40" alt="משתמש" />
{/* עם ראשי תיבות */}
<Avatar color="blue" radius="xl">
יכ
</Avatar>
{/* גדלים */}
<Avatar size="sm" color="red">S</Avatar>
<Avatar size="md" color="green">M</Avatar>
<Avatar size="lg" color="orange">L</Avatar>
<Avatar size="xl" color="grape">XL</Avatar>
{/* קבוצת אווטארים */}
<Avatar.Group>
<Avatar color="blue">א</Avatar>
<Avatar color="red">ב</Avatar>
<Avatar color="green">ג</Avatar>
<Avatar>+5</Avatar>
</Avatar.Group>
</Group>
);
}
שכבות-על - Overlays¶
Modal - חלון מודאלי¶
import { Modal, Button, TextInput, Group, Stack } from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
function ModalExample() {
const [opened, { open, close }] = useDisclosure(false);
return (
<>
<Modal opened={opened} onClose={close} title="הוספת משתמש" centered>
<Stack>
<TextInput label="שם" placeholder="הכנס שם" />
<TextInput label="אימייל" placeholder="הכנס אימייל" />
<Group justify="flex-end" mt="md">
<Button variant="default" onClick={close}>
ביטול
</Button>
<Button onClick={close}>שמור</Button>
</Group>
</Stack>
</Modal>
<Button onClick={open}>פתח מודאל</Button>
</>
);
}
- useDisclosure מנהל את מצב פתוח/סגור
- centered ממרכז את המודאל במסך
- onClose נקרא גם בלחיצה על הרקע וגם על X
Drawer - מגירה¶
import { Drawer, Button, NavLink, Stack } from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
function DrawerExample() {
const [opened, { open, close }] = useDisclosure(false);
return (
<>
<Drawer
opened={opened}
onClose={close}
title="תפריט ניווט"
position="right"
size="sm"
>
<Stack>
<NavLink label="בית" onClick={close} />
<NavLink label="מוצרים" onClick={close} />
<NavLink label="אודות" onClick={close} />
<NavLink label="צור קשר" onClick={close} />
</Stack>
</Drawer>
<Button onClick={open}>פתח תפריט</Button>
</>
);
}
- position - right, left, top, bottom
- size - xs, sm, md, lg, xl או ערך מספרי
Tooltip - הסבר¶
import { Tooltip, Button, Group } from "@mantine/core";
function TooltipExamples() {
return (
<Group>
<Tooltip label="שמור את השינויים">
<Button>שמור</Button>
</Tooltip>
<Tooltip label="אפשרות זו מושבתת" position="bottom">
<Button disabled>מושבת</Button>
</Tooltip>
<Tooltip
label="מידע נוסף על הפעולה"
multiline
w={200}
withArrow
>
<Button variant="light">מידע</Button>
</Tooltip>
{/* Tooltip עם עיכוב */}
<Tooltip label="מופיע אחרי 500ms" openDelay={500}>
<Button variant="outline">עם עיכוב</Button>
</Tooltip>
</Group>
);
}
Popover - חלון צף¶
import { Popover, Button, Text, Stack, TextInput } from "@mantine/core";
function PopoverExample() {
return (
<Popover width={300} position="bottom" withArrow shadow="md">
<Popover.Target>
<Button>פתח חלון צף</Button>
</Popover.Target>
<Popover.Dropdown>
<Stack gap="sm">
<Text size="sm" fw={500}>
סינון תוצאות
</Text>
<TextInput placeholder="חפש..." size="xs" />
<Button size="xs" fullWidth>
החל
</Button>
</Stack>
</Popover.Dropdown>
</Popover>
);
}
Menu - תפריט¶
import { Menu, Button } from "@mantine/core";
function MenuExample() {
return (
<Menu shadow="md" width={200}>
<Menu.Target>
<Button>תפריט</Button>
</Menu.Target>
<Menu.Dropdown>
<Menu.Label>פעולות</Menu.Label>
<Menu.Item>עריכה</Menu.Item>
<Menu.Item>שכפול</Menu.Item>
<Menu.Item>העברה</Menu.Item>
<Menu.Divider />
<Menu.Label>מתקדם</Menu.Label>
<Menu.Item>ייצוא</Menu.Item>
<Menu.Item color="red">מחיקה</Menu.Item>
</Menu.Dropdown>
</Menu>
);
}
ניווט - Navigation¶
Tabs - כרטיסיות ניווט¶
import { Tabs, Text } from "@mantine/core";
function TabsExample() {
return (
<Tabs defaultValue="general">
<Tabs.List>
<Tabs.Tab value="general">כללי</Tabs.Tab>
<Tabs.Tab value="security">אבטחה</Tabs.Tab>
<Tabs.Tab value="notifications">התראות</Tabs.Tab>
</Tabs.List>
<Tabs.Panel value="general" pt="md">
<Text>הגדרות כלליות</Text>
</Tabs.Panel>
<Tabs.Panel value="security" pt="md">
<Text>הגדרות אבטחה</Text>
</Tabs.Panel>
<Tabs.Panel value="notifications" pt="md">
<Text>הגדרות התראות</Text>
</Tabs.Panel>
</Tabs>
);
}
- defaultValue מגדיר את הכרטיסייה הפעילה בהתחלה
- ניתן להוסיף variant="outline" או variant="pills"
Breadcrumbs - פירורי לחם¶
import { Breadcrumbs, Anchor } from "@mantine/core";
function BreadcrumbsExample() {
const items = [
{ title: "בית", href: "/" },
{ title: "מוצרים", href: "/products" },
{ title: "אלקטרוניקה", href: "/products/electronics" },
{ title: "אוזניות", href: "#" },
];
return (
<Breadcrumbs>
{items.map((item) => (
<Anchor href={item.href} key={item.title}>
{item.title}
</Anchor>
))}
</Breadcrumbs>
);
}
Pagination - עימוד¶
import { Pagination } from "@mantine/core";
import { useState } from "react";
function PaginationExample() {
const [activePage, setPage] = useState(1);
return (
<Pagination
value={activePage}
onChange={setPage}
total={10}
siblings={1}
boundaries={1}
/>
);
}
- total - מספר העמודים
- siblings - כמה עמודים להציג מכל צד של הנוכחי
- boundaries - כמה עמודים להציג בקצוות
NavLink - קישור ניווט¶
import { NavLink, Stack } from "@mantine/core";
import { useState } from "react";
function NavLinkExample() {
const [active, setActive] = useState(0);
const items = [
{ label: "דשבורד", description: "סקירה כללית" },
{ label: "משתמשים", description: "ניהול משתמשים" },
{ label: "הגדרות", description: "הגדרות מערכת" },
];
return (
<Stack gap={0} w={250}>
{items.map((item, index) => (
<NavLink
key={item.label}
active={index === active}
label={item.label}
description={item.description}
onClick={() => setActive(index)}
/>
))}
</Stack>
);
}
משוב - Feedback¶
Notification - התראה¶
import { Notification, Stack } from "@mantine/core";
function NotificationExamples() {
return (
<Stack maw={400}>
<Notification title="פעולה בוצעה" color="green" withCloseButton>
ההזמנה נשמרה בהצלחה
</Notification>
<Notification title="שגיאה" color="red">
לא ניתן לשמור את הנתונים
</Notification>
<Notification title="אזהרה" color="yellow">
החשבון שלך עומד לפוג
</Notification>
<Notification title="טוען..." loading>
מעבד את הבקשה שלך
</Notification>
</Stack>
);
}
Alert - התראה בולטת¶
import { Alert, Stack } from "@mantine/core";
function AlertExamples() {
return (
<Stack>
<Alert variant="light" color="blue" title="מידע">
שחרור גרסה חדשה מתוכנן לשבוע הבא
</Alert>
<Alert variant="light" color="green" title="הצלחה">
הפרופיל עודכן בהצלחה
</Alert>
<Alert variant="light" color="red" title="שגיאה">
אירעה שגיאה בעת שמירת הנתונים
</Alert>
<Alert variant="filled" color="yellow" title="אזהרה">
שים לב - פעולה זו היא בלתי הפיכה
</Alert>
</Stack>
);
}
Progress - סרגל התקדמות¶
import { Progress, Stack, Text } from "@mantine/core";
function ProgressExamples() {
return (
<Stack>
<Progress value={45} />
<Progress value={75} color="green" size="lg" radius="xl" />
{/* עם תווית */}
<div>
<Text size="sm" mb={4}>העלאת קובץ - 67%</Text>
<Progress value={67} animated />
</div>
{/* מרובה חלקים */}
<Progress.Root size="xl">
<Progress.Section value={35} color="cyan">
<Progress.Label>קבצים</Progress.Label>
</Progress.Section>
<Progress.Section value={25} color="pink">
<Progress.Label>תמונות</Progress.Label>
</Progress.Section>
<Progress.Section value={15} color="orange">
<Progress.Label>אחר</Progress.Label>
</Progress.Section>
</Progress.Root>
</Stack>
);
}
Skeleton - שלד טעינה¶
import { Skeleton, Stack, Group } from "@mantine/core";
function SkeletonExample() {
return (
<Stack>
{/* שורות טקסט */}
<Skeleton height={8} radius="xl" />
<Skeleton height={8} radius="xl" width="70%" />
<Skeleton height={8} radius="xl" width="50%" />
{/* כרטיס טעינה */}
<Group>
<Skeleton height={50} circle />
<div style={{ flex: 1 }}>
<Skeleton height={8} radius="xl" mb={8} />
<Skeleton height={8} radius="xl" width="60%" />
</div>
</Group>
{/* תמונה */}
<Skeleton height={200} radius="md" />
</Stack>
);
}
LoadingOverlay - שכבת טעינה¶
import { LoadingOverlay, Box, Text, Button } from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
function LoadingOverlayExample() {
const [visible, { toggle }] = useDisclosure(false);
return (
<>
<Button onClick={toggle} mb="md">
{visible ? "הסתר טעינה" : "הצג טעינה"}
</Button>
<Box pos="relative" mih={200}>
<LoadingOverlay
visible={visible}
zIndex={1000}
overlayProps={{ radius: "sm", blur: 2 }}
/>
<Text>תוכן שמוסתר בזמן טעינה</Text>
<Text>עוד תוכן...</Text>
</Box>
</>
);
}
- pos="relative" על ההורה חשוב - ה-LoadingOverlay ממוקם absolute
- blur מטשטש את התוכן שמאחור
דוגמה מלאה - דף ניהול משתמשים¶
import {
Container,
Title,
Table,
Badge,
Group,
Button,
Modal,
TextInput,
Select,
Stack,
Avatar,
Menu,
Tabs,
Pagination,
Text,
} from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
import { useState } from "react";
interface User {
id: number;
name: string;
email: string;
role: string;
status: "active" | "inactive" | "suspended";
}
const usersData: User[] = [
{ id: 1, name: "יוסי כהן", email: "yossi@example.com", role: "מנהל", status: "active" },
{ id: 2, name: "דנה לוי", email: "dana@example.com", role: "מפתח", status: "active" },
{ id: 3, name: "אבי מזרחי", email: "avi@example.com", role: "מעצב", status: "inactive" },
{ id: 4, name: "שירה אברהם", email: "shira@example.com", role: "QA", status: "suspended" },
];
const statusColors: Record<string, string> = {
active: "green",
inactive: "gray",
suspended: "red",
};
const statusLabels: Record<string, string> = {
active: "פעיל",
inactive: "לא פעיל",
suspended: "מושהה",
};
function UsersPage() {
const [opened, { open, close }] = useDisclosure(false);
const [activePage, setPage] = useState(1);
return (
<Container size="lg" py="xl">
<Group justify="space-between" mb="lg">
<Title order={2}>ניהול משתמשים</Title>
<Button onClick={open}>הוסף משתמש</Button>
</Group>
<Tabs defaultValue="all" mb="lg">
<Tabs.List>
<Tabs.Tab value="all">הכל (4)</Tabs.Tab>
<Tabs.Tab value="active">פעילים (2)</Tabs.Tab>
<Tabs.Tab value="inactive">לא פעילים (1)</Tabs.Tab>
<Tabs.Tab value="suspended">מושהים (1)</Tabs.Tab>
</Tabs.List>
</Tabs>
<Table striped highlightOnHover withTableBorder>
<Table.Thead>
<Table.Tr>
<Table.Th>משתמש</Table.Th>
<Table.Th>אימייל</Table.Th>
<Table.Th>תפקיד</Table.Th>
<Table.Th>סטטוס</Table.Th>
<Table.Th>פעולות</Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>
{usersData.map((user) => (
<Table.Tr key={user.id}>
<Table.Td>
<Group gap="sm">
<Avatar size="sm" color="blue" radius="xl">
{user.name.charAt(0)}
</Avatar>
<Text size="sm" fw={500}>
{user.name}
</Text>
</Group>
</Table.Td>
<Table.Td>
<Text size="sm">{user.email}</Text>
</Table.Td>
<Table.Td>
<Text size="sm">{user.role}</Text>
</Table.Td>
<Table.Td>
<Badge color={statusColors[user.status]} variant="light">
{statusLabels[user.status]}
</Badge>
</Table.Td>
<Table.Td>
<Menu shadow="md" width={150}>
<Menu.Target>
<Button variant="subtle" size="xs">
...
</Button>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item>עריכה</Menu.Item>
<Menu.Item>צפייה</Menu.Item>
<Menu.Divider />
<Menu.Item color="red">מחיקה</Menu.Item>
</Menu.Dropdown>
</Menu>
</Table.Td>
</Table.Tr>
))}
</Table.Tbody>
</Table>
<Group justify="center" mt="lg">
<Pagination value={activePage} onChange={setPage} total={5} />
</Group>
{/* מודאל הוספת משתמש */}
<Modal opened={opened} onClose={close} title="הוספת משתמש חדש" centered>
<Stack>
<TextInput label="שם מלא" placeholder="הכנס שם" required />
<TextInput label="אימייל" placeholder="הכנס אימייל" required />
<Select
label="תפקיד"
placeholder="בחר תפקיד"
data={["מנהל", "מפתח", "מעצב", "QA"]}
required
/>
<Group justify="flex-end" mt="md">
<Button variant="default" onClick={close}>
ביטול
</Button>
<Button onClick={close}>הוסף</Button>
</Group>
</Stack>
</Modal>
</Container>
);
}
סיכום¶
- Mantine מציעה מגוון רחב של קומפוננטות קלט: TextInput, NumberInput, Select, Checkbox, Radio, Switch, Textarea
- קומפוננטות הצגת נתונים כוללות Table, Badge, Card, Avatar, Image
- שכבות-על: Modal, Drawer, Tooltip, Popover, Menu - כולן עובדות עם useDisclosure
- קומפוננטות ניווט: Tabs, Breadcrumbs, Pagination, NavLink
- קומפוננטות משוב: Notification, Alert, Progress, Skeleton, LoadingOverlay
- כל הקומפוננטות תומכות ב-variants, colors, sizes ו-system props
- שילוב הקומפוננטות יחד יוצר ממשקים מקצועיים ועקביים