working UI

This commit is contained in:
evilchili committed 2025-10-17 19:33:29 -07:00
1 parent c1272788f0
commit 83527b85f4
12 files changed
+42664 -75

No files matched your search

+4 -1
View File
@@ -16,8 +16,10 @@
{% block nav %}
{% include "nav.html" %}
{% include "breadcrumbs.html" %}
{% endblock %}
<div class='table-wrapper'>
<div class='main-aligned'>
<div class='table-wrapper'>
<main>
{% for message in g.messages %}
<div class="alert">
@@ -26,6 +28,7 @@
{% endfor %}
{% block content %}{% endblock %}
</main>
</div>
</div>
<footer>
{% block footer %}
+1 -5
View File
@@ -1,7 +1,3 @@
<div id='breadcrumbs'>
<div id='breadcrumbs' class='main-aligned'>
<a href="{{ url_for('index') }}">Home</a>{% for (uri, name) in breadcrumbs %}.<a href="{{ uri }}">{{ name }}</a>{% endfor %}
<div id='actions'>
<a id='action__edit' data-state='view'>edit</a>
<a id='action__save' style='display: none;'>save</a>
</div>
</div>
+1 -1
View File
@@ -1,5 +1,5 @@
<nav>
<ul class="container">
<ul class="container main-aligned">
<li><a href='{{ root.uri }}'>Home</a></li>
{% for subpage in root.members %}
{% if user.can_read(subpage) %}
+7 -48
View File
@@ -1,60 +1,19 @@
{% extends "base.html" %}
{% block styles %}
<link rel="stylesheet" href="https://uicdn.toast.com/editor/latest/toastui-editor.min.css" />
<link rel="stylesheet" href="/static/toastui-editor.css" />
{% endblock %}
{% block content %}
{% include "breadcrumbs.html" %}
<div id='viewer'></div>
<div id='editor'></div>
<div id='editor' class='read-only'></div>
{% endblock %}
{% block scripts %}
<script src="https://uicdn.toast.com/editor/latest/toastui-editor-all.min.js"></script>
<script>
const viewerEl = document.querySelector("#viewer");
const editorEl = document.querySelector("#editor");;
const initialValue = (
"# " + document.getElementById("data_form__title").value +
"\n" +
document.getElementById("data_form__body").value
);
const editor = new toastui.Editor({
el: editorEl,
initialEditType: 'wysiwyg',
initialValue: initialValue,
minHeight: '500px',
previewStyle: 'tab',
usageStatistics: false
});
const edit_btn = document.querySelector("#action__edit");
const save_btn = document.querySelector("#action__save");
toggleEditor = function() {
if (edit_btn.dataset.state == 'view') {
edit_btn.dataset.state = 'edit';
edit_btn.innerText = "discard draft";
viewerEl.style['display'] = "none";
editorEl.style['display'] = "inline";
save_btn.style['display'] = 'inline-block';
} else {
edit_btn.dataset.state = 'view';
edit_btn.innerText = "edit";
editorEl.style['display'] = "none";
viewerEl.style['display'] = "inline";
save_btn.style['display'] = 'none';
}
};
edit_btn.addEventListener('click', toggleEditor)
viewerEl.innerHTML = editor.getHTML();
</script>
<script src="/static/toastui-editor-all.js"></script>
<script src="/static/site.js"></script>
<script>
initialize();
</script>
{% endblock %}
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

