@deriv-web-design/ui 0.1.34 → 0.1.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -52,10 +52,16 @@ __export(index_exports, {
52
52
  CryptoMarketsTable: () => CryptoMarketsTable,
53
53
  CryptoTradeConverter: () => CryptoTradeConverter,
54
54
  DEFAULT_AWARDS: () => DEFAULT_AWARDS,
55
+ DEFAULT_CRYPTO_MARKETS_TABLE_LABELS: () => DEFAULT_CRYPTO_MARKETS_TABLE_LABELS,
56
+ DEFAULT_CRYPTO_MARKETS_TABLE_TITLE: () => DEFAULT_CRYPTO_MARKETS_TABLE_TITLE,
57
+ DEFAULT_CRYPTO_TRADE_CONVERTER_LABELS: () => DEFAULT_CRYPTO_TRADE_CONVERTER_LABELS,
58
+ DEFAULT_CRYPTO_TRADE_CONVERTER_SUBTITLE: () => DEFAULT_CRYPTO_TRADE_CONVERTER_SUBTITLE,
59
+ DEFAULT_CRYPTO_TRADE_CONVERTER_TITLE: () => DEFAULT_CRYPTO_TRADE_CONVERTER_TITLE,
55
60
  DEFAULT_CULTURE_CARDS: () => DEFAULT_CULTURE_CARDS,
56
61
  DEFAULT_EXCHANGE_RATE_WS_URL: () => DEFAULT_EXCHANGE_RATE_WS_URL,
57
62
  DEFAULT_SYMBOLS_ENDPOINT: () => DEFAULT_SYMBOLS_ENDPOINT,
58
63
  DEFAULT_TRADE_HREF: () => DEFAULT_TRADE_HREF,
64
+ DEFAULT_TRADING_CALCULATOR_LABELS: () => DEFAULT_TRADING_CALCULATOR_LABELS,
59
65
  DayNightTransition: () => DayNightTransition,
60
66
  DocumentChips: () => DocumentChips,
61
67
  DotLottieInView: () => DotLottieInView,
@@ -8321,7 +8327,55 @@ var ParallaxBanner = ({
8321
8327
 
8322
8328
  // templates/TradingCalculator/TradingCalculator.tsx
8323
8329
  var import_react34 = require("react");
8330
+
8331
+ // templates/TradingCalculator/TradingCalculator.types.ts
8332
+ var DEFAULT_TRADING_CALCULATOR_LABELS = {
8333
+ calculatorPanel: "Calculator",
8334
+ resultsPanel: "Results",
8335
+ calculate: "Calculate",
8336
+ calculating: "Calculating\u2026",
8337
+ reset: "Reset",
8338
+ symbols: "Symbols",
8339
+ volume: "Volume in Lots",
8340
+ assetPrice: "Asset price",
8341
+ contractSize: "Contract size",
8342
+ pipSize: "Pip size",
8343
+ effectiveLeverage: "Effective Leverage",
8344
+ pointSize: "Point size",
8345
+ marginRequired: "Margin Required",
8346
+ tradeVolume: "Trade Volume",
8347
+ pipValue: "Pip value",
8348
+ swapChargeLong: "Swap Charge Long",
8349
+ swapChargeShort: "Swap Charge Short",
8350
+ swapLongRate: "Swap long rate",
8351
+ swapShortRate: "Swap short rate",
8352
+ swapCalculationType: "Swap calculation type",
8353
+ threeDaySwap: "Three-day swap",
8354
+ weekendSwaps: "Weekend swaps",
8355
+ threeDaySwapFallback: "No",
8356
+ loadingSymbols: "Loading symbols\u2026",
8357
+ symbolsUnavailable: "Symbols unavailable",
8358
+ searchSymbolPlaceholder: "Search symbol, e.g. EURUSD",
8359
+ symbolsLoadError: "Unable to load symbols.",
8360
+ retry: "Retry",
8361
+ selectSymbolError: "Select a symbol to continue.",
8362
+ volumeRequiredError: "Enter a valid volume in lots.",
8363
+ volumePositiveError: "Volume must be greater than 0.",
8364
+ volumeRangeError: "Volume must be between {min} and {max} lots.",
8365
+ assetPriceError: "Enter a valid asset price.",
8366
+ exchangeRateError: "Unable to fetch live exchange rates. Please try again.",
8367
+ exchangeRateErrorWithMessage: "Unable to fetch live exchange rates ({message}). Please try again.",
8368
+ noSymbolsMatch: 'No symbols match "{query}"'
8369
+ };
8370
+
8371
+ // templates/TradingCalculator/TradingCalculator.tsx
8324
8372
  var import_jsx_runtime60 = require("react/jsx-runtime");
8373
+ function fillTemplate2(template, values) {
8374
+ return template.replace(/\{(\w+)\}/g, (_, key) => values[key] ?? "");
8375
+ }
8376
+ function mergeLabels(overrides) {
8377
+ return { ...DEFAULT_TRADING_CALCULATOR_LABELS, ...overrides };
8378
+ }
8325
8379
  var DEFAULT_SYMBOLS_ENDPOINT = "https://deriv-static-pricingfeedv2.firebaseio.com/n8n/row/prod/dynamic_trading_specs.json";
8326
8380
  var DEFAULT_EXCHANGE_RATE_WS_URL = "wss://ws.derivws.com/websockets/v3?app_id=1089";
8327
8381
  var USD = "USD";
@@ -8481,7 +8535,15 @@ function groupByAssetClass(symbols) {
8481
8535
  }
8482
8536
  return [...groups.entries()];
8483
8537
  }
8484
- var SymbolCombobox = ({ symbolsState, onRetry, selected, onSelect, error, inputId }) => {
8538
+ var SymbolCombobox = ({
8539
+ symbolsState,
8540
+ onRetry,
8541
+ selected,
8542
+ onSelect,
8543
+ error,
8544
+ inputId,
8545
+ copy
8546
+ }) => {
8485
8547
  const [query, setQuery] = (0, import_react34.useState)("");
8486
8548
  const [isOpen, setIsOpen] = (0, import_react34.useState)(false);
8487
8549
  const [activeIndex, setActiveIndex] = (0, import_react34.useState)(-1);
@@ -8539,7 +8601,8 @@ var SymbolCombobox = ({ symbolsState, onRetry, selected, onSelect, error, inputI
8539
8601
  {
8540
8602
  id: inputId,
8541
8603
  label: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(import_jsx_runtime60.Fragment, { children: [
8542
- "Symbols ",
8604
+ copy.symbols,
8605
+ " ",
8543
8606
  /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: "tcalc-required", children: "*" })
8544
8607
  ] }),
8545
8608
  role: "combobox",
@@ -8554,7 +8617,7 @@ var SymbolCombobox = ({ symbolsState, onRetry, selected, onSelect, error, inputI
8554
8617
  disabled: isLoading || isError,
8555
8618
  hideClearButton: true,
8556
8619
  trailingSlot: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(ChevronIcon3, {}),
8557
- placeholder: isLoading ? "Loading symbols\u2026" : isError ? "Symbols unavailable" : "Search symbol, e.g. EURUSD",
8620
+ placeholder: isLoading ? copy.loadingSymbols : isError ? copy.symbolsUnavailable : copy.searchSymbolPlaceholder,
8558
8621
  value: displayValue,
8559
8622
  onFocus: () => setIsOpen(true),
8560
8623
  onChange: (event) => {
@@ -8566,11 +8629,7 @@ var SymbolCombobox = ({ symbolsState, onRetry, selected, onSelect, error, inputI
8566
8629
  }
8567
8630
  ),
8568
8631
  isOpen && !isLoading && !isError && /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "tcalc-dropdown", id: listboxId, role: "listbox", children: [
8569
- grouped.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("p", { className: "tcalc-dropdownEmpty", children: [
8570
- 'No symbols match "',
8571
- query,
8572
- '"'
8573
- ] }),
8632
+ grouped.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("p", { className: "tcalc-dropdownEmpty", children: fillTemplate2(copy.noSymbolsMatch, { query }) }),
8574
8633
  grouped.map(([assetClass, items]) => /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "tcalc-dropdownGroup", children: [
8575
8634
  /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("p", { className: "tcalc-dropdownGroupTitle", children: assetClass }),
8576
8635
  items.map((item) => {
@@ -8602,9 +8661,10 @@ var SymbolCombobox = ({ symbolsState, onRetry, selected, onSelect, error, inputI
8602
8661
  ] }),
8603
8662
  isError && /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("p", { className: "tcalc-fieldError", children: [
8604
8663
  /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(ErrorIcon, {}),
8605
- " Unable to load symbols.",
8606
8664
  " ",
8607
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("button", { type: "button", className: "tcalc-retryLink", onClick: onRetry, children: "Retry" })
8665
+ copy.symbolsLoadError,
8666
+ " ",
8667
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("button", { type: "button", className: "tcalc-retryLink", onClick: onRetry, children: copy.retry })
8608
8668
  ] }),
8609
8669
  !isError && error && /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("p", { className: "tcalc-fieldError", children: [
8610
8670
  /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(ErrorIcon, {}),
@@ -8632,9 +8692,11 @@ var TradingCalculator = ({
8632
8692
  symbolsEndpoint = DEFAULT_SYMBOLS_ENDPOINT,
8633
8693
  exchangeRateWsUrl = DEFAULT_EXCHANGE_RATE_WS_URL,
8634
8694
  defaultSymbol,
8695
+ labels: labelsProp,
8635
8696
  className,
8636
8697
  ...props
8637
8698
  }) => {
8699
+ const copy = mergeLabels(labelsProp);
8638
8700
  const showMargin = variant === "main" || variant === "margin";
8639
8701
  const showPip = variant === "main" || variant === "pip";
8640
8702
  const showSwap = variant === "main" || variant === "swap";
@@ -8678,15 +8740,18 @@ var TradingCalculator = ({
8678
8740
  const volume = Number.parseFloat(volumeInLots);
8679
8741
  const price = Number.parseFloat(assetPrice);
8680
8742
  const nextErrors = {};
8681
- if (!selectedSymbol) nextErrors.symbol = "Select a symbol to continue.";
8743
+ if (!selectedSymbol) nextErrors.symbol = copy.selectSymbolError;
8682
8744
  if (!volumeInLots || Number.isNaN(volume)) {
8683
- nextErrors.volume = "Enter a valid volume in lots.";
8745
+ nextErrors.volume = copy.volumeRequiredError;
8684
8746
  } else if (volume <= 0) {
8685
- nextErrors.volume = "Volume must be greater than 0.";
8747
+ nextErrors.volume = copy.volumePositiveError;
8686
8748
  } else if (selectedSymbol && (volume < selectedSymbol.volume_min || volume > selectedSymbol.volume_limit)) {
8687
- nextErrors.volume = `Volume must be between ${selectedSymbol.volume_min} and ${selectedSymbol.volume_limit} lots.`;
8749
+ nextErrors.volume = fillTemplate2(copy.volumeRangeError, {
8750
+ min: String(selectedSymbol.volume_min),
8751
+ max: String(selectedSymbol.volume_limit)
8752
+ });
8688
8753
  }
8689
- if (!assetPrice || Number.isNaN(price) || price <= 0) nextErrors.price = "Enter a valid asset price.";
8754
+ if (!assetPrice || Number.isNaN(price) || price <= 0) nextErrors.price = copy.assetPriceError;
8690
8755
  setErrors(nextErrors);
8691
8756
  if (Object.keys(nextErrors).length > 0 || !selectedSymbol) return;
8692
8757
  setCalcError(null);
@@ -8697,7 +8762,7 @@ var TradingCalculator = ({
8697
8762
  setResults(computeResults(selectedSymbol, volume, price, rates));
8698
8763
  } catch (error) {
8699
8764
  setCalcError(
8700
- error instanceof Error ? `Unable to fetch live exchange rates (${error.message}). Please try again.` : "Unable to fetch live exchange rates. Please try again."
8765
+ error instanceof Error ? fillTemplate2(copy.exchangeRateErrorWithMessage, { message: error.message }) : copy.exchangeRateError
8701
8766
  );
8702
8767
  } finally {
8703
8768
  setIsCalculating(false);
@@ -8708,7 +8773,7 @@ var TradingCalculator = ({
8708
8773
  const priceInputId = (0, import_react34.useId)();
8709
8774
  return /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("section", { className: ["tcalc-section", className ?? ""].filter(Boolean).join(" "), ...props, children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { className: "tcalc-container", children: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "tcalc-card", children: [
8710
8775
  /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "tcalc-column", children: [
8711
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("h3", { className: "tcalc-panelTitle", children: "Calculator" }),
8776
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("h3", { className: "tcalc-panelTitle", children: copy.calculatorPanel }),
8712
8777
  /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
8713
8778
  SymbolCombobox,
8714
8779
  {
@@ -8717,20 +8782,21 @@ var TradingCalculator = ({
8717
8782
  selected: selectedSymbol,
8718
8783
  onSelect: handleSelectSymbol,
8719
8784
  error: errors.symbol,
8720
- inputId: symbolInputId
8785
+ inputId: symbolInputId,
8786
+ copy
8721
8787
  }
8722
8788
  ),
8723
8789
  /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "tcalc-infoGrid", children: [
8724
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(InfoRow, { label: "Contract size", value: selectedSymbol ? integerFormatter.format(selectedSymbol.contract_size) : "-" }),
8725
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(InfoRow, { label: "Pip size", value: selectedSymbol ? String(selectedSymbol.pip_size) : "-" }),
8790
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(InfoRow, { label: copy.contractSize, value: selectedSymbol ? integerFormatter.format(selectedSymbol.contract_size) : "-" }),
8791
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(InfoRow, { label: copy.pipSize, value: selectedSymbol ? String(selectedSymbol.pip_size) : "-" }),
8726
8792
  /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
8727
8793
  InfoRow,
8728
8794
  {
8729
- label: "Effective Leverage",
8795
+ label: copy.effectiveLeverage,
8730
8796
  value: selectedSymbol ? `1 : ${Math.round((selectedSymbol.effective_leverage + Number.EPSILON) * 100) / 100}` : "-"
8731
8797
  }
8732
8798
  ),
8733
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(InfoRow, { label: "Point size", value: selectedSymbol ? String(selectedSymbol.point_size) : "-" })
8799
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(InfoRow, { label: copy.pointSize, value: selectedSymbol ? String(selectedSymbol.point_size) : "-" })
8734
8800
  ] }),
8735
8801
  /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "tcalc-field", children: [
8736
8802
  /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
@@ -8739,7 +8805,8 @@ var TradingCalculator = ({
8739
8805
  id: volumeInputId,
8740
8806
  className: "tcalc-numberField",
8741
8807
  label: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(import_jsx_runtime60.Fragment, { children: [
8742
- "Volume in Lots ",
8808
+ copy.volume,
8809
+ " ",
8743
8810
  /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: "tcalc-required", children: "*" })
8744
8811
  ] }),
8745
8812
  status: errors.volume ? "fail" : "neutral",
@@ -8756,7 +8823,7 @@ var TradingCalculator = ({
8756
8823
  const isNegative = value !== "" && !Number.isNaN(parsed) && parsed <= 0;
8757
8824
  setErrors((prev) => ({
8758
8825
  ...prev,
8759
- volume: isNegative ? "Volume must be greater than 0." : void 0
8826
+ volume: isNegative ? copy.volumePositiveError : void 0
8760
8827
  }));
8761
8828
  }
8762
8829
  }
@@ -8774,7 +8841,8 @@ var TradingCalculator = ({
8774
8841
  id: priceInputId,
8775
8842
  className: "tcalc-numberField",
8776
8843
  label: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(import_jsx_runtime60.Fragment, { children: [
8777
- "Asset price ",
8844
+ copy.assetPrice,
8845
+ " ",
8778
8846
  /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: "tcalc-required", children: "*" })
8779
8847
  ] }),
8780
8848
  status: errors.price ? "fail" : "neutral",
@@ -8803,7 +8871,7 @@ var TradingCalculator = ({
8803
8871
  type: "button",
8804
8872
  colorScheme: "coral",
8805
8873
  variant: "primary",
8806
- label: isCalculating ? "Calculating\u2026" : "Calculate",
8874
+ label: isCalculating ? copy.calculating : copy.calculate,
8807
8875
  onClick: handleCalculate,
8808
8876
  disabled: isCalculating,
8809
8877
  fullWidth: true
@@ -8815,7 +8883,7 @@ var TradingCalculator = ({
8815
8883
  type: "button",
8816
8884
  colorScheme: "black",
8817
8885
  variant: "secondary",
8818
- label: "Reset",
8886
+ label: copy.reset,
8819
8887
  onClick: handleReset,
8820
8888
  fullWidth: true
8821
8889
  }
@@ -8823,16 +8891,16 @@ var TradingCalculator = ({
8823
8891
  ] })
8824
8892
  ] }),
8825
8893
  /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "tcalc-column tcalc-column--results", children: [
8826
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("h3", { className: "tcalc-panelTitle", children: "Results" }),
8827
- showMargin && /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(ResultBox, { label: "Margin Required", value: results ? formatMoney(results.marginRequired) : "$" }),
8894
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("h3", { className: "tcalc-panelTitle", children: copy.resultsPanel }),
8895
+ showMargin && /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(ResultBox, { label: copy.marginRequired, value: results ? formatMoney(results.marginRequired) : "$" }),
8828
8896
  showTradeVolume && showPip && /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "tcalc-resultRow", children: [
8829
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(ResultBox, { label: "Trade Volume", value: results ? formatInteger(results.tradeVolume) : "$" }),
8830
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(ResultBox, { label: "Pip value", value: results ? formatMoney(results.pipValue) : "$" })
8897
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(ResultBox, { label: copy.tradeVolume, value: results ? formatInteger(results.tradeVolume) : "$" }),
8898
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(ResultBox, { label: copy.pipValue, value: results ? formatMoney(results.pipValue) : "$" })
8831
8899
  ] }),
8832
- !showTradeVolume && showPip && /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(ResultBox, { label: "Pip value", value: results ? formatMoney(results.pipValue) : "$" }),
8900
+ !showTradeVolume && showPip && /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(ResultBox, { label: copy.pipValue, value: results ? formatMoney(results.pipValue) : "$" }),
8833
8901
  showSwap && /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "tcalc-resultRow", children: [
8834
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(ResultBox, { label: "Swap Charge Long", value: results ? formatMoney(results.swapChargeLong) : "$" }),
8835
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(ResultBox, { label: "Swap Charge Short", value: results ? formatMoney(results.swapChargeShort) : "$" })
8902
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(ResultBox, { label: copy.swapChargeLong, value: results ? formatMoney(results.swapChargeLong) : "$" }),
8903
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(ResultBox, { label: copy.swapChargeShort, value: results ? formatMoney(results.swapChargeShort) : "$" })
8836
8904
  ] }),
8837
8905
  calcError && /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("p", { className: "tcalc-fieldError tcalc-calcError", children: [
8838
8906
  /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(ErrorIcon, {}),
@@ -8841,12 +8909,12 @@ var TradingCalculator = ({
8841
8909
  ] }),
8842
8910
  showSwap && /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(import_jsx_runtime60.Fragment, { children: [
8843
8911
  /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "tcalc-infoGrid", children: [
8844
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(InfoRow, { label: "Swap long rate", value: selectedSymbol ? String(selectedSymbol.swap_rate_long) : "-" }),
8845
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(InfoRow, { label: "Swap short rate", value: selectedSymbol ? String(selectedSymbol.swap_rate_short) : "-" })
8912
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(InfoRow, { label: copy.swapLongRate, value: selectedSymbol ? String(selectedSymbol.swap_rate_long) : "-" }),
8913
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(InfoRow, { label: copy.swapShortRate, value: selectedSymbol ? String(selectedSymbol.swap_rate_short) : "-" })
8846
8914
  ] }),
8847
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(InfoRow, { separator: ":", label: "Swap calculation type", value: selectedSymbol ? selectedSymbol.swap_mode : "-" }),
8848
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(InfoRow, { separator: ":", label: "Three-day swap", value: selectedSymbol ? selectedSymbol.three_day_swap || "No" : "-" }),
8849
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(InfoRow, { separator: ":", label: "Weekend swaps", value: selectedSymbol ? String(selectedSymbol.weekend_swaps ?? "-") : "-" })
8915
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(InfoRow, { separator: ":", label: copy.swapCalculationType, value: selectedSymbol ? selectedSymbol.swap_mode : "-" }),
8916
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(InfoRow, { separator: ":", label: copy.threeDaySwap, value: selectedSymbol ? selectedSymbol.three_day_swap || copy.threeDaySwapFallback : "-" }),
8917
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(InfoRow, { separator: ":", label: copy.weekendSwaps, value: selectedSymbol ? String(selectedSymbol.weekend_swaps ?? "-") : "-" })
8850
8918
  ] })
8851
8919
  ] })
8852
8920
  ] }) }) });
@@ -9444,9 +9512,11 @@ function browserSupportsMarketStreaming() {
9444
9512
 
9445
9513
  // templates/cryptoMarketData/conversion.ts
9446
9514
  function convertAmounts(options) {
9515
+ const waitingForPrice = options.messages?.waitingForPrice ?? "Waiting for a live price";
9516
+ const invalidAmount = options.messages?.invalidAmount ?? "Enter a valid amount";
9447
9517
  const price = options.price;
9448
9518
  if (price == null || !Number.isFinite(price) || price <= 0) {
9449
- return { pay: options.payInput, receive: options.receiveInput, valid: false, error: "Waiting for a live price" };
9519
+ return { pay: options.payInput, receive: options.receiveInput, valid: false, error: waitingForPrice };
9450
9520
  }
9451
9521
  const source = options.edited === "pay" ? options.payInput : options.receiveInput;
9452
9522
  const trimmed = source.trim();
@@ -9459,7 +9529,7 @@ function convertAmounts(options) {
9459
9529
  pay: options.payInput,
9460
9530
  receive: options.receiveInput,
9461
9531
  valid: false,
9462
- error: "Enter a valid amount"
9532
+ error: invalidAmount
9463
9533
  };
9464
9534
  }
9465
9535
  if (options.side === "buy") {
@@ -9676,8 +9746,8 @@ function createMarketFeed(discovered, handlers, options = {}) {
9676
9746
  const resolved = resolveMetaId(key, meta);
9677
9747
  if (!resolved) continue;
9678
9748
  applyTick(resolved.id, resolved.meta, tick);
9679
- subscribeTicker(resolved.id);
9680
- subscribeKline(resolved.id);
9749
+ subscribeTicker(key);
9750
+ subscribeKline(key);
9681
9751
  }
9682
9752
  return;
9683
9753
  }
@@ -10004,9 +10074,39 @@ function useCryptoMarketData(options) {
10004
10074
  return snapshot;
10005
10075
  }
10006
10076
 
10077
+ // templates/CryptoTradeConverter/CryptoTradeConverter.types.ts
10078
+ var DEFAULT_CRYPTO_TRADE_CONVERTER_TITLE = "Buy and sell in seconds";
10079
+ var DEFAULT_CRYPTO_TRADE_CONVERTER_SUBTITLE = "Choose your asset, enter an amount, and confirm. Your crypto lands in your account instantly, ready to trade, hold, or withdraw whenever you want.";
10080
+ var DEFAULT_CRYPTO_TRADE_CONVERTER_LABELS = {
10081
+ buy: "Buy",
10082
+ sell: "Sell",
10083
+ connecting: "Connecting to live prices\u2026",
10084
+ livePrices: "Live prices",
10085
+ waitingForPrice: "Waiting for a live price",
10086
+ unsupportedBrowser: "Please use a current browser to view live prices.",
10087
+ selectMarket: "Select a market",
10088
+ chooseMarket: "Choose a market",
10089
+ tradeSideGroup: "Trade side",
10090
+ youPay: "You pay",
10091
+ youReceive: "You receive",
10092
+ amountToSpend: "Amount to spend",
10093
+ primaryAmountAria: "Primary trade amount",
10094
+ convertedAmountAria: "Converted trade amount",
10095
+ estimateTemplate: "Estimate at 1 {base} = {price} {quote}. Fees and spread excluded.",
10096
+ change24hSuffix: "(24h)",
10097
+ waitingForLivePrice: "Waiting for live price\u2026",
10098
+ invalidAmount: "Enter a valid amount"
10099
+ };
10100
+
10007
10101
  // templates/CryptoTradeConverter/CryptoTradeConverter.tsx
10008
10102
  var import_jsx_runtime62 = require("react/jsx-runtime");
10009
10103
  var DEFAULT_TRADE_HREF = "https://deriv.exchange";
10104
+ function fillTemplate3(template, values) {
10105
+ return template.replace(/\{(\w+)\}/g, (_, key) => values[key] ?? "");
10106
+ }
10107
+ function mergeLabels2(overrides) {
10108
+ return { ...DEFAULT_CRYPTO_TRADE_CONVERTER_LABELS, ...overrides };
10109
+ }
10010
10110
  function matchPair(rows, defaultPair) {
10011
10111
  if (!rows.length) return null;
10012
10112
  if (!defaultPair) {
@@ -10026,8 +10126,8 @@ function CoinAvatar({ base, icon, label }) {
10026
10126
  className: "ctc-avatar",
10027
10127
  src: icon,
10028
10128
  alt: "",
10029
- width: 32,
10030
- height: 32,
10129
+ width: 30,
10130
+ height: 30,
10031
10131
  loading: "lazy",
10032
10132
  decoding: "async",
10033
10133
  onError: () => setFailed(true),
@@ -10035,14 +10135,36 @@ function CoinAvatar({ base, icon, label }) {
10035
10135
  }
10036
10136
  );
10037
10137
  }
10138
+ function ChevronIcon4({ className }) {
10139
+ return /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
10140
+ "svg",
10141
+ {
10142
+ className,
10143
+ width: 20,
10144
+ height: 20,
10145
+ viewBox: "0 0 24 24",
10146
+ fill: "none",
10147
+ stroke: "currentColor",
10148
+ strokeWidth: "2.2",
10149
+ strokeLinecap: "round",
10150
+ strokeLinejoin: "round",
10151
+ "aria-hidden": "true",
10152
+ children: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("polyline", { points: "6 9 12 15 18 9" })
10153
+ }
10154
+ );
10155
+ }
10038
10156
  var CryptoTradeConverter = ({
10039
10157
  variant: _variant = "default",
10040
10158
  market: _market = "spot",
10041
10159
  defaultPair,
10042
10160
  tradeHref = DEFAULT_TRADE_HREF,
10043
10161
  apiOrigin,
10162
+ title = DEFAULT_CRYPTO_TRADE_CONVERTER_TITLE,
10163
+ subtitle = DEFAULT_CRYPTO_TRADE_CONVERTER_SUBTITLE,
10164
+ labels: labelsProp,
10044
10165
  className
10045
10166
  }) => {
10167
+ const copy = mergeLabels2(labelsProp);
10046
10168
  const reactId = (0, import_react37.useId)().replace(/:/g, "");
10047
10169
  const listboxId = `ctc-listbox-${reactId}`;
10048
10170
  const payId = `ctc-pay-${reactId}`;
@@ -10070,9 +10192,13 @@ var CryptoTradeConverter = ({
10070
10192
  price: selected?.last ?? null,
10071
10193
  payInput: pay,
10072
10194
  receiveInput: receive,
10073
- edited
10195
+ edited,
10196
+ messages: {
10197
+ waitingForPrice: copy.waitingForPrice,
10198
+ invalidAmount: copy.invalidAmount
10199
+ }
10074
10200
  }),
10075
- [side, selected?.last, pay, receive, edited]
10201
+ [side, selected?.last, pay, receive, edited, copy.waitingForPrice, copy.invalidAmount]
10076
10202
  );
10077
10203
  (0, import_react37.useEffect)(() => {
10078
10204
  if (edited === "pay") setReceive(conversion.receive);
@@ -10141,6 +10267,15 @@ var CryptoTradeConverter = ({
10141
10267
  const priceReady = selected?.last != null && Number.isFinite(selected.last) && selected.last > 0;
10142
10268
  const ctaDisabled = !priceReady || snapshot.status === "unsupported" || snapshot.status === "error" || !conversion.valid;
10143
10269
  const changeClass = selected?.change24hPct == null ? "" : selected.change24hPct >= 0 ? "ctc-change--up" : "ctc-change--down";
10270
+ const payValue = edited === "pay" ? pay : conversion.pay;
10271
+ const receiveValue = edited === "receive" ? receive : conversion.receive;
10272
+ const receiveMeta = priceReady && selected ? fillTemplate3(copy.estimateTemplate, {
10273
+ base: selected.base,
10274
+ price: formatPrice(selected.last, selected.pricePrecision),
10275
+ quote: selected.quote
10276
+ }) : snapshot.status === "unsupported" ? copy.unsupportedBrowser : conversion.error || (snapshot.status === "idle" || snapshot.status === "connecting" ? copy.connecting : copy.waitingForLivePrice);
10277
+ const ctaLabel = side === "buy" ? copy.buy : copy.sell;
10278
+ const ctaClass = ["ctc-cta__btn", side === "sell" ? "is-sell" : "is-buy"].join(" ");
10144
10279
  return /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
10145
10280
  "section",
10146
10281
  {
@@ -10151,36 +10286,23 @@ var CryptoTradeConverter = ({
10151
10286
  "aria-labelledby": `ctc-heading-${reactId}`,
10152
10287
  children: /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "ctc-container", children: [
10153
10288
  /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("header", { className: "ctc-header", children: [
10154
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("h2", { id: `ctc-heading-${reactId}`, className: "ctc-title", children: "Buy and sell in seconds" }),
10155
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("p", { className: "ctc-subtitle", children: "Choose your asset, enter an amount, and confirm. Your crypto lands in your account instantly, ready to trade, hold, or withdraw whenever you want." })
10289
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("h2", { id: `ctc-heading-${reactId}`, className: "ctc-title", children: title }),
10290
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("p", { className: "ctc-subtitle", children: subtitle })
10156
10291
  ] }),
10157
- /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "ctc-card", children: [
10158
- /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: `ctc-status ctc-status--${statusTone}`, id: statusId, role: "status", "aria-live": "polite", children: [
10159
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "ctc-status-dot", "aria-hidden": "true" }),
10160
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { children: snapshot.status === "idle" || snapshot.status === "connecting" ? "Connecting to live prices\u2026" : snapshot.statusMessage || (priceReady ? "Live prices" : "Waiting for live price\u2026") })
10161
- ] }),
10162
- /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "ctc-seg", role: "group", "aria-label": "Trade side", children: [
10163
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
10164
- "button",
10165
- {
10166
- type: "button",
10167
- className: side === "buy" ? "ctc-seg__btn is-active is-buy" : "ctc-seg__btn is-buy",
10168
- "aria-pressed": side === "buy",
10169
- onClick: () => setSide("buy"),
10170
- children: "Buy"
10171
- }
10172
- ),
10173
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
10174
- "button",
10175
- {
10176
- type: "button",
10177
- className: side === "sell" ? "ctc-seg__btn is-active is-sell" : "ctc-seg__btn is-sell",
10178
- "aria-pressed": side === "sell",
10179
- onClick: () => setSide("sell"),
10180
- children: "Sell"
10181
- }
10182
- )
10183
- ] }),
10292
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("div", { className: "ctc-card", children: /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "ctc-widget", children: [
10293
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)(
10294
+ "div",
10295
+ {
10296
+ className: `ctc-status ctc-status--${statusTone}`,
10297
+ id: statusId,
10298
+ role: "status",
10299
+ "aria-live": "polite",
10300
+ children: [
10301
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "ctc-status-dot", "aria-hidden": "true" }),
10302
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { children: snapshot.status === "idle" || snapshot.status === "connecting" ? copy.connecting : snapshot.statusMessage || (priceReady ? copy.livePrices : copy.waitingForLivePrice) })
10303
+ ]
10304
+ }
10305
+ ),
10184
10306
  /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "ctc-pair", children: [
10185
10307
  /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)(
10186
10308
  "button",
@@ -10217,137 +10339,180 @@ var CryptoTradeConverter = ({
10217
10339
  " ",
10218
10340
  /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { className: ["ctc-change", changeClass].filter(Boolean).join(" "), children: [
10219
10341
  formatPct(selected.change24hPct),
10220
- " (24h)"
10342
+ " ",
10343
+ copy.change24hSuffix
10221
10344
  ] })
10222
10345
  ] })
10223
10346
  ] })
10224
10347
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { className: "ctc-pair__meta", children: [
10225
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "ctc-pair__name", children: "Select a market" }),
10348
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "ctc-pair__name", children: copy.selectMarket }),
10226
10349
  /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "ctc-pair__price", children: "\u2014" })
10227
10350
  ] }),
10228
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "ctc-pair__chevron", "aria-hidden": "true", children: "\u25BE" })
10351
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(ChevronIcon4, { className: "ctc-pair__chevron" })
10229
10352
  ]
