style / tools optis & new tools calculator
All checks were successful
Deploy Sokko G / deploy (push) Successful in 6s
All checks were successful
Deploy Sokko G / deploy (push) Successful in 6s
This commit is contained in:
parent
6b84607b25
commit
0db35a0d2c
23 changed files with 1106 additions and 24 deletions
|
|
@ -16,7 +16,7 @@ const STORAGE_KEYS = {
|
|||
const APP_STORAGE_PREFIX = "sokkog:";
|
||||
const APP_STORAGE_LIMIT_BYTES = 5 * 1024 * 1024;
|
||||
const APP_STORAGE_WARNING_RATIO = 0.85;
|
||||
const ID_PREFIXES = { tbx: "t", mod: "m", item: "i", shot: "s", link: "l", counter: "c" };
|
||||
const ID_PREFIXES = { tbx: "t", mod: "m", item: "i", shot: "s", link: "l", counter: "c", calc: "r" };
|
||||
const ID_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||
const TOOLBOX_ICON_BASE = "/static/img/toolbox-icons/";
|
||||
const TOOLBOX_ICON_FILES = [
|
||||
|
|
@ -95,6 +95,24 @@ const DEFAULT_SITE_CONTENT = {
|
|||
text: "",
|
||||
items: []
|
||||
},
|
||||
modules: {
|
||||
notepad: { placeholder: "Notes rapides..." },
|
||||
checklist: {
|
||||
itemPlaceholder: "Nouvel item",
|
||||
importPlaceholder: "Importer plusieurs items...\nPotion:10\nMéga potion:5"
|
||||
},
|
||||
links: {
|
||||
titlePlaceholder: "Nom du lien",
|
||||
urlPlaceholder: "https://...",
|
||||
importPlaceholder: "Importer plusieurs liens...\nBuild:https://example.com/build?class=rogue\nMap:https://example.com/map"
|
||||
},
|
||||
counters: { labelPlaceholder: "Nom du compteur" },
|
||||
calculator: {
|
||||
expressionPlaceholder: "10*10",
|
||||
labelPlaceholder: "Lingots de fer",
|
||||
scrollableTitle: "Liste scrollable"
|
||||
}
|
||||
},
|
||||
emptyTitle: "Aucune toolbox",
|
||||
emptyText: ""
|
||||
}
|
||||
|
|
@ -253,7 +271,7 @@ function parsePositiveInt(value, fallback = 1) {
|
|||
function clampQty(value, target) {
|
||||
const parsed = Number.parseInt(value, 10);
|
||||
const safeValue = Number.isFinite(parsed) ? parsed : 0;
|
||||
return Math.min(Math.max(safeValue, 0), Math.max(1, target));
|
||||
return Math.min(safeValue, Math.max(1, target));
|
||||
}
|
||||
|
||||
function normalizeChecklistItem(item) {
|
||||
|
|
@ -327,6 +345,26 @@ function normalizeCountersData(data) {
|
|||
};
|
||||
}
|
||||
|
||||
function normalizeCalculatorValue(value) {
|
||||
const parsed = Number(value);
|
||||
return Number.isFinite(parsed) ? parsed : 0;
|
||||
}
|
||||
|
||||
function normalizeCalculatorData(data) {
|
||||
const entries = (data?.entries || [])
|
||||
.map((entry) => ({
|
||||
id: entry?.id || uid("calc"),
|
||||
parentId: String(entry?.parentId || ""),
|
||||
label: String(entry?.label || "").trim(),
|
||||
value: normalizeCalculatorValue(entry?.value)
|
||||
}));
|
||||
const entryIds = new Set(entries.map((entry) => entry.id));
|
||||
return {
|
||||
scrollResults: data?.scrollResults === true,
|
||||
entries: entries.map((entry) => entryIds.has(entry.parentId) ? entry : { ...entry, parentId: "" })
|
||||
};
|
||||
}
|
||||
|
||||
function compactChecklistItemForStorage(item) {
|
||||
const normalized = normalizeChecklistItem(item);
|
||||
const compact = { id: normalized.id, label: normalized.label };
|
||||
|
|
@ -369,6 +407,20 @@ function compactModuleDataForStorage(type, value) {
|
|||
}));
|
||||
return counters.length ? { counters } : null;
|
||||
}
|
||||
if (type === "calculator") {
|
||||
const normalized = normalizeCalculatorData(value);
|
||||
const entries = normalized.entries.map((entry) => {
|
||||
const compact = {
|
||||
id: entry.id,
|
||||
label: entry.label,
|
||||
value: entry.value
|
||||
};
|
||||
if (entry.parentId) compact.parentId = entry.parentId;
|
||||
return compact;
|
||||
});
|
||||
if (!entries.length && !normalized.scrollResults) return null;
|
||||
return normalized.scrollResults ? { entries, scrollResults: true } : { entries };
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
|
@ -409,6 +461,20 @@ function remapModuleDataForExport(type, data, nextId) {
|
|||
return { counters: compact.counters.map((counter) => ({ ...counter, id: nextId("counter") })) };
|
||||
}
|
||||
|
||||
if (type === "calculator") {
|
||||
const entryIdMap = new Map();
|
||||
compact.entries.forEach((entry) => entryIdMap.set(entry.id, nextId("calc")));
|
||||
const remapped = {
|
||||
entries: compact.entries.map((entry) => {
|
||||
const remapped = { ...entry, id: entryIdMap.get(entry.id) };
|
||||
if (entry.parentId) remapped.parentId = entryIdMap.get(entry.parentId) || "";
|
||||
return remapped;
|
||||
})
|
||||
};
|
||||
if (compact.scrollResults) remapped.scrollResults = true;
|
||||
return remapped;
|
||||
}
|
||||
|
||||
return compact;
|
||||
}
|
||||
|
||||
|
|
@ -842,6 +908,8 @@ function App() {
|
|||
{drawerGameId && (
|
||||
<ToolboxDrawer
|
||||
gameId={drawerGameId}
|
||||
game={getGame(drawerGameId)}
|
||||
siteContent={siteContent}
|
||||
toolboxes={store.toolboxes}
|
||||
links={store.links}
|
||||
actions={actions}
|
||||
|
|
@ -1207,14 +1275,17 @@ function ToolboxGameIcon({ game }) {
|
|||
);
|
||||
}
|
||||
|
||||
function ToolboxView({ toolbox, toolboxGame, embedded, actions, updateToolbox, updateModuleData, addScreenshotFiles }) {
|
||||
function ToolboxView({ siteContent, toolbox, toolboxGame, embedded, actions, updateToolbox, updateModuleData, addScreenshotFiles }) {
|
||||
const moduleColumns = Number(toolbox.moduleColumns) === 1 ? 1 : 2;
|
||||
const toolboxGameCover = getGameCardCover(toolboxGame);
|
||||
const moduleText = siteContent?.toolboxes?.modules || DEFAULT_SITE_CONTENT.toolboxes.modules;
|
||||
const moduleContext = {
|
||||
getModuleData,
|
||||
moduleText,
|
||||
normalizeChecklistData,
|
||||
normalizeLinksData,
|
||||
normalizeCountersData,
|
||||
normalizeCalculatorData,
|
||||
normalizeUrl,
|
||||
hostnameFromUrl,
|
||||
copyText,
|
||||
|
|
@ -1255,6 +1326,16 @@ function ToolboxView({ toolbox, toolboxGame, embedded, actions, updateToolbox, u
|
|||
<EditableTitle className="toolbox-title" value={toolbox.name} fallback="Nouvelle toolbox" onSave={(name) => name !== toolbox.name && updateToolbox({ ...toolbox, name })} />
|
||||
)}
|
||||
</div>
|
||||
{toolboxGame && (
|
||||
<a
|
||||
className="button toolbox-hero-link"
|
||||
href={embedded ? `#/toolbox/${toolbox.id}` : `#/games/${toolboxGame.id}`}
|
||||
onClick={embedded ? () => actions.setDrawerGameId("") : undefined}
|
||||
>
|
||||
<Icon name="enter" />
|
||||
<span>{embedded ? "Vers la toolbox complète" : "Vers la page de jeu"}</span>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
<div className="actions">
|
||||
<AddToolControls onAdd={addModule} />
|
||||
|
|
@ -1291,7 +1372,7 @@ function ToolboxView({ toolbox, toolboxGame, embedded, actions, updateToolbox, u
|
|||
);
|
||||
}
|
||||
|
||||
function ToolboxDrawer({ gameId, toolboxes, links, actions, updateToolbox, updateModuleData, addScreenshotFiles }) {
|
||||
function ToolboxDrawer({ gameId, game, siteContent, toolboxes, links, actions, updateToolbox, updateModuleData, addScreenshotFiles }) {
|
||||
const selectedId = links[gameId] || "";
|
||||
const toolbox = toolboxes.find((item) => item.id === selectedId);
|
||||
const [width, setWidth] = useState(() => {
|
||||
|
|
@ -1343,7 +1424,7 @@ function ToolboxDrawer({ gameId, toolboxes, links, actions, updateToolbox, updat
|
|||
</div>
|
||||
<button className="drawer-close-button" onClick={() => actions.setDrawerGameId("")} aria-label="Fermer" title="Fermer"><Icon name="close" /></button>
|
||||
</header>
|
||||
{toolbox ? <ToolboxView toolbox={toolbox} embedded actions={actions} updateToolbox={updateToolbox} updateModuleData={updateModuleData} addScreenshotFiles={addScreenshotFiles} /> : (
|
||||
{toolbox ? <ToolboxView siteContent={siteContent} toolbox={toolbox} toolboxGame={game} embedded actions={actions} updateToolbox={updateToolbox} updateModuleData={updateModuleData} addScreenshotFiles={addScreenshotFiles} /> : (
|
||||
<div className="empty"><p>Aucune toolbox associée à cette page jeu.</p><button className="primary" onClick={() => actions.setCreateModal({ gameId })}>Créer et associer</button></div>
|
||||
)}
|
||||
<StorageQuota />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue