#define TABLEGEN_EXPOSE_XML #include "Tablegen/Tablegen.hpp" #include #include #include #include namespace Tablegen { namespace vw = std::views; namespace { constexpr std::string_view generalStylesFormat( "\n" "{}.kanji-table {{ interpolate-size: allow-keywords; width: {}%; margin: {}%; }}\n" "{}.kanji-table-row {{ width: 100%; vertical-align: top; }}\n" "{}.kanji-table-item {{ width: {}%; padding: {}%; }}\n" "{}.kanji-container {{ background: {}; border-radius: 25px; }}\n" "{}.kanji-button {{ display: flex; justify-content: center; align-items: center; " "border-radius: 25px; border: 0; height: 2em; width: 100%; " "padding: 2%; background: {}; font-size: {}{}; }}\n" "{}.kanji-button:hover {{ background: {}; }}\n" "{}.kanji-image-wrap {{ width: 100%; border-radius: 25px; overflow: hidden; " "box-sizing: border-box; height: 0; transition: height {}s; " "&.kanji-show {{ height: max-content; }} }}\n" "{}.kanji-image {{ width: 100%; height: 100%; display: block; appearance: none; }}\n" ); constexpr std::string_view imageStyleFormat( "{}#kanji-image-{}{} {{ content: url({}); }}\n" ); constexpr std::string_view buttonScriptFormat( "{}document.getElementById(\"kanji-button-{}\")" ".addEventListener(\"click\", e => {{ " "document.getElementById(\"kanji-{}\")" ".classList.toggle(\"kanji-show\"); }});\n" ); constexpr std::string_view scriptFormat( "\n{}document.body.onload = () => {{\n{}{}}}\n{}" ); constexpr std::string_view stylesFormat( "{}{}{}{}" ); std::string GeneralStyles(const Settings& settings, std::string_view indent) { return std::format(generalStylesFormat, indent, settings.TableWidth, settings.TableMargin, indent, indent, settings.TableItemWidth, settings.TableItemPadding, indent, settings.TableItemColour.ToHex(), indent, settings.ButtonColour.ToHex(), settings.LabelFontSize, ToString(settings.LabelFontUnits), indent, settings.ButtonHoverColour.ToHex(), indent, settings.ButtonAnimationLength, indent); } constexpr std::string ImageStyle(std::string_view indent, std::size_t index, std::string_view imageName, std::string_view imageFormat, std::string_view extraSpecifier = "") { std::string imagePath = std::vformat(imageFormat, std::make_format_args(imageName)); return std::format(imageStyleFormat, indent, index, extraSpecifier, imagePath); } constexpr std::string ButtonScript(std::string_view indent, std::size_t index) { return std::format(buttonScriptFormat, indent, index, index); } constexpr std::string Script(std::string_view indent, std::string_view tagIndent, std::string_view buttonStatements) { return std::format(scriptFormat, indent, buttonStatements, indent, tagIndent); } constexpr std::string Styles(const Settings& settings, std::string_view indent, std::string_view tagIndent, std::string_view staticImageStyles, std::string_view animatedImageStyles) { return std::format(stylesFormat, GeneralStyles(settings, indent), staticImageStyles, animatedImageStyles, tagIndent); } } std::string GeneratePage(const Settings& settings) { pugi::xml_document doc = GenerateDocument(settings); std::uint32_t formatOptions; formatOptions = pugi::format_indent | pugi::format_no_escapes | pugi::format_no_declaration; std::stringstream str; doc.save(str, " ", formatOptions); return str.str(); } void GeneratePage(const std::string& path, const Settings& settings) { pugi::xml_document doc = GenerateDocument(settings); std::uint32_t formatOptions; formatOptions = pugi::format_indent | pugi::format_no_escapes | pugi::format_no_declaration; doc.save_file("test.html", " ", formatOptions); } pugi::xml_document GenerateDocument(const Settings& settings) { pugi::xml_document doc; pugi::xml_node tableRoot = doc; if (settings.FullDocument == Flag::Enable) { doc.append_child(pugi::node_doctype).set_value("html"); pugi::xml_node html = doc.append_child("html"); pugi::xml_node head = html.append_child("head");\ head.append_child("title").append_child(pugi::node_pcdata).set_value("Kanji Table"); tableRoot = html.append_child("body").append_child("main"); } pugi::xml_node table = tableRoot.append_child("table"); table.append_attribute("class") = "kanji-table"; pugi::xml_node comment = table.append_child(pugi::node_comment); comment.set_value(" Autogenerated by Tablegen, please avoid editing manually. "); pugi::xml_node script = table.append_child("script"); pugi::xml_node styles = table.append_child("style"); std::string staticImg; std::string animatedImg; std::string buttons; std::size_t indentLevel = settings.OverrideIndentLevel == Flag::Enable ? settings.IndentLevel : settings.FullDocument == Flag::Enable ? 5 : 2; std::string indent(indentLevel * 4, ' '); std::string tagIndent((indentLevel > 0 ? indentLevel - 1 : 0) * 4, ' '); pugi::xml_node tableBody = table.append_child("tbody"); for (pugi::xml_node row; const auto& [i, character] : vw::enumerate(settings.Characters)) { //////////////////////////////////////////////////////////////////// // HTML //////////////////////////////////////////////////////////////////// if (i % 3 == 0) { row = tableBody.append_child("tr"); row.append_attribute("class") = "kanji-table-row"; } pugi::xml_node element = row.append_child("td"); element.append_attribute("class") = "kanji-table-item"; pugi::xml_node container = element.append_child("div"); container.append_attribute("class") = "kanji-container"; pugi::xml_node button = container.append_child("button"); button.append_attribute("class") = "kanji-button"; button.append_attribute("id") = std::format("kanji-button-{}", i); pugi::xml_node label = button.append_child("span"); label.append_attribute("class") = "kanji-button-text"; label.append_child(pugi::node_pcdata).set_value(character.Label); pugi::xml_node imageWrap = container.append_child("div"); imageWrap.append_attribute("class") = "kanji-image-wrap"; imageWrap.append_attribute("id") = std::format("kanji-{}", i); pugi::xml_node image = imageWrap.append_child("input"); image.append_attribute("type") = "checkbox"; image.append_attribute("class") = "kanji-image"; image.append_attribute("id") = std::format("kanji-image-{}", i); //////////////////////////////////////////////////////////////////// // CSS //////////////////////////////////////////////////////////////////// staticImg.append(ImageStyle(indent, i, character.ImagePath, settings.ImageFormat)); animatedImg.append(ImageStyle(indent, i, character.AnimationPath, settings.AnimationFormat, ":checked")); //////////////////////////////////////////////////////////////////// // JavaScript //////////////////////////////////////////////////////////////////// buttons.append(ButtonScript(indent, i)); } styles.append_child(pugi::node_pcdata).set_value(Styles(settings, indent, tagIndent, staticImg, animatedImg)); script.append_child(pugi::node_pcdata).set_value(Script(indent, tagIndent, buttons)); return doc; } }