10230
10353
  }
10231
10354
  ),
10232
- pickerOpen ? /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
10355
+ pickerOpen ? /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)(
10233
10356
  "ul",
10234
10357
  {
10235
10358
  ref: listRef,
10236
10359
  id: listboxId,
10237
10360
  className: "ctc-listbox",
10238
10361
  role: "listbox",
10239
- "aria-label": "Market pairs",
10362
+ "aria-label": copy.chooseMarket,
10240
10363
  tabIndex: -1,
10241
10364
  onKeyDown: onPickerKeyDown,
10242
- children: sortedRows.map((row, index) => {
10243
- const selectedOption = selected?.id === row.id;
10244
- const active = index === activeOption;
10245
- const rowChange = row.change24hPct == null ? "" : row.change24hPct >= 0 ? "ctc-change--up" : "ctc-change--down";
10246
- return /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)(
10247
- "li",
10248
- {
10249
- id: `${listboxId}-opt-${index}`,
10250
- role: "option",
10251
- "aria-selected": selectedOption,
10252
- className: [
10253
- "ctc-option",
10254
- active ? "is-active" : "",
10255
- selectedOption ? "is-selected" : ""
10256
- ].filter(Boolean).join(" "),
10257
- onMouseEnter: () => setActiveOption(index),
10258
- onClick: () => {
10259
- setSelectedId(row.id);
10260
- closePicker();
10365
+ children: [
10366
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("li", { className: "ctc-listbox__header", role: "presentation", children: copy.chooseMarket }),
10367
+ sortedRows.map((row, index) => {
10368
+ const selectedOption = selected?.id === row.id;
10369
+ const active = index === activeOption;
10370
+ const rowChange = row.change24hPct == null ? "" : row.change24hPct >= 0 ? "ctc-change--up" : "ctc-change--down";
10371
+ return /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)(
10372
+ "li",
10373
+ {
10374
+ id: `${listboxId}-opt-${index}`,
10375
+ role: "option",
10376
+ "aria-selected": selectedOption,
10377
+ className: [
10378
+ "ctc-option",
10379
+ active ? "is-active" : "",
10380
+ selectedOption ? "is-selected" : ""
10381
+ ].filter(Boolean).join(" "),
10382
+ onMouseEnter: () => setActiveOption(index),
10383
+ onClick: () => {
10384
+ setSelectedId(row.id);
10385
+ closePicker();
10386
+ },
10387
+ children: [
10388
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(CoinAvatar, { base: row.base, icon: row.icon, label: row.name }),
10389
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { className: "ctc-option__text", children: [
10390
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "ctc-option__name", children: row.name }),
10391
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "ctc-option__sub", children: row.longName })
10392
+ ] }),
10393
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { className: "ctc-option__stats", children: [
10394
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { children: formatPrice(row.last, row.pricePrecision) }),
10395
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: ["ctc-change", rowChange].filter(Boolean).join(" "), children: formatPct(row.change24hPct) })
10396
+ ] })
10397
+ ]
10261
10398
  },
10262
- children: [
10263
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(CoinAvatar, { base: row.base, icon: row.icon, label: row.name }),
10264
- /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { className: "ctc-option__text", children: [
10265
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "ctc-option__name", children: row.name }),
10266
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "ctc-option__sub", children: row.longName })
10267
- ] }),
10268
- /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { className: "ctc-option__stats", children: [
10269
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { children: formatPrice(row.last, row.pricePrecision) }),
10270
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: ["ctc-change", rowChange].filter(Boolean).join(" "), children: formatPct(row.change24hPct) })
10271
- ] })
10272
- ]
10273
- },
10274
- row.id
10275
- );
10276
- })
10399
+ row.id
10400
+ );
10401
+ })
10402
+ ]
10277
10403
  }
10278
10404
  ) : null
10279
10405
  ] }),
10280
- /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "ctc-fields", children: [
10406
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "ctc-seg", role: "group", "aria-label": copy.tradeSideGroup, children: [
10281
10407
  /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
10282
- TextField,
10408
+ "button",
10283
10409
  {
10284
- id: payId,
10285
- label: `You pay (${labels.pay})`,
10286
- inputMode: "decimal",
10287
- autoComplete: "off",
10288
- placeholder: "0.00",
10289
- value: edited === "pay" ? pay : conversion.pay,
10290
- status: conversion.error ? "fail" : "neutral",
10291
- "aria-invalid": Boolean(conversion.error),
10292
- "aria-describedby": statusId,
10293
- onChange: (event) => {
10294
- setEdited("pay");
10295
- setPay(event.target.value);
10296
- }
10410
+ type: "button",
10411
+ className: side === "buy" ? "ctc-seg__btn is-active is-buy" : "ctc-seg__btn is-buy",
10412
+ "aria-pressed": side === "buy",
10413
+ onClick: () => setSide("buy"),
10414
+ children: copy.buy
10297
10415
  }
10298
10416
  ),
10299
10417
  /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
10300
- TextField,
10418
+ "button",
10301
10419
  {
10302
- id: receiveId,
10303
- label: `You receive (${labels.receive})`,
10304
- inputMode: "decimal",
10305
- autoComplete: "off",
10306
- placeholder: "0.00",
10307
- value: edited === "receive" ? receive : conversion.receive,
10308
- status: conversion.error ? "fail" : "neutral",
10309
- "aria-invalid": Boolean(conversion.error),
10310
- "aria-describedby": priceReady && selected ? `ctc-rate-${reactId}` : statusId,
10311
- onChange: (event) => {
10312
- setEdited("receive");
10313
- setReceive(event.target.value);
10314
- }
10420
+ type: "button",
10421
+ className: side === "sell" ? "ctc-seg__btn is-active is-sell" : "ctc-seg__btn is-sell",
10422
+ "aria-pressed": side === "sell",
10423
+ onClick: () => setSide("sell"),
10424
+ children: copy.sell
10425
+ }
10426
+ )
10427
+ ] }),
10428
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "ctc-fields", children: [
10429
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)(
10430
+ "label",
10431
+ {
10432
+ className: ["ctc-amount", conversion.error ? "is-invalid" : ""].filter(Boolean).join(" "),
10433
+ htmlFor: payId,
10434
+ children: [
10435
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "ctc-amount__label", children: copy.youPay }),
10436
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { className: "ctc-amount__row", children: [
10437
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
10438
+ "input",
10439
+ {
10440
+ id: payId,
10441
+ className: "ctc-amount__input",
10442
+ type: "text",
10443
+ inputMode: "decimal",
10444
+ autoComplete: "off",
10445
+ placeholder: "0.00",
10446
+ value: payValue,
10447
+ "aria-label": copy.primaryAmountAria,
10448
+ "aria-invalid": Boolean(conversion.error),
10449
+ "aria-describedby": statusId,
10450
+ onChange: (event) => {
10451
+ setEdited("pay");
10452
+ setPay(event.target.value);
10453
+ }
10454
+ }
10455
+ ),
10456
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "ctc-amount__unit", children: labels.pay })
10457
+ ] }),
10458
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "ctc-amount__meta", children: copy.amountToSpend })
10459
+ ]
10460
+ }
10461
+ ),
10462
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)(
10463
+ "label",
10464
+ {
10465
+ className: ["ctc-amount", conversion.error ? "is-invalid" : ""].filter(Boolean).join(" "),
10466
+ htmlFor: receiveId,
10467
+ children: [
10468
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "ctc-amount__label", children: copy.youReceive }),
10469
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("span", { className: "ctc-amount__row", children: [
10470
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
10471
+ "input",
10472
+ {
10473
+ id: receiveId,
10474
+ className: "ctc-amount__input",
10475
+ type: "text",
10476
+ inputMode: "decimal",
10477
+ autoComplete: "off",
10478
+ placeholder: "0.00",
10479
+ value: receiveValue,
10480
+ "aria-label": copy.convertedAmountAria,
10481
+ "aria-invalid": Boolean(conversion.error),
10482
+ "aria-describedby": `ctc-rate-${reactId}`,
10483
+ onChange: (event) => {
10484
+ setEdited("receive");
10485
+ setReceive(event.target.value);
10486
+ }
10487
+ }
10488
+ ),
10489
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { className: "ctc-amount__unit", children: labels.receive })
10490
+ ] }),
10491
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { id: `ctc-rate-${reactId}`, className: "ctc-amount__meta", children: receiveMeta })
10492
+ ]
10315
10493
  }
10316
10494
  )
10317
10495
  ] }),
10318
- priceReady && selected ? /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("p", { id: `ctc-rate-${reactId}`, className: "ctc-rate", children: [
10319
- "Estimate at 1 ",
10320
- selected.base,
10321
- " = ",
10322
- formatPrice(selected.last, selected.pricePrecision),
10323
- " ",
10324
- selected.quote,
10325
- ". Fees and spread excluded."
10326
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("p", { className: "ctc-rate", role: "status", children: snapshot.status === "unsupported" ? "Please use a current browser to view live prices." : conversion.error || "Waiting for a live market price\u2026" }),
10327
10496
  conversion.error ? /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("p", { className: "ctc-error", role: "alert", children: conversion.error }) : null,
10328
10497
  /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("div", { className: "ctc-cta", children: ctaDisabled ? /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
10329
- Button,
10498
+ "button",
10330
10499
  {
10331
10500
  type: "button",
10332
- colorScheme: "coral",
10333
- variant: "primary",
10334
- fullWidth: true,
10335
- label: side === "buy" ? "Buy" : "Sell",
10501
+ className: ctaClass,
10336
10502
  disabled: true,
10337
- "aria-disabled": "true"
10503
+ "aria-disabled": "true",
10504
+ children: ctaLabel
10338
10505
  }
10339
- ) : /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("a", { className: "ctc-cta-link", href: tradeHref, rel: "noopener noreferrer", children: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
10340
- Button,
10506
+ ) : /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
10507
+ "a",
10341
10508
  {
10342
- type: "button",
10343
- colorScheme: "coral",
10344
- variant: "primary",
10345
- fullWidth: true,
10346
- label: side === "buy" ? "Buy" : "Sell",
10347
- tabIndex: -1
10509
+ className: ["ctc-cta-link", side === "sell" ? "is-sell" : "is-buy"].join(" "),
10510
+ href: tradeHref,
10511
+ rel: "noopener noreferrer",
10512
+ children: ctaLabel
10348
10513
  }
10349
- ) }) })
10350
- ] })
10514
+ ) })
10515
+ ] }) })
10351
10516
  ] })
10352
10517
  }
10353
10518
  );
@@ -10355,7 +10520,37 @@ var CryptoTradeConverter = ({
10355
10520
 
10356
10521
  // templates/CryptoMarketsTable/CryptoMarketsTable.tsx
10357
10522
  var import_react38 = require("react");
10523
+
10524
+ // templates/CryptoMarketsTable/CryptoMarketsTable.types.ts
10525
+ var DEFAULT_CRYPTO_MARKETS_TABLE_TITLE = "Explore the markets";
10526
+ var DEFAULT_CRYPTO_MARKETS_TABLE_LABELS = {
10527
+ spot: "Spot",
10528
+ futures: "Futures",
10529
+ live: "Live",
10530
+ connecting: "Connecting\u2026",
10531
+ marketTypeGroup: "Market type",
10532
+ quoteCurrencyTabs: "Quote currency",
10533
+ scrollRegion: "Market prices. Scroll horizontally to see all columns.",
10534
+ columnName: "Name",
10535
+ columnPrice: "Price",
10536
+ column1h: "1h %",
10537
+ column24h: "24h %",
10538
+ column7d: "7d chart",
10539
+ unsupportedBrowser: "Please use a current browser to view market prices.",
10540
+ loadError: "Unable to load market data. Retrying\u2026",
10541
+ loading: "Loading market data\u2026",
10542
+ emptyPairsTemplate: "No {quote} pairs on {kind} yet\u2026",
10543
+ sparklineAriaTemplate: "{name} available hourly price history, up to 7 days"
10544
+ };
10545
+
10546
+ // templates/CryptoMarketsTable/CryptoMarketsTable.tsx
10358
10547
  var import_jsx_runtime63 = require("react/jsx-runtime");
10548
+ function fillTemplate4(template, values) {
10549
+ return template.replace(/\{(\w+)\}/g, (_, key) => values[key] ?? "");
10550
+ }
10551
+ function mergeLabels3(overrides) {
10552
+ return { ...DEFAULT_CRYPTO_MARKETS_TABLE_LABELS, ...overrides };
10553
+ }
10359
10554
  function CoinAvatar2({ base, icon }) {
10360
10555
  const [failed, setFailed] = (0, import_react38.useState)(false);
10361
10556
  if (failed) {
@@ -10461,8 +10656,11 @@ var CryptoMarketsTable = ({
10461
10656
  initialQuoteCurrency = "USDT",
10462
10657
  apiOrigin,
10463
10658
  maxRows,
10659
+ title = DEFAULT_CRYPTO_MARKETS_TABLE_TITLE,
10660
+ labels: labelsProp,
10464
10661
  className
10465
10662
  }) => {
10663
+ const copy = mergeLabels3(labelsProp);
10466
10664
  const reactId = (0, import_react38.useId)().replace(/:/g, "");
10467
10665
  const [kind, setKind] = (0, import_react38.useState)(initialMarket);
10468
10666
  const [quote, setQuote] = (0, import_react38.useState)(initialQuoteCurrency.toUpperCase());
@@ -10509,7 +10707,14 @@ var CryptoMarketsTable = ({
10509
10707
  }
10510
10708
  };
10511
10709
  const statusTone = snapshot.status === "live" ? "live" : snapshot.status === "reconnecting" || snapshot.status === "stale" ? "warn" : snapshot.status === "error" || snapshot.status === "unsupported" ? "error" : "muted";
10512
- const emptyMessage = snapshot.status === "unsupported" ? "Please use a current browser to view market prices." : snapshot.status === "error" ? "Unable to load market data. Retrying\u2026" : snapshot.status === "connecting" || snapshot.status === "idle" ? "Loading market data\u2026" : `No ${quote} pairs on ${kind} yet\u2026`;
10710
+ const emptyMessage = snapshot.status === "unsupported" ? copy.unsupportedBrowser : snapshot.status === "error" ? copy.loadError : snapshot.status === "connecting" || snapshot.status === "idle" ? copy.loading : fillTemplate4(copy.emptyPairsTemplate, { quote, kind });
10711
+ const columnLabels = [
10712
+ ["name", copy.columnName],
10713
+ ["last", copy.columnPrice],
10714
+ ["change1hPct", copy.column1h],
10715
+ ["change24hPct", copy.column24h],
10716
+ ["change7dPct", copy.column7d]
10717
+ ];
10513
10718
  return /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
10514
10719
  "section",
10515
10720
  {
@@ -10518,26 +10723,26 @@ var CryptoMarketsTable = ({
10518
10723
  "aria-labelledby": `cmt-heading-${reactId}`,
10519
10724
  children: /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "cmt-container", children: [
10520
10725
  /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("header", { className: "cmt-header", children: [
10521
- /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("h2", { id: `cmt-heading-${reactId}`, className: "cmt-title", children: "Explore the markets" }),
10726
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("h2", { id: `cmt-heading-${reactId}`, className: "cmt-title", children: title }),
10522
10727
  /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("p", { className: `cmt-status cmt-status--${statusTone}`, role: "status", "aria-live": "polite", children: [
10523
10728
  /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { className: "cmt-status-dot", "aria-hidden": "true" }),
10524
- /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { children: snapshot.status === "live" ? "Live" : snapshot.statusMessage || "Connecting\u2026" })
10729
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { children: snapshot.status === "live" ? copy.live : snapshot.statusMessage || copy.connecting })
10525
10730
  ] })
10526
10731
  ] }),
10527
10732
  /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "cmt-card", children: [
10528
10733
  /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: "cmt-toolbar", children: [
10529
- /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("div", { className: "cmt-seg", role: "group", "aria-label": "Market type", children: ["spot", "futures"].map((value) => /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
10734
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("div", { className: "cmt-seg", role: "group", "aria-label": copy.marketTypeGroup, children: ["spot", "futures"].map((value) => /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
10530
10735
  "button",
10531
10736
  {
10532
10737
  type: "button",
10533
10738
  className: kind === value ? "cmt-seg__btn is-active" : "cmt-seg__btn",
10534
10739
  "aria-pressed": kind === value,
10535
10740
  onClick: () => setKind(value),
10536
- children: value === "spot" ? "Spot" : "Futures"
10741
+ children: value === "spot" ? copy.spot : copy.futures
10537
10742
  },
10538
10743
  value
10539
10744
  )) }),
10540
- /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("div", { className: "cmt-tabs", role: "tablist", "aria-label": "Quote currency", children: snapshot.quotes.map((q) => /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
10745
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("div", { className: "cmt-tabs", role: "tablist", "aria-label": copy.quoteCurrencyTabs, children: snapshot.quotes.map((q) => /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
10541
10746
  "button",
10542
10747
  {
10543
10748
  type: "button",
@@ -10557,15 +10762,9 @@ var CryptoMarketsTable = ({
10557
10762
  className: ["cmt-scroll", scrolled ? "is-scrolled" : ""].filter(Boolean).join(" "),
10558
10763
  tabIndex: 0,
10559
10764
  role: "region",
10560
- "aria-label": "Market prices. Scroll horizontally to see all columns.",
10765
+ "aria-label": copy.scrollRegion,
10561
10766
  children: /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("table", { className: "cmt-table", children: [
10562
- /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("tr", { children: [
10563
- ["name", "Name"],
10564
- ["last", "Price"],
10565
- ["change1hPct", "1h %"],
10566
- ["change24hPct", "24h %"],
10567
- ["change7dPct", "7d chart"]
10568
- ].map(([key, label]) => {
10767
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("tr", { children: columnLabels.map(([key, label]) => {
10569
10768
  const active = sortKey === key;
10570
10769
  return /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
10571
10770
  "th",
@@ -10610,7 +10809,9 @@ var CryptoMarketsTable = ({
10610
10809
  {
10611
10810
  values: row.spark7d,
10612
10811
  up: (row.change7dPct ?? 0) >= 0,
10613
- label: `${row.name} available hourly price history, up to 7 days`,
10812
+ label: fillTemplate4(copy.sparklineAriaTemplate, {
10813
+ name: row.name
10814
+ }),
10614
10815
  reducedMotion
10615
10816
  }
10616
10817
  ) })
@@ -12263,10 +12464,16 @@ var AffiliatesSlider = ({
12263
12464
  CryptoMarketsTable,
12264
12465
  CryptoTradeConverter,
12265
12466
  DEFAULT_AWARDS,
12467
+ DEFAULT_CRYPTO_MARKETS_TABLE_LABELS,
12468
+ DEFAULT_CRYPTO_MARKETS_TABLE_TITLE,
12469
+ DEFAULT_CRYPTO_TRADE_CONVERTER_LABELS,
12470
+ DEFAULT_CRYPTO_TRADE_CONVERTER_SUBTITLE,
12471
+ DEFAULT_CRYPTO_TRADE_CONVERTER_TITLE,
12266
12472
  DEFAULT_CULTURE_CARDS,
12267
12473
  DEFAULT_EXCHANGE_RATE_WS_URL,
12268
12474
  DEFAULT_SYMBOLS_ENDPOINT,
12269
12475
  DEFAULT_TRADE_HREF,
12476
+ DEFAULT_TRADING_CALCULATOR_LABELS,
12270
12477
  DayNightTransition,
12271
12478
  DocumentChips,
12272
12479
  DotLottieInView,