Coder Social home page Coder Social logo

nenadpnc / cl-editor Goto Github PK

View Code? Open in Web Editor NEW
291.0 11.0 22.0 3.44 MB

Lightweight text editor built with svelte, typescript

Home Page: https://nenadpnc.github.io/cl-editor/

License: MIT License

HTML 5.67% JavaScript 46.20% SCSS 20.32% Svelte 27.80%
editor wysiwyg wysiwyg-html-editor html-editor typescript svelte

cl-editor's Introduction

Lightweight text editor

Built with svelte (no external dependencies)

File size (bundle includes css, html and js)

  • min: 30kb
  • gzip: 10kb

Installation

npm:

npm install --save cl-editor

HTML:

<head>
  ...
</head>
<body>
  ...
  <div id="editor"></div>
  ...
</body>

Usage

import Editor from 'cl-editor';
// or
const Editor = require('cl-editor');
// Initialize editor
const editor = new Editor({
    // <HTMLElement> required
    target: document.getElementById('editor'),
    // optional
    props: {
        // <Array[string | Object]> string if overwriting, object if customizing/creating
        // available actions:
        // 'viewHtml', 'undo', 'redo', 'b', 'i', 'u', 'strike', 'sup', 'sub', 'h1', 'h2', 'p', 'blockquote', 
        // 'ol', 'ul', 'hr', 'left', 'right', 'center', 'justify', 'a', 'image', 'forecolor', 'backcolor', 'removeFormat'
        actions: [
            'b', 'i', 'u', 'strike', 'ul', 'ol',
            {
                name: 'copy', // required
                icon: '<b>C</b>', // string or html string (ex. <svg>...</svg>)
                title: 'Copy',
                result: () => {
                    // copy current selection or whole editor content
                    const selection = window.getSelection();
                    if (!selection.toString().length) {
                        const range = document.createRange();
                        range.selectNodeContents(editor.refs.editor);
                        selection.removeAllRanges();
                        selection.addRange(range);
                    }
                    editor.exec('copy');
                }
            },
            'h1', 'h2', 'p'
        ],
        // default 300px
        height: '300px',
        // initial html
        html: '',
        // remove format action clears formatting, but also removes some html tags.
        // you can specify which tags you want to be removed.
        removeFormatTags: ['h1', 'h2', 'blackquote'] // default
    }
})

API

// Methods
editor.exec(cmd: string, value?: string) // execute document command (document.executeCommand(cmd, false, value))
editor.getHtml(sanitize?: boolean) // returns html string from editor. if passed true as argument, html will be sanitized before return
editor.getText() // returns text string from editor
editor.setHtml(html: string, sanitize?: boolean) // sets html for editor. if second argument is true, html will be sanitized
editor.saveRange() // saves current editor cursor position or user selection
editor.restoreRange() // restores cursor position or user selection
// saveRange and restoreRange are useful when making custom actions
// that demands that focus is shifted from editor to, for example, modal window.
// Events
editor.$on('change', (event) => console.log(event)) // on every keyup event
editor.$on('blur', (event) => console.log(event)) // on editor blur event
// Props
editor.refs.<editor | raw | modal | colorPicker> // references to editor, raw (textarea), modal and colorPicker HTMLElements

Actions

The actions prop lists predefined actions (and/or adds new actions) to be shown in the toolbar. If the prop is not set, all actions defined and exported in actions.js are made available, in the order in which they are defined. To limit or change the order of predefined actions shown, set it by passing an array of names of actions defined, eg.:

actions={["b", "i", "u", "h2", "ul", "left", "center", "justify", "forecolor"]}

The editor looks up to see if name is already defined, and adds it to the toolbar if it is.

You can add a custom action by inserting it in the array, like how "copy" is defined in example above. Take a look at actions.js for more examples.

Usage in Svelte

It is easier to import and work directly from the source if you are using Svelte. You can handle change events via on:change.

<script>
  import Editor from "cl-editor/src/Editor.svelte"

  let html = '<h3>hello</h3>'
  let editor

</script>

{@html html}
<Editor {html} on:change={(evt)=>html = evt.detail}/>

Example of customising the color picker palette

<script>
  import Editor from "cl-editor/src/Editor.svelte"

  let html = '<h3>hello</h3>'
  let colors = ['#000000', '#e60000', '#ff9900', '#ffff00', '#008a00', '#0066cc', '#9933ff',
        '#ffffff', '#facccc', '#ffebcc', '#ffffcc', '#cce8cc', '#cce0f5', '#ebd6ff',
        '#bbbbbb', '#f06666', '#ffc266', '#ffff66', '#66b966', '#66a3e0', '#c285ff',
        '#888888', '#a10000', '#b26b00', '#b2b200', '#006100', '#0047b2', '#6b24b2',
        '#444444', '#5c0000', '#663d00', '#666600', '#003700', '#002966', '#3d1466']
  let editor

</script>

{@html html}
<Editor {html} {colors} on:change={(evt)=>html = evt.detail}/>

To limit or define the tools shown in the toolbar, pass in an actions prop.

To easily get the editor content DOM element, pass an contentId prop, eg. contentId='notes-content'.

This is useful if you want to listen to resize of the editor and respond accordingly.

To do so, first enable resize on the editor:

.cl-content {
  resize: both;
}

Now observe the resize:

<script>
  const ro = new ResizeObserver(entries => {
    const contentWd = entries[0].contentRect.width
    // respond to contentWd ...
  })
  let editor
  $: editor && ro.observe(document.getElementById('notes-content'))
</script>

<Editor {...otherEditorCfgs} contentId='notes-content' bind:this={editor} />

Run demo

git clone https://github.com/nenadpnc/cl-text-editor.git cl-editor
cd cl-editor
npm i
npm run dev

References

This library is inspired by these open source repos:

Licence

MIT License

cl-editor's People

Contributors

aam-git avatar dependabot[bot] avatar fabian9799 avatar lukashechenberger avatar mansurashraf avatar mationai avatar miyogurt avatar nenadpnc avatar nikibreg avatar northkode avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cl-editor's Issues

Incorrect undo on multiple instances

Hi there,

thank you for a great Svelte-ready component. I am testing out currently and using 2 editor instances on the same page.
It works well generally but the undo function has a problem:

  • make some changes in instance # 1
  • make some changes in instance # 2
  • click the Undo button on instance # 1

Undo works as expected on instance # 1, the problem is that it also undo the changes on instance # 2!

Looks like either the undo stack is shared between instances, or the undo event is passed to both instances.

Best regards

Extra Configurability

