Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
79f0044d
Commit
79f0044d
authored
Jan 29, 2020
by
Marvin Karegyeya
Committed by
Kushal Pandya
Jan 29, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactor gl dropdown.js
parent
b4a2ba41
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
701 additions
and
695 deletions
+701
-695
app/assets/javascripts/gl_dropdown.js
app/assets/javascripts/gl_dropdown.js
+696
-695
changelogs/unreleased/Refactor-gl_dropdown-js.yml
changelogs/unreleased/Refactor-gl_dropdown-js.yml
+5
-0
No files found.
app/assets/javascripts/gl_dropdown.js
View file @
79f0044d
/* eslint-disable
func-names, no-underscore-dangle, one-var, no-cond-assign, no-return-assign, no-else-return, camelcase, no-lonely-if, guard-for-in, no-restricted-syntax, consistent-return, no-param-reassign, no-loop-func
*/
/* eslint-disable
one-var, consistent-return
*/
import
$
from
'
jquery
'
;
import
$
from
'
jquery
'
;
import
_
from
'
underscore
'
;
import
_
from
'
underscore
'
;
...
@@ -32,121 +32,124 @@ const FILTER_INPUT = '.dropdown-input .dropdown-input-field:not(.dropdown-no-fil
...
@@ -32,121 +32,124 @@ const FILTER_INPUT = '.dropdown-input .dropdown-input-field:not(.dropdown-no-fil
const
NO_FILTER_INPUT
=
'
.dropdown-input .dropdown-input-field.dropdown-no-filter
'
;
const
NO_FILTER_INPUT
=
'
.dropdown-input .dropdown-input-field.dropdown-no-filter
'
;
function
GitLabDropdownInput
(
input
,
options
)
{
class
GitLabDropdownInput
{
const
_this
=
this
;
constructor
(
input
,
options
)
{
this
.
input
=
input
;
this
.
input
=
input
;
this
.
options
=
options
;
this
.
options
=
options
;
this
.
fieldName
=
this
.
options
.
fieldName
||
'
field-name
'
;
this
.
fieldName
=
this
.
options
.
fieldName
||
'
field-name
'
;
const
$inputContainer
=
this
.
input
.
parent
();
const
$inputContainer
=
this
.
input
.
parent
();
const
$clearButton
=
$inputContainer
.
find
(
'
.js-dropdown-input-clear
'
);
const
$clearButton
=
$inputContainer
.
find
(
'
.js-dropdown-input-clear
'
);
$clearButton
.
on
(
'
click
'
,
e
=>
{
$clearButton
.
on
(
'
click
'
,
e
=>
{
// Clear click
// Clear click
e
.
preventDefault
();
e
.
preventDefault
();
e
.
stopPropagation
();
e
.
stopPropagation
();
return
this
.
input
return
this
.
input
.
val
(
''
)
.
val
(
''
)
.
trigger
(
'
input
'
)
.
trigger
(
'
input
'
)
.
focus
();
.
focus
();
});
this
.
input
.
on
(
'
keydown
'
,
e
=>
{
const
keyCode
=
e
.
which
;
if
(
keyCode
===
13
&&
!
options
.
elIsInput
)
{
e
.
preventDefault
();
}
})
.
on
(
'
input
'
,
e
=>
{
let
val
=
e
.
currentTarget
.
value
||
_this
.
options
.
inputFieldName
;
val
=
val
.
split
(
'
'
)
.
join
(
'
-
'
)
// replaces space with dash
.
replace
(
/
[^
a-zA-Z0-9 -
]
/g
,
''
)
.
toLowerCase
()
// replace non alphanumeric
.
replace
(
/
(
-
)\1
+/g
,
'
-
'
);
// replace repeated dashes
_this
.
cb
(
_this
.
options
.
fieldName
,
val
,
{},
true
);
_this
.
input
.
closest
(
'
.dropdown
'
)
.
find
(
'
.dropdown-toggle-text
'
)
.
text
(
val
);
});
});
}
GitLabDropdownInput
.
prototype
.
onInput
=
function
(
cb
)
{
this
.
input
this
.
cb
=
cb
;
.
on
(
'
keydown
'
,
e
=>
{
};
const
keyCode
=
e
.
which
;
if
(
keyCode
===
13
&&
!
options
.
elIsInput
)
{
e
.
preventDefault
();
}
})
.
on
(
'
input
'
,
e
=>
{
let
val
=
e
.
currentTarget
.
value
||
this
.
options
.
inputFieldName
;
val
=
val
.
split
(
'
'
)
.
join
(
'
-
'
)
// replaces space with dash
.
replace
(
/
[^
a-zA-Z0-9 -
]
/g
,
''
)
.
toLowerCase
()
// replace non alphanumeric
.
replace
(
/
(
-
)\1
+/g
,
'
-
'
);
// replace repeated dashes
this
.
cb
(
this
.
options
.
fieldName
,
val
,
{},
true
);
this
.
input
.
closest
(
'
.dropdown
'
)
.
find
(
'
.dropdown-toggle-text
'
)
.
text
(
val
);
});
}
function
GitLabDropdownFilter
(
input
,
options
)
{
onInput
(
cb
)
{
let
ref
,
timeout
;
this
.
cb
=
cb
;
this
.
input
=
input
;
}
this
.
options
=
options
;
this
.
filterInputBlur
=
(
ref
=
this
.
options
.
filterInputBlur
)
!=
null
?
ref
:
true
;
const
$inputContainer
=
this
.
input
.
parent
();
const
$clearButton
=
$inputContainer
.
find
(
'
.js-dropdown-input-clear
'
);
$clearButton
.
on
(
'
click
'
,
e
=>
{
// Clear click
e
.
preventDefault
();
e
.
stopPropagation
();
return
this
.
input
.
val
(
''
)
.
trigger
(
'
input
'
)
.
focus
();
});
// Key events
timeout
=
''
;
this
.
input
.
on
(
'
keydown
'
,
e
=>
{
const
keyCode
=
e
.
which
;
if
(
keyCode
===
13
&&
!
options
.
elIsInput
)
{
e
.
preventDefault
();
}
})
.
on
(
'
input
'
,
()
=>
{
if
(
this
.
input
.
val
()
!==
''
&&
!
$inputContainer
.
hasClass
(
HAS_VALUE_CLASS
))
{
$inputContainer
.
addClass
(
HAS_VALUE_CLASS
);
}
else
if
(
this
.
input
.
val
()
===
''
&&
$inputContainer
.
hasClass
(
HAS_VALUE_CLASS
))
{
$inputContainer
.
removeClass
(
HAS_VALUE_CLASS
);
}
// Only filter asynchronously only if option remote is set
if
(
this
.
options
.
remote
)
{
clearTimeout
(
timeout
);
return
(
timeout
=
setTimeout
(()
=>
{
$inputContainer
.
parent
().
addClass
(
'
is-loading
'
);
return
this
.
options
.
query
(
this
.
input
.
val
(),
data
=>
{
$inputContainer
.
parent
().
removeClass
(
'
is-loading
'
);
return
this
.
options
.
callback
(
data
);
});
},
250
));
}
else
{
return
this
.
filter
(
this
.
input
.
val
());
}
});
}
}
GitLabDropdownFilter
.
prototype
.
shouldBlur
=
function
(
keyCode
)
{
class
GitLabDropdownFilter
{
return
BLUR_KEYCODES
.
indexOf
(
keyCode
)
!==
-
1
;
constructor
(
input
,
options
)
{
};
let
ref
,
timeout
;
this
.
input
=
input
;
this
.
options
=
options
;
// eslint-disable-next-line no-cond-assign
this
.
filterInputBlur
=
(
ref
=
this
.
options
.
filterInputBlur
)
!=
null
?
ref
:
true
;
const
$inputContainer
=
this
.
input
.
parent
();
const
$clearButton
=
$inputContainer
.
find
(
'
.js-dropdown-input-clear
'
);
$clearButton
.
on
(
'
click
'
,
e
=>
{
// Clear click
e
.
preventDefault
();
e
.
stopPropagation
();
return
this
.
input
.
val
(
''
)
.
trigger
(
'
input
'
)
.
focus
();
});
// Key events
timeout
=
''
;
this
.
input
.
on
(
'
keydown
'
,
e
=>
{
const
keyCode
=
e
.
which
;
if
(
keyCode
===
13
&&
!
options
.
elIsInput
)
{
e
.
preventDefault
();
}
})
.
on
(
'
input
'
,
()
=>
{
if
(
this
.
input
.
val
()
!==
''
&&
!
$inputContainer
.
hasClass
(
HAS_VALUE_CLASS
))
{
$inputContainer
.
addClass
(
HAS_VALUE_CLASS
);
}
else
if
(
this
.
input
.
val
()
===
''
&&
$inputContainer
.
hasClass
(
HAS_VALUE_CLASS
))
{
$inputContainer
.
removeClass
(
HAS_VALUE_CLASS
);
}
// Only filter asynchronously only if option remote is set
if
(
this
.
options
.
remote
)
{
clearTimeout
(
timeout
);
// eslint-disable-next-line no-return-assign
return
(
timeout
=
setTimeout
(()
=>
{
$inputContainer
.
parent
().
addClass
(
'
is-loading
'
);
return
this
.
options
.
query
(
this
.
input
.
val
(),
data
=>
{
$inputContainer
.
parent
().
removeClass
(
'
is-loading
'
);
return
this
.
options
.
callback
(
data
);
});
},
250
));
}
return
this
.
filter
(
this
.
input
.
val
());
});
}
GitLabDropdownFilter
.
prototype
.
filter
=
function
(
search_text
)
{
static
shouldBlur
(
keyCode
)
{
let
elements
,
group
,
key
,
results
,
tmp
;
return
BLUR_KEYCODES
.
indexOf
(
keyCode
)
!==
-
1
;
if
(
this
.
options
.
onFilter
)
{
}
this
.
options
.
onFilter
(
search_text
);
}
filter
(
searchText
)
{
const
data
=
this
.
options
.
data
();
let
group
,
results
,
tmp
;
if
(
data
!=
null
&&
!
this
.
options
.
filterByText
)
{
if
(
this
.
options
.
onFilter
)
{
results
=
data
;
this
.
options
.
onFilter
(
searchText
);
if
(
search_text
!==
''
)
{
}
// When data is an array of objects therefore [object Array] e.g.
const
data
=
this
.
options
.
data
();
// [
if
(
data
!=
null
&&
!
this
.
options
.
filterByText
)
{
// { prop: 'foo' },
results
=
data
;
// { prop: 'baz' }
if
(
searchText
!==
''
)
{
// ]
// When data is an array of objects therefore [object Array] e.g.
if
(
_
.
isArray
(
data
))
{
// [
results
=
fuzzaldrinPlus
.
filter
(
data
,
search_text
,
{
// { prop: 'foo' },
key
:
this
.
options
.
keys
,
// { prop: 'baz' }
});
// ]
}
else
{
if
(
_
.
isArray
(
data
))
{
results
=
fuzzaldrinPlus
.
filter
(
data
,
searchText
,
{
key
:
this
.
options
.
keys
,
});
}
// If data is grouped therefore an [object Object]. e.g.
// If data is grouped therefore an [object Object]. e.g.
// {
// {
// groupName1: [
// groupName1: [
...
@@ -158,33 +161,32 @@ GitLabDropdownFilter.prototype.filter = function(search_text) {
...
@@ -158,33 +161,32 @@ GitLabDropdownFilter.prototype.filter = function(search_text) {
// { prop: 'def' }
// { prop: 'def' }
// ]
// ]
// }
// }
if
(
isObject
(
data
))
{
else
if
(
isObject
(
data
))
{
results
=
{};
results
=
{};
for
(
key
in
data
)
{
Object
.
keys
(
data
).
forEach
(
key
=>
{
group
=
data
[
key
];
group
=
data
[
key
];
tmp
=
fuzzaldrinPlus
.
filter
(
group
,
search
_t
ext
,
{
tmp
=
fuzzaldrinPlus
.
filter
(
group
,
search
T
ext
,
{
key
:
this
.
options
.
keys
,
key
:
this
.
options
.
keys
,
});
});
if
(
tmp
.
length
)
{
if
(
tmp
.
length
)
{
results
[
key
]
=
tmp
.
map
(
item
=>
item
);
results
[
key
]
=
tmp
.
map
(
item
=>
item
);
}
}
}
}
);
}
}
}
}
return
this
.
options
.
callback
(
results
);
}
}
return
this
.
options
.
callback
(
results
);
const
elements
=
this
.
options
.
elements
();
}
else
{
if
(
searchText
)
{
elements
=
this
.
options
.
elements
();
// eslint-disable-next-line func-names
if
(
search_text
)
{
elements
.
each
(
function
()
{
elements
.
each
(
function
()
{
const
$el
=
$
(
this
);
const
$el
=
$
(
this
);
const
matches
=
fuzzaldrinPlus
.
match
(
$el
.
text
().
trim
(),
search
_t
ext
);
const
matches
=
fuzzaldrinPlus
.
match
(
$el
.
text
().
trim
(),
search
T
ext
);
if
(
!
$el
.
is
(
'
.dropdown-header
'
))
{
if
(
!
$el
.
is
(
'
.dropdown-header
'
))
{
if
(
matches
.
length
)
{
if
(
matches
.
length
)
{
return
$el
.
show
().
removeClass
(
'
option-hidden
'
);
return
$el
.
show
().
removeClass
(
'
option-hidden
'
);
}
else
{
return
$el
.
hide
().
addClass
(
'
option-hidden
'
);
}
}
return
$el
.
hide
().
addClass
(
'
option-hidden
'
);
}
}
});
});
}
else
{
}
else
{
...
@@ -196,235 +198,240 @@ GitLabDropdownFilter.prototype.filter = function(search_text) {
...
@@ -196,235 +198,240 @@ GitLabDropdownFilter.prototype.filter = function(search_text) {
.
find
(
'
.dropdown-menu-empty-item
'
)
.
find
(
'
.dropdown-menu-empty-item
'
)
.
toggleClass
(
'
hidden
'
,
elements
.
is
(
'
:visible
'
));
.
toggleClass
(
'
hidden
'
,
elements
.
is
(
'
:visible
'
));
}
}
};
function
GitLabDropdownRemote
(
dataEndpoint
,
options
)
{
this
.
dataEndpoint
=
dataEndpoint
;
this
.
options
=
options
;
}
}
GitLabDropdownRemote
.
prototype
.
execute
=
function
()
{
class
GitLabDropdownRemote
{
if
(
typeof
this
.
dataEndpoint
===
'
string
'
)
{
constructor
(
dataEndpoint
,
options
)
{
return
this
.
fetchData
();
this
.
dataEndpoint
=
dataEndpoint
;
}
else
if
(
typeof
this
.
dataEndpoint
===
'
function
'
)
{
this
.
options
=
options
;
}
execute
()
{
if
(
typeof
this
.
dataEndpoint
===
'
string
'
)
{
return
this
.
fetchData
();
}
else
if
(
typeof
this
.
dataEndpoint
===
'
function
'
)
{
if
(
this
.
options
.
beforeSend
)
{
this
.
options
.
beforeSend
();
}
return
this
.
dataEndpoint
(
''
,
data
=>
{
// Fetch the data by calling the data function
if
(
this
.
options
.
success
)
{
this
.
options
.
success
(
data
);
}
if
(
this
.
options
.
beforeSend
)
{
return
this
.
options
.
beforeSend
();
}
});
}
}
fetchData
()
{
if
(
this
.
options
.
beforeSend
)
{
if
(
this
.
options
.
beforeSend
)
{
this
.
options
.
beforeSend
();
this
.
options
.
beforeSend
();
}
}
return
this
.
dataEndpoint
(
''
,
data
=>
{
// Fetch the data by calling the data function
// Fetch the data through ajax if the data is a string
return
axios
.
get
(
this
.
dataEndpoint
).
then
(({
data
})
=>
{
if
(
this
.
options
.
success
)
{
if
(
this
.
options
.
success
)
{
this
.
options
.
success
(
data
);
return
this
.
options
.
success
(
data
);
}
if
(
this
.
options
.
beforeSend
)
{
return
this
.
options
.
beforeSend
();
}
}
});
});
}
}
};
}
GitLabDropdownRemote
.
prototype
.
fetchData
=
function
()
{
if
(
this
.
options
.
beforeSend
)
{
this
.
options
.
beforeSend
();
}
// Fetch the data through ajax if the data is a string
class
GitLabDropdown
{
return
axios
.
get
(
this
.
dataEndpoint
).
then
(({
data
})
=>
{
constructor
(
el1
,
options
)
{
if
(
this
.
options
.
success
)
{
let
selector
,
self
;
return
this
.
options
.
success
(
data
);
this
.
el
=
el1
;
this
.
options
=
options
;
this
.
updateLabel
=
this
.
updateLabel
.
bind
(
this
);
this
.
hidden
=
this
.
hidden
.
bind
(
this
);
this
.
opened
=
this
.
opened
.
bind
(
this
);
this
.
shouldPropagate
=
this
.
shouldPropagate
.
bind
(
this
);
self
=
this
;
selector
=
$
(
this
.
el
).
data
(
'
target
'
);
this
.
dropdown
=
selector
!=
null
?
$
(
selector
)
:
$
(
this
.
el
).
parent
();
// Set Defaults
this
.
filterInput
=
this
.
options
.
filterInput
||
this
.
getElement
(
FILTER_INPUT
);
this
.
noFilterInput
=
this
.
options
.
noFilterInput
||
this
.
getElement
(
NO_FILTER_INPUT
);
this
.
highlight
=
Boolean
(
this
.
options
.
highlight
);
this
.
icon
=
Boolean
(
this
.
options
.
icon
);
this
.
filterInputBlur
=
this
.
options
.
filterInputBlur
!=
null
?
this
.
options
.
filterInputBlur
:
true
;
// If no input is passed create a default one
self
=
this
;
// If selector was passed
if
(
_
.
isString
(
this
.
filterInput
))
{
this
.
filterInput
=
this
.
getElement
(
this
.
filterInput
);
}
}
});
const
searchFields
=
this
.
options
.
search
?
this
.
options
.
search
.
fields
:
[];
};
if
(
this
.
options
.
data
)
{
// If we provided data
function
GitLabDropdown
(
el1
,
options
)
{
// data could be an array of objects or a group of arrays
let
selector
,
self
;
if
(
_
.
isObject
(
this
.
options
.
data
)
&&
!
_
.
isFunction
(
this
.
options
.
data
))
{
this
.
el
=
el1
;
this
.
fullData
=
this
.
options
.
data
;
this
.
options
=
options
;
currentIndex
=
-
1
;
this
.
updateLabel
=
this
.
updateLabel
.
bind
(
this
);
this
.
parseData
(
this
.
options
.
data
);
this
.
hidden
=
this
.
hidden
.
bind
(
this
);
this
.
focusTextInput
();
this
.
opened
=
this
.
opened
.
bind
(
this
);
}
else
{
this
.
shouldPropagate
=
this
.
shouldPropagate
.
bind
(
this
);
this
.
remote
=
new
GitLabDropdownRemote
(
this
.
options
.
data
,
{
self
=
this
;
dataType
:
this
.
options
.
dataType
,
selector
=
$
(
this
.
el
).
data
(
'
target
'
);
beforeSend
:
this
.
toggleLoading
.
bind
(
this
),
this
.
dropdown
=
selector
!=
null
?
$
(
selector
)
:
$
(
this
.
el
).
parent
();
success
:
data
=>
{
// Set Defaults
this
.
fullData
=
data
;
this
.
filterInput
=
this
.
options
.
filterInput
||
this
.
getElement
(
FILTER_INPUT
);
this
.
parseData
(
this
.
fullData
);
this
.
noFilterInput
=
this
.
options
.
noFilterInput
||
this
.
getElement
(
NO_FILTER_INPUT
);
this
.
focusTextInput
();
this
.
highlight
=
Boolean
(
this
.
options
.
highlight
);
this
.
icon
=
Boolean
(
this
.
options
.
icon
);
// Update dropdown position since remote data may have changed dropdown size
this
.
filterInputBlur
=
this
.
options
.
filterInputBlur
!=
null
?
this
.
options
.
filterInputBlur
:
true
;
this
.
dropdown
.
find
(
'
.dropdown-menu-toggle
'
).
dropdown
(
'
update
'
);
// If no input is passed create a default one
self
=
this
;
if
(
// If selector was passed
this
.
options
.
filterable
&&
if
(
_
.
isString
(
this
.
filterInput
))
{
this
.
filter
&&
this
.
filterInput
=
this
.
getElement
(
this
.
filterInput
);
this
.
filter
.
input
&&
}
this
.
filter
.
input
.
val
()
&&
const
searchFields
=
this
.
options
.
search
?
this
.
options
.
search
.
fields
:
[];
this
.
filter
.
input
.
val
().
trim
()
!==
''
if
(
this
.
options
.
data
)
{
)
{
// If we provided data
return
this
.
filter
.
input
.
trigger
(
'
input
'
);
// data could be an array of objects or a group of arrays
}
if
(
_
.
isObject
(
this
.
options
.
data
)
&&
!
_
.
isFunction
(
this
.
options
.
data
))
{
},
this
.
fullData
=
this
.
options
.
data
;
instance
:
this
,
currentIndex
=
-
1
;
});
this
.
parseData
(
this
.
options
.
data
);
}
this
.
focusTextInput
();
}
else
{
this
.
remote
=
new
GitLabDropdownRemote
(
this
.
options
.
data
,
{
dataType
:
this
.
options
.
dataType
,
beforeSend
:
this
.
toggleLoading
.
bind
(
this
),
success
:
data
=>
{
this
.
fullData
=
data
;
this
.
parseData
(
this
.
fullData
);
this
.
focusTextInput
();
// Update dropdown position since remote data may have changed dropdown size
this
.
dropdown
.
find
(
'
.dropdown-menu-toggle
'
).
dropdown
(
'
update
'
);
if
(
this
.
options
.
filterable
&&
this
.
filter
&&
this
.
filter
.
input
&&
this
.
filter
.
input
.
val
()
&&
this
.
filter
.
input
.
val
().
trim
()
!==
''
)
{
return
this
.
filter
.
input
.
trigger
(
'
input
'
);
}
},
instance
:
this
,
});
}
}
}
if
(
this
.
noFilterInput
.
length
)
{
if
(
this
.
noFilterInput
.
length
)
{
this
.
plainInput
=
new
GitLabDropdownInput
(
this
.
noFilterInput
,
this
.
options
);
this
.
plainInput
=
new
GitLabDropdownInput
(
this
.
noFilterInput
,
this
.
options
);
this
.
plainInput
.
onInput
(
this
.
addInput
.
bind
(
this
));
this
.
plainInput
.
onInput
(
this
.
addInput
.
bind
(
this
));
}
}
// Init filterable
// Init filterable
if
(
this
.
options
.
filterable
)
{
if
(
this
.
options
.
filterable
)
{
this
.
filter
=
new
GitLabDropdownFilter
(
this
.
filterInput
,
{
this
.
filter
=
new
GitLabDropdownFilter
(
this
.
filterInput
,
{
elIsInput
:
$
(
this
.
el
).
is
(
'
input
'
),
elIsInput
:
$
(
this
.
el
).
is
(
'
input
'
),
filterInputBlur
:
this
.
filterInputBlur
,
filterInputBlur
:
this
.
filterInputBlur
,
filterByText
:
this
.
options
.
filterByText
,
filterByText
:
this
.
options
.
filterByText
,
onFilter
:
this
.
options
.
onFilter
,
onFilter
:
this
.
options
.
onFilter
,
remote
:
this
.
options
.
filterRemote
,
remote
:
this
.
options
.
filterRemote
,
query
:
this
.
options
.
data
,
query
:
this
.
options
.
data
,
keys
:
searchFields
,
keys
:
searchFields
,
instance
:
this
,
instance
:
this
,
elements
:
()
=>
{
elements
:
()
=>
{
selector
=
`.dropdown-content li:not(
${
NON_SELECTABLE_CLASSES
}
)`
;
selector
=
`.dropdown-content li:not(
${
NON_SELECTABLE_CLASSES
}
)`
;
if
(
this
.
dropdown
.
find
(
'
.dropdown-toggle-page
'
).
length
)
{
selector
=
`.dropdown-page-one
${
selector
}
`
;
}
return
$
(
selector
,
this
.
dropdown
);
},
data
:
()
=>
this
.
fullData
,
callback
:
data
=>
{
this
.
parseData
(
data
);
if
(
this
.
filterInput
.
val
()
!==
''
)
{
selector
=
SELECTABLE_CLASSES
;
if
(
this
.
dropdown
.
find
(
'
.dropdown-toggle-page
'
).
length
)
{
if
(
this
.
dropdown
.
find
(
'
.dropdown-toggle-page
'
).
length
)
{
selector
=
`.dropdown-page-one
${
selector
}
`
;
selector
=
`.dropdown-page-one
${
selector
}
`
;
}
}
if
(
$
(
this
.
el
).
is
(
'
input
'
))
{
return
$
(
selector
,
this
.
dropdown
);
currentIndex
=
-
1
;
},
}
else
{
data
:
()
=>
this
.
fullData
,
$
(
selector
,
this
.
dropdown
)
callback
:
data
=>
{
.
first
()
this
.
parseData
(
data
);
.
find
(
'
a
'
)
if
(
this
.
filterInput
.
val
()
!==
''
)
{
.
addClass
(
'
is-focused
'
);
selector
=
SELECTABLE_CLASSES
;
currentIndex
=
0
;
if
(
this
.
dropdown
.
find
(
'
.dropdown-toggle-page
'
).
length
)
{
selector
=
`.dropdown-page-one
${
selector
}
`
;
}
if
(
$
(
this
.
el
).
is
(
'
input
'
))
{
currentIndex
=
-
1
;
}
else
{
$
(
selector
,
this
.
dropdown
)
.
first
()
.
find
(
'
a
'
)
.
addClass
(
'
is-focused
'
);
currentIndex
=
0
;
}
}
}
}
},
},
});
});
}
// Event listeners
this
.
dropdown
.
on
(
'
shown.bs.dropdown
'
,
this
.
opened
);
this
.
dropdown
.
on
(
'
hidden.bs.dropdown
'
,
this
.
hidden
);
$
(
this
.
el
).
on
(
'
update.label
'
,
this
.
updateLabel
);
this
.
dropdown
.
on
(
'
click
'
,
'
.dropdown-menu, .dropdown-menu-close
'
,
this
.
shouldPropagate
);
this
.
dropdown
.
on
(
'
keyup
'
,
e
=>
{
// Escape key
if
(
e
.
which
===
27
)
{
return
$
(
'
.dropdown-menu-close
'
,
this
.
dropdown
).
trigger
(
'
click
'
);
}
}
});
// Event listeners
this
.
dropdown
.
on
(
'
blur
'
,
'
a
'
,
e
=>
{
this
.
dropdown
.
on
(
'
shown.bs.dropdown
'
,
this
.
opened
);
let
$dropdownMenu
,
$relatedTarget
;
this
.
dropdown
.
on
(
'
hidden.bs.dropdown
'
,
this
.
hidden
);
if
(
e
.
relatedTarget
!=
null
)
{
$
(
this
.
el
).
on
(
'
update.label
'
,
this
.
updateLabel
);
$relatedTarget
=
$
(
e
.
relatedTarget
);
this
.
dropdown
.
on
(
'
click
'
,
'
.dropdown-menu, .dropdown-menu-close
'
,
this
.
shouldPropagate
);
$dropdownMenu
=
$relatedTarget
.
closest
(
'
.dropdown-menu
'
);
this
.
dropdown
.
on
(
'
keyup
'
,
e
=>
{
if
(
$dropdownMenu
.
length
===
0
)
{
// Escape key
return
this
.
dropdown
.
removeClass
(
'
show
'
);
if
(
e
.
which
===
27
)
{
return
$
(
'
.dropdown-menu-close
'
,
this
.
dropdown
).
trigger
(
'
click
'
);
}
});
this
.
dropdown
.
on
(
'
blur
'
,
'
a
'
,
e
=>
{
let
$dropdownMenu
,
$relatedTarget
;
if
(
e
.
relatedTarget
!=
null
)
{
$relatedTarget
=
$
(
e
.
relatedTarget
);
$dropdownMenu
=
$relatedTarget
.
closest
(
'
.dropdown-menu
'
);
if
(
$dropdownMenu
.
length
===
0
)
{
return
this
.
dropdown
.
removeClass
(
'
show
'
);
}
}
}
}
});
if
(
this
.
dropdown
.
find
(
'
.dropdown-toggle-page
'
).
length
)
{
this
.
dropdown
.
find
(
'
.dropdown-toggle-page, .dropdown-menu-back
'
).
on
(
'
click
'
,
e
=>
{
e
.
preventDefault
();
e
.
stopPropagation
();
return
this
.
togglePage
();
});
});
}
if
(
this
.
options
.
selectable
)
{
selector
=
'
.dropdown-content a
'
;
if
(
this
.
dropdown
.
find
(
'
.dropdown-toggle-page
'
).
length
)
{
if
(
this
.
dropdown
.
find
(
'
.dropdown-toggle-page
'
).
length
)
{
selector
=
'
.dropdown-page-one .dropdown-content a
'
;
this
.
dropdown
.
find
(
'
.dropdown-toggle-page, .dropdown-menu-back
'
).
on
(
'
click
'
,
e
=>
{
}
e
.
preventDefault
();
this
.
dropdown
.
on
(
'
click
'
,
selector
,
e
=>
{
e
.
stopPropagation
();
const
$el
=
$
(
e
.
currentTarget
);
return
this
.
togglePage
();
const
selected
=
self
.
rowClicked
(
$el
);
});
const
selectedObj
=
selected
?
selected
[
0
]
:
null
;
}
const
isMarking
=
selected
?
selected
[
1
]
:
null
;
if
(
this
.
options
.
selectable
)
{
if
(
this
.
options
.
clicked
)
{
selector
=
'
.dropdown-content a
'
;
this
.
options
.
clicked
.
call
(
this
,
{
if
(
this
.
dropdown
.
find
(
'
.dropdown-toggle-page
'
).
length
)
{
selectedObj
,
selector
=
'
.dropdown-page-one .dropdown-content a
'
;
$el
,
e
,
isMarking
,
});
}
}
this
.
dropdown
.
on
(
'
click
'
,
selector
,
e
=>
{
const
$el
=
$
(
e
.
currentTarget
);
const
selected
=
self
.
rowClicked
(
$el
);
const
selectedObj
=
selected
?
selected
[
0
]
:
null
;
const
isMarking
=
selected
?
selected
[
1
]
:
null
;
if
(
this
.
options
.
clicked
)
{
this
.
options
.
clicked
.
call
(
this
,
{
selectedObj
,
$el
,
e
,
isMarking
,
});
}
// Update label right after all modifications in dropdown has been done
// Update label right after all modifications in dropdown has been done
if
(
this
.
options
.
toggleLabel
)
{
if
(
this
.
options
.
toggleLabel
)
{
this
.
updateLabel
(
selectedObj
,
$el
,
this
);
this
.
updateLabel
(
selectedObj
,
$el
,
this
);
}
}
$el
.
trigger
(
'
blur
'
);
$el
.
trigger
(
'
blur
'
);
});
});
}
}
}
}
// Finds an element inside wrapper element
// Finds an element inside wrapper element
GitLabDropdown
.
prototype
.
getElement
=
function
(
selector
)
{
getElement
(
selector
)
{
return
this
.
dropdown
.
find
(
selector
);
return
this
.
dropdown
.
find
(
selector
);
};
}
GitLabDropdown
.
prototype
.
toggleLoading
=
function
()
{
toggleLoading
()
{
return
$
(
'
.dropdown-menu
'
,
this
.
dropdown
).
toggleClass
(
LOADING_CLASS
);
return
$
(
'
.dropdown-menu
'
,
this
.
dropdown
).
toggleClass
(
LOADING_CLASS
);
};
}
GitLabDropdown
.
prototype
.
togglePage
=
function
()
{
togglePage
()
{
const
menu
=
$
(
'
.dropdown-menu
'
,
this
.
dropdown
);
const
menu
=
$
(
'
.dropdown-menu
'
,
this
.
dropdown
);
if
(
menu
.
hasClass
(
PAGE_TWO_CLASS
))
{
if
(
menu
.
hasClass
(
PAGE_TWO_CLASS
))
{
if
(
this
.
remote
)
{
if
(
this
.
remote
)
{
this
.
remote
.
execute
();
this
.
remote
.
execute
();
}
}
}
menu
.
toggleClass
(
PAGE_TWO_CLASS
);
// Focus first visible input on active page
return
this
.
dropdown
.
find
(
'
[class^="dropdown-page-"]:visible :text:visible:first
'
).
focus
();
}
}
menu
.
toggleClass
(
PAGE_TWO_CLASS
);
// Focus first visible input on active page
return
this
.
dropdown
.
find
(
'
[class^="dropdown-page-"]:visible :text:visible:first
'
).
focus
();
};
GitLabDropdown
.
prototype
.
parseData
=
function
(
data
)
{
parseData
(
data
)
{
let
groupData
,
html
,
name
;
let
groupData
,
html
;
this
.
renderedData
=
data
;
this
.
renderedData
=
data
;
if
(
this
.
options
.
filterable
&&
data
.
length
===
0
)
{
if
(
this
.
options
.
filterable
&&
data
.
length
===
0
)
{
// render no matching results
// render no matching results
html
=
[
this
.
noResults
()];
html
=
[
this
.
noResults
()];
}
else
{
}
// Handle array groups
// Handle array groups
if
(
isObject
(
data
))
{
else
if
(
isObject
(
data
))
{
html
=
[];
html
=
[];
for
(
name
in
data
)
{
Object
.
keys
(
data
).
forEach
(
name
=>
{
groupData
=
data
[
name
];
groupData
=
data
[
name
];
html
.
push
(
html
.
push
(
this
.
renderItem
(
this
.
renderItem
(
...
@@ -436,461 +443,455 @@ GitLabDropdown.prototype.parseData = function(data) {
...
@@ -436,461 +443,455 @@ GitLabDropdown.prototype.parseData = function(data) {
),
),
);
);
this
.
renderData
(
groupData
,
name
).
map
(
item
=>
html
.
push
(
item
));
this
.
renderData
(
groupData
,
name
).
map
(
item
=>
html
.
push
(
item
));
}
}
);
}
else
{
}
else
{
// Render each row
// Render each row
html
=
this
.
renderData
(
data
);
html
=
this
.
renderData
(
data
);
}
}
}
// Render the full menu
// Render the full menu
const
fullHtml
=
this
.
renderMenu
(
html
);
const
full_html
=
this
.
renderMenu
(
h
tml
);
return
this
.
appendMenu
(
fullH
tml
);
return
this
.
appendMenu
(
full_html
);
}
};
renderData
(
data
,
group
)
{
GitLabDropdown
.
prototype
.
renderData
=
function
(
data
,
group
)
{
return
data
.
map
((
obj
,
index
)
=>
this
.
renderItem
(
obj
,
group
||
false
,
index
));
return
data
.
map
((
obj
,
index
)
=>
this
.
renderItem
(
obj
,
group
||
false
,
index
));
}
};
shouldPropagate
(
e
)
{
GitLabDropdown
.
prototype
.
shouldPropagate
=
function
(
e
)
{
let
$target
;
let
$target
;
if
(
this
.
options
.
multiSelect
||
this
.
options
.
shouldPropagate
===
false
)
{
if
(
this
.
options
.
multiSelect
||
this
.
options
.
shouldPropagate
===
false
)
{
$target
=
$
(
e
.
target
);
$target
=
$
(
e
.
target
);
if
(
if
(
$target
&&
$target
&&
!
$target
.
hasClass
(
'
dropdown-menu-close
'
)
&&
!
$target
.
hasClass
(
'
dropdown-menu-close
'
)
&&
!
$target
.
hasClass
(
'
dropdown-menu-close-icon
'
)
&&
!
$target
.
hasClass
(
'
dropdown-menu-close-icon
'
)
&&
!
$target
.
data
(
'
isLink
'
)
!
$target
.
data
(
'
isLink
'
)
)
{
)
{
e
.
stopPropagation
();
e
.
stopPropagation
();
// This prevents automatic scrolling to the top
// This prevents automatic scrolling to the top
if
(
$target
.
closest
(
'
a
'
).
length
)
{
if
(
$target
.
closest
(
'
a
'
).
length
)
{
return
false
;
return
false
;
}
}
}
}
return
true
;
return
true
;
}
}
}
};
GitLabDropdown
.
prototype
.
filteredFullData
=
function
()
{
return
this
.
fullData
.
filter
(
r
=>
typeof
r
===
'
object
'
&&
!
Object
.
prototype
.
hasOwnProperty
.
call
(
r
,
'
beforeDivider
'
)
&&
!
Object
.
prototype
.
hasOwnProperty
.
call
(
r
,
'
header
'
),
);
};
GitLabDropdown
.
prototype
.
opened
=
function
(
e
)
{
filteredFullData
()
{
this
.
resetRows
();
return
this
.
fullData
.
filter
(
this
.
addArrowKeyEvent
();
r
=>
typeof
r
===
'
object
'
&&
const
dropdownToggle
=
this
.
dropdown
.
find
(
'
.dropdown-menu-toggle
'
);
!
Object
.
prototype
.
hasOwnProperty
.
call
(
r
,
'
beforeDivider
'
)
&&
const
hasFilterBulkUpdate
=
dropdownToggle
.
hasClass
(
'
js-filter-bulk-update
'
);
!
Object
.
prototype
.
hasOwnProperty
.
call
(
r
,
'
header
'
),
const
shouldRefreshOnOpen
=
dropdownToggle
.
hasClass
(
'
js-gl-dropdown-refresh-on-open
'
);
const
hasMultiSelect
=
dropdownToggle
.
hasClass
(
'
js-multiselect
'
);
// Makes indeterminate items effective
if
(
this
.
fullData
&&
(
shouldRefreshOnOpen
||
hasFilterBulkUpdate
))
{
this
.
parseData
(
this
.
fullData
);
}
// Process the data to make sure rendered data
// matches the correct layout
const
inputValue
=
this
.
filterInput
.
val
();
if
(
this
.
fullData
&&
hasMultiSelect
&&
this
.
options
.
processData
&&
inputValue
.
length
===
0
)
{
this
.
options
.
processData
.
call
(
this
.
options
,
inputValue
,
this
.
filteredFullData
(),
this
.
parseData
.
bind
(
this
),
);
);
}
}
const
contentHtml
=
$
(
'
.dropdown-content
'
,
this
.
dropdown
).
html
();
opened
(
e
)
{
if
(
this
.
remote
&&
contentHtml
===
''
)
{
this
.
resetRows
();
this
.
remote
.
execute
();
this
.
addArrowKeyEvent
();
}
else
{
this
.
focusTextInput
();
}
if
(
this
.
options
.
showMenuAbove
)
{
const
dropdownToggle
=
this
.
dropdown
.
find
(
'
.dropdown-menu-toggle
'
);
this
.
positionMenuAbove
();
const
hasFilterBulkUpdate
=
dropdownToggle
.
hasClass
(
'
js-filter-bulk-update
'
);
}
const
shouldRefreshOnOpen
=
dropdownToggle
.
hasClass
(
'
js-gl-dropdown-refresh-on-open
'
);
const
hasMultiSelect
=
dropdownToggle
.
hasClass
(
'
js-multiselect
'
);
if
(
this
.
options
.
opened
)
{
// Makes indeterminate items effective
if
(
this
.
options
.
preserveContext
)
{
if
(
this
.
fullData
&&
(
shouldRefreshOnOpen
||
hasFilterBulkUpdate
))
{
this
.
options
.
opened
(
e
);
this
.
parseData
(
this
.
fullData
);
}
else
{
this
.
options
.
opened
.
call
(
this
,
e
);
}
}
}
return
this
.
dropdown
.
trigger
(
'
shown.gl.dropdown
'
);
// Process the data to make sure rendered data
};
// matches the correct layout
const
inputValue
=
this
.
filterInput
.
val
();
if
(
this
.
fullData
&&
hasMultiSelect
&&
this
.
options
.
processData
&&
inputValue
.
length
===
0
)
{
this
.
options
.
processData
.
call
(
this
.
options
,
inputValue
,
this
.
filteredFullData
(),
this
.
parseData
.
bind
(
this
),
);
}
GitLabDropdown
.
prototype
.
positionMenuAbove
=
function
()
{
const
contentHtml
=
$
(
'
.dropdown-content
'
,
this
.
dropdown
).
html
();
const
$menu
=
this
.
dropdown
.
find
(
'
.dropdown-menu
'
);
if
(
this
.
remote
&&
contentHtml
===
''
)
{
this
.
remote
.
execute
();
}
else
{
this
.
focusTextInput
();
}
$menu
.
addClass
(
'
dropdown-open-top
'
);
if
(
this
.
options
.
showMenuAbove
)
{
$menu
.
css
(
'
top
'
,
'
initial
'
);
this
.
positionMenuAbove
();
$menu
.
css
(
'
bottom
'
,
'
100%
'
);
}
};
if
(
this
.
options
.
opened
)
{
if
(
this
.
options
.
preserveContext
)
{
this
.
options
.
opened
(
e
);
}
else
{
this
.
options
.
opened
.
call
(
this
,
e
);
}
}
GitLabDropdown
.
prototype
.
hidden
=
function
(
e
)
{
return
this
.
dropdown
.
trigger
(
'
shown.gl.dropdown
'
);
this
.
resetRows
();
this
.
removeArrowKeyEvent
();
const
$input
=
this
.
dropdown
.
find
(
'
.dropdown-input-field
'
);
if
(
this
.
options
.
filterable
)
{
$input
.
blur
();
}
}
if
(
this
.
dropdown
.
find
(
'
.dropdown-toggle-page
'
).
length
)
{
$
(
'
.dropdown-menu
'
,
this
.
dropdown
).
removeClass
(
PAGE_TWO_CLASS
);
positionMenuAbove
()
{
const
$menu
=
this
.
dropdown
.
find
(
'
.dropdown-menu
'
);
$menu
.
addClass
(
'
dropdown-open-top
'
);
$menu
.
css
(
'
top
'
,
'
initial
'
);
$menu
.
css
(
'
bottom
'
,
'
100%
'
);
}
}
if
(
this
.
options
.
hidden
)
{
this
.
options
.
hidden
.
call
(
this
,
e
);
hidden
(
e
)
{
this
.
resetRows
();
this
.
removeArrowKeyEvent
();
const
$input
=
this
.
dropdown
.
find
(
'
.dropdown-input-field
'
);
if
(
this
.
options
.
filterable
)
{
$input
.
blur
();
}
if
(
this
.
dropdown
.
find
(
'
.dropdown-toggle-page
'
).
length
)
{
$
(
'
.dropdown-menu
'
,
this
.
dropdown
).
removeClass
(
PAGE_TWO_CLASS
);
}
if
(
this
.
options
.
hidden
)
{
this
.
options
.
hidden
.
call
(
this
,
e
);
}
return
this
.
dropdown
.
trigger
(
'
hidden.gl.dropdown
'
);
}
}
return
this
.
dropdown
.
trigger
(
'
hidden.gl.dropdown
'
);
};
// Render the full menu
// Render the full menu
GitLabDropdown
.
prototype
.
renderMenu
=
function
(
html
)
{
renderMenu
(
html
)
{
if
(
this
.
options
.
renderMenu
)
{
if
(
this
.
options
.
renderMenu
)
{
return
this
.
options
.
renderMenu
(
html
);
return
this
.
options
.
renderMenu
(
html
);
}
else
{
}
return
$
(
'
<ul>
'
).
append
(
html
);
return
$
(
'
<ul>
'
).
append
(
html
);
}
}
};
// Append the menu into the dropdown
// Append the menu into the dropdown
GitLabDropdown
.
prototype
.
appendMenu
=
function
(
html
)
{
appendMenu
(
html
)
{
return
this
.
clearMenu
().
append
(
html
);
return
this
.
clearMenu
().
append
(
html
);
};
}
GitLabDropdown
.
prototype
.
clearMenu
=
function
()
{
clearMenu
()
{
let
selector
;
let
selector
=
'
.dropdown-content
'
;
selector
=
'
.dropdown-content
'
;
if
(
this
.
dropdown
.
find
(
'
.dropdown-toggle-page
'
).
length
)
{
if
(
this
.
dropdown
.
find
(
'
.dropdown-toggle-page
'
).
length
)
{
if
(
this
.
options
.
containerSelector
)
{
if
(
this
.
options
.
containerSelector
)
{
selector
=
this
.
options
.
containerSelector
;
selector
=
this
.
options
.
containerSelector
;
}
else
{
}
else
{
selector
=
'
.dropdown-page-one .dropdown-content
'
;
selector
=
'
.dropdown-page-one .dropdown-content
'
;
}
}
}
return
$
(
selector
,
this
.
dropdown
).
empty
();
}
}
re
turn
$
(
selector
,
this
.
dropdown
).
empty
();
re
nderItem
(
data
,
group
,
index
)
{
}
;
let
parent
;
GitLabDropdown
.
prototype
.
renderItem
=
function
(
data
,
group
,
index
)
{
if
(
this
.
dropdown
&&
this
.
dropdown
[
0
])
{
let
parent
;
parent
=
this
.
dropdown
[
0
].
parentNode
;
}
if
(
this
.
dropdown
&&
this
.
dropdown
[
0
])
{
parent
=
this
.
dropdown
[
0
].
parentNode
;
}
return
renderItem
({
instance
:
this
,
options
:
Object
.
assign
({},
this
.
options
,
{
icon
:
this
.
icon
,
highlight
:
this
.
highlight
,
highlightText
:
text
=>
this
.
highlightTextMatches
(
text
,
this
.
filterInput
.
val
()),
highlightTemplate
:
this
.
highlightTemplate
.
bind
(
this
),
parent
,
}),
data
,
group
,
index
,
});
};
GitLabDropdown
.
prototype
.
highlightTemplate
=
function
(
text
,
template
)
{
return
renderItem
({
return
`"<b>
${
_
.
escape
(
text
)}
</b>"
${
template
}
`
;
instance
:
this
,
};
options
:
Object
.
assign
({},
this
.
options
,
{
icon
:
this
.
icon
,
highlight
:
this
.
highlight
,
highlightText
:
text
=>
this
.
highlightTextMatches
(
text
,
this
.
filterInput
.
val
()),
highlightTemplate
:
this
.
highlightTemplate
.
bind
(
this
),
parent
,
}),
data
,
group
,
index
,
});
}
GitLabDropdown
.
prototype
.
highlightTextMatches
=
function
(
text
,
term
)
{
// eslint-disable-next-line class-methods-use-this
const
occurrences
=
fuzzaldrinPlus
.
match
(
text
,
term
);
highlightTemplate
(
text
,
template
)
{
const
{
indexOf
}
=
[];
return
`"<b>
${
_
.
escape
(
text
)}
</b>"
${
template
}
`
;
}
return
text
// eslint-disable-next-line class-methods-use-this
.
split
(
''
)
highlightTextMatches
(
text
,
term
)
{
.
map
((
character
,
i
)
=>
{
const
occurrences
=
fuzzaldrinPlus
.
match
(
text
,
term
);
if
(
indexOf
.
call
(
occurrences
,
i
)
!==
-
1
)
{
const
{
indexOf
}
=
[];
return
`<b>
${
character
}
</b>`
;
}
else
{
return
text
.
split
(
''
)
.
map
((
character
,
i
)
=>
{
if
(
indexOf
.
call
(
occurrences
,
i
)
!==
-
1
)
{
return
`<b>
${
character
}
</b>`
;
}
return
character
;
return
character
;
})
.
join
(
''
);
}
// eslint-disable-next-line class-methods-use-this
noResults
()
{
return
'
<li class="dropdown-menu-empty-item"><a>No matching results</a></li>
'
;
}
rowClicked
(
el
)
{
let
field
,
groupName
,
selectedIndex
,
selectedObject
,
isMarking
;
const
{
fieldName
}
=
this
.
options
;
const
isInput
=
$
(
this
.
el
).
is
(
'
input
'
);
if
(
this
.
renderedData
)
{
groupName
=
el
.
data
(
'
group
'
);
if
(
groupName
)
{
selectedIndex
=
el
.
data
(
'
index
'
);
selectedObject
=
this
.
renderedData
[
groupName
][
selectedIndex
];
}
else
{
selectedIndex
=
el
.
closest
(
'
li
'
).
index
();
this
.
selectedIndex
=
selectedIndex
;
selectedObject
=
this
.
renderedData
[
selectedIndex
];
}
}
})
}
.
join
(
''
);
};
GitLabDropdown
.
prototype
.
noResults
=
function
()
{
if
(
this
.
options
.
vue
)
{
return
'
<li class="dropdown-menu-empty-item"><a>No matching results</a></li>
'
;
if
(
el
.
hasClass
(
ACTIVE_CLASS
))
{
};
el
.
removeClass
(
ACTIVE_CLASS
);
}
else
{
el
.
addClass
(
ACTIVE_CLASS
);
}
GitLabDropdown
.
prototype
.
rowClicked
=
function
(
el
)
{
return
[
selectedObject
];
let
field
,
groupName
,
selectedIndex
,
selectedObject
,
isMarking
;
const
{
fieldName
}
=
this
.
options
;
const
isInput
=
$
(
this
.
el
).
is
(
'
input
'
);
if
(
this
.
renderedData
)
{
groupName
=
el
.
data
(
'
group
'
);
if
(
groupName
)
{
selectedIndex
=
el
.
data
(
'
index
'
);
selectedObject
=
this
.
renderedData
[
groupName
][
selectedIndex
];
}
else
{
selectedIndex
=
el
.
closest
(
'
li
'
).
index
();
this
.
selectedIndex
=
selectedIndex
;
selectedObject
=
this
.
renderedData
[
selectedIndex
];
}
}
}
if
(
this
.
options
.
vue
)
{
field
=
[];
if
(
el
.
hasClass
(
ACTIVE_CLASS
))
{
const
value
=
this
.
options
.
id
?
this
.
options
.
id
(
selectedObject
,
el
)
:
selectedObject
.
id
;
el
.
removeClass
(
ACTIVE_CLASS
);
if
(
isInput
)
{
}
else
{
field
=
$
(
this
.
el
);
el
.
addClass
(
ACTIVE_CLASS
);
}
else
if
(
value
!=
null
)
{
field
=
this
.
dropdown
.
parent
()
.
find
(
`input[name='
${
fieldName
}
'][value='
${
value
.
toString
().
replace
(
/'/g
,
"
\\
'
"
)}
']`
);
}
}
return
[
selectedObject
];
if
(
this
.
options
.
isSelectable
&&
!
this
.
options
.
isSelectable
(
selectedObject
,
el
))
{
}
return
[
selectedObject
];
}
field
=
[];
if
(
el
.
hasClass
(
ACTIVE_CLASS
)
&&
value
!==
0
)
{
const
value
=
this
.
options
.
id
?
this
.
options
.
id
(
selectedObject
,
el
)
:
selectedObject
.
id
;
isMarking
=
false
;
if
(
isInput
)
{
el
.
removeClass
(
ACTIVE_CLASS
);
field
=
$
(
this
.
el
);
if
(
field
&&
field
.
length
)
{
}
else
if
(
value
!=
null
)
{
this
.
clearField
(
field
,
isInput
);
field
=
this
.
dropdown
}
.
parent
()
}
else
if
(
el
.
hasClass
(
INDETERMINATE_CLASS
))
{
.
find
(
`input[name='
${
fieldName
}
'][value='
${
value
.
toString
().
replace
(
/'/g
,
"
\\
'
"
)}
']`
);
isMarking
=
true
;
}
el
.
addClass
(
ACTIVE_CLASS
);
el
.
removeClass
(
INDETERMINATE_CLASS
);
if
(
this
.
options
.
isSelectable
&&
!
this
.
options
.
isSelectable
(
selectedObject
,
el
))
{
if
(
field
&&
field
.
length
&&
value
==
null
)
{
return
[
selectedObject
];
this
.
clearField
(
field
,
isInput
);
}
if
(
el
.
hasClass
(
ACTIVE_CLASS
)
&&
value
!==
0
)
{
isMarking
=
false
;
el
.
removeClass
(
ACTIVE_CLASS
);
if
(
field
&&
field
.
length
)
{
this
.
clearField
(
field
,
isInput
);
}
}
else
if
(
el
.
hasClass
(
INDETERMINATE_CLASS
))
{
isMarking
=
true
;
el
.
addClass
(
ACTIVE_CLASS
);
el
.
removeClass
(
INDETERMINATE_CLASS
);
if
(
field
&&
field
.
length
&&
value
==
null
)
{
this
.
clearField
(
field
,
isInput
);
}
if
((
!
field
||
!
field
.
length
)
&&
fieldName
)
{
this
.
addInput
(
fieldName
,
value
,
selectedObject
);
}
}
else
{
isMarking
=
true
;
if
(
!
this
.
options
.
multiSelect
||
el
.
hasClass
(
'
dropdown-clear-active
'
))
{
this
.
dropdown
.
find
(
`.
${
ACTIVE_CLASS
}
`
).
removeClass
(
ACTIVE_CLASS
);
if
(
!
isInput
)
{
this
.
dropdown
.
parent
()
.
find
(
`input[name='
${
fieldName
}
']`
)
.
remove
();
}
}
}
if
(
field
&&
field
.
length
&&
value
==
null
)
{
this
.
clearField
(
field
,
isInput
);
}
// Toggle active class for the tick mark
el
.
addClass
(
ACTIVE_CLASS
);
if
(
value
!=
null
)
{
if
((
!
field
||
!
field
.
length
)
&&
fieldName
)
{
if
((
!
field
||
!
field
.
length
)
&&
fieldName
)
{
this
.
addInput
(
fieldName
,
value
,
selectedObject
);
this
.
addInput
(
fieldName
,
value
,
selectedObject
);
}
else
if
(
field
&&
field
.
length
)
{
}
field
.
val
(
value
).
trigger
(
'
change
'
);
}
else
{
isMarking
=
true
;
if
(
!
this
.
options
.
multiSelect
||
el
.
hasClass
(
'
dropdown-clear-active
'
))
{
this
.
dropdown
.
find
(
`.
${
ACTIVE_CLASS
}
`
).
removeClass
(
ACTIVE_CLASS
);
if
(
!
isInput
)
{
this
.
dropdown
.
parent
()
.
find
(
`input[name='
${
fieldName
}
']`
)
.
remove
();
}
}
if
(
field
&&
field
.
length
&&
value
==
null
)
{
this
.
clearField
(
field
,
isInput
);
}
// Toggle active class for the tick mark
el
.
addClass
(
ACTIVE_CLASS
);
if
(
value
!=
null
)
{
if
((
!
field
||
!
field
.
length
)
&&
fieldName
)
{
this
.
addInput
(
fieldName
,
value
,
selectedObject
);
}
else
if
(
field
&&
field
.
length
)
{
field
.
val
(
value
).
trigger
(
'
change
'
);
}
}
}
}
}
return
[
selectedObject
,
isMarking
];
}
}
return
[
selectedObject
,
isMarking
];
focusTextInput
()
{
};
if
(
this
.
options
.
filterable
)
{
const
initialScrollTop
=
$
(
window
).
scrollTop
();
GitLabDropdown
.
prototype
.
focusTextInput
=
function
(
)
{
if
(
this
.
dropdown
.
is
(
'
.show
'
)
&&
!
this
.
filterInput
.
is
(
'
:focus
'
)
)
{
if
(
this
.
options
.
filterable
)
{
this
.
filterInput
.
focus
();
const
initialScrollTop
=
$
(
window
).
scrollTop
();
}
if
(
this
.
dropdown
.
is
(
'
.show
'
)
&&
!
this
.
filterInput
.
is
(
'
:focus
'
))
{
if
(
$
(
window
).
scrollTop
()
<
initialScrollTop
)
{
this
.
filterInput
.
focus
();
$
(
window
).
scrollTop
(
initialScrollTop
);
}
}
}
}
if
(
$
(
window
).
scrollTop
()
<
initialScrollTop
)
{
addInput
(
fieldName
,
value
,
selectedObject
,
single
)
{
$
(
window
).
scrollTop
(
initialScrollTop
);
// Create hidden input for form
if
(
single
)
{
$
(
`input[name="
${
fieldName
}
"]`
).
remove
();
}
}
}
};
GitLabDropdown
.
prototype
.
addInput
=
function
(
fieldName
,
value
,
selectedObject
,
single
)
{
const
$input
=
$
(
'
<input>
'
)
// Create hidden input for form
.
attr
(
'
type
'
,
'
hidden
'
)
if
(
single
)
{
.
attr
(
'
name
'
,
fieldName
)
$
(
`input[name="
${
fieldName
}
"]`
).
remove
();
.
val
(
value
);
}
if
(
this
.
options
.
inputId
!=
null
)
{
$input
.
attr
(
'
id
'
,
this
.
options
.
inputId
);
}
const
$input
=
$
(
'
<input>
'
)
if
(
this
.
options
.
multiSelect
)
{
.
attr
(
'
type
'
,
'
hidden
'
)
Object
.
keys
(
selectedObject
).
forEach
(
attribute
=>
{
.
attr
(
'
name
'
,
fieldName
)
$input
.
attr
(
`data-
${
attribute
}
`
,
selectedObject
[
attribute
]);
.
val
(
value
);
});
if
(
this
.
options
.
inputId
!=
null
)
{
}
$input
.
attr
(
'
id
'
,
this
.
options
.
inputId
);
}
if
(
this
.
options
.
multiSelect
)
{
if
(
this
.
options
.
inputMeta
)
{
Object
.
keys
(
selectedObject
).
forEach
(
attribute
=>
{
$input
.
attr
(
'
data-meta
'
,
selectedObject
[
this
.
options
.
inputMeta
]);
$input
.
attr
(
`data-
${
attribute
}
`
,
selectedObject
[
attribute
]);
}
});
}
if
(
this
.
options
.
inputMeta
)
{
this
.
dropdown
.
before
(
$input
).
trigger
(
'
change
'
);
$input
.
attr
(
'
data-meta
'
,
selectedObject
[
this
.
options
.
inputMeta
]);
}
}
this
.
dropdown
.
before
(
$input
).
trigger
(
'
change
'
);
selectRowAtIndex
(
index
)
{
};
// If we pass an option index
let
selector
;
GitLabDropdown
.
prototype
.
selectRowAtIndex
=
function
(
index
)
{
if
(
typeof
index
!==
'
undefined
'
)
{
let
selector
;
selector
=
`
${
SELECTABLE_CLASSES
}
:eq(
${
index
}
) a`
;
// If we pass an option index
if
(
typeof
index
!==
'
undefined
'
)
{
selector
=
`
${
SELECTABLE_CLASSES
}
:eq(
${
index
}
) a`
;
}
else
{
selector
=
'
.dropdown-content .is-focused
'
;
}
if
(
this
.
dropdown
.
find
(
'
.dropdown-toggle-page
'
).
length
)
{
selector
=
`.dropdown-page-one
${
selector
}
`
;
}
// simulate a click on the first link
const
$el
=
$
(
selector
,
this
.
dropdown
);
if
(
$el
.
length
)
{
const
href
=
$el
.
attr
(
'
href
'
);
if
(
href
&&
href
!==
'
#
'
)
{
visitUrl
(
href
);
}
else
{
}
else
{
$el
.
trigger
(
'
click
'
);
selector
=
'
.dropdown-content .is-focused
'
;
}
if
(
this
.
dropdown
.
find
(
'
.dropdown-toggle-page
'
).
length
)
{
selector
=
`.dropdown-page-one
${
selector
}
`
;
}
// simulate a click on the first link
const
$el
=
$
(
selector
,
this
.
dropdown
);
if
(
$el
.
length
)
{
const
href
=
$el
.
attr
(
'
href
'
);
if
(
href
&&
href
!==
'
#
'
)
{
visitUrl
(
href
);
}
else
{
$el
.
trigger
(
'
click
'
);
}
}
}
}
}
};
GitLabDropdown
.
prototype
.
addArrowKeyEvent
=
function
()
{
addArrowKeyEvent
()
{
let
selector
;
const
ARROW_KEY_CODES
=
[
38
,
40
];
const
ARROW_KEY_CODES
=
[
38
,
40
];
let
selector
=
SELECTABLE_CLASSES
;
selector
=
SELECTABLE_CLASSES
;
if
(
this
.
dropdown
.
find
(
'
.dropdown-toggle-page
'
).
length
)
{
if
(
this
.
dropdown
.
find
(
'
.dropdown-toggle-page
'
).
length
)
{
selector
=
`.dropdown-page-one
${
selector
}
`
;
selector
=
`.dropdown-page-one
${
selector
}
`
;
}
}
return
$
(
'
body
'
).
on
(
'
keydown
'
,
e
=>
{
return
$
(
'
body
'
).
on
(
'
keydown
'
,
e
=>
{
let
$listItems
,
PREV_INDEX
;
let
$listItems
,
PREV_INDEX
;
const
currentKeyCode
=
e
.
which
;
const
currentKeyCode
=
e
.
which
;
if
(
ARROW_KEY_CODES
.
indexOf
(
currentKeyCode
)
!==
-
1
)
{
if
(
ARROW_KEY_CODES
.
indexOf
(
currentKeyCode
)
!==
-
1
)
{
e
.
preventDefault
();
e
.
preventDefault
();
e
.
stopImmediatePropagation
();
e
.
stopImmediatePropagation
();
PREV_INDEX
=
currentIndex
;
PREV_INDEX
=
currentIndex
;
$listItems
=
$
(
selector
,
this
.
dropdown
);
$listItems
=
$
(
selector
,
this
.
dropdown
);
// if @options.filterable
// if @options.filterable
// $input.blur()
// $input.blur()
if
(
currentKeyCode
===
40
)
{
if
(
currentKeyCode
===
40
)
{
// Move down
// Move down
if
(
currentIndex
<
$listItems
.
length
-
1
)
{
if
(
currentIndex
<
$listItems
.
length
-
1
)
{
currentIndex
+=
1
;
currentIndex
+=
1
;
}
}
else
if
(
currentKeyCode
===
38
)
{
// Move up
if
(
currentIndex
>
0
)
{
currentIndex
-=
1
;
}
}
}
}
else
if
(
currentKeyCode
===
38
)
{
if
(
currentIndex
!==
PREV_INDEX
)
{
// Move up
this
.
highlightRowAtIndex
(
$listItems
,
currentIndex
);
if
(
currentIndex
>
0
)
{
currentIndex
-=
1
;
}
}
return
false
;
}
}
if
(
currentIndex
!==
PREV_INDEX
)
{
if
(
currentKeyCode
===
13
&&
currentIndex
!==
-
1
)
{
this
.
highlightRowAtIndex
(
$listItems
,
currentIndex
);
e
.
preventDefault
();
this
.
selectRowAtIndex
();
}
}
return
false
;
});
}
if
(
currentKeyCode
===
13
&&
currentIndex
!==
-
1
)
{
e
.
preventDefault
();
this
.
selectRowAtIndex
();
}
});
};
GitLabDropdown
.
prototype
.
removeArrowKeyEvent
=
function
()
{
return
$
(
'
body
'
).
off
(
'
keydown
'
);
};
GitLabDropdown
.
prototype
.
resetRows
=
function
resetRows
()
{
currentIndex
=
-
1
;
$
(
'
.is-focused
'
,
this
.
dropdown
).
removeClass
(
'
is-focused
'
);
};
GitLabDropdown
.
prototype
.
highlightRowAtIndex
=
function
(
$listItems
,
index
)
{
if
(
!
$listItems
)
{
$listItems
=
$
(
SELECTABLE_CLASSES
,
this
.
dropdown
);
}
// Remove the class for the previously focused row
$
(
'
.is-focused
'
,
this
.
dropdown
).
removeClass
(
'
is-focused
'
);
// Update the class for the row at the specific index
const
$listItem
=
$listItems
.
eq
(
index
);
$listItem
.
find
(
'
a:first-child
'
).
addClass
(
'
is-focused
'
);
// Dropdown content scroll area
const
$dropdownContent
=
$listItem
.
closest
(
'
.dropdown-content
'
);
const
dropdownScrollTop
=
$dropdownContent
.
scrollTop
();
const
dropdownContentHeight
=
$dropdownContent
.
outerHeight
();
const
dropdownContentTop
=
$dropdownContent
.
prop
(
'
offsetTop
'
);
const
dropdownContentBottom
=
dropdownContentTop
+
dropdownContentHeight
;
// Get the offset bottom of the list item
const
listItemHeight
=
$listItem
.
outerHeight
();
const
listItemTop
=
$listItem
.
prop
(
'
offsetTop
'
);
const
listItemBottom
=
listItemTop
+
listItemHeight
;
if
(
!
index
)
{
// Scroll the dropdown content to the top
$dropdownContent
.
scrollTop
(
0
);
}
else
if
(
index
===
$listItems
.
length
-
1
)
{
// Scroll the dropdown content to the bottom
$dropdownContent
.
scrollTop
(
$dropdownContent
.
prop
(
'
scrollHeight
'
));
}
else
if
(
listItemBottom
>
dropdownContentBottom
+
dropdownScrollTop
)
{
// Scroll the dropdown content down
$dropdownContent
.
scrollTop
(
listItemBottom
-
dropdownContentBottom
+
CURSOR_SELECT_SCROLL_PADDING
,
);
}
else
if
(
listItemTop
<
dropdownContentTop
+
dropdownScrollTop
)
{
// Scroll the dropdown content up
return
$dropdownContent
.
scrollTop
(
listItemTop
-
dropdownContentTop
-
CURSOR_SELECT_SCROLL_PADDING
,
);
}
}
};
GitLabDropdown
.
prototype
.
updateLabel
=
function
(
selected
,
el
,
instance
)
{
// eslint-disable-next-line class-methods-use-this
if
(
selected
==
null
)
{
removeArrowKeyEvent
(
)
{
selected
=
null
;
return
$
(
'
body
'
).
off
(
'
keydown
'
)
;
}
}
if
(
el
==
null
)
{
el
=
null
;
resetRows
()
{
}
currentIndex
=
-
1
;
if
(
instance
==
null
)
{
$
(
'
.is-focused
'
,
this
.
dropdown
).
removeClass
(
'
is-focused
'
);
instance
=
null
;
}
}
let
toggleText
=
this
.
options
.
toggleLabel
(
selected
,
el
,
instance
);
highlightRowAtIndex
(
$listItems
,
index
)
{
if
(
this
.
options
.
updateLabel
)
{
if
(
!
$listItems
)
{
// Option to override the dropdown label text
// eslint-disable-next-line no-param-reassign
toggleText
=
this
.
options
.
updateLabel
;
$listItems
=
$
(
SELECTABLE_CLASSES
,
this
.
dropdown
);
}
// Remove the class for the previously focused row
$
(
'
.is-focused
'
,
this
.
dropdown
).
removeClass
(
'
is-focused
'
);
// Update the class for the row at the specific index
const
$listItem
=
$listItems
.
eq
(
index
);
$listItem
.
find
(
'
a:first-child
'
).
addClass
(
'
is-focused
'
);
// Dropdown content scroll area
const
$dropdownContent
=
$listItem
.
closest
(
'
.dropdown-content
'
);
const
dropdownScrollTop
=
$dropdownContent
.
scrollTop
();
const
dropdownContentHeight
=
$dropdownContent
.
outerHeight
();
const
dropdownContentTop
=
$dropdownContent
.
prop
(
'
offsetTop
'
);
const
dropdownContentBottom
=
dropdownContentTop
+
dropdownContentHeight
;
// Get the offset bottom of the list item
const
listItemHeight
=
$listItem
.
outerHeight
();
const
listItemTop
=
$listItem
.
prop
(
'
offsetTop
'
);
const
listItemBottom
=
listItemTop
+
listItemHeight
;
if
(
!
index
)
{
// Scroll the dropdown content to the top
$dropdownContent
.
scrollTop
(
0
);
}
else
if
(
index
===
$listItems
.
length
-
1
)
{
// Scroll the dropdown content to the bottom
$dropdownContent
.
scrollTop
(
$dropdownContent
.
prop
(
'
scrollHeight
'
));
}
else
if
(
listItemBottom
>
dropdownContentBottom
+
dropdownScrollTop
)
{
// Scroll the dropdown content down
$dropdownContent
.
scrollTop
(
listItemBottom
-
dropdownContentBottom
+
CURSOR_SELECT_SCROLL_PADDING
,
);
}
else
if
(
listItemTop
<
dropdownContentTop
+
dropdownScrollTop
)
{
// Scroll the dropdown content up
return
$dropdownContent
.
scrollTop
(
listItemTop
-
dropdownContentTop
-
CURSOR_SELECT_SCROLL_PADDING
,
);
}
}
}
return
$
(
this
.
el
)
updateLabel
(
selected
=
null
,
el
=
null
,
instance
=
null
)
{
.
find
(
'
.dropdown-toggle-text
'
)
let
toggleText
=
this
.
options
.
toggleLabel
(
selected
,
el
,
instance
);
.
text
(
toggleText
);
if
(
this
.
options
.
updateLabel
)
{
};
// Option to override the dropdown label text
toggleText
=
this
.
options
.
updateLabel
;
}
GitLabDropdown
.
prototype
.
clearField
=
function
(
field
,
isInput
)
{
return
$
(
this
.
el
)
return
isInput
?
field
.
val
(
''
)
:
field
.
remove
();
.
find
(
'
.dropdown-toggle-text
'
)
};
.
text
(
toggleText
);
}
// eslint-disable-next-line class-methods-use-this
clearField
(
field
,
isInput
)
{
return
isInput
?
field
.
val
(
''
)
:
field
.
remove
();
}
}
// eslint-disable-next-line func-names
$
.
fn
.
glDropdown
=
function
(
opts
)
{
$
.
fn
.
glDropdown
=
function
(
opts
)
{
// eslint-disable-next-line func-names
return
this
.
each
(
function
()
{
return
this
.
each
(
function
()
{
if
(
!
$
.
data
(
this
,
'
glDropdown
'
))
{
if
(
!
$
.
data
(
this
,
'
glDropdown
'
))
{
return
$
.
data
(
this
,
'
glDropdown
'
,
new
GitLabDropdown
(
this
,
opts
));
return
$
.
data
(
this
,
'
glDropdown
'
,
new
GitLabDropdown
(
this
,
opts
));
...
...
changelogs/unreleased/Refactor-gl_dropdown-js.yml
0 → 100644
View file @
79f0044d
---
title
:
refactoring gl_dropdown.js to use ES6 classes instead of constructor functions
merge_request
:
20488
author
:
nuwe1
type
:
other
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment