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

[WIP] factorio-style-components (#34)

* Added factorio style button

* Added panel inset panel, button css improvements

* Fix my blueprints list and edit button

* Comments related fixes

* Fixing to guidelines and small style adjustments

Co-authored-by: Bart <45095973+barthuijgen@users.noreply.github.com>
This commit is contained in:
Alexander Horbunov 2021-03-13 00:10:32 +02:00 committed by GitHub
parent 9ce9c4b4df
commit 7278f23f48
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 185 additions and 80 deletions

View File

@ -0,0 +1,82 @@
import React from "react";
import styled from "@emotion/styled";
import clsx from "clsx";
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
primary?: boolean;
}
export const Button: React.FC<ButtonProps> = ({ primary, className, children, ...props }) => {
return (
<StyledButton className={clsx({ primary }, className)} {...props}>
{children}
</StyledButton>
);
};
const StyledButton = styled.button`
background-color: #8e8e8e;
padding: 10px 12px;
text-align: left;
color: #000;
font-weight: 600;
display: inline-block;
border: none;
line-height: 100%;
vertical-align: middle;
white-space: nowrap;
box-shadow: inset 8px 0 4px -8px #000, inset -8px 0 4px -8px #000, inset 0 10px 2px -8px #e3e3e3,
inset 0 10px 2px -8px #282828, inset 0 -9px 2px -8px #000, 0 0 4px 0 #000;
position: relative;
cursor: pointer;
user-select: none;
height: 36px;
outline: none;
&.primary {
background-color: #5eb663;
}
&.danger {
background-color: #fe5a5a;
box-shadow: inset 8px 0 4px -8px #000, inset -8px 0 4px -8px #000, inset 0 10px 2px -8px #fda1a1,
inset 0 10px 2px -8px #8b0101, inset 0 -9px 2px -8px #000, 0 0 4px 0 #000;
}
&:hover {
color: #000;
text-decoration: none;
outline: 0;
box-shadow: inset 8px 0 4px -8px #000, inset -8px 0 4px -8px #000, inset 0 9px 2px -8px #fff,
inset 0 8px 4px -8px #000, inset 0 -8px 4px -8px #000, inset 0 -9px 2px -8px #432400,
0 0 4px 0 #000, inset 0 0 4px 2px #f9b44b;
background-color: #e39827;
filter: drop-shadow(0 0 2px #f9b44b);
}
&:active {
position: relative;
padding-top: 12px;
padding-bottom: 8px;
vertical-align: -2px;
box-shadow: inset 0 10px 2px -8px #000, inset 0 9px 2px -8px #000, inset 8px 0 4px -8px #563a10,
inset 8px 0 4px -8px #563a10, inset -8px 0 4px -8px #563a10, inset -8px 0 4px -8px #563a10,
inset 0 9px 2px -8px #563a10, inset 0 -9px 2px -8px #563a10, inset 0 -8.5px 0 -8px #563a10,
0 0 4px 0 #000;
background-color: #f1be64;
filter: none;
outline: 0;
}
&:disabled {
padding-top: 10px;
padding-bottom: 10px;
cursor: default;
vertical-align: 0;
background-color: #3d3d3d;
color: #818181;
box-shadow: inset 8px 0 4px -8px #000, inset -8px 0 4px -8px #000, inset 0 8px 4px -8px #000,
inset 0 -6px 4px -8px #818181, inset 0 -8px 4px -8px #000, 0 0 4px 0 #000;
filter: none;
}
`;

View File

@ -1,6 +1,7 @@
import React, { useMemo, useState } from "react";
import { Button, ButtonProps } from "@chakra-ui/react";
import { MdCheck, MdClose } from "react-icons/md";
import { IoMdClipboard } from "react-icons/io";
import { Button, ButtonProps } from "../components/Button";
const SUCCESS_ICON_DURATION = 2000;
@ -8,44 +9,45 @@ export const CopyButton: React.FC<
Omit<ButtonProps, "children"> & { content: string; label?: string }
> = ({ content, label, ...props }) => {
const [loading, setLoading] = useState(false);
const [icon, setIcon] = useState<"red" | "green" | null>(null);
const [iconType, setIconType] = useState<"success" | "error" | null>(null);
const iconProps = useMemo(() => {
if (icon === "green") {
return {
colorScheme: "green",
leftIcon: <MdCheck />,
};
} else if (icon === "red") {
return {
colorScheme: "red",
leftIcon: <MdClose />,
};
} else {
return { colorScheme: "green" };
const icon = useMemo(() => {
switch (iconType) {
case "success":
return <MdCheck />;
case "error":
return <MdClose />;
default:
return <IoMdClipboard />;
}
}, [icon]);
}, [iconType]);
const handleClick = async () => {
setLoading(true);
try {
await navigator.clipboard.writeText(content);
setIconType("success");
setTimeout(() => setIconType(null), SUCCESS_ICON_DURATION);
setLoading(false);
} catch (err) {
setIconType("error");
setLoading(false);
}
};
return (
<Button
{...props}
isLoading={loading}
{...iconProps}
onClick={() => {
setLoading(true);
navigator.clipboard
.writeText(content)
.then(() => {
setLoading(false);
setIcon("green");
setTimeout(() => setIcon(null), SUCCESS_ICON_DURATION);
})
.catch(() => {
setLoading(false);
setIcon("red");
});
css={{
display: "inline-flex",
minWidth: "128px",
}}
disabled={loading}
{...props}
onClick={handleClick}
>
<span className="icon" css={{ marginRight: "5px" }}>
{icon}
</span>
{label || "copy"}
</Button>
);

View File

@ -23,17 +23,30 @@ const panelStyles = css`
align-items: center;
}
`;
const panelInsetStyles = css`
padding: 4px;
box-shadow: inset 0 0 3px 0 #000, 0 -2px 2px -1px #000, -2px 0 2px -2px #28221f,
-2px 0 2px -2px #28221f, 2px 0 2px -2px #28221f, 2px 0 2px -2px #28221f, 0 3px 3px -3px #8f8c8b,
0 2px 2px -2px #8f8c8b, 0 1px 1px -1px #8f8c8b;
background-color: #242324;
margin-top: 12px;
`;
const boxShadow = `inset 0 0 3px 0 #000, 0 -2px 2px -1px #000, -2px 0 2px -2px #28221f,
-2px 0 2px -2px #28221f, 2px 0 2px -2px #28221f, 2px 0 2px -2px #28221f,
0 3px 3px -3px #8f8c8b, 0 2px 2px -2px #8f8c8b, 0 1px 1px -1px #8f8c8b`;
export const Panel: React.FC<
Omit<BoxProps, "title"> & { title?: ReactNode; css?: SerializedStyles }
> = ({ children, title, css: prop_css, ...props }) => (
Omit<BoxProps, "title" | "bottom"> & {
title?: ReactNode;
css?: SerializedStyles;
bottom?: ReactNode;
}
> = ({ children, title, bottom, css: prop_css, ...props }) => (
<Box css={prop_css ? [panelStyles, prop_css] : [panelStyles]} {...props}>
{title ? <h2>{title}</h2> : null}
{title && <h2>{title}</h2>}
<Box color="white" height="100%" padding="12px" bg="#414040" boxShadow={boxShadow}>
{children}
</Box>
{bottom && <Box css={panelInsetStyles}>{bottom}</Box>}
</Box>
);

View File

@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react";
import { NextPage } from "next";
import Link from "next/link";
import BBCode from "bbcode-to-react";
import { Button, Grid, Image, Box } from "@chakra-ui/react";
import { Grid, Image, Box } from "@chakra-ui/react";
import {
getBlueprintBookById,
getBlueprintById,
@ -32,6 +32,7 @@ import { pageHandler } from "../../utils/page-handler";
import styled from "@emotion/styled";
import { css } from "@emotion/react";
import { AiOutlineHeart, AiFillHeart } from "react-icons/ai";
import { Button } from "../../components/Button";
type Selected =
| { type: "blueprint"; data: Pick<Blueprint, "id" | "blueprint_hash" | "image_hash" | "label"> }
@ -157,11 +158,13 @@ export const Index: NextPage<IndexProps> = ({
<span className="text">{blueprint_page.title}</span>
{auth && (
<Button
colorScheme="green"
onClick={onClickFavorite}
css={{ position: "absolute", right: "10px", top: "-7px", height: "35px" }}
css={{ display: "inline-flex", float: "right", fontSize: "initial" }}
>
Favorite {isFavorite ? <AiFillHeart /> : <AiOutlineHeart />}
Favorite
<span className="icon" css={{ marginLeft: "5px" }}>
{isFavorite ? <AiFillHeart /> : <AiOutlineHeart />}
</span>
</Button>
)}
</div>
@ -220,21 +223,16 @@ export const Index: NextPage<IndexProps> = ({
</tbody>
</StyledTable>
</Box>
<Box css={{ marginLeft: "1rem" }}>
{selected.data.blueprint_hash && typeof window !== "undefined" && (
<CopyButton
label="copy url"
content={`${window.location.origin}/api/string/${selected.data.blueprint_hash}`}
marginBottom="0.5rem"
/>
<Box primary css={{ marginLeft: "1rem" }}>
{selectedBlueprintString && (
<CopyButton primary label="Copy Blueprint" content={selectedBlueprintString} />
)}
</Box>
<Box css={{ marginLeft: "1rem" }}>
{selectedBlueprintString && (
{selected.data.blueprint_hash && typeof window !== "undefined" && (
<CopyButton
label="copy blueprint"
content={selectedBlueprintString}
marginBottom="0.5rem"
label="Copy URL"
content={`${window.location.origin}/api/string/${selected.data.blueprint_hash}`}
/>
)}
</Box>
@ -319,16 +317,14 @@ export const Index: NextPage<IndexProps> = ({
>
<Box>
<Button
colorScheme="green"
onClick={() => {
setShowDetails(showDetails === "string" ? "none" : "string");
}}
>
{showDetails === "string" ? "hide" : "show"} string
{showDetails === "string" ? "Hide" : "Show"} string
</Button>
<Button
css={{ marginLeft: "1rem" }}
colorScheme="green"
onClick={() => {
setShowDetails(showDetails === "json" ? "none" : "json");
if (!selectedData) {
@ -341,7 +337,7 @@ export const Index: NextPage<IndexProps> = ({
}
}}
>
{showDetails === "json" ? "hide" : "show"} json
{showDetails === "json" ? "Hide" : "Show"} json
</Button>
</Box>
<Box css={{ marginTop: "1rem" }}>

View File

@ -72,7 +72,7 @@ export const Index: NextPage<IndexProps> = ({
borderRight: "1px solid #b7b7b7",
paddingRight: "1rem",
marginRight: "1rem",
width: "250px",
width: "213px",
}}
>
<Box>
@ -167,11 +167,13 @@ export const Index: NextPage<IndexProps> = ({
/>
</Box>
</Box>
<Box>
<Box css={{ display: "flex", flexWrap: "wrap", minHeight: "400px" }}>
{blueprints.map((bp) => (
<BlueprintLink key={bp.id} blueprint={bp} type="tile" />
))}
<Box css={{ display: "flex", flexDirection: "column" }}>
<Box css={{ display: "flex", flexWrap: "wrap", minHeight: "400px", flexGrow: 1 }}>
{blueprints.length ? (
blueprints.map((bp) => <BlueprintLink key={bp.id} blueprint={bp} type="tile" />)
) : (
<p css={{ marginTop: "10px" }}>No results found</p>
)}
</Box>
<Box css={{ marginTop: "15px" }}>
<Pagination page={currentPage} totalPages={totalPages} totalItems={totalItems} />

View File

@ -7,7 +7,6 @@ import {
FormErrorMessage,
Input,
SimpleGrid,
Button,
Box,
Text,
Textarea,
@ -20,6 +19,7 @@ import { Panel } from "../../components/Panel";
import { validateCreateBlueprintForm } from "../../utils/validate";
import { ImageEditor } from "../../components/ImageEditor";
import { Select } from "../../components/Select";
import { Button } from "../../components/Button";
import { pageHandler } from "../../utils/page-handler";
const FieldStyle = css`
@ -131,7 +131,7 @@ export const UserBlueprintCreate: NextPage = () => {
</Field>
<Box css={{ display: "flex", alignItems: "center" }}>
<Button type="submit" colorScheme="green" disabled={isSubmitting}>
<Button primary type="submit" disabled={isSubmitting}>
Submit
</Button>
{status && <Text css={{ marginLeft: "1rem", color: "red" }}>{status}</Text>}

View File

@ -9,7 +9,6 @@ import {
FormErrorMessage,
Input,
SimpleGrid,
Button,
Box,
Text,
Textarea,
@ -22,12 +21,13 @@ import {
getBlueprintStringByHash,
} from "@factorio-sites/database";
import { Blueprint, BlueprintBook, BlueprintPage } from "@factorio-sites/types";
import { TAGS } from "@factorio-sites/common-utils";
import { pageHandler } from "../../../utils/page-handler";
import { Panel } from "../../../components/Panel";
import { validateCreateBlueprintForm } from "../../../utils/validate";
import { ImageEditor } from "../../../components/ImageEditor";
import { Select } from "../../../components/Select";
import { TAGS } from "@factorio-sites/common-utils";
import { Button } from "../../../components/Button";
const FieldStyle = css`
margin-bottom: 1rem;
@ -156,7 +156,7 @@ export const UserBlueprint: NextPage<UserBlueprintProps> = ({ blueprintPage, sel
</Field>
<Box css={{ display: "flex", alignItems: "center" }}>
<Button type="submit" colorScheme="green" disabled={isSubmitting}>
<Button primary type="submit" disabled={isSubmitting}>
Submit
</Button>
{status && <Text css={{ marginLeft: "1rem", color: "red" }}>{status}</Text>}

View File

@ -1,12 +1,13 @@
import React from "react";
import { NextPage } from "next";
import Link from "next/link";
import { Button, SimpleGrid, Box } from "@chakra-ui/react";
import { SimpleGrid, Box } from "@chakra-ui/react";
import { getBlueprintPageByUserId } from "@factorio-sites/database";
import { BlueprintPage } from "@factorio-sites/types";
import { pageHandler } from "../../utils/page-handler";
import { BlueprintLink } from "../../components/BlueprintLink";
import { Panel } from "../../components/Panel";
import { Button } from "../../components/Button";
interface UserBlueprintsProps {
blueprints: BlueprintPage[];
}
@ -27,14 +28,18 @@ export const UserBlueprints: NextPage<UserBlueprintsProps> = ({ blueprints }) =>
>
<Link href="/user/blueprint-create">
<a>
<Button colorScheme="green">create blueprint</Button>
<Button primary>Create Blueprint</Button>
</a>
</Link>
</Box>
<Box>
{blueprints.map((bp) => (
<BlueprintLink key={bp.id} blueprint={bp} editLink type="row" />
))}
{blueprints.length !== 0 ? (
blueprints.map((bp) => (
<BlueprintLink key={bp.id} blueprint={bp} editLink type="row" />
))
) : (
<p css={{ marginTop: "10px" }}>You don't have any blueprints yet</p>
)}
</Box>
</Panel>
</SimpleGrid>

View File

@ -1,17 +1,11 @@
import React from "react";
import { NextPage } from "next";
import { useRouter } from "next/router";
import {
FormControl,
FormLabel,
FormErrorMessage,
Input,
SimpleGrid,
Button,
} from "@chakra-ui/react";
import { FormControl, FormLabel, FormErrorMessage, Input, SimpleGrid } from "@chakra-ui/react";
import { Formik, Field, FieldProps } from "formik";
import { css } from "@emotion/react";
import { Panel } from "../../components/Panel";
import { Button } from "../../components/Button";
import { validateUserForm } from "../../utils/validate";
import { useAuth } from "../../providers/auth";
import { pageHandler } from "../../utils/page-handler";
@ -79,8 +73,13 @@ export const UserEdit: NextPage = () => {
)}
</Field>
<Button type="submit" colorScheme="green" disabled={isSubmitting}>
Save
<Button
primary
css={{ width: 80, textAlign: "center" }}
type="submit"
disabled={isSubmitting}
>
{isSubmitting ? "Saving..." : "Save"}
</Button>
</form>
)}

View File

@ -40,6 +40,7 @@
"@hookstate/core": "3.0.6",
"@prisma/client": "2.18.0",
"bbcode-to-react": "0.2.9",
"clsx": "1.1.1",
"bcrypt": "5.0.1",
"cookie": "0.4.1",
"document-register-element": "1.14.10",

View File

@ -6207,6 +6207,11 @@ clone@^2.1.1:
resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=
clsx@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188"
integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==
co@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"