diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2aa2b7b..e6a1442 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,32 @@
# Strike by Appiphony
+### Release 0.4.0 — April 18, 2017
+
+#### New Features
+* **Strike Multi Lookup:** Similar to Strike Multi Select Picklist, Strike Multi Lookup gives the user the ability to select multiple Salesforce records in one field
+* **Strike Popover:** Similar to Strike Tooltip, a Strike Popover appears when a user hovers over its trigger element and can display markup/components in its body
+* **Strike Tile:** A grouping of information that can be used to describe a record (fiddler and documentation coming soon; used in Strike Multi Lookup)
+#### Improvements
+* **Strike Datepicker:** Datepickers now use the system's datepicker on mobile devices [GitHub Issue #14]
+* **Strike Input:** Added support for the `checkbox`, `radio`, and `toggle` types
+* **Strike Input:** Added the `showError(errorMessage)` and `hideError()` methods
+* **Strike Multi Select Picklist:** Added the `onchange` attribute that calls a controller method when a selection is added or removed
+* **Strike Multi Select Picklist:** A "no results" message has been added
+* **Strike Option:** Renamed the `strike_filterBy()` and `strike_optionSelected()` methods to `filterBy()` and `select()` respectively
+* **Strike Option Group:** Renamed the `strike_filterBy()` method to `filterBy()`
+* **Strike Select:** Strike Option Groups no longer display when `searchable` is set to `true` and no results in that group are found
+* **Strike Select:** A "no results" message has been added when searchable is set to true
+* **Strike Textarea:** Added the `blur()`, `focus()`, `select()`, `showError(errorMessage)`, and `hideError()` methods
+#### Bug Fixes
+* **Strike Datepicker:** Fixed an issue where datepickers were unintentionally closing when clicking the Today link, left/right month arrows, and year select [GitHub Issue #9]
+* **Strike Multi Select Picklist:** Fixed an issue where options were not filterable on mobile devices, though the input was still in focus behind the overlay [GitHub Issue #10]
+* **Strike Multi Select Picklist:** Fixed an issue where multiple selections could not be made on iOS [GitHub Issue #11]
+* **Strike Multi Select Picklist:** Fixed an issue where options were not reset after closing the picklist immediately after a search that yielded no results [GitHub Issue #13]
+* **Strike Select:** Fixed an issue where clicking/tapping on the search input (when `searchable` is set to `true`) closes the dropdown
+* **Strike Select:** Fixed an issue where searches also returned option groups and sometimes displayed incorrect results
+
+---
+
### Release 0.3.0 — March 31, 2017
#### Improvements
@@ -38,11 +65,11 @@
* **Strike Textarea:** Added the `helpText` attribute for descriptive tooltips (appears after the form label)
#### Bug Fixes
* **Strike Datepicker:** Fixed an issue where the menu opens when tabbing into the input
-* **Strike Datepicker:** Fixed an issue where clicking the SVG portion of the datepicker's button did not open the menu in Microsoft Edge (GitHub Issue #5)
-* **Strike Lookup**: Fixed an issue where records were still selected after closing the menu without confirming a selection (GitHub Issue #2)
-* **Strike Lookup**: Fixed an issue where the menu doesn't close when tabbing out of the input (GitHub Issue #4)
+* **Strike Datepicker:** Fixed an issue where clicking the SVG portion of the datepicker's button did not open the menu in Microsoft Edge [GitHub Issue #5]
+* **Strike Lookup**: Fixed an issue where records were still selected after closing the menu without confirming a selection [GitHub Issue #2]
+* **Strike Lookup**: Fixed an issue where the menu doesn't close when tabbing out of the input [GitHub Issue #4]
* **Strike Lookup**: Fixed an issue where the input's search icon was incorrectly positioned
-* **Strike Lookup**: Removed `debugger` line (GitHub Issue #6)
+* **Strike Lookup**: Removed `debugger` line [GitHub Issue #6]
* **Strike Multi Select Picklist**: Fixed an issue where the input's search icon was incorrectly positioned
* **Strike Multi Select Picklist**: Fixed an issue where selected pills did not have spacing between them
* **Strike Select:** Fixed an issue where the component was not focusable
@@ -54,7 +81,7 @@
### Release 0.1.1 — March 1, 2017
#### Bug Fixes
-* **Lookup:** Fixed an issue where records with `null` values for the `search` and `subtitle` fields caused lookups to throw an exception (GitHub Issue #1)
+* **Lookup:** Fixed an issue where records with `null` values for the `search` and `subtitle` fields caused lookups to throw an exception [GitHub Issue #1]
---
diff --git a/README.md b/README.md
index 33c4c50..c4ae341 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# Strike by Appiphony
### Work smarter (not harder) with Salesforce Lightning Components
-Current release: 0.3.0
+Current release: 0.4.0
See it in action here: http://www.lightningstrike.io
---
@@ -12,11 +12,16 @@ See it in action here: h
* Inputs
* Lookups
* Modals
+* Multi Lookups
* Multi Select Picklists
* Options
+* Option Groups
* Pills
+* Popovers
* Selects
+* SVGs
* Textareas
+* Tiles
* Tooltips
---
diff --git a/aura/strike_datepicker/strike_datepicker.cmp b/aura/strike_datepicker/strike_datepicker.cmp
index 85abf64..9abf445 100644
--- a/aura/strike_datepicker/strike_datepicker.cmp
+++ b/aura/strike_datepicker/strike_datepicker.cmp
@@ -45,31 +45,52 @@
+
-
-
diff --git a/aura/strike_datepicker/strike_datepicker.css b/aura/strike_datepicker/strike_datepicker.css
index 5c541a6..414df20 100644
--- a/aura/strike_datepicker/strike_datepicker.css
+++ b/aura/strike_datepicker/strike_datepicker.css
@@ -1,56 +1,72 @@
.THIS .strike-live-area {
pointer-events: none;
}
+
.THIS .strike-live-area .slds-form-element__label,
.THIS .strike-live-area .slds-input {
pointer-events: auto;
}
+
.THIS .strike-live-area .slds-input[readonly] {
cursor: text;
}
+
.THIS .strike-live-area .slds-input[readonly]:hover,
.THIS .strike-live-area .slds-input[readonly]:focus {
background-color: white;
}
+
.THIS .strike-live-area .slds-input[readonly][disabled] {
cursor: not-allowed;
}
+
.THIS .strike-live-area .slds-input[readonly][disabled]:hover {
background-color: t(colorBackgroundInputDisabled);
}
+
.THIS .strike-datepicker-input {
position: relative;
}
+
.THIS .strike-datepicker-input .strike-datepicker-input__icon {
position: absolute;
bottom: 0.5rem;
right: 0.5rem;
}
+
.THIS .strike-datepicker-input .strike-datepicker-input__icon:focus,
.THIS .strike-datepicker-input .strike-datepicker-input__icon:active {
outline: none;
box-shadow: none;
}
+
.THIS .strike-datepicker-input .strike-datepicker-input__icon .slds-button__icon {
- pointer-events: none; /* Edge fix */
+ pointer-events: none;
+ /* Edge fix */
}
+
.THIS .slds-datepicker {
top: 3.5rem;
}
+
.THIS .slds-datepicker .slds-select[size],
.THIS .slds-datepicker .slds-select[multiple] {
height: calc(1.875rem + (1px * 2));
}
+
.THIS .slds-datepicker thead abbr {
text-transform: capitalize;
}
+
.THIS .slds-datepicker .slds-form-element__label {
display: none;
}
+
.THIS .slds-datepicker:focus,
.THIS .slds-datepicker:active {
outline: none;
}
+
.THIS .sd-select_container:before,
.THIS .sd-select_container:after {
position: absolute;
@@ -62,17 +78,21 @@
border-left: 3px solid transparent;
border-right: 3px solid transparent;
}
+
.THIS .sd-select_container {
position: relative;
}
+
.THIS .sd-select_container:before {
border-bottom: 5px solid t(colorBorderInverse);
top: calc((1.75rem / 2) - 6px);
}
+
.THIS .sd-select_container:after {
- border-top: 5px solid t(colorBorderInverse);
+ border-top: 5px solid t(colorBorderInverse);
bottom: calc((1.75rem / 2) - 6px);
}
+
.THIS .sd-select_container select.uiInput {
-moz-appearance: none;
-webkit-appearance: none;
@@ -86,29 +106,57 @@
border: 1px solid #d8dde6;
border-radius: .25rem;
width: 100%;
- transition: border .1s linear,background-color .1s linear;
+ transition: border .1s linear, background-color .1s linear;
height: calc(1.875rem + (1px * 2));
}
+
+
/* ----- Override lightning:input error states ----- */
+
.THIS.slds-form-element .is-required.slds-has-error .slds-input {
box-shadow: none;
border-color: t(colorBorderInput);
}
+
.THIS.slds-form-element .is-required.slds-has-error .slds-input:focus,
.THIS.slds-form-element .is-required.slds-has-error .slds-input:active {
border-color: t(colorBorderInputActive);
box-shadow: 0 0 3px #0070d2;
}
+
.THIS.slds-form-element.slds-has-error .is-required.slds-has-error .slds-input {
box-shadow: rgb(194, 57, 52) 0 0 0 1px inset;
border-color: t(colorBorderError);
}
+
.THIS.slds-form-element.slds-has-error .is-required.slds-has-error .slds-input:focus {
box-shadow: rgb(194, 57, 52) 0 0 0 1px inset, 0 0 3px #0070d2;
}
+
.THIS.slds-form-element.slds-has-error .strike-live-area .slds-input[readonly][disabled]:hover {
background-color: t(colorBackgroundInput);
}
+
.THIS .slds-form-element__help:not(.sd-error-message) {
display: none;
-}
\ No newline at end of file
+}
+
+
+/* --------------------------------------------------
+Salesforce1 Styles
+-------------------------------------------------- */
+
+.THIS.slds-form-element.sd-datepicker--mobile .slds-datepicker {
+ display: none; /* Now using system datepicker on mobile */
+ margin: 0;
+ border: none;
+ border-radius: 0;
+ box-shadow: none;
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ max-width: 100%;
+}
diff --git a/aura/strike_datepicker/strike_datepickerController.js b/aura/strike_datepicker/strike_datepickerController.js
index db3ff44..1aeca7b 100644
--- a/aura/strike_datepicker/strike_datepickerController.js
+++ b/aura/strike_datepicker/strike_datepickerController.js
@@ -4,22 +4,24 @@
helper.createLocaleDatePatternMap(component);
helper.processDateValue(component);
- component.closeDatepicker = function(){
- if(component.isValid()){
+ component.closeDatepicker = $A.getCallback(function(){
+ if (component.isValid()) {
helper.closeDatepicker(component, event, helper);
} else {
window.removeEventListener('click', component.closeDatepicker);
}
- }
+ });
window.addEventListener('click', $A.getCallback(component.closeDatepicker));
+
+ component.set('v.isMobile', $A.get('$Browser.formFactor') == 'DESKTOP' ? false : true);
},
valueChanged: function(component, event, helper) {
var dateDebouncer = component.get('v.dateDebouncer');
var value = component.get('v.value');
var clickedValue = component.get('v.clickedValue');
- if(value == clickedValue){
+ if (value == clickedValue) {
return;
}
@@ -31,11 +33,10 @@
var displayDate = component.get('v.displayDate');
var datePattern = component.get('v.valueFormat');
var locale = helper.getLocale();
- var localeDatePattern = component.get('v.datePatternMap')[locale];
-
+ var localeDatePattern = helper.getLocaleDatePattern(component, locale);
var selectedDate = $A.localizationService.parseDateTime(displayDate, localeDatePattern, null, true);
- if(selectedDate == null){
+ if (selectedDate == null) {
component.set('v.value', null);
component.set('v.timestamp', null);
@@ -45,7 +46,7 @@
var formattedDisplayDate = $A.localizationService.formatDate(selectedDate.toString(), datePattern);
- if(currentValue == formattedDisplayDate){
+ if (currentValue == formattedDisplayDate) {
helper.closeDatepicker(component, event, helper);
return;
}
@@ -58,8 +59,10 @@
event.stopPropagation();
var notDisabled = !component.get('v.disabled');
var displayDate = component.get('v.displayDate');
+ var dateInput = component.find('date-input');
if(notDisabled && $A.util.isEmpty(displayDate)){
+ dateInput.blur();
helper.determineReadOnly(component);
component.set('v.datePickerOpen', true);
}
@@ -115,6 +118,9 @@
component.set('v.calendarRows', helper.getCalendarRows(component));
component.set('v.selectedMonthText', monthLabels[prevMonth].fullName);
},
+ closeDatepicker: function (component, event, helper) {
+ helper.closeDatepicker(component, event, helper);
+ },
yearChanged: function (component, event, helper) {
component.set('v.calendarRows', helper.getCalendarRows(component));
},
diff --git a/aura/strike_datepicker/strike_datepickerHelper.js b/aura/strike_datepicker/strike_datepickerHelper.js
index d409781..4ab8bf1 100644
--- a/aura/strike_datepicker/strike_datepickerHelper.js
+++ b/aura/strike_datepicker/strike_datepickerHelper.js
@@ -19,12 +19,12 @@
var displayDate = component.get('v.displayDate');
component.set('v.currentDate', null);
currentDate = new Date();
- } else if($A.util.isEmpty(currentDate)){ //invalid date
+ } else if ($A.util.isEmpty(currentDate)) { //invalid date
component.set('v.currentDate', null);
return;
} else { //valid date
var locale = this.getLocale();
- var localeDatePattern = component.get('v.datePatternMap')[locale];
+ var localeDatePattern = this.getLocaleDatePattern(component, locale);
var formattedDisplayDate = $A.localizationService.formatDate(currentDate, localeDatePattern);
var timestamp = Date.UTC(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate());
@@ -43,11 +43,22 @@
this.setYearValues(component, year);
this.buildCalendar(component, currentDate);
},
+ getLocaleDatePattern: function(component, locale) {
+ var localeDatePattern;
+
+ if (component.get('v.isMobile')) {
+ localeDatePattern = 'YYYY-MM-DD'; // iOS and Android format
+ } else {
+ localeDatePattern = component.get('v.datePatternMap')[locale];
+ }
+
+ return localeDatePattern;
+ },
buildCalendar: function (component, currentDate) {
var year = currentDate.getFullYear();
-
var month = currentDate.getMonth();
var monthLabels = component.get('v.monthLabels');
+
component.set("v.selectedYear", parseInt(year));
component.set("v.selectedMonth", month);
component.set("v.selectedMonthText", monthLabels[month].fullName);
diff --git a/aura/strike_input/strike_input.cmp b/aura/strike_input/strike_input.cmp
index c9262bd..de16204 100644
--- a/aura/strike_input/strike_input.cmp
+++ b/aura/strike_input/strike_input.cmp
@@ -1,24 +1,24 @@
-
-
+
-
+
+
-
-
+
+
@@ -27,78 +27,118 @@
+
-
+
+
+
+
+
+
+
+
diff --git a/aura/strike_input/strike_input.css b/aura/strike_input/strike_input.css
index d8e2173..381aeab 100644
--- a/aura/strike_input/strike_input.css
+++ b/aura/strike_input/strike_input.css
@@ -1,3 +1,16 @@
.THIS .si-help-text-icon .slds-icon {
fill: #9ea9b5;
-}
\ No newline at end of file
+}
+
+.THIS label.slds-checkbox--toggle {
+ pointer-events: none;
+}
+
+.THIS label.slds-checkbox--toggle > * {
+ pointer-events: auto;
+}
+
+.THIS input[type="date"] {
+ -moz-appearance: none;
+ -webkit-appearance: none;
+}
diff --git a/aura/strike_input/strike_inputController.js b/aura/strike_input/strike_inputController.js
index 7dfbdb9..1a1bd9f 100644
--- a/aura/strike_input/strike_inputController.js
+++ b/aura/strike_input/strike_inputController.js
@@ -2,7 +2,7 @@
onInit: function (component, event, helper) {
var randomNumber = Math.floor(1000 + Math.random() * 9000);
- component.set('v.inputId', randomNumber);
+ component.set('v.idNumber', randomNumber);
},
handleBlur: function(component, event, helper) {
var type = component.get('v.type');
@@ -10,19 +10,24 @@
var value = component.get('v.value');
// component.set('v.error', false);
- if(type == 'email'){
+ /*if(type == 'email'){
helper.checkIfEmail(component, event, helper);
- }
+ }*/
- if(!$A.util.isEmpty(pattern) && !$A.util.isEmpty(value)){
+ /*if(!$A.util.isEmpty(pattern) && !$A.util.isEmpty(value)){ // No longer supported
var patternMatcher = new RegExp('^' + pattern + '$');
if(!value.match(patternMatcher)){
component.set('v.errorMessage', component.get('v.messageWhenPatternMismatch'));
component.set('v.error', true);
}
- }
+ }*/
component.getEvent('onblur').fire();
+
+ var dateTimeInputTypes = ['date', 'datetime-local', 'month', 'week', 'time'];
+ if(dateTimeInputTypes.indexOf(type) !== -1) {
+ helper.resetValue(component, event, helper);
+ }
},
handleChange: function(component, event, helper) {
component.getEvent('onchange').fire();
@@ -30,14 +35,16 @@
handleFocus: function(component, event, helper) {
component.getEvent('onfocus').fire();
},
- updateValue: function(component, event, helper){
- var inputEl = component.find('inputField').getElement();
- var value = component.get('v.value');
- if(inputEl.value != value){
- component.set('v.value', inputEl.value);
+ updateValue: function(component, event, helper) {
+ var type = component.get('v.type');
+ var dateTimeInputTypes = ['date', 'datetime-local', 'month', 'week', 'time'];
+ if(dateTimeInputTypes.indexOf(type) === -1) {
+ helper.resetValue(component, event, helper);
}
- component.getEvent('oninput').fire();
+ if(component.get('v.isInsideIterator')) {
+ component.getEvent('oninput').fire();
+ }
},
updateChecked: function(component, event, helper) {
if (component.get('v.type') == 'checkbox'
@@ -51,10 +58,29 @@
}
}
},
+ handleChangeMaxlength: function(component, event, helper) {
+ let maxlength = component.get('v.maxlength');
+ if(!$A.util.isEmpty(maxlength)) {
+ component.set('v.maxlength', Math.abs(maxlength));
+ }
+ },
focus: function(component, event, helper){
component.find('inputField').getElement().focus();
},
+ blur: function(component, event, helper){
+ component.find('inputField').getElement().blur();
+ },
select: function(component, event, helper){
component.find('inputField').getElement().select();
+ },
+ showError: function(component, event, helper) {
+ var errorMessage = event.getParam('arguments').errorMessage;
+
+ component.set('v.errorMessage', errorMessage);
+ component.set('v.error', true);
+ },
+ hideError: function(component, event, helper) {
+ component.set('v.errorMessage', null);
+ component.set('v.error', false);
}
})
diff --git a/aura/strike_input/strike_inputHelper.js b/aura/strike_input/strike_inputHelper.js
index 512e00e..776e9eb 100644
--- a/aura/strike_input/strike_inputHelper.js
+++ b/aura/strike_input/strike_inputHelper.js
@@ -1,5 +1,5 @@
({
- checkIfEmail: function (component, event, helper) {
+ /*checkIfEmail: function (component, event, helper) {
var value = component.find('inputField').getElement('value').value
if (value && value.indexOf('@') === -1) {
@@ -9,5 +9,12 @@
component.set('v.error', false);
component.set('v.errorMessage', null);
}
- }
+ },*/
+ resetValue: function(component, event, helper) {
+ var inputEl = component.find('inputField').getElement();
+ var value = component.get('v.value');
+ if(inputEl.value != value) {
+ component.set('v.value', inputEl.value);
+ }
+ },
})
diff --git a/aura/strike_lookup/strike_lookup.cmp b/aura/strike_lookup/strike_lookup.cmp
index 271171e..2f16458 100644
--- a/aura/strike_lookup/strike_lookup.cmp
+++ b/aura/strike_lookup/strike_lookup.cmp
@@ -21,7 +21,7 @@
-
+
diff --git a/aura/strike_lookup/strike_lookup.css b/aura/strike_lookup/strike_lookup.css
index 76e4a6a..46168ea 100644
--- a/aura/strike_lookup/strike_lookup.css
+++ b/aura/strike_lookup/strike_lookup.css
@@ -79,6 +79,10 @@
fill: #9ea9b5;
}
+.THIS .slds-pill__remove .slds-button__icon {
+ pointer-events: none;
+}
+
/* --------------------------------------------------
Salesforce1 Styles
diff --git a/aura/strike_multiLookup/strike_multiLookup.cmp b/aura/strike_multiLookup/strike_multiLookup.cmp
new file mode 100644
index 0000000..1e62c19
--- /dev/null
+++ b/aura/strike_multiLookup/strike_multiLookup.cmp
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ *
+
+ {!v.label}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {!v.errorMessage}
+
+
+
diff --git a/aura/strike_multiLookup/strike_multiLookup.cmp-meta.xml b/aura/strike_multiLookup/strike_multiLookup.cmp-meta.xml
new file mode 100644
index 0000000..a904a10
--- /dev/null
+++ b/aura/strike_multiLookup/strike_multiLookup.cmp-meta.xml
@@ -0,0 +1,5 @@
+
+
+ 39.0
+ A Lightning Component Bundle
+
diff --git a/aura/strike_multiLookup/strike_multiLookup.css b/aura/strike_multiLookup/strike_multiLookup.css
new file mode 100644
index 0000000..af1c31b
--- /dev/null
+++ b/aura/strike_multiLookup/strike_multiLookup.css
@@ -0,0 +1,191 @@
+.THIS .sl-pill__label {
+ margin-right: auto;
+}
+
+.THIS .slds-pill.sl-pill--loading {
+ border-color: transparent;
+ padding-left: 9px;
+ position: relative;
+ top: -1px;
+}
+
+.THIS .slds-pill.sl-pill--loading:hover {
+ background: transparent;
+}
+
+.THIS .slds-form-element__control .sl-search-icon {
+ position: absolute;
+ right: 20px;
+ top: 50%;
+ transform: translateY(-50%);
+ padding-bottom: 1px;
+}
+
+.THIS .slds-form-element__control .sl-search-icon .slds-icon {
+ fill: t(colorTextInputIcon);
+}
+
+.THIS .slds-lookup__item-action .slds-media__figure {
+ margin-right: .5rem;
+ margin-top: .25rem;
+}
+
+.THIS .slds-lookup__item-action .slds-media__figure .slds-icon {
+ margin-right: 0;
+ margin-top: 0;
+}
+
+.THIS .slds-form-element__control .slds-pill_container,
+.THIS .slds-form-element__control .slds-pill {
+ transition: border .1s linear, background-color .1s linear;
+}
+
+.THIS .slds-form-element__control.sl-disabled .slds-pill_container,
+.THIS .slds-form-element__control.sl-disabled .slds-pill {
+ background-color: t(colorBackgroundShade);
+ cursor: not-allowed;
+}
+
+.THIS .slds-form-element__control.sl-disabled .slds-pill_container {
+ border-color: t(colorBorderInputDisabled);
+}
+
+.THIS .slds-form-element__control.sl-disabled .slds-pill {
+ border-color: transparent;
+}
+
+.THIS.slds-form-element.slds-has-error .slds-pill__label {
+ font-weight: normal;
+ color: t(colorTextDefault);
+}
+
+.THIS .slds-lookup__item-action.sl-no-results,
+.THIS .slds-lookup__item-action.sl-no-results:hover,
+.THIS .slds-lookup__item-action.sl-searching,
+.THIS .slds-lookup__item-action.sl-searching:hover {
+ cursor: auto;
+}
+
+.THIS .slds-lookup__item-action.sl-no-results:hover,
+.THIS .slds-lookup__item-action.sl-no-results:focus,
+.THIS .slds-lookup__item-action.sl-no-results:active,
+.THIS .slds-lookup__item-action.sl-searching:hover,
+.THIS .slds-lookup__item-action.sl-searching:focus,
+.THIS .slds-lookup__item-action.sl-searching:active {
+ background: white;
+}
+
+.THIS .sl-help-text-icon .slds-icon {
+ fill: #9ea9b5;
+}
+
+
+/* --------------------------------------------------
+Salesforce1 Styles
+-------------------------------------------------- */
+
+.THIS:not(.sl-lookup--mobile).scrollable,
+.THIS:not(.sl-lookup--open).scrollable {
+ overflow: initial;
+}
+
+.THIS.sl-lookup--mobile.slds-is-open .slds-lookup__menu,
+.THIS.sl-lookup--mobile .sl-lookup--mobile__cancel,
+.THIS.sl-lookup--mobile .slds-lookup__list .slds-dropdown__item .slds-icon_container,
+.THIS.sl-lookup--mobile .slds-lookup__list .slds-dropdown__item .slds-icon,
+/*.THIS.sl-lookup--mobile .slds-lookup__list .sl-lookup__new,*/
+/*.THIS.sl-lookup--mobile .slds-pill__icon_container,*/
+.THIS.sl-lookup--mobile.sl-lookup--open .slds-form-element__label,
+.THIS.sl-lookup--mobile.sl-lookup--open .slds-form-element__help {
+ display: none;
+}
+
+.THIS.sl-lookup--mobile.slds-is-open.sl-lookup--open .slds-lookup__menu,
+.THIS.sl-lookup--mobile.sl-lookup--open .sl-lookup--mobile__cancel {
+ display: block;
+}
+
+.THIS.sl-lookup--mobile .slds-lookup__item-action {
+ padding-left: t(spacingXSmall);
+ padding-right: t(spacingXSmall);
+}
+
+.THIS.sl-lookup--mobile.sl-lookup--open .sl-lookup__input {
+ position: relative;
+}
+
+.THIS.sl-lookup--mobile.sl-lookup--open .sl-lookup__input_container {
+ padding: t(spacingXSmall);
+}
+
+.THIS.sl-lookup--mobile .slds-pill_container {
+ background: white;
+ position: relative;
+ z-index: 1;
+}
+
+.THIS.sl-lookup--mobile .sl-input--disabled,
+.THIS.sl-lookup--mobile .sl-input--disabled:hover {
+ background: t(colorBackgroundInputDisabled);
+}
+
+.THIS.sl-lookup--mobile .slds-form-element__control {
+ height: 100%;
+}
+
+.THIS.sl-lookup--mobile.sl-lookup--open .slds-form-element__control {
+ background: t(colorBackground);
+}
+
+.THIS.sl-lookup--mobile.sl-lookup--open {
+ padding: 0;
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ border: none;
+ overflow: initial;
+ overflow-x: hidden;
+ overflow-y: auto;
+ z-index: t(zIndexOverlay);
+ background: white;
+ -webkit-overflow-scrolling: touch;
+}
+
+.THIS.sl-lookup--mobile .slds-lookup__menu {
+ padding: 0;
+ margin-bottom: 52px;
+ position: relative;
+ z-index: 0;
+ top: initial;
+ border: none;
+ background: t(colorBackground);
+}
+
+.THIS.sl-lookup--mobile .slds-lookup__list {
+ max-height: none;
+ overflow: initial;
+ overflow-x: initial;
+ overflow-y: initial;
+}
+
+.THIS.sl-lookup--mobile .slds-lookup__list li {
+ border-bottom: 1px solid t(colorBorder);
+ padding: t(spacingXSmall);
+}
+
+.THIS.sl-lookup--mobile .slds-lookup__list li:first-child {
+ border-top: 1px solid t(colorBorder);
+}
+
+.THIS.sl-lookup--mobile .sl-lookup--mobile__dropdown-header strong {
+ vertical-align: middle;
+ display: inline-block;
+ font-size: 18px;
+ margin-top: 4px;
+}
+
+.THIS.sl-lookup--mobile .slds-lookup__list .sl-lookup__new .slds-icon {
+ margin-top: 0;
+}
\ No newline at end of file
diff --git a/aura/strike_multiLookup/strike_multiLookupController.js b/aura/strike_multiLookup/strike_multiLookupController.js
new file mode 100644
index 0000000..38d4dd5
--- /dev/null
+++ b/aura/strike_multiLookup/strike_multiLookupController.js
@@ -0,0 +1,217 @@
+({
+ onInit: function(component, event, helper) {
+ component.handleClick = $A.getCallback(function() {
+ if (!component.isValid()) {
+ window.removeEventListener('click', component.handleClick);
+
+ return;
+ }
+
+ helper.closeMenu(component, event, helper);
+ });
+
+ window.addEventListener('click', component.handleClick);
+
+ component.set('v.initCallsRunning', 2);
+
+ helper.getRecentRecords(component, event, helper);
+ helper.getRecordByValue(component, event, helper);
+
+ var randomNumber = Math.floor(1000 + Math.random() * 9000);
+
+ component.set('v.idNumber', randomNumber);
+
+ component.set('v.isMobile', $A.get('$Browser.formFactor') == 'DESKTOP' ? false : true);
+ },
+ handleInputClick: function(component, event, helper) {
+ event.stopPropagation();
+ },
+ handleSearchingClick: function(component, event, helper) {
+ component.set('v.searching', false);
+ },
+ handleInputFocus: function(component, event, helper) {
+ $A.util.addClass(component.find('lookup'), 'sl-lookup--open');
+
+ if (component.get('v.disabled')) {
+ return;
+ }
+
+ helper.getRecordsBySearchTerm(component, event, helper);
+ },
+ cancelLookup: function(component, event, helper) {
+ helper.closeMobileLookup(component, event, helper);
+ },
+ handleInputKeyDown: function(component, event, helper) {
+ if (component.get('v.disabled')) {
+ return;
+ }
+
+ const KEYCODE_TAB = 9;
+
+ var keyCode = event.which || event.keyCode || 0;
+
+ if (keyCode == KEYCODE_TAB) {
+ helper.closeMenu(component, event, helper);
+ }
+ },
+ handleInputKeyPress: function(component, event, helper) {
+ if (component.get('v.disabled')) {
+ return;
+ }
+ },
+ handleInputKeyUp: function(component, event, helper) {
+ if (component.get('v.disabled')) {
+ return;
+ }
+
+ const KEYCODE_ENTER = 13;
+ const KEYCODE_UP = 38;
+ const KEYCODE_DOWN = 40;
+
+ var keyCode = event.which || event.keyCode || 0;
+
+ if (keyCode == KEYCODE_ENTER) {
+ helper.updateValueByFocusIndex(component, event, helper);
+ } else if (keyCode == KEYCODE_UP) {
+ helper.moveRecordFocusUp(component, event, helper);
+ } else if (keyCode == KEYCODE_DOWN) {
+ helper.moveRecordFocusDown(component, event, helper);
+ } else {
+
+ helper.getRecordsBySearchTerm(component, event, helper);
+ }
+ },
+ handleRecordClick: function(component, event, helper) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ var focusIndex = event.currentTarget.dataset.index;
+
+ component.set('v.focusIndex', focusIndex);
+
+ helper.updateValueByFocusIndex(component, event, helper);
+ },
+ handleNewRecordClick: function(component, event, helper) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ helper.addNewRecord(component, event, helper);
+ },
+ handleFocusIndexChange: function(component, event, helper) {
+ var focusIndex = component.get('v.focusIndex');
+ var lookupMenu = component.find('lookupMenu').getElement();
+ var options = lookupMenu.getElementsByTagName('li');
+ var focusScrollTop = 0;
+ var focusScrollBottom = 0;
+
+ for (var i = 0; i < options.length; i++) {
+ var optionSpan = options[i].getElementsByTagName('span')[0];
+
+ if (i == focusIndex) {
+ $A.util.addClass(optionSpan, 'slds-has-focus');
+ } else {
+ if (i < focusIndex) {
+ focusScrollTop += options[i].scrollHeight;
+ }
+
+ $A.util.removeClass(optionSpan, 'slds-has-focus');
+ }
+ }
+
+ if (focusIndex !== null) {
+ focusScrollBottom = focusScrollTop + options[focusIndex].scrollHeight;
+ }
+
+ if (focusScrollTop < lookupMenu.scrollTop) {
+ lookupMenu.scrollTop = focusScrollTop;
+ } else if (focusScrollBottom > lookupMenu.scrollTop + lookupMenu.clientHeight) {
+ lookupMenu.scrollTop = focusScrollBottom - lookupMenu.clientHeight;
+ }
+ },
+ handleValueChange: function(component, event, helper) {
+ var value = component.get('v.value');
+ var internalValue = component.get('v.internalValue');
+
+ if ($A.util.isEmpty(value)) {
+ var tiles = component.find('tiles');
+
+ component.set('v.valueLabel', '');
+ if(!$A.util.isEmpty(tiles)){
+ if(!Array.isArray(tiles)){
+ tiles = [tiles];
+ }
+
+ tiles.forEach(function(tile){
+ tile.destroyTile();
+ });
+ }
+ } else if(value != internalValue){
+ helper.getRecordByValue(component, event, helper);
+ }
+ },
+ handleFilterChange: function(component, event, helper) {
+ component.set('v.initCallsRunning', 2);
+
+ helper.getRecordByValue(component, event, helper);
+ helper.getRecentRecords(component, event, helper);
+
+ component.find('lookupInput').getElement().value = '';
+ helper.getRecordsBySearchTerm(component, event, helper);
+ component.set('v.value', '');
+ },
+ handleLimitChange: function(component, event, helper) {
+ component.find('lookupInput').getElement().value = '';
+ helper.getRecordsBySearchTerm(component, event, helper);
+ },
+ handleObjectChange: function(component, event, helper) {
+ component.set('v.initCallsRunning', 2);
+
+ helper.getRecentRecords(component, event, helper);
+ helper.getRecordByValue(component, event, helper);
+
+ component.find('lookupInput').getElement().value = '';
+
+ helper.getRecordsBySearchTerm(component, event, helper);
+ component.set('v.value', '');
+ },
+ handleOrderChange: function(component, event, helper) {
+ component.set('v.initCallsRunning', 1);
+
+ helper.getRecentRecords(component, event, helper);
+
+ component.find('lookupInput').getElement().value = '';
+ helper.getRecordsBySearchTerm(component, event, helper);
+ },
+ handleSearchfieldChange: function(component, event, helper) {
+ component.set('v.initCallsRunning', 2);
+
+ helper.getRecentRecords(component, event, helper);
+ helper.getRecordByValue(component, event, helper);
+
+ component.find('lookupInput').getElement().value = '';
+ helper.getRecordsBySearchTerm(component, event, helper);
+ },
+ handleSubtitlefieldChange: function(component, event, helper) {
+ component.set('v.initCallsRunning', 1);
+
+ helper.getRecentRecords(component, event, helper);
+
+ component.find('lookupInput').getElement().value = '';
+ helper.getRecordsBySearchTerm(component, event, helper);
+ },
+ handleComponentDestroyed: function(component, event, helper){
+ var sourceCmpValue = event.getSource().get('v.value');
+ helper.removeOptionTile(component, event, sourceCmpValue);
+ helper.removeFromComponentValue(component, event, sourceCmpValue);
+ },
+ showError: function(component, event, helper) {
+ var errorMessage = event.getParam('arguments').errorMessage;
+
+ component.set('v.errorMessage', errorMessage);
+ component.set('v.error', true);
+ },
+ hideError: function(component, event, helper) {
+ component.set('v.errorMessage', null);
+ component.set('v.error', false);
+ }
+})
diff --git a/aura/strike_multiLookup/strike_multiLookupHelper.js b/aura/strike_multiLookup/strike_multiLookupHelper.js
new file mode 100644
index 0000000..1a78206
--- /dev/null
+++ b/aura/strike_multiLookup/strike_multiLookupHelper.js
@@ -0,0 +1,338 @@
+({
+ checkIfInitialized: function(component, event, helper) {
+ var initCallsRunning = component.get('v.initCallsRunning');
+
+ if (--initCallsRunning < 0) {
+ initCallsRunning = 0;
+ }
+
+ component.set('v.initCallsRunning', initCallsRunning);
+ },
+ closeMenu: function(component, event, helper) {
+ component.set('v.focusIndex', null);
+ component.set('v.openMenu', false);
+ },
+ getParams: function(component, event, helper, filterIds) {
+ var filter = component.get('v.filter');
+ var limit = component.get('v.limit');
+ var object = component.get('v.object');
+ var order = component.get('v.order');
+ var searchField = component.get('v.searchField');
+ var subtitleField = component.get('v.subtitleField');
+
+ var value = component.get('v.value')
+
+ if(!$A.util.isEmpty(value) && filterIds){
+ var selectedIds = [];
+
+ value.split(';').forEach(function(splitVal){
+ selectedIds.push('\'' + splitVal + '\'')
+ })
+
+ if($A.util.isEmpty(filter)){
+ filter = ''
+ } else {
+ filter += ' AND '
+ }
+
+ filter += 'Id NOT IN (' + selectedIds.join(',') + ')';
+ }
+
+ return {
+ filter: filter,
+ limit: limit,
+ object: object,
+ order: order,
+ searchField: searchField,
+ subtitleField: subtitleField
+ };
+ },
+ getRecentRecords: function(component, event, helper) {
+ var returnedRecords = [];
+
+ var getRecordsAction = component.get('c.getRecentRecords');
+
+ getRecordsAction.setParams({
+ jsonString: JSON.stringify(helper.getParams(component, event, helper, true))
+ });
+
+ getRecordsAction.setCallback(this, function(res) {
+ if (res.getState() === 'SUCCESS') {
+ var returnValue = JSON.parse(res.getReturnValue());
+
+ if (returnValue.isSuccess) {
+ returnValue.results.data.forEach(function(record) {
+ returnedRecords.push({
+ label: record.label,
+ sublabel: record.sublabel,
+ value: record.value
+ });
+ });
+ }
+ }
+ component.set('v.recentRecords', returnedRecords);
+
+ helper.checkIfInitialized(component, event, helper);
+ });
+
+ $A.enqueueAction(getRecordsAction);
+ },
+ getRecordByValue: function(component, event, helper) {
+ var value = component.get('v.value');
+
+ if (!value) {
+ helper.checkIfInitialized(component, event, helper);
+
+ return;
+ }
+
+ var getRecordsAction = component.get('c.getRecords');
+ var params = helper.getParams(component, event, helper, false);
+ var selectedIds = [];
+
+ value.split(';').forEach(function(splitVal){
+ selectedIds.push('\'' + splitVal + '\'')
+ });
+
+ if ($A.util.isEmpty(params.filter)) {
+ params.filter = 'Id IN (' + selectedIds.join(',') + ')';
+ } else {
+ params.filter = 'Id IN (' + selectedIds.join(',') + ') AND (' + params.filter + ')';
+ }
+
+ getRecordsAction.setParams({
+ jsonString: JSON.stringify(params)
+ });
+
+ getRecordsAction.setCallback(this, function(res) {
+ if (res.getState() === 'SUCCESS') {
+ var returnValue = JSON.parse(res.getReturnValue());
+
+ if (returnValue.isSuccess) {
+ var tiles = component.get('v.selectedOptionTiles');
+ returnValue.results.data.forEach(function(record) {
+ var tile = {
+ title: record.label,
+ subtitle: record.sublabel,
+ value: record.value
+ }
+
+ tiles.push(tile);
+ });
+
+ component.set('v.selectedOptionTiles', tiles);
+ component.set('v.internalValue', value);
+ }
+ }
+
+ helper.checkIfInitialized(component, event, helper);
+ });
+
+ $A.enqueueAction(getRecordsAction);
+ },
+ getRecordsBySearchTerm: function(component, event, helper) {
+ var searchTerm = component.find('lookupInput').getElement().value;
+
+ var lastSearchTerm = component.get('v.lastSearchTerm');
+ var searchTimeout = component.get('v.searchTimeout');
+ var showRecentRecords = component.get('v.showRecentRecords');
+
+ clearTimeout(searchTimeout);
+
+ if ($A.util.isEmpty(searchTerm)) {
+ if (showRecentRecords) {
+ helper.setRecords(component, event, helper, component.get('v.recentRecords'));
+ } else {
+ helper.setRecords(component, event, helper, []);
+ }
+
+ return;
+ } else if (searchTerm == lastSearchTerm) {
+ component.set('v.searching', false);
+ helper.openMenu(component, event, helper);
+
+ return;
+ }
+
+ component.set('v.searching', true);
+
+ component.set('v.searchTimeout', setTimeout($A.getCallback(function() {
+ if (!component.isValid()) {
+ return;
+ }
+
+ var getRecordsAction = component.get('c.getRecords');
+ var params = helper.getParams(component, event, helper, true);
+
+ params.searchTerm = component.find('lookupInput').getElement().value;
+
+ getRecordsAction.setParams({
+ jsonString: JSON.stringify(params)
+ });
+
+ getRecordsAction.setCallback(this, function(res) {
+ if (res.getState() === 'SUCCESS') {
+ var returnValue = JSON.parse(res.getReturnValue());
+
+ if (returnValue.isSuccess && returnValue.results.searchTerm == component.find('lookupInput').getElement().value) {
+ var returnedRecords = [];
+
+ returnValue.results.data.forEach(function(record) {
+ returnedRecords.push({
+ label: record.label,
+ sublabel: record.sublabel,
+ value: record.value
+ });
+ });
+
+ helper.setRecords(component, event, helper, returnedRecords);
+ }
+ } else {
+ helper.setRecords(component, event, helper, []);
+ }
+ });
+
+ $A.enqueueAction(getRecordsAction);
+ }), 200));
+ },
+ setRecords: function(component, event, helper, returnedRecords) {
+ component.set('v.focusIndex', null);
+ component.set('v.lastSearchTerm', component.find('lookupInput').getElement().value);
+ component.set('v.records', returnedRecords);
+ component.set('v.searching', false);
+
+ helper.openMenu(component, event, helper);
+ },
+ openMenu: function(component, event, helper) {
+ var showRecentRecords = component.get('v.showRecentRecords') && !$A.util.isEmpty(component.get('v.recentRecords'));
+ component.set('v.openMenu', !component.get('v.disabled') && (!$A.util.isEmpty(component.get('v.lastSearchTerm')) || showRecentRecords));
+ },
+ closeMobileLookup: function(component, event, helper) {
+ $A.util.removeClass(component.find('lookup'), 'sl-lookup--open');
+ component.find('lookupInput').getElement().value = ''
+ },
+ updateValueByFocusIndex: function(component, event, helper) {
+ var focusIndex = component.get('v.focusIndex');
+
+ if (focusIndex == null) {
+ focusIndex = 0;
+ }
+
+ var records = component.get('v.records');
+
+ if (focusIndex < records.length) {
+ var tile = {
+ title: records[focusIndex].label,
+ subtitle: records[focusIndex].sublabel,
+ value: records[focusIndex].value
+ }
+
+ var tiles = component.get('v.selectedOptionTiles');
+ tiles.push(tile);
+ component.set('v.selectedOptionTiles', tiles);
+
+ var internalValue = component.get('v.internalValue');
+
+ if($A.util.isEmpty(internalValue)){
+ internalValue = records[focusIndex].value;
+ } else {
+ internalValue += ';' + records[focusIndex].value;
+ }
+
+ component.set('v.internalValue', internalValue);
+ component.set('v.value', internalValue);
+ component.find('lookupInput').getElement().value = '';
+ component.set('v.records',[]);
+ component.set('v.lastSearchTerm', '');
+
+ helper.closeMenu(component, event, helper);
+ } else if (focusIndex == records.length) {
+ helper.addNewRecord(component, event, helper);
+ }
+
+ helper.closeMobileLookup(component, event, helper);
+ },
+ addNewRecord: function(component, event, helper) {
+ if (!component.get('v.allowNewRecords')) {
+ return;
+ }
+
+ var addRecordEvent;
+ var overrideNewEvent = component.get('v.overrideNewEvent');
+
+ if (overrideNewEvent) {
+ addRecordEvent = component.getEvent('strike_evt_addNewRecord');
+ } else {
+ addRecordEvent = $A.get('e.force:createRecord');
+
+ addRecordEvent.setParams({
+ entityApiName: component.get('v.object')
+ });
+ }
+ addRecordEvent.fire();
+
+ helper.closeMenu(component, event, helper);
+ },
+ moveRecordFocusUp: function(component, event, helper) {
+ var openMenu = component.get('v.openMenu');
+
+ if (openMenu) {
+ var focusIndex = component.get('v.focusIndex');
+ var options = component.find('lookupMenu').getElement().getElementsByTagName('li');
+
+ if (focusIndex == null || focusIndex == 0) {
+ focusIndex = options.length - 1;
+ } else {
+ --focusIndex;
+ }
+
+ component.set('v.focusIndex', focusIndex);
+ }
+ },
+ moveRecordFocusDown: function(component, event, helper) {
+ var openMenu = component.get('v.openMenu');
+
+ if (openMenu) {
+ var focusIndex = component.get('v.focusIndex');
+ var options = component.find('lookupMenu').getElement().getElementsByTagName('li');
+
+ if (focusIndex == null || focusIndex == options.length - 1) {
+ focusIndex = 0;
+ } else {
+ ++focusIndex;
+ }
+
+ component.set('v.focusIndex', focusIndex);
+ }
+ },
+ removeOptionTile: function(component, event, sourceCmpValue) {
+
+ var currentOptionTiles = component.get('v.selectedOptionTiles');
+
+ var destroyedIndex;
+
+ currentOptionTiles.forEach(function(tileObj, index){
+ if(tileObj.value == sourceCmpValue){
+ destroyedIndex = index;
+ }
+ });
+
+ currentOptionTiles.splice(destroyedIndex, 1);
+ component.set('v.selectedOptionTiles', currentOptionTiles);
+ },
+ removeFromComponentValue: function(component, event, sourceCmpValue) {
+
+ var parentCmpValue = component.get('v.internalValue');
+
+
+ var valueArray = parentCmpValue.split(';');
+
+ var valueIndex = valueArray.indexOf(sourceCmpValue);
+
+ valueArray.splice(valueIndex, 1);
+
+ var newValue = valueArray.join(';');
+ component.set('v.internalValue', newValue);
+ component.set('v.value', newValue);
+ }
+})
diff --git a/aura/strike_multiLookup/strike_multiLookupRenderer.js b/aura/strike_multiLookup/strike_multiLookupRenderer.js
new file mode 100644
index 0000000..3b65170
--- /dev/null
+++ b/aura/strike_multiLookup/strike_multiLookupRenderer.js
@@ -0,0 +1,7 @@
+({
+ unrender: function(component) {
+ this.superUnrender();
+
+ window.removeEventListener('click', component.handleClick);
+ }
+})
diff --git a/aura/strike_multiSelectPicklist/strike_multiSelectPicklist.cmp b/aura/strike_multiSelectPicklist/strike_multiSelectPicklist.cmp
index 13c5ab2..3371fa0 100644
--- a/aura/strike_multiSelectPicklist/strike_multiSelectPicklist.cmp
+++ b/aura/strike_multiSelectPicklist/strike_multiSelectPicklist.cmp
@@ -27,8 +27,11 @@
-
+
+
+
+
@@ -54,7 +57,7 @@
-