Spaces:
Running
Running
import classNames from "classnames"; | |
import { | |
faChevronDown, | |
faChevronRight, | |
faTrash, | |
faCaretDown, | |
faCaretUp, | |
} from "@fortawesome/free-solid-svg-icons"; | |
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; | |
import { ListItem } from "components/editor-icons/comps/list/list-item"; | |
import { | |
Icons as ICONS, | |
IconCustomIcon, | |
IconCustomText, | |
} from "components/svg/icons"; | |
import { IconItem, IconType } from "@/types/editor"; | |
import { ColorPicker } from "components/color-picker"; | |
import { Range } from "components/range"; | |
import { Input } from "components/input"; | |
import { Switch } from "@/components/switch"; | |
import { PremiumOverlay } from "@/components/premium/overlay"; | |
import { FormattedMessage, useIntl } from "react-intl"; | |
import { Label } from "@/components/label"; | |
export const IconSelected = ({ | |
index, | |
totalIcons, | |
icon, | |
current, | |
setCurrent, | |
onDelete, | |
onChange, | |
onChangeOrder, | |
}: { | |
index: number; | |
totalIcons: number; | |
icon: IconType; | |
current?: number | null; | |
setCurrent: (index: number | null) => void; | |
onDelete: (index: number) => void; | |
onChange: (idnex: number, icon: IconType) => void; | |
onChangeOrder: (index: number, value: number) => void; | |
}) => { | |
const findIcon: any = icon?.custom_text?.enabled | |
? IconCustomText | |
: icon?.image | |
? IconCustomIcon | |
: ICONS?.find((i: IconItem) => icon.component === i.name); | |
const handleChange = (index: number, icon: any) => { | |
onChange(index, icon); | |
}; | |
const intl = useIntl(); | |
return ( | |
<div className="flex items-center justify-start gap-3 w-full"> | |
<div | |
className={classNames( | |
"bg-dark-600 w-full rounded-lg pl-4 py-4 pr-6 group cursor-pointer transition-all border-2 border-dark-600 duration-200 hover:border-blue", | |
{ | |
"!border-blue": current === index, | |
} | |
)} | |
> | |
<div | |
className="tracking-wider flex items-center justify-between gap-4" | |
onClick={() => setCurrent(current === index ? null : index)} | |
> | |
<div className="flex items-center justify-start gap-4"> | |
<div className="w-10"> | |
{icon?.image ? ( | |
<div className="flex items-center justify-center p-2 cursor-pointer rounded-lg relative group border border-dark-200 border-solid"> | |
<img | |
src={icon?.image} | |
className="w-full h-full object-contain" | |
/> | |
</div> | |
) : ( | |
<ListItem | |
icon={findIcon} | |
fill={findIcon?.defaultColor ?? "#fff"} | |
tooltip={false} | |
hoverable={false} | |
onSelect={() => {}} | |
/> | |
)} | |
</div> | |
<div> | |
<p className="text-white font-semibold"> | |
<FormattedMessage id={findIcon?.name} /> | |
</p> | |
<p className="text-xs text-dark-200"> | |
{findIcon?.tags?.join(", ")} | |
</p> | |
</div> | |
</div> | |
<div className="flex items-center justify-end gap-4"> | |
<div | |
className="bg-danger bg-opacity-60 rounded-lg text-white hover:bg-opacity-100 cursor-pointer w-8 h-8 flex items-center justify-center" | |
onClick={(e: any) => { | |
e.preventDefault(); | |
e.stopPropagation(); | |
onDelete(index); | |
}} | |
> | |
<FontAwesomeIcon icon={faTrash} className="w-4" /> | |
</div> | |
<div className="flex items-center justify-end"> | |
<FontAwesomeIcon | |
icon={current === index ? faChevronDown : faChevronRight} | |
className="text-dark-100 transition-all duration-200 w-3" | |
/> | |
</div> | |
</div> | |
</div> | |
{current === index && ( | |
<div className="border-t border-dark-400 pt-3 mt-4 grid grid-cols-2 gap-4"> | |
<div className="col-span-2"> | |
<Label className="mb-1"> | |
<FormattedMessage id="iconsEditor.editor.customisation.positions" /> | |
</Label> | |
<div className="grid grid-cols-2 gap-x-6 gap-y-3"> | |
<div className="gap-4 w-full col-span-2 grid grid-cols-3"> | |
<div className="flex items-center gap-3 col-span-2"> | |
<p className="font-semibold uppercase text-dark-200 text-xs"> | |
X | |
</p> | |
<Range | |
value={icon?.position?.x ?? 0} | |
max={ | |
icon?.custom_text?.enabled | |
? 1000 | |
: icon?.position?.xPath ?? undefined | |
} | |
onChange={(value) => { | |
const newIcon = { | |
...icon, | |
position: { ...icon.position, x: Number(value) }, | |
}; | |
handleChange(index, newIcon); | |
}} | |
/> | |
</div> | |
<Input | |
value={icon?.position?.x} | |
type="number" | |
placeholder={intl.formatMessage({ | |
id: "iconsEditor.editor.customisation.positions.x", | |
})} | |
className="bg-dark-500 rounded px-2 py-2 text-sm text-white placeholder-dark-200" | |
onChange={(newX) => { | |
let x: number = Number(newX); | |
if (x > 100) x = 100; | |
const newIcon = { | |
...icon, | |
position: { ...icon.position, x: x }, | |
}; | |
handleChange(index, newIcon); | |
}} | |
/> | |
</div> | |
<div className="gap-4 w-full col-span-2 grid grid-cols-3"> | |
<div className="flex items-center gap-3 col-span-2"> | |
<p className="font-semibold uppercase text-dark-200 text-xs"> | |
Y | |
</p> | |
<Range | |
value={icon?.position?.y ?? 0} | |
max={ | |
icon?.custom_text?.enabled | |
? 1000 | |
: icon?.position?.yPath ?? undefined | |
} | |
onChange={(value) => { | |
const newIcon = { | |
...icon, | |
position: { ...icon.position, y: Number(value) }, | |
}; | |
handleChange(index, newIcon); | |
}} | |
/> | |
</div> | |
<Input | |
value={icon?.position?.y} | |
type="number" | |
placeholder={intl.formatMessage({ | |
id: "iconsEditor.editor.customisation.positions.y", | |
})} | |
className="bg-dark-500 rounded px-2 py-2 text-sm text-white placeholder-dark-200" | |
onChange={(newY) => { | |
let y = Number(newY); | |
if (y > 100) y = 100; | |
const newIcon = { | |
...icon, | |
position: { ...icon.position, y: y }, | |
}; | |
handleChange(index, newIcon); | |
}} | |
/> | |
</div> | |
<div className="gap-4 w-full col-span-2 grid grid-cols-3"> | |
<div className="flex items-center gap-3 col-span-2"> | |
<p className="font-semibold uppercase text-dark-200 text-xs"> | |
Z | |
</p> | |
<Range | |
value={icon?.position?.angle ?? 0} | |
max={360} | |
onChange={(value) => { | |
const newIcon = { | |
...icon, | |
position: { ...icon.position, angle: Number(value) }, | |
}; | |
handleChange(index, newIcon); | |
}} | |
/> | |
</div> | |
<Input | |
value={icon?.position?.angle} | |
placeholder={intl.formatMessage({ | |
id: "iconsEditor.editor.customisation.angle", | |
})} | |
type="number" | |
className="bg-dark-500 rounded px-2 py-2 text-sm text-white placeholder-dark-200" | |
onChange={(newAngle) => { | |
let angle = Number(newAngle); | |
if (angle > 360) angle = 360; | |
const newIcon = { | |
...icon, | |
position: { ...icon.position, angle: angle }, | |
}; | |
handleChange(index, newIcon); | |
}} | |
/> | |
</div> | |
{!icon?.custom_text?.enabled && ( | |
<div className="gap-4 w-full col-span-2 grid grid-cols-3"> | |
<div className="flex items-center gap-3 col-span-2"> | |
<p className="font-semibold uppercase text-dark-200 text-xs"> | |
<FormattedMessage id="iconsEditor.editor.customisation.scale" /> | |
</p> | |
<Range | |
value={icon?.position?.scale ?? 1} | |
min={0} | |
max={2} | |
step={0.1} | |
onChange={(value) => { | |
const newIcon = { | |
...icon, | |
position: { | |
...icon.position, | |
scale: Number(value), | |
}, | |
}; | |
handleChange(index, newIcon); | |
}} | |
/> | |
</div> | |
<Input | |
value={ | |
icon?.position?.scale | |
? icon?.position?.scale * 100 | |
: undefined | |
} | |
type="number" | |
placeholder={intl.formatMessage({ | |
id: "iconsEditor.editor.customisation.scale", | |
})} | |
className="bg-dark-500 rounded px-2 py-2 text-sm text-white placeholder-dark-200" | |
onChange={(newScale) => { | |
let scale = Number(newScale); | |
if (scale > 100) scale = 100; | |
const newIcon = { | |
...icon, | |
position: { | |
...icon.position, | |
scale: Math.trunc(scale) / 100, | |
}, | |
}; | |
handleChange(index, newIcon); | |
}} | |
/> | |
</div> | |
)} | |
</div> | |
</div> | |
{!icon?.image && ( | |
<div className="col-span-2"> | |
<Label className="!mb-2.5"> | |
<FormattedMessage | |
id="iconsEditor.editor.customisation.gradientColor" | |
values={{ | |
span: (t) => ( | |
<span className="text-xs opacity-40">{t}</span> | |
), | |
}} | |
/> | |
</Label> | |
<ColorPicker | |
data={icon} | |
value={icon?.stringColor ?? icon?.colour} | |
onChange={(c: any, datas) => { | |
let newIcon = { ...icon, stringColor: c }; | |
if (c.includes("gradient")) { | |
const { colors, degrees } = datas; | |
const gradientType = c?.startsWith("linear") | |
? "linearGradient" | |
: "radialGradient"; | |
const angle = c?.startsWith("linear") | |
? c?.replace("linear-gradient(", "")?.split("deg")?.[0] | |
: 90; | |
newIcon["gradient"] = { | |
...newIcon.gradient, | |
enabled: true, | |
colours: colors, | |
angle, | |
type: gradientType, | |
}; | |
} else { | |
newIcon = { | |
...newIcon, | |
colour: c, | |
gradient: { | |
...icon?.gradient, | |
enabled: false, | |
}, | |
}; | |
} | |
handleChange(index, newIcon); | |
}} | |
/> | |
</div> | |
)} | |
{icon?.custom_text?.enabled && ( | |
<> | |
<div className="col-span-2"> | |
<Label className="!mb-2.5"> | |
<FormattedMessage id="iconsEditor.editor.customisation.customText" /> | |
</Label> | |
<div className="grid grid-cols-2 gap-5"> | |
<div> | |
<p className="font-semibold uppercase text-dark-200 text-xs mb-2"> | |
<FormattedMessage id="iconsEditor.editor.customisation.customText.text" /> | |
</p> | |
<input | |
type="text" | |
className="bg-dark-500 w-full rounded px-4 py-2 text-sm text-white placeholder-dark-200 outline-none border-none" | |
placeholder="10" | |
value={icon?.custom_text?.text} | |
onChange={({ target }) => { | |
const newIcon = { | |
...icon, | |
custom_text: { | |
...icon.custom_text, | |
text: target.value, | |
}, | |
}; | |
handleChange(index, newIcon); | |
}} | |
/> | |
</div> | |
<div> | |
<p className="font-semibold uppercase text-dark-200 text-xs mb-2"> | |
<FormattedMessage id="iconsEditor.editor.customisation.customText.fontSize" /> | |
</p> | |
<input | |
type="number" | |
className="bg-dark-500 w-full rounded px-4 py-2 text-sm text-white placeholder-dark-200 outline-none border-none" | |
placeholder="10" | |
value={icon?.custom_text?.size} | |
onChange={({ target }) => { | |
const newIcon = { | |
...icon, | |
custom_text: { | |
...icon.custom_text, | |
size: Number(target.value), | |
}, | |
}; | |
handleChange(index, newIcon); | |
}} | |
/> | |
</div> | |
</div> | |
</div> | |
</> | |
)} | |
<div className="relative col-span-2 grid grid-cols-2 gap-4"> | |
{!icon?.image && ( | |
<div> | |
<Label className="!mb-2.5"> | |
<FormattedMessage id="iconsEditor.editor.customisation.border" /> | |
</Label> | |
<div className="flex items-start justify-start gap-5"> | |
<div className="w-full"> | |
<p className="font-semibold uppercase text-dark-200 text-xs mb-2"> | |
<FormattedMessage id="iconsEditor.editor.customisation.width" /> | |
</p> | |
<input | |
type="number" | |
className="bg-dark-500 w-full rounded px-4 py-2 text-sm text-white placeholder-dark-200 outline-none border-none" | |
placeholder={ | |
intl.formatMessage({ | |
id: "iconsEditor.editor.customisation.width", | |
}) + "...px" | |
} | |
min={0} | |
value={icon?.border?.width} | |
onChange={({ target }) => { | |
const newIcon = { | |
...icon, | |
border: { | |
...icon.border, | |
width: target?.value | |
? Number(target.value) | |
: undefined, | |
}, | |
}; | |
handleChange(index, newIcon); | |
}} | |
/> | |
</div> | |
<div> | |
<p className="font-semibold uppercase text-dark-200 text-xs mb-2"> | |
<FormattedMessage id="iconsEditor.editor.customisation.color" /> | |
</p> | |
<ColorPicker | |
value={icon?.border?.colour} | |
gradients={false} | |
onChange={(c: any) => { | |
const newIcon = { | |
...icon, | |
border: { | |
...icon.border, | |
colour: c, | |
}, | |
}; | |
handleChange(index, newIcon); | |
}} | |
/> | |
</div> | |
</div> | |
</div> | |
)} | |
<div className="col-span-2"> | |
<div className="flex items-center justify-start gap-3 mb-2.5 "> | |
<Label className="!mb-0"> | |
<FormattedMessage id="iconsEditor.editor.customisation.shadow" /> | |
</Label> | |
<Switch | |
value={icon?.shadow?.enabled} | |
onChange={(enabled: boolean) => { | |
const newIcon = { | |
...icon, | |
shadow: { | |
...icon.shadow, | |
enabled, | |
}, | |
}; | |
handleChange(index, newIcon); | |
}} | |
/> | |
</div> | |
{icon?.shadow?.enabled && ( | |
<div className="gap-5 grid grid-cols-2"> | |
<div className="w-full"> | |
<p className="font-semibold uppercase text-dark-200 text-xs mb-2"> | |
X | |
</p> | |
<input | |
type="number" | |
className="bg-dark-500 w-full rounded px-4 py-2 text-sm text-white placeholder-dark-200 outline-none border-none" | |
placeholder={intl.formatMessage({ | |
id: "iconsEditor.editor.customisation.shadow.x.placeholder", | |
})} | |
min={0} | |
value={icon?.shadow?.position?.x} | |
onChange={({ target }) => { | |
const newIcon = { | |
...icon, | |
shadow: { | |
...icon.shadow, | |
position: { | |
...icon?.shadow?.position, | |
x: target?.value | |
? Number(target.value) | |
: undefined, | |
}, | |
}, | |
}; | |
handleChange(index, newIcon); | |
}} | |
/> | |
</div> | |
<div className="w-full"> | |
<p className="font-semibold uppercase text-dark-200 text-xs mb-2"> | |
Y | |
</p> | |
<input | |
type="number" | |
className="bg-dark-500 w-full rounded px-4 py-2 text-sm text-white placeholder-dark-200 outline-none border-none" | |
placeholder={intl.formatMessage({ | |
id: "iconsEditor.editor.customisation.shadow.y.placeholder", | |
})} | |
min={0} | |
value={icon?.shadow?.position?.y} | |
onChange={({ target }) => { | |
const newIcon = { | |
...icon, | |
shadow: { | |
...icon.shadow, | |
position: { | |
...icon?.shadow?.position, | |
y: target?.value | |
? Number(target.value) | |
: undefined, | |
}, | |
}, | |
}; | |
handleChange(index, newIcon); | |
}} | |
/> | |
</div> | |
<div className="w-full"> | |
<p className="font-semibold uppercase text-dark-200 text-xs mb-2"> | |
<FormattedMessage id="iconsEditor.editor.customisation.shadow.blur" /> | |
</p> | |
<input | |
type="number" | |
className="bg-dark-500 w-full rounded px-4 py-2 text-sm text-white placeholder-dark-200 outline-none border-none" | |
placeholder={intl.formatMessage({ | |
id: "iconsEditor.editor.customisation.shadow.blur.placeholder", | |
})} | |
min={0} | |
value={icon?.shadow?.position?.blur} | |
onChange={({ target }) => { | |
const newIcon = { | |
...icon, | |
shadow: { | |
...icon.shadow, | |
position: { | |
...icon?.shadow?.position, | |
blur: target?.value | |
? Number(target.value) | |
: undefined, | |
}, | |
}, | |
}; | |
handleChange(index, newIcon); | |
}} | |
/> | |
</div> | |
<div> | |
<p className="font-semibold uppercase text-dark-200 text-xs mb-2"> | |
<FormattedMessage id="iconsEditor.editor.customisation.color" /> | |
</p> | |
<ColorPicker | |
value={icon?.shadow?.colour} | |
gradients={false} | |
onChange={(c: any) => { | |
const newIcon = { | |
...icon, | |
shadow: { | |
...icon.shadow, | |
colour: c, | |
}, | |
}; | |
handleChange(index, newIcon); | |
}} | |
/> | |
</div> | |
</div> | |
)} | |
</div> | |
</div> | |
</div> | |
)} | |
</div> | |
{totalIcons > 1 && ( | |
<div className="flex flex-col gap-0.5"> | |
{index !== 0 && ( | |
<FontAwesomeIcon | |
icon={faCaretUp} | |
className="text-dark-200 cursor-pointer hover:text-white w-3" | |
onClick={() => onChangeOrder(index, index - 1)} | |
/> | |
)} | |
{index !== totalIcons - 1 && ( | |
<FontAwesomeIcon | |
icon={faCaretDown} | |
className="text-dark-200 cursor-pointer hover:text-white w-3" | |
onClick={() => onChangeOrder(index, index + 1)} | |
/> | |
)} | |
</div> | |
)} | |
</div> | |
); | |
}; | |