Skip to content

Commit 02596e3

Browse files
committed
added 2 new props delimiters & valuesFromPaste furqanZafar#21
1 parent 898e8e5 commit 02596e3

11 files changed

+135
-14
lines changed

.travis.yml

-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
language: node_js
22
node_js:
33
- "0.10"
4-
- "0.11"
5-
- "0.12"
64
before script:
75
- npm install -g gulp
86
script:

public/components/App.ls

+10
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,16 @@ Select one more item and the control will be disabled until one or more are dele
5151
Add and remove items in any order without touching your mouse.
5252
Use your left/right arrow keys to move the cursor between items.
5353
"""
54+
languages:
55+
jsx: fs.read-file-sync \public/examples/multi/TagsBasic.jsx, \utf8
56+
ls: fs.read-file-sync \public/examples/multi/TagsBasic.ls, \utf8
57+
58+
59+
* title: "Tags - Advance usage"
60+
description: """
61+
default values, comma to confirm selection, custom error messages (like "Tag already exists")
62+
convert pasted text into tags, edit existing tags by pressing [backspace]
63+
"""
5464
languages:
5565
jsx: fs.read-file-sync \public/examples/multi/Tags.jsx, \utf8
5666
ls: fs.read-file-sync \public/examples/multi/Tags.ls, \utf8

public/examples/multi/Tags.jsx

+21-3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,24 @@ Form = React.createClass({
77

88
values = {this.state.tags}
99

10+
// delimtiers :: [KeyCode]
11+
delimiters = {[188]}
12+
13+
// valuesFromPaste :: [Item] -> [Item] -> String -> [Item]
14+
valuesFromPaste = {function(options, values, pastedText){
15+
return pastedText
16+
.split(",")
17+
.filter(function(text){
18+
var labels = values.map(function(item){
19+
return item.label;
20+
})
21+
return labels.indexOf(text) == -1;
22+
})
23+
.map(function(text){
24+
return {label: text, value: text};
25+
});
26+
}}
27+
1028
// restoreOnBackspace :: Item -> String
1129
restoreOnBackspace = {function(item){
1230
return item.label;
@@ -30,12 +48,12 @@ Form = React.createClass({
3048
// renderNoResultsFound :: [Item] -> String -> ReactElement
3149
renderNoResultsFound = {function(values, search) {
3250
return <div className = "no-results-found">
33-
{(function(){
51+
{function(){
3452
if (search.trim().length == 0)
3553
return "Type a few characters to create a tag";
36-
else if (values.map(function(value){ return value.label; }).indexOf(search) != -1)
54+
else if (values.map(function(item){ return item.label; }).indexOf(search.trim()) != -1)
3755
return "Tag already exists";
38-
})()}
56+
}()}
3957
</div>
4058
}}
4159
/>;

public/examples/multi/Tags.ls

+12-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,17 @@ Form = React.create-class do
66

77
values: @state.tags
88

9+
# 188 = comma
10+
# delimiters :: [KeyCode]
11+
delimiters: [188]
12+
13+
# values-from-paste :: [Item] -> [Item] -> String -> [Item]
14+
values-from-paste: (options, values, pasted-text) ~>
15+
pasted-text
16+
|> Str.split \,
17+
|> reject ~> it in map (.label), values
18+
|> map ~> label: it, value: it
19+
920
# restore-on-backspace :: Item -> String
1021
restore-on-backspace: (.label)
1122

@@ -22,7 +33,7 @@ Form = React.create-class do
2233
div class-name: \no-results-found,
2334
if search.trim!.length == 0
2435
"Type a few characters to create a tag"
25-
else if (search in map (.label), values)
36+
else if (search.trim! in map (.label), values)
2637
"Tag already exists"
2738

2839
# get-initial-state :: a -> UIState

public/examples/multi/TagsBasic.jsx

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
Form = React.createClass({
2+
3+
// render :: a -> ReactElement
4+
render: function(){
5+
self = this;
6+
return <MultiSelect
7+
8+
// createFromSearch :: [Item] -> [Item] -> String -> Item?
9+
createFromSearch = {function(options, values, search){
10+
labels = values.map(function(value){
11+
return value.label;
12+
})
13+
if (search.trim().length == 0 || labels.indexOf(search.trim()) != -1)
14+
return null;
15+
return {label: search.trim(), value: search.trim()};
16+
}}
17+
18+
/>;
19+
},
20+
21+
});
22+
23+
render(<Form/>, mountNode);

public/examples/multi/TagsBasic.ls

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Form = React.create-class do
2+
3+
# render :: a -> ReactElement
4+
render: ->
5+
React.create-element MultiSelect,
6+
7+
# create-from-search :: [Item] -> [Item] -> String -> Item?
8+
create-from-search: (options, values, search) ->
9+
return null if search.trim!.length == 0 or search.trim! in map (.label), values
10+
label: search.trim!, value: search.trim!
11+
12+
render (React.create-element Form, null), mount-node

src/MultiSelect.ls

+25-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ module.exports = React.create-class do
1212
# anchor :: Item
1313
# class-name :: String
1414
close-on-select: false
15+
# values-from-paste :: String -> [Item]
1516
default-values: []
17+
delimiters: []
1618
# disabled :: Boolean
1719
# create-from-search :: [Item] -> [Item] -> String -> Item?
1820
# filter-options :: [Item] -> [Item] -> String -> [Item]
@@ -25,6 +27,7 @@ module.exports = React.create-class do
2527
on-blur: ((values, reason) !->) # :: [Item] -> String -> Void
2628
on-enter: ((highlighted-option) !->) # :: Item -> Void
2729
on-focus: ((values, reason) !->) # :: [Item] -> String -> Void
30+
on-paste: ((e) !-> true) # Event -> Boolean
2831
# on-search-change :: String -> (a -> Void) -> Void
2932
# on-value-change :: Item -> (a -> Void) -> Void
3033
# options :: [Item]
@@ -40,16 +43,18 @@ module.exports = React.create-class do
4043
# render :: a -> ReactElement
4144
render: ->
4245

43-
{anchor, search, values, on-anchor-change, on-search-change, on-values-change, filtered-options, options} = @get-computed-state!
46+
{anchor, search, values, on-anchor-change, on-search-change,
47+
on-values-change, filtered-options, options} = @get-computed-state!
4448

4549
# props
46-
{autosize, disabled, dropdown-direction, group-id, groups, groups-as-columns, on-enter, render-group-title,
50+
{autosize, delimiters, disabled, dropdown-direction, group-id, groups, groups-as-columns, on-enter, render-group-title,
4751
transition-enter, transition-leave, transition-enter-timeout, transition-leave-timeout, uid} = @props
4852

4953
ReactSelectize {
5054

5155
autosize
5256
class-name: "multi-select" + if !!@props.class-name then " #{@props.class-name}" else ""
57+
delimiters
5358
disabled
5459
dropdown-direction
5560
group-id
@@ -104,6 +109,18 @@ module.exports = React.create-class do
104109

105110
on-focus: (, reason) !~> @props.on-focus values, reason
106111

112+
# on-paste :: Event -> Boolean
113+
on-paste:
114+
| typeof @props?.values-from-paste == \undefined => @props.on-paste
115+
| _ => ({clipboard-data}:e) ~>
116+
do ~>
117+
new-values = values ++ (@props.values-from-paste options, values, clipboard-data.get-data \text)
118+
<~ on-values-change new-values
119+
on-anchor-change last new-values
120+
121+
e.prevent-default!
122+
false
123+
107124
# STYLE
108125
placeholder: @props.placeholder
109126
style: @props.style
@@ -113,7 +130,8 @@ module.exports = React.create-class do
113130
| typeof @props.restore-on-backspace == \function => restore-on-backspace: @props.restore-on-backspace
114131
| _ => {})
115132
<<< (switch
116-
| typeof @props.render-no-results-found == \function => render-no-results-found: ~> @props.render-no-results-found values, search
133+
| typeof @props.render-no-results-found == \function => render-no-results-found: ~>
134+
@props.render-no-results-found values, search
117135
| _ => {})
118136

119137

@@ -147,7 +165,10 @@ module.exports = React.create-class do
147165

148166
# filter options and create new one from search text
149167
filtered-options = @props.filter-options unfiltered-options, values, search
150-
new-option = if typeof @props.create-from-search == \function then (@props.create-from-search filtered-options, values, search) else null
168+
new-option =
169+
| typeof @props.create-from-search == \function => @props.create-from-search filtered-options, values, search
170+
| _ => null
171+
151172
options = (if !!new-option then [{} <<< new-option <<< new-option: true] else []) ++ filtered-options
152173

153174
{anchor, search, values, on-anchor-change, on-search-change, on-values-change, filtered-options, options}

src/ReactSelectize.ls

+9-1
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ module.exports = create-class do
150150
document.body.remove-child $input
151151

152152
# class-name :: String
153+
delimiters: []
153154
disabled: false
154155
dropdown-direction: 1
155156
first-option-index-to-highlight: (options) -> 0
@@ -163,6 +164,7 @@ module.exports = create-class do
163164
on-focus: ((values, reason) !->) # [Item] -> String -> Void
164165
on-highlighted-uid-change: ((uid, callback) !-> ) # (Eq e) => e -> (a -> Void) -> Void
165166
on-open-change: ((open, callback) !->) # Boolean -> (a -> Void) -> Void
167+
on-paste: ((e) !-> true) # Event -> Boolean
166168
on-search-change: ((search, callback) !-> ) # String -> (a -> Void) -> Void
167169
on-values-change: ((values, callback) !->) # [Item] -> (a -> Void) -> Void
168170
open: false
@@ -269,6 +271,10 @@ module.exports = create-class do
269271
@props.on-open-change true, -> callback true
270272
@props.on-focus @props.values, if !!result then \event else \focus
271273

274+
# on-paste :: Event -> Boolean
275+
on-paste: @props.on-paste
276+
277+
# on-key-down :: Event -> Boolean
272278
on-key-down: (e) ~>
273279

274280
# always handle the tab, backspace & escape keys
@@ -338,7 +344,7 @@ module.exports = create-class do
338344
@focus!
339345

340346
# ENTER
341-
if e.which == 13 and @props.open
347+
if (e.which in [13] ++ @props.delimiters) and @props.open
342348

343349
# find the highlighted option if any and invoke the on-enter prop
344350
highlighted-option =
@@ -349,6 +355,8 @@ module.exports = create-class do
349355
# select the highlighted option (if any)
350356
@select-highlighted-uid anchor-index
351357

358+
return cancel-event e
359+
352360
# move anchor position left / right using arrow keys (only when search field is empty)
353361
if @props.search.length == 0
354362

src/SimpleSelect.ls

+4-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ module.exports = React.create-class do
1111
get-default-props: ->
1212
# class-name :: String
1313
# disabled :: Boolean
14+
delimiters: []
1415
# create-from-search :: [Item] -> String -> Item?
1516
# editable :: Item -> String
1617
# filter-options :: [Item] -> String -> [Item]
@@ -39,13 +40,14 @@ module.exports = React.create-class do
3940
{search, value, values, on-search-change, on-value-change, filtered-options, options} = @get-computed-state!
4041

4142
# props
42-
{autosize, disabled, dropdown-direction, group-id, groups, groups-as-columns, on-enter, render-group-title,
43-
transition-enter, transition-leave, transition-enter-timeout, transition-leave-timeout, uid} = @props
43+
{autosize, delimiters, disabled, dropdown-direction, group-id, groups, groups-as-columns, on-enter,
44+
render-group-title, transition-enter, transition-leave, transition-enter-timeout, transition-leave-timeout, uid} = @props
4445

4546
ReactSelectize {
4647

4748
autosize
4849
class-name: "simple-select" + if !!@props.class-name then " #{@props.class-name}" else ""
50+
delimiters
4951
disabled
5052
dropdown-direction
5153
group-id

test/common-tests.ls

+9
Original file line numberDiff line numberDiff line change
@@ -370,3 +370,12 @@ module.exports = (select-class) !->
370370
press-return input-element
371371
set-input-text input-element, \apples
372372
press-return input-element
373+
374+
specify "delimiters", ->
375+
select = create-select do
376+
delimiters: [186, 188]
377+
click-to-open-select-control
378+
input-element = get-input select
379+
set-input-text input-element, \app
380+
key-down input-element, which: 188
381+
find-rendered-DOM-component-with-class select, \simple-value

test/multi-select.ls

+10-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ ReactSelectize = require \../src/index.ls
99

1010
# TestUtils
1111
{find-rendered-DOM-component-with-class, scry-rendered-DOM-components-with-class,
12-
find-rendered-DOM-component-with-tag, Simulate:{change, click, focus, key-down}}:TestUtils = require \react-addons-test-utils
12+
find-rendered-DOM-component-with-tag, Simulate:{change, click, focus, key-down, paste}}:TestUtils = require \react-addons-test-utils
1313

1414
# utils
1515
{create-select, get-input, set-input-text, get-item-text, click-to-open-select-control, find-highlighted-option,
@@ -164,3 +164,12 @@ describe "MultiSelect", ->
164164
click-to-open-select-control select
165165
find-rendered-DOM-component-with-class select, \simple-option
166166

167+
specify "must create values from pasted text & override the on-paste prop", ->
168+
select = create-multi-select do
169+
values-from-paste: (, , search) ~> search.split \, |> map ~> label: it, value: it
170+
on-paste: (e) ~> true
171+
click-to-open-select-control
172+
input = get-input select
173+
paste input, clipboard-data: get-data: -> "a,b,c"
174+
assert select.values!.length == 3
175+

0 commit comments

Comments
 (0)