enable upload of own images
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
+79
-11
@@ -1,4 +1,4 @@
|
|||||||
import { useState } from "react";
|
import { useRef, useState, useEffect } from "react";
|
||||||
import factionBackground from "./assets/factionBackground.png";
|
import factionBackground from "./assets/factionBackground.png";
|
||||||
import adeptusAstartesIcon from "./assets/adeptusAstartesIcon.png";
|
import adeptusAstartesIcon from "./assets/adeptusAstartesIcon.png";
|
||||||
import rangedIcon from "./assets/rangedIcon.png";
|
import rangedIcon from "./assets/rangedIcon.png";
|
||||||
@@ -63,6 +63,8 @@ const Force = ({ force, onePerPage }) => {
|
|||||||
|
|
||||||
const Unit = ({ unit, index, catalog, onePerPage, forceRules }) => {
|
const Unit = ({ unit, index, catalog, onePerPage, forceRules }) => {
|
||||||
const [hide, setHide] = useState(false);
|
const [hide, setHide] = useState(false);
|
||||||
|
const uploadRef = useRef();
|
||||||
|
const [image, setImage] = useState(null);
|
||||||
let {
|
let {
|
||||||
name,
|
name,
|
||||||
weapons,
|
weapons,
|
||||||
@@ -118,6 +120,20 @@ const Unit = ({ unit, index, catalog, onePerPage, forceRules }) => {
|
|||||||
|
|
||||||
const overridePrimary = getOverridePrimary(factions, keywords);
|
const overridePrimary = getOverridePrimary(factions, keywords);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Check if the browser is Safari, and if so, remove the accept attribute
|
||||||
|
// from the file input element. This is because Safari doesn't support
|
||||||
|
// extensions on the accept attribute for input type=file
|
||||||
|
// (https://caniuse.com/input-file-accept). If set, they will not allow any
|
||||||
|
// file to be selected.
|
||||||
|
if (
|
||||||
|
navigator.userAgent.match(/AppleWebKit.*Safari/) &&
|
||||||
|
!navigator.userAgent.includes("Chrome")
|
||||||
|
) {
|
||||||
|
uploadRef.current?.removeAttribute("accept");
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
@@ -153,6 +169,8 @@ const Unit = ({ unit, index, catalog, onePerPage, forceRules }) => {
|
|||||||
background:
|
background:
|
||||||
"linear-gradient(90deg, rgba(20,21,25,1) 0%, rgba(48,57,62,1) 45%, rgba(73,74,79,1) 100%)",
|
"linear-gradient(90deg, rgba(20,21,25,1) 0%, rgba(48,57,62,1) 45%, rgba(73,74,79,1) 100%)",
|
||||||
color: "#fff",
|
color: "#fff",
|
||||||
|
minHeight: 200,
|
||||||
|
position: "relative",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -203,7 +221,10 @@ const Unit = ({ unit, index, catalog, onePerPage, forceRules }) => {
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
fontSize: "1.7em",
|
fontFamily: "ConduitITCStd",
|
||||||
|
fontSize: "2.5em",
|
||||||
|
letterSpacing: "1px",
|
||||||
|
lineHeight: "1",
|
||||||
fontWeight: 800,
|
fontWeight: 800,
|
||||||
textTransform: "uppercase",
|
textTransform: "uppercase",
|
||||||
zIndex: 1,
|
zIndex: 1,
|
||||||
@@ -211,10 +232,11 @@ const Unit = ({ unit, index, catalog, onePerPage, forceRules }) => {
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
|
marginBottom: 2,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{name}
|
{name}
|
||||||
<span style={{ textTransform: "initial", fontSize: "1.1rem" }}>
|
<span style={{ textTransform: "initial", fontSize: "1.2rem" }}>
|
||||||
{cost.points}pts
|
{cost.points}pts
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -241,9 +263,62 @@ const Unit = ({ unit, index, catalog, onePerPage, forceRules }) => {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<WoundTracker woundTracker={woundTracker} />
|
<WoundTracker woundTracker={woundTracker} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
right: 0,
|
||||||
|
top: 0,
|
||||||
|
height: "100%",
|
||||||
|
bottom: 0,
|
||||||
|
width: "50%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{image && (
|
||||||
|
<img
|
||||||
|
src={image}
|
||||||
|
alt=""
|
||||||
|
style={{ width: "100%", height: "100%", objectFit: "contain" }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<label
|
||||||
|
className="button print-display-none"
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
top: 0,
|
||||||
|
right: 0,
|
||||||
|
border: "1px solid #999",
|
||||||
|
top: 1,
|
||||||
|
right: 1,
|
||||||
|
padding: "1px 4px",
|
||||||
|
fontSize: "0.8rem",
|
||||||
|
backgroundColor: "#f0f0f0",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
ref={uploadRef}
|
||||||
|
type="file"
|
||||||
|
className="print-display-none"
|
||||||
|
accept=".jpg,.png,.jpeg,.gif,.bmp,.tif,.tiff,.webp,.svg,.jfif,.pjpeg,.pjp,.avif,.apng,.ico,.cur,.ani"
|
||||||
|
onChange={(e) => {
|
||||||
|
if (e.target.files && e.target.files[0]) {
|
||||||
|
let reader = new FileReader();
|
||||||
|
reader.onload = function (ev) {
|
||||||
|
setImage(ev.target.result);
|
||||||
|
}.bind(this);
|
||||||
|
reader.readAsDataURL(e.target.files[0]);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
display: "none",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
Upload image
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@@ -358,14 +433,7 @@ const Unit = ({ unit, index, catalog, onePerPage, forceRules }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ModelStats = ({
|
const ModelStats = ({ modelStat, index, showName, showWeapons, modelList }) => {
|
||||||
modelStats,
|
|
||||||
modelStat,
|
|
||||||
index,
|
|
||||||
showName,
|
|
||||||
showWeapons,
|
|
||||||
modelList,
|
|
||||||
}) => {
|
|
||||||
let { move, toughness, save, wounds, leadership, name, bs, ws, attacks } =
|
let { move, toughness, save, wounds, leadership, name, bs, ws, attacks } =
|
||||||
modelStat;
|
modelStat;
|
||||||
if (!wounds) {
|
if (!wounds) {
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ const Weapon = ({ weapon, modelStats, isMelee, className, forceRules }) => {
|
|||||||
replaceAbilityWithType(
|
replaceAbilityWithType(
|
||||||
[
|
[
|
||||||
"Each time an attack is made with this weapon, that attack automatically hits the target.",
|
"Each time an attack is made with this weapon, that attack automatically hits the target.",
|
||||||
|
"Each time an attack is made with this weapon profile, that attack automatically hits the target.",
|
||||||
"This weapon automatically hits its target.",
|
"This weapon automatically hits its target.",
|
||||||
"This weapon automatically hits its targets.",
|
"This weapon automatically hits its targets.",
|
||||||
"When resolving an attack made with this weapon, do not make a hit roll: it automatically scores a hit.",
|
"When resolving an attack made with this weapon, do not make a hit roll: it automatically scores a hit.",
|
||||||
@@ -119,6 +120,13 @@ const Weapon = ({ weapon, modelStats, isMelee, className, forceRules }) => {
|
|||||||
"Torrent",
|
"Torrent",
|
||||||
"When resolving an attack made with this weapon, do not make a hit roll: it automatically scores a hit."
|
"When resolving an attack made with this weapon, do not make a hit roll: it automatically scores a hit."
|
||||||
);
|
);
|
||||||
|
replaceAbilityWithType(
|
||||||
|
[
|
||||||
|
"Each time an attack is made with this weapon, you can re-roll the wound roll.",
|
||||||
|
],
|
||||||
|
"Twin-Linked",
|
||||||
|
"Each time an attack is made with this weapon, you can re-roll the wound roll."
|
||||||
|
);
|
||||||
replaceAbilityWithType(
|
replaceAbilityWithType(
|
||||||
["This weapon can target units that are not visible to the bearer."],
|
["This weapon can target units that are not visible to the bearer."],
|
||||||
"Indirect Fire",
|
"Indirect Fire",
|
||||||
|
|||||||
+25
-3
@@ -20,6 +20,22 @@
|
|||||||
--size-32: 32px;
|
--size-32: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "ConduitITCStd";
|
||||||
|
src: url("fonts/ConduitITCStd ExtraBold.woff2") format("woff2");
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "ConduitITCStd";
|
||||||
|
src: url("fonts/ConduitITCStd Bold.woff2") format("woff2");
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "ConduitITCStd";
|
||||||
|
src: url("fonts/ConduitITCStd-Regular.woff2") format("woff2");
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
print-color-adjust: exact;
|
print-color-adjust: exact;
|
||||||
@@ -46,7 +62,10 @@ h1 {
|
|||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button,
|
||||||
|
.button {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
color: #111113;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
border: 2px solid #999;
|
border: 2px solid #999;
|
||||||
padding: 0.6em 1.2em;
|
padding: 0.6em 1.2em;
|
||||||
@@ -56,12 +75,15 @@ button {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: border-color 0.25s;
|
transition: border-color 0.25s;
|
||||||
}
|
}
|
||||||
button:hover {
|
button:hover,
|
||||||
|
.button:hover {
|
||||||
border-color: var(--primary-color);
|
border-color: var(--primary-color);
|
||||||
background-color: var(--primary-color-transparent);
|
background-color: var(--primary-color-transparent);
|
||||||
}
|
}
|
||||||
button:focus,
|
button:focus,
|
||||||
button:focus-visible {
|
button:focus-visible,
|
||||||
|
.button:focus,
|
||||||
|
.button:focus-visible {
|
||||||
outline: 4px auto -webkit-focus-ring-color;
|
outline: 4px auto -webkit-focus-ring-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user