quill icon indicating copy to clipboard operation
quill copied to clipboard

The list bullet is not displayed properly

Open zaqijr7 opened this issue 1 year ago • 7 comments

  1. Install Quil

npm install [email protected]

  1. Setup Quill in React
import { forwardRef, useEffect, useLayoutEffect, useRef } from 'react';
import Quill, { Range, Delta } from 'quill/core';
import Toolbar from 'quill/modules/toolbar';
import Snow from 'quill/themes/snow';
import Bubble from 'quill/themes/bubble';
import Bold from 'quill/formats/bold';
import Italic from 'quill/formats/italic';
import Header from 'quill/formats/header';
import List from 'quill/formats/list';
import Link from 'quill/formats/link';

import 'quill/dist/quill.snow.css';
import 'quill/dist/quill.core.css';

interface EditorProps {
  readOnly?: boolean;
  defaultValue?: Delta;
  onTextChange?: (_delta: Delta, _oldDelta: Delta, _source: string) => void;
  onSelectionChange?: (
    _range: Range | null,
    _oldRange: Range | null,
    _source: string
  ) => void;
}

Quill.register({
  'modules/toolbar': Toolbar,
  'themes/snow': Snow,
  'themes/bubble': Bubble,
  'formats/bold': Bold,
  'formats/italic': Italic,
  'formats/header': Header,
  'formats/list': List,
  'formats/link': Link,
});

const Editor = forwardRef<Quill | null, EditorProps>(
  ({ readOnly, defaultValue, onTextChange, onSelectionChange }, ref) => {
    const containerRef = useRef<HTMLDivElement | null>(null);
    const defaultValueRef = useRef<Delta | undefined>(defaultValue);
    const onTextChangeRef = useRef<typeof onTextChange>(onTextChange);
    const onSelectionChangeRef =
      useRef<typeof onSelectionChange>(onSelectionChange);

    useLayoutEffect(() => {
      onTextChangeRef.current = onTextChange;
      onSelectionChangeRef.current = onSelectionChange;
    }, [onTextChange, onSelectionChange]);

    useEffect(() => {
      if (ref && typeof ref === 'object' && ref.current) {
        ref.current.enable(!readOnly);
      }
    }, [ref, readOnly]);

    useEffect(() => {
      const container = containerRef.current;
      if (!container) return;

      const editorContainer = container.appendChild(
        container.ownerDocument.createElement('div')
      );

      const quill = new Quill(editorContainer, {
        theme: 'snow',
        modules: {
          toolbar: [
            [{ header: [1, 2, false] }],
            ['bold', 'italic', 'underline'],
            [{ list: 'ordered' }, { list: 'bullet' }],
            ['link', 'image']
          ],
        }
      });

      if (ref && typeof ref === 'object') {
        ref.current = quill;
      }

      if (defaultValueRef.current) {
        quill.setContents(defaultValueRef.current);
      }

      quill.on(Quill.events.TEXT_CHANGE, (...args) => {
        if (onTextChangeRef.current) {
          onTextChangeRef.current(...args);
        }
      });

      quill.on(Quill.events.SELECTION_CHANGE, (...args) => {
        if (onSelectionChangeRef.current) {
          onSelectionChangeRef.current(...args);
        }
      });

      return () => {
        if (ref && typeof ref === 'object') {
          ref.current = null;
        }
        container.innerHTML = '';
      };
    }, [ref]);

    return <div ref={containerRef} id="editorku"></div>;
  }
);

Editor.displayName = 'Editor';

export default Editor;

Wrap in a component

import React, { useRef, useState } from 'react';
import Editor from './Editor';
import Quill, { Delta as Deltas, Range } from 'quill/core';

type RangeType = Range | null;
type TextChangeType = { delta: Deltas; oldDelta: Deltas; source: string };

const Delta = Quill.import('delta');

const TextEditor: React.FC = () => {
  const [range, setRange] = useState<RangeType>(null);
  const [lastChange, setLastChange] = useState<TextChangeType | null>(null);
  const [readOnly, setReadOnly] = useState<boolean>(false);

  const quillRef = useRef<Quill | null>(null);

  return (
    <div>
      <Editor
        ref={quillRef}
        readOnly={readOnly}
        defaultValue={new Delta(lastChange?.oldDelta)}
        onSelectionChange={(range: RangeType) => setRange(range)}
        onTextChange={(delta: Deltas, oldDelta: Deltas, source: string) =>
          setLastChange({ delta, oldDelta, source })
        }
      />
    </div>
  );
};

export default TextEditor;

  1. Run code
  2. Typing in quill text editor
  3. Create list bullet

Expected behavior: The expectation is show disc bullet in list

Actual behavior: The displayed list contains both numbers and bullets. image

Platforms: Chrome, Firefox, Mac Os Monterey

Include browser, operating system and respective versions

Version:

"quill": "2.0.2",
"react": "^18.3.1",

zaqijr7 avatar Oct 30 '24 06:10 zaqijr7

Same here, using

    "ngx-quill": "^26.0.8",
    "quill": "^2.0.2",

in Angular 18 application.

fipil avatar Oct 31 '24 21:10 fipil

same using: 
"quill": "^2.0.2",
   "primevue": "^3.50.0",
      "vue": "^3.0.0"

Z0dya avatar Nov 01 '24 12:11 Z0dya

when click on <ul> in Quill editor is generated <ol>! How getSemanticHTML() will solve this wrong HTML tag? A bit confused with this. I have Svelte 5 project but IMO this issue is framework agnostic as it is IMO Quill core issue.

CleanShot 2024-11-09 at 08 16 04

StanSkrivanek avatar Nov 03 '24 20:11 StanSkrivanek

same using quill: 2.0.3 ngx-quill: 26.0.10

tourniayrem avatar Dec 30 '24 10:12 tourniayrem

Same issue here. Is it intended behavior not to generate as <ul>?

dlq84 avatar Jan 10 '25 14:01 dlq84

Same problem in Vue 3 when using @vueup/vue-quill": "^1.2.0". Also in version 2.0.3.

ErikVerheul avatar Jan 26 '25 09:01 ErikVerheul

I'm using Quill version 2.0.3 with the "snow" css and the bullet character does not render at all. The numbered list does show numerals 1, 2, 3, etc. This is a custom use of Quill. I am not using React/Vue/Angular etc.

Ah now I see the problem. Quill changed the format string from 'unordered' to 'bullet' in eg quill.format('list', 'bullet'); I read through all the change notes and didn't see any mention of that!

icecandy avatar Apr 04 '25 15:04 icecandy