Giter Site home page Giter Site logo

peterpeterparker / stylo Goto Github PK

View Code? Open in Web Editor NEW
718.0 9.0 29.0 1.22 MB

Another kind of rich text editor

Home Page: https://stylojs.com

License: MIT License

JavaScript 0.32% TypeScript 86.92% SCSS 5.73% HTML 7.03%
editor wysiwyg rich-text-editor stenciljs web-components custom-elements wysiwyg-editor

stylo's Introduction

Stylo

Another kind of rich text editor.

  • Interactive design 🎯
  • Customizable 💪
  • Framework agnostic 😎
  • Lightweight 🪶
  • Future Proof 🚀
  • Open Source ⭐️

A project from Papyrs, a blogging platform on web3.

GitHub release Tweet

Table of contents

Getting Started

Stylo is an open source WYSIWYG interactive editor for JavaScript. Its goal is to bring great user experience and interactivity to the web, for everyone, with no dependencies.

Concept

The library - a web component - needs as bare minimum property a reference to an editable HTML element (contenteditable="true").

It needs only one single top container set as editable and will maintain a list of children, paragraphs, that are themselves HTML elements.

<article contenteditable="true">
  <div>Lorem ipsum dolor sit amet.</div>
  <hr />
  <ul>
    <li>Hello</li>
    <li>World</li>
  </ul>
  <div>In ac tortor suscipit.</div>
</article>

To keep track of the changes for a custom "undo redo" stack and to forward the information to your application, the component mainly uses the MutationObserver API.

It also uses some keyboard, mouse or touch events to present UI elements or apply styling changes.

Installation

You can use Stylo via CDN or by installing it locally.

CDN

Add the following code to your page to load the editor.

<script type="module" src="https://unpkg.com/@papyrs/stylo@latest/dist/stylo/stylo.esm.js"></script>

That's it, the component is imported and loaded.

Local Installation

Install the editor in your project from npm:

npm install @papyrs/stylo

Afterwards you will need to load - i.e. import - the component in your application. Use one of the following methods, the one that fits the best your needs or framework.

Loader

Lazy load the components with the help of a loader. This is the recommended solution to load Stylo in vite projects.

import {defineCustomElements} from '@papyrs/stylo/dist/loader';
defineCustomElements();

Import

Import the library.

import '@papyrs/stylo';

Custom Elements

It is also possible to import only selected element, as for example the <stylo-color /> component.

import {StyloColor} from '@papyrs/stylo/dist/components/stylo-color';
customElements.define('stylo-color', StyloColor);

Note: it will recursively define all children components for a component when it is registered.

Usage

To integrate the editor to your application, add the following tag next to your editable element:

<stylo-editor></stylo-editor>

The component needs to find place at the same level because its UI elements are absolute positioned.

Once added, provide a reference to your container.

// Your editable element
const article = document.querySelector('article[contenteditable="true"]');

// Stylo
const stylo = document.querySelector('stylo-editor');

// Set the `containerRef` property
stylo.containerRef = article;

Config

The editor is provided with a default configuration. It can be customized by setting the property config of the <stylo-editor/> component.

For more information:

Plugins

A plugin is a transform function that adds a new paragraph to the editable container.

You can contribute by adding new plugins to this repo or create custom plugins for your application only.

The list of plugins available at runtime by the editor is fully customizable.

Development

Stylo exposes interfaces and utilities to ease the development of new plugins. Basically, a plugin should provide:

  • text: the text, a string, displayed to the user in the UI popover
  • icon: an icon displayed to the user in the UI popover. it can be one of the built-in icons (src/types/plugin.ts) or an inline SVG - i.e. an SVG provided as string
  • createParagraphs: the function that effectively create the new paragraph(s), add these elements to the DOM and can optionally give focus to the newly created first or last element

For example, a plugin that generates a new paragraph that is itself a Web Component name <hello-world/> would look as following:

import {
  createEmptyElement,
  StyloPlugin,
  StyloPluginCreateParagraphsParams,
  transformParagraph
} from '@papyrs/stylo';

export const hr: StyloPlugin = {
  text: 'My Hello World',
  icon: `<svg width="32" height="32" viewBox="0 0 512 512">
        ...
    </svg>
  `,
  createParagraphs: async ({container, paragraph}: StyloPluginCreateParagraphsParams) => {
    // Create your Web Component or HTML Element
    const helloWorld = document.createElement('hello-world');

    // Set properties, attributes or styles
    helloWorld.setAttributes('yolo', 'true');

    transformParagraph({
      elements: [helloWorld, createEmptyElement({nodeName: 'div'})],
      paragraph,
      container,
      focus: 'first'
    });
  }
};

In addition, it is worth to note that createParagraphs is a promise. This gives you the ability to hi-jack the user flow to trigger some functions in your application before the DOM is actually modified. As for example opening a modal after a plugin as been selected by the user.

Things to pay attention to:

  • when users are using your plugins, they should not end up trapped not being able to continue editing and create new paragraphs. That's why we advise to generate an empty div (in above example createEmptyElement) at the same time as your element(s)
  • Stylo expect all the direct children - the paragraphs - of the editable container to be HTML elements i.e. no text or comment nodes

Find some custom plugins in DeckDeckGo repo.

Toolbar

The inline editor that is uses to style texts (bold, italic, colors, etc.) is a web component named <stylo-toolbar/>.

It is used per default with Stylo on desktop but can also be used as a standalone component.

Because mobile devices are already shipped with their own tooltip, the toolbar is not activated by Stylo on such device.

Menus

Optionally, menus can be defined for particular elements - i.e. paragraphs. They will be displayed with an absolute positioning after click events.

Custom menus can be configured following the (src/types/menu.ts) interface.

If for example you would like to display a custom menu for all code paragraphs, this can be done as following:

export const editorConfig: Partial<StyloConfig> = {
  menus: [
    {
      match: ({paragraph}: {paragraph: HTMLElement}) => paragraph.nodeName.toLowerCase() === 'code',
      actions: [
        {
          text: 'Edit code',
          icon: `<svg ...
          </svg>`,
          action: async ({paragraph}: {paragraph: HTMLElement}) => {
            // Apply some modifications or any other actions of your choice
          }
        }
      ]
    }
  ]
};

Stylo provides a sample menu for images (src/menus/img.menu.ts).

Events

If you are using a rich text editor, there is a chance that you are looking to persist users entries and changes.

For such purpose, the <stylo-editor/> component triggers following custom events:

  • addParagraphs: triggered each time new paragraph(s) is added to the editable container
  • deleteParagraphs: triggered each time paragraph(s) are removed
  • updateParagraphs: triggered each time paragraph(s) are updated

Each paragraph is a direct child of the editable container.

Unlike addParagraphs and deleteParagraphs that are triggered only if elements are such level are added or removed, updateParagraphs is triggered if the paragraphs themselves or any of their children (HTML elements and text nodes) are modified.

Stylo can detect changes for paragraphs and elements that are added or updated but cannot detect deleted paragraphs without a hint. The Mutation Observer API does not provide yet enough information. To overcome this issue, Stylo set an attribute with empty value to identify what elements are paragraphs.

Changes following keyboard inputs are debounced.

Attributes

Following attributes are ignored to prevent the observer to trigger and keep track of changes that are not made by the user on purpose:

  • paragraph_id: the attribute added to identify each paragraph
  • placeholder: the attribute used by Stylo to display the placeholder about the '/'
  • class: only inline style is considered changes
  • spellcheck
  • contenteditable
  • data-gramm, data-gramm_id, data-gramm_editor and data-gr-id: Grammarly flooding the DOM

The list of excluded attributes and the paragraph_id hint can be customized through the configuration (src/types/config.ts).

Listener

If you are manipulating the contenteditable - i.e. the DOM - on your side, you might want to add these changes to the "undo-redo" history.

For such purpose, the editor is listening for the events snapshotParagraph of type CustomEvent<void> that can be triggered from the child of the editable element you are about to modify.

Contributing

We welcome contributions in the form of issues, pull requests, documentation improvements or thoughtful discussions in the GitHub issue tracker.

To provide code changes, make sure you have a recent version of Node.js installed (LTS recommended).

Fork and clone this repository. Head over to your terminal and run the following command:

git clone [email protected]:[YOUR_USERNAME]/stylo.git
cd stylo
npm ci
npm run start

Before submitting changes, make sure to have run at least once a build (npm run build) to generate the documentation.

Tests suite can be run with npm run test.

This project is developed with Stencil.

i18n

English, German, Spanish and Dutch are currently supported. More translations are also welcomed!

Contributions

Customization

The text options of plugins and menus can either be static string or a translation keys.

To provide a list of custom translations that matches these keys, Stylo accepts a custom record of string (src/types/config.ts).

Through the same configuration it is also possible to switch languages on the fly.

License

MIT © David Dal Busco and Nicolas Mattia

stylo's People

Contributors

bytesun avatar jogibear9988 avatar malteludwig avatar mrnmkmn avatar nmattia avatar peterpeterparker avatar wobsoriano avatar zrains 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

stylo's Issues

Info: Demonstration usage inside of webcomponents

This is only for information:

I created a designer framework for webcomponents. Inside of it there is also the possibility to edit formated text, where I now included stylo. A demo is here: https://node-projects.github.io/web-component-designer-demo/index.html

To test drag the "div with text" form the elements panel (see lements/demo/div with text) to the designer and doubleclick it.

It's only a preview of the integration at the moment, there is still work to do (I think mainly at my designer, not here).

Any chance for adding video to the widget?

This is an amazing plugin, but it lacks the one important aspect of article writing - attaching videos. Seems to have all other features one would need.

Is this something being considered?

Doesn't need to be elaborate. Add link to youtube, options to resize similar to that of resizing images. Done.

When typing Japanese, line breaks are automatically inserted.

Expected Behavior

When Japanese is converted from hiragana to kanji and the enter key is pressed, the character is confirmed and the rest of the sentence can be entered.

Actual Behavior

Pressing the enter key to convert from hiragana to kanji automatically breaks the line.

Reproduction

Steps to reproduce the issue:

1.Type Hiragana
2.Convert to Kanji with Space key
3.Fix the word with Enter key

Screenshots

2022-05-16.17.08.49.mov

Environment

  • Browser(s):
  • Operating System (e.g. Windows, macOS, Ubuntu):
    -MacBook Pro (13-inch, M1, 2020) mac OS Big Sur 11.6.1(20G224)
    ※I've tried with Android(Pixel6), It didn't reappear, no problem.

Selection mark disappear

before i customs text color

image

when i click the input element, selection mark disappear, but the color can still change normally.

image

actually, click the toolbar will take same effect.

Vite related issue

Reproduction

Steps to reproduce the issue:

  1. Create any vite app
  2. Install and import library import '@papyrs/stylo'

Error in terminal:

381|  var ce = (e2) => {
382|    const t2 = e2.h.replace(/-/g, "_"), n2 = e2.q, l2 = ie.get(n2);
383|    return l2 ? l2[t2] : import(`./${n2}.entry.js`).then((e3) => (ie.set(n2, e3), e3[t2]), oe);
   |                                ^
384|  };
385|  var re = /* @__PURE__ */ new Map();
The above dynamic import cannot be analyzed by vite.
See https://github.com/rollup/plugins/tree/master/packages/dynamic-import-vars#limitations for supported dynamic import formats.
If this is intended to be left as-is, you can use the /* @vite-ignore */ comment inside the import() call to suppress this warning.

  Plugin: vite:import-analysis
  File: /test-project/node_modules/.vite/@papyrs_stylo.js?v=ac2d9e89

Error in browser console:

Screen Shot 2022-02-10 at 10 41 16 AM

how to customize link functionality?

I have totally different modal already developed in UI to customize link, like uploading file, and custom links.

Now what I need to do when use click 'add a link' button it should open my modal with link if already present. then the my modal do the work and return link, this link now should apply to html element.

so what should be normal procedure I need to follow?

feat: transform pasted url to link

Feature Description

If for example a text "papyrs/papyrs#33" is pasted it should be automatically converted to a link <a href="https://github.com/papyrs/papyrs/issues/33">https://github.com/papyrs/papyrs/issues/33</a>

Android v12 keyboard focus

Actual Behavior

On Android v12 (and v12 only?), the keyboard is briefly show on selection change but goes away almost instantly

Something I fixed a couple of weeks ago and I could not reproduce with all my devices.

If anyone can reproduce the issue while running the project locally, ping me. That would help a lot!

delay "Press / for plugins"

Feature Description

Adding a bit of timeout / delay before displaying Press "/" for plugins would be neat. It's a bit glitchy when I hit quickly new lines after new lines.

A few odd behaviors.

There are 2 behaviors I have found that are, less than ideal for the writer.

First is if one loads the page with focus on the editiblecontent div, the editor tools will not open when highlighting text with a mouse (actually no tools will open unless I also add the <div paragraph_id="" placeholder="Press &quot;/&quot; for plugins">​</div>) One MUST click someone on the editible content with a mouse before they can highlight with keyboard to open the edit tools modal. I have no idea if this has any effect on small displays but I would imagine not seeing as every touch of the screen would act as a click.

To reproducer on a PC merely load a page with the editible div in focus at page load document.querySelector('#whatever').focus(); document.querySelector('#whatever').innerHTML = '<div paragraph_id="" placeholder="Press &quot;/&quot; for plugins">​</div>'; then use your keyboard to highlight text without using your mouse at all.

Second is less obscure. Backspacing to delete a line, stops all tools for opening, the (+) does not appear, the "type / for...." does not appear, typing / does nothing, highlighting text does nothing with mouse or keyboard. To reproduce in a blank document using the editor, hit enter a bunch of times, then backspace one entire line to the line before and try to open tools. Its an odd behavior.

Integration of Stylo inside of other webcomponents

Feature Description

I'm creating a designer for usage with webcomponents: https://github.com/node-projects/web-component-designer (demo: https://node-projects.github.io/web-component-designer-demo/index.html)).
Inside of this I try to include stylo as a editor of rich text content.
For this some bugs/fixes need to be made.
I create a pull request when I got it workin, than we can discuss how to integrate the needed changes. (If you'd like to)

Use Case

Usage of stylo inside of webcomponents and a designer

Colors don't close

I don't think this is the same issue as being discussed on the other thread, but if it is feel free to close. I notice nothing short of a right click closes the color box modal when its opened.

To reproduce visit https://stylojs.com/, highlight text, select the color tool, change the color of text. The modal does not close. Click elsewhere on the page, the modal does not close. Start typing again in the editor and still the modal does not close. right click and the modal now closes.

Again, this is above my ability but I have been trying to figure out where to place the code cause I think I know the solution.

In my mind the modal should be closed upon one of three events.

  1. User clicking button to select color
  2. onkeyup event of the input box if input.length == 7 (pound sign + 6 numbers)
  3. user pressing esc key to close box

Using inside of my Designer - Events not raised, Text not formated

Expected Behavior

I should be able to formt my Text using Stylo

Actual Behavior

I use your component inside of my WebComponentDesigner, but again once more, the vents are not raised.
I did work (fixed it long time ago), but I havend Tested it for weeks, so I don't know what is breaking it.
Do you have any Ideas? Or do I need to debug this once more (will take a long time to get back into you component, at least as I don't use React very often)

Reproduction

Steps to reproduce the issue:

  1. Go to my Designer: https://node-projects.github.io/web-component-designer-demo/index.html

  2. Add following Code (in Source View):

     <span style="position:absolute;left:84px;top:90px;">
         <div style="transform-origin:50% 50%;">Test</div>
     </span>
    
  3. Switch to Design view and Edit Text via DoubleClick

Screenshots

Aufnahme_2022_10_26_21_57_16_792

Environment

  • Browser(s): Chrome 107
  • Operating System (e.g. Windows, macOS, Ubuntu): Windows

quick test on latest FF

Tested on FF 96

  • Links not working (i cannot open a link, only delink a text)
  • when reordering images i get a duplicated image in the new and old spot.

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.