mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-05-09 19:01:47 +00:00
feat(categorias): adiciona seletor pesquisável de ícones
This commit is contained in:
@@ -1,15 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { RiMoreLine } from "@remixicon/react";
|
import { RiMoreLine } from "@remixicon/react";
|
||||||
import { useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import { Button } from "@/shared/components/ui/button";
|
|
||||||
import { Input } from "@/shared/components/ui/input";
|
import { Input } from "@/shared/components/ui/input";
|
||||||
import { Label } from "@/shared/components/ui/label";
|
import { Label } from "@/shared/components/ui/label";
|
||||||
import {
|
|
||||||
Popover,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverTrigger,
|
|
||||||
} from "@/shared/components/ui/popover";
|
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
@@ -25,6 +19,7 @@ import { getCategoryIconOptions } from "@/shared/lib/categories/icons";
|
|||||||
import { cn } from "@/shared/utils/ui";
|
import { cn } from "@/shared/utils/ui";
|
||||||
|
|
||||||
import { CategoryIcon } from "./category-icon";
|
import { CategoryIcon } from "./category-icon";
|
||||||
|
import { CategoryPickerDialog } from "./category-picker-dialog";
|
||||||
import { TypeSelectContent } from "./category-select-items";
|
import { TypeSelectContent } from "./category-select-items";
|
||||||
import type { CategoryFormValues } from "./types";
|
import type { CategoryFormValues } from "./types";
|
||||||
|
|
||||||
@@ -33,17 +28,17 @@ interface CategoryFormFieldsProps {
|
|||||||
onChange: (field: keyof CategoryFormValues, value: string) => void;
|
onChange: (field: keyof CategoryFormValues, value: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const iconOptions = getCategoryIconOptions();
|
||||||
|
|
||||||
export function CategoryFormFields({
|
export function CategoryFormFields({
|
||||||
values,
|
values,
|
||||||
onChange,
|
onChange,
|
||||||
}: CategoryFormFieldsProps) {
|
}: CategoryFormFieldsProps) {
|
||||||
const [popoverOpen, setPopoverOpen] = useState(false);
|
const [pickerOpen, setPickerOpen] = useState(false);
|
||||||
const iconOptions = getCategoryIconOptions();
|
|
||||||
|
|
||||||
const handleIconSelect = (icon: string) => {
|
const selectedIconLabel = useMemo(() => {
|
||||||
onChange("icon", icon);
|
return iconOptions.find((o) => o.value === values.icon)?.label ?? null;
|
||||||
setPopoverOpen(false);
|
}, [values.icon]);
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-1 gap-4">
|
<div className="grid grid-cols-1 gap-4">
|
||||||
@@ -83,45 +78,36 @@ export function CategoryFormFields({
|
|||||||
|
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Label>Ícone</Label>
|
<Label>Ícone</Label>
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<div className="flex size-12 items-center justify-center rounded-lg border bg-muted/30 text-primary">
|
|
||||||
{values.icon ? (
|
|
||||||
<CategoryIcon name={values.icon} className="size-7" />
|
|
||||||
) : (
|
|
||||||
<RiMoreLine className="size-6 text-muted-foreground" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<Popover open={popoverOpen} onOpenChange={setPopoverOpen}>
|
|
||||||
<PopoverTrigger asChild>
|
|
||||||
<Button type="button" variant="outline" className="flex-1">
|
|
||||||
Selecionar ícone
|
|
||||||
</Button>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent className="w-[480px] p-3" align="start">
|
|
||||||
<div className="grid max-h-96 grid-cols-8 gap-2 overflow-y-auto">
|
|
||||||
{iconOptions.map((option) => (
|
|
||||||
<button
|
<button
|
||||||
key={option.value}
|
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => handleIconSelect(option.value)}
|
onClick={() => setPickerOpen(true)}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex size-12 items-center justify-center rounded-lg border transition-all hover:border-primary hover:bg-primary/5",
|
"flex w-full items-center gap-2 rounded-md border p-2 text-left transition-colors hover:border-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
||||||
values.icon === option.value
|
|
||||||
? "border-primary bg-primary/10 text-primary"
|
|
||||||
: "border-border text-muted-foreground hover:text-primary",
|
|
||||||
)}
|
)}
|
||||||
title={option.label}
|
|
||||||
>
|
>
|
||||||
<CategoryIcon name={option.value} className="size-6" />
|
<span className="flex size-8 shrink-0 items-center justify-center overflow-hidden rounded-full border border-border/40 bg-muted/30 text-primary">
|
||||||
|
{values.icon ? (
|
||||||
|
<CategoryIcon name={values.icon} className="size-5" />
|
||||||
|
) : (
|
||||||
|
<RiMoreLine className="size-4 text-muted-foreground" />
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
<span className="flex min-w-0 flex-1 flex-col">
|
||||||
|
<span className="truncate text-sm font-medium text-foreground">
|
||||||
|
{selectedIconLabel ?? "Selecionar ícone"}
|
||||||
|
</span>
|
||||||
|
<span className="text-xs text-muted-foreground">
|
||||||
|
Clique para trocar o ícone
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
))}
|
|
||||||
</div>
|
<CategoryPickerDialog
|
||||||
</PopoverContent>
|
open={pickerOpen}
|
||||||
</Popover>
|
value={values.icon}
|
||||||
</div>
|
onOpenChange={setPickerOpen}
|
||||||
<p className="text-xs text-muted-foreground">
|
onSelect={(icon) => onChange("icon", icon)}
|
||||||
Escolha um ícone que represente melhor esta categoria.
|
/>
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,20 +1,18 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import type { RemixiconComponentType } from "@remixicon/react";
|
import type { ComponentType } from "react";
|
||||||
import * as RemixIcons from "@remixicon/react";
|
import { getIconComponent } from "@/shared/utils/icons";
|
||||||
import { cn } from "@/shared/utils/ui";
|
import { cn } from "@/shared/utils/ui";
|
||||||
|
|
||||||
const ICONS = RemixIcons as Record<string, RemixiconComponentType | undefined>;
|
|
||||||
const FALLBACK_ICON = ICONS.RiPriceTag3Line;
|
|
||||||
|
|
||||||
interface CategoryIconProps {
|
interface CategoryIconProps {
|
||||||
name?: string | null;
|
name?: string | null;
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CategoryIcon({ name, className }: CategoryIconProps) {
|
export function CategoryIcon({ name, className }: CategoryIconProps) {
|
||||||
const IconComponent =
|
const IconComponent = (
|
||||||
(name ? ICONS[name] : undefined) ?? FALLBACK_ICON ?? null;
|
name ? getIconComponent(name) : getIconComponent("RiPriceTag3Line")
|
||||||
|
) as ComponentType<{ className?: string }> | null;
|
||||||
|
|
||||||
if (!IconComponent) {
|
if (!IconComponent) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
117
src/features/categories/components/category-picker-dialog.tsx
Normal file
117
src/features/categories/components/category-picker-dialog.tsx
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useMemo, useState } from "react";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from "@/shared/components/ui/dialog";
|
||||||
|
import { Input } from "@/shared/components/ui/input";
|
||||||
|
import { CATEGORY_ICON_GROUPS } from "@/shared/lib/categories/icons";
|
||||||
|
import { cn } from "@/shared/utils/ui";
|
||||||
|
|
||||||
|
import { CategoryIcon } from "./category-icon";
|
||||||
|
|
||||||
|
interface CategoryPickerDialogProps {
|
||||||
|
open: boolean;
|
||||||
|
value: string;
|
||||||
|
onOpenChange: (open: boolean) => void;
|
||||||
|
onSelect: (icon: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CategoryPickerDialog({
|
||||||
|
open,
|
||||||
|
value,
|
||||||
|
onOpenChange,
|
||||||
|
onSelect,
|
||||||
|
}: CategoryPickerDialogProps) {
|
||||||
|
const [search, setSearch] = useState("");
|
||||||
|
|
||||||
|
const handleOpenChange = (isOpen: boolean) => {
|
||||||
|
if (!isOpen) setSearch("");
|
||||||
|
onOpenChange(isOpen);
|
||||||
|
};
|
||||||
|
|
||||||
|
const filteredGroups = useMemo(() => {
|
||||||
|
const query = search.toLowerCase().trim();
|
||||||
|
if (!query) return CATEGORY_ICON_GROUPS;
|
||||||
|
|
||||||
|
return CATEGORY_ICON_GROUPS.flatMap((group) => {
|
||||||
|
const icons = group.icons.filter(
|
||||||
|
(icon) =>
|
||||||
|
icon.label.toLowerCase().includes(query) ||
|
||||||
|
group.label.toLowerCase().includes(query),
|
||||||
|
);
|
||||||
|
return icons.length > 0 ? [{ ...group, icons }] : [];
|
||||||
|
});
|
||||||
|
}, [search]);
|
||||||
|
|
||||||
|
const totalVisible = filteredGroups.reduce(
|
||||||
|
(acc, g) => acc + g.icons.length,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onOpenChange={handleOpenChange}>
|
||||||
|
<DialogContent className="sm:max-w-2xl">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Escolher ícone</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Selecione o ícone que melhor representa esta categoria.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="Pesquisar ícone..."
|
||||||
|
value={search}
|
||||||
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
|
className="h-8 text-sm"
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
|
||||||
|
{totalVisible === 0 ? (
|
||||||
|
<p className="py-4 text-center text-sm text-muted-foreground">
|
||||||
|
Nenhum ícone encontrado para “{search}”
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
<div className="flex max-h-96 flex-col gap-4 overflow-y-auto pr-1">
|
||||||
|
{filteredGroups.map((group) => (
|
||||||
|
<div key={group.label}>
|
||||||
|
<p className="mb-2 text-xs font-medium text-muted-foreground">
|
||||||
|
{group.label}
|
||||||
|
</p>
|
||||||
|
<div className="grid grid-cols-8 gap-1.5">
|
||||||
|
{group.icons.map((option) => (
|
||||||
|
<button
|
||||||
|
key={option.value}
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
onSelect(option.value);
|
||||||
|
handleOpenChange(false);
|
||||||
|
}}
|
||||||
|
onPointerDown={(e) => e.stopPropagation()}
|
||||||
|
aria-label={option.label}
|
||||||
|
aria-pressed={value === option.value}
|
||||||
|
title={option.label}
|
||||||
|
className={cn(
|
||||||
|
"flex size-10 items-center justify-center rounded-lg border transition-all hover:border-primary hover:bg-primary/5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
||||||
|
value === option.value
|
||||||
|
? "border-primary bg-primary/10 text-primary"
|
||||||
|
: "border-border text-muted-foreground hover:text-primary",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<CategoryIcon name={option.value} className="size-5" />
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -156,6 +156,186 @@ export const CATEGORY_ICON_OPTIONS: CategoryIconOption[] = [
|
|||||||
{ label: "Nuvem Upload", value: "RiCloudUploadLine" },
|
{ label: "Nuvem Upload", value: "RiCloudUploadLine" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export type CategoryIconGroup = {
|
||||||
|
label: string;
|
||||||
|
icons: CategoryIconOption[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CATEGORY_ICON_GROUPS: CategoryIconGroup[] = [
|
||||||
|
{
|
||||||
|
label: "Finanças",
|
||||||
|
icons: [
|
||||||
|
{ label: "Dinheiro", value: "RiMoneyDollarCircleLine" },
|
||||||
|
{ label: "Carteira", value: "RiWallet3Line" },
|
||||||
|
{ label: "Carteira 2", value: "RiWalletLine" },
|
||||||
|
{ label: "Cartão", value: "RiBankCard2Line" },
|
||||||
|
{ label: "Banco", value: "RiBankLine" },
|
||||||
|
{ label: "Moedas", value: "RiHandCoinLine" },
|
||||||
|
{ label: "Gráfico", value: "RiLineChartLine" },
|
||||||
|
{ label: "Ações", value: "RiStockLine" },
|
||||||
|
{ label: "Troca", value: "RiExchangeLine" },
|
||||||
|
{ label: "Reembolso", value: "RiRefundLine" },
|
||||||
|
{ label: "Recompensa", value: "RiRefund2Line" },
|
||||||
|
{ label: "Leilão", value: "RiAuctionLine" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Compras",
|
||||||
|
icons: [
|
||||||
|
{ label: "Carrinho", value: "RiShoppingCartLine" },
|
||||||
|
{ label: "Sacola", value: "RiShoppingBagLine" },
|
||||||
|
{ label: "Cesta", value: "RiShoppingBasketLine" },
|
||||||
|
{ label: "Presente", value: "RiGiftLine" },
|
||||||
|
{ label: "Cupom", value: "RiCouponLine" },
|
||||||
|
{ label: "Ticket", value: "RiTicket2Line" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Alimentação",
|
||||||
|
icons: [
|
||||||
|
{ label: "Restaurante", value: "RiRestaurantLine" },
|
||||||
|
{ label: "Garfo e faca", value: "RiRestaurant2Line" },
|
||||||
|
{ label: "Café", value: "RiCupLine" },
|
||||||
|
{ label: "Bebida", value: "RiDrinksFill" },
|
||||||
|
{ label: "Pizza", value: "RiCake3Line" },
|
||||||
|
{ label: "Cerveja", value: "RiBeerLine" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Transporte",
|
||||||
|
icons: [
|
||||||
|
{ label: "Ônibus", value: "RiBusLine" },
|
||||||
|
{ label: "Carro", value: "RiCarLine" },
|
||||||
|
{ label: "Táxi", value: "RiTaxiLine" },
|
||||||
|
{ label: "Moto", value: "RiMotorbikeLine" },
|
||||||
|
{ label: "Avião", value: "RiFlightTakeoffLine" },
|
||||||
|
{ label: "Navio", value: "RiShipLine" },
|
||||||
|
{ label: "Trem", value: "RiTrainLine" },
|
||||||
|
{ label: "Metrô", value: "RiSubwayLine" },
|
||||||
|
{ label: "Bicicleta", value: "RiBikeLine" },
|
||||||
|
{ label: "Mapa", value: "RiMapPinLine" },
|
||||||
|
{ label: "Combustível", value: "RiGasStationLine" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Moradia",
|
||||||
|
icons: [
|
||||||
|
{ label: "Casa", value: "RiHomeLine" },
|
||||||
|
{ label: "Prédio", value: "RiBuilding2Line" },
|
||||||
|
{ label: "Apartamento", value: "RiBuildingLine" },
|
||||||
|
{ label: "Ferramentas", value: "RiToolsLine" },
|
||||||
|
{ label: "Lâmpada", value: "RiLightbulbLine" },
|
||||||
|
{ label: "Energia", value: "RiFlashlightLine" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Saúde e bem-estar",
|
||||||
|
icons: [
|
||||||
|
{ label: "Saúde", value: "RiStethoscopeLine" },
|
||||||
|
{ label: "Hospital", value: "RiHospitalLine" },
|
||||||
|
{ label: "Coração", value: "RiHeart2Line" },
|
||||||
|
{ label: "Pulso", value: "RiHeartPulseLine" },
|
||||||
|
{ label: "Mental", value: "RiMentalHealthLine" },
|
||||||
|
{ label: "Farmácia", value: "RiFirstAidKitLine" },
|
||||||
|
{ label: "Fitness", value: "RiRunLine" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Educação",
|
||||||
|
icons: [
|
||||||
|
{ label: "Livro", value: "RiBook2Line" },
|
||||||
|
{ label: "Graduação", value: "RiGraduationCapLine" },
|
||||||
|
{ label: "Escola", value: "RiSchoolLine" },
|
||||||
|
{ label: "Lápis", value: "RiPencilLine" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Trabalho",
|
||||||
|
icons: [
|
||||||
|
{ label: "Maleta", value: "RiBriefcaseLine" },
|
||||||
|
{ label: "Pasta", value: "RiBriefcase4Line" },
|
||||||
|
{ label: "Escritório", value: "RiUserStarLine" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Lazer",
|
||||||
|
icons: [
|
||||||
|
{ label: "Controle", value: "RiGamepadLine" },
|
||||||
|
{ label: "Filme", value: "RiMovie2Line" },
|
||||||
|
{ label: "Música", value: "RiMusic2Line" },
|
||||||
|
{ label: "Microfone", value: "RiMicLine" },
|
||||||
|
{ label: "Fone", value: "RiHeadphoneLine" },
|
||||||
|
{ label: "Câmera", value: "RiCameraLine" },
|
||||||
|
{ label: "Praia", value: "RiUmbrellaLine" },
|
||||||
|
{ label: "Futebol", value: "RiFootballLine" },
|
||||||
|
{ label: "Basquete", value: "RiBasketballLine" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Tecnologia",
|
||||||
|
icons: [
|
||||||
|
{ label: "WiFi", value: "RiWifiLine" },
|
||||||
|
{ label: "Celular", value: "RiSmartphoneLine" },
|
||||||
|
{ label: "Computador", value: "RiComputerLine" },
|
||||||
|
{ label: "Monitor", value: "RiMonitorLine" },
|
||||||
|
{ label: "Teclado", value: "RiKeyboardLine" },
|
||||||
|
{ label: "Mouse", value: "RiMouseLine" },
|
||||||
|
{ label: "Fone Bluetooth", value: "RiBluetoothLine" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Pessoas",
|
||||||
|
icons: [
|
||||||
|
{ label: "Usuário", value: "RiUserLine" },
|
||||||
|
{ label: "Grupo", value: "RiGroupLine" },
|
||||||
|
{ label: "Família", value: "RiParentLine" },
|
||||||
|
{ label: "Bebê", value: "RiBabyCarriageLine" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Outros",
|
||||||
|
icons: [
|
||||||
|
{ label: "Animais", value: "RiBearSmileLine" },
|
||||||
|
{ label: "Camiseta", value: "RiTShirtLine" },
|
||||||
|
{ label: "Arquivo", value: "RiFileTextLine" },
|
||||||
|
{ label: "Documento", value: "RiArticleLine" },
|
||||||
|
{ label: "Balança", value: "RiScales2Line" },
|
||||||
|
{ label: "Escudo", value: "RiShieldCheckLine" },
|
||||||
|
{ label: "Serviço", value: "RiServiceLine" },
|
||||||
|
{ label: "Alerta", value: "RiAlertLine" },
|
||||||
|
{ label: "Troféu", value: "RiMedalLine" },
|
||||||
|
{ label: "Mais", value: "RiMore2Line" },
|
||||||
|
{ label: "Estrela", value: "RiStarLine" },
|
||||||
|
{ label: "Foguete", value: "RiRocketLine" },
|
||||||
|
{ label: "Ampulheta", value: "RiHourglassLine" },
|
||||||
|
{ label: "Calendário", value: "RiCalendarLine" },
|
||||||
|
{ label: "Relógio", value: "RiTimeLine" },
|
||||||
|
{ label: "Timer", value: "RiTimer2Line" },
|
||||||
|
{ label: "Fogo", value: "RiFireLine" },
|
||||||
|
{ label: "Gota", value: "RiDropLine" },
|
||||||
|
{ label: "Sol", value: "RiSunLine" },
|
||||||
|
{ label: "Lua", value: "RiMoonLine" },
|
||||||
|
{ label: "Nuvem", value: "RiCloudLine" },
|
||||||
|
{ label: "Raio", value: "RiFlashlightFill" },
|
||||||
|
{ label: "Planta", value: "RiPlantLine" },
|
||||||
|
{ label: "Árvore", value: "RiSeedlingLine" },
|
||||||
|
{ label: "Globo", value: "RiGlobalLine" },
|
||||||
|
{ label: "Localização", value: "RiMapPin2Line" },
|
||||||
|
{ label: "Bússola", value: "RiCompassLine" },
|
||||||
|
{ label: "Reciclagem", value: "RiRecycleLine" },
|
||||||
|
{ label: "Cadeado", value: "RiLockLine" },
|
||||||
|
{ label: "Chave", value: "RiKeyLine" },
|
||||||
|
{ label: "Configurações", value: "RiSettings3Line" },
|
||||||
|
{ label: "Link", value: "RiLinkLine" },
|
||||||
|
{ label: "Anexo", value: "RiAttachmentLine" },
|
||||||
|
{ label: "Download", value: "RiDownloadLine" },
|
||||||
|
{ label: "Upload", value: "RiUploadLine" },
|
||||||
|
{ label: "Nuvem Download", value: "RiCloudDownloadLine" },
|
||||||
|
{ label: "Nuvem Upload", value: "RiCloudUploadLine" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all available category icon options
|
* Gets all available category icon options
|
||||||
* @returns Array of icon options
|
* @returns Array of icon options
|
||||||
|
|||||||
Reference in New Issue
Block a user