Nice work on this plugin. I was using quill editor before this (stopped due to security bulletin that wasn't patched), I added some extra configurability to things like colours on my local quill install, would you be happy for me to push some extra configuration changes to do the same for this plugin?

Issue with headings, lists

Hi there, thank you again for all the hard work on this project.

An issue I'm running into seems consistent across browsers. I'm unsure if it's this library or document.execCommand.

Explanation:
Text above / before the selection gets edited, when I believe it shouldn't.

Reproduction:
Starting with the code: <h1>Headline</h1><div>two</div>

  1. Highlight "two"
  2. Click "H2". Expected Output: <h1>Headline</h1><h2>two</h2>
  3. Actual Output: <h2>Headline<br>two</h2>

this problem seems to happen on h1, h2, ol, ul

View it here:
https://svelte.dev/repl/1bda56e129be48d58fbd27bbfd4c5e03?version=3.46.4

h1 and h2 tags don't get removed

Hi there, thanks for this work.

One issue I'm seeing is the inability to remove a headline (h1 or h2) from a selection, once it's already been applied. The behavior isn't consistent compared to the bold tag for example.

You can see it here:

https://svelte.dev/repl/e88308c4b9b94ec9a9d0026eff89c4a9?version=3.46.4

Bold/underline/italics will apply and remove as expected. H1+h2 do not. I'm working with the source here to find the underlying issue in the meanwhile.

edit: it looks like it's an underlying issue with document.execCommand

Error when using in SvelteKit Type script project

As I follow the guide in readme to use this editor with a SvelteKit Typescript project, I got hit with the error:

Element does not support attributes because type definitions are missing for this Svelte Component or element cannot be used as such.

Underlying error:
JSX element class does not support attributes because it does not have a '$$prop_def' property.ts(2607)
Type definitions are missing for this Svelte Component. It needs a class definition with at least the property '$$prop_def' which should contain a map of input property definitions.
Example:
  class ComponentName { $$prop_def: { propertyName: string; } }
If you are using Svelte 3.31+, use SvelteComponentTyped:
  import type { SvelteComponentTyped } from "svelte";
  class ComponentName extends SvelteComponentTyped<{propertyName: string;}> {}

Underlying error:
'Editor' cannot be used as a JSX component.
  Its instance type 'Editor' is not a valid JSX element.
    Property '$$prop_def' is missing in type 'Editor' but required in type 'ElementClass'.ts(2786)

It appears that the type definition needs some changes

setHtml while viewHtml action is active doesn't replace contents?

When the viewHtml action is active setHtml edits $references.editor.innerHTML but that's not what is being currently displayed, when viewHtml is "deactivated" it replaces $references.editor.innerHTML with its current contents effectively discarding whatever setHtml calls were made.

Not sure if this is the intended behavior? Seems potentially broken.

function cleanHTML returns incorrect html

Hi,

I ran into some issue when trying to copy/paste text into the editor. I found out that the cleanHTML function output incorrect html.

I did some modifications that seem to work for my needs however they might break some of your logic.

export const cleanHtml = (input: string) => {
	let output = input
		.replace(/\r?\n|\r/g, ' ')
		.replace(/<!--(.*?)-->/g, '')
		.replace(new RegExp('<(/)*(meta|link|span|\\?xml:|st1:|o:|font|w:sdt)(.*?)>', 'gi'), '')
		.replace(/<!\[if !supportLists\]>(.*?)<!\[endif\]>/gi, '')
		.replace(/style="[^"]*"/gi, '')
		.replace(/style='[^']*'/gi, '')
		.replace(/&nbsp;/gi, ' ')
		.replace(/>(\s+)</g, '><')
		.replace(/class="[^"]*"/gi, '')
		.replace(/class='[^']*'/gi, '')
		.replace(/<[^/].*?>/g, i => i.split(/[ >]/g)[0] + '>')
		.trim()
	// 4. Remove everything in between and including tags '<style(.)style(.)>'
    output = removeBadTags(output);
    return output;
};

Selecting text with cursor selects everything above it

Hi, I really like this component, so thanks for that. I am using it in Svelte, and it looks like this:

<script>
	import Editor from 'cl-editor';

	let html = '';
	let editorElement;

	$: { console.log('new html', html) }
</script>

<div>
        <Editor 
	    {html}
	    bind:this={editorElement}
	    on:change={(e) => (html = e.detail)}
	    actions={['h1', 'h2', 'p', 'b', 'i', 'u', 'strike', 'ol', 'ul', 'left', 'center', 'right', 'removeFormat']}
	/>
</div>

When I try to highlight some of the text inside the editor (with the mouse cursor) it selects everything on previous lines. It doesn't do it to sequential lines.

here I tried to only select the bottom 'hello'.
image

and here I tried to only select the middle 'Hello' text.
image

The issue only occurs in Chrome, it does not seem to be happening on Firefox. I haven't tried other browsers.
I am using Chrome Version 91.0.4472.114 (Official Build) (64-bit).

Is there any fix for this, or could this be my fault somehow? I tried for about 3 hours to make this work but I could not get it to work.

One strange thing to note, If I initialize the html variable like this

let html = '<h1>Text here</h1>';

It works sometimes on only the second line. I cannot figure this out for the life of me. Any ideas?

Events don't seem to work

I'm trying to wrap this into an angular component to use it in an angular project
I can't find a way to listen to change event, I followed the readme.md with something like this:
editor.on('change', (html) => console.log(html));
editor.$on('change', (html) => console.log(html));
and I got an error Property 'on' does not exist on type 'Editor'
image

Action line should be renamed hr or vice versa

In the example provided the horizontal line action is named 'hr' however in the source code there is no such thing as hr but line.

from action.ts

  line: {
    icon: '&#8213;',
    title: 'Horizontal Line',
    result: () => exec('insertHorizontalRule')
  },

In example:

this.editor = new Editor({
// <HTMLElement> required
target: document.getElementById('content'),
// optional
data: {
// <Array[string | Object]> string if overwriting, object if customizing/creating
// available actions:
// 'viewHtml', 'undo', 'redo', 'b', 'i', 'u', 'strike', 'sup', 'sub', 'h1', 'h2', 'p', 'blockquote', 
// 'ol', 'ul', 'hr', 'left', 'right', 'center', 'justify', 'a', 'image', 'forecolor', 'backcolor', 'removeFormat'

cl-button Font-Size is unset

Hey there,

I've been experimenting with your library, it's really great.

I have noticed that the button sizes are not set in the library css, causing sometimes the buttons in inherit a (much larger) font size of parent.

.cl-button {

I think is a css font-size: .4em; is added here, this should help fix the button size when embedded in other apps.

image

unable to make it work

hi, I have a problem: i get this error when I try to yarn dev:

rollup v1.21.2
bundles src/main.js โ†’ public/bundle.js...
[!] (plugin svelte) ParseError: Expected valid tag name
node_modules/cl-editor/src/Editor.html
1: <:Window on:click="_documentClick(event)" />
    ^
2: <div class="cl" ref:editorWrapper>
3:   <div class="cl-actionbar">      
ParseError: Expected valid tag name
    at error$1 (/mnt/c/Users/jdgar/Github/Bitagora/bitagora_frontend/node_modules/svelte/src/compiler/utils/error.ts:25:16)
    at Parser$2.error (/mnt/c/Users/jdgar/Github/Bitagora/bitagora_frontend/node_modules/svelte/src/compiler/parse/index.ts:93:3)
    at read_tag_name (/mnt/c/Users/jdgar/Github/Bitagora/bitagora_frontend/node_modules/svelte/src/compiler/parse/state/tag.ts:281:10)     
    at tag (/mnt/c/Users/jdgar/Github/Bitagora/bitagora_frontend/node_modules/svelte/src/compiler/parse/state/tag.ts:76:15)
    at new Parser$2 (/mnt/c/Users/jdgar/Github/Bitagora/bitagora_frontend/node_modules/svelte/src/compiler/parse/index.ts:45:12)
    at parse$1 (/mnt/c/Users/jdgar/Github/Bitagora/bitagora_frontend/node_modules/svelte/src/compiler/parse/index.ts:208:17)
    at parse (/mnt/c/Users/jdgar/Github/Bitagora/bitagora_frontend/node_modules/svelte/src/compiler/compile/index.ts:68:14)
    at preprocessPromise.then.code (/mnt/c/Users/jdgar/Github/Bitagora/bitagora_frontend/node_modules/rollup-plugin-svelte/index.js:252:22)

do I need to install typescript on svelte? or is not required from scratch.

i just added to App.svelte


<script>import Editor from "cl-editor";
const editor = new Editor({
    target: document.getElementById("editor")
  });
</script>

<div id="editor"></div>

thank you

Just wanted to say thanks!

Hey, just wanted to say thanks for making this project. I got set up successfully with it today inside my Svelte project and it's a great WYSIWYG library. Nice to see it just work! Thanks for all your hard work on this and for sharing it for others to use ๐Ÿ‘

binding editor HTML

First off, thanks for this! Super useful!

I am using svelte also and found myself wanting do something like <Editor bind:html height={"200"} />, but had to do this:

<script>
  import Editor from "cl-editor/src/Editor.svelte"

  let html = '<h3>hello</h3>'
  let editor

  $: editor && editor.$on('change', () => html = editor.getHtml())
</script>

{@html html}
<Editor bind:this={editor} {html} height={"200"} />

I'm probably missing something, but the simpler, more idiomatic version with bind:html did not work for me.

Even then, the $on('change') doesn't seem to fire when editing raw html

Am I doing it wrong? very likely xD

Provide example for usage within svelte

Eventually I found this comment so I could get it to work as follows:

<script>
  import Editor from "cl-editor/dist";
  import { onMount } from "svelte";
  let editArea;
  onMount(async () => {
    const editor = new Editor({
      target: editArea
    });
  });
</script>

<h1>cl-editor</h1>
<div bind:this={editArea} />

I'm not sure if this is the best way and why I'm getting TypeError: $references.editor is undefined when importing from "cl-editor" rather than "cl-editor/dict" some doucmentation on how to use cl-editor within svelte would be appreciated.

Feature request: remove format blockon second click if activated

Current behavior

When I click on a format block like h1 (or something similar) I activate it.
I can't deactivate the format block with a second click.

Wanted behavior
When I click on a format block like h1 (or something similar) I activate the style.
When my cursor is in the active h1 element and I click the h1 button again, I want that the format block is removed.

This should work for all format blocks.

Problem toggling bold/italics tag when there is a new line

How to reproduce:

  1. Go to the demo page: https://nenadpnc.github.io/cl-editor/
  2. Scroll to the "Custom action example"
  3. Place cursor somewhere in the placeholder text and pressing enter, moving some of the text to a new line.
  4. Select the new line, and make it bold. This works as expected...
  5. .But you cannot toggle off the bold, nothing happens when you click the button.
    image

It seems like it's somehow connected to the div tags that are created when you press enter. Possibly a work-around would be to only add br tags when pressing enter.

`./src/Editor.html` not in npm package

When installing via npm i cl-editor, the svelte entry file (./src/Editor.html) is missing.

As of now, this package cannot be used in existing server side rendered svelte apps (via Sapper in my case).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.