+102 -13
View File
@@ -14,7 +14,7 @@ body {
margin: 0px;
box-sizing: border-box;
overflow-wrap: break-word;
overflow-y: scroll;
overflow-y: auto;
scrollbar-gutter: stable;
}
@@ -124,6 +124,10 @@ nav > ul > li {
position: relative;
}
nav > ul > li:first-child {
padding-left: 0px;
}
nav > ul > li > a {
color: #fff;
display: inline-block;
@@ -174,10 +178,12 @@ nav > ul > li:hover > a {
opacity: 1;
}
#breadcrumbs {
height: var(--breadcrumbs-height);
border-bottom: 1px dotted #DEDEDE;
display: block;
align-content: center;
height: 46px;
line-height: 46px;
width: 100%;
}
#data_form {
@@ -185,13 +191,22 @@ nav > ul > li:hover > a {
}
#editor {
display: none;
}
#viewer {
display: inline;
}
.toastui-editor-defaultUI-toolbar {
padding: 0px !important;
margin: 0px !important;
border: 0px !important;
background: transparent !important;
}
.toastui-editor-toolbar {
box-shadow: 0px 3px 5px #CCC;
margin-bottom: 20px !important;
}
/*
#actions {
display: flex;
float: right;
@@ -212,15 +227,20 @@ nav > ul > li:hover > a {
color: white;
background: blue;
}
*/
.main-aligned {
display: block;
margin: auto;
max-width: var(--max-width);
min-width: var(--min-width);
}
.table-wrapper {
padding: 0px;
padding-top: var(--wrapper-padding);
position: relative;
display: table;
margin: auto;
width: var(--max-width);
max-width: 960px;
width: 100%;
}
main {
@@ -256,6 +276,75 @@ footer {
font-family: var(--default-font-family) !important;
}
#editor .toastui-editor-md-container > div > div,
#editor .toastui-editor-ww-container > div > div {
border: 1px dotted #ccc;
}
#editor .toastui-editor-defaultUI-toolbar {
margin-bottom: 20px;
}
#editor .toastui-editor-toolbar {
position: sticky !important;
top: 0 !important;
z-index: 99 !important;
width: 100% !important;
background: #FFF !important;
}
/* applied to #editor */
.read-only {
}
#editor.read-only .toastui-editor-toolbar {
box-shadow: none;
}
#editor.read-only .toastui-editor-ww-container > div > div {
background: #FFF;
}
div.toastui-editor-md-splitter,
div.toastui-editor-md-preview {
display: none !Important;
}
div.toastui-editor.md-mode {
width: 100% !important;
}
#editor button.toastui-editor-toolbar-icons {
filter:unset;
pointer-events: all;
opacity: 1.0;
}
#editor.read-only button.toastui-editor-toolbar-icons {
filter:saturate(0) !Important;
pointer-events: none;
opacity: 0.3;
}
#editor button.actions {
background-image: none;
margin: 0;
}
#editor.read-only button.actions {
display: none;
}
#editor.read-only #toggleButton {
display: block !important;
}
.toastui-editor-defaultUI-toolbar > div:nth-child(5) {
flex: auto;
justify-content: flex-end;
}
menu {
display: none;
}
+119
View File
@@ -0,0 +1,119 @@
const editor = document.querySelector("#editor");
var toolBar = null;
var contents = null;
var pageContent = null;
var saveButton = null;
var editorUI = null;
isReadOnly = function() {
if (editor) {
return editor.className == 'read-only';
}
return undefined;
}
setReadOnly = function() {
pageContent.innerHTML = editorUI.getHTML();
editorUI.changeMode('wysiwig');
editorUI.blur();
editor.classList.add('read-only');
pageContent.contentEditable = false;
}
setEditable = function() {
editor.classList.remove('read-only');
pageContent.contentEditable = true;
editorUI.moveCursorToStart();
editorUI.focus();
}
makeMarkdownButton = function() {
button = document.createElement('button');
button.classList.add("toastui-editor-toolbar-icons");
button.classList.add("last");
button.ariaLabel = "Toggle Markdown";
button.style.backgroundImage = 'none';
button.innerHTML = 'MD';
button.style.margin = '0';
button.addEventListener('click', () => {
if (editorUI.isMarkdownMode()) {
editorUI.changeMode("wysiwig");
} else {
editorUI.changeMode("markdown");
}
});
return button;
};
makeSaveButton = function() {
const button = document.createElement('button');
button.className = 'actions';
button.innerHTML = 'save';
button.id = 'saveButton';
button.addEventListener('click', () => {
});
saveButton = button;
return button;
};
toggleButton = function() {
const button = document.createElement('button');
button.className = 'actions';
button.id = 'toggleButton';
button.innerHTML = 'edit';
button.addEventListener('click', () => {
if (isReadOnly()) {
setEditable();
} else {
setReadOnly();
}
});
return button;
}
handleContentChange = function() {
}
initialize = function() {
return new toastui.Editor({
el: editor,
initialEditType: 'wysiwyg',
initialValue: "",
hideModeSwitch : true,
previewStyle: 'vertical',
usageStatistics: false,
autofocus: false,
toolbarItems: [
['heading', 'bold', 'italic' ],
['ul', 'ol', 'indent', 'outdent'],
['table', 'image', 'link'],
[
{ el: makeMarkdownButton(), tooltip: 'Toggle MD' },
],
[
{ el: makeSaveButton(), command: 'save', tooltip: 'Save Changes' },
{ el: toggleButton(), tooltip: 'Toggle Edit Mode' }
],
],
events: {
'loadUI': function(e) {
editorUI = e;
pageContent = document.querySelector(".toastui-editor-ww-container > div > div");
toolBar = document.querySelector('.toastui-editor-toolbar');
e.setMarkdown(document.getElementById("data_form__body").value);
setReadOnly();
},
'changeMode': function() {
if (editor && Array(editor.classList).includes('read-only')) {
setReadOnly();
}
},
'change': handleContentChange,
}
});
};
+3 -2
View File
@@ -16,6 +16,7 @@
--footer-height: 150px;
--breadcrumbs-height: 50px;
--wrapper-padding: 20px;
--main-height: calc(100vh - var(--nav-height) - var(--footer-height) - var(--breadcrumbs-height) + var(--wrapper-padding));
--max-width: calc(100vw - 20px);
--main-height: calc(100vh - var(--nav-height) - var(--footer-height) - var(--breadcrumbs-height));
--max-width: 1024px;
--min-width: 710px;
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,24 @@
/*!
* @toast-ui/editor
* @version 3.2.2 | Fri Feb 17 2023
* @author NHN Cloud FE Development Lab <dl_javascript@nhn.com>
* @license MIT
*/
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.toastui=t():(e.toastui=e.toastui||{},e.toastui.Editor=t())}(self,(function(){return function(){var e={368:function(e){
/*! @license DOMPurify 2.3.3 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.3.3/LICENSE */
e.exports=function(){"use strict";function e(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t<e.length;t++)n[t]=e[t];return n}return Array.from(e)}var t=Object.hasOwnProperty,n=Object.setPrototypeOf,r=Object.isFrozen,o=Object.getPrototypeOf,i=Object.getOwnPropertyDescriptor,s=Object.freeze,a=Object.seal,l=Object.create,c="undefined"!=typeof Reflect&&Reflect,u=c.apply,d=c.construct;u||(u=function(e,t,n){return e.apply(t,n)}),s||(s=function(e){return e}),a||(a=function(e){return e}),d||(d=function(t,n){return new(Function.prototype.bind.apply(t,[null].concat(e(n))))});var p=x(Array.prototype.forEach),h=x(Array.prototype.pop),f=x(Array.prototype.push),m=x(String.prototype.toLowerCase),g=x(String.prototype.match),v=x(String.prototype.replace),y=x(String.prototype.indexOf),b=x(String.prototype.trim),w=x(RegExp.prototype.test),k=C(TypeError);function x(e){return function(t){for(var n=arguments.length,r=Array(n>1?n-1:0),o=1;o<n;o++)r[o-1]=arguments[o];return u(e,t,r)}}function C(e){return function(){for(var t=arguments.length,n=Array(t),r=0;r<t;r++)n[r]=arguments[r];return d(e,n)}}function T(e,t){n&&n(e,null);for(var o=t.length;o--;){var i=t[o];if("string"==typeof i){var s=m(i);s!==i&&(r(t)||(t[o]=s),i=s)}e[i]=!0}return e}function M(e){var n=l(null),r=void 0;for(r in e)u(t,e,[r])&&(n[r]=e[r]);return n}function S(e,t){for(;null!==e;){var n=i(e,t);if(n){if(n.get)return x(n.get);if("function"==typeof n.value)return x(n.value)}e=o(e)}function r(e){return console.warn("fallback value for",e),null}return r}var E=s(["a","abbr","acronym","address","area","article","aside","audio","b","bdi","bdo","big","blink","blockquote","body","br","button","canvas","caption","center","cite","code","col","colgroup","content","data","datalist","dd","decorator","del","details","dfn","dialog","dir","div","dl","dt","element","em","fieldset","figcaption","figure","font","footer","form","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","img","input","ins","kbd","label","legend","li","main","map","mark","marquee","menu","menuitem","meter","nav","nobr","ol","optgroup","option","output","p","picture","pre","progress","q","rp","rt","ruby","s","samp","section","select","shadow","small","source","spacer","span","strike","strong","style","sub","summary","sup","table","tbody","td","template","textarea","tfoot","th","thead","time","tr","track","tt","u","ul","var","video","wbr"]),N=s(["svg","a","altglyph","altglyphdef","altglyphitem","animatecolor","animatemotion","animatetransform","circle","clippath","defs","desc","ellipse","filter","font","g","glyph","glyphref","hkern","image","line","lineargradient","marker","mask","metadata","mpath","path","pattern","polygon","polyline","radialgradient","rect","stop","style","switch","symbol","text","textpath","title","tref","tspan","view","vkern"]),O=s(["feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feDistantLight","feFlood","feFuncA","feFuncB","feFuncG","feFuncR","feGaussianBlur","feMerge","feMergeNode","feMorphology","feOffset","fePointLight","feSpecularLighting","feSpotLight","feTile","feTurbulence"]),D=s(["animate","color-profile","cursor","discard","fedropshadow","feimage","font-face","font-face-format","font-face-name","font-face-src","font-face-uri","foreignobject","hatch","hatchpath","mesh","meshgradient","meshpatch","meshrow","missing-glyph","script","set","solidcolor","unknown","use"]),A=s(["math","menclose","merror","mfenced","mfrac","mglyph","mi","mlabeledtr","mmultiscripts","mn","mo","mover","mpadded","mphantom","mroot","mrow","ms","mspace","msqrt","mstyle","msub","msup","msubsup","mtable","mtd","mtext","mtr","munder","munderover"]),L=s(["maction","maligngroup","malignmark","mlongdiv","mscarries","mscarry","msgroup","mstack","msline","msrow","semantics","annotation","annotation-xml","mprescripts","none"]),I=s(["#text"]),R=s(["accept","action","align","alt","autocapitalize","autocomplete","autopictureinpicture","autoplay","background","bgcolor","border","capture","cellpadding","cellspacing","checked","cite","class","clear","color","cols","colspan","controls","controlslist","coords","crossorigin","datetime","decoding","default","dir","disabled","disablepictureinpicture","disableremoteplayback","download","draggable","enctype","enterkeyhint","face","for","headers","height","hidden","high","href","hreflang","id","inputmode","integrity","ismap","kind","label","lang","list","loading","loop","low","max","maxlength","media","method","min","minlength","multiple","muted","name","noshade","novalidate","nowrap","open","optimum","pattern","placeholder","playsinline","poster","preload","pubdate","radiogroup","readonly","rel","required","rev","reversed","role","rows","rowspan","spellcheck","scope","selected","shape","size","sizes","span","srclang","start","src","srcset","step","style","summary","tabindex","title","translate","type","usemap","valign","value","width","xmlns","slot"]),P=s(["accent-height","accumulatLine truncated
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
var e=function(t,n){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},e(t,n)};function t(t,n){if("function"!=typeof n&&null!==n)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}var o=function(){return o=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e},o.apply(this,arguments)};Object.create;function i(e,t,n){if(n||2===arguments.length)for(var r,o=0,i=t.length;o<i;o++)!r&&o in t||(r||(r=Array.prototype.slice.call(t,0,o)),r[o]=t[o]);return e.concat(r||Array.prototype.slice.call(t))}function s(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e}Object.create;function a(e){this.content=e}a.prototype={constructor:a,find:function(e){for(var t=0;t<this.content.length;t+=2)if(this.content[t]===e)return t;return-1},get:function(e){var t=this.find(e);return-1==t?void 0:this.content[t+1]},update:function(e,t,n){var r=n&&n!=e?this.remove(n):this,o=r.find(e),i=r.content.slice();return-1==o?i.push(n||e,t):(i[o+1]=t,n&&(i[o]=n)),new a(i)},remove:function(e){var t=this.find(e);if(-1==t)return this;var n=this.content.slice();return n.splice(t,2),new a(n)},addToStart:function(e,t){return new a([e,t].concat(this.remove(e).content))},addToEnd:function(e,t){var n=this.remove(e).content.slice();return n.push(e,t),new a(n)},addBefore:function(e,t,n){var r=this.remove(t),o=r.content.slice(),i=r.find(e);return o.splice(-1==i?o.length:i,0,t,n),new a(o)},forEach:function(e){for(var t=0;t<this.content.length;t+=2)e(this.content[t],this.content[t+1])},prepend:function(e){return(e=a.from(e)).size?new a(e.content.concat(this.subtract(e).content)):this},append:function(e){return(e=a.from(e)).size?new a(this.subtract(e).content.concat(e.content)):this},subtract:function(e){var t=this;e=a.from(e);for(var n=0;n<e.content.length;n+=2)t=t.remove(e.content[n]);return t},get size(){return this.content.length>>1}},a.from=function(e){if(e instanceof a)return e;var t=[];if(e)for(var n in e)t.push(n,e[n]);return new a(t)};var l=a;function c(e,t,n){for(let r=0;;r++){if(r==e.childCount||r==t.childCount)return e.childCount==t.childCount?null:n;let o=e.child(r),i=t.child(r);if(o!=i){if(!o.sameMarkup(i))return n;if(o.isText&&o.text!=i.text){for(let e=0;o.text[e]==i.text[e];e++)n++;return n}if(o.content.size||i.content.size){let e=c(o.content,i.content,n+1);if(null!=e)return e}n+=o.nodeSize}else n+=o.nodeSize}}function u(e,t,n,r){for(let o=e.childCount,i=t.childCount;;){if(0==o||0==i)return o==i?null:{a:n,b:r};let s=e.child(--o),a=t.child(--i),l=s.nodeSize;if(s!=a){if(!s.sameMarkup(a))return{a:n,b:r};if(s.isText&&s.text!=a.text){let e=0,t=Math.min(s.text.length,a.text.length);for(;e<t&&s.text[s.text.length-e-1]==a.text[a.text.length-e-1];)e++,n--,r--;return{a:n,b:r}}if(s.content.size||a.content.size){let e=u(s.content,a.content,n-1,r-1);if(e)return e}n-=l,r-=l}else n-=l,r-=l}}class d{constructor(e,t){if(this.content=e,this.size=t||0,null==t)for(let t=0;t<e.length;t++)this.size+=e[t].nodeSize}nodesBetween(e,t,n,r=0,o){for(let i=0,s=0;s<t;i++){let a=this.content[i],l=s+a.nodeSize;if(l>e&&!1!==n(a,r+s,o||null,i)&&a.content.size){let o=s+1;a.nodesBetween(Math.max(0,e-o),Math.min(a.content.size,t-o),n,r+o)}s=l}}descendants(e){this.nodesBetween(0,this.size,e)}textBetween(e,t,n,r){let o="",i=!0;return this.nodesBetween(e,t,((s,a)=>{s.isText?(o+=s.text.slice(Math.max(e,a)-a,t-a),i=!n):s.isLeaf?(r?o+="function"==typeof r?r(s):r:s.type.spec.leafText&&(o+=s.type.spec.leafText(s)),i=!n):!i&&s.isBlock&&(o+=n,i=!0)}),0),o}append(e){if(!e.size)return this;if(!this.size)return e;let t=this.lastChild,n=e.firstChild,r=this.content.slice(),o=0;for(t.isText&&t.sameMarkup(n)&&(r[r.length-1]=t.withText(t.text+n.text),o=1);o<e.content.length;o++)r.push(e.content[o]);return new d(r,this.size+e.size)}cut(e,t=this.size){if(0==e&&t==this.size)return this;let n=[],r=0;if(t>e)for(let o=0,i=0;i<t;o++){let s=this.content[o],a=i+s.nodeSize;a>e&&((i<e||a>t)&&(s=s.isText?s.cut(Math.max(0,e-i),Math.min(s.text.length,t-i)):s.cut(Math.max(0,e-i-1),Math.min(s.content.size,t-i-1))),n.push(s),r+=s.nodeSize),i=a}return new d(n,r)}cutByIndex(e,t){return e==t?d.empty:0==e&&t==this.content.length?this:new d(this.content.slice(e,t))}replaceChild(e,t){let n=this.content[e];if(n==t)return this;let r=this.content.slice(),o=this.size+t.nodeSize-n.nodeSize;return r[e]=t,new d(r,o)}addToStart(e){return new d([e].concat(this.content),this.size+e.nodeSize)}addToEnd(e){return new d(this.content.concat(e),this.size+e.nodeSize)}eq(e){if(this.content.length!=e.content.length)return!1;for(let t=0;t<this.content.length;t++)if(!this.content[t].eq(e.content[t]))return!1;return!0}get firstChild(){return Line truncated
File diff suppressed because it is too large. Load diff
+6 -5
View File
@@ -142,11 +142,12 @@ def edit(table, path):
@app.web.before_request
def before_request():
g.messages = []
user_id = session.get("user_id", 1)
g.user = app.db.User.get(doc_id=user_id)
session["user_id"] = user_id
session["user"] = dict(g.user.serialize())
g.root = get_page(app.config.VIEW_URI)
if not request.path.startswith('/static'):
user_id = session.get("user_id", 1)
g.user = app.db.User.get(doc_id=user_id)
session["user_id"] = user_id
session["user"] = dict(g.user.serialize())
g.root = get_page(app.config.VIEW_URI)
@app.web.after_request