Compare commits
2 commits
f046ffdd0a
...
b19d5d33f7
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b19d5d33f7 | ||
![]() |
2694f0bdad |
4 changed files with 106 additions and 23 deletions
|
@ -53,6 +53,37 @@ function renderToast({ message, type = ToastConfig.defaultType, timeout = ToastC
|
|||
});
|
||||
}
|
||||
|
||||
const EditorWidget = (() => {
|
||||
let tempIdCounter = 1;
|
||||
|
||||
function autoResizeTextarea(textarea) {
|
||||
textarea.style.height = 'auto';
|
||||
textarea.style.height = `${textarea.scrollHeight + 2}px`;
|
||||
}
|
||||
|
||||
function createEditorWidget(template, id, timestamp, content = '') {
|
||||
let html = template.innerHTML
|
||||
.replace(/__ID__/g, id)
|
||||
.replace(/__TIMESTAMP__/g, timestamp)
|
||||
.replace(/__CONTENT__/g, content);
|
||||
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.innerHTML = html;
|
||||
|
||||
return wrapper.firstElementChild;
|
||||
}
|
||||
|
||||
function createTempId(prefix = "temp") {
|
||||
return `${prefix}-${tempIdCounter++}`;
|
||||
}
|
||||
|
||||
return {
|
||||
autoResizeTextarea,
|
||||
createEditorWidget,
|
||||
createTempId
|
||||
};
|
||||
})();
|
||||
|
||||
const ComboBoxWidget = (() => {
|
||||
let tempIdCounter = 1;
|
||||
|
||||
|
|
47
inventory/templates/fragments/_editor_fragment.html
Normal file
47
inventory/templates/fragments/_editor_fragment.html
Normal file
|
@ -0,0 +1,47 @@
|
|||
{% import "fragments/_icon_fragment.html" as icons %}
|
||||
|
||||
{% macro render_editor(id, title, mode='edit', content=None) %}
|
||||
<!-- Editor Fragment -->
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-black">{{ title }}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link{% if mode == 'edit' %} active{% endif %}" data-bs-toggle="tab" data-bs-target="#editor{{ id }}" id="editTab{{ id }}">{{ icons.render_icon('pencil', 16) }}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link{% if mode == 'view' %} active{% endif %}" data-bs-toggle="tab" data-bs-target="#viewer{{ id }}">{{ icons.render_icon('file-earmark-richtext', 16) }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content" id="tabContent{{ id }}">
|
||||
<div class="tab-pane fade{% if mode == 'edit' %} show active border border-top-0{% endif %}" id="editor{{ id }}">
|
||||
<textarea id="textEditor{{ id }}" name="update{{ id }}" class="form-control border-top-0 rounded-top-0" data-note-id="{{ id }}">{{ content if content }}</textarea>
|
||||
</div>
|
||||
<div class="tab-pane fade{% if mode == 'view' %} show active border border-top-0{% endif %} p-2 markdown-body" id="viewer{{ id }}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
textEditor{{ id }} = document.getElementById("textEditor{{ id }}");
|
||||
editTab{{ id }} = document.getElementById("editTab{{ id }}");
|
||||
viewer{{ id }} = document.getElementById("viewer{{ id }}")
|
||||
|
||||
textEditor{{ id }}.addEventListener("input", () => EditorWidget.autoResizeTextarea(textEditor{{ id }}));
|
||||
EditorWidget.autoResizeTextarea(textEditor{{ id }});
|
||||
|
||||
viewer{{ id }}.innerHTML = marked.parse(textEditor{{ id }}.value);
|
||||
|
||||
editTab{{ id }}.addEventListener("click", () => {
|
||||
EditorWidget.autoResizeTextarea(textEditor{{ id }});
|
||||
});
|
||||
|
||||
textEditor{{ id }}.addEventListener("input", () => {
|
||||
viewer{{ id }}.innerHTML = marked.parse(textEditor{{ id }}.value);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endmacro %}
|
|
@ -1,5 +1,6 @@
|
|||
{% import "fragments/_breadcrumb_fragment.html" as breadcrumbs %}
|
||||
{% import "fragments/_combobox_fragment.html" as combos %}
|
||||
{% import "fragments/_editor_fragment.html" as editor %}
|
||||
{% import "fragments/_icon_fragment.html" as icons %}
|
||||
{% import "fragments/_link_fragment.html" as links %}
|
||||
{% import "fragments/_table_fragment.html" as tables %}
|
||||
|
@ -19,10 +20,13 @@
|
|||
rel="stylesheet" integrity="sha384-gdnBcErvPbrURVoR9w3NhVMliw+ZmcTCmq+64xj2Ksx21nRJFX3qW0zFvBotL5rm"
|
||||
crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.13.1/font/bootstrap-icons.min.css">
|
||||
<link rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.8.1/github-markdown.min.css"
|
||||
integrity="sha512-BrOPA520KmDMqieeM7XFe6a3u3Sb3F1JBaQnrIAmWg3EYrciJ+Qqe6ZcKCdfPv26rGcgTrJnZ/IdQEct8h3Zhw=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/widget.css') }}">
|
||||
<style>
|
||||
{% block style %}
|
||||
|
||||
{% endblock %}
|
||||
</style>
|
||||
</head>
|
||||
|
@ -53,7 +57,7 @@
|
|||
</div>
|
||||
</nav>
|
||||
<main class="container-flex m-5">
|
||||
{% block content %}{% endblock %}
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.6/dist/js/bootstrap.bundle.min.js"
|
||||
integrity="sha384-j1CDi7MgGQ12Z7Qab0qlWQ/Qqz24Gc6BM0thvEMVjHnfYGF0rmFCozFSxQBxwHKO"
|
||||
|
@ -66,6 +70,7 @@
|
|||
src="https://cdn.datatables.net/v/bs5/jq-3.7.0/moment-2.29.4/dt-2.3.2/b-3.2.3/b-colvis-3.2.3/b-print-3.2.3/cr-2.1.1/cc-1.0.4/r-3.0.4/rg-1.5.1/rr-1.5.0/sc-2.4.3/sr-1.4.1/datatables.min.js"
|
||||
integrity="sha384-tNYRX2RiDDDRKCJgPF8Pw3rTxC1GUe1pt5qH1SBmwcazrEUj7Ii4C1Tz9wCCRUI4"
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/lib/marked.umd.js"></script>
|
||||
<script src="{{ url_for('static', filename='js/widget.js') }}"></script>
|
||||
<script>
|
||||
const searchInput = document.querySelector('#search');
|
||||
|
@ -82,6 +87,7 @@
|
|||
renderToast({ message, type });
|
||||
localStorage.removeItem("toastMessage");
|
||||
}
|
||||
|
||||
{% block script %} {% endblock %}
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
{% block title %}{{ title }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<nav>
|
||||
<nav>
|
||||
{{ breadcrumbs.breadcrumb_header(
|
||||
breadcrumbs=[
|
||||
{'label': 'Work Log', 'url': url_for('main.list_worklog')}
|
||||
|
@ -88,15 +88,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{#
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<label for="notes" class="form-label">Notes</label>
|
||||
<textarea name="notes" id="notes" class="form-control"
|
||||
rows="15">{{ log.notes if log.notes else '' }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
#}
|
||||
<div class="container" id="updates-container">
|
||||
<div class="row">
|
||||
<div class="col-11">
|
||||
|
@ -109,19 +100,19 @@
|
|||
</div>
|
||||
</div>
|
||||
{% for update in log.updates %}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="input-group mb-3">
|
||||
<span class="input-group-text">{{ update.timestamp.strftime('%Y-%m-%d %H:%M:%S') }}</span>
|
||||
<textarea name="update{{ loop.index0 }}" id="" class="form-control" rows="5" data-note-id="{{ update.id if update.id }}">{{ update.content }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ editor.render_editor(
|
||||
id = update.id,
|
||||
title = update.timestamp.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
mode = 'view',
|
||||
content = update.content
|
||||
) }}
|
||||
{% endfor %}
|
||||
<template id="editor-template">
|
||||
{{ editor.render_editor('__ID__', '__TIMESTAMP__', 'edit', '') }}
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
</nav>
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
|
@ -137,6 +128,7 @@
|
|||
|
||||
if (addUpdateButton) {
|
||||
addUpdateButton.addEventListener("click", (e) => {
|
||||
{#
|
||||
const row = document.createElement("div");
|
||||
row.classList.add("row");
|
||||
|
||||
|
@ -159,14 +151,21 @@
|
|||
updateContent.dataset.noteId = "";
|
||||
updateContent.name = "updateNew";
|
||||
|
||||
// Hook in auto-resize
|
||||
updateContent.addEventListener("input", () => autoResizeTextarea(updateContent));
|
||||
autoResizeTextarea(updateContent);
|
||||
|
||||
// Stitch it all together
|
||||
igroup.appendChild(ts);
|
||||
igroup.appendChild(updateContent);
|
||||
col.appendChild(igroup);
|
||||
row.appendChild(col);
|
||||
#}
|
||||
const template = document.getElementById("editor-template");
|
||||
const newEditor = EditorWidget.createEditorWidget(template, EditorWidget.createTempId("new"), formatDate(new Date()));
|
||||
|
||||
const updatesContainer = document.getElementById("updates-container");
|
||||
updatesContainer.appendChild(row);
|
||||
updatesContainer.appendChild(newEditor);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue