1
0
mirror of https://github.com/barthuijgen/factorio-sites.git synced 2025-03-17 21:17:57 +02:00

Added FactorioCode component for blueprint titles

This commit is contained in:
Bart 2021-03-17 16:23:28 +01:00
parent 4bdf7fc28e
commit 3ab956d122
10 changed files with 145 additions and 110 deletions

View File

@ -1,4 +1,5 @@
*.Dockerfile
/credentials
/.vscode
/.cache
/.cache
/node_modules

View File

@ -1,59 +0,0 @@
import parser, { Tag } from "bbcode-to-react";
import { FactorioIcon } from "./FactorioIcon";
class ImgTag extends Tag {
toReact() {
const content = this.getContent(true);
const img = (this.params as Record<string, string>).img;
const [type, item] = img.split("/");
if (type === "item") {
return (
<>
<FactorioIcon type={type} icon={item} size={20} />
{content && <span>{content}</span>}
</>
);
}
return null;
}
}
class ItemTag extends Tag {
toReact() {
const content = this.getContent(true);
const item = (this.params as Record<string, string>).item;
if (item) {
return (
<>
<FactorioIcon type="item" icon={item} size={20} />
{content && <span>{content}</span>}
</>
);
}
return null;
}
}
class VirtualSignalTag extends Tag {
toReact() {
const signal = (this.params as Record<string, string>)["virtual-signal"];
const content = this.getContent(true);
if (signal) {
return (
<>
<FactorioIcon type="signal" icon={signal} size={20} />
{content && <span>{content}</span>}
</>
);
}
return null;
}
}
parser.registerTag("img", ImgTag as typeof Tag);
parser.registerTag("item", ItemTag as typeof Tag);
parser.registerTag("virtual-signal", VirtualSignalTag as typeof Tag);
export const BBCode: React.FC<{ code: string }> = ({ code }) => {
return <>{parser.toReact(code)}</>;
};

View File

