169 lines
6.3 KiB
Lua
169 lines
6.3 KiB
Lua
-- AD Guide
|
|
-- Fælles guide-motor (ren Lua, ingen spil-API) for "Opsæt mark" og "Opsæt flow": ét trin ad gangen,
|
|
-- trin der ikke er relevante springes over, tælling "Trin 3 af 7" over de synlige trin, og [Ret] der
|
|
-- åbner ét enkelt trin. Motoren kender ikke marker eller flows; en trin-definition giver rækkefølgen og
|
|
-- hvornår et trin vises:
|
|
-- def = {order = {stepId, ...}, isVisible = function(stepId, guide) -> bool}
|
|
-- Guiden er en tabel {def, draft, context, step, single, returnTo, snapshot}; alle funktioner returnerer
|
|
-- en ny guide (kladden kopieres), så [Annullér] aldrig har ændret noget.
|
|
|
|
ADGuide = {}
|
|
|
|
-- Kopi af rene data (tabeller uden cykler); funktioner og tal/tekster deles.
|
|
function ADGuide.deepCopy(value)
|
|
if type(value) ~= "table" then return value end
|
|
local copy = {}
|
|
for key, item in pairs(value) do copy[key] = ADGuide.deepCopy(item) end
|
|
return copy
|
|
end
|
|
|
|
-- Ny liste med item tilføjet (on) eller fjernet; aldrig dubletter.
|
|
function ADGuide.toggleItem(list, item, on)
|
|
local result, found = {}, false
|
|
for _, value in ipairs(list or {}) do
|
|
if value == item then
|
|
found = true
|
|
if on then table.insert(result, value) end
|
|
else
|
|
table.insert(result, value)
|
|
end
|
|
end
|
|
if on and not found then table.insert(result, item) end
|
|
return result
|
|
end
|
|
|
|
local function clone(guide, changes)
|
|
local copy = {}
|
|
for key, value in pairs(guide) do copy[key] = value end
|
|
for key, value in pairs(changes or {}) do
|
|
if value == false and (key == "snapshot") then copy[key] = nil else copy[key] = value end
|
|
end
|
|
return copy
|
|
end
|
|
|
|
local function isVisible(guide, step)
|
|
return guide.def.isVisible == nil or guide.def.isVisible(step, guide) == true
|
|
end
|
|
|
|
function ADGuide.visibleSteps(guide)
|
|
local steps = {}
|
|
for _, step in ipairs(guide.def.order) do
|
|
if isVisible(guide, step) then table.insert(steps, step) end
|
|
end
|
|
return steps
|
|
end
|
|
|
|
local function orderIndex(guide, step)
|
|
for index, id in ipairs(guide.def.order) do
|
|
if id == step then return index end
|
|
end
|
|
return 0
|
|
end
|
|
|
|
-- Første synlige trin efter (dir = 1) eller før (dir = -1) det aktuelle i rækkefølgen.
|
|
local function neighbour(guide, dir)
|
|
local order = guide.def.order
|
|
local index = orderIndex(guide, guide.step) + dir
|
|
while index >= 1 and index <= #order do
|
|
if isVisible(guide, order[index]) then return order[index] end
|
|
index = index + dir
|
|
end
|
|
return nil
|
|
end
|
|
|
|
-- options = {step = id, single = bool, returnTo = "close"|"summary"}
|
|
function ADGuide.new(def, draft, context, options)
|
|
options = options or {}
|
|
local guide = {def = def, draft = draft, context = context, step = options.step,
|
|
single = options.single == true, returnTo = options.returnTo or "close"}
|
|
if guide.step == nil then
|
|
guide.step = ADGuide.visibleSteps(guide)[1] or def.order[1]
|
|
end
|
|
if guide.single then guide.snapshot = ADGuide.deepCopy(draft) end
|
|
return guide
|
|
end
|
|
|
|
-- index (nil hvis det aktuelle trin ikke er synligt), total synlige trin.
|
|
function ADGuide.position(guide)
|
|
local steps = ADGuide.visibleSteps(guide)
|
|
local index = nil
|
|
for position, step in ipairs(steps) do
|
|
if step == guide.step then index = position end
|
|
end
|
|
return index, #steps
|
|
end
|
|
|
|
-- Sidste trin: ingen synlige efter, eller et [Ret]-trin der gemmer direkte.
|
|
function ADGuide.isLast(guide)
|
|
if guide.single and guide.returnTo == "close" then return true end
|
|
if guide.single then return false end
|
|
return neighbour(guide, 1) == nil
|
|
end
|
|
|
|
-- Næste: event "done" når der gemmes (sidste trin, eller [Ret] fra fanens oversigt).
|
|
function ADGuide.next(guide)
|
|
if guide.single then
|
|
if guide.returnTo == "summary" then
|
|
return clone(guide, {step = "summary", single = false, snapshot = false}), nil
|
|
end
|
|
return guide, "done"
|
|
end
|
|
local step = neighbour(guide, 1)
|
|
if step == nil then return guide, "done" end
|
|
return clone(guide, {step = step}), nil
|
|
end
|
|
|
|
-- Tilbage: event "cancel" når guiden lukkes (første trin, eller [Ret] fra fanens oversigt).
|
|
-- I et [Ret]-trin fra guidens eget oversigts-trin fortrydes trinnets ændringer.
|
|
function ADGuide.back(guide)
|
|
if guide.single then
|
|
if guide.returnTo == "summary" then
|
|
return clone(guide, {step = "summary", single = false, draft = guide.snapshot or guide.draft, snapshot = false}), nil
|
|
end
|
|
return guide, "cancel"
|
|
end
|
|
local step = neighbour(guide, -1)
|
|
if step == nil then return guide, "cancel" end
|
|
return clone(guide, {step = step}), nil
|
|
end
|
|
|
|
-- [Ret]: åbn kun ét trin. returnTo "summary" (guidens oversigt) eller "close" (gem og luk).
|
|
function ADGuide.jump(guide, step, returnTo)
|
|
return clone(guide, {step = step, single = true, returnTo = returnTo or "close", snapshot = ADGuide.deepCopy(guide.draft)})
|
|
end
|
|
|
|
function ADGuide.withDraft(guide, draft)
|
|
return clone(guide, {draft = draft})
|
|
end
|
|
|
|
function ADGuide.withContext(guide, context)
|
|
return clone(guide, {context = context})
|
|
end
|
|
|
|
-- label = {key = "...", args = {...}} (oversættes) eller {text = "..."} (færdig tekst); valgfri prefix
|
|
-- (færdig tekst foran, fx "Mælk: " foran den oversatte politik).
|
|
function ADGuide.labelText(label, getText)
|
|
if type(label) ~= "table" then return "" end
|
|
if label.prefix ~= nil then
|
|
local rest = {}
|
|
for key, value in pairs(label) do if key ~= "prefix" then rest[key] = value end end
|
|
return tostring(label.prefix) .. ADGuide.labelText(rest, getText)
|
|
end
|
|
if label.text ~= nil then return tostring(label.text) end
|
|
local template = getText(label.key)
|
|
if label.args == nil or #label.args == 0 then return tostring(template) end
|
|
local unpackFn = table.unpack or unpack
|
|
local ok, text = pcall(string.format, template, unpackFn(label.args))
|
|
return ok and text or tostring(template)
|
|
end
|
|
|
|
-- Resumé-linje {step, title, parts} -> "Afgrøde: Behold det der gror, så igen".
|
|
function ADGuide.lineText(line, getText)
|
|
local parts = {}
|
|
for _, part in ipairs(line.parts or {}) do
|
|
local text = ADGuide.labelText(part, getText)
|
|
if text ~= "" then table.insert(parts, text) end
|
|
end
|
|
return ADGuide.labelText(line.title, getText) .. ": " .. (#parts > 0 and table.concat(parts, ", ") or "-")
|
|
end
|