Opened 4 years ago

Closed 5 months ago

#1614 closed enhancement (fixed)

With cursor at the start of a list item, [tab] should create a sublist

Reported by: ejucovy Owned by: ejucovy
Priority: normal Milestone:
Component: Xinha Core Version: trunk
Severity: normal Keywords:
Cc:

Description

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.

Change History (4)

comment:1 Changed 4 years ago by ejucovy

  • Owner changed from gogo to ejucovy
  • Status changed from new to assigned

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] === null ) {
        return;
    }

    containing_list_type = ["ol", "ul"][containing_list[1]];
    containing_list = containing_list[0];

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

    ev.preventDefault();

    if( ev.shiftKey ) {
      if( this._getFirstAncestorForNodeAndWhy(containing_list, ["ol", "ul"])[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.

comment:2 Changed 4 years ago by ejucovy

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

comment:3 Changed 4 years ago by ejucovy

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>.

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

comment:4 Changed 5 months ago by gogo

  • Resolution set to fixed
  • Status changed from assigned to closed

[1375], [1376] ListOperations? plugin added to do this.

Note: See TracTickets for help on using tickets.