Skip to content

Commit ed2c2eb

Browse files
author
Olivier Dufour
committed
223:add options with suggestion
1 parent 7c19315 commit ed2c2eb

File tree

2 files changed

+124
-34
lines changed

2 files changed

+124
-34
lines changed

addon/options.css

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,4 +154,30 @@ body {
154154

155155
.center-label {
156156
text-align: center;
157+
}
158+
.suggestions {
159+
border: 1px solid #999;
160+
border-top-width: 0;
161+
list-style: none;
162+
margin-top: 0;
163+
max-height: 143px;
164+
overflow-y: auto;
165+
padding-left: 0;
166+
width: calc(300px + 1rem);
167+
}
168+
169+
.suggestions li {
170+
padding: 0.5rem;
171+
}
172+
173+
.suggestion-active,
174+
.suggestions li:hover {
175+
background-color: #999;
176+
color: #444;
177+
cursor: pointer;
178+
font-weight: 700;
179+
}
180+
181+
.suggestions li:not(:last-of-type) {
182+
border-bottom: 1px solid #999;
157183
}

addon/options.js

Lines changed: 98 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ class OptionsTabSelector extends React.Component {
9090
{option: Option, props: {type: "option", title: "Custom favicon (org specific)", key: this.sfHost + "_customFavicon", values: ["blue", "green", "orange", "pink", "purple", "red", "yellow"]}},
9191
{option: CustomLinkOption, props: {title: "Custom links (org specific)", key: this.sfHost + "_orgLinks"}},
9292
{option: Option, props: {type: "number", title: "Number of flow version to keep", key: "clearOlderFlowsKeep", placeholder: "5 by default", default: 5}},
93+
{option: Option, props: {type: "number", title: "Height of popup menu", key: "popupHeight", placeholder: "600 by default", default: 600}},
94+
{option: Option, props: {type: "number", title: "Width of popup menu", key: "popupWidth", placeholder: "280 by default", default: 280}},
9395
]
9496
},
9597
{
@@ -107,13 +109,13 @@ class OptionsTabSelector extends React.Component {
107109
tabTitle: "Tab3",
108110
title: "Data Export",
109111
content: [
110-
{option: CSVSeparatorOption, props: {key: 1}},
112+
{option: Option, props: {type: "text", title: "csv file separator", key: "csvSeparator", suggestions: [",", ";", "|"], default: ","}},
111113
{option: Option, props: {type: "toggle", title: "Display Query Execution Time", key: "displayQueryPerformance", default: true}},
112114
{option: Option, props: {type: "toggle", title: "Use SObject context on Data Export", key: "useSObjectContextOnDataImportLink", default: true}},
113115
{option: Option, props: {type: "toggle", title: "Skip technical comlumns", key: "skipTechnicalColumns", default: true}},
114116
{option: Option, props: {type: "toggle", title: "convert date to local timezone", key: "convertToLocalTime", default: true}},
115-
{option: Option, props: {type: "text", title: "Date format", key: "dateFormat", placeholder: "yyyy-MM-dd"}},
116-
{option: Option, props: {type: "text", title: "Date time format", key: "datetimeFormat", placeholder: "yyyy-MM-ddTHH:mm:ss.SSS+/-HH:mm"}},
117+
{option: Option, props: {type: "text", title: "Date format", key: "dateFormat", suggestions: ["yyyy-MM-dd", "dd/MM/yyyy", "MM/dd/yyyy"]}},
118+
{option: Option, props: {type: "text", title: "Date time format", key: "datetimeFormat", suggestions: ["yyyy-MM-ddTHH:mm:ss.SSS+/-HH:mm", "dd/MM/yyyy HH:mm:ss.SSS+/-HH:mm"]}},
117119
{option: Option, props: {type: "option", title: "Decimal format", key: "decimalFormat", default: ".", values: [".", ","]}},
118120
{option: QueryTemplatesOption, props: {title: "Query Templates", key: "queryTemplates", placeholder: "SELECT..."}},
119121
{option: QueryTemplatesOption, props: {title: "Saved Query History", key: "insextSavedQueryHistory", node: "query", withName: true, defaultValue: "{\"useToolingApi\": false}", placeholder: "SELECT..."}}
@@ -306,37 +308,116 @@ class Option extends React.Component {
306308
super(props);
307309
this.onChange = this.onChange.bind(this);
308310
this.onChangeToggle = this.onChangeToggle.bind(this);
311+
this.onBlur = this.onBlur.bind(this);
312+
this.onFocus = this.onFocus.bind(this);
313+
this.onKeyDown = this.onKeyDown.bind(this);
314+
this.onSuggestionClick = this.onSuggestionClick.bind(this);
315+
this.onChange = this.onChange.bind(this);
309316
this.key = props.storageKey;
310317
this.type = props.type;
311318
this.label = props.label;
312319
this.placeholder = props.placeholder;
313320
let value = localStorage.getItem(this.key);
314321
if (props.default !== undefined && value === null) {
315-
value = JSON.stringify(props.default);
322+
if (props.default instanceof String) {
323+
value = props.default;
324+
} else {
325+
value = JSON.stringify(props.default);
326+
}
316327
localStorage.setItem(this.key, value);
317328
}
318-
this.state = {[this.key]: this.type == "toggle" ? !!JSON.parse(value) : value};
329+
this.state = {activeSuggestion: 0,
330+
filteredSuggestions: [],
331+
showSuggestions: false,
332+
[this.key]: this.type == "toggle" ? !!JSON.parse(value) : value};
319333
this.title = props.title;
320334
}
321-
335+
onFocus() {
336+
let {suggestions} = this.props;
337+
this.setState({
338+
activeSuggestion: 0,
339+
filteredSuggestions: suggestions,
340+
showSuggestions: true
341+
});
342+
}
343+
onBlur() {
344+
setTimeout(() => {
345+
//no need to refresh if already refresh by click on value
346+
if (!this.state || !this.state.showSuggestions) {
347+
return;
348+
}
349+
this.setState({
350+
activeSuggestion: 0,
351+
filteredSuggestions: [],
352+
showSuggestions: false
353+
});
354+
}, 100); // Set timeout for 500ms
355+
}
356+
onSuggestionClick(e) {
357+
this.setState({
358+
activeSuggestion: 0,
359+
filteredSuggestions: [],
360+
showSuggestions: false
361+
});
362+
this.setState({[this.key]: e.target.innerText});
363+
localStorage.setItem(this.key, e.target.innerText);
364+
}
365+
onKeyDown(e){
366+
const {activeSuggestion, filteredSuggestions} = this.state;
367+
switch (e.keyCode) {
368+
case 40:
369+
if (activeSuggestion - 1 === filteredSuggestions.length) {
370+
return;
371+
}
372+
this.setState({activeSuggestion: activeSuggestion + 1});
373+
break;
374+
case 38:
375+
if (activeSuggestion === 0) {
376+
return;
377+
}
378+
this.setState({activeSuggestion: activeSuggestion - 1});
379+
break;
380+
case 13:
381+
this.setState({
382+
activeSuggestion: 0,
383+
showSuggestions: false
384+
});
385+
this.setState({[this.key]: filteredSuggestions[activeSuggestion]});
386+
localStorage.setItem(this.key, filteredSuggestions[activeSuggestion]);
387+
e.preventDefault();
388+
break;
389+
}
390+
}
322391
onChangeToggle(e) {
323392
const enabled = e.target.checked;
324393
this.setState({[this.key]: enabled});
325394
localStorage.setItem(this.key, JSON.stringify(enabled));
326395
}
327396

328397
onChange(e) {
398+
let {suggestions} = this.props;
329399
let inputValue = e.target.value;
330400
this.setState({[this.key]: inputValue});
331401
if (this.type == "option" && inputValue == this.props.values[0]) {
332402
localStorage.removeItem(this.key);
333403
return;
334404
}
335405
localStorage.setItem(this.key, inputValue);
406+
const filteredSuggestions = suggestions.filter(
407+
suggestion =>
408+
suggestion.toLowerCase().indexOf(inputValue.toLowerCase()) > -1
409+
);
410+
411+
this.setState({
412+
activeSuggestion: 0,
413+
filteredSuggestions,
414+
showSuggestions: true
415+
});
336416
}
337417

338418
render() {
339419
const id = this.key;
420+
let {activeSuggestion, filteredSuggestions, showSuggestions} = this.state;
340421
if (this.type == "toggle") {
341422
return h("div", {className: "slds-grid slds-border_bottom slds-p-horizontal_small slds-p-vertical_xx-small"},
342423
h("div", {className: "slds-col slds-size_4-of-12 text-align-middle"},
@@ -361,7 +442,17 @@ class Option extends React.Component {
361442
),
362443
h("div", {className: "slds-col slds-size_2-of-12 slds-form-element slds-grid slds-grid_align-end slds-grid_vertical-align-center slds-gutters_small"},
363444
h("div", {className: "slds-form-element__control slds-col slds-size_6-of-12"},
364-
h("input", {type: this.type, id: "restHeaderInput", className: "slds-input", placeholder: this.placeholder, value: cleanInputValue(this.state[this.key]), onChange: this.onChange}),
445+
h("input", {type: this.type, id: "restHeaderInput", className: "slds-input", placeholder: this.placeholder, value: cleanInputValue(this.state[this.key]), onChange: this.onChange, onFocus: this.onFocus, onBlur: this.onBlur, onKeyDown: this.onKeyDown}),
446+
(showSuggestions && filteredSuggestions.length)
447+
? h("ul", {className: "suggestions"},
448+
filteredSuggestions.map((suggestion, index) => {
449+
let SuggestionClass;
450+
if (index === activeSuggestion) {
451+
SuggestionClass = "suggestion-active";
452+
}
453+
return h("li", {className: SuggestionClass, key: suggestion, onMouseDown: this.onSuggestionClick}, suggestion);
454+
})
455+
) : ""
365456
)
366457
)
367458
);
@@ -414,33 +505,6 @@ class APIKeyOption extends React.Component {
414505
}
415506
}
416507

417-
class CSVSeparatorOption extends React.Component {
418-
419-
constructor(props) {
420-
super(props);
421-
this.onChangeCSVSeparator = this.onChangeCSVSeparator.bind(this);
422-
this.state = {csvSeparator: localStorage.getItem("csvSeparator") ? localStorage.getItem("csvSeparator") : ","};
423-
}
424-
425-
onChangeCSVSeparator(e) {
426-
let csvSeparator = e.target.value;
427-
this.setState({csvSeparator});
428-
localStorage.setItem("csvSeparator", csvSeparator);
429-
}
430-
431-
render() {
432-
return h("div", {className: "slds-grid slds-border_bottom slds-p-horizontal_small slds-p-vertical_xx-small"},
433-
h("div", {className: "slds-col slds-size_4-of-12 text-align-middle"},
434-
h("span", {}, "CSV Separator")
435-
),
436-
h("div", {className: "slds-col slds-size_7-of-12 slds-form-element slds-grid slds-grid_align-end slds-grid_vertical-align-center slds-gutters_small"}),
437-
h("div", {className: "slds-col slds-size_1-of-12 slds-form-element slds-grid slds-grid_align-end slds-grid_vertical-align-center slds-gutters_small"},
438-
h("input", {type: "text", id: "csvSeparatorInput", className: "slds-input slds-text-align_right slds-m-right_small", placeholder: "CSV Separator", value: cleanInputValue(this.state.csvSeparator), onChange: this.onChangeCSVSeparator})
439-
)
440-
);
441-
}
442-
}
443-
444508
class enableLogsOption extends React.Component {
445509

446510
constructor(props) {

0 commit comments

Comments
 (0)