sleemanj / xinha

WYSIWYG HTML Editor Component (turns <textarea> into HTML editors)
http://trac.xinha.org/
Other
13 stars 2 forks source link

With cursor at the start of a list item, [tab] should create a sublist (Trac #1614) #1614

Closed sleemanj closed 4 years ago

sleemanj commented 10 years ago

When working in a <ul> or <ol> context, it would be nice to be able to create a sub-list by pressing tab. Specifically, I would propose the following behavior:

  1. If the cursor is at the beginning of an empty ul > li, and the user presses tab, a <ul><li> should be created at the current position, causing the structure to be <ul><li><ul><li>[CURSOR]</li></ul></li></ul>
    1. Similarly, if the cursor is at the beginning of an empty ol > li, a sub-<ol> should be created.
  2. If the cursor is at the beginning of a non-empty list item and tab is pressed, the current list item should be reparented underneath a new <ul><li>, so that the resulting structure is <ul><li><ul><li>[CURSOR]Existing text</li></ul></li></ul>
    1. Similarly for an <ol>.
  3. If the cursor is not at the beginning of a line, and tab is pressed, the default behavior should continue to occur.

Right now you have to press the "INDENT" toolbar button to create a sub-list.

Reported by ejucovy, migrated from http://trac.xinha.org/ticket/1614

sleemanj commented 10 years ago

Very rough solution is this patch:

Index: XinhaCore.js
===================================================================
--- XinhaCore.js  (revision 1327)
+++ XinhaCore.js  (working copy)
@@ -5120,7 +5120,11 @@
   {
     types = [types];
   }
+  return this._getFirstAncestorForNodeAndWhy(prnt, types);
+};

+Xinha.prototype._getFirstAncestorForNodeAndWhy = function(node, types) {
+  var prnt = node.parentNode;
   while ( prnt )
   {
     if ( prnt.nodeType == 1 )

Plus this event handler in your xinha_config:

  xinha_config.Events.onKeyPress = function(ev) {
    if( ev.keyCode !== 9 ) { return; }

    var sel = this.getSelection(),
        rng = this.createRange(sel),
        containing_list = this._getFirstAncestorAndWhy(sel, ["ol", "ul"]);

    if( containing_list[0](../commit/0) === null ) {
        return;
    }

    containing_list_type = ["ol", "ul"][containing_list[1](../commit/1)];
    containing_list = containing_list[0](../commit/0);

    if( rng.startOffset !== 0 ) {
      return;
    }

    ev.preventDefault();

    if( ev.shiftKey ) {
      if( this._getFirstAncestorForNodeAndWhy(containing_list, ["ol", "ul"])[0](../commit/0) !== null ) {
        this.execCommand("outdent");
      }
    } else {
      this.execCommand("indent");
    }

    return true;
  }

The problem with this is that both Firefox and Chrome interpret the "indent" command here by inserting a ul without a parent li, resulting in the following invalid HTML structure: <ul><ul><li> -- instead of the correct <ul><li><ul><li>.

I'll continue working on this to come up with a better solution.

I also realized that Xinha itself doesn't capture tab events at all by default -- the tabbing behavior I've observed is actually specific to Chrome (or probably Chrome + Safari). In Firefox, the tab event just reaches the browser window and causes the xinha frame to lose focus. It would be nice to handle this in a consistent way across browsers; I'll file a separate ticket for this.

sleemanj commented 10 years ago

ejucovy commented:

(Note that the above implementation also handled dedenting lists via shift+tab, which seems to be a pretty universal interpretation.)

sleemanj commented 10 years ago

ejucovy commented:

The problem with this is that both Firefox and Chrome interpret the "indent" command here by inserting a ul without a parent li, resulting in the following invalid HTML structure:

    • -- instead of the correct
        • .

Actually it seems like browsers strongly prefer this, both in their default stylesheets and in their content-editable behavior: http://stackoverflow.com/a/9859082/689985

sleemanj commented 10 years ago

1375, 1376 ListOperations plugin added to do this.