fix(web): remove redundant prop-types and unblock lint pipeline
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 10s
Build & Deploy / 🧪 QA (push) Failing after 2m24s
Build & Deploy / 🏗️ Build (push) Failing after 3m40s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🩺 Health Check (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 3s
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 10s
Build & Deploy / 🧪 QA (push) Failing after 2m24s
Build & Deploy / 🏗️ Build (push) Failing after 3m40s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🩺 Health Check (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 3s
This commit is contained in:
191
apps/web/src/payload/components/IconSelector/index.tsx
Normal file
191
apps/web/src/payload/components/IconSelector/index.tsx
Normal file
@@ -0,0 +1,191 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState } from "react";
|
||||
import { useField } from "@payloadcms/ui";
|
||||
import * as LucideIcons from "lucide-react";
|
||||
|
||||
const COMMON_ICONS = [
|
||||
"Check",
|
||||
"X",
|
||||
"AlertTriangle",
|
||||
"Info",
|
||||
"ArrowRight",
|
||||
"ArrowUpRight",
|
||||
"ChevronRight",
|
||||
"Settings",
|
||||
"Tool",
|
||||
"Terminal",
|
||||
"Code",
|
||||
"Database",
|
||||
"Server",
|
||||
"Cpu",
|
||||
"Zap",
|
||||
"Shield",
|
||||
"Lock",
|
||||
"Key",
|
||||
"Eye",
|
||||
"Search",
|
||||
"Filter",
|
||||
"BarChart",
|
||||
"LineChart",
|
||||
"PieChart",
|
||||
"TrendingUp",
|
||||
"TrendingDown",
|
||||
"Users",
|
||||
"User",
|
||||
"Briefcase",
|
||||
"Building",
|
||||
"Globe",
|
||||
"Mail",
|
||||
"FileText",
|
||||
"File",
|
||||
"Folder",
|
||||
"Image",
|
||||
"Video",
|
||||
"MessageSquare",
|
||||
"Clock",
|
||||
"Calendar",
|
||||
"CheckCircle",
|
||||
"XCircle",
|
||||
"Play",
|
||||
"Pause",
|
||||
"Activity",
|
||||
"Box",
|
||||
"Layers",
|
||||
"Layout",
|
||||
"Monitor",
|
||||
"Smartphone",
|
||||
"Tablet",
|
||||
"Wifi",
|
||||
"Cloud",
|
||||
"Crosshair",
|
||||
"Target",
|
||||
"Trophy",
|
||||
"Star",
|
||||
"Heart",
|
||||
];
|
||||
|
||||
export default function IconSelectorField({ path }: { path: string }) {
|
||||
const { value, setValue } = useField<string>({ path });
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
|
||||
const filteredIcons = COMMON_ICONS.filter((name) =>
|
||||
name.toLowerCase().includes(searchTerm.toLowerCase()),
|
||||
);
|
||||
|
||||
const handleIconClick = (iconName: string) => {
|
||||
// Toggle off if clicking the current value
|
||||
if (value === iconName) {
|
||||
setValue(null);
|
||||
} else {
|
||||
setValue(iconName);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="field-type" style={{ marginBottom: "1.5rem" }}>
|
||||
<label
|
||||
className="field-label"
|
||||
style={{
|
||||
display: "block",
|
||||
marginBottom: "8px",
|
||||
fontSize: "0.875rem",
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
Icon Selection (Lucide)
|
||||
</label>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search icons..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
style={{
|
||||
padding: "8px 12px",
|
||||
border: "1px solid var(--theme-elevation-200)",
|
||||
borderRadius: "4px",
|
||||
background: "var(--theme-elevation-50)",
|
||||
color: "var(--theme-text)",
|
||||
fontSize: "0.875rem",
|
||||
width: "100%",
|
||||
marginBottom: "12px",
|
||||
boxSizing: "border-box",
|
||||
}}
|
||||
/>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: "repeat(auto-fill, minmax(40px, 1fr))",
|
||||
gap: "8px",
|
||||
maxHeight: "200px",
|
||||
overflowY: "auto",
|
||||
padding: "4px",
|
||||
}}
|
||||
>
|
||||
{filteredIcons.map((iconName) => {
|
||||
const Icon = (LucideIcons as any)[iconName];
|
||||
if (!Icon) return null;
|
||||
const isSelected = value === iconName;
|
||||
|
||||
return (
|
||||
<button
|
||||
key={iconName}
|
||||
type="button"
|
||||
title={iconName}
|
||||
onClick={() => handleIconClick(iconName)}
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
width: "40px",
|
||||
height: "40px",
|
||||
borderRadius: "6px",
|
||||
background: isSelected
|
||||
? "var(--theme-elevation-200)"
|
||||
: "transparent",
|
||||
border: isSelected
|
||||
? "1px solid var(--theme-elevation-400)"
|
||||
: "1px solid var(--theme-elevation-150)",
|
||||
color: isSelected
|
||||
? "var(--theme-text)"
|
||||
: "var(--theme-elevation-500)",
|
||||
cursor: "pointer",
|
||||
transition: "all 0.1s ease",
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.background = isSelected
|
||||
? "var(--theme-elevation-200)"
|
||||
: "var(--theme-elevation-100)";
|
||||
e.currentTarget.style.color = "var(--theme-text)";
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.background = isSelected
|
||||
? "var(--theme-elevation-200)"
|
||||
: "transparent";
|
||||
e.currentTarget.style.color = isSelected
|
||||
? "var(--theme-text)"
|
||||
: "var(--theme-elevation-500)";
|
||||
}}
|
||||
>
|
||||
<Icon size={20} strokeWidth={1.5} />
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{filteredIcons.length === 0 && (
|
||||
<div
|
||||
style={{
|
||||
fontSize: "0.875rem",
|
||||
color: "var(--theme-elevation-500)",
|
||||
marginTop: "8px",
|
||||
}}
|
||||
>
|
||||
No matching icons found.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user