started to implement a horizontal rule command

updated customKeymap with a keyboard shortcut to insert hr element
updated editor appearance
implemented a custom schema filler doc that prefilled a couple of nodes into the editor
moved css to component because it wasn't reading from a separate component
This commit is contained in:
miggymofongo 2025-01-08 01:05:20 -04:00
parent d4d85804bd
commit 468a454323
3 changed files with 148 additions and 130 deletions

View file

@ -13,14 +13,19 @@ import { Transaction } from 'prosemirror-state';
import { EditorView } from 'prosemirror-view';
import { Schema } from 'prosemirror-model';
import { MenuBar } from './write-menu';
import { keymap } from 'prosemirror-keymap';
import { toggleMark } from 'prosemirror-commands';
import {undo, redo, history} from 'prosemirror-history'
import { baseKeymap } from 'prosemirror-commands';
/* i begin by creating a custom schema. following the guide
on prosemirror.net, i created a basic schema with a few
types of nodes.
texts are
*/
export const customSchema = new Schema({
nodes: {
text: {
@ -49,7 +54,15 @@ export const customSchema = new Schema({
toDOM() {
return ['p', { class: 'boring' }, 0];
},
parseDOM: [{ tag: 'p.boring', priority: 60 }],
parseDOM: [{ tag: 'p', priority: 60 }],
},
hr: {
group: 'block',
selectable: true,
parseDOM: [{ tag: 'horizontal_rule' }],
toDOM() {
return ['hr', { class: 'horizontal-rule'}]
}
},
doc: {
content: 'block+',
@ -77,9 +90,20 @@ export const customSchema = new Schema({
],
inclusive: false,
},
bold: {
},
emphasis: {
}
},
});
// function for error reports
function errorReport(message: string): void {
@ -87,25 +111,53 @@ export const customSchema = new Schema({
}
// Commands
/* COMMANDS
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
* type to the star node, and creates one when
* dispatch is provided */
function insertStar(state: EditorState, dispatch?: (tr: Transaction) => void): boolean {
const type = customSchema.nodes.star;
const star = customSchema.nodes.star;
const { $from } = state.selection;
// If the parent cannot replace with the 'star' node, return false
if (!$from.parent.canReplaceWith($from.index(), $from.index(), type)) {
if (!$from.parent.canReplaceWith($from.index(), $from.index(), star)) {
return false;
}
// If dispatch is provided, apply the transaction
if (dispatch) {
dispatch(state.tr.replaceSelectionWith(type.create()));
dispatch(state.tr.replaceSelectionWith(star.create()));
}
// Always return true if insertion conditions are met
return true;
}
/* function that inserts an <hr> line break
*/
function insertHR(state: EditorState, dispatch?: (tr: Transaction) => void): boolean {
const hr = customSchema.nodes.hr; // Access the HR node from the schema
if (!hr) {
console.error('HR node is not defined in the schema.');
return false;
}
if (dispatch) {
dispatch(state.tr.replaceSelectionWith(hr.create()));
}
return true;
}
/* this command will prompt you for a url, which will
apply link mark to your selection and make your selection a hyperlink */
@ -122,47 +174,29 @@ function toggleLink(state: EditorState, dispatch?: (tr: Transaction) => void): b
}
/* starting to build a function that inserts an <hr> line break
function insertHR(state: EditorState, dispatch?: (tr: Transaction) => void): boolean {
const type = customSchema.nodes.boring_paragraph
return false
} */
// custom keymap to apply to state
const customKeymap = keymap({
'Ctrl-Space': insertStar,
'Ctrl-Shift-Space': insertStar,
'Ctrl-b': (state, dispatch) => {
console.log("Ctrl-b pressed, toggling shouting mark...");
return toggleMark(customSchema.marks.shouting)(state, dispatch);
},
'Ctrl-q': (state, dispatch) => {
console.log("you should have just gotten an alert");
'Ctrl-k': (state, dispatch) => {
console.log("you should have just gotten an alert to place a url into a hyperlink");
return toggleLink(state, dispatch);
},
'Ctrl-Shift-h': insertHR,
});
const menuItems = [
{
label: 'Bold',
command: (state, dispatch, view) => {
// Example command logic for bold
console.log('Bold clicked');
return true; // Return true if active, false otherwise
},
},
{
label: 'Italic',
command: (state, dispatch, view) => {
console.log('Italic clicked');
return true;
},
},
];
@ -177,86 +211,60 @@ export class AppWrite extends LitElement {
static styles = [
styles,
css`
.ProseMirror {
background: black;
color: white;
background-clip: padding-box;
padding: 5px 0;
position: relative;
white-space: pre-wrap;
word-wrap: break-word;
outline: none;
background-color: white;
color: black;
border-radius: 4px;
padding: 10px;
min-height: 200px; /* Ensure there's enough height to type */
}
.ProseMirror:empty::before {
content: "Start typing here...";
color: black;
pointer-events: none; /* Ensures the placeholder doesn't block typing */
}
.ProseMirror {
word-wrap: break-word;
white-space: pre-wrap;
white-space: break-spaces;
-webkit-font-variant-ligatures: none;
font-variant-ligatures: none;
font-feature-settings: "liga" 0; /* the above doesn't seem to work in Edge */
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 li {
position: relative;
.ProseMirror a {
color: var(--markdown-editor-typography-anchor-color, -webkit-link);
text-decoration: var(--markdown-editor-typography-anchor-text-decoration);
}
.ProseMirror-hideselection *::selection { background: transparent; }
.ProseMirror-hideselection *::-moz-selection { background: transparent; }
.ProseMirror-hideselection { caret-color: transparent; }
/* See https://github.com/ProseMirror/prosemirror/issues/1421#issuecomment-1759320191 */
.ProseMirror [draggable][contenteditable=false] { user-select: text }
.ProseMirror-selectednode {
outline: 2px solid #8cf;
.ProseMirror-focused .ProseMirror-gapcursor {
display: block;
}
/* Make sure li selections wrap around markers */
li.ProseMirror-selectednode {
outline: none;
}
li.ProseMirror-selectednode:after {
content: "";
position: absolute;
left: -32px;
right: -2px; top: -2px; bottom: -2px;
border: 2px solid #8cf;
pointer-events: none;
}
/* Protect against generic img rules */
img.ProseMirror-separator {
display: inline !important;
border: none !important;
margin: 0 !important;
}
shouting {
all: unset; /* Remove inherited or conflicting styles */
font-weight: bold;
text-transform: uppercase;
color: red;
}
color: red; }
.boring {
background: grey;
}
`
];
@ -271,6 +279,7 @@ export class AppWrite extends LitElement {
errorReport('Editor container not here');
return;
}
}
private initializeEditor() {
@ -280,22 +289,29 @@ export class AppWrite extends LitElement {
return;
}
/* this is a schema of initial content to pre-populate the */
const initialContent = customSchema.nodes.doc.createAndFill([
customSchema.nodes.paragraph.create(
null,
customSchema.text("Welcome to the editor! Start typing here.")
)]);
/* this is a schema of initial content to pre-populate the editor
with guidance */
if (!initialContent) {
let doc = customSchema.node("doc", null, [
customSchema.node("paragraph", null, [customSchema.text("write anything")]),
customSchema.node("boring_paragraph", null, [customSchema.text("you can't apply any marks to text in this boring paragraph")])
])
if (!doc) {
console.error("failed to create initial document")
return;
}
/* here i create a state with doc as the schema,
which includes a couple */
const state = EditorState.create({
doc: initialContent,
doc: doc,
plugins: [
history(),
keymap({
@ -304,11 +320,13 @@ export class AppWrite extends LitElement {
}),
customKeymap,
keymap(baseKeymap),
],
});
let view = new EditorView(this.editorContainer, {
state,
dispatchTransaction(transaction) {
@ -322,6 +340,7 @@ export class AppWrite extends LitElement {
console.log(state)
}
@ -344,13 +363,13 @@ export class AppWrite extends LitElement {
protected render() {
return html`
<app-header ?enableBack="${true}"></app-header>
<main><app-header ?enableBack="${true}"></app-header>
<main>
<div class="ProseMirror"></div>
</main>
`;
}
}

View file

@ -4,6 +4,7 @@ export const editorStyles = css` {
.ProseMirror {
background: white;
color: black;
@ -52,8 +53,7 @@ shouting {
all: unset; /* Remove inherited or conflicting styles */
font-weight: bold;
text-transform: uppercase;
color: red;
}
color: red; }
.boring {
background: grey;

View file

@ -45,6 +45,5 @@ export const styles = css`
flex-direction: row;
align-items: flex-start;
justify-content: space-between;
}
}}
`