Compare commits
No commits in common. "ea6813b415ec12da68278d5398652122f90519ca" and "b93cfafc5aeba2e7baeb59cdd5f3c28932c61997" have entirely different histories.
ea6813b415
...
b93cfafc5a
|
|
@ -2,7 +2,6 @@
|
|||
<dictionary name="project">
|
||||
<words>
|
||||
<w>ncpf</w>
|
||||
<w>nuclearcraft</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
</component>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
27
index.html
27
index.html
|
|
@ -7,28 +7,11 @@
|
|||
<script type="module" src="scripts/main.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav id="top-menu" class="menu-bar">
|
||||
<div data-section="File" data-alt="f">
|
||||
<button data-alt="n" data-button="new-file">New</button>
|
||||
<button data-alt="o" data-button="open-file">Open</button>
|
||||
<div data-section="Recent Files" data-alt="r">
|
||||
<button disabled>-</button>
|
||||
</div>
|
||||
</div>
|
||||
<div data-section="Edit" data-alt="e">
|
||||
<button data-alt="u" data-button="undo">Undo</button>
|
||||
<button data-alt="r" data-button="redo" disabled>Redo</button>
|
||||
</div>
|
||||
<div data-section="Select" data-alt="s">
|
||||
<button data-alt="i">Invert</button>
|
||||
</div>
|
||||
<div data-section="View" data-alt="v">
|
||||
<div data-section="Test" data-alt="e">
|
||||
<div data-section="Test" data-alt="e">
|
||||
<button disabled>-</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<nav id="top-menu">
|
||||
<button>File</button>
|
||||
<button>Edit</button>
|
||||
<button>Select</button>
|
||||
<button>View</button>
|
||||
</nav>
|
||||
<nav id="left-menu" data-state="planning">
|
||||
<span data-resize="left-menu"></span>
|
||||
|
|
|
|||
148
scripts/atlas.js
148
scripts/atlas.js
|
|
@ -2,7 +2,8 @@ import { emitEvent } from "./events.js";
|
|||
|
||||
let atlasSize = 0; // in items
|
||||
let atlasResolution = 0; // in px
|
||||
export let atlases = [];
|
||||
export let atlasBlobURL = "about:blank";
|
||||
export let atlasMetadata = [];
|
||||
|
||||
async function getTextureSize(data) {
|
||||
const texture = data.texture;
|
||||
|
|
@ -33,68 +34,54 @@ function putTextureOnCanvas(ctx, atlasSize, index, data) {
|
|||
}
|
||||
|
||||
export async function buildAtlas(data) {
|
||||
// TODO: atlas caching
|
||||
|
||||
emitEvent("config:process:atlas:start");
|
||||
const textures = [
|
||||
...Object.entries(data.configuration),
|
||||
...data.addons.flatMap(a => Object.entries(a.configuration)),
|
||||
const overhaulSFRBlocks = [
|
||||
...data.configuration["nuclearcraft:overhaul_sfr"]?.blocks,
|
||||
...data.addons.flatMap(a => a.configuration["nuclearcraft:overhaul_sfr"]?.blocks)
|
||||
]
|
||||
.flatMap(([config, data]) => Object.entries(data)
|
||||
.map(([type, entries]) => [config, type, entries]))
|
||||
.flatMap(([c, t, e]) => Array.isArray(e)
|
||||
? [[c, t, e]]
|
||||
: t === "modules" && e["plannerator:global_elements"]
|
||||
? [[c, "plannerator:global_elements", e["plannerator:global_elements"].elements]]
|
||||
: [])
|
||||
.flatMap(([c, t, l]) => l.map((v) => [c, t, v]))
|
||||
.filter(([,, b]) => b && b.modules && b.modules["plannerator:texture"])
|
||||
.map(([c, t, b]) => ({
|
||||
configuration: c,
|
||||
configurationList: t,
|
||||
texture: b.modules["plannerator:texture"].texture,
|
||||
...(Object.fromEntries(Object.entries(b).filter(([k]) => k !== "modules")))
|
||||
}));
|
||||
.filter(b => b && b.modules && b.modules["plannerator:texture"])
|
||||
.map(b => (
|
||||
{
|
||||
...b,
|
||||
texture: b.modules["plannerator:texture"].texture,
|
||||
}
|
||||
));
|
||||
|
||||
if (textures.length === 0) {
|
||||
if (overhaulSFRBlocks.length === 0) {
|
||||
throw new Error("Configuration processing failed - no textured blocks found");
|
||||
}
|
||||
|
||||
await Promise.all(textures.map(getTextureSize));
|
||||
await Promise.all(overhaulSFRBlocks.map(getTextureSize));
|
||||
|
||||
atlases = [];
|
||||
let offset = 0;
|
||||
for (const t of Object.values(Object.groupBy(textures, t => t.size.toString()))) {
|
||||
if (t[0].size[0] !== t[0].size[1]) {
|
||||
throw new Error("Configuration processing failed - textures must be square");
|
||||
}
|
||||
|
||||
const atlasEdgeSizeBlocks = 2 ** Math.ceil(Math.log2(Math.sqrt(t.length)));
|
||||
const atlasEdgeSize = atlasEdgeSizeBlocks * t[0].size[0];
|
||||
|
||||
const canvas = new OffscreenCanvas(atlasEdgeSize, atlasEdgeSize);
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
await Promise.all(t.map((b, i) => putTextureOnCanvas(ctx, atlasEdgeSizeBlocks, i, b)));
|
||||
|
||||
t.forEach((m, i) => {
|
||||
m.index = offset + i;
|
||||
delete m.texture;
|
||||
});
|
||||
|
||||
const blob = await canvas.convertToBlob({ type: "image/png" });
|
||||
atlases.push({
|
||||
offset,
|
||||
url: URL.createObjectURL(blob),
|
||||
metadata: t,
|
||||
size: atlasEdgeSizeBlocks,
|
||||
resolution: atlasEdgeSize,
|
||||
length: t.length,
|
||||
});
|
||||
offset += t.length;
|
||||
// TODO: handle configurations with unique sizes
|
||||
if (new Set(overhaulSFRBlocks.map(v => v.size.toString())).size > 1) {
|
||||
throw new Error("Configuration processing failed - unable to handle multi-sized textures yet");
|
||||
}
|
||||
|
||||
console.log(atlases);
|
||||
if (overhaulSFRBlocks[0].size[0] !== overhaulSFRBlocks[0].size[1]) {
|
||||
throw new Error("Configuration processing failed - textures must be square");
|
||||
}
|
||||
|
||||
// TODO: handle sizes other than 16px
|
||||
if (overhaulSFRBlocks[0].size[0] !== 16) {
|
||||
throw new Error("Configuration processing failed - textures must be 16px");
|
||||
}
|
||||
|
||||
const atlasEdgeSizeBlocks = 2 ** Math.ceil(Math.log2(Math.sqrt(overhaulSFRBlocks.length)));
|
||||
const atlasEdgeSize = atlasEdgeSizeBlocks * overhaulSFRBlocks[0].size[0];
|
||||
|
||||
const canvas = new OffscreenCanvas(atlasEdgeSize, atlasEdgeSize);
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
await Promise.all(overhaulSFRBlocks.map((b, i) => putTextureOnCanvas(ctx, atlasEdgeSizeBlocks, i, b)));
|
||||
|
||||
const blob = await canvas.convertToBlob({ type: "image/png" });
|
||||
atlasSize = atlasEdgeSizeBlocks;
|
||||
atlasResolution = atlasEdgeSize;
|
||||
atlasBlobURL = URL.createObjectURL(blob);
|
||||
atlasMetadata = overhaulSFRBlocks;
|
||||
|
||||
console.log("Atlas blob: %s", atlasBlobURL);
|
||||
|
||||
emitEvent("config:process:atlas:end");
|
||||
}
|
||||
|
|
@ -109,44 +96,21 @@ export function makeAtlasImage(index) {
|
|||
return div;
|
||||
}
|
||||
|
||||
const originalIndex = index;
|
||||
for (const atlas of atlases) {
|
||||
if (index < atlas.length) {
|
||||
const atlasIndexX = index % atlas.size;
|
||||
const atlasIndexY = (index - atlasIndexX) / atlas.size;
|
||||
const size = atlas.metadata[index].size;
|
||||
const x = atlasIndexX * size[0];
|
||||
const y = atlasIndexY * size[1];
|
||||
const atlasIndexX = index % atlasSize;
|
||||
const atlasIndexY = (index - atlasIndexX) / atlasSize;
|
||||
const size = atlasMetadata[index].size;
|
||||
const x = atlasIndexX * size[0];
|
||||
const y = atlasIndexY * size[1];
|
||||
|
||||
const div = document.createElement("div");
|
||||
div.draggable = false;
|
||||
div.dataset.originalIndex = originalIndex;
|
||||
div.classList.add("atlas-texture");
|
||||
div.style.backgroundImage = `url(${atlas.url})`;
|
||||
div.style.setProperty("--atlas-resolution", `${atlas.resolution}px`);
|
||||
div.style.setProperty("--raw-x", `${x}px`);
|
||||
div.style.setProperty("--raw-y", `${y}px`);
|
||||
div.style.setProperty("--size-x", `${size[0]}px`);
|
||||
div.style.setProperty("--size-y", `${size[1]}px`);
|
||||
const div = document.createElement("div");
|
||||
div.draggable = false;
|
||||
div.classList.add("atlas-texture");
|
||||
div.style.backgroundImage = `url(${atlasBlobURL})`;
|
||||
div.style.setProperty("--atlas-resolution", `${atlasResolution}px`);
|
||||
div.style.setProperty("--raw-x", `${x}px`);
|
||||
div.style.setProperty("--raw-y", `${y}px`);
|
||||
div.style.setProperty("--size-x", `${size[0]}px`);
|
||||
div.style.setProperty("--size-y", `${size[1]}px`);
|
||||
|
||||
return div;
|
||||
}
|
||||
index -= atlas.length;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function getAtlasMetadata(index) {
|
||||
if (index < -1) return null;
|
||||
if (index === -1) return {};
|
||||
|
||||
for (const atlas of atlases) {
|
||||
if (index < atlas.length - 1) {
|
||||
return atlas.metadata[index];
|
||||
}
|
||||
index -= atlas.length;
|
||||
}
|
||||
|
||||
return null;
|
||||
return div;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,11 +36,6 @@ export class Filter {
|
|||
|
||||
test(...data) {
|
||||
for (const datum of data) {
|
||||
if (typeof datum !== "string") {
|
||||
console.log("cannot test %o", datum);
|
||||
continue;
|
||||
}
|
||||
|
||||
const normalized = datum.trim().toLocaleLowerCase().trim();
|
||||
for (const n of this.negate) {
|
||||
if (normalized.includes(n)) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import { initializePlanner } from "./planner.js";
|
|||
import { initializeMenuResizing } from "./resize-menus.js";
|
||||
import { getState, setState, STATE } from "./state.js";
|
||||
import { initializeTooltips } from "./tooltip.js";
|
||||
import { initializeMenuBar } from "./menu-bar.js";
|
||||
|
||||
window.addEventListener("load", () => {
|
||||
emitEvent("setup-start");
|
||||
|
|
@ -38,9 +37,8 @@ window.addEventListener("load", () => {
|
|||
initializePlanner();
|
||||
initializeMenuResizing();
|
||||
initializeTooltips();
|
||||
initializeMenuBar();
|
||||
|
||||
emitEvent("setup-end");
|
||||
|
||||
void loadConfiguration("addons");
|
||||
void loadConfiguration("default");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
export function initializeMenuBar() {}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
import { configuration } from "./configuration.js";
|
||||
|
||||
function findInPossible(entryList, entry) {
|
||||
switch (entry.type) {
|
||||
case "legacy_block":
|
||||
return entryList.find(v => v.name === entry.name
|
||||
&& v.metadata === entry.metadata
|
||||
&& (!entry.blockstate || JSON.stringify(v.blockstate) === JSON.stringify(entry.blockstate)));
|
||||
|
||||
case "oredict":
|
||||
return entryList.find(v => v.oredict === entry.oredict);
|
||||
|
||||
default:
|
||||
console.warn("unknown type", entry.type);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function resolveBlockName(block, { configurationHint }) {
|
||||
|
||||
if (block.type === "module") {
|
||||
switch (block.name) {
|
||||
case "nuclearcraft:overhaul_sfr:fuel_cell":
|
||||
return "Fuel Cell";
|
||||
|
||||
case "nuclearcraft:overhaul_sfr:moderator":
|
||||
return "Moderator";
|
||||
|
||||
case "nuclearcraft:overhaul_sfr:casing":
|
||||
return "Casing";
|
||||
|
||||
case "nuclearcraft:overhaul_sfr:reflector":
|
||||
return "Reflector";
|
||||
|
||||
case "nuclearcraft:overhaul_sfr:neutron_shield":
|
||||
return "Neutron Shield";
|
||||
|
||||
case "nuclearcraft:overhaul_sfr:irradiator":
|
||||
return "Irradiator";
|
||||
|
||||
default:
|
||||
console.warn("unknown module", block);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const possibleBlocks = [
|
||||
...Object.entries(configuration.configuration),
|
||||
...configuration.addons.flatMap(a => Object.entries(a.configuration)),
|
||||
]
|
||||
.filter(([k, v]) => (!configurationHint || k === configurationHint) && "blocks" in v)
|
||||
.flatMap(([, v]) => v.blocks);
|
||||
|
||||
const entry = findInPossible(possibleBlocks, block);
|
||||
if (!entry) return null;
|
||||
|
||||
return entry.modules["plannerator:display_name"].display_name;
|
||||
}
|
||||
|
||||
export function lookupFromMetadata(metadata) {
|
||||
if (!metadata.configuration || !metadata.configurationList || !metadata.type)
|
||||
return null;
|
||||
|
||||
const possibleEntries = [
|
||||
...Object.entries(configuration.configuration),
|
||||
...configuration.addons.flatMap(a => Object.entries(a.configuration)),
|
||||
]
|
||||
.filter(([k, v]) => k === metadata.configuration && metadata.configurationList in v)
|
||||
.flatMap(([, v]) => v[metadata.configurationList])
|
||||
.filter(v => v.type === metadata.type);
|
||||
|
||||
return findInPossible(possibleEntries, metadata);
|
||||
}
|
||||
|
|
@ -1,9 +1,8 @@
|
|||
import { atlases, makeAtlasImage } from "./atlas.js";
|
||||
import { DomList } from "./dom-list.js";
|
||||
import {atlasMetadata, makeAtlasImage} from "./atlas.js";
|
||||
import {DomList} from "./dom-list.js";
|
||||
import { emitEvent, listenTo } from "./events.js";
|
||||
import { Filter } from "./filter.js";
|
||||
import { createTooltip } from "./tooltip.js";
|
||||
import { lookupFromMetadata, resolveBlockName } from "./ncpf-utils.js";
|
||||
|
||||
let suppressHover = false;
|
||||
let multiblockSize = [7, 7, 5]; // width (screen x), depth (screen y), height (xy grids)
|
||||
|
|
@ -104,7 +103,9 @@ function listClickEvent(e) {
|
|||
}
|
||||
|
||||
export function initializePlanner() {
|
||||
listenTo("block-picker:filter", buildBlockPicker);
|
||||
listenTo("block-picker:filter", e => {
|
||||
buildBlockPicker();
|
||||
});
|
||||
|
||||
listenTo("config:end", buildBlockPicker);
|
||||
|
||||
|
|
@ -113,25 +114,81 @@ export function initializePlanner() {
|
|||
|
||||
function buildBlockPicker() {
|
||||
const filter = Filter.fromInput("block-filter");
|
||||
console.log(filter);
|
||||
|
||||
const grid = new DomList(document.querySelector("#block-list"));
|
||||
grid.clear();
|
||||
|
||||
const blocks = atlases.flatMap(a => a.metadata)
|
||||
.filter(m => m.configuration === "nuclearcraft:overhaul_sfr" && m.configurationList === "blocks");
|
||||
console.log(blocks);
|
||||
blocks.forEach(m => {
|
||||
const item = grid.makeItem();
|
||||
item.dataset.index = m.index.toString(10);
|
||||
const tooltipData = [...getBlockTooltip(m)];
|
||||
item.dataset.tooltip = createTooltip(tooltipData).toString(10);
|
||||
if (!filter.test(...tooltipData)) {
|
||||
item.classList.add("hidden");
|
||||
atlasMetadata.forEach((m, i) => {
|
||||
const item = grid.makeItem();
|
||||
item.dataset.index = i.toString(10);
|
||||
if (m.modules["plannerator:display_name"].display_name === "Lithium Heat Sink") {
|
||||
console.log(m);
|
||||
void {
|
||||
"cooling": 130,
|
||||
"rules": [
|
||||
{
|
||||
"rules": [
|
||||
{
|
||||
"rules": [
|
||||
{
|
||||
"min": 2,
|
||||
"max": 2,
|
||||
"block": {
|
||||
"metadata": 1,
|
||||
"name": "nuclearcraft:solid_fission_sink2",
|
||||
"type": "legacy_block",
|
||||
"blockstate": {
|
||||
"type": "lead"
|
||||
}
|
||||
},
|
||||
"type": "between"
|
||||
},
|
||||
{
|
||||
"min": 1,
|
||||
"max": 1,
|
||||
"block": {
|
||||
"metadata": 1,
|
||||
"name": "nuclearcraft:solid_fission_sink2",
|
||||
"type": "legacy_block",
|
||||
"blockstate": {
|
||||
"type": "lead"
|
||||
}
|
||||
},
|
||||
"type": "axial"
|
||||
}
|
||||
],
|
||||
"type": "and"
|
||||
},
|
||||
{
|
||||
"min": 1,
|
||||
"max": 6,
|
||||
"block": {
|
||||
"name": "nuclearcraft:overhaul_sfr:casing",
|
||||
"type": "module"
|
||||
},
|
||||
"type": "between"
|
||||
}
|
||||
item.appendChild(makeAtlasImage(m.index));
|
||||
item.addEventListener("click", listClickEvent);
|
||||
grid.addItem(item);
|
||||
});
|
||||
],
|
||||
"type": "and"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
item.dataset.tooltip = createTooltip([
|
||||
m.modules["plannerator:display_name"].display_name,
|
||||
...("nuclearcraft:overhaul_sfr:heat_sink" in m.modules ? [
|
||||
"Heatsink",
|
||||
`Cooling: ${m.modules["nuclearcraft:overhaul_sfr:heat_sink"].cooling ?? "???"} H/t`,
|
||||
] : []),
|
||||
]).toString(10);
|
||||
if (!filter.test(m.modules["plannerator:display_name"].display_name)) {
|
||||
item.classList.add("hidden");
|
||||
}
|
||||
item.appendChild(makeAtlasImage(i));
|
||||
item.addEventListener("click", listClickEvent);
|
||||
grid.addItem(item);
|
||||
});
|
||||
}
|
||||
|
||||
function buildPlanner() {
|
||||
|
|
@ -169,140 +226,3 @@ function buildPlanner() {
|
|||
list.addItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
function* getBlockTooltip(metadata) {
|
||||
const data = lookupFromMetadata(metadata);
|
||||
|
||||
if (!data) {
|
||||
console.warn("failed to get data from metadata", metadata);
|
||||
return;
|
||||
}
|
||||
|
||||
yield data.modules["plannerator:display_name"].display_name;
|
||||
|
||||
let hasAdditionalData = false;
|
||||
|
||||
if ("nuclearcraft:overhaul_sfr:heat_sink" in data.modules) {
|
||||
hasAdditionalData = true;
|
||||
const heatSink = data.modules["nuclearcraft:overhaul_sfr:heat_sink"];
|
||||
yield "Heatsink";
|
||||
if (heatSink.cooling) {
|
||||
yield `Cooling: ${heatSink.cooling} H/t`;
|
||||
}
|
||||
if (heatSink.rules) {
|
||||
for (const rule of heatSink.rules) {
|
||||
yield "Requires " + parseRuleAsTooltip(rule, { configurationHint: metadata.configuration });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ("nuclearcraft:overhaul_sfr:casing" in data.modules) {
|
||||
hasAdditionalData = true;
|
||||
yield "Casing";
|
||||
}
|
||||
|
||||
if ("nuclearcraft:overhaul_sfr:controller" in data.modules) {
|
||||
hasAdditionalData = true;
|
||||
yield "Controller";
|
||||
}
|
||||
|
||||
if ("nuclearcraft:overhaul_sfr:fuel_cell" in data.modules) {
|
||||
hasAdditionalData = true;
|
||||
yield "Fuel Cell";
|
||||
}
|
||||
|
||||
if ("nuclearcraft:overhaul_sfr:port" in data.modules) {
|
||||
hasAdditionalData = true;
|
||||
if (data.modules["nuclearcraft:overhaul_sfr:port"].output) {
|
||||
yield "Output Port";
|
||||
} else {
|
||||
yield "Input Port";
|
||||
}
|
||||
}
|
||||
|
||||
if ("nuclearcraft:overhaul_sfr:irradiator" in data.modules) {
|
||||
hasAdditionalData = true;
|
||||
yield "Irradiator";
|
||||
}
|
||||
|
||||
if ("nuclearcraft:overhaul_sfr:conductor" in data.modules) {
|
||||
hasAdditionalData = true;
|
||||
yield "Conductor";
|
||||
}
|
||||
|
||||
if ("nuclearcraft:overhaul_sfr:moderator" in data.modules) {
|
||||
hasAdditionalData = true;
|
||||
const moderator = data.modules["nuclearcraft:overhaul_sfr:moderator"];
|
||||
yield "Moderator";
|
||||
yield `Flux: ${moderator.flux}`;
|
||||
yield `Efficiency: ${moderator.efficiency}`;
|
||||
}
|
||||
|
||||
if ("nuclearcraft:overhaul_sfr:reflector" in data.modules) {
|
||||
hasAdditionalData = true;
|
||||
const reflector = data.modules["nuclearcraft:overhaul_sfr:reflector"];
|
||||
yield "Reflector";
|
||||
yield `Efficiency: ${reflector.efficiency}`;
|
||||
yield `Reflectivity: ${reflector.reflectivity}`;
|
||||
}
|
||||
|
||||
if ("nuclearcraft:overhaul_sfr:neutron_shield" in data.modules) {
|
||||
hasAdditionalData = true;
|
||||
const shield = data.modules["nuclearcraft:overhaul_sfr:neutron_shield"];
|
||||
yield "Neutron Shield";
|
||||
yield `Efficiency: ${shield.efficiency}`;
|
||||
yield `Heat per Flux: ${shield.heat_per_flux}`;
|
||||
}
|
||||
|
||||
if (!hasAdditionalData) {
|
||||
console.log("unknown block", data);
|
||||
}
|
||||
}
|
||||
|
||||
function parseRuleAsTooltip(rule, props) {
|
||||
switch (rule.type) {
|
||||
case "and":
|
||||
return rule.rules.map(r => parseRuleAsTooltip(r, props)).join(" AND ");
|
||||
|
||||
case "or":
|
||||
return rule.rules.map(r => parseRuleAsTooltip(r, props)).join(" OR ");
|
||||
|
||||
case "between": {
|
||||
const block = resolveBlockName(rule.block, props);
|
||||
if (!block) {
|
||||
console.warn("could not find block %o", rule.block);
|
||||
return "???";
|
||||
}
|
||||
if (rule.min === rule.max) {
|
||||
return `exactly ${rule.min} ${block}`;
|
||||
} else if (rule.max === 6) {
|
||||
return `at least ${rule.min} ${block}`;
|
||||
} else if (rule.min === 0) {
|
||||
return `no more than ${rule.max} ${block}`;
|
||||
} else {
|
||||
return `between ${rule.min} and ${rule.max} ${block}`;
|
||||
}
|
||||
}
|
||||
|
||||
case "axial": {
|
||||
const block = resolveBlockName(rule.block, props);
|
||||
if (!block) {
|
||||
console.warn("could not find block %o", rule.block);
|
||||
return "???";
|
||||
}
|
||||
if (rule.min === rule.max) {
|
||||
return `exactly ${rule.min} axial ${block}`;
|
||||
} else if (rule.max === 6) {
|
||||
return `at least ${rule.min} axial ${block}`;
|
||||
} else if (rule.min === 0) {
|
||||
return `no more than ${rule.max} axial ${block}`;
|
||||
} else {
|
||||
return `between ${rule.min} and ${rule.max} axial ${block}`;
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
console.warn("unknown rule: %o", rule);
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue