Giter Site home page Giter Site logo

aurbano / smart-area Goto Github PK

View Code? Open in Web Editor NEW
67.0 5.0 19.0 443 KB

:memo: Textareas on Steroids - AngularJS 1 directive

Home Page: https://aurbano.github.io/smart-area

License: MIT License

CSS 19.16% JavaScript 11.33% HTML 69.51%
textarea syntax-highlighting angular directive autocomplete

smart-area's Introduction

smart-area

Textareas on Steroids - just adding an AngularJS directive

Features

  • Word highlighting (syntax, keywords... ) with support for multiple sets
  • Autocomplete
  • Dropdown suggestions (mention users after typing @, hashtag support, keywords... )
  • Minimal configuration required, but highly customizable if necessary

Getting started

Get a copy of smart-area either by Downloading the files, or using a package manager:

bower install --save smart-area
npm install --save smart-area

It requires jQuery to function properly at the moment, I'll try to make it depend only on jqLite so it works on vanilla Angularjs.

To setup smartArea on an element simply add the attribute:

<textarea
  name="sampleTextarea"
  id="sampleTextarea"
  class="form-control code"
  ng-model="myModel"
  ng-trim="false"
  spellcheck="false"

  smart-area="areaConfig">
</textarea>

Examples

All of these examples use the same HTML markup, only the configuration changes.

User mentions

Test in Plunkr

$scope.areaConfig = {
  dropdown: [{
      // trigger can be a string or a regexp
      // in this case this regexp should match Twitter-style @mentions
      trigger: /@([A-Za-z]+[_A-Za-z0-9]+)/gi,
      // The list() function gets called when the trigger matches
      // it should return a list of elements to be suggested
      list: function(match, callback){
        // match is the regexp return, in this case it returns
        // [0] the full match, [1] the first capture group => username
        $http.get('/fakeApi/users', {
          user: match[1]
        }).success(function(data){
          // Prepare the fake data
          var listData = data.map(function(element){
            return {
              display: element.userName, // This gets displayed in the dropdown
              item: element // This will get passed to onSelect
            };
          });
          callback(listData);
        }).error(function(err){
          console.error(err);
        });
      },
      // Function that gets called when an item is selected
      // it's return value gets added to the textarea
      onSelect: function(item){
        return item.userName;
      },
      // mode can be append or replace
      // here we want replace because the user has already typed
      // part of the username
      mode: 'replace'
    }
  ]
};

Syntax highlighter and autocompleter for a SQL editing textarea

$scope.areaConfig = {
  autocomplete: [{
      words: ['SELECT', 'UPDATE', 'DELETE', 'INSERT'],
      cssClass: 'highlight sqlMain',
      autocompleteOnSpace: true
    },
    {
      words: ['WHERE', 'FROM', 'ORDER BY', 'LIMIT', 'JOIN'],
      cssClass: 'highlight sqlQuery',
      autocompleteOnSpace: true
    },
    {
      words: ['users','messages','friends','events'],
      cssClass: 'highlight tables'
    }
  ]
};

autocompleteOnSpace sets the trigger to run even when the user hasn't started typing, so that it already suggests commands. It defaults to false, which could be useful for other words, in the example the table names.

API

The configuration options are mostly optional and have sensible defaults:

autocomplete

You can specify here sets of words that will be available on autocomplete. autocomplete expects an array of objects, each being a different autocomplete rule:

$scope.areaConfig = {
  autocomplete: [{
      words: [string | RegExp],
      cssClass: string,
      autocompleteOnSpace: boolean
    }
  ]
};
  • words is required, it must be an array of either strings or RegExp objects. If they are strings they will be available on the autocomplete dropdown, if they are regular expressions they will only be used to apply CSS classes.
  • cssClass is an optional string with classes that will be set on the elements, you can think of it as syntax highlighting.
  • autocompleteOnSpace defaults to false, it determines whether the words on the list will be suggested before the user begins typing a word.

dropdown

This element is a lot more powerful than the autocomplete, it's essentially the same concept, but with custom trigger, list function, and selection functions.

$scope.areaConfig = {
  dropdown: [{
      trigger: string | RegExp
      list: function(match, callback){} | function(callback){},
      onSelect: function(item){},
      filter: boolean,
      mode: 'replace' | 'append'
    }
  ]
};
  • trigger: This can be either a string or a RegExp. In either case, smarArea will call the list function when the user places the cursor after it.
  • list: This function gets called as soon as the trigger is found. If the trigger is a string, the only parameter will be (callback). If the trigger is a RexExp you'll get (match, callback), where match is the result of the regular expression. In both cases you must execute the callback with one parameter:
    • list() should not return anything, use the callback instead (This brings async support).
    • The data to be used by the list will be passed to callback (See the examples)
    • The format of that data is: [{display: string, data: Object}, ...]
    • data is an Object where you can pass information to the onSelect event.
  • onSelect: This function gets called when an element is selected from the dropdown. I receives the selected object that you returned on the list() function (So inside onSelect you can access item.data for you custom properties).
  • filter: Defaults to false, if set to true it will add a filtering field to the dropdown.
  • mode: Either replace or append. Use replace when you want to complete something the user is typing (i.e. user mentions). Use append when you suggest something that goes after the trigger, without removing the trigger.

CSS

The directive doesn't force any styles on the dropdown or textareas so that you can fully integrate it with your design. I would recommend using the stylesheet from the repo, and then modifiying to suit your needs.

Dropdown element

This is the only visible element created by the directive, smart-area doesn't include any styling, it only has some layouting CSS. The dropdown is generated with the following markup:

<div class="sa-dropdown">
  <input type="text" class="form-control"/>
  <ul class="dropdown-menu" role="menu" style="position:static">
    <li ng-repeat="element in dropdown.content | filter:dropdown.filter" role="presentation">
      <a href="" role="menuitem"></a>
    </li>
  </ul>
</div>

As you can see, it uses bootstrap CSS classes, so if you site uses it you'll be all set. Otherwise, create CSS styles for:

  • input.form-control
  • ul.dropdown-menu (and children)
  • ul.dropdown-menu a.active

Extras

Autogrow

If you want to have the textarea autogrow as user types I recommend using smart-area coupled with another directive. I'm using angular-elastic on a project and it works nicely with it:

Plunkr demo

<textarea
  name="sampleTextarea"
  id="sampleTextarea"
  class="form-control code"
  ng-model="myModel"
  ng-trim="false"
  spellcheck="false"
  
  msd-elastic // Directive for autogrowing text areas

  smart-area="areaConfig">
</textarea>

By Alejandro U. Alvarez - AGPLv3 License

Analytics

smart-area's People

Contributors

anaidenko avatar aurbano avatar macherel avatar mont5piques avatar omniphx avatar richardsawyer 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

Watchers

 avatar  avatar  avatar  avatar  avatar

smart-area's Issues

Smart Area and Angular Tabs

Smart area is not working inside angular tabs. the fake area and original textarea are seperated and the width of textarea is not taken.

Last new line is ignored

This seems to be an issue with the way Angular does the binding, because even with ng-trim="false" the last new line is ignored.

Dropdown trigger does not work for first "word"

I am trying to use smart-area to fill in some pre-existing words in a logic equation. This works, but only if the word I am typing in is not in the first location.

At line 371 in smart-area.js I find:

var searchable = text.substr(0, position),
                            match, found = false, lastPosition = 0;
while ((match = element.trigger.exec(searchable)) !== null){
                            if(match.index === lastPosition){
                                break;
                            }
                            lastPosition = match.index;
                            if(match.index + match[0].length === position){
                                found = true;
                                break;
                            }
                        }

When the trigger matches on the first word, lastPosition is 0, and processing stops. I am trying the initialization lastPosition = -1, and this works, but I don't know if this breaks anything else.

Another thing I noted is that searchable is not changed in the while loop, so the while test result never changes, were you intending something else for this, like finding all matches until arriving at the cursor position?

Collecting user objects to send email or any operations on the user mentioned.

Hi , this is a cool component, I have a question - It would be nice if there is a capability to collect the users mentioned in a text area, so that any capabilities on the user can be done. For instance, just like github as I am typing, I can use the @ symbol and the user search opens up, so I can select the user. But after I click "Submit new issue", if I have to send email to those users by saying "You have been mentioned in this issue". Also, the search dropdown can be a User object, I might use the object variables to do these operations, but I might only use the full name for display purpose. Not sure if this can be done with existing features.

I am hopeful we can use the onSelect function in the config, but lets say I collect the user objects in an array , the problem is when the backspace or clear that name, I cant remove that name from that array right ?

Adding directive inside tagged link

Hi!

I would like to know if it's possible to add a directive to the

return '<span class="'+autoList.cssClass+'">'+match+'</span>';

For example, if I do this :

return '<span directive class="'+autoList.cssClass+'">'+match+'</span>';

My directive is not working because the template was never compiled.

Here we just send the plain HTML :

$scope.fakeArea = $sce.trustAsHtml(text);

Even if I try to compile the template before with $compile and then send the outerHTML, it still doesn't work... ๐Ÿ˜ž

Thanks everyone!
Great library by the way!

Allow a Trigger Function for the dropdown API

Right now, the API only allows for the trigger to be a string or a RegEx. For my application, I want the trigger to fire every time the input is a prefix for a word within a dynamic list of words. As a workaround I, I created an object that matches the RegEx API that the current smart-area code uses.

To show what I mean, I have this:

var WordTrigger = function() {
    this.words = [];
    WordTrigger.prototype.exec = (text) => {

        var lastSpaceIdx = lastWhitespaceIndex(text);
        var currToken = text.substring(lastSpaceIdx >= 0 ? lastSpaceIdx+1 : 0).toLowerCase();

        if (currToken === '') {
          return null;
        }

        var results = setFilter(this.bigramDist.words, word => _.startsWith(word, currToken) && word !== currToken);
        var completions = results.map(result => result.substring(currToken.length));

        if (_.isEmpty(results)) {
          return null;
        }

        var retVal = [''];
        retVal.index = text.length;
        retVal.input = text;
        // The above 3 lines are just to appease the smart-area code
        // real results are stored in the 'results' property
        retVal.results = _.zipWith(results, completions, function (result, completion) {
            return {
                'display': result,
                'item': completion
            };
        });

        return retVal;

    };

    WordTrigger.prototype.addWord = (word) => {
        this.words.push(word);
    };
};

And in my areaConfig I just have this:

'trigger': new WordTrigger(), ...

This isn't a very clean workaround and I feel like it would be simple enough to just add support for trigger functions.

AGPLv3 makes it nearly impossible to use the library

Hi,

Recently I was searching for a simple way to add autocomplete to a textarea in some proprietary maintenance application of my company. The application will be used internally by a handful of users. smart-area seems to perfectly fit my needs, but unfortunately it's a JavaScript library licensed under (A)GPLv3.

Using GPLv3 for JavaScript libraries is an ongoing discussion and it's generally considered a bad idea to use it for JS-libs [citation needed].

My question is: was it intentionally to use exactly this license, or would LGPLv3 be a better choice?

Another thing is, that the license definition in the package.json is wrong. It should be "AGPL-3.0" not "AGPL-V3" (see https://spdx.org/licenses/). Webjars refuses to generate a .jar from the lib because of this.

References for the [citation needed]:

http://stackoverflow.com/questions/1239470/restrictions-of-gpl-on-javascript-libraries
http://greendrake.info/#nfy0
https://www.gnu.org/philosophy/javascript-trap.html
...

Thanks for considering,

Manfred

Move cssClass option from autocomplete to dropdown

We have multiple tags (hashtag, cashtag and mention) defined in dropdown array. But "cssClass" property is one for all tags while we need to highlight each tag with own color. Can you make "cssClass" property unique for each tag?

Transfer Ownership

@aurbano This project hasn't seen any commits in about 3 years. Have you considered allowing someone else to take over as maintainer?

[FEAT] Autogrow textarea

It would be nice to have an option for it to autogrow as a user put in larger text or returns.

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.