website example

This commit is contained in:
Jorijn van der Graaf 2025-11-12 17:50:58 +01:00
commit c4f764e4ce
6 changed files with 198 additions and 182 deletions

View file

@ -1,7 +1,5 @@
# About
![alt text](https://github.com/Catcrafts/Crafter.Web/blob/master/hello.png?raw=true)
Crafter.CppDOM is a C++ library that exposes the browser DOM api's to C++ WebAssembly.
# HtmlElement vs HtmlElementView

View file

@ -42,10 +42,29 @@ function writeStringToWasm(str) {
return ptr;
}
function getElementById(id, idLenght) {
const obj = document.getElementById(decoder.decode(new Int8Array(window.crafter_webbuild_wasi.instance.exports.memory.buffer, id, idLenght)));
jsmemory.set(++memorycounter, obj);
return memorycounter;
function getElementById(id, idLength) {
try {
// Decode the id from WASM memory
const elementId = decoder.decode(
new Int8Array(window.crafter_webbuild_wasi.instance.exports.memory.buffer, id, idLength)
);
// Attempt to get the element
const obj = document.getElementById(elementId);
// Check if element exists
if (!obj) {
throw new Error(`Element with id "${elementId}" does not exist.`);
}
// Store in jsmemory
jsmemory.set(++memorycounter, obj);
return memorycounter;
} catch (err) {
console.error(err);
return 0;
}
}
function setInnerHTML(ptr, html, htmlLenght) {

View file

@ -12,7 +12,8 @@ struct Note {
void RenderNotes();
// Global vector to store notes
std::vector<Note> notes;
std::vector<Note>* notes = new std::vector<Note>();
std::vector<HtmlElementView>* noteButtons = new std::vector<HtmlElementView>();
// Create the head section
HtmlElementView* head = new HtmlElementView("head", R"(
@ -20,16 +21,22 @@ HtmlElementView* head = new HtmlElementView("head", R"(
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
width: 100vw;
height: 100vh;
display: flex;
justify-content: center; /* Horizontal centering */
align-items: center; /* Vertical centering */
}
.app-container {
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
width: 50vw;
max-width: 600px;
}
h1 {
color: #333;
@ -44,7 +51,6 @@ HtmlElementView* head = new HtmlElementView("head", R"(
.input-section textarea {
width: 100%;
height: 80px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
resize: vertical;
@ -125,7 +131,6 @@ HtmlElementView* body = new HtmlElementView("body", R"(
<button id="addNoteBtn">Add Note</button>
</div>
<div id="error"></div>
<div id="loading"></div>
<div class="notes-section">
@ -136,121 +141,97 @@ HtmlElementView* body = new HtmlElementView("body", R"(
)");
HtmlElementView* addNoteBtn = new HtmlElementView("addNoteBtn");
HtmlElementView* noteInput = new HtmlElementView("noteInput");
HtmlElementView* loadingIndicator = new HtmlElementView("loading");
HtmlElementView* notesContainer = new HtmlElementView("notesContainer");
// Function to fetch all notes from the backend
void FetchNotes() {
HtmlElementView loadingIndicator("loading");
loadingIndicator.SetInnerHTML("<p>Loading notes...</p>");
loadingIndicator->SetInnerHTML("<p>Loading notes...</p>");
// Make HTTP request to backend
Fetch("http://localhost:3000/getNotes", [](std::string content) {
// Parse response - each note is separated by \n\n
notes.clear();
Fetch("http://localhost:3000/getNotes", [](std::string input) {
notes->clear();
std::istringstream stream(input);
std::string line;
// Simple parsing of note data
std::uint_fast32_t pos = 0;
std::uint_fast32_t prevPos = 0;
while (std::getline(stream, line)) {
// Skip empty lines
if (line.empty()) continue;
while ((pos = content.find('\n', prevPos)) != std::string::npos) {
if (pos > prevPos) {
// Extract ID
std::string idStr = content.substr(prevPos, pos - prevPos);
std::uint_fast32_t id = std::stoul(idStr);
// First line is ID
std::uint32_t id = std::stoi(line);
// Find the next newline for content
std::uint_fast32_t contentStart = pos + 1;
std::uint_fast32_t contentEnd = content.find('\n', contentStart);
if (contentEnd != std::string::npos) {
std::string noteContent = content.substr(contentStart, contentEnd - contentStart);
notes.push_back({id, noteContent});
}
// Read content until the next empty line
std::string content;
while (std::getline(stream, line) && !line.empty()) {
if (!content.empty()) content += "\n"; // preserve line breaks
content += line;
}
prevPos = content.find('\n', pos) + 1;
if (prevPos >= content.length()) break;
notes->push_back(Note{id, content});
}
// Update UI with notes
//RenderNotes();
loadingIndicator->SetInnerHTML("<p>Notes loaded!</p>");
RenderNotes();
});
}
// Function to add a new note via the backend
void AddNote(const std::string& content) {
if (content.empty()) return;
HtmlElementView loadingIndicator("loading");
loadingIndicator.SetInnerHTML("<p>Saving note...</p>");
// Make POST request to create note
Fetch("http://localhost:3000/createNote", content, [](std::string content) {});
}
// Function to delete a note via the backend
void DeleteNote(std::uint32_t id) {
HtmlElementView loadingIndicator("loading");
loadingIndicator.SetInnerHTML("<p>Deleting note...</p>");
loadingIndicator->SetInnerHTML("<p>Deleting note...</p>");
// Make POST request to delete note
Fetch("http://localhost:3000/deleteNote", std::to_string(id), [](std::string content) {
FetchNotes();
loadingIndicator->SetInnerHTML("<p>Note deleted!</p>");
});
}
// Function to render all notes to the DOM
void RenderNotes() {
HtmlElementView notesContainer("notesContainer");
if (notes.empty()) {
notesContainer.SetInnerHTML("<p>No notes yet. Add one below!</p>");
if (notes->empty()) {
notesContainer->SetInnerHTML("<p>No notes yet. Add one below!</p>");
return;
}
std::string html = "<div class='notes-list'>";
for (const Note& note : notes) {
noteButtons->clear();
for (const Note& note : *notes) {
html += std::format(
R"(<div class='note-item' id='note-{}'>
<div class='note-content'>{}</div>
<button class='delete-btn' onclick='DeleteNote({})'>Delete</button>
<button id="note-{}-delete" class='delete-btn'>Delete</button>
</div>)",
note.id, note.content, note.id
);
}
html += "</div>";
notesContainer.SetInnerHTML(html);
notesContainer->SetInnerHTML(html);
// Add event listeners to delete buttons
for (const Note& note : notes) {
HtmlElementView deleteBtn(std::format("note-{}-delete", note.id));
deleteBtn.AddClickListener([id = note.id](MouseEvent event) {
for (const Note& note : *notes) {
HtmlElementView& view = noteButtons->emplace_back(std::format("note-{}-delete", note.id));
view.AddClickListener([id = note.id](MouseEvent event) {
DeleteNote(id);
});
}
}
std::string* currentNoteValue = new std::string();
// Main function
int main() {
// Initialize the app
FetchNotes();
// Add input listener to track textarea changes
noteInput->AddInputListener([&](InputEvent event) {
*currentNoteValue += event.data;
std::cout << *currentNoteValue << std::endl;
});
// Add click listener to add note button
addNoteBtn->AddClickListener([&](MouseEvent event) {
std::cout << "click!" << std::endl;
std::cout << *currentNoteValue << std::endl;
// Use the captured value from input event
if (!currentNoteValue->empty()) {
AddNote(*currentNoteValue);
// Clear the textarea by setting its value to empty string
noteInput->SetInnerHTML("");
*currentNoteValue = ""; // Reset the stored value
std::string noteValue = noteInput->GetValue();
std::cout << noteValue << std::endl;
if (!noteValue.empty()) {
loadingIndicator->SetInnerHTML("<p>Saving note...</p>");
Fetch("http://localhost:3000/createNote", noteValue, [&noteValue](std::string content) {
notes->push_back({static_cast<std::uint32_t>(std::stoi(content)), noteValue});
noteInput->SetValue("");
RenderNotes();
loadingIndicator->SetInnerHTML("<p>Saved note!</p>");
});
}
});
}

View file

@ -34,6 +34,10 @@ namespace Crafter {
CppDOMBindings::SetInnerHTML(ptr, html);
}
HtmlElementView::HtmlElementView(HtmlElementView&& other) : ptr(other.ptr), clickHandlers(std::move(other.clickHandlers)), mouseOverHandlers(std::move(other.mouseOverHandlers)), mouseOutHandlers(std::move(other.mouseOutHandlers)), mouseMoveHandlers(std::move(other.mouseMoveHandlers)), mouseDownHandlers(std::move(other.mouseDownHandlers)), mouseUpHandlers(std::move(other.mouseUpHandlers)), focusHandlers(std::move(other.focusHandlers)), blurHandlers(std::move(other.blurHandlers)), keyDownHandlers(std::move(other.keyDownHandlers)), keyUpHandlers(std::move(other.keyUpHandlers)), keyPressHandlers(std::move(other.keyPressHandlers)), changeHandlers(std::move(other.changeHandlers)), submitHandlers(std::move(other.submitHandlers)), inputHandlers(std::move(other.inputHandlers)), resizeHandlers(std::move(other.resizeHandlers)), scrollHandlers(std::move(other.scrollHandlers)), contextMenuHandlers(std::move(other.contextMenuHandlers)), dragStartHandlers(std::move(other.dragStartHandlers)), dragEndHandlers(std::move(other.dragEndHandlers)), dropHandlers(std::move(other.dropHandlers)), dragOverHandlers(std::move(other.dragOverHandlers)), dragEnterHandlers(std::move(other.dragEnterHandlers)), dragLeaveHandlers(std::move(other.dragLeaveHandlers)), wheelHandlers(std::move(other.wheelHandlers)) {
other.ptr = nullptr;
}
void HtmlElementView::SetInnerHTML(const std::string_view html) {
CppDOMBindings::SetInnerHTML(ptr, html);
}
@ -413,104 +417,106 @@ namespace Crafter {
CppDOMBindings::RemoveWheelListener(ptr, id);
}
HtmlElementView::~HtmlElementView(){
for(std::int32_t handler : clickHandlers) {
CppDOMBindings::clickHandlers->erase(handler);
CppDOMBindings::RemoveClickListener(ptr, handler);
HtmlElementView::~HtmlElementView() {
if(ptr != nullptr) {
for(std::int32_t handler : clickHandlers) {
CppDOMBindings::clickHandlers->erase(handler);
CppDOMBindings::RemoveClickListener(ptr, handler);
}
for(std::int32_t handler : mouseOverHandlers) {
CppDOMBindings::mouseOverHandlers->erase(handler);
CppDOMBindings::RemoveMouseOverListener(ptr, handler);
}
for(std::int32_t handler : mouseOutHandlers) {
CppDOMBindings::mouseOutHandlers->erase(handler);
CppDOMBindings::RemoveMouseOutListener(ptr, handler);
}
for(std::int32_t handler : mouseMoveHandlers) {
CppDOMBindings::mouseMoveHandlers->erase(handler);
CppDOMBindings::RemoveMouseMoveListener(ptr, handler);
}
for(std::int32_t handler : mouseDownHandlers) {
CppDOMBindings::mouseDownHandlers->erase(handler);
CppDOMBindings::RemoveMouseDownListener(ptr, handler);
}
for(std::int32_t handler : mouseUpHandlers) {
CppDOMBindings::mouseUpHandlers->erase(handler);
CppDOMBindings::RemoveMouseUpListener(ptr, handler);
}
for(std::int32_t handler : focusHandlers) {
CppDOMBindings::focusHandlers->erase(handler);
CppDOMBindings::RemoveFocusListener(ptr, handler);
}
for(std::int32_t handler : blurHandlers) {
CppDOMBindings::blurHandlers->erase(handler);
CppDOMBindings::RemoveBlurListener(ptr, handler);
}
for(std::int32_t handler : keyDownHandlers) {
CppDOMBindings::keyDownHandlers->erase(handler);
CppDOMBindings::RemoveKeyDownListener(ptr, handler);
}
for(std::int32_t handler : keyUpHandlers) {
CppDOMBindings::keyUpHandlers->erase(handler);
CppDOMBindings::RemoveKeyUpListener(ptr, handler);
}
for(std::int32_t handler : keyPressHandlers) {
CppDOMBindings::keyPressHandlers->erase(handler);
CppDOMBindings::RemoveKeyPressListener(ptr, handler);
}
for(std::int32_t handler : changeHandlers) {
CppDOMBindings::changeHandlers->erase(handler);
CppDOMBindings::RemoveChangeListener(ptr, handler);
}
for(std::int32_t handler : submitHandlers) {
CppDOMBindings::submitHandlers->erase(handler);
CppDOMBindings::RemoveSubmitListener(ptr, handler);
}
for(std::int32_t handler : inputHandlers) {
CppDOMBindings::inputHandlers->erase(handler);
CppDOMBindings::RemoveInputListener(ptr, handler);
}
for(std::int32_t handler : resizeHandlers) {
CppDOMBindings::resizeHandlers->erase(handler);
CppDOMBindings::RemoveResizeListener(ptr, handler);
}
for(std::int32_t handler : scrollHandlers) {
CppDOMBindings::scrollHandlers->erase(handler);
CppDOMBindings::RemoveScrollListener(ptr, handler);
}
for(std::int32_t handler : contextMenuHandlers) {
CppDOMBindings::contextMenuHandlers->erase(handler);
CppDOMBindings::RemoveContextMenuListener(ptr, handler);
}
for(std::int32_t handler : dragStartHandlers) {
CppDOMBindings::dragStartHandlers->erase(handler);
CppDOMBindings::RemoveDragStartListener(ptr, handler);
}
for(std::int32_t handler : dragEndHandlers) {
CppDOMBindings::dragEndHandlers->erase(handler);
CppDOMBindings::RemoveDragEndListener(ptr, handler);
}
for(std::int32_t handler : dropHandlers) {
CppDOMBindings::dropHandlers->erase(handler);
CppDOMBindings::RemoveDropListener(ptr, handler);
}
for(std::int32_t handler : dragOverHandlers) {
CppDOMBindings::dragOverHandlers->erase(handler);
CppDOMBindings::RemoveDragOverListener(ptr, handler);
}
for(std::int32_t handler : dragEnterHandlers) {
CppDOMBindings::dragEnterHandlers->erase(handler);
CppDOMBindings::RemoveDragEnterListener(ptr, handler);
}
for(std::int32_t handler : dragLeaveHandlers) {
CppDOMBindings::dragLeaveHandlers->erase(handler);
CppDOMBindings::RemoveDragLeaveListener(ptr, handler);
}
for(std::int32_t handler : wheelHandlers) {
CppDOMBindings::wheelHandlers->erase(handler);
CppDOMBindings::RemoveWheelListener(ptr, handler);
}
CppDOMBindings::FreeJs(ptr);
}
for(std::int32_t handler : mouseOverHandlers) {
CppDOMBindings::mouseOverHandlers->erase(handler);
CppDOMBindings::RemoveMouseOverListener(ptr, handler);
}
for(std::int32_t handler : mouseOutHandlers) {
CppDOMBindings::mouseOutHandlers->erase(handler);
CppDOMBindings::RemoveMouseOutListener(ptr, handler);
}
for(std::int32_t handler : mouseMoveHandlers) {
CppDOMBindings::mouseMoveHandlers->erase(handler);
CppDOMBindings::RemoveMouseMoveListener(ptr, handler);
}
for(std::int32_t handler : mouseDownHandlers) {
CppDOMBindings::mouseDownHandlers->erase(handler);
CppDOMBindings::RemoveMouseDownListener(ptr, handler);
}
for(std::int32_t handler : mouseUpHandlers) {
CppDOMBindings::mouseUpHandlers->erase(handler);
CppDOMBindings::RemoveMouseUpListener(ptr, handler);
}
for(std::int32_t handler : focusHandlers) {
CppDOMBindings::focusHandlers->erase(handler);
CppDOMBindings::RemoveFocusListener(ptr, handler);
}
for(std::int32_t handler : blurHandlers) {
CppDOMBindings::blurHandlers->erase(handler);
CppDOMBindings::RemoveBlurListener(ptr, handler);
}
for(std::int32_t handler : keyDownHandlers) {
CppDOMBindings::keyDownHandlers->erase(handler);
CppDOMBindings::RemoveKeyDownListener(ptr, handler);
}
for(std::int32_t handler : keyUpHandlers) {
CppDOMBindings::keyUpHandlers->erase(handler);
CppDOMBindings::RemoveKeyUpListener(ptr, handler);
}
for(std::int32_t handler : keyPressHandlers) {
CppDOMBindings::keyPressHandlers->erase(handler);
CppDOMBindings::RemoveKeyPressListener(ptr, handler);
}
for(std::int32_t handler : changeHandlers) {
CppDOMBindings::changeHandlers->erase(handler);
CppDOMBindings::RemoveChangeListener(ptr, handler);
}
for(std::int32_t handler : submitHandlers) {
CppDOMBindings::submitHandlers->erase(handler);
CppDOMBindings::RemoveSubmitListener(ptr, handler);
}
for(std::int32_t handler : inputHandlers) {
CppDOMBindings::inputHandlers->erase(handler);
CppDOMBindings::RemoveInputListener(ptr, handler);
}
for(std::int32_t handler : resizeHandlers) {
CppDOMBindings::resizeHandlers->erase(handler);
CppDOMBindings::RemoveResizeListener(ptr, handler);
}
for(std::int32_t handler : scrollHandlers) {
CppDOMBindings::scrollHandlers->erase(handler);
CppDOMBindings::RemoveScrollListener(ptr, handler);
}
for(std::int32_t handler : contextMenuHandlers) {
CppDOMBindings::contextMenuHandlers->erase(handler);
CppDOMBindings::RemoveContextMenuListener(ptr, handler);
}
for(std::int32_t handler : dragStartHandlers) {
CppDOMBindings::dragStartHandlers->erase(handler);
CppDOMBindings::RemoveDragStartListener(ptr, handler);
}
for(std::int32_t handler : dragEndHandlers) {
CppDOMBindings::dragEndHandlers->erase(handler);
CppDOMBindings::RemoveDragEndListener(ptr, handler);
}
for(std::int32_t handler : dropHandlers) {
CppDOMBindings::dropHandlers->erase(handler);
CppDOMBindings::RemoveDropListener(ptr, handler);
}
for(std::int32_t handler : dragOverHandlers) {
CppDOMBindings::dragOverHandlers->erase(handler);
CppDOMBindings::RemoveDragOverListener(ptr, handler);
}
for(std::int32_t handler : dragEnterHandlers) {
CppDOMBindings::dragEnterHandlers->erase(handler);
CppDOMBindings::RemoveDragEnterListener(ptr, handler);
}
for(std::int32_t handler : dragLeaveHandlers) {
CppDOMBindings::dragLeaveHandlers->erase(handler);
CppDOMBindings::RemoveDragLeaveListener(ptr, handler);
}
for(std::int32_t handler : wheelHandlers) {
CppDOMBindings::wheelHandlers->erase(handler);
CppDOMBindings::RemoveWheelListener(ptr, handler);
}
CppDOMBindings::FreeJs(ptr);
}
HtmlElement::HtmlElement(const std::string_view id): HtmlElementView(id) {
@ -521,8 +527,14 @@ namespace Crafter {
}
HtmlElement::HtmlElement(HtmlElement&& other): HtmlElementView(std::move(other)) {
}
HtmlElement::~HtmlElement() {
CppDOMBindings::DeleteElement(ptr);
if(ptr != nullptr) {
CppDOMBindings::DeleteElement(ptr);
}
}
}

View file

@ -30,6 +30,7 @@ export namespace Crafter::CppDOMBindings {
}
__attribute__((import_module("env"), import_name("setInnerHTML"))) void SetInnerHTML(void* ptr, const char* html, std::int32_t htmlLenght);
void SetInnerHTML(void* ptr, const std::string_view html) {
std::cout << "bind" << std::endl;
SetInnerHTML(ptr, html.data(), html.size());
}

View file

@ -27,7 +27,7 @@ import :EventTypes;
namespace Crafter {
export class HtmlElementView {
public:
void* const ptr;
void* ptr;
std::vector<std::int32_t> clickHandlers;
std::vector<std::int32_t> mouseOverHandlers;
std::vector<std::int32_t> mouseOutHandlers;
@ -55,6 +55,10 @@ namespace Crafter {
HtmlElementView(const std::string_view id);
HtmlElementView(const std::string_view id, const std::string_view html);
HtmlElementView(HtmlElementView&&);
HtmlElementView(const HtmlElementView&) = delete;
HtmlElementView& operator=(const HtmlElementView&) = delete;
~HtmlElementView();
void SetInnerHTML(const std::string_view html);
void SetStyle(const std::string_view style);
void SetProperty(const std::string_view property, const std::string_view value);
@ -138,14 +142,15 @@ namespace Crafter {
std::int32_t AddWheelListener(std::function<void(WheelEvent)> callback);
void RemoveWheelListener(std::int32_t id);
~HtmlElementView();
};
export class HtmlElement : public HtmlElementView {
public:
HtmlElement(const std::string_view id);
HtmlElement(const std::string_view id, const std::string_view html);
HtmlElement(HtmlElement&&);
HtmlElement(const HtmlElement&) = delete;
HtmlElement& operator=(const HtmlElement&) = delete;
~HtmlElement();
};
}