mirror of
https://github.com/LCE-Hub/LCE-Emerald-Launcher.git
synced 2026-09-24 17:11:00 +00:00
feat(versionsview): full redesign
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 72 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 216 KiB After Width: | Height: | Size: 8.5 KiB |
@@ -63,15 +63,6 @@ const DeleteConfirmButton = memo(function DeleteConfirmButton({
|
|||||||
|
|
||||||
const ROW_ESTIMATE = 52;
|
const ROW_ESTIMATE = 52;
|
||||||
const ROW_GAP = 4;
|
const ROW_GAP = 4;
|
||||||
|
|
||||||
function formatPlaytime(seconds: number): string {
|
|
||||||
const h = Math.floor(seconds / 3600);
|
|
||||||
const m = Math.floor((seconds % 3600) / 60);
|
|
||||||
if (h > 0) return `${h}h ${m}m`;
|
|
||||||
if (m > 0) return `${m}m`;
|
|
||||||
return seconds > 0 ? `${seconds}s` : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
const VersionsView = memo(function VersionsView() {
|
const VersionsView = memo(function VersionsView() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { setActiveView } = useUI();
|
const { setActiveView } = useUI();
|
||||||
@@ -123,11 +114,11 @@ const VersionsView = memo(function VersionsView() {
|
|||||||
} | null>(null);
|
} | null>(null);
|
||||||
const [isCustomizeModalOpen, setIsCustomizeModalOpen] = useState(false);
|
const [isCustomizeModalOpen, setIsCustomizeModalOpen] = useState(false);
|
||||||
const [customizeTarget, setCustomizeTarget] = useState<Edition | null>(null);
|
const [customizeTarget, setCustomizeTarget] = useState<Edition | null>(null);
|
||||||
const [playtimeMap, setPlaytimeMap] = useState<
|
const [_playtimeMap, setPlaytimeMap] = useState<
|
||||||
Record<string, PlaytimeResponse>
|
Record<string, PlaytimeResponse>
|
||||||
>({});
|
>({});
|
||||||
const [initialPath, setInitialPath] = useState<string>("");
|
const [initialPath, setInitialPath] = useState<string>("");
|
||||||
const [hoveredBtn, setHoveredBtn] = useState<{
|
const [_hoveredBtn, setHoveredBtn] = useState<{
|
||||||
row: number;
|
row: number;
|
||||||
btn: string;
|
btn: string;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
@@ -144,8 +135,13 @@ const VersionsView = memo(function VersionsView() {
|
|||||||
name: string;
|
name: string;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
const [argsSchemas, setArgsSchemas] = useState<Record<string, boolean>>({});
|
const [argsSchemas, setArgsSchemas] = useState<Record<string, boolean>>({});
|
||||||
|
const [thumb, setThumb] = useState({ height: 0, top: 0 });
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const listRef = useRef<HTMLDivElement>(null);
|
const listRef = useRef<HTMLDivElement>(null);
|
||||||
|
const trackRef = useRef<HTMLDivElement>(null);
|
||||||
|
const thumbRef = useRef<HTMLDivElement>(null);
|
||||||
|
const dragOffsetRef = useRef(0);
|
||||||
|
const draggingRef = useRef(false);
|
||||||
const menuRef = useRef<HTMLDivElement>(null);
|
const menuRef = useRef<HTMLDivElement>(null);
|
||||||
const [menuDir, setMenuDir] = useState<"down" | "up">("down");
|
const [menuDir, setMenuDir] = useState<"down" | "up">("down");
|
||||||
const ITEM_COUNT = visibleEditions.length + 3;
|
const ITEM_COUNT = visibleEditions.length + 3;
|
||||||
@@ -162,6 +158,63 @@ const VersionsView = memo(function VersionsView() {
|
|||||||
visibleEditionsRef.current = visibleEditions;
|
visibleEditionsRef.current = visibleEditions;
|
||||||
const focusIndexRef = useRef(focusIndex);
|
const focusIndexRef = useRef(focusIndex);
|
||||||
const openMenuIdRef = useRef(openMenuId);
|
const openMenuIdRef = useRef(openMenuId);
|
||||||
|
const updateThumb = useCallback(() => {
|
||||||
|
const container = listRef.current;
|
||||||
|
const track = trackRef.current;
|
||||||
|
if (!container || !track) return;
|
||||||
|
const scrollHeight = container.scrollHeight;
|
||||||
|
const clientHeight = container.clientHeight;
|
||||||
|
const scrollable = scrollHeight - clientHeight;
|
||||||
|
if (scrollable <= 0) {
|
||||||
|
setThumb((prev) =>
|
||||||
|
prev.height === 0 && prev.top === 0 ? prev : { height: 0, top: 0 },
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const trackHeight = track.clientHeight;
|
||||||
|
const HANDLE = 40;
|
||||||
|
const STEP = HANDLE + 4;
|
||||||
|
const maxTop = trackHeight - HANDLE;
|
||||||
|
const maxIndex = Math.max(1, Math.floor(maxTop / STEP));
|
||||||
|
const index = Math.min(
|
||||||
|
maxIndex,
|
||||||
|
Math.round((container.scrollTop / scrollable) * maxIndex),
|
||||||
|
);
|
||||||
|
const top = index * STEP;
|
||||||
|
setThumb((prev) =>
|
||||||
|
prev.top === top && prev.height === HANDLE
|
||||||
|
? prev
|
||||||
|
: { height: HANDLE, top },
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleThumbPointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
draggingRef.current = true;
|
||||||
|
dragOffsetRef.current =
|
||||||
|
e.clientY - e.currentTarget.getBoundingClientRect().top;
|
||||||
|
e.currentTarget.setPointerCapture(e.pointerId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleThumbPointerMove = (e: React.PointerEvent<HTMLDivElement>) => {
|
||||||
|
if (!draggingRef.current) return;
|
||||||
|
const container = listRef.current;
|
||||||
|
const track = trackRef.current;
|
||||||
|
if (!container || !track) return;
|
||||||
|
const trackRect = track.getBoundingClientRect();
|
||||||
|
const trackTop = trackRect.top + track.clientTop;
|
||||||
|
const y = e.clientY - trackTop - dragOffsetRef.current;
|
||||||
|
const trackHeight = track.clientHeight;
|
||||||
|
const maxTop = Math.max(0, trackHeight - thumb.height);
|
||||||
|
const scrollable = container.scrollHeight - container.clientHeight;
|
||||||
|
if (scrollable <= 0) return;
|
||||||
|
const clampedY = Math.min(Math.max(y, 0), maxTop);
|
||||||
|
container.scrollTop = (clampedY / maxTop) * scrollable;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleThumbPointerUp = () => {
|
||||||
|
draggingRef.current = false;
|
||||||
|
};
|
||||||
|
|
||||||
const updateVisibleRange = useCallback(() => {
|
const updateVisibleRange = useCallback(() => {
|
||||||
const container = listRef.current;
|
const container = listRef.current;
|
||||||
@@ -176,11 +229,8 @@ const VersionsView = memo(function VersionsView() {
|
|||||||
let end = -1;
|
let end = -1;
|
||||||
for (let i = 0; i < n; i++) {
|
for (let i = 0; i < n; i++) {
|
||||||
const itemTop = acc;
|
const itemTop = acc;
|
||||||
const itemBottom = acc + (heights[i] ?? ROW_ESTIMATE);
|
const itemNeo = acc + (heights[i] ?? ROW_ESTIMATE); //neo: me fr fr :3
|
||||||
if (
|
if (itemTop >= scrollTop && itemNeo <= scrollTop + viewportHeight) {
|
||||||
itemTop >= scrollTop &&
|
|
||||||
itemBottom <= scrollTop + viewportHeight
|
|
||||||
) {
|
|
||||||
if (start === n) start = i;
|
if (start === n) start = i;
|
||||||
end = i;
|
end = i;
|
||||||
}
|
}
|
||||||
@@ -204,7 +254,8 @@ const VersionsView = memo(function VersionsView() {
|
|||||||
setVisibleRange((prev) =>
|
setVisibleRange((prev) =>
|
||||||
prev.start === start && prev.end === end ? prev : { start, end },
|
prev.start === start && prev.end === end ? prev : { start, end },
|
||||||
);
|
);
|
||||||
}, []);
|
updateThumb();
|
||||||
|
}, [updateThumb]);
|
||||||
|
|
||||||
const setRowRef = (index: number) => (el: HTMLDivElement | null) => {
|
const setRowRef = (index: number) => (el: HTMLDivElement | null) => {
|
||||||
if (el) {
|
if (el) {
|
||||||
@@ -428,11 +479,8 @@ const VersionsView = memo(function VersionsView() {
|
|||||||
}, [installedVersions]);
|
}, [installedVersions]);
|
||||||
|
|
||||||
const handleEditionClick = (edition: Edition, index: number) => {
|
const handleEditionClick = (edition: Edition, index: number) => {
|
||||||
const isInstalled = installedVersions.includes(edition.instanceId);
|
|
||||||
if (isInstalled) {
|
|
||||||
playPressSound();
|
playPressSound();
|
||||||
setSelectedProfile(edition.instanceId);
|
setSelectedProfile(edition.instanceId);
|
||||||
}
|
|
||||||
setFocusIndex(index);
|
setFocusIndex(index);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -465,11 +513,11 @@ const VersionsView = memo(function VersionsView() {
|
|||||||
transition={{ duration: animationsEnabled ? 0.25 : 0 }}
|
transition={{ duration: animationsEnabled ? 0.25 : 0 }}
|
||||||
className="flex flex-col items-center w-full max-w-5xl outline-none"
|
className="flex flex-col items-center w-full max-w-5xl outline-none"
|
||||||
>
|
>
|
||||||
<div className="w-full min-w-[480px] p-6 mb-4 mc-options-bg">
|
<div className="w-full min-w-120 p-1 mb-3 mc-options-bg relative flex items-stretch">
|
||||||
<div
|
<div
|
||||||
ref={listRef}
|
ref={listRef}
|
||||||
onScroll={updateVisibleRange}
|
onScroll={updateVisibleRange}
|
||||||
className="w-full max-h-[48vh] overflow-y-auto snap-y snap-mandatory mc-versionrecess hidden-scrollbar"
|
className="flex-1 min-w-0 h-85 max-h-85 overflow-y-auto snap-y snap-mandatory mc-versionrecess hidden-scrollbar"
|
||||||
>
|
>
|
||||||
<div className="flex flex-col gap-1 hidden-scrollbar">
|
<div className="flex flex-col gap-1 hidden-scrollbar">
|
||||||
{visibleEditions.map((edition: Edition, i: number) => {
|
{visibleEditions.map((edition: Edition, i: number) => {
|
||||||
@@ -499,6 +547,10 @@ const VersionsView = memo(function VersionsView() {
|
|||||||
ref={setRowRef(i)}
|
ref={setRowRef(i)}
|
||||||
className={`w-[calc(100%-20px)] snap-start flex items-center gap-3 ${!isFocused ? "mc-button-ninesliced" : "mc-button-ninesliced-selected"} ${isComingSoon ? "opacity-50 cursor-not-allowed" : ""} relative ${openMenuId === edition.id ? "z-50" : "z-0"}`}
|
className={`w-[calc(100%-20px)] snap-start flex items-center gap-3 ${!isFocused ? "mc-button-ninesliced" : "mc-button-ninesliced-selected"} ${isComingSoon ? "opacity-50 cursor-not-allowed" : ""} relative ${openMenuId === edition.id ? "z-50" : "z-0"}`}
|
||||||
onMouseEnter={() => !isComingSoon && setFocusIndex(i)}
|
onMouseEnter={() => !isComingSoon && setFocusIndex(i)}
|
||||||
|
onMouseLeave={() => setFocusIndex(-1)} //neo: you see this, smartcmd?
|
||||||
|
onClick={() =>
|
||||||
|
!isComingSoon && handleEditionClick(edition, i)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<div className="w-12 h-12 flex items-center justify-center shrink-0 bg-[url(/images/empty.png)] bg-cover bg-center bg-no-repeat">
|
<div className="w-12 h-12 flex items-center justify-center shrink-0 bg-[url(/images/empty.png)] bg-cover bg-center bg-no-repeat">
|
||||||
{edition.logo ? (
|
{edition.logo ? (
|
||||||
@@ -524,55 +576,15 @@ const VersionsView = memo(function VersionsView() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
onClick={() =>
|
|
||||||
!isComingSoon && handleEditionClick(edition, i)
|
|
||||||
}
|
|
||||||
className={`flex-1 text-left min-w-0 outline-none rounded cursor-pointer ${isComingSoon ? "cursor-not-allowed" : ""}`}
|
className={`flex-1 text-left min-w-0 outline-none rounded cursor-pointer ${isComingSoon ? "cursor-not-allowed" : ""}`}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span
|
<span
|
||||||
className={"text-xl tracking-wide truncate text-white"}
|
className={`text-xl tracking-wide truncate ${!isFocused ? "text-white" : "text-[#ffff00]"}`}
|
||||||
style={{ textShadow: "none" }}
|
style={{ textShadow: "none" }}
|
||||||
>
|
>
|
||||||
{edition.name}
|
{edition.name}
|
||||||
</span>
|
</span>
|
||||||
{isInstalled && (
|
|
||||||
<button
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
playPressSound();
|
|
||||||
setPlaytimeTarget({
|
|
||||||
id: edition.instanceId,
|
|
||||||
name: edition.name,
|
|
||||||
});
|
|
||||||
setIsPlaytimeModalOpen(true);
|
|
||||||
}}
|
|
||||||
className="flex items-center gap-1.5 px-2 py-1 bg-black/60 border border-[#555] hover:border-[#FFFF55] group transition-colors flex-shrink-0"
|
|
||||||
title={t("versions.viewPlaytime")}
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
width="12"
|
|
||||||
height="12"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="#AAAAAA"
|
|
||||||
strokeWidth="2"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
className="group-hover:stroke-[#FFFF55] transition-colors"
|
|
||||||
>
|
|
||||||
<circle cx="12" cy="12" r="10" />
|
|
||||||
<polyline points="12 6 12 12 16 14" />
|
|
||||||
</svg>
|
|
||||||
<span className="text-xs text-[#AAAAAA] group-hover:text-[#FFFF55] leading-none transition-colors">
|
|
||||||
{playtimeMap[edition.instanceId]
|
|
||||||
? formatPlaytime(
|
|
||||||
playtimeMap[edition.instanceId].totalSeconds,
|
|
||||||
)
|
|
||||||
: ""}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
{edition.category &&
|
{edition.category &&
|
||||||
edition.category.map((cat: string) => (
|
edition.category.map((cat: string) => (
|
||||||
<span
|
<span
|
||||||
@@ -605,17 +617,10 @@ const VersionsView = memo(function VersionsView() {
|
|||||||
setHoveredBtn({ row: i, btn: "main" })
|
setHoveredBtn({ row: i, btn: "main" })
|
||||||
}
|
}
|
||||||
onMouseLeave={() => setHoveredBtn(null)}
|
onMouseLeave={() => setHoveredBtn(null)}
|
||||||
className={`w-9 h-9 flex items-center justify-center ${
|
className={
|
||||||
isDownloading ? "opacity-50" : ""
|
"w-10 h-10 flex items-center justify-center bg-[url(/images/empty.png)] bg-cover bg-no-repeat bg-center"
|
||||||
}`}
|
}
|
||||||
style={{
|
style={{
|
||||||
backgroundImage:
|
|
||||||
(hoveredBtn?.row === i &&
|
|
||||||
hoveredBtn?.btn === "main") ||
|
|
||||||
(focusIndex === i && focusBtn === 0)
|
|
||||||
? "url('/images/Button_Square_Highlighted.png')"
|
|
||||||
: "url('/images/Button_Square.png')",
|
|
||||||
backgroundSize: "100% 100%",
|
|
||||||
imageRendering: "pixelated",
|
imageRendering: "pixelated",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -626,7 +631,7 @@ const VersionsView = memo(function VersionsView() {
|
|||||||
: "/images/Download_Icon.png"
|
: "/images/Download_Icon.png"
|
||||||
}
|
}
|
||||||
alt=""
|
alt=""
|
||||||
className="w-5 h-5 object-contain"
|
className="w-8 h-8 object-contain"
|
||||||
style={{
|
style={{
|
||||||
imageRendering: "pixelated",
|
imageRendering: "pixelated",
|
||||||
filter: isDownloading
|
filter: isDownloading
|
||||||
@@ -648,31 +653,20 @@ const VersionsView = memo(function VersionsView() {
|
|||||||
setHoveredBtn({ row: i, btn: "menu" })
|
setHoveredBtn({ row: i, btn: "menu" })
|
||||||
}
|
}
|
||||||
onMouseLeave={() => setHoveredBtn(null)}
|
onMouseLeave={() => setHoveredBtn(null)}
|
||||||
className="w-9 h-9 flex flex-col items-center justify-center gap-1 transition-colors relative"
|
className="w-10 h-10 flex flex-col items-center justify-center gap-1 transition-colors relative bg-[url(/images/empty.png)] bg-cover bg-no-repeat bg-center"
|
||||||
style={{
|
style={{
|
||||||
backgroundImage:
|
|
||||||
(hoveredBtn?.row === i &&
|
|
||||||
hoveredBtn?.btn === "menu") ||
|
|
||||||
(focusIndex === i &&
|
|
||||||
(focusBtn === 0 || focusBtn === 1))
|
|
||||||
? "url('/images/Button_Square_Highlighted.png')"
|
|
||||||
: "url('/images/Button_Square.png')",
|
|
||||||
backgroundSize: "100% 100%",
|
|
||||||
imageRendering: "pixelated",
|
imageRendering: "pixelated",
|
||||||
filter: updatesAvailable?.[edition.instanceId]
|
|
||||||
? "drop-shadow(0 0 4px rgba(255,255,0,0.8))"
|
|
||||||
: "none",
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div className={"w-8 h-8"}>
|
||||||
className={`w-1.5 h-1.5 ${updatesAvailable?.[edition.instanceId] ? "bg-[#ffff55]" : "bg-white"}`}
|
<img
|
||||||
/>
|
src={
|
||||||
<div
|
updatesAvailable?.[edition.instanceId]
|
||||||
className={`w-1.5 h-1.5 ${updatesAvailable?.[edition.instanceId] ? "bg-[#ffff55]" : "bg-white"}`}
|
? "/images/Update_Icon.png"
|
||||||
/>
|
: "/images/gear.png"
|
||||||
<div
|
}
|
||||||
className={`w-1.5 h-1.5 ${updatesAvailable?.[edition.instanceId] ? "bg-[#ffff55]" : "bg-white"}`}
|
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{openMenuId === edition.id && (
|
{openMenuId === edition.id && (
|
||||||
@@ -1104,8 +1098,33 @@ const VersionsView = memo(function VersionsView() {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="w-full flex items-center justify-center gap-4 p-2 mt-1">
|
<div
|
||||||
|
ref={trackRef}
|
||||||
|
className="w-13 h-85 max-h-85 shrink-0 relative select-none mc-versionrecess"
|
||||||
|
>
|
||||||
|
{thumb.height > 0 && (
|
||||||
|
<div
|
||||||
|
ref={thumbRef}
|
||||||
|
onPointerDown={handleThumbPointerDown}
|
||||||
|
onPointerMove={handleThumbPointerMove}
|
||||||
|
onPointerUp={handleThumbPointerUp}
|
||||||
|
onPointerCancel={handleThumbPointerUp}
|
||||||
|
className="absolute left-1/2 -translate-x-[30px] w-10 h-10 mc-options-bg cursor-pointer"
|
||||||
|
style={{ top: thumb.top - 12 }}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
//neo: oh my goodness this took me a lot to figure out, anyway its just a transparent <hr> so the div isnt empty so that the border-image renders
|
||||||
|
}
|
||||||
|
<hr style={{ color: "transparent" }} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!isAndroid && (
|
||||||
|
<div className="absolute left-3 bottom-6 z-10 flex items-center gap-1 pb-1 pr-1 w-fit mc-help">
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
playPressSound();
|
playPressSound();
|
||||||
@@ -1114,15 +1133,8 @@ const VersionsView = memo(function VersionsView() {
|
|||||||
}}
|
}}
|
||||||
onMouseEnter={() => setFocusIndex(visibleEditions.length)}
|
onMouseEnter={() => setFocusIndex(visibleEditions.length)}
|
||||||
onMouseLeave={() => setHoveredBtn(null)}
|
onMouseLeave={() => setHoveredBtn(null)}
|
||||||
className="w-8 h-8 flex items-center justify-center text-[#3a3a3a]"
|
className="w-8 h-8 flex items-center justify-center text-[#333333] mc-optionbutton"
|
||||||
style={{
|
style={{
|
||||||
backgroundImage:
|
|
||||||
(hoveredBtn?.row === visibleEditions.length &&
|
|
||||||
hoveredBtn?.btn === "add") ||
|
|
||||||
focusIndex === visibleEditions.length
|
|
||||||
? "url('/images/Button_Square_Highlighted.png')"
|
|
||||||
: "url('/images/Button_Square.png')",
|
|
||||||
backgroundSize: "100% 100%",
|
|
||||||
imageRendering: "pixelated",
|
imageRendering: "pixelated",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -1139,7 +1151,6 @@ const VersionsView = memo(function VersionsView() {
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{!isAndroid && (
|
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
playPressSound();
|
playPressSound();
|
||||||
@@ -1148,29 +1159,26 @@ const VersionsView = memo(function VersionsView() {
|
|||||||
onMouseEnter={() => setFocusIndex(visibleEditions.length + 1)}
|
onMouseEnter={() => setFocusIndex(visibleEditions.length + 1)}
|
||||||
onMouseLeave={() => setHoveredBtn(null)}
|
onMouseLeave={() => setHoveredBtn(null)}
|
||||||
title={t("modals.customTu.importTitle")}
|
title={t("modals.customTu.importTitle")}
|
||||||
className="w-8 h-8 flex items-center justify-center text-[#3a3a3a]"
|
className="w-8 h-8 flex items-center justify-center text-[#333333] mc-optionbutton"
|
||||||
style={{
|
style={{
|
||||||
backgroundImage:
|
|
||||||
(hoveredBtn?.row === visibleEditions.length &&
|
|
||||||
hoveredBtn?.btn === "folder_import") ||
|
|
||||||
focusIndex === visibleEditions.length + 1
|
|
||||||
? "url('/images/Button_Square_Highlighted.png')"
|
|
||||||
: "url('/images/Button_Square.png')",
|
|
||||||
backgroundSize: "100% 100%",
|
|
||||||
imageRendering: "pixelated",
|
imageRendering: "pixelated",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src="/images/Folder_Icon.png"
|
src="/images/Folder_Icon.png"
|
||||||
alt={t("modals.customTu.importTitle")}
|
alt={t("modals.customTu.importTitle")}
|
||||||
className="w-5 h-5 object-contain"
|
className="w-5 h-5 object-contain invert"
|
||||||
style={{ imageRendering: "pixelated" }}
|
style={{ imageRendering: "pixelated" }}
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
<div className="mc-help text-xl text-[#FFFFFF] max-h-24 min-h-24 overflow-hidden px-4 w-280">
|
||||||
|
{visibleEditions[focusIndex]?.desc ??
|
||||||
|
visibleEditions.find((e) => e.id == useConfig().profile)?.desc ??
|
||||||
|
""}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!isAndroid && (
|
{!isAndroid && (
|
||||||
@@ -1182,7 +1190,7 @@ const VersionsView = memo(function VersionsView() {
|
|||||||
playBackSound();
|
playBackSound();
|
||||||
setActiveView("main");
|
setActiveView("main");
|
||||||
}}
|
}}
|
||||||
className="w-48 h-10 flex items-center justify-center text-xl mc-text-shadow outline-none border-none text-white"
|
className="w-48 h-10 flex items-center justify-center text-xl mc-text-shadow text-white"
|
||||||
style={{
|
style={{
|
||||||
backgroundImage:
|
backgroundImage:
|
||||||
focusIndex === visibleEditions.length + 2
|
focusIndex === visibleEditions.length + 2
|
||||||
|
|||||||
+10
-2
@@ -82,6 +82,13 @@
|
|||||||
image-rendering: pixelated;
|
image-rendering: pixelated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mc-optionbutton {
|
||||||
|
appearance: none;
|
||||||
|
background: url("/images/options_background.png") center / 100% 100%
|
||||||
|
no-repeat;
|
||||||
|
image-rendering: pixelated;
|
||||||
|
}
|
||||||
|
|
||||||
.mc-button-ninesliced {
|
.mc-button-ninesliced {
|
||||||
/*neo: tysm https://leanrada.com/9-slicer/ */
|
/*neo: tysm https://leanrada.com/9-slicer/ */
|
||||||
border-image: url("/images/Button_Background.png") 13 13 13 13 fill / 13px
|
border-image: url("/images/Button_Background.png") 13 13 13 13 fill / 13px
|
||||||
@@ -104,7 +111,8 @@
|
|||||||
|
|
||||||
.mc-versionrecess {
|
.mc-versionrecess {
|
||||||
/*neo: tysm https://leanrada.com/9-slicer/ */
|
/*neo: tysm https://leanrada.com/9-slicer/ */
|
||||||
border-image: url("/images/versionrecess.png") 12 30 30 12 fill / 12px 30px 30px 12px;
|
border-image: url("/images/versionrecess.png") 12 30 30 12 fill / 12px 30px
|
||||||
|
30px 12px;
|
||||||
border-width: 12px;
|
border-width: 12px;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
@@ -113,7 +121,7 @@
|
|||||||
|
|
||||||
.mc-help {
|
.mc-help {
|
||||||
/*neo: tysm https://leanrada.com/9-slicer/ */
|
/*neo: tysm https://leanrada.com/9-slicer/ */
|
||||||
border-image: url("/images/helpbg.png") 5 15 17 6 fill / 5px 15px 17px 6px;
|
border-image: url("/images/helpbg.png") 7 16 16 7 fill / 7px 16px 16px 7px;
|
||||||
border-width: 12px;
|
border-width: 12px;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
|
|||||||
+4
-1
@@ -309,6 +309,7 @@ export default function App() {
|
|||||||
"model-editor",
|
"model-editor",
|
||||||
"swf-editor",
|
"swf-editor",
|
||||||
"goldmapper",
|
"goldmapper",
|
||||||
|
"versions", //neo: didnt expect that but oh well
|
||||||
]);
|
]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleContextMenu = (e: MouseEvent) => e.preventDefault();
|
const handleContextMenu = (e: MouseEvent) => e.preventDefault();
|
||||||
@@ -776,7 +777,9 @@ export default function App() {
|
|||||||
style={{ fontWeight: "normal" }}
|
style={{ fontWeight: "normal" }}
|
||||||
>
|
>
|
||||||
<div className="flex-1 text-left whitespace-nowrap">
|
<div className="flex-1 text-left whitespace-nowrap">
|
||||||
{t("app.version", { version: `${pkg.version} (${__BUILD_DATE__})` })}
|
{t("app.version", {
|
||||||
|
version: `${pkg.version} (${__BUILD_DATE__})`,
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 text-right whitespace-nowrap">
|
<div className="flex-1 text-right whitespace-nowrap">
|
||||||
{connected && t("app.controllerConnected")}
|
{connected && t("app.controllerConnected")}
|
||||||
|
|||||||
Reference in New Issue
Block a user