feat(cabinet): migrate all icons to the panel's Phosphor set

Replace the cabinet's hand-written heroicons-style SVG icon components with
the Remnawave panel's own icon family — Phosphor via react-icons/pi (Duotone).
All icons now live in a single central barrel (src/components/icons:
index + extended-icons + editor-icons, 144 icons); every feature file imports
from it instead of redefining inline SVGs (~440 icon defs removed).

- Add react-icons dependency
- Desktop nav (AppShell) now uses the central Phosphor icons, removing a
  stroke-vs-duotone inconsistency the partial migration introduced
- Icons with custom props (SortIcon's direction, expandable chevrons) kept as
  thin Phosphor wrappers; the RemnawaveIcon brand logo and animated SVGs
  (checkmarks, spinners) are intentionally left as-is
- Restore the original per-call-site icon sizes wherever the centralized
  default differed (211 call sites)
This commit is contained in:
c0mrade
2026-05-31 18:50:50 +03:00
parent a62d689fd3
commit d0e0b6b7e3
94 changed files with 1368 additions and 5059 deletions

View File

@@ -18,6 +18,15 @@ import {
verticalListSortingStrategy,
} from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { PiCaretDownDuotone } from 'react-icons/pi';
import {
GripIcon,
TrashIcon,
PlusIcon,
LinkIcon,
ArrowUpIcon,
ArrowDownIcon,
} from '@/components/icons';
import {
menuLayoutApi,
type MenuConfig,
@@ -31,82 +40,10 @@ import { Toggle } from './Toggle';
import { useNotify } from '../../platform/hooks/useNotify';
import { useNativeDialog } from '../../platform/hooks/useNativeDialog';
const GripIcon = () => (
<svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"
/>
</svg>
);
const TrashIcon = () => (
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0"
/>
</svg>
);
const PlusIcon = () => (
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
);
const ChevronIcon = ({ expanded }: { expanded: boolean }) => (
<svg
<PiCaretDownDuotone
className={`h-3.5 w-3.5 transition-transform ${expanded ? 'rotate-180' : ''}`}
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
</svg>
);
const LinkIcon = () => (
<svg
className="h-3.5 w-3.5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M13.19 8.688a4.5 4.5 0 011.242 7.244l-4.5 4.5a4.5 4.5 0 01-6.364-6.364l1.757-1.757m9.86-2.54a4.5 4.5 0 00-1.242-7.244l-4.5-4.5a4.5 4.5 0 00-6.364 6.364L4.03 8.591"
/>
</svg>
);
const ArrowUpIcon = () => (
<svg
className="h-3.5 w-3.5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path strokeLinecap="round" strokeLinejoin="round" d="M4.5 15.75l7.5-7.5 7.5 7.5" />
</svg>
);
const ArrowDownIcon = () => (
<svg
className="h-3.5 w-3.5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path strokeLinecap="round" strokeLinejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" />
</svg>
/>
);
function generateId(): string {
@@ -200,7 +137,7 @@ function ButtonChip({
: 'cursor-default text-dark-700'
}`}
>
<ArrowUpIcon />
<ArrowUpIcon className="h-3.5 w-3.5" />
</button>
<button
onClick={onMoveDown ?? undefined}
@@ -212,7 +149,7 @@ function ButtonChip({
: 'cursor-default text-dark-700'
}`}
>
<ArrowDownIcon />
<ArrowDownIcon className="h-3.5 w-3.5" />
</button>
</div>
<span className={`h-2.5 w-2.5 shrink-0 rounded-full ${colorDotClass}`} />
@@ -221,7 +158,7 @@ function ButtonChip({
</span>
{!isBuiltin && (
<span className="text-dark-500" title="URL">
<LinkIcon />
<LinkIcon className="h-3.5 w-3.5" />
</span>
)}
<Toggle checked={button.enabled} onChange={() => onUpdate({ enabled: !button.enabled })} />
@@ -236,7 +173,7 @@ function ButtonChip({
onClick={onRemove}
className="rounded-lg p-1 text-dark-500 transition-colors hover:bg-error-500/10 hover:text-error-400"
>
<TrashIcon />
<TrashIcon className="h-4 w-4" />
</button>
)}
</div>
@@ -425,7 +362,7 @@ function SortableRow({
onClick={() => onRemoveRow(row.id)}
className="rounded-lg p-1.5 text-dark-500 transition-colors hover:bg-error-500/10 hover:text-error-400"
>
<TrashIcon />
<TrashIcon className="h-4 w-4" />
</button>
)}
</div>
@@ -481,7 +418,7 @@ function InlineAddPanel({ rowId, usedBuiltinIds, onAddBuiltin, onAddCustom }: In
onClick={() => setIsOpen(true)}
className="flex w-full items-center justify-center gap-2 rounded-xl border-2 border-dashed border-dark-700/50 py-2.5 text-sm text-dark-500 transition-colors hover:border-dark-600 hover:text-dark-400"
>
<PlusIcon />
<PlusIcon className="h-4 w-4" />
{t('admin.menuEditor.addButton')}
</button>
);
@@ -516,7 +453,7 @@ function InlineAddPanel({ rowId, usedBuiltinIds, onAddBuiltin, onAddCustom }: In
}}
className="flex w-full items-center gap-2 rounded-lg px-3 py-2 text-left text-sm text-dark-200 transition-colors hover:bg-dark-700/50"
>
<LinkIcon />
<LinkIcon className="h-3.5 w-3.5" />
{t('admin.menuEditor.addUrlButton')}
</button>
<button
@@ -843,7 +780,7 @@ export function MenuEditorTab() {
onClick={addRow}
className="flex w-full items-center justify-center gap-2 rounded-2xl border-2 border-dashed border-dark-700/50 py-4 text-sm font-medium text-dark-500 transition-colors hover:border-dark-600 hover:text-dark-400"
>
<PlusIcon />
<PlusIcon className="h-4 w-4" />
{t('admin.menuEditor.addRow')}
</button>