From 77f7f54ab77646c9ca1f8df92dae2a7f0aa0c433 Mon Sep 17 00:00:00 2001 From: Koen Punt Date: Tue, 2 Aug 2011 16:48:29 +0200 Subject: [PATCH 1/5] Feature: Option adding --- coffee/chosen.jquery.coffee | 29 +- coffee/chosen.proto.coffee | 37 +- coffee/lib/abstract-chosen.coffee | 18 +- public/create-example.jquery.html | 1482 ++++++++++++++++++++++++++++ public/create-example.proto.html | 1493 +++++++++++++++++++++++++++++ 5 files changed, 3052 insertions(+), 7 deletions(-) create mode 100644 public/create-example.jquery.html create mode 100644 public/create-example.proto.html diff --git a/coffee/chosen.jquery.coffee b/coffee/chosen.jquery.coffee index 2efa5c79c1d..91cc31384cd 100644 --- a/coffee/chosen.jquery.coffee +++ b/coffee/chosen.jquery.coffee @@ -187,7 +187,7 @@ class Chosen extends AbstractChosen @search_choices.find("li.search-choice").remove() else if not @is_multiple this.single_set_selected_text() - if @disable_search or @form_field.options.length <= @disable_search_threshold + if @disable_search or @form_field.options.length <= @disable_search_threshold and not @create_option @search_field[0].readOnly = true @container.addClass "chosen-container-single-nosearch" else @@ -332,6 +332,10 @@ class Chosen extends AbstractChosen if @result_highlight high = @result_highlight + if high.hasClass "create-option" + this.select_create_option(@search_field.val()) + return this.results_hide() + this.result_clear_highlight() if @is_multiple and @max_selected_options <= this.choices_count() @@ -407,10 +411,29 @@ class Chosen extends AbstractChosen no_results: (terms) -> no_results_html = $('
  • ' + @results_none_found + ' ""
  • ') no_results_html.find("span").first().html(terms) - @search_results.append no_results_html @form_field_jq.trigger("chosen:no_results", {chosen:this}) + show_create_option: (terms) -> + create_option_html = $('
  • ' + @create_option_text + ': "' + terms + '"
  • ') + @search_results.append create_option_html + + create_option_clear: -> + @search_results.find(".create-option").remove() + + select_create_option: (terms) -> + if $.isFunction(@create_option) + @create_option.call this, terms + else + this.select_append_option( {value: terms, text: terms} ) + + select_append_option: ( options ) -> + option = $('') + @create_option_temp = new Template('
  • #{text}: "#{terms}"
  • ') set_up_html: -> container_classes = ["chosen-container"] @@ -181,7 +183,7 @@ class @Chosen extends AbstractChosen @search_choices.select("li.search-choice").invoke("remove") else if not @is_multiple this.single_set_selected_text() - if @disable_search or @form_field.options.length <= @disable_search_threshold + if @disable_search or @form_field.options.length <= @disable_search_threshold and not @create_option @search_field.readOnly = true @container.addClassName "chosen-container-single-nosearch" else @@ -325,6 +327,11 @@ class @Chosen extends AbstractChosen result_select: (evt) -> if @result_highlight high = @result_highlight + + if high.hasClassName "create-option" + this.select_create_option(@search_field.value) + return this.results_hide() + this.result_clear_highlight() if @is_multiple and @max_selected_options <= this.choices_count() @@ -406,15 +413,39 @@ class @Chosen extends AbstractChosen @search_results.insert @no_results_temp.evaluate( terms: terms ) @form_field.fire("chosen:no_results", {chosen: this}) + show_create_option: (terms) -> + create_option_html = @create_option_temp.evaluate( terms: terms, text: @create_option_text ) + @search_results.insert create_option_html + @search_results.down(".create-option").observe "click", (evt) => this.select_create_option(terms) + + create_option_clear: -> + co = null + co.remove() while co = @search_results.down(".create-option") + + select_create_option: ( terms ) -> + if Object.isFunction( @create_option ) + @create_option.call this, terms + else + this.select_append_option( value: terms, text: terms ) + + select_append_option: ( options ) -> + option = @new_option_temp.evaluate( options ) + @form_field.insert option + Event.fire @form_field, "chosen:updated" + if typeof Event.simulate is 'function' + @form_field.simulate("change") + @search_field.simulate("focus") + no_results_clear: -> nr = null nr.remove() while nr = @search_results.down(".no-results") - keydown_arrow: -> if @results_showing and @result_highlight next_sib = @result_highlight.next('.active-result') this.result_do_highlight next_sib if next_sib + else if @results_showing and @create_option + this.result_do_highlight(@search_results.select('.create-option').first()) else this.results_show() diff --git a/coffee/lib/abstract-chosen.coffee b/coffee/lib/abstract-chosen.coffee index 903fcea254d..af708a43c88 100644 --- a/coffee/lib/abstract-chosen.coffee +++ b/coffee/lib/abstract-chosen.coffee @@ -29,6 +29,9 @@ class AbstractChosen @inherit_select_classes = @options.inherit_select_classes || false @display_selected_options = if @options.display_selected_options? then @options.display_selected_options else true @display_disabled_options = if @options.display_disabled_options? then @options.display_disabled_options else true + @create_option = @options.create_option || false + @persistent_create_option = @options.persistent_create_option || false + @skip_no_results = @options.skip_no_results || false set_default_text: -> if @form_field.getAttribute("data-placeholder") @@ -39,6 +42,7 @@ class AbstractChosen @default_text = @options.placeholder_text_single || @options.placeholder_text || AbstractChosen.default_single_text @results_none_found = @form_field.getAttribute("data-no_results_text") || @options.no_results_text || AbstractChosen.default_no_result_text + @create_option_text = @form_field.getAttribute("data-create_option_text") || @options.create_option_text || AbstractChosen.default_create_option_text mouse_enter: -> @mouse_on_container = true mouse_leave: -> @mouse_on_container = false @@ -101,6 +105,9 @@ class AbstractChosen this.outerHTML(group_el) + append_option: (option) -> + this.select_append_option(option) + results_update_field: -> this.set_default_text() this.results_reset_cleanup() if not @is_multiple @@ -128,11 +135,13 @@ class AbstractChosen this.no_results_clear() results = 0 + exact_result = false searchText = this.get_search_text() escapedSearchText = searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&") zregex = new RegExp(escapedSearchText, 'i') regex = this.get_search_regex(escapedSearchText) + eregex = new RegExp('^' + escapedSearchText + '$', 'i') for option in @results_data @@ -156,6 +165,8 @@ class AbstractChosen option.search_match = this.search_string_match(option.search_text, regex) results += 1 if option.search_match and not option.group + exact_result = eregex.test option.html + if option.search_match if searchText.length startpos = option.search_text.search zregex @@ -171,11 +182,14 @@ class AbstractChosen if results < 1 and searchText.length this.update_results_content "" - this.no_results searchText + this.no_results searchText unless @create_option and @skip_no_results else this.update_results_content this.results_option_build() this.winnow_results_set_highlight() + if @create_option and (results < 1 or (!exact_result and @persistent_create_option)) and searchText.length + this.show_create_option( searchText ) + get_search_regex: (escaped_search_string) -> regex_anchor = if @search_contains then "" else "^" new RegExp(regex_anchor + escaped_search_string, 'i') @@ -269,4 +283,4 @@ class AbstractChosen @default_multiple_text: "Select Some Options" @default_single_text: "Select an Option" @default_no_result_text: "No results match" - + @default_create_option_text: "Add Option" diff --git a/public/create-example.jquery.html b/public/create-example.jquery.html new file mode 100644 index 00000000000..a176a2ae906 --- /dev/null +++ b/public/create-example.jquery.html @@ -0,0 +1,1482 @@ + + + + + + + + +
    +
    +
    +
    +

    Chosen

    +
    +

    Chosen is a jQuery plugin that makes long, unwieldy select boxes much more user-friendly.

    + +

    + Downloads + Project Source + Contribute +

    + +

    Standard Select

    +
    +
    + Turns This + +
    +
    + Into This + +
    +
    + +

    Multiple Select

    +
    +
    + Turns This + +
    +
    + Into This + +
    +
    + +

    <optgroup> Support

    +
    +
    + Single Select with Groups + +
    +
    + Multiple Select with Groups + +
    +
    + +

    Selected and Disabled Support

    +
    +

    Chosen automatically highlights selected options and removes disabled options.

    +
    + Single Select + +
    +
    + Multiple Select + +
    +
    + +

    Hide Search on Single Select

    +
    +

    The disable_search_threshold option can be specified to hide the search input on single selects if there are fewer than (n) options.

    + + $(".chosen-select").chosen({disable_search_threshold: 10}); + +

    +
    + +
    +
    + +

    Default Text Support

    +
    +

    Chosen automatically sets the default field text ("Choose a country...") by reading the select element's data-placeholder value. If no data-placeholder value is present, it will default to "Select an Option" or "Select Some Options" depending on whether the select is single or multiple. You can change these elements in the plugin js file as you see fit.

    + <select data-placeholder="Choose a country..." style="width:350px;" multiple class="chosen-select"> +

    Note: on single selects, the first element is assumed to be selected by the browser. To take advantage of the default text support, you will need to include a blank option as the first element of your select list.

    +
    + +

    No Results Text Support

    +
    +

    Setting the "No results" search text is as easy as passing an option when you create Chosen:

    + + $(".chosen-select").chosen({no_results_text: "Oops, nothing found!"}); + +

    +
    + Single Select + +
    +
    + Multiple Select + +
    +
    + +

    Limit Selected Options in Multiselect

    +
    +

    You can easily limit how many options can user select:

    + + $(".chosen-select").chosen({max_selected_options: 5}); + +

    If you try to select another option with limit reached chosen:maxselected event is triggered:

    + + $(".chosen-select").bind("chosen:maxselected", function () { ... }); + +
    + +

    Allow Deselect on Single Selects

    +
    +

    When a single select box isn't a required field, you can set allow_single_deselect: true and Chosen will add a UI element for option deselection. This will only work if the first option has blank text.

    +
    + +
    +
    + +

    Right to Left Support

    +
    +

    Chosen supports right to left select boxes too. just add "chosen-rtl" in addition to "chosen-select" to your select tags and you are good to go.

    +

    <select class="chosen-select chosen-rtl">

    +
    + Single right to left select + +
    +
    + Multiple right to left select + +
    +
    + +

    Change / Update Events

    +
    +
      +
    • +

      Form Field Change

      +

      When working with form fields, you often want to perform some behavior after a value has been selected or deselected. Whenever a user selects a field in Chosen, it triggers a "change" event* on the original form field. That let's you do something like this:

      +

      $("#form_field").chosen().change( … );

      +
    • +
    • +

      Updating Chosen Dynamically

      +

      If you need to update the options in your select field and want Chosen to pick up the changes, you'll need to trigger the "chosen:updated" event on the field. Chosen will re-build itself based on the updated content.

      +

      $("#form_field").trigger("chosen:updated");

      +
    • +
    +
    + +

    Custom Width Support

    +
    +

    Using a custom width with Chosen is as easy as passing an option when you create Chosen:

    + + $(".chosen-select").chosen({width: "95%"}); + +

    +
    + Single Select + +
    +
    + Multiple Select + +
    +
    + +

    Labels work, too

    +
    +

    Use labels just like you would a standard select

    +

    +
    + + +
    +
    + + +
    +
    + +

    Setup

    +

    Using Chosen is easy as can be.

    +
      +
    1. Download the plugin and copy the chosen files to your app.
    2. +
    3. Activate the plugin on the select boxes of your choice: $(".chosen-select").chosen()
    4. +
    5. Disco.
    6. +
    + +

    FAQs

    +
      +
    • +

      Something doesn't work. Can you fix it?

      +

      Yes! Please report all issues using the GitHub issue tracking tool. Please include the plugin version (jQuery or Prototype), browser and OS. The more information provided, the easier it is to fix a problem.

      +
    • +
    • +

      What browsers are supported?

      +

      All modern browsers are supported (Firefox, Chrome, Safari and IE9). Legacy support for IE8 is also enabled.

      +
    • +
    • +

      Didn't there used to be a Prototype version of Chosen?

      +

      There still is!

      +
    • +
    + +

    Credits

    + + + + + +
    +
    + + + +
    +
    + +
    + + diff --git a/public/create-example.proto.html b/public/create-example.proto.html new file mode 100644 index 00000000000..8eed74c5ee0 --- /dev/null +++ b/public/create-example.proto.html @@ -0,0 +1,1493 @@ + + + + + + + + +
    +
    +
    +

    Chosen - Prototype Version

    +
    +

    Chosen is a Prototype plugin that makes long, unwieldy select boxes much more user-friendly.

    + +

    + Downloads + Project Source + Contribute +

    + +

    Looking for the jQuery version?

    + +

    Standard Select

    +
    +
    + Turns This + +
    +
    + Into This + +
    +
    + +

    Multiple Select

    +
    +
    + Turns This + +
    +
    + Into This + +
    +
    + +

    <optgroup> Support

    +
    +
    + Single Select with Groups + +
    +
    + Multiple Select with Groups + +
    +
    + +

    Selected and Disabled Support

    +
    +

    Chosen automatically highlights selected options and removes disabled options.

    +
    + Single Select + +
    +
    + Multiple Select + +
    +
    + +

    Hide Search on Single Select

    +
    +

    The disable_search_threshold option can be specified to hide the search input on single selects if there are fewer than (n) options.

    + + New Chosen($("chosen_select_field"),{disable_search_threshold: 10}); + +

    +
    + +
    +
    + +

    Default Text Support

    +
    +

    Chosen automatically sets the default field text ("Choose a country...") by reading the select element's data-placeholder value. If no data-placeholder value is present, it will default to "Select an Option" or "Select Some Options" depending on whether the select is single or multiple. You can change these elements in the plugin js file as you see fit.

    + <select data-placeholder="Choose a country..." style="width:350px;" multiple class="chosen-select"> +

    Note: on single selects, the first element is assumed to be selected by the browser. To take advantage of the default text support, you will need to include a blank option as the first element of your select list.

    +
    + +

    No Results Text Support

    +
    +

    Setting the "No results" search text is as easy as passing an option when you create Chosen:

    + + New Chosen($("chosen_select_field"),{no_results_text: "Oops, nothing found!"}); + +

    + +
    + Single Select + +
    +
    + Multiple Select + +
    +
    + +

    Limit Selected Options in Multiselect

    +
    +

    You can easily limit how many options can user select:

    + + new Chosen($("chosen_select_field"),{max_selected_options: 5}); + +

    If you try to select another option with limit reached chosen:maxselected event is triggered:

    + + $("chosen_select_field").observe("chosen:maxselected", function(evt) { ... }); + +

    +
    + +

    Allow Deselect on Single Selects

    +
    +

    When a single select box isn't a required field, you can set allow_single_deselect: true and Chosen will add a UI element for option deselection. This will only work if the first option has blank text.

    +
    + +
    +
    + +

    Right to Left Support

    +
    +

    Chosen supports right to left select boxes too. just add "chosen-rtl" in addition to "chosen-select" to your select tags and you are good to go.

    +

    <select class="chosen-select chosen-rtl">

    +
    + Single right to left select + +
    +
    + Multiple right to left select + +
    +
    + +

    Change / Update Events

    +
    +
      +
    • +

      Form Field Change

      +

      When working with form fields, you often want to perform some behavior after a value has been selected or deselected. Whenever a user selects a field in Chosen, it triggers a "change" event* on the original form field. That let's you do something like this:

      +

      $("#form_field").chosen().change( … );

      +

      Note: Prototype doesn't offer support for triggering standard browser events. Event.simulate is required to trigger the change event when using the Prototype version.

      +
    • +
    • +

      Updating Chosen Dynamically

      +

      If you need to update the options in your select field and want Chosen to pick up the changes, you'll need to trigger the "chosen:updated" event on the field. Chosen will re-build itself based on the updated content.

      +
        +

        Event.fire($("form_field"), "chosen:updated");

        +
      +
    • +
    +
    + +

    Custom Width Support

    +
    +

    Using a custom width with Chosen is as easy as passing an option when you create Chosen:

    + + New Chosen($("chosen_select_field"),{width: "95%"}); + +

    +
    + Single Select + +
    +
    + Multiple Select + +
    +
    + +

    Labels work, too

    +
    +

    Use labels just like you would a standard select

    +

    +
    + + +
    +
    + + +
    +
    + +

    Setup

    +

    Using Chosen is easy as can be.

    +
      +
    1. Download the plugin and copy the chosen files to your app.
    2. +
    3. Activate the plugin by creating a new instance of Chosen: New Chosen(some_form_field,some_options);
    4. +
    5. Disco.
    6. +
    + +

    FAQs

    +
      +
    • +

      Something doesn't work. Can you fix it?

      +

      Yes! Please report all issues using the GitHub issue tracking tool. Please include the plugin version (jQuery or Prototype), browser and OS. The more information provided, the easier it is to fix a problem.

      +
    • +
    • +

      What browsers are supported?

      +

      All modern browsers are supported (Firefox, Chrome, Safari and IE9). Legacy support for IE8 is also enabled.

      +
    • +
    +
    + + + +
    + +
    + + From cb16639408e72090c840c3ccee3ed22a98a262f0 Mon Sep 17 00:00:00 2001 From: dkavanagh Date: Wed, 8 Oct 2014 22:20:21 -0400 Subject: [PATCH 2/5] added create_with_enter option that means a user can type a new value and press enter to get it added. our UX expert insisted on this --- coffee/lib/abstract-chosen.coffee | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/coffee/lib/abstract-chosen.coffee b/coffee/lib/abstract-chosen.coffee index af708a43c88..f6257e135fe 100644 --- a/coffee/lib/abstract-chosen.coffee +++ b/coffee/lib/abstract-chosen.coffee @@ -30,6 +30,7 @@ class AbstractChosen @display_selected_options = if @options.display_selected_options? then @options.display_selected_options else true @display_disabled_options = if @options.display_disabled_options? then @options.display_disabled_options else true @create_option = @options.create_option || false + @create_with_enter = @options.create_with_enter || false @persistent_create_option = @options.persistent_create_option || false @skip_no_results = @options.skip_no_results || false @@ -231,6 +232,10 @@ class AbstractChosen this.results_search() when 13 evt.preventDefault() + searchText = this.get_search_text() + if @create_option and @create_with_enter and @results_showing and searchTe + @select_create_option(searchText) + return this.results_hide() this.result_select(evt) if this.results_showing when 27 this.results_hide() if @results_showing From ffa9bf944b08954ba3cbf30ef8eed70b91a31f70 Mon Sep 17 00:00:00 2001 From: dkavanagh Date: Fri, 17 Oct 2014 08:15:00 -0400 Subject: [PATCH 3/5] fixed truncated line --- coffee/lib/abstract-chosen.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coffee/lib/abstract-chosen.coffee b/coffee/lib/abstract-chosen.coffee index f6257e135fe..ff488731e7a 100644 --- a/coffee/lib/abstract-chosen.coffee +++ b/coffee/lib/abstract-chosen.coffee @@ -233,7 +233,7 @@ class AbstractChosen when 13 evt.preventDefault() searchText = this.get_search_text() - if @create_option and @create_with_enter and @results_showing and searchTe + if @create_option and @create_with_enter and @results_showing and searchText.length @select_create_option(searchText) return this.results_hide() this.result_select(evt) if this.results_showing From 854e537803910dd24a27cff3aa815a892fef7c49 Mon Sep 17 00:00:00 2001 From: dkavanagh Date: Wed, 8 Apr 2015 09:03:45 -0400 Subject: [PATCH 4/5] changed @ to this. --- coffee/lib/abstract-chosen.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coffee/lib/abstract-chosen.coffee b/coffee/lib/abstract-chosen.coffee index ff488731e7a..015bb828728 100644 --- a/coffee/lib/abstract-chosen.coffee +++ b/coffee/lib/abstract-chosen.coffee @@ -233,8 +233,8 @@ class AbstractChosen when 13 evt.preventDefault() searchText = this.get_search_text() - if @create_option and @create_with_enter and @results_showing and searchText.length - @select_create_option(searchText) + if this.create_option and this.create_with_enter and this.results_showing and searchText.length + this.select_create_option(searchText) return this.results_hide() this.result_select(evt) if this.results_showing when 27 From 0903bf0a9a09fe28c677bf2181bda3027cc96b2d Mon Sep 17 00:00:00 2001 From: dkavanagh Date: Wed, 8 Apr 2015 09:07:05 -0400 Subject: [PATCH 5/5] reverted variable refs to @ --- coffee/lib/abstract-chosen.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coffee/lib/abstract-chosen.coffee b/coffee/lib/abstract-chosen.coffee index 015bb828728..0d5dd446fbd 100644 --- a/coffee/lib/abstract-chosen.coffee +++ b/coffee/lib/abstract-chosen.coffee @@ -233,7 +233,7 @@ class AbstractChosen when 13 evt.preventDefault() searchText = this.get_search_text() - if this.create_option and this.create_with_enter and this.results_showing and searchText.length + if @create_option and @create_with_enter and @results_showing and searchText.length this.select_create_option(searchText) return this.results_hide() this.result_select(evt) if this.results_showing