@xenide-io/the-old-ui-theme 0.2.9 → 0.3.1
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 +1 -1
- package/dist/chunk-5HSOBJ4F.mjs +5968 -0
- package/dist/index-BgG8Homu.d.mts +1461 -0
- package/dist/index-BgG8Homu.d.ts +1461 -0
- package/dist/index.d.mts +206 -100
- package/dist/index.d.ts +206 -100
- package/dist/index.js +4275 -1455
- package/dist/index.mjs +689 -735
- package/dist/tailwind-preset.js +7 -0
- package/dist/tailwind-preset.mjs +7 -0
- package/dist/ui.d.mts +3 -2
- package/dist/ui.d.ts +3 -2
- package/dist/ui.js +3449 -701
- package/dist/ui.mjs +106 -3
- package/package.json +20 -16
- package/src/app/globals.css +1953 -266
- package/src/components/charts/index.ts +9 -8
- package/src/components/charts/quill/BarChart.tsx +85 -0
- package/src/components/charts/quill/FunnelChart.tsx +49 -0
- package/src/components/charts/quill/InsightShell.tsx +27 -0
- package/src/components/charts/quill/MetricCard.tsx +44 -0
- package/src/components/charts/quill/PieChart.tsx +81 -0
- package/src/components/charts/quill/Sparkline.tsx +57 -0
- package/src/components/charts/quill/TimeSeriesLineChart.tsx +62 -0
- package/src/components/charts/quill/index.ts +9 -0
- package/src/components/icons/IconBase.tsx +20 -12
- package/src/components/icons/createIconBase.tsx +12 -7
- package/src/components/icons/icons.tsx +210 -18
- package/src/components/icons/index.ts +3 -0
- package/src/components/icons/lemon-brand-icons.tsx +16 -0
- package/src/components/icons/lemon-category-icons.tsx +72 -0
- package/src/components/icons/lemon-icons.tsx +57 -0
- package/src/components/sections/AlertShowcase.tsx +2 -1
- package/src/components/sections/BadgeShowcase.tsx +1 -1
- package/src/components/sections/ButtonShowcase.tsx +6 -10
- package/src/components/sections/CardShowcase.tsx +22 -6
- package/src/components/sections/DropdownShowcase.tsx +4 -6
- package/src/components/sections/FilterChipsShowcase.tsx +74 -26
- package/src/components/sections/FormShowcase.tsx +2 -1
- package/src/components/sections/IconShowcase.tsx +195 -212
- package/src/components/sections/KbdShowcase.tsx +47 -21
- package/src/components/sections/ModalShowcase.tsx +7 -6
- package/src/components/sections/NewComponentsShowcase.tsx +1052 -0
- package/src/components/sections/ProductLayoutsShowcase.tsx +78 -0
- package/src/components/sections/ProgressShowcase.tsx +50 -53
- package/src/components/sections/QuillChartShowcase.tsx +92 -0
- package/src/components/sections/QuillDashboardShowcase.tsx +149 -0
- package/src/components/sections/SidebarShowcase.tsx +151 -0
- package/src/components/sections/SwapShowcase.tsx +13 -5
- package/src/components/sections/TableShowcase.tsx +2 -0
- package/src/components/sections/TooltipShowcase.tsx +19 -25
- package/src/components/ui/Alert.tsx +28 -19
- package/src/components/ui/AlertDialog.tsx +62 -0
- package/src/components/ui/AppLayout.tsx +27 -0
- package/src/components/ui/AuthLayout.tsx +58 -0
- package/src/components/ui/Autocomplete.tsx +106 -0
- package/src/components/ui/Badge.tsx +10 -5
- package/src/components/ui/Banner.tsx +63 -0
- package/src/components/ui/Button.tsx +22 -12
- package/src/components/ui/ButtonChrome.tsx +1 -1
- package/src/components/ui/ButtonGroup.tsx +16 -0
- package/src/components/ui/Calendar.tsx +6 -7
- package/src/components/ui/Card.tsx +21 -11
- package/src/components/ui/Chip.tsx +46 -0
- package/src/components/ui/Collapsible.tsx +46 -0
- package/src/components/ui/CollapsibleSection.tsx +11 -11
- package/src/components/ui/Combobox.tsx +119 -0
- package/src/components/ui/ContextMenu.tsx +81 -0
- package/src/components/ui/DataTable.tsx +133 -0
- package/src/components/ui/DatePicker.tsx +96 -0
- package/src/components/ui/Dialog.tsx +75 -0
- package/src/components/ui/Divider.tsx +39 -0
- package/src/components/ui/Dot.tsx +32 -0
- package/src/components/ui/DropdownMenu.tsx +189 -65
- package/src/components/ui/FilterBar.tsx +165 -0
- package/src/components/ui/FilterChips.tsx +65 -35
- package/src/components/ui/FilterControls.tsx +30 -0
- package/src/components/ui/FormField.tsx +69 -0
- package/src/components/ui/Input.tsx +166 -140
- package/src/components/ui/InputGroup.tsx +16 -0
- package/src/components/ui/Kbd.tsx +88 -13
- package/src/components/ui/Lettermark.tsx +47 -0
- package/src/components/ui/Link.tsx +32 -0
- package/src/components/ui/Loader.tsx +96 -0
- package/src/components/ui/LoadingBar.tsx +25 -0
- package/src/components/ui/Menubar.tsx +66 -0
- package/src/components/ui/Metric.tsx +34 -0
- package/src/components/ui/Modal.tsx +66 -36
- package/src/components/ui/NumberField.tsx +76 -0
- package/src/components/ui/Popover.tsx +73 -0
- package/src/components/ui/ProgressCircle.tsx +63 -0
- package/src/components/ui/Resizable.tsx +71 -0
- package/src/components/ui/Row.tsx +51 -0
- package/src/components/ui/ScrollArea.tsx +54 -0
- package/src/components/ui/SettingsLayout.tsx +94 -0
- package/src/components/ui/ShowcaseWrapper.tsx +4 -16
- package/src/components/ui/Sidebar.tsx +77 -0
- package/src/components/ui/Snack.tsx +48 -0
- package/src/components/ui/Spinner.tsx +30 -0
- package/src/components/ui/Splotch.tsx +33 -0
- package/src/components/ui/Stepper.tsx +5 -6
- package/src/components/ui/Table.tsx +2 -0
- package/src/components/ui/Tag.tsx +65 -0
- package/src/components/ui/Toggle.tsx +62 -0
- package/src/components/ui/Tooltip.test.tsx +58 -0
- package/src/components/ui/Tooltip.tsx +211 -0
- package/src/components/ui/Widget.tsx +31 -0
- package/src/components/ui/first-slice.test.tsx +98 -0
- package/src/components/ui/index.ts +335 -22
- package/src/components/ui/interaction-primitives.test.tsx +77 -0
- package/src/components/ui/kbd-icons.test.tsx +90 -0
- package/src/components/ui/product-patterns.test.tsx +77 -0
- package/src/styles/themes.css +411 -1
- package/tailwind-preset.ts +7 -0
- package/dist/chunk-SHF57J7U.mjs +0 -2910
- package/dist/index-CmS6hcUk.d.mts +0 -753
- package/dist/index-CmS6hcUk.d.ts +0 -753
- package/src/components/charts/ph-insight-charts.tsx +0 -162
- package/src/components/dashboard/DashboardFiltersBar.tsx +0 -19
- package/src/components/dashboard/DashboardGrid.tsx +0 -23
- package/src/components/dashboard/DashboardInsightPlaceholder.tsx +0 -37
- package/src/components/dashboard/DashboardKpiCard.tsx +0 -25
- package/src/components/dashboard/DashboardToolbar.tsx +0 -23
- package/src/components/dashboard/DashboardWell.tsx +0 -10
- package/src/components/dashboard/InsightMiniCard.tsx +0 -26
- package/src/components/dashboard/InsightShell.tsx +0 -37
- package/src/components/dashboard/InsightShellHeader.tsx +0 -22
- package/src/components/dashboard/MiniTrendBars.tsx +0 -51
- package/src/components/dashboard/index.ts +0 -31
- package/src/components/dashboard/types.ts +0 -9
- package/src/components/sections/ChartShowcase.tsx +0 -190
- package/src/components/sections/DashboardShowcase.tsx +0 -191
- package/src/components/sections/StatShowcase.tsx +0 -129
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState, useMemo, type ReactNode } from "react";
|
|
4
|
+
import { cn } from "@/lib/cn";
|
|
5
|
+
|
|
6
|
+
export interface DataTableColumn<T> {
|
|
7
|
+
key: string;
|
|
8
|
+
label: string;
|
|
9
|
+
render?: (item: T) => ReactNode;
|
|
10
|
+
sortable?: boolean;
|
|
11
|
+
className?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface DataTableProps<T> {
|
|
15
|
+
columns: DataTableColumn<T>[];
|
|
16
|
+
data: T[];
|
|
17
|
+
pageSize?: number;
|
|
18
|
+
searchable?: boolean;
|
|
19
|
+
searchPlaceholder?: string;
|
|
20
|
+
className?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function DataTable<T extends Record<string, any>>({
|
|
24
|
+
columns,
|
|
25
|
+
data,
|
|
26
|
+
pageSize = 10,
|
|
27
|
+
searchable = false,
|
|
28
|
+
searchPlaceholder = "Search...",
|
|
29
|
+
className,
|
|
30
|
+
}: DataTableProps<T>) {
|
|
31
|
+
const [search, setSearch] = useState("");
|
|
32
|
+
const [sortKey, setSortKey] = useState<string | null>(null);
|
|
33
|
+
const [sortDir, setSortDir] = useState<"asc" | "desc">("asc");
|
|
34
|
+
const [page, setPage] = useState(0);
|
|
35
|
+
|
|
36
|
+
const filtered = useMemo(() => {
|
|
37
|
+
let items = data;
|
|
38
|
+
if (search) {
|
|
39
|
+
const q = search.toLowerCase();
|
|
40
|
+
items = items.filter((item) =>
|
|
41
|
+
Object.values(item).some((v) => String(v).toLowerCase().includes(q))
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
if (sortKey) {
|
|
45
|
+
items = [...items].sort((a, b) => {
|
|
46
|
+
const av = a[sortKey];
|
|
47
|
+
const bv = b[sortKey];
|
|
48
|
+
const cmp = av < bv ? -1 : av > bv ? 1 : 0;
|
|
49
|
+
return sortDir === "asc" ? cmp : -cmp;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return items;
|
|
53
|
+
}, [data, search, sortKey, sortDir]);
|
|
54
|
+
|
|
55
|
+
const totalPages = Math.ceil(filtered.length / pageSize);
|
|
56
|
+
const paged = filtered.slice(page * pageSize, (page + 1) * pageSize);
|
|
57
|
+
|
|
58
|
+
const handleSort = (key: string) => {
|
|
59
|
+
if (sortKey === key) {
|
|
60
|
+
setSortDir((d) => (d === "asc" ? "desc" : "asc"));
|
|
61
|
+
} else {
|
|
62
|
+
setSortKey(key);
|
|
63
|
+
setSortDir("asc");
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<div className={cn("ph-data-table", className)}>
|
|
69
|
+
{searchable && (
|
|
70
|
+
<div className="ph-data-table__toolbar">
|
|
71
|
+
<input
|
|
72
|
+
type="text"
|
|
73
|
+
value={search}
|
|
74
|
+
onChange={(e) => { setSearch(e.target.value); setPage(0); }}
|
|
75
|
+
placeholder={searchPlaceholder}
|
|
76
|
+
className="ph-input ph-input-sm"
|
|
77
|
+
/>
|
|
78
|
+
</div>
|
|
79
|
+
)}
|
|
80
|
+
<div className="ph-table-wrap">
|
|
81
|
+
<table className="ph-table">
|
|
82
|
+
<thead>
|
|
83
|
+
<tr>
|
|
84
|
+
{columns.map((col) => (
|
|
85
|
+
<th
|
|
86
|
+
key={col.key}
|
|
87
|
+
className={cn(col.sortable && "cursor-pointer select-none", col.className)}
|
|
88
|
+
onClick={() => col.sortable && handleSort(col.key)}
|
|
89
|
+
>
|
|
90
|
+
{col.label}
|
|
91
|
+
{sortKey === col.key && (
|
|
92
|
+
<span className="ml-1 inline-block">{sortDir === "asc" ? "\u2191" : "\u2193"}</span>
|
|
93
|
+
)}
|
|
94
|
+
</th>
|
|
95
|
+
))}
|
|
96
|
+
</tr>
|
|
97
|
+
</thead>
|
|
98
|
+
<tbody>
|
|
99
|
+
{paged.map((item, i) => (
|
|
100
|
+
<tr key={i}>
|
|
101
|
+
{columns.map((col) => (
|
|
102
|
+
<td key={col.key} className={col.className}>
|
|
103
|
+
{col.render ? col.render(item) : item[col.key]}
|
|
104
|
+
</td>
|
|
105
|
+
))}
|
|
106
|
+
</tr>
|
|
107
|
+
))}
|
|
108
|
+
{paged.length === 0 && (
|
|
109
|
+
<tr>
|
|
110
|
+
<td colSpan={columns.length} className="text-center text-ph-mutedtext py-8">No results</td>
|
|
111
|
+
</tr>
|
|
112
|
+
)}
|
|
113
|
+
</tbody>
|
|
114
|
+
</table>
|
|
115
|
+
</div>
|
|
116
|
+
{totalPages > 1 && (
|
|
117
|
+
<div className="ph-data-table__pagination">
|
|
118
|
+
<button disabled={page === 0} onClick={() => setPage((p) => p - 1)} className="ph-data-table__page-btn">
|
|
119
|
+
Previous
|
|
120
|
+
</button>
|
|
121
|
+
<span className="text-xs text-ph-mutedtext">
|
|
122
|
+
Page {page + 1} of {totalPages}
|
|
123
|
+
</span>
|
|
124
|
+
<button disabled={page >= totalPages - 1} onClick={() => setPage((p) => p + 1)} className="ph-data-table__page-btn">
|
|
125
|
+
Next
|
|
126
|
+
</button>
|
|
127
|
+
</div>
|
|
128
|
+
)}
|
|
129
|
+
</div>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
DataTable.displayName = "DataTable";
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState, useMemo, type InputHTMLAttributes } from "react";
|
|
4
|
+
import { cn } from "@/lib/cn";
|
|
5
|
+
|
|
6
|
+
const MONTHS = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
|
|
7
|
+
const DAYS = ["Su","Mo","Tu","We","Th","Fr","Sa"];
|
|
8
|
+
|
|
9
|
+
export interface DatePickerProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "value" | "onChange" | "type"> {
|
|
10
|
+
value?: Date;
|
|
11
|
+
onChange?: (date: Date) => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function DatePicker({ value, onChange, className, ...props }: DatePickerProps) {
|
|
15
|
+
const [open, setOpen] = useState(false);
|
|
16
|
+
const [viewDate, setViewDate] = useState(value ?? new Date());
|
|
17
|
+
const selected = value;
|
|
18
|
+
|
|
19
|
+
const days = useMemo(() => {
|
|
20
|
+
const year = viewDate.getFullYear();
|
|
21
|
+
const month = viewDate.getMonth();
|
|
22
|
+
const first = new Date(year, month, 1).getDay();
|
|
23
|
+
const last = new Date(year, month + 1, 0).getDate();
|
|
24
|
+
const prevLast = new Date(year, month, 0).getDate();
|
|
25
|
+
const cells: { day: number; month: "prev" | "current" | "next" }[] = [];
|
|
26
|
+
for (let i = first - 1; i >= 0; i--) cells.push({ day: prevLast - i, month: "prev" });
|
|
27
|
+
for (let d = 1; d <= last; d++) cells.push({ day: d, month: "current" });
|
|
28
|
+
const remaining = 42 - cells.length;
|
|
29
|
+
for (let d = 1; d <= remaining; d++) cells.push({ day: d, month: "next" });
|
|
30
|
+
return cells;
|
|
31
|
+
}, [viewDate]);
|
|
32
|
+
|
|
33
|
+
const isSelected = (d: number, m: "prev" | "current" | "next") => {
|
|
34
|
+
if (!selected || m !== "current") return false;
|
|
35
|
+
const date = new Date(viewDate.getFullYear(), viewDate.getMonth(), d);
|
|
36
|
+
return date.toDateString() === selected.toDateString();
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const selectDay = (d: number, m: string) => {
|
|
40
|
+
const month = m === "prev" ? viewDate.getMonth() - 1 : m === "next" ? viewDate.getMonth() + 1 : viewDate.getMonth();
|
|
41
|
+
const year = month < 0 ? viewDate.getFullYear() - 1 : month > 11 ? viewDate.getFullYear() + 1 : viewDate.getFullYear();
|
|
42
|
+
const adjustedMonth = ((month % 12) + 12) % 12;
|
|
43
|
+
const date = new Date(year, adjustedMonth, d);
|
|
44
|
+
onChange?.(date);
|
|
45
|
+
setOpen(false);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const nav = (delta: number) => setViewDate(new Date(viewDate.getFullYear(), viewDate.getMonth() + delta, 1));
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<div className={cn("ph-datepicker relative", className)}>
|
|
52
|
+
<input
|
|
53
|
+
type="text"
|
|
54
|
+
readOnly
|
|
55
|
+
value={selected ? `${MONTHS[selected.getMonth()]} ${selected.getDate()}, ${selected.getFullYear()}` : ""}
|
|
56
|
+
onFocus={() => setOpen(true)}
|
|
57
|
+
placeholder="Pick a date"
|
|
58
|
+
className="ph-input w-full cursor-pointer"
|
|
59
|
+
{...props}
|
|
60
|
+
/>
|
|
61
|
+
{open && (
|
|
62
|
+
<div className="ph-datepicker__dropdown">
|
|
63
|
+
<div className="ph-datepicker__nav">
|
|
64
|
+
<button type="button" onClick={() => nav(-1)} className="ph-datepicker__nav-btn" aria-label="Previous month">
|
|
65
|
+
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M7 3L4 6l3 3"/></svg>
|
|
66
|
+
</button>
|
|
67
|
+
<span className="ph-datepicker__nav-label">{MONTHS[viewDate.getMonth()]} {viewDate.getFullYear()}</span>
|
|
68
|
+
<button type="button" onClick={() => nav(1)} className="ph-datepicker__nav-btn" aria-label="Next month">
|
|
69
|
+
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M5 3l3 3-3 3"/></svg>
|
|
70
|
+
</button>
|
|
71
|
+
</div>
|
|
72
|
+
<div className="ph-datepicker__grid">
|
|
73
|
+
{DAYS.map((d) => <div key={d} className="ph-datepicker__day-header">{d}</div>)}
|
|
74
|
+
{days.map((d, i) => (
|
|
75
|
+
<button
|
|
76
|
+
key={i}
|
|
77
|
+
type="button"
|
|
78
|
+
disabled={d.month !== "current"}
|
|
79
|
+
onClick={() => selectDay(d.day, d.month)}
|
|
80
|
+
className={cn(
|
|
81
|
+
"ph-datepicker__day",
|
|
82
|
+
d.month !== "current" && "ph-datepicker__day--other",
|
|
83
|
+
isSelected(d.day, d.month) && "ph-datepicker__day--selected"
|
|
84
|
+
)}
|
|
85
|
+
>
|
|
86
|
+
{d.day}
|
|
87
|
+
</button>
|
|
88
|
+
))}
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
)}
|
|
92
|
+
</div>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
DatePicker.displayName = "DatePicker";
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { useState, type ReactNode } from "react";
|
|
2
|
+
import { Modal, type ModalProps } from "@/components/ui/Modal";
|
|
3
|
+
import { Button, type ButtonProps } from "@/components/ui/Button";
|
|
4
|
+
|
|
5
|
+
export interface DialogProps extends Omit<ModalProps, "onClose"> {
|
|
6
|
+
isOpen?: boolean;
|
|
7
|
+
onClose?: () => void;
|
|
8
|
+
onConfirm?: () => void | Promise<void>;
|
|
9
|
+
confirmText?: string;
|
|
10
|
+
cancelText?: string;
|
|
11
|
+
confirmProps?: Partial<ButtonProps>;
|
|
12
|
+
cancelProps?: Partial<ButtonProps>;
|
|
13
|
+
children?: ReactNode;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function Dialog({
|
|
17
|
+
isOpen: openProp,
|
|
18
|
+
onClose,
|
|
19
|
+
onConfirm,
|
|
20
|
+
confirmText = "Confirm",
|
|
21
|
+
cancelText = "Cancel",
|
|
22
|
+
confirmProps,
|
|
23
|
+
cancelProps,
|
|
24
|
+
children,
|
|
25
|
+
title,
|
|
26
|
+
...modalProps
|
|
27
|
+
}: DialogProps) {
|
|
28
|
+
const [loading, setLoading] = useState(false);
|
|
29
|
+
const isOpen = openProp ?? true;
|
|
30
|
+
|
|
31
|
+
const handleConfirm = async () => {
|
|
32
|
+
if (!onConfirm) return;
|
|
33
|
+
setLoading(true);
|
|
34
|
+
try {
|
|
35
|
+
await onConfirm();
|
|
36
|
+
} finally {
|
|
37
|
+
setLoading(false);
|
|
38
|
+
}
|
|
39
|
+
onClose?.();
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<Modal
|
|
44
|
+
open={isOpen}
|
|
45
|
+
onClose={onClose}
|
|
46
|
+
title={title}
|
|
47
|
+
footer={
|
|
48
|
+
<div className="flex justify-end gap-2">
|
|
49
|
+
<Button variant="secondary" {...cancelProps} onClick={onClose}>
|
|
50
|
+
{cancelText}
|
|
51
|
+
</Button>
|
|
52
|
+
{onConfirm && (
|
|
53
|
+
<Button
|
|
54
|
+
variant="primary"
|
|
55
|
+
loading={loading}
|
|
56
|
+
{...confirmProps}
|
|
57
|
+
onClick={handleConfirm}
|
|
58
|
+
>
|
|
59
|
+
{confirmText}
|
|
60
|
+
</Button>
|
|
61
|
+
)}
|
|
62
|
+
</div>
|
|
63
|
+
}
|
|
64
|
+
{...modalProps}
|
|
65
|
+
>
|
|
66
|
+
{children}
|
|
67
|
+
</Modal>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
Dialog.displayName = "Dialog";
|
|
72
|
+
|
|
73
|
+
export function confirmDialog(props: DialogProps) {
|
|
74
|
+
return <Dialog {...props} />;
|
|
75
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { HTMLAttributes } from "react";
|
|
2
|
+
import { cn } from "@/lib/cn";
|
|
3
|
+
|
|
4
|
+
export interface DividerProps extends HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
vertical?: boolean;
|
|
6
|
+
dashed?: boolean;
|
|
7
|
+
thick?: boolean;
|
|
8
|
+
label?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function Divider({
|
|
12
|
+
vertical = false,
|
|
13
|
+
dashed = false,
|
|
14
|
+
thick = false,
|
|
15
|
+
label,
|
|
16
|
+
className,
|
|
17
|
+
...props
|
|
18
|
+
}: DividerProps) {
|
|
19
|
+
return (
|
|
20
|
+
<div
|
|
21
|
+
role="separator"
|
|
22
|
+
aria-orientation={vertical ? "vertical" : "horizontal"}
|
|
23
|
+
className={cn(
|
|
24
|
+
"ph-divider",
|
|
25
|
+
vertical && "ph-divider--vertical",
|
|
26
|
+
thick && "ph-divider--thick",
|
|
27
|
+
dashed && "ph-divider--dashed",
|
|
28
|
+
!!label && "ph-divider--with-label",
|
|
29
|
+
!className && (vertical ? "mx-2 self-stretch" : "my-2"),
|
|
30
|
+
className
|
|
31
|
+
)}
|
|
32
|
+
{...props}
|
|
33
|
+
>
|
|
34
|
+
{label && <span className="ph-divider__label">{label}</span>}
|
|
35
|
+
</div>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
Divider.displayName = "Divider";
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { HTMLAttributes } from "react";
|
|
2
|
+
import { cn } from "@/lib/cn";
|
|
3
|
+
|
|
4
|
+
export interface DotProps extends HTMLAttributes<HTMLSpanElement> {
|
|
5
|
+
color?: "green" | "yellow" | "red" | "blue" | "gray";
|
|
6
|
+
size?: "sm" | "md";
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const colorMap = {
|
|
10
|
+
green: "bg-ph-success",
|
|
11
|
+
yellow: "bg-ph-warning",
|
|
12
|
+
red: "bg-ph-danger",
|
|
13
|
+
blue: "bg-ph-info",
|
|
14
|
+
gray: "bg-ph-mutedtext",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function Dot({ color = "gray", size = "md", className, ...props }: DotProps) {
|
|
18
|
+
return (
|
|
19
|
+
<span
|
|
20
|
+
className={cn(
|
|
21
|
+
"ph-dot inline-block shrink-0 rounded-full",
|
|
22
|
+
size === "sm" ? "h-1.5 w-1.5" : "h-2.5 w-2.5",
|
|
23
|
+
colorMap[color],
|
|
24
|
+
className
|
|
25
|
+
)}
|
|
26
|
+
aria-hidden="true"
|
|
27
|
+
{...props}
|
|
28
|
+
/>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
Dot.displayName = "Dot";
|