@@ -14,7 +14,7 @@ Three transforms do the work, all of them toggleable in the UI:
|
|||||||
|
|
||||||
| Transform | What it does |
|
| Transform | What it does |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| **Merge duplicates** | Mutually exclusive wargear forces you to take a datasheet twice - one Skatros with a radium jezzail, another with a transuranic arquebus. Copies of the same unit are folded into one card carrying every option. Points are not summed; the card keeps the highest cost of the copies it absorbed. |
|
| **Merge duplicates** | Mutually exclusive wargear forces you to take a datasheet twice - one Skatros with a radium jezzail, another with a transuranic arquebus. Copies of the same unit are folded into one card carrying every option. |
|
||||||
| **Drop Leader/Support** | Removes the attachment rules. Once the army is built they say nothing you need mid-game, and they are long enough to push the rules you *do* need off the card. |
|
| **Drop Leader/Support** | Removes the attachment rules. Once the army is built they say nothing you need mid-game, and they are long enough to push the rules you *do* need off the card. |
|
||||||
| **Split choice abilities** | An ability like *Canticles of the Omnissiah* arrives as one blob of text. This splits it into the intro rule plus one titled row per option, which is how the datasheets print it. |
|
| **Split choice abilities** | An ability like *Canticles of the Omnissiah* arrives as one blob of text. This splits it into the intro rule plus one titled row per option, which is how the datasheets print it. |
|
||||||
|
|
||||||
@@ -142,6 +142,21 @@ git merge upstream/main
|
|||||||
Conflicts should be confined to `src/App.jsx`, `index.html`, `vite.config.js` and
|
Conflicts should be confined to `src/App.jsx`, `index.html`, `vite.config.js` and
|
||||||
`package.json`. `src/transforms/` is entirely new and will never conflict.
|
`package.json`. `src/transforms/` is entirely new and will never conflict.
|
||||||
|
|
||||||
|
The fork also hides three things upstream shows, because a generic datasheet has
|
||||||
|
nothing to say with them: the roster overview card and its charts, the unit
|
||||||
|
composition, and the per-unit points cost. Those decisions live in
|
||||||
|
[`src/fork.js`](src/fork.js) - another file upstream does not have - and
|
||||||
|
`src/10th/Roster.jsx` reaches for them on as few lines as possible:
|
||||||
|
|
||||||
|
- it imports `ShortSummaryTable` from `../fork` instead of `./ShortSummaryTable`,
|
||||||
|
a one-line change that leaves the render site untouched. The upstream
|
||||||
|
component is still in the tree, unused, so its future diffs keep applying.
|
||||||
|
- `hideModelCount` is pinned to `HIDE_UNIT_COMPOSITION` instead of being a piece
|
||||||
|
of checkbox state.
|
||||||
|
|
||||||
|
Only the two checkboxes and the `pts` span are deleted outright, so a merge that
|
||||||
|
touches them will say so rather than quietly bringing them back.
|
||||||
|
|
||||||
## Credit
|
## Credit
|
||||||
|
|
||||||
All the hard parts - the parsing, the card layout, the print CSS - are
|
All the hard parts - the parsing, the card layout, the print CSS - are
|
||||||
|
|||||||
+2
-22
@@ -20,7 +20,7 @@ import { Weapons, hasDifferentProfiles } from "./Weapons";
|
|||||||
import { useIndexedDB } from "../helpers/useIndexedDB"; // New hook for IndexedDB
|
import { useIndexedDB } from "../helpers/useIndexedDB"; // New hook for IndexedDB
|
||||||
import { ImgEditor } from "./ImgEditor";
|
import { ImgEditor } from "./ImgEditor";
|
||||||
import { trySettingLocalStorage } from "../helpers/useLocalStorage";
|
import { trySettingLocalStorage } from "../helpers/useLocalStorage";
|
||||||
import { ShortSummaryTable } from "./ShortSummaryTable";
|
import { HIDE_UNIT_COMPOSITION, ShortSummaryTable } from "../fork";
|
||||||
|
|
||||||
const getShortSummarySubtitle = (force) => {
|
const getShortSummarySubtitle = (force) => {
|
||||||
const details = [];
|
const details = [];
|
||||||
@@ -183,7 +183,7 @@ const Force = ({ force, onePerPage, colorUserChoice }) => {
|
|||||||
|
|
||||||
const Unit = ({ unit, catalog, onePerPage, forceRules, colorUserChoice }) => {
|
const Unit = ({ unit, catalog, onePerPage, forceRules, colorUserChoice }) => {
|
||||||
const [hide, setHide] = useState(false);
|
const [hide, setHide] = useState(false);
|
||||||
const [hideModelCount, setHideModelCount] = useState(false);
|
const hideModelCount = HIDE_UNIT_COMPOSITION;
|
||||||
const uploadRef = useRef();
|
const uploadRef = useRef();
|
||||||
let {
|
let {
|
||||||
name,
|
name,
|
||||||
@@ -315,23 +315,6 @@ const Unit = ({ unit, catalog, onePerPage, forceRules, colorUserChoice }) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="flex justify-end gap-3 pb-0.5">
|
<div className="flex justify-end gap-3 pb-0.5">
|
||||||
<label
|
|
||||||
className="print-display-none"
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "flex-end",
|
|
||||||
gap: 4,
|
|
||||||
userSelect: "none",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
className="hide-model-selection"
|
|
||||||
type="checkbox"
|
|
||||||
onChange={(e) => setHideModelCount(e.target.checked)}
|
|
||||||
/>
|
|
||||||
<span className="print-display-none">Hide Unit Composition</span>
|
|
||||||
</label>
|
|
||||||
<label
|
<label
|
||||||
className="print-display-none"
|
className="print-display-none"
|
||||||
style={{
|
style={{
|
||||||
@@ -424,9 +407,6 @@ const Unit = ({ unit, catalog, onePerPage, forceRules, colorUserChoice }) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{name}
|
{name}
|
||||||
<span style={{ textTransform: "initial", fontSize: "1.2rem" }}>
|
|
||||||
{cost.points}pts
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="relative flex gap-4">
|
<div className="relative flex gap-4">
|
||||||
<div
|
<div
|
||||||
|
|||||||
-36
@@ -86,24 +86,12 @@ function App() {
|
|||||||
const [onePerPage, setOnePerPage] = useState(false);
|
const [onePerPage, setOnePerPage] = useState(false);
|
||||||
const [primaryColor, setPrimaryColor] = useState("#536766");
|
const [primaryColor, setPrimaryColor] = useState("#536766");
|
||||||
const [colorUserChoice, setColorUserChoice] = useState(false);
|
const [colorUserChoice, setColorUserChoice] = useState(false);
|
||||||
const [hideModelSelections, setHideModelSelections] = useState(false);
|
|
||||||
const uploadRef = useRef();
|
const uploadRef = useRef();
|
||||||
|
|
||||||
const throttledSetPrimaryColor = useRef(
|
const throttledSetPrimaryColor = useRef(
|
||||||
throttle((color) => setPrimaryColor(color), 50),
|
throttle((color) => setPrimaryColor(color), 50),
|
||||||
).current;
|
).current;
|
||||||
|
|
||||||
const toggleHideModelSelections = (hide) => {
|
|
||||||
const checkboxes = document.querySelectorAll(
|
|
||||||
'input[type="checkbox"].hide-model-selection',
|
|
||||||
);
|
|
||||||
checkboxes.forEach((checkbox) => {
|
|
||||||
if (checkbox.checked !== hide) {
|
|
||||||
checkbox.click();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
async function handleFileSelect(event) {
|
async function handleFileSelect(event) {
|
||||||
const files = event?.target?.files;
|
const files = event?.target?.files;
|
||||||
|
|
||||||
@@ -431,30 +419,6 @@ function App() {
|
|||||||
One Datacard per Page when Printing
|
One Datacard per Page when Printing
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
{
|
|
||||||
// only show when 10th or 11th edition
|
|
||||||
(edition === 10 || edition === 11) && (
|
|
||||||
<label
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: 4,
|
|
||||||
minHeight: 26,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
value={hideModelSelections}
|
|
||||||
onChange={(e) => {
|
|
||||||
setHideModelSelections(e.target.checked);
|
|
||||||
toggleHideModelSelections(e.target.checked);
|
|
||||||
}}
|
|
||||||
className="hide-model-selection"
|
|
||||||
/>
|
|
||||||
<span className="select-none">Hide all Unit Compositions</span>
|
|
||||||
</label>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 4 }}>
|
<div style={{ display: "flex", alignItems: "center", gap: 4 }}>
|
||||||
<label style={{ display: "flex", alignItems: "center", gap: 4 }}>
|
<label style={{ display: "flex", alignItems: "center", gap: 4 }}>
|
||||||
<input
|
<input
|
||||||
|
|||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
// Fork-local display decisions.
|
||||||
|
//
|
||||||
|
// BrevyScribe prints the generic datasheet for a unit rather than a record of
|
||||||
|
// one particular list, so a few of the things FancyScribe shows have nothing
|
||||||
|
// left to say here. They are switched off from this file - which upstream does
|
||||||
|
// not have, and which therefore can never conflict - so that the components
|
||||||
|
// upstream owns are touched on as few lines as possible.
|
||||||
|
|
||||||
|
// The roster overview table summarises the list that was uploaded: its total
|
||||||
|
// cost, a unit-by-unit breakdown and the charts drawn from them. None of that
|
||||||
|
// survives the transforms, so the card is not rendered at all.
|
||||||
|
//
|
||||||
|
// 10th/Roster.jsx imports this in place of ./ShortSummaryTable, which leaves the
|
||||||
|
// render site there byte-identical to upstream. ShortSummaryTable.jsx itself is
|
||||||
|
// kept in the tree, unused, so upstream changes to it keep merging cleanly (Vite
|
||||||
|
// tree-shakes it, and chart.js with it, out of the build).
|
||||||
|
export const ShortSummaryTable = () => null;
|
||||||
|
|
||||||
|
// The unit composition lists the models this particular list took, which is the
|
||||||
|
// list-specific detail the fork exists to strip - and after duplicate units are
|
||||||
|
// merged into one card it is misleading as well. It is always hidden, and the
|
||||||
|
// checkboxes that used to toggle it are gone.
|
||||||
|
export const HIDE_UNIT_COMPOSITION = true;
|
||||||
Reference in New Issue
Block a user