- updated prefilled doc
- refactor separated editor syles and schema into separate components
This commit is contained in:
parent
468a454323
commit
8abd046aff
5 changed files with 351 additions and 282 deletions
27
README.md
27
README.md
|
@ -2,26 +2,31 @@
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Aren't you tired of distracting social media platforms?
|
Aren't you tired of distracting social media platforms?
|
||||||
So many buttons and advertising makes a simple task of reading
|
So many buttons and advertising makes a simple task of
|
||||||
and writing notes to your community very draining. You can use
|
reading and composing notes to your community very draining. You can use
|
||||||
a next generation social media protocol to transform
|
a next generation social media protocol to transform
|
||||||
your personal website into a micro social media client.
|
your personal website into a micro social media client
|
||||||
|
that will clear up space on your phone for media. </p>
|
||||||
|
|
||||||
Use a library like <a href="https://prosemirror.net/">ProseMirror</a>,
|
|
||||||
|
<p>
|
||||||
|
I'm using <a href="https://prosemirror.net/">ProseMirror</a>,
|
||||||
to build a WYSIWYM style rich text editor for visitors
|
to build a WYSIWYM style rich text editor for visitors
|
||||||
to compose notes with. They can then post it to a set of
|
to compose notes with. I'm taking inspiration from tumblr
|
||||||
programmed relays.
|
and medium's text editors to build something minimal and
|
||||||
|
intuitive that will run easily via browsers.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
<p>This is an installable personal website that is accessible through chrome-based and firefox browsers. The website is built with PWA Builder and Lit Web Components. It utilizes the Nostr
|
<p>I scaffolded the project with PWA Builder and am using
|
||||||
protocol to fetch profile metadata, short text, and long form notes (event kinds 0, 1, and 30023) from a relay. </p>
|
Lit to power up my Web Components. The website utilizes
|
||||||
|
the Nostr protocol to sign in via browser extension to
|
||||||
|
fetch profile metadata and pull event kinds 0, 1, and 30023
|
||||||
|
from a relay. </p>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<p>If you're on any chrome-based, firefox or safari browser try visiting the webpage then tapping on the arrow
|
<p>If you're on any chrome-based, firefox or safari browser try visiting the webpage then tapping on the arrow pointing up in the bottom toolbar. Scroll down a bit to tap on "Add to Home Screen".
|
||||||
pointing up in the bottom toolbar. Scroll down a bit to tap on "Add to Home Screen".
|
|
||||||
|
|
||||||
If you're on a chromium-based browser you should be able to do the same.</p>
|
If you're on a chromium-based browser you should be able to do the same.</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
157
src/components/schema.ts
Normal file
157
src/components/schema.ts
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
// custom-schema.ts
|
||||||
|
import { Schema, NodeSpec, MarkSpec } from "prosemirror-model";
|
||||||
|
|
||||||
|
// Define custom nodes
|
||||||
|
export const nodes = {
|
||||||
|
text: {
|
||||||
|
group: "inline",
|
||||||
|
} as NodeSpec,
|
||||||
|
star: {
|
||||||
|
inline: true,
|
||||||
|
group: "inline",
|
||||||
|
toDOM() {
|
||||||
|
return ["star", "⭐"];
|
||||||
|
},
|
||||||
|
parseDOM: [{ tag: "star" }],
|
||||||
|
} as NodeSpec,
|
||||||
|
paragraph: {
|
||||||
|
group: "block",
|
||||||
|
content: "inline*",
|
||||||
|
toDOM() {
|
||||||
|
return ["p", 0];
|
||||||
|
},
|
||||||
|
parseDOM: [{ tag: "p" }],
|
||||||
|
} as NodeSpec,
|
||||||
|
boring_paragraph: {
|
||||||
|
group: "block",
|
||||||
|
content: "text*",
|
||||||
|
marks: "",
|
||||||
|
toDOM() {
|
||||||
|
return ["p", { class: "boring" }, 0];
|
||||||
|
},
|
||||||
|
parseDOM: [{ tag: "p", priority: 60 }],
|
||||||
|
} as NodeSpec,
|
||||||
|
blockquote: {
|
||||||
|
content: "block+",
|
||||||
|
group: "block",
|
||||||
|
parseDOM: [{ tag: "blockquote" }],
|
||||||
|
toDOM() {
|
||||||
|
return ["blockquote", 0];
|
||||||
|
},
|
||||||
|
} as NodeSpec,
|
||||||
|
horizontal_rule: {
|
||||||
|
group: "block",
|
||||||
|
selectable: false,
|
||||||
|
parseDOM: [{ tag: "hr" }],
|
||||||
|
toDOM() {
|
||||||
|
return ["hr"];
|
||||||
|
},
|
||||||
|
} as NodeSpec,
|
||||||
|
heading: {
|
||||||
|
attrs: { level: { default: 1 } },
|
||||||
|
content: "inline*",
|
||||||
|
group: "block",
|
||||||
|
defining: true,
|
||||||
|
parseDOM: [
|
||||||
|
{ tag: "h1", attrs: { level: 1 } },
|
||||||
|
{ tag: "h2", attrs: { level: 2 } },
|
||||||
|
{ tag: "h3", attrs: { level: 3 } },
|
||||||
|
],
|
||||||
|
toDOM(node) {
|
||||||
|
return ["h" + node.attrs.level, 0];
|
||||||
|
},
|
||||||
|
} as NodeSpec,
|
||||||
|
code_block: {
|
||||||
|
content: "text*",
|
||||||
|
group: "block",
|
||||||
|
marks: "",
|
||||||
|
defining: true,
|
||||||
|
code: true,
|
||||||
|
parseDOM: [{ tag: "pre", preserveWhitespace: "full" }],
|
||||||
|
toDOM() {
|
||||||
|
return ["pre", ["code", 0]];
|
||||||
|
},
|
||||||
|
} as NodeSpec,
|
||||||
|
image: {
|
||||||
|
inline: true,
|
||||||
|
attrs: {
|
||||||
|
src: {},
|
||||||
|
alt: { default: null },
|
||||||
|
title: { default: null },
|
||||||
|
},
|
||||||
|
group: "inline",
|
||||||
|
draggable: true,
|
||||||
|
parseDOM: [
|
||||||
|
{
|
||||||
|
tag: "img[src]",
|
||||||
|
getAttrs(dom: any) {
|
||||||
|
return {
|
||||||
|
src: dom.getAttribute("src"),
|
||||||
|
alt: dom.getAttribute("alt"),
|
||||||
|
title: dom.getAttribute("title"),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
toDOM(node) {
|
||||||
|
return ["img", node.attrs];
|
||||||
|
},
|
||||||
|
} as NodeSpec,
|
||||||
|
doc: {
|
||||||
|
content: "block+",
|
||||||
|
} as NodeSpec,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Define custom marks
|
||||||
|
export const marks = {
|
||||||
|
shouting: {
|
||||||
|
toDOM() {
|
||||||
|
return ["shouting", 0];
|
||||||
|
},
|
||||||
|
parseDOM: [{ tag: "shouting" }],
|
||||||
|
} as MarkSpec,
|
||||||
|
link: {
|
||||||
|
attrs: { href: {} },
|
||||||
|
toDOM(node) {
|
||||||
|
return ["a", { href: node.attrs.href }, 0];
|
||||||
|
},
|
||||||
|
parseDOM: [
|
||||||
|
{
|
||||||
|
tag: "a",
|
||||||
|
getAttrs(dom) {
|
||||||
|
return { href: dom.getAttribute("href") };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
inclusive: false,
|
||||||
|
} as MarkSpec,
|
||||||
|
bold: {
|
||||||
|
parseDOM: [
|
||||||
|
{ tag: "strong" },
|
||||||
|
{ tag: "b", getAttrs: () => null },
|
||||||
|
{
|
||||||
|
style: "font-weight",
|
||||||
|
getAttrs: (value: string) => (value === "bold" ? null : false),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
toDOM() {
|
||||||
|
return ["strong", 0];
|
||||||
|
},
|
||||||
|
} as MarkSpec,
|
||||||
|
emphasis: {
|
||||||
|
parseDOM: [
|
||||||
|
{ tag: "em" },
|
||||||
|
{ tag: "i", getAttrs: () => null },
|
||||||
|
{
|
||||||
|
style: "font-style",
|
||||||
|
getAttrs: (value: string) => (value === "italic" ? null : false),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
toDOM() {
|
||||||
|
return ["em", 0];
|
||||||
|
},
|
||||||
|
} as MarkSpec,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Export the schema
|
||||||
|
export const customSchema = new Schema({ nodes, marks });
|
|
@ -1,4 +1,4 @@
|
||||||
import { LitElement, html, css } from 'lit';
|
import { LitElement, html } from 'lit';
|
||||||
import { customElement } from 'lit/decorators.js';
|
import { customElement } from 'lit/decorators.js';
|
||||||
|
|
||||||
import { styles } from './about-styles';
|
import { styles } from './about-styles';
|
||||||
|
@ -11,9 +11,7 @@ export class AppAbout extends LitElement {
|
||||||
static styles = [
|
static styles = [
|
||||||
sharedStyles,
|
sharedStyles,
|
||||||
styles,
|
styles,
|
||||||
css`
|
|
||||||
|
|
||||||
`
|
|
||||||
]
|
]
|
||||||
|
|
||||||
connectedCallback(): void {
|
connectedCallback(): void {
|
||||||
|
@ -31,10 +29,21 @@ export class AppAbout extends LitElement {
|
||||||
<h3>What am I looking at?</h3>
|
<h3>What am I looking at?</h3>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
This is a personal micro-social media client that can be installed to
|
This is a personal PWA installable
|
||||||
your desktop or smartphone home screen. You can sign in via an extension
|
to any home screen built with web
|
||||||
to display your profile data and notes from my relay. You
|
components, the nostr protocol,
|
||||||
can compose a note using a rich text editor I built with Prosemirror.
|
and ProseMirror.
|
||||||
|
|
||||||
|
<h3> What can I do with this?</h3>
|
||||||
|
<ul><li>You can use it to sign in with a
|
||||||
|
nostr identity and display your
|
||||||
|
profile data. </li><li>Visit the note
|
||||||
|
wall to view recent notes from my relay.</li>
|
||||||
|
<li>You can also compose a note using a rich
|
||||||
|
text editor I built with Prosemirror. </li>
|
||||||
|
Keep a folder of your top friends on
|
||||||
|
your home screen to show off and use to
|
||||||
|
leave them notes.</li></ul>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
@ -43,7 +52,12 @@ export class AppAbout extends LitElement {
|
||||||
<p>Look for "Add to Home Screen" in your browser toolbar
|
<p>Look for "Add to Home Screen" in your browser toolbar
|
||||||
to install it to your homescreen. </p>
|
to install it to your homescreen. </p>
|
||||||
|
|
||||||
<h3>What is Nostr?</h3>
|
<h3>What is ProseMirror</h3>
|
||||||
|
<p>ProseMirror is the rich text editor library under the
|
||||||
|
hood of Tip Tap. It seems overwhelming at first because
|
||||||
|
it's basically a lego set, but it gives you a cool
|
||||||
|
amount of control over the end product.</p>
|
||||||
|
<h3>What is nostr?</h3>
|
||||||
<p>Notes and Other Stuff Transmitted Over Relays is a simple open source
|
<p>Notes and Other Stuff Transmitted Over Relays is a simple open source
|
||||||
social media protocol that enables anybody to implement social media functionalities
|
social media protocol that enables anybody to implement social media functionalities
|
||||||
into their websites.</p>
|
into their websites.</p>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { LitElement, css, html } from 'lit';
|
import { LitElement, html } from 'lit';
|
||||||
import { customElement, query } from 'lit/decorators.js';
|
import { customElement, query } from 'lit/decorators.js';
|
||||||
|
|
||||||
import '@shoelace-style/shoelace/dist/components/card/card.js';
|
import '@shoelace-style/shoelace/dist/components/card/card.js';
|
||||||
|
@ -6,122 +6,35 @@ import '@shoelace-style/shoelace/dist/components/card/card.js';
|
||||||
import '@shoelace-style/shoelace/dist/components/button/button.js';
|
import '@shoelace-style/shoelace/dist/components/button/button.js';
|
||||||
|
|
||||||
import { styles } from '../../styles/shared-styles';
|
import { styles } from '../../styles/shared-styles';
|
||||||
|
import { editorStyles } from './write-styles';
|
||||||
|
|
||||||
|
|
||||||
import {EditorState} from 'prosemirror-state'
|
import {EditorState} from 'prosemirror-state'
|
||||||
import { Transaction } from 'prosemirror-state';
|
import { Transaction } from 'prosemirror-state';
|
||||||
import { EditorView } from 'prosemirror-view';
|
import { EditorView } from 'prosemirror-view';
|
||||||
import { Schema } from 'prosemirror-model';
|
import { customSchema } from '../../components/schema';
|
||||||
|
|
||||||
import { keymap } from 'prosemirror-keymap';
|
import { keymap } from 'prosemirror-keymap';
|
||||||
import { toggleMark } from 'prosemirror-commands';
|
import { toggleMark } from 'prosemirror-commands';
|
||||||
import {undo, redo, history} from 'prosemirror-history'
|
import {undo, redo, history} from 'prosemirror-history'
|
||||||
import { baseKeymap } from 'prosemirror-commands';
|
import { baseKeymap } from 'prosemirror-commands';
|
||||||
|
|
||||||
|
|
||||||
/* i begin by creating a custom schema. following the guide
|
// function for error reports
|
||||||
on prosemirror.net, i created a basic schema with a few
|
|
||||||
types of nodes.
|
|
||||||
|
|
||||||
texts are
|
|
||||||
|
|
||||||
*/
|
|
||||||
export const customSchema = new Schema({
|
|
||||||
nodes: {
|
|
||||||
text: {
|
|
||||||
group: 'inline',
|
|
||||||
},
|
|
||||||
star: {
|
|
||||||
inline: true,
|
|
||||||
group: 'inline',
|
|
||||||
toDOM() {
|
|
||||||
return ['star', '⭐'];
|
|
||||||
},
|
|
||||||
parseDOM: [{ tag: 'star' }],
|
|
||||||
},
|
|
||||||
paragraph: {
|
|
||||||
group: 'block',
|
|
||||||
content: 'inline*',
|
|
||||||
toDOM() {
|
|
||||||
return ['p', 0];
|
|
||||||
},
|
|
||||||
parseDOM: [{ tag: 'p' }],
|
|
||||||
},
|
|
||||||
boring_paragraph: {
|
|
||||||
group: 'block',
|
|
||||||
content: 'text*',
|
|
||||||
marks: '',
|
|
||||||
toDOM() {
|
|
||||||
return ['p', { class: 'boring' }, 0];
|
|
||||||
},
|
|
||||||
parseDOM: [{ tag: 'p', priority: 60 }],
|
|
||||||
},
|
|
||||||
hr: {
|
|
||||||
group: 'block',
|
|
||||||
selectable: true,
|
|
||||||
parseDOM: [{ tag: 'horizontal_rule' }],
|
|
||||||
toDOM() {
|
|
||||||
return ['hr', { class: 'horizontal-rule'}]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
doc: {
|
|
||||||
content: 'block+',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
marks: {
|
|
||||||
shouting: {
|
|
||||||
toDOM() {
|
|
||||||
return ['shouting', 0];
|
|
||||||
},
|
|
||||||
parseDOM: [{ tag: 'shouting' }],
|
|
||||||
},
|
|
||||||
link: {
|
|
||||||
attrs: { href: {} },
|
|
||||||
toDOM(node) {
|
|
||||||
return ['a', { href: node.attrs.href }, 0];
|
|
||||||
},
|
|
||||||
parseDOM: [
|
|
||||||
{
|
|
||||||
tag: 'a',
|
|
||||||
getAttrs(dom) {
|
|
||||||
return { href: dom };
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
inclusive: false,
|
|
||||||
},
|
|
||||||
bold: {
|
|
||||||
|
|
||||||
},
|
|
||||||
emphasis: {
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// function for error reports
|
|
||||||
|
|
||||||
function errorReport(message: string): void {
|
function errorReport(message: string): void {
|
||||||
console.error(message);
|
console.error(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* COMMANDS
|
||||||
|
commands are functions that take an editor state
|
||||||
|
and a dispatch function and returns a boolean
|
||||||
/* COMMANDS
|
to implement editing actions.*/
|
||||||
commands are functions that take an editor state
|
|
||||||
and a dispatch function and returns a boolean
|
|
||||||
to implement editing actions.*/
|
|
||||||
|
|
||||||
|
|
||||||
/**this command inserts a star by your cursor. it sets
|
/**this command inserts a star by your cursor. it sets
|
||||||
* type to the star node, and creates one when
|
* type to the star node, and creates one when
|
||||||
* dispatch is provided */
|
* dispatch is provided */
|
||||||
|
|
||||||
function insertStar(state: EditorState, dispatch?: (tr: Transaction) => void): boolean {
|
function insertStar(state: EditorState, dispatch?: (tr: Transaction) => void): boolean {
|
||||||
const star = customSchema.nodes.star;
|
const star = customSchema.nodes.star;
|
||||||
const { $from } = state.selection;
|
const { $from } = state.selection;
|
||||||
|
@ -140,10 +53,10 @@ function insertStar(state: EditorState, dispatch?: (tr: Transaction) => void): b
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* function that inserts an <hr> line break
|
/* function that inserts an <hr> line break*/
|
||||||
*/
|
|
||||||
function insertHR(state: EditorState, dispatch?: (tr: Transaction) => void): boolean {
|
function insertHR(state: EditorState, dispatch?: (tr: Transaction) => void): boolean {
|
||||||
const hr = customSchema.nodes.hr; // Access the HR node from the schema
|
const hr = customSchema.nodes.horizontal_rule; // attach the HR node from the schema to a variable
|
||||||
if (!hr) {
|
if (!hr) {
|
||||||
console.error('HR node is not defined in the schema.');
|
console.error('HR node is not defined in the schema.');
|
||||||
return false;
|
return false;
|
||||||
|
@ -155,32 +68,69 @@ function insertHR(state: EditorState, dispatch?: (tr: Transaction) => void): boo
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**command function that inserts a blockquote */
|
||||||
|
|
||||||
|
function insertQuote(state: EditorState, dispatch?: (tr: Transaction) => void): boolean {
|
||||||
|
console.log("you just put a blockquote");
|
||||||
|
|
||||||
|
const { schema, tr, selection } = state;
|
||||||
|
const blockquote = schema.nodes.blockquote;
|
||||||
|
|
||||||
|
// Handle when no selection is made (empty selection)
|
||||||
|
if (selection.empty) {
|
||||||
|
console.log("No selection, inserting a blockquote...");
|
||||||
|
|
||||||
|
const blockquoteNode = blockquote.createAndFill();
|
||||||
|
if (blockquoteNode) {
|
||||||
|
// Only dispatch if `dispatch` is defined
|
||||||
|
if (dispatch) {
|
||||||
|
dispatch(tr.replaceSelectionWith(blockquoteNode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log("Inserting blockquote at selection");
|
||||||
|
|
||||||
|
// Handle the case where there is a selection
|
||||||
|
const blockquoteNode = blockquote.createAndFill();
|
||||||
|
if (blockquoteNode) {
|
||||||
|
// Only dispatch if `dispatch` is defined
|
||||||
|
if (dispatch) {
|
||||||
|
dispatch(tr.replaceSelectionWith(blockquoteNode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* this command will apply the link mark to hyperlink your selection */
|
||||||
/* this command will prompt you for a url, which will
|
|
||||||
apply link mark to your selection and make your selection a hyperlink */
|
|
||||||
|
|
||||||
|
|
||||||
function toggleLink(state: EditorState, dispatch?: (tr: Transaction) => void): boolean {
|
function toggleLink(state: EditorState, dispatch?: (tr: Transaction) => void): boolean {
|
||||||
let {doc, selection} = state
|
let {doc, selection} = state;
|
||||||
if (selection.empty) return false
|
if (selection.empty) return false;
|
||||||
let attrs = null
|
let attrs = null
|
||||||
if (!doc.rangeHasMark(selection.from, selection.to, customSchema.marks.link)) {
|
if (!doc.rangeHasMark(selection.from, selection.to, customSchema.marks.link)) {
|
||||||
attrs = {href: prompt("Link to where?", "")}
|
attrs = {href: prompt("Paste your URL in here please", "")}
|
||||||
if (!attrs.href) return false
|
if (!attrs.href) return false
|
||||||
}
|
}
|
||||||
return toggleMark(customSchema.marks.link, attrs)(state, dispatch)
|
return toggleMark(customSchema.marks.link, attrs)(state, dispatch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* these functions add the bold and emphasis marks */
|
||||||
|
function toggleBold(state: EditorState, dispatch?: (tr: Transaction) => void): boolean {
|
||||||
|
return toggleMark(customSchema.marks.bold)(state, dispatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleEmphasis(state: EditorState, dispatch?: (tr: Transaction) => void): boolean {
|
||||||
|
return toggleMark(customSchema.marks.emphasis)(state, dispatch)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// custom keymap to apply to state
|
// custom keymap to apply to state
|
||||||
const customKeymap = keymap({
|
const customKeymap = keymap({
|
||||||
'Ctrl-Shift-Space': insertStar,
|
'Shift-Space': insertStar,
|
||||||
'Ctrl-b': (state, dispatch) => {
|
'Ctrl-g': (state, dispatch) => {
|
||||||
console.log("Ctrl-b pressed, toggling shouting mark...");
|
console.log("Ctrl-b pressed, toggling shouting mark...");
|
||||||
return toggleMark(customSchema.marks.shouting)(state, dispatch);
|
return toggleMark(customSchema.marks.shouting)(state, dispatch);
|
||||||
},
|
},
|
||||||
|
@ -188,17 +138,15 @@ const customKeymap = keymap({
|
||||||
console.log("you should have just gotten an alert to place a url into a hyperlink");
|
console.log("you should have just gotten an alert to place a url into a hyperlink");
|
||||||
return toggleLink(state, dispatch);
|
return toggleLink(state, dispatch);
|
||||||
},
|
},
|
||||||
|
'Ctrl-e': toggleEmphasis,
|
||||||
|
'Ctrl-b': toggleBold,
|
||||||
|
|
||||||
'Ctrl-Shift-h': insertHR,
|
'Ctrl-h': insertHR,
|
||||||
|
'Ctrl-q': insertQuote
|
||||||
|
},
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
@customElement('app-write')
|
@customElement('app-write')
|
||||||
export class AppWrite extends LitElement {
|
export class AppWrite extends LitElement {
|
||||||
|
@ -212,76 +160,44 @@ export class AppWrite extends LitElement {
|
||||||
static styles = [
|
static styles = [
|
||||||
styles,
|
styles,
|
||||||
|
|
||||||
css`
|
editorStyles
|
||||||
.ProseMirror {
|
|
||||||
background: black;
|
|
||||||
color: white;
|
|
||||||
background-clip: padding-box;
|
|
||||||
padding: 5px 0;
|
|
||||||
position: relative;
|
|
||||||
word-wrap: break-word;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
-webkit-font-variant-ligatures: none;
|
|
||||||
font-variant-ligatures: none;
|
|
||||||
padding: 1rem;
|
|
||||||
line-height: 1.2;
|
|
||||||
outline: none;
|
|
||||||
font-family: var(
|
|
||||||
--markdown-editor-typography-font-family,
|
|
||||||
var(--mdc-typography-font-family, Montserrat, sans-serif)
|
|
||||||
);
|
|
||||||
font-size: var(
|
|
||||||
--markdown-editor-typography-font-size,
|
|
||||||
var(--mdc-typography-subtitle1-font-size, 1rem)
|
|
||||||
);
|
|
||||||
font-weight: var(
|
|
||||||
--markdown-editor-typography-font-weight,
|
|
||||||
var(--mdc-typography-subtitle1-font-weight, 400)
|
|
||||||
);
|
|
||||||
letter-spacing: var(
|
|
||||||
--markdown-editor-typography-letter-spacing,
|
|
||||||
var(--mdc-typography-subtitle1-letter-spacing, 0.009375em)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ProseMirror pre {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ProseMirror a {
|
|
||||||
color: var(--markdown-editor-typography-anchor-color, -webkit-link);
|
|
||||||
text-decoration: var(--markdown-editor-typography-anchor-text-decoration);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ProseMirror-focused .ProseMirror-gapcursor {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
shouting {
|
|
||||||
all: unset; /* Remove inherited or conflicting styles */
|
|
||||||
font-weight: bold;
|
|
||||||
text-transform: uppercase;
|
|
||||||
color: red; }
|
|
||||||
|
|
||||||
.boring {
|
|
||||||
background: grey;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
protected async firstUpdated() {
|
super();
|
||||||
console.log("Welcome to the compose page");
|
|
||||||
await this.updateComplete;
|
|
||||||
console.log('Rendered HTML:', this.shadowRoot?.innerHTML);
|
|
||||||
|
|
||||||
this.initializeEditor()
|
|
||||||
if (!this.editorContainer) {
|
|
||||||
errorReport('Editor container not here');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.editorView.destroy
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
connectedCallback(): void {
|
||||||
|
|
||||||
|
super.connectedCallback();
|
||||||
|
console.log('AppWrite added to the DOM')
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback(): void {
|
||||||
|
super.disconnectedCallback();
|
||||||
|
if (this.editorView) {
|
||||||
|
this.editorView.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
protected async firstUpdated() {
|
||||||
|
console.log("Welcome to the compose page");
|
||||||
|
await this.updateComplete;
|
||||||
|
|
||||||
|
this.initializeEditor()
|
||||||
|
if (!this.editorContainer) {
|
||||||
|
errorReport('Editor container not here');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private initializeEditor() {
|
private initializeEditor() {
|
||||||
|
|
||||||
if (!this.editorContainer) {
|
if (!this.editorContainer) {
|
||||||
|
@ -289,84 +205,75 @@ export class AppWrite extends LitElement {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this is a schema of initial content to pre-populate the editor
|
/* a prepopulated document of initial content to guide the user */
|
||||||
with guidance */
|
|
||||||
|
|
||||||
|
|
||||||
let doc = customSchema.node("doc", null, [
|
let doc = customSchema.node('doc', null, [
|
||||||
customSchema.node("paragraph", null, [customSchema.text("write anything")]),
|
customSchema.node('heading', null, [customSchema.text('Título')]),
|
||||||
customSchema.node("boring_paragraph", null, [customSchema.text("you can't apply any marks to text in this boring paragraph")])
|
|
||||||
|
customSchema.node('blockquote', null, [
|
||||||
|
customSchema.node('paragraph', null, [customSchema.text('"WEPA"')]),
|
||||||
|
]),
|
||||||
|
customSchema.node('paragraph', null, [customSchema.text('Escribe algo')]),
|
||||||
|
customSchema.node('boring_paragraph', null, [
|
||||||
|
customSchema.text('no se puede marcar ningún texto en párrafos aburridos como este. mira el <hr> debajo de este párafo. puedes colocar uno con Ctrl + h'),
|
||||||
|
]),
|
||||||
|
customSchema.node('horizontal_rule', null),
|
||||||
|
customSchema.node('paragraph', null, [customSchema.text('Escribe mas aquí...')]),
|
||||||
|
customSchema.node('paragraph', null, [customSchema.text('Presiona Shift + Space para colocar una estrella')]),
|
||||||
|
customSchema.node('paragraph', null, [customSchema.text('Presiona Ctrl + g para marcar la selección con la marca "shouting" ')])
|
||||||
|
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
])
|
if (!doc) {
|
||||||
|
|
||||||
if (!doc) {
|
|
||||||
console.error("failed to create initial document")
|
console.error("failed to create initial document")
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* here i create a state with doc as the schema,
|
/* here i create a state with doc to,
|
||||||
which includes a couple */
|
which places the prepopulated document of content
|
||||||
|
and include history and keymap as plugins
|
||||||
|
*/
|
||||||
|
|
||||||
const state = EditorState.create({
|
const state = EditorState.create({
|
||||||
doc: doc,
|
doc: doc,
|
||||||
plugins: [
|
plugins: [
|
||||||
history(),
|
history(),
|
||||||
keymap({
|
keymap({
|
||||||
'Mod-z': undo,
|
'Mod-z': undo,
|
||||||
'Mod-y': redo,
|
'Mod-y': redo,
|
||||||
}),
|
}),
|
||||||
customKeymap,
|
customKeymap,
|
||||||
keymap(baseKeymap),
|
keymap(baseKeymap),
|
||||||
|
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/* this */
|
||||||
|
|
||||||
|
this.editorView = new EditorView(this.editorContainer, {
|
||||||
let view = new EditorView(this.editorContainer, {
|
state,
|
||||||
state,
|
dispatchTransaction: (transaction) => {
|
||||||
dispatchTransaction(transaction) {
|
console.log('Document size went from', transaction.before.content.size, "to",
|
||||||
console.log('Document size went from', transaction.before.content.size, "to",
|
transaction.doc.content.size
|
||||||
transaction.doc.content.size
|
)
|
||||||
)
|
let newState = this.editorView.state.apply(transaction);
|
||||||
let newState = view.state.apply(transaction)
|
this.editorView.updateState(newState);
|
||||||
view.updateState(newState)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
console.log(state)
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
console.log(state)
|
||||||
|
|
||||||
connectedCallback(): void {
|
}
|
||||||
super.connectedCallback();
|
|
||||||
|
|
||||||
console.log('AppWrite added to the DOM')
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnectedCallback(): void {
|
|
||||||
super.disconnectedCallback();
|
|
||||||
if (this.editorView) {
|
|
||||||
this.editorView.destroy();
|
|
||||||
this.editorView = null!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
return html`
|
return html`
|
||||||
<main><app-header ?enableBack="${true}"></app-header>
|
<main><app-header ?enableBack="${true}"></app-header>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="ProseMirror"></div>
|
<div class="ProseMirror"></div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
import { css } from "lit"
|
import { css } from "lit"
|
||||||
|
|
||||||
export const editorStyles = css` {
|
export const editorStyles = css`
|
||||||
|
|
||||||
|
:host .ProseMirror {
|
||||||
|
background: black;
|
||||||
|
color: white;
|
||||||
.ProseMirror {
|
|
||||||
background: white;
|
|
||||||
color: black;
|
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
padding: 5px 0;
|
padding: 5px 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -36,7 +33,7 @@ export const editorStyles = css` {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ProseMirror pre {
|
.ProseMirror pre {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,8 +45,7 @@ export const editorStyles = css` {
|
||||||
.ProseMirror-focused .ProseMirror-gapcursor {
|
.ProseMirror-focused .ProseMirror-gapcursor {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
shouting {
|
||||||
shouting {
|
|
||||||
all: unset; /* Remove inherited or conflicting styles */
|
all: unset; /* Remove inherited or conflicting styles */
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
@ -58,21 +54,11 @@ export const editorStyles = css` {
|
||||||
.boring {
|
.boring {
|
||||||
background: grey;
|
background: grey;
|
||||||
}
|
}
|
||||||
|
blockquote {
|
||||||
.plus {
|
font-style: italic;
|
||||||
position: absolute;
|
border-left: 2px solid gray;
|
||||||
|
padding-left: 10px;
|
||||||
padding: 8px;
|
color: darkgray;
|
||||||
background-color: #4CAF50;
|
|
||||||
color: red;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.plus:hover {
|
|
||||||
background-color: #45a049;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue