Skip to content

Commit c1fd2f3

Browse files
feat: replace source currency native select with custom dropdown
Show all currencies in the source selector using the same dropdown UI as the destination selector. Supported currencies (USD, EUR, GBP, CAD, SGD, HKD) appear at the top; remaining currencies are grouped below a "Coming soon" divider in a disabled state. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 7ea358d commit c1fd2f3

2 files changed

Lines changed: 104 additions & 29 deletions

File tree

mintlify/platform-overview/core-concepts/sample-rates.mdx

Lines changed: 73 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -218,9 +218,14 @@ export const ProviderTh = ({ name }) => {
218218
return <img src={src} alt={name} className="rate-explorer-provider-logo" />;
219219
};
220220

221-
export const DropdownList = ({ currencies, highlightIdx, setHighlightIdx, addCurrency, dropdownQuery, setDropdownQuery, setShowDropdown }) => {
221+
export const DropdownList = ({ currencies, highlightIdx, setHighlightIdx, addCurrency, dropdownQuery, setDropdownQuery, setShowDropdown, disabledCodes }) => {
222222
const listRef = React.useRef(null);
223223
const [scrollState, setScrollState] = React.useState('top');
224+
const disabled = disabledCodes || new Set();
225+
const hasDisabled = disabled.size > 0;
226+
const enabledItems = hasDisabled ? currencies.filter(c => !disabled.has(c.code)) : currencies;
227+
const disabledItems = hasDisabled ? currencies.filter(c => disabled.has(c.code)) : [];
228+
const sortedCurrencies = hasDisabled ? [...enabledItems, ...disabledItems] : currencies;
224229

225230
const updateScroll = React.useCallback(() => {
226231
const el = listRef.current;
@@ -259,14 +264,16 @@ export const DropdownList = ({ currencies, highlightIdx, setHighlightIdx, addCur
259264
onKeyDown={(e) => {
260265
if (e.key === 'ArrowDown') {
261266
e.preventDefault();
262-
setHighlightIdx(i => Math.min(i + 1, currencies.length - 1));
267+
setHighlightIdx(i => Math.min(i + 1, sortedCurrencies.length - 1));
263268
} else if (e.key === 'ArrowUp') {
264269
e.preventDefault();
265270
setHighlightIdx(i => Math.max(i - 1, -1));
266-
} else if (e.key === 'Enter' && currencies.length > 0) {
271+
} else if (e.key === 'Enter' && sortedCurrencies.length > 0) {
267272
e.preventDefault();
268273
const idx = highlightIdx >= 0 ? highlightIdx : 0;
269-
addCurrency(currencies[idx].code);
274+
if (!disabled.has(sortedCurrencies[idx].code)) {
275+
addCurrency(sortedCurrencies[idx].code);
276+
}
270277
} else if (e.key === 'Escape') {
271278
setShowDropdown(false);
272279
setDropdownQuery('');
@@ -281,14 +288,33 @@ export const DropdownList = ({ currencies, highlightIdx, setHighlightIdx, addCur
281288
onScroll={updateScroll}
282289
style={mask ? { WebkitMaskImage: mask, maskImage: mask } : undefined}
283290
>
284-
{currencies.map((c, idx) => (
285-
<button key={c.code} className={'rate-explorer-dropdown-item' + (idx === highlightIdx ? ' rate-explorer-dropdown-item-active' : '')} onClick={() => addCurrency(c.code)} onMouseEnter={() => setHighlightIdx(idx)}>
291+
{enabledItems.map((c, idx) => (
292+
<button
293+
key={c.code}
294+
className={'rate-explorer-dropdown-item' + (idx === highlightIdx ? ' rate-explorer-dropdown-item-active' : '')}
295+
onClick={() => addCurrency(c.code)}
296+
onMouseEnter={() => setHighlightIdx(idx)}
297+
>
286298
<img src={flagUrl(c.code)} width="20" height="20" alt="" className="rate-explorer-flag-img" />
287299
<span className="rate-explorer-dropdown-code">{c.code}</span>
288300
<span className="rate-explorer-dropdown-name">{c.name}</span>
289301
</button>
290302
))}
291-
{currencies.length === 0 && (
303+
{disabledItems.length > 0 && (
304+
<div className="rate-explorer-dropdown-divider">Coming soon</div>
305+
)}
306+
{disabledItems.map((c, idx) => (
307+
<button
308+
key={c.code}
309+
className={'rate-explorer-dropdown-item rate-explorer-dropdown-item-disabled' + ((idx + enabledItems.length) === highlightIdx ? ' rate-explorer-dropdown-item-active' : '')}
310+
onMouseEnter={() => setHighlightIdx(idx + enabledItems.length)}
311+
>
312+
<img src={flagUrl(c.code)} width="20" height="20" alt="" className="rate-explorer-flag-img" />
313+
<span className="rate-explorer-dropdown-code">{c.code}</span>
314+
<span className="rate-explorer-dropdown-name">{c.name}</span>
315+
</button>
316+
))}
317+
{sortedCurrencies.length === 0 && (
292318
<div className="rate-explorer-dropdown-empty">No currencies found</div>
293319
)}
294320
</div>
@@ -414,10 +440,14 @@ export const RateExplorer = () => {
414440
} catch (e) {}
415441
return null;
416442
});
443+
const [showSourceDropdown, setShowSourceDropdown] = React.useState(false);
444+
const [sourceDropdownQuery, setSourceDropdownQuery] = React.useState('');
445+
const [sourceHighlightIdx, setSourceHighlightIdx] = React.useState(-1);
417446
const [debouncedAmount, setDebouncedAmount] = React.useState(1000);
418447
const [isVisible, setIsVisible] = React.useState(false);
419448
const containerRef = React.useRef(null);
420449
const dropdownRef = React.useRef(null);
450+
const sourceDropdownRef = React.useRef(null);
421451
const explorerRef = React.useRef(null);
422452
const headerBarRef = React.useRef(null);
423453

@@ -517,6 +547,10 @@ export const RateExplorer = () => {
517547
setShowDropdown(false);
518548
setDropdownQuery('');
519549
}
550+
if (sourceDropdownRef.current && !sourceDropdownRef.current.contains(e.target)) {
551+
setShowSourceDropdown(false);
552+
setSourceDropdownQuery('');
553+
}
520554
};
521555
document.addEventListener('mousedown', handleClick);
522556
return () => document.removeEventListener('mousedown', handleClick);
@@ -716,6 +750,20 @@ export const RateExplorer = () => {
716750
!dropdownQuery || c.code.toLowerCase().includes(dropdownQuery.toLowerCase()) || c.name.toLowerCase().includes(dropdownQuery.toLowerCase())
717751
);
718752

753+
const sourceDropdownCurrencies = CURRENCIES.filter(c =>
754+
!sourceDropdownQuery || c.code.toLowerCase().includes(sourceDropdownQuery.toLowerCase()) || c.name.toLowerCase().includes(sourceDropdownQuery.toLowerCase())
755+
);
756+
757+
const disabledSourceCodes = new Set(
758+
CURRENCIES.filter(c => !SOURCE_CODES.includes(c.code)).map(c => c.code)
759+
);
760+
761+
const selectSource = (code) => {
762+
setSourceCurrency(code);
763+
setShowSourceDropdown(false);
764+
setSourceDropdownQuery('');
765+
};
766+
719767
return (
720768
<div ref={containerRef}>
721769
<div ref={explorerRef} className="rate-explorer not-prose">
@@ -733,19 +781,24 @@ export const RateExplorer = () => {
733781
onFocus={handleAmountFocus}
734782
/>
735783
</div>
736-
<div className="rate-explorer-source-trigger">
737-
<img src={flagUrl(sourceCurrency)} width="16" height="16" alt="" className="rate-explorer-flag-img" />
738-
<select
739-
className="rate-explorer-source-select"
740-
value={sourceCurrency}
741-
onChange={(e) => setSourceCurrency(e.target.value)}
742-
>
743-
{SOURCE_OPTIONS.map((opt) => (
744-
<option key={opt.value} value={opt.value}>{opt.label}</option>
745-
))}
746-
</select>
747-
<span className="rate-explorer-source-label">{sourceCurrency}</span>
748-
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" className="rate-explorer-chevron"><path d="M3 4.5L6 7.5L9 4.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/></svg>
784+
<div className="rate-explorer-dropdown-anchor" ref={sourceDropdownRef}>
785+
<button className="rate-explorer-source-trigger" onClick={() => { setShowSourceDropdown(!showSourceDropdown); setSourceHighlightIdx(-1); }}>
786+
<img src={flagUrl(sourceCurrency)} width="16" height="16" alt="" className="rate-explorer-flag-img" />
787+
<span className="rate-explorer-source-label">{sourceCurrency}</span>
788+
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" className="rate-explorer-chevron"><path d="M3 4.5L6 7.5L9 4.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/></svg>
789+
</button>
790+
{showSourceDropdown && (
791+
<DropdownList
792+
currencies={sourceDropdownCurrencies}
793+
highlightIdx={sourceHighlightIdx}
794+
setHighlightIdx={setSourceHighlightIdx}
795+
addCurrency={selectSource}
796+
dropdownQuery={sourceDropdownQuery}
797+
setDropdownQuery={setSourceDropdownQuery}
798+
setShowDropdown={setShowSourceDropdown}
799+
disabledCodes={disabledSourceCodes}
800+
/>
801+
)}
749802
</div>
750803
</div>
751804
<div className="rate-explorer-header-mid">

mintlify/style.css

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4244,7 +4244,6 @@ html.dark .rate-explorer-input {
42444244

42454245
/* ---------- Source currency trigger ---------- */
42464246
.rate-explorer-source-trigger {
4247-
position: relative;
42484247
display: flex;
42494248
align-items: center;
42504249
gap: 8px;
@@ -4254,21 +4253,15 @@ html.dark .rate-explorer-input {
42544253
border: 0.5px solid var(--ls-black-10);
42554254
border-radius: var(--ls-radius-sm);
42564255
cursor: pointer;
4256+
font-family: "Suisse Intl", sans-serif;
4257+
font-feature-settings: "salt" 1;
42574258
}
42584259

42594260
html.dark .rate-explorer-source-trigger {
42604261
background: var(--ls-gray-850);
42614262
border-color: var(--ls-white-06);
42624263
}
42634264

4264-
.rate-explorer-source-select {
4265-
position: absolute;
4266-
inset: 0;
4267-
opacity: 0;
4268-
cursor: pointer;
4269-
font-size: 14px;
4270-
}
4271-
42724265
.rate-explorer-source-label {
42734266
font-size: 14px;
42744267
font-weight: 500;
@@ -4570,6 +4563,35 @@ html.dark .rate-explorer-dropdown-item-active {
45704563
text-align: center;
45714564
}
45724565

4566+
.rate-explorer-dropdown-item-disabled {
4567+
opacity: 0.45;
4568+
cursor: default;
4569+
}
4570+
4571+
.rate-explorer-dropdown-item-disabled:hover {
4572+
background: none;
4573+
}
4574+
4575+
html.dark .rate-explorer-dropdown-item-disabled:hover {
4576+
background: none;
4577+
}
4578+
4579+
.rate-explorer-dropdown-divider {
4580+
font-size: 11px;
4581+
font-weight: 500;
4582+
color: var(--ls-gray-400);
4583+
padding: 8px 12px 4px;
4584+
font-family: "Suisse Intl", sans-serif;
4585+
font-feature-settings: "salt" 1;
4586+
border-top: 0.5px solid var(--ls-black-06);
4587+
margin-top: 4px;
4588+
}
4589+
4590+
html.dark .rate-explorer-dropdown-divider {
4591+
color: var(--ls-gray-500);
4592+
border-color: var(--ls-white-06);
4593+
}
4594+
45734595
/* ---------- View toggle ---------- */
45744596
.rate-explorer-toggle {
45754597
display: flex;

0 commit comments

Comments
 (0)