smclab / react-faceted-token-input

A React component which is a tokenized input with faceted behavior
http://smclab.github.io/react-faceted-token-input/
GNU Lesser General Public License v2.1
9 stars 2 forks source link

Add RTL languages support #7

Closed GianBe closed 8 years ago

GianBe commented 8 years ago

As of this release the component doesn't support rtl languages in the token navigation. RTL languages are the ones that write right to left while LTR write from left to right.

For example: If your language is arabic you will have the following situation inside an input with tokens: [token]``[#token]``[token] Now if you press the right arrow on the keybord the result will be: [#token]``[token]``[token] While the expected result is: [token]``[token]``[#token]

This is a problem caused by the definition of the forward and backward direction in the code that in a rtl language are inverted.

I was trying to implement a solution to the problem, that is not really a proper solution:

The problem of the navigation inside the tokens depends on this code inside of key-utils.js:

  export const isForward = (event) => (event.which === RIGHT) || isEnd(event);
  export const isBackward = (event) => (event.which === LEFT) || isHome(event);

that needs to differentiate from rtl and ltr. My solution will operate directly in the browser with the use of the dir property inside of the HTML input.

dir can force the displayed type of language to rtl or ltr will accept:

A good reference for the use of dir in HTML is here

The dir property with an helper function will give the right method of navigation.

Now some actual code for this solution:

First of all we need 2 new states inside the FacetedTokenInput class:

  firstInput: true,
  textDirection: 'auto'

Inside the actual render method in the FacetedTokenInput class you will need to add:

  dir={ this.state.textDirection } 

as a property of the div element.

Now you will need to modify the onChange function of FacetedTokenInput:

  let newTextDirection;

  if (firstInput) {
    newTextDirection = isRTL(searchText) ? 'rtl' : 'ltr';
  }
  else {
    newTextDirection = textDirection;
  }

So if it isn't the first input we keep the current textDirection, if it is we call the helper function isRTL() that will get the current input and help detect the right language style.

Finally the core of this solution, isRTL() is:

const isRTL = (str) => {
  const ltrChars = 'A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF'+'\u2C00-\uFB1C\uFDFE-\uFE6F\uFEFD-\uFFFF',
        rtlChars = '\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC',
        rtlDirCheck = new RegExp('^[^'+ltrChars+']*['+rtlChars+']');

  return rtlDirCheck.test(str);
}

Thanks to the help of the internet I basically found this type of function and is similar to the one that Google uses to change the position of the microphone inside its input.

The function returns a boolean that represent the result of the application of the regular expression to the passed string (false if is ltr, true if is rtl).

To complete the changes the new isForward and isBackward functions are:

export const isForward = (event, RTL) => ((event.which === RIGHT) && (RTL === 'ltr')) || 
  ((event.which === LEFT) && (RTL === 'rtl')) || 
  isEnd(event);

export const isBackward = (event, RTL) => ((event.which === LEFT) && (RTL === 'ltr')) || 
  ((event.which === RIGHT) && (RTL === 'rtl')) || 
  isHome(event);

A new element is passed to the function RTL that represent the textDirection state inside FacetedTokenInput.

With all those changes the result is the right navigation inside of the tokens. The text itself is a little different, at the moment it doesn't work, probably some of the problems will disappear in a real rtl language enviroment.

Probably some changes to the onLeftRight function are needed.

GianBe commented 8 years ago

A little update:

I'm inclined to belive that the way the selection of the text behave in the component is actually right

GianBe commented 8 years ago

The current available version of fbjs does not have bidi support.

The files containing the bidi algorithm are available in the 0.8.0-alpha1 version (the one used in draftjs)