This commit is contained in:
Martin Kleinschrodt 2022-07-16 13:48:28 +02:00
parent be067e5008
commit 9450945143
9 changed files with 5824 additions and 3824 deletions

File diff suppressed because it is too large Load Diff

View File

@ -30,6 +30,7 @@
"@types/marked": "4.0.3",
"@types/papaparse": "5.2.5",
"@types/qrcode": "1.4.1",
"@types/turndown": "5.0.1",
"@types/ua-parser-js": "0.7.36",
"@types/workbox-precaching": "4.3.1",
"@types/workbox-sw": "4.3.1",
@ -49,6 +50,7 @@
"papaparse": "5.3.1",
"qrcode": "1.5.0",
"reflect-metadata": "0.1.13",
"turndown": "7.1.1",
"typescript": "4.4.3",
"ua-parser-js": "0.7.28",
"workbox-precaching": "6.2.0",

View File

@ -18,6 +18,7 @@ import { descriptionForAudit, iconForAudit, titleTextForAudit } from "../lib/aud
import "./popover";
import "./rich-input";
import "./rich-content";
import { RichInput } from "./rich-input";
@customElement("pl-field")
export class FieldElement extends LitElement {
@ -218,12 +219,15 @@ export class FieldElement extends LitElement {
.value-display {
display: block;
margin: 0 0.4em 0.4em 1.5em;
white-space: pre-wrap;
overflow-wrap: break-word;
user-select: text;
cursor: text;
}
pre.value-display {
white-space: pre-wrap;
overflow-wrap: break-word;
}
.value-display.small {
margin-left: 1.8em;
}
@ -262,7 +266,7 @@ export class FieldElement extends LitElement {
case "note":
return html` <pl-rich-content
class="small value-display"
type="html"
type="markdown"
.content=${this.field.value}
></pl-rich-content>`;
default:
@ -276,7 +280,7 @@ export class FieldElement extends LitElement {
return html`
<pl-rich-input
class="small value-input"
@input=${() => (this.field.value = this._valueInput.value)}
@input=${(e: Event) => (this.field.value = (e.target! as RichInput).value)}
.value=${this.field.value}
>
</pl-rich-input>

View File

@ -610,6 +610,10 @@ export class PlIcon extends LitElement {
content: "\\f033";
}
:host([icon="strikethrough"]) > div::before {
content: "\\f0cc";
}
:host([icon="list"]) > div::before {
content: "\\f03a";
}

View File

@ -733,7 +733,7 @@ export class ItemsList extends StateMixin(LitElement) {
<div class="horizontal layout">
<pl-button class="slim transparent" @click=${() => (this.multiSelect = true)}>
<pl-icon icon="checked"></pl-icon>
<pl-icon icon="list-check"></pl-icon>
</pl-button>
<pl-button

View File

@ -3,7 +3,7 @@ import { sanitize } from "dompurify";
import { html, LitElement } from "lit";
import { customElement, property } from "lit/decorators.js";
import { unsafeHTML } from "lit/directives/unsafe-html.js";
import { mardownToHtml } from "../lib/markdown";
import { markdownToLitTemplate } from "../lib/markdown";
import { content, shared } from "../styles";
import { icons } from "../styles/icons";
@ -37,7 +37,7 @@ export class RichContent extends LitElement {
render() {
switch (this.type) {
case "markdown":
return mardownToHtml(this.content, this.sanitize);
return markdownToLitTemplate(this.content, this.sanitize);
case "html":
const content = this.sanitize
? sanitize(this.content, { ADD_TAGS: ["pl-icon"], ADD_ATTR: ["icon"] })

View File

@ -6,23 +6,28 @@ import "./button";
import "./icon";
import "./list";
import "./popover";
// import { EmojiClickEvent } from "./emoji-picker";
// import "./emoji-picker";
import { htmlToMarkdown, markdownToHtml } from "../lib/markdown";
@customElement("pl-rich-input")
export class RichInput extends LitElement {
get value() {
return this._editor.getHTML();
const html = this._editor.getHTML();
const md = htmlToMarkdown(html);
return md;
}
set value(content: string) {
const html = markdownToHtml(content).replace(/\n/g, "");
this._editor.commands.clearContent();
this._editor.commands.insertContent(content);
this._editor.commands.insertContent(html);
}
private _editor = new Editor({
extensions: [StarterKit],
onTransaction: () => this.requestUpdate(),
onTransaction: () => {
this.requestUpdate();
this.dispatchEvent(new CustomEvent("input"));
},
onFocus: () => this.classList.add("focused"),
onBlur: () => this.classList.remove("focused"),
});
@ -36,13 +41,6 @@ export class RichInput extends LitElement {
this._editor.commands.focus();
}
// private _emojiSelected(e: EmojiClickEvent) {
// console.log(e);
// if (e.detail.unicode) {
// this._editor.chain().focus().insertContent(e.detail.unicode).run();
// }
// }
static styles = [
shared,
content,
@ -150,14 +148,6 @@ export class RichInput extends LitElement {
<pl-icon icon="list-ol"></pl-icon>
</pl-button>
<pl-button
class="transparent slim"
.toggled=${this._editor?.isActive("orderedList")}
@click=${() => this._editor.chain().focus().toggleOrderedList().run()}
>
<pl-icon icon="list-check"></pl-icon>
</pl-button>
<pl-button
class="transparent slim"
.toggled=${this._editor?.isActive("blockquote")}

View File

@ -1,8 +1,53 @@
import { addHook, sanitize } from "dompurify";
import { marked } from "marked";
import TurnDown from "turndown";
import { unsafeHTML } from "lit/directives/unsafe-html.js";
import { html } from "lit";
const turndown = new TurnDown({
// blankReplacement: (_content, node) => {
// return node.nodeName === "P" ? `<p>&nbsp;</p>\n` : "";
// },
});
turndown.addRule("p", {
filter: "p",
replacement: (content, node) => {
if (node.nextSibling) {
content = content + "\n\n";
}
if (node.previousSibling) {
content = "\n\n" + content;
}
return content;
},
});
turndown.addRule("strikethrough", {
filter: ["s"],
replacement: function (content) {
return "~" + content + "~";
},
});
turndown.addRule("li", {
filter: "li",
replacement: (content, node, options) => {
content = content
.replace(/^\n+/, "") // remove leading newlines
.replace(/\n+$/, "\n") // replace trailing newlines with just a single one
.replace(/\n/gm, "\n "); // indent
var prefix = options.bulletListMarker + " ";
var parent = node.parentNode as HTMLElement | null;
if (parent?.nodeName === "OL") {
var start = parent.getAttribute("start");
var index = Array.prototype.indexOf.call(parent.children, node);
prefix = (start ? Number(start) + index : index + 1) + ". ";
}
return prefix + content + (node.nextSibling && !/\n$/.test(content) ? "\n" : "");
},
});
// Add a hook to make all links open a new window
addHook("afterSanitizeAttributes", function (node) {
// set all elements owning target to target=_blank
@ -11,12 +56,21 @@ addHook("afterSanitizeAttributes", function (node) {
}
});
export function mardownToHtml(md: string, san = true) {
export function markdownToHtml(md: string, san = true) {
let markup = marked(md, {
headerIds: false,
});
if (san) {
markup = sanitize(markup);
}
return markup;
}
export function htmlToMarkdown(html: string) {
return turndown.turndown(html);
}
export function markdownToLitTemplate(md: string, san = true) {
const markup = markdownToHtml(md, san);
return html`${unsafeHTML(markup)}`;
}

View File

@ -190,7 +190,7 @@ export const FIELD_DEFS: { [t in FieldType]: FieldDef } = {
return $l("Note");
},
format(value: string) {
return value.replace(/(<([^>]+)>)/gi, " ");
return value.split("\n")[0] || "";
},
},
[FieldType.Text]: {