@ -2,7 +2,7 @@ import { css } from "@emotion/react";
import Link from "next/link";
import { ChildTreeBlueprintBookEnriched } from "@factorio-sites/web-utils";
import { FactorioIcon } from "./FactorioIcon";
import { BBCode } from "./BBCode";
import { FactorioCode } from "./FactorioCode";
const componentStyles = css`
.blueprint,
@ -51,7 +51,7 @@ export const BookChildTree: React.FC<BookChildTreeProps> = ({
/>
))}
<span className="label">
<BBCode code={blueprint_book.name || ""} />
<FactorioCode code={blueprint_book.name || ""} />
</span>
</a>
</Link>
@ -70,7 +70,7 @@ export const BookChildTree: React.FC<BookChildTreeProps> = ({
/>
))}
<span className="label">
<BBCode code={child.name || ""} />
<FactorioCode code={child.name || ""} />
</span>
</a>
</Link>

View File

@ -0,0 +1,107 @@
import { Box } from "@chakra-ui/layout";
import styled from "@emotion/styled";
import { IconSignalTypes } from "@factorio-sites/types";
import { ReactNode } from "react";
import { FactorioIcon } from "./FactorioIcon";
const ICON_TYPES = ["entity", "item", "recipe", "fluid", "virtual-signal", "img"];
const ICON_REGEX = new RegExp(
`(\\[(?<type>${ICON_TYPES.join(
"|"
)})=(?<icon>.*?)\\]|\\[color=(?<color>.*?)\\](?<content>.*?)\\[\\/color\\])`,
"g"
);
const RBG_COLOR_REGEX = /^\d+,\d+,\d+$/;
const IMG_ICON_REGEX = /^\[img=(.*?)\/(.*?)\]$/;
interface Match {
value: string;
start: number;
end: number;
groups: Record<string, string | undefined>;
}
const regexMatchAll = (string: string, regexp: RegExp): Match[] => {
const matches = [];
let match;
while ((match = ICON_REGEX.exec(string)) !== null) {
matches.push({
value: match[0],
start: match.index,
end: ICON_REGEX.lastIndex,
groups: match.groups || {},
});
}
return matches;
};
const parseFactorioCode = (string: string): ReactNode => {
const iconMatches = regexMatchAll(string, ICON_REGEX);
if (!iconMatches.length) return <span>{string}</span>;
// console.log(string, iconMatches);
const result = [] as ReactNode[];
let lastHandledIndex = 0;
iconMatches.forEach((match) => {
if (match.start > lastHandledIndex) {
let content = string.substr(lastHandledIndex, match.start - lastHandledIndex);
content = content.replace(/ /g, "\u00A0");
result.push(<span key={lastHandledIndex}>{content}</span>);
}
if (match.groups.color && match.groups.content) {
if (match.groups.color.match(RBG_COLOR_REGEX)) {
match.groups.color = `rgb(${match.groups.color})`;
}
result.push(
<span
key={match.start}
css={{ color: match.groups.color, display: "inline-flex", alignItems: "center" }}
>
{parseFactorioCode(match.groups.content)}
</span>
);
} else if (match.groups.type && match.groups.icon) {
if (match.groups.type === "img") {
const [, type, icon] = match.value.match(IMG_ICON_REGEX) || [];
if (type && icon) {
match.groups.type = type;
match.groups.icon = icon;
}
console.log(match);
}
result.push(
<FactorioIcon
key={match.start}
type={match.groups.type as IconSignalTypes}
icon={match.groups.icon as string}
size={20}
/>
);
} else {
console.warn("[FactorioCode] Match without proper capture groups", match);
}
lastHandledIndex = match.end;
});
if (lastHandledIndex < string.length) {
result.push(<span key={lastHandledIndex}>{string.substr(lastHandledIndex)}</span>);
}
return result;
};
const StyledBox = styled(Box)`
display: inline-flex;
align-items: center;
`;
export const FactorioCode: React.FC<{ code: string }> = ({ code }) => {
return <StyledBox>{parseFactorioCode(code)}</StyledBox>;
};

View File

@ -1,7 +1,7 @@
import { IconSignalTypes } from "@factorio-sites/types";
interface FactorioIconProps {
type: IconSignalTypes | "signal";
type: IconSignalTypes | "signal" | "virtual-signal" | "entity" | "recipe";
icon: string;
size: number;
}
@ -12,15 +12,17 @@ function getUrlByType(type: FactorioIconProps["type"], icon: string) {
return `https://storage.googleapis.com/factorio-blueprints-assets/factorio/graphics/icons/${icon}.png`;
case "fluid":
return `https://storage.googleapis.com/factorio-blueprints-assets/factorio/graphics/icons/fluid/${icon}.png`;
case "virtual":
case "virtual-signal":
case "signal":
return `https://storage.googleapis.com/factorio-blueprints-assets/factorio/graphics/icons/signal/${icon.replace(
/-/,
"_"
)}.png`;
case "virtual":
return null;
// case "virtual":
// return null;
default:
console.log("icon type not found", type, icon);
// console.log("icon type not found", type, icon);
return null;
}
}
@ -33,7 +35,10 @@ const ICON_RENAMES: Record<string, string> = {
export const FactorioIcon: React.FC<FactorioIconProps> = ({ type, icon, size }) => {
if (ICON_RENAMES[icon]) icon = ICON_RENAMES[icon];
const url = getUrlByType(type, icon);
if (!url) return null;
if (!url) {
// console.warn(`No icon for type ${type} icon ${icon}`);
return <span css={{ color: "#ffa700" }}>[{icon}]</span>;
}
return (
<div
css={{

View File

@ -1,5 +1,4 @@
import React, { useEffect, useState } from "react";
import BBCode from "bbcode-to-react";
import { Grid, Box } from "@chakra-ui/react";
import { Blueprint as IBlueprint, BlueprintPage, BlueprintStringData } from "@factorio-sites/types";
import { chakraResponsive, parseBlueprintStringClient } from "@factorio-sites/web-utils";
@ -14,6 +13,7 @@ import { BlueprintData } from "./BlueprintData";
import { BlueprintInfo } from "./BlueprintInfo";
import { BlueprintTags } from "./BlueprintTags";
import { BlueprintEntities } from "./BlueprintEntities";
import { FactorioCode } from "../FactorioCode";
const StyledBlueptintPage = styled(Grid)`
grid-gap: 16px;
@ -154,7 +154,7 @@ export const BlueprintSubPage: React.FC<BlueprintProps> = ({
title={
<span>
Components for{" "}
{data?.blueprint?.label ? BBCode.toReact(data.blueprint.label) : "blueprint"}
{data?.blueprint?.label ? <FactorioCode code={data.blueprint.label} /> : "blueprint"}
</span>
}
>

View File

@ -1,5 +1,4 @@
import React, { useEffect, useState } from "react";
import BBCode from "bbcode-to-react";
import { Box, Grid } from "@chakra-ui/react";
import styled from "@emotion/styled";
import {
@ -25,23 +24,17 @@ import { BlueprintData } from "./BlueprintData";
import { BlueprintInfo } from "./BlueprintInfo";
import { BlueprintTags } from "./BlueprintTags";
import { BlueprintEntities } from "./BlueprintEntities";
import { FactorioCode } from "../FactorioCode";
const StyledBlueptintPage = styled(Grid)`
grid-gap: 16px;
.title {
position: relative;
width: 100%;
display: flex;
align-items: center;
.text {
white-space: nowrap;
width: calc(100% - 120px);
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
}
.title .text {
white-space: nowrap;
width: calc(100% - 120px);
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
}
.panel {
@ -143,10 +136,10 @@ export const BlueprintBookSubPage: React.FC<BlueprintBookSubPageProps> = ({
<Panel
className="child-tree"
title={
<div className="title">
<>
<span className="text">{blueprint_page.title}</span>
<FavoriteButton is_favorite={favorite} blueprint_page_id={blueprint_page.id} />
</div>
</>
}
gridColumn={chakraResponsive({ mobile: "1", desktop: "1 / span 2" })}
gridRow={chakraResponsive({ mobile: "2", desktop: "1" })}
@ -167,7 +160,7 @@ export const BlueprintBookSubPage: React.FC<BlueprintBookSubPageProps> = ({
gridColumn={chakraResponsive({ mobile: "1", desktop: "3 / span 2" })}
gridRow={chakraResponsive({ mobile: "1", desktop: "1" })}
title={
<div className="title">
<>
<span>Image</span>
<img
src="/fbe.svg"
@ -190,7 +183,7 @@ export const BlueprintBookSubPage: React.FC<BlueprintBookSubPageProps> = ({
/>
)}
</Box>
</div>
</>
}
>
{selectedBlueprintString && <ImageEditor string={selectedBlueprintString}></ImageEditor>}
@ -201,10 +194,10 @@ export const BlueprintBookSubPage: React.FC<BlueprintBookSubPageProps> = ({
gridColumn={chakraResponsive({ mobile: "1", desktop: "1 / span 2" })}
gridRow={chakraResponsive({ mobile: null, desktop: "2 / span 2" })}
title={
<div className="title">
<>
<span className="text">Description </span>
<FavoriteButton is_favorite={favorite} blueprint_page_id={blueprint_page.id} />
</div>
</>
}
>
<StyledMarkdown>{blueprint_page.description_markdown}</StyledMarkdown>
@ -233,9 +226,11 @@ export const BlueprintBookSubPage: React.FC<BlueprintBookSubPageProps> = ({
title={
<span>
Components for{" "}
{selectedData?.blueprint?.label
? BBCode.toReact(selectedData.blueprint.label)
: "blueprint"}
{selectedData?.blueprint?.label ? (
<FactorioCode code={selectedData.blueprint.label} />
) : (
"blueprint"
)}
</span>
}
>

View File

@ -111,11 +111,11 @@ export const Index: NextPage<IndexProps> = ({
<Box>
<RadioGroup
onChange={(value: string) => router.push(routerQueryToHref({ order: value }))}
value={(router.query.order as string) || "date"}
value={(router.query.order as string) || "favorites"}
>
<Stack>
<Radio value="date">Last updated</Radio>
<Radio value="favorites">Favorites</Radio>
<Radio value="date">Last updated</Radio>
</Stack>
</RadioGroup>
</Box>
@ -203,7 +203,7 @@ export async function getServerSideProps({ query }: NextPageContext) {
await init();
const page = Number(query.page || "1");
const perPage = Number(query["per-page"] || "20");
const order = (query["order"] as string) || "date";
const order = (query["order"] as string) || "favorites";
const _query = query.q ? String(query.q) : undefined;
const tags = query.tags ? String(query.tags).split(",") : undefined;
const entities = query.entities ? String(query.entities).split(",") : undefined;

View File

@ -39,7 +39,6 @@
"@google-cloud/storage": "5.8.1",
"@hookstate/core": "3.0.6",
"@prisma/client": "2.19.0",
"bbcode-to-react": "0.2.9",
"bcrypt": "5.0.1",
"clsx": "1.1.1",
"cookie": "0.4.1",
@ -78,7 +77,6 @@
"@nrwl/web": "11.5.1",
"@nrwl/workspace": "11.5.1",
"@testing-library/react": "11.2.5",
"@types/bbcode-to-react": "0.2.0",
"@types/bcrypt": "3.0.0",
"@types/cookie": "0.4.0",
"@types/jest": "26.0.20",

View File

@ -4071,13 +4071,6 @@
dependencies:
"@babel/types" "^7.3.0"
"@types/bbcode-to-react@0.2.0":
version "0.2.0"
resolved "https://registry.yarnpkg.com/@types/bbcode-to-react/-/bbcode-to-react-0.2.0.tgz#7aa9e5c8ba8fea5b1e4968b96413aab426b0c4b7"
integrity sha512-qcrZwvQgwiEdZbola1LrGghV7fGxHQCM09Pa/cRVmjpD3dOkE8N/H2S9kuJf1O8wg7AM1J80USyi3pbr1Ra0uQ==
dependencies:
"@types/react" "*"
"@types/bcrypt@3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/bcrypt/-/bcrypt-3.0.0.tgz#851489a9065a067cb7f3c9cbe4ce9bed8bba0876"
@ -5316,11 +5309,6 @@ batch@0.6.1:
resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16"
integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=
bbcode-to-react@0.2.9:
version "0.2.9"
resolved "https://registry.yarnpkg.com/bbcode-to-react/-/bbcode-to-react-0.2.9.tgz#75ec5358c78e9c2a486a62274bb5f4695fb99e60"
integrity sha512-ZuqVg44xi0nqMbZJMB/j1WxVvDnpfQYHLGFdZW8FQfW7uoCMyF48iEUVbYontcdrD5uZTcqMs3qbGeIa/bCi+g==
bcrypt-pbkdf@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"