@deriv-web-design/ui 0.1.35 → 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.mjs CHANGED
@@ -8216,7 +8216,55 @@ var ParallaxBanner = ({
8216
8216
 
8217
8217
  // templates/TradingCalculator/TradingCalculator.tsx
8218
8218
  import { useCallback as useCallback6, useEffect as useEffect18, useId as useId6, useMemo as useMemo7, useRef as useRef21, useState as useState23 } from "react";
8219
+
8220
+ // templates/TradingCalculator/TradingCalculator.types.ts
8221
+ var DEFAULT_TRADING_CALCULATOR_LABELS = {
8222
+ calculatorPanel: "Calculator",
8223
+ resultsPanel: "Results",
8224
+ calculate: "Calculate",
8225
+ calculating: "Calculating\u2026",
8226
+ reset: "Reset",
8227
+ symbols: "Symbols",
8228
+ volume: "Volume in Lots",
8229
+ assetPrice: "Asset price",
8230
+ contractSize: "Contract size",
8231
+ pipSize: "Pip size",
8232
+ effectiveLeverage: "Effective Leverage",
8233
+ pointSize: "Point size",
8234
+ marginRequired: "Margin Required",
8235
+ tradeVolume: "Trade Volume",
8236
+ pipValue: "Pip value",
8237
+ swapChargeLong: "Swap Charge Long",
8238
+ swapChargeShort: "Swap Charge Short",
8239
+ swapLongRate: "Swap long rate",
8240
+ swapShortRate: "Swap short rate",
8241
+ swapCalculationType: "Swap calculation type",
8242
+ threeDaySwap: "Three-day swap",
8243
+ weekendSwaps: "Weekend swaps",
8244
+ threeDaySwapFallback: "No",
8245
+ loadingSymbols: "Loading symbols\u2026",
8246
+ symbolsUnavailable: "Symbols unavailable",
8247
+ searchSymbolPlaceholder: "Search symbol, e.g. EURUSD",
8248
+ symbolsLoadError: "Unable to load symbols.",
8249
+ retry: "Retry",
8250
+ selectSymbolError: "Select a symbol to continue.",
8251
+ volumeRequiredError: "Enter a valid volume in lots.",
8252
+ volumePositiveError: "Volume must be greater than 0.",
8253
+ volumeRangeError: "Volume must be between {min} and {max} lots.",
8254
+ assetPriceError: "Enter a valid asset price.",
8255
+ exchangeRateError: "Unable to fetch live exchange rates. Please try again.",
8256
+ exchangeRateErrorWithMessage: "Unable to fetch live exchange rates ({message}). Please try again.",
8257
+ noSymbolsMatch: 'No symbols match "{query}"'
8258
+ };
8259
+
8260
+ // templates/TradingCalculator/TradingCalculator.tsx
8219
8261
  import { Fragment as Fragment11, jsx as jsx60, jsxs as jsxs56 } from "react/jsx-runtime";
8262
+ function fillTemplate2(template, values) {
8263
+ return template.replace(/\{(\w+)\}/g, (_, key) => values[key] ?? "");
8264
+ }
8265
+ function mergeLabels(overrides) {
8266
+ return { ...DEFAULT_TRADING_CALCULATOR_LABELS, ...overrides };
8267
+ }
8220
8268
  var DEFAULT_SYMBOLS_ENDPOINT = "https://deriv-static-pricingfeedv2.firebaseio.com/n8n/row/prod/dynamic_trading_specs.json";
8221
8269
  var DEFAULT_EXCHANGE_RATE_WS_URL = "wss://ws.derivws.com/websockets/v3?app_id=1089";
8222
8270
  var USD = "USD";
@@ -8376,7 +8424,15 @@ function groupByAssetClass(symbols) {
8376
8424
  }
8377
8425
  return [...groups.entries()];
8378
8426
  }
8379
- var SymbolCombobox = ({ symbolsState, onRetry, selected, onSelect, error, inputId }) => {
8427
+ var SymbolCombobox = ({
8428
+ symbolsState,
8429
+ onRetry,
8430
+ selected,
8431
+ onSelect,
8432
+ error,
8433
+ inputId,
8434
+ copy
8435
+ }) => {
8380
8436
  const [query, setQuery] = useState23("");
8381
8437
  const [isOpen, setIsOpen] = useState23(false);
8382
8438
  const [activeIndex, setActiveIndex] = useState23(-1);
@@ -8434,7 +8490,8 @@ var SymbolCombobox = ({ symbolsState, onRetry, selected, onSelect, error, inputI
8434
8490
  {
8435
8491
  id: inputId,
8436
8492
  label: /* @__PURE__ */ jsxs56(Fragment11, { children: [
8437
- "Symbols ",
8493
+ copy.symbols,
8494
+ " ",
8438
8495
  /* @__PURE__ */ jsx60("span", { className: "tcalc-required", children: "*" })
8439
8496
  ] }),
8440
8497
  role: "combobox",
@@ -8449,7 +8506,7 @@ var SymbolCombobox = ({ symbolsState, onRetry, selected, onSelect, error, inputI
8449
8506
  disabled: isLoading || isError,
8450
8507
  hideClearButton: true,
8451
8508
  trailingSlot: /* @__PURE__ */ jsx60(ChevronIcon3, {}),
8452
- placeholder: isLoading ? "Loading symbols\u2026" : isError ? "Symbols unavailable" : "Search symbol, e.g. EURUSD",
8509
+ placeholder: isLoading ? copy.loadingSymbols : isError ? copy.symbolsUnavailable : copy.searchSymbolPlaceholder,
8453
8510
  value: displayValue,
8454
8511
  onFocus: () => setIsOpen(true),
8455
8512
  onChange: (event) => {
@@ -8461,11 +8518,7 @@ var SymbolCombobox = ({ symbolsState, onRetry, selected, onSelect, error, inputI
8461
8518
  }
8462
8519
  ),
8463
8520
  isOpen && !isLoading && !isError && /* @__PURE__ */ jsxs56("div", { className: "tcalc-dropdown", id: listboxId, role: "listbox", children: [
8464
- grouped.length === 0 && /* @__PURE__ */ jsxs56("p", { className: "tcalc-dropdownEmpty", children: [
8465
- 'No symbols match "',
8466
- query,
8467
- '"'
8468
- ] }),
8521
+ grouped.length === 0 && /* @__PURE__ */ jsx60("p", { className: "tcalc-dropdownEmpty", children: fillTemplate2(copy.noSymbolsMatch, { query }) }),
8469
8522
  grouped.map(([assetClass, items]) => /* @__PURE__ */ jsxs56("div", { className: "tcalc-dropdownGroup", children: [
8470
8523
  /* @__PURE__ */ jsx60("p", { className: "tcalc-dropdownGroupTitle", children: assetClass }),
8471
8524
  items.map((item) => {
@@ -8497,9 +8550,10 @@ var SymbolCombobox = ({ symbolsState, onRetry, selected, onSelect, error, inputI
8497
8550
  ] }),
8498
8551
  isError && /* @__PURE__ */ jsxs56("p", { className: "tcalc-fieldError", children: [
8499
8552
  /* @__PURE__ */ jsx60(ErrorIcon, {}),
8500
- " Unable to load symbols.",
8501
8553
  " ",
8502
- /* @__PURE__ */ jsx60("button", { type: "button", className: "tcalc-retryLink", onClick: onRetry, children: "Retry" })
8554
+ copy.symbolsLoadError,
8555
+ " ",
8556
+ /* @__PURE__ */ jsx60("button", { type: "button", className: "tcalc-retryLink", onClick: onRetry, children: copy.retry })
8503
8557
  ] }),
8504
8558
  !isError && error && /* @__PURE__ */ jsxs56("p", { className: "tcalc-fieldError", children: [
8505
8559
  /* @__PURE__ */ jsx60(ErrorIcon, {}),
@@ -8527,9 +8581,11 @@ var TradingCalculator = ({
8527
8581
  symbolsEndpoint = DEFAULT_SYMBOLS_ENDPOINT,
8528
8582
  exchangeRateWsUrl = DEFAULT_EXCHANGE_RATE_WS_URL,
8529
8583
  defaultSymbol,
8584
+ labels: labelsProp,
8530
8585
  className,
8531
8586
  ...props
8532
8587
  }) => {
8588
+ const copy = mergeLabels(labelsProp);
8533
8589
  const showMargin = variant === "main" || variant === "margin";
8534
8590
  const showPip = variant === "main" || variant === "pip";
8535
8591
  const showSwap = variant === "main" || variant === "swap";
@@ -8573,15 +8629,18 @@ var TradingCalculator = ({
8573
8629
  const volume = Number.parseFloat(volumeInLots);
8574
8630
  const price = Number.parseFloat(assetPrice);
8575
8631
  const nextErrors = {};
8576
- if (!selectedSymbol) nextErrors.symbol = "Select a symbol to continue.";
8632
+ if (!selectedSymbol) nextErrors.symbol = copy.selectSymbolError;
8577
8633
  if (!volumeInLots || Number.isNaN(volume)) {
8578
- nextErrors.volume = "Enter a valid volume in lots.";
8634
+ nextErrors.volume = copy.volumeRequiredError;
8579
8635
  } else if (volume <= 0) {
8580
- nextErrors.volume = "Volume must be greater than 0.";
8636
+ nextErrors.volume = copy.volumePositiveError;
8581
8637
  } else if (selectedSymbol && (volume < selectedSymbol.volume_min || volume > selectedSymbol.volume_limit)) {
8582
- nextErrors.volume = `Volume must be between ${selectedSymbol.volume_min} and ${selectedSymbol.volume_limit} lots.`;
8638
+ nextErrors.volume = fillTemplate2(copy.volumeRangeError, {
8639
+ min: String(selectedSymbol.volume_min),
8640
+ max: String(selectedSymbol.volume_limit)
8641
+ });
8583
8642
  }
8584
- if (!assetPrice || Number.isNaN(price) || price <= 0) nextErrors.price = "Enter a valid asset price.";
8643
+ if (!assetPrice || Number.isNaN(price) || price <= 0) nextErrors.price = copy.assetPriceError;
8585
8644
  setErrors(nextErrors);
8586
8645
  if (Object.keys(nextErrors).length > 0 || !selectedSymbol) return;
8587
8646
  setCalcError(null);
@@ -8592,7 +8651,7 @@ var TradingCalculator = ({
8592
8651
  setResults(computeResults(selectedSymbol, volume, price, rates));
8593
8652
  } catch (error) {
8594
8653
  setCalcError(
8595
- error instanceof Error ? `Unable to fetch live exchange rates (${error.message}). Please try again.` : "Unable to fetch live exchange rates. Please try again."
8654
+ error instanceof Error ? fillTemplate2(copy.exchangeRateErrorWithMessage, { message: error.message }) : copy.exchangeRateError
8596
8655
  );
8597
8656
  } finally {
8598
8657
  setIsCalculating(false);
@@ -8603,7 +8662,7 @@ var TradingCalculator = ({
8603
8662
  const priceInputId = useId6();
8604
8663
  return /* @__PURE__ */ jsx60("section", { className: ["tcalc-section", className ?? ""].filter(Boolean).join(" "), ...props, children: /* @__PURE__ */ jsx60("div", { className: "tcalc-container", children: /* @__PURE__ */ jsxs56("div", { className: "tcalc-card", children: [
8605
8664
  /* @__PURE__ */ jsxs56("div", { className: "tcalc-column", children: [
8606
- /* @__PURE__ */ jsx60("h3", { className: "tcalc-panelTitle", children: "Calculator" }),
8665
+ /* @__PURE__ */ jsx60("h3", { className: "tcalc-panelTitle", children: copy.calculatorPanel }),
8607
8666
  /* @__PURE__ */ jsx60(
8608
8667
  SymbolCombobox,
8609
8668
  {
@@ -8612,20 +8671,21 @@ var TradingCalculator = ({
8612
8671
  selected: selectedSymbol,
8613
8672
  onSelect: handleSelectSymbol,
8614
8673
  error: errors.symbol,
8615
- inputId: symbolInputId
8674
+ inputId: symbolInputId,
8675
+ copy
8616
8676
  }
8617
8677
  ),
8618
8678
  /* @__PURE__ */ jsxs56("div", { className: "tcalc-infoGrid", children: [
8619
- /* @__PURE__ */ jsx60(InfoRow, { label: "Contract size", value: selectedSymbol ? integerFormatter.format(selectedSymbol.contract_size) : "-" }),
8620
- /* @__PURE__ */ jsx60(InfoRow, { label: "Pip size", value: selectedSymbol ? String(selectedSymbol.pip_size) : "-" }),
8679
+ /* @__PURE__ */ jsx60(InfoRow, { label: copy.contractSize, value: selectedSymbol ? integerFormatter.format(selectedSymbol.contract_size) : "-" }),
8680
+ /* @__PURE__ */ jsx60(InfoRow, { label: copy.pipSize, value: selectedSymbol ? String(selectedSymbol.pip_size) : "-" }),
8621
8681
  /* @__PURE__ */ jsx60(
8622
8682
  InfoRow,
8623
8683
  {
8624
- label: "Effective Leverage",
8684
+ label: copy.effectiveLeverage,
8625
8685
  value: selectedSymbol ? `1 : ${Math.round((selectedSymbol.effective_leverage + Number.EPSILON) * 100) / 100}` : "-"
8626
8686
  }
8627
8687
  ),
8628
- /* @__PURE__ */ jsx60(InfoRow, { label: "Point size", value: selectedSymbol ? String(selectedSymbol.point_size) : "-" })
8688
+ /* @__PURE__ */ jsx60(InfoRow, { label: copy.pointSize, value: selectedSymbol ? String(selectedSymbol.point_size) : "-" })
8629
8689
  ] }),
8630
8690
  /* @__PURE__ */ jsxs56("div", { className: "tcalc-field", children: [
8631
8691
  /* @__PURE__ */ jsx60(
@@ -8634,7 +8694,8 @@ var TradingCalculator = ({
8634
8694
  id: volumeInputId,
8635
8695
  className: "tcalc-numberField",
8636
8696
  label: /* @__PURE__ */ jsxs56(Fragment11, { children: [
8637
- "Volume in Lots ",
8697
+ copy.volume,
8698
+ " ",
8638
8699
  /* @__PURE__ */ jsx60("span", { className: "tcalc-required", children: "*" })
8639
8700
  ] }),
8640
8701
  status: errors.volume ? "fail" : "neutral",
@@ -8651,7 +8712,7 @@ var TradingCalculator = ({
8651
8712
  const isNegative = value !== "" && !Number.isNaN(parsed) && parsed <= 0;
8652
8713
  setErrors((prev) => ({
8653
8714
  ...prev,
8654
- volume: isNegative ? "Volume must be greater than 0." : void 0
8715
+ volume: isNegative ? copy.volumePositiveError : void 0
8655
8716
  }));
8656
8717
  }
8657
8718
  }
@@ -8669,7 +8730,8 @@ var TradingCalculator = ({
8669
8730
  id: priceInputId,
8670
8731
  className: "tcalc-numberField",
8671
8732
  label: /* @__PURE__ */ jsxs56(Fragment11, { children: [
8672
- "Asset price ",
8733
+ copy.assetPrice,
8734
+ " ",
8673
8735
  /* @__PURE__ */ jsx60("span", { className: "tcalc-required", children: "*" })
8674
8736
  ] }),
8675
8737
  status: errors.price ? "fail" : "neutral",
@@ -8698,7 +8760,7 @@ var TradingCalculator = ({
8698
8760
  type: "button",
8699
8761
  colorScheme: "coral",
8700
8762
  variant: "primary",
8701
- label: isCalculating ? "Calculating\u2026" : "Calculate",
8763
+ label: isCalculating ? copy.calculating : copy.calculate,
8702
8764
  onClick: handleCalculate,
8703
8765
  disabled: isCalculating,
8704
8766
  fullWidth: true
@@ -8710,7 +8772,7 @@ var TradingCalculator = ({
8710
8772
  type: "button",
8711
8773
  colorScheme: "black",
8712
8774
  variant: "secondary",
8713
- label: "Reset",
8775
+ label: copy.reset,
8714
8776
  onClick: handleReset,
8715
8777
  fullWidth: true
8716
8778
  }
@@ -8718,16 +8780,16 @@ var TradingCalculator = ({
8718
8780
  ] })
8719
8781
  ] }),
8720
8782
  /* @__PURE__ */ jsxs56("div", { className: "tcalc-column tcalc-column--results", children: [
8721
- /* @__PURE__ */ jsx60("h3", { className: "tcalc-panelTitle", children: "Results" }),
8722
- showMargin && /* @__PURE__ */ jsx60(ResultBox, { label: "Margin Required", value: results ? formatMoney(results.marginRequired) : "$" }),
8783
+ /* @__PURE__ */ jsx60("h3", { className: "tcalc-panelTitle", children: copy.resultsPanel }),
8784
+ showMargin && /* @__PURE__ */ jsx60(ResultBox, { label: copy.marginRequired, value: results ? formatMoney(results.marginRequired) : "$" }),
8723
8785
  showTradeVolume && showPip && /* @__PURE__ */ jsxs56("div", { className: "tcalc-resultRow", children: [
8724
- /* @__PURE__ */ jsx60(ResultBox, { label: "Trade Volume", value: results ? formatInteger(results.tradeVolume) : "$" }),
8725
- /* @__PURE__ */ jsx60(ResultBox, { label: "Pip value", value: results ? formatMoney(results.pipValue) : "$" })
8786
+ /* @__PURE__ */ jsx60(ResultBox, { label: copy.tradeVolume, value: results ? formatInteger(results.tradeVolume) : "$" }),
8787
+ /* @__PURE__ */ jsx60(ResultBox, { label: copy.pipValue, value: results ? formatMoney(results.pipValue) : "$" })
8726
8788
  ] }),
8727
- !showTradeVolume && showPip && /* @__PURE__ */ jsx60(ResultBox, { label: "Pip value", value: results ? formatMoney(results.pipValue) : "$" }),
8789
+ !showTradeVolume && showPip && /* @__PURE__ */ jsx60(ResultBox, { label: copy.pipValue, value: results ? formatMoney(results.pipValue) : "$" }),
8728
8790
  showSwap && /* @__PURE__ */ jsxs56("div", { className: "tcalc-resultRow", children: [
8729
- /* @__PURE__ */ jsx60(ResultBox, { label: "Swap Charge Long", value: results ? formatMoney(results.swapChargeLong) : "$" }),
8730
- /* @__PURE__ */ jsx60(ResultBox, { label: "Swap Charge Short", value: results ? formatMoney(results.swapChargeShort) : "$" })
8791
+ /* @__PURE__ */ jsx60(ResultBox, { label: copy.swapChargeLong, value: results ? formatMoney(results.swapChargeLong) : "$" }),
8792
+ /* @__PURE__ */ jsx60(ResultBox, { label: copy.swapChargeShort, value: results ? formatMoney(results.swapChargeShort) : "$" })
8731
8793
  ] }),
8732
8794
  calcError && /* @__PURE__ */ jsxs56("p", { className: "tcalc-fieldError tcalc-calcError", children: [
8733
8795
  /* @__PURE__ */ jsx60(ErrorIcon, {}),
@@ -8736,12 +8798,12 @@ var TradingCalculator = ({
8736
8798
  ] }),
8737
8799
  showSwap && /* @__PURE__ */ jsxs56(Fragment11, { children: [
8738
8800
  /* @__PURE__ */ jsxs56("div", { className: "tcalc-infoGrid", children: [
8739
- /* @__PURE__ */ jsx60(InfoRow, { label: "Swap long rate", value: selectedSymbol ? String(selectedSymbol.swap_rate_long) : "-" }),
8740
- /* @__PURE__ */ jsx60(InfoRow, { label: "Swap short rate", value: selectedSymbol ? String(selectedSymbol.swap_rate_short) : "-" })
8801
+ /* @__PURE__ */ jsx60(InfoRow, { label: copy.swapLongRate, value: selectedSymbol ? String(selectedSymbol.swap_rate_long) : "-" }),
8802
+ /* @__PURE__ */ jsx60(InfoRow, { label: copy.swapShortRate, value: selectedSymbol ? String(selectedSymbol.swap_rate_short) : "-" })
8741
8803
  ] }),
8742
- /* @__PURE__ */ jsx60(InfoRow, { separator: ":", label: "Swap calculation type", value: selectedSymbol ? selectedSymbol.swap_mode : "-" }),
8743
- /* @__PURE__ */ jsx60(InfoRow, { separator: ":", label: "Three-day swap", value: selectedSymbol ? selectedSymbol.three_day_swap || "No" : "-" }),
8744
- /* @__PURE__ */ jsx60(InfoRow, { separator: ":", label: "Weekend swaps", value: selectedSymbol ? String(selectedSymbol.weekend_swaps ?? "-") : "-" })
8804
+ /* @__PURE__ */ jsx60(InfoRow, { separator: ":", label: copy.swapCalculationType, value: selectedSymbol ? selectedSymbol.swap_mode : "-" }),
8805
+ /* @__PURE__ */ jsx60(InfoRow, { separator: ":", label: copy.threeDaySwap, value: selectedSymbol ? selectedSymbol.three_day_swap || copy.threeDaySwapFallback : "-" }),
8806
+ /* @__PURE__ */ jsx60(InfoRow, { separator: ":", label: copy.weekendSwaps, value: selectedSymbol ? String(selectedSymbol.weekend_swaps ?? "-") : "-" })
8745
8807
  ] })
8746
8808
  ] })
8747
8809
  ] }) }) });
@@ -9346,9 +9408,11 @@ function browserSupportsMarketStreaming() {
9346
9408
 
9347
9409
  // templates/cryptoMarketData/conversion.ts
9348
9410
  function convertAmounts(options) {
9411
+ const waitingForPrice = options.messages?.waitingForPrice ?? "Waiting for a live price";
9412
+ const invalidAmount = options.messages?.invalidAmount ?? "Enter a valid amount";
9349
9413
  const price = options.price;
9350
9414
  if (price == null || !Number.isFinite(price) || price <= 0) {
9351
- return { pay: options.payInput, receive: options.receiveInput, valid: false, error: "Waiting for a live price" };
9415
+ return { pay: options.payInput, receive: options.receiveInput, valid: false, error: waitingForPrice };
9352
9416
  }
9353
9417
  const source = options.edited === "pay" ? options.payInput : options.receiveInput;
9354
9418
  const trimmed = source.trim();
@@ -9361,7 +9425,7 @@ function convertAmounts(options) {
9361
9425
  pay: options.payInput,
9362
9426
  receive: options.receiveInput,
9363
9427
  valid: false,
9364
- error: "Enter a valid amount"
9428
+ error: invalidAmount
9365
9429
  };
9366
9430
  }
9367
9431
  if (options.side === "buy") {
@@ -9906,9 +9970,39 @@ function useCryptoMarketData(options) {
9906
9970
  return snapshot;
9907
9971
  }
9908
9972
 
9973
+ // templates/CryptoTradeConverter/CryptoTradeConverter.types.ts
9974
+ var DEFAULT_CRYPTO_TRADE_CONVERTER_TITLE = "Buy and sell in seconds";
9975
+ 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.";
9976
+ var DEFAULT_CRYPTO_TRADE_CONVERTER_LABELS = {
9977
+ buy: "Buy",
9978
+ sell: "Sell",
9979
+ connecting: "Connecting to live prices\u2026",
9980
+ livePrices: "Live prices",
9981
+ waitingForPrice: "Waiting for a live price",
9982
+ unsupportedBrowser: "Please use a current browser to view live prices.",
9983
+ selectMarket: "Select a market",
9984
+ chooseMarket: "Choose a market",
9985
+ tradeSideGroup: "Trade side",
9986
+ youPay: "You pay",
9987
+ youReceive: "You receive",
9988
+ amountToSpend: "Amount to spend",
9989
+ primaryAmountAria: "Primary trade amount",
9990
+ convertedAmountAria: "Converted trade amount",
9991
+ estimateTemplate: "Estimate at 1 {base} = {price} {quote}. Fees and spread excluded.",
9992
+ change24hSuffix: "(24h)",
9993
+ waitingForLivePrice: "Waiting for live price\u2026",
9994
+ invalidAmount: "Enter a valid amount"
9995
+ };
9996
+
9909
9997
  // templates/CryptoTradeConverter/CryptoTradeConverter.tsx
9910
9998
  import { Fragment as Fragment14, jsx as jsx62, jsxs as jsxs58 } from "react/jsx-runtime";
9911
9999
  var DEFAULT_TRADE_HREF = "https://deriv.exchange";
10000
+ function fillTemplate3(template, values) {
10001
+ return template.replace(/\{(\w+)\}/g, (_, key) => values[key] ?? "");
10002
+ }
10003
+ function mergeLabels2(overrides) {
10004
+ return { ...DEFAULT_CRYPTO_TRADE_CONVERTER_LABELS, ...overrides };
10005
+ }
9912
10006
  function matchPair(rows, defaultPair) {
9913
10007
  if (!rows.length) return null;
9914
10008
  if (!defaultPair) {
@@ -9961,8 +10055,12 @@ var CryptoTradeConverter = ({
9961
10055
  defaultPair,
9962
10056
  tradeHref = DEFAULT_TRADE_HREF,
9963
10057
  apiOrigin,
10058
+ title = DEFAULT_CRYPTO_TRADE_CONVERTER_TITLE,
10059
+ subtitle = DEFAULT_CRYPTO_TRADE_CONVERTER_SUBTITLE,
10060
+ labels: labelsProp,
9964
10061
  className
9965
10062
  }) => {
10063
+ const copy = mergeLabels2(labelsProp);
9966
10064
  const reactId = useId7().replace(/:/g, "");
9967
10065
  const listboxId = `ctc-listbox-${reactId}`;
9968
10066
  const payId = `ctc-pay-${reactId}`;
@@ -9990,9 +10088,13 @@ var CryptoTradeConverter = ({
9990
10088
  price: selected?.last ?? null,
9991
10089
  payInput: pay,
9992
10090
  receiveInput: receive,
9993
- edited
10091
+ edited,
10092
+ messages: {
10093
+ waitingForPrice: copy.waitingForPrice,
10094
+ invalidAmount: copy.invalidAmount
10095
+ }
9994
10096
  }),
9995
- [side, selected?.last, pay, receive, edited]
10097
+ [side, selected?.last, pay, receive, edited, copy.waitingForPrice, copy.invalidAmount]
9996
10098
  );
9997
10099
  useEffect21(() => {
9998
10100
  if (edited === "pay") setReceive(conversion.receive);
@@ -10063,8 +10165,12 @@ var CryptoTradeConverter = ({
10063
10165
  const changeClass = selected?.change24hPct == null ? "" : selected.change24hPct >= 0 ? "ctc-change--up" : "ctc-change--down";
10064
10166
  const payValue = edited === "pay" ? pay : conversion.pay;
10065
10167
  const receiveValue = edited === "receive" ? receive : conversion.receive;
10066
- const receiveMeta = priceReady && selected ? `Estimate at 1 ${selected.base} = ${formatPrice(selected.last, selected.pricePrecision)} ${selected.quote}. Fees and spread excluded.` : snapshot.status === "unsupported" ? "Please use a current browser to view live prices." : conversion.error || (snapshot.status === "idle" || snapshot.status === "connecting" ? "Connecting to live prices\u2026" : "Waiting for live price\u2026");
10067
- const ctaLabel = side === "buy" ? "Buy" : "Sell";
10168
+ const receiveMeta = priceReady && selected ? fillTemplate3(copy.estimateTemplate, {
10169
+ base: selected.base,
10170
+ price: formatPrice(selected.last, selected.pricePrecision),
10171
+ quote: selected.quote
10172
+ }) : snapshot.status === "unsupported" ? copy.unsupportedBrowser : conversion.error || (snapshot.status === "idle" || snapshot.status === "connecting" ? copy.connecting : copy.waitingForLivePrice);
10173
+ const ctaLabel = side === "buy" ? copy.buy : copy.sell;
10068
10174
  const ctaClass = ["ctc-cta__btn", side === "sell" ? "is-sell" : "is-buy"].join(" ");
10069
10175
  return /* @__PURE__ */ jsx62(
10070
10176
  "section",
@@ -10076,8 +10182,8 @@ var CryptoTradeConverter = ({
10076
10182
  "aria-labelledby": `ctc-heading-${reactId}`,
10077
10183
  children: /* @__PURE__ */ jsxs58("div", { className: "ctc-container", children: [
10078
10184
  /* @__PURE__ */ jsxs58("header", { className: "ctc-header", children: [
10079
- /* @__PURE__ */ jsx62("h2", { id: `ctc-heading-${reactId}`, className: "ctc-title", children: "Buy and sell in seconds" }),
10080
- /* @__PURE__ */ jsx62("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." })
10185
+ /* @__PURE__ */ jsx62("h2", { id: `ctc-heading-${reactId}`, className: "ctc-title", children: title }),
10186
+ /* @__PURE__ */ jsx62("p", { className: "ctc-subtitle", children: subtitle })
10081
10187
  ] }),
10082
10188
  /* @__PURE__ */ jsx62("div", { className: "ctc-card", children: /* @__PURE__ */ jsxs58("div", { className: "ctc-widget", children: [
10083
10189
  /* @__PURE__ */ jsxs58(
@@ -10089,7 +10195,7 @@ var CryptoTradeConverter = ({
10089
10195
  "aria-live": "polite",
10090
10196
  children: [
10091
10197
  /* @__PURE__ */ jsx62("span", { className: "ctc-status-dot", "aria-hidden": "true" }),
10092
- /* @__PURE__ */ jsx62("span", { children: snapshot.status === "idle" || snapshot.status === "connecting" ? "Connecting to live prices\u2026" : snapshot.statusMessage || (priceReady ? "Live prices" : "Waiting for live price\u2026") })
10198
+ /* @__PURE__ */ jsx62("span", { children: snapshot.status === "idle" || snapshot.status === "connecting" ? copy.connecting : snapshot.statusMessage || (priceReady ? copy.livePrices : copy.waitingForLivePrice) })
10093
10199
  ]
10094
10200
  }
10095
10201
  ),
@@ -10129,12 +10235,13 @@ var CryptoTradeConverter = ({
10129
10235
  " ",
10130
10236
  /* @__PURE__ */ jsxs58("span", { className: ["ctc-change", changeClass].filter(Boolean).join(" "), children: [
10131
10237
  formatPct(selected.change24hPct),
10132
- " (24h)"
10238
+ " ",
10239
+ copy.change24hSuffix
10133
10240
  ] })
10134
10241
  ] })
10135
10242
  ] })
10136
10243
  ] }) : /* @__PURE__ */ jsxs58("span", { className: "ctc-pair__meta", children: [
10137
- /* @__PURE__ */ jsx62("span", { className: "ctc-pair__name", children: "Select a market" }),
10244
+ /* @__PURE__ */ jsx62("span", { className: "ctc-pair__name", children: copy.selectMarket }),
10138
10245
  /* @__PURE__ */ jsx62("span", { className: "ctc-pair__price", children: "\u2014" })
10139
10246
  ] }),
10140
10247
  /* @__PURE__ */ jsx62(ChevronIcon4, { className: "ctc-pair__chevron" })
@@ -10148,11 +10255,11 @@ var CryptoTradeConverter = ({
10148
10255
  id: listboxId,
10149
10256
  className: "ctc-listbox",
10150
10257
  role: "listbox",
10151
- "aria-label": "Choose a market",
10258
+ "aria-label": copy.chooseMarket,
10152
10259
  tabIndex: -1,
10153
10260
  onKeyDown: onPickerKeyDown,
10154
10261
  children: [
10155
- /* @__PURE__ */ jsx62("li", { className: "ctc-listbox__header", role: "presentation", children: "Choose a market" }),
10262
+ /* @__PURE__ */ jsx62("li", { className: "ctc-listbox__header", role: "presentation", children: copy.chooseMarket }),
10156
10263
  sortedRows.map((row, index) => {
10157
10264
  const selectedOption = selected?.id === row.id;
10158
10265
  const active = index === activeOption;
@@ -10192,7 +10299,7 @@ var CryptoTradeConverter = ({
10192
10299
  }
10193
10300
  ) : null
10194
10301
  ] }),
10195
- /* @__PURE__ */ jsxs58("div", { className: "ctc-seg", role: "group", "aria-label": "Trade side", children: [
10302
+ /* @__PURE__ */ jsxs58("div", { className: "ctc-seg", role: "group", "aria-label": copy.tradeSideGroup, children: [
10196
10303
  /* @__PURE__ */ jsx62(
10197
10304
  "button",
10198
10305
  {
@@ -10200,7 +10307,7 @@ var CryptoTradeConverter = ({
10200
10307
  className: side === "buy" ? "ctc-seg__btn is-active is-buy" : "ctc-seg__btn is-buy",
10201
10308
  "aria-pressed": side === "buy",
10202
10309
  onClick: () => setSide("buy"),
10203
- children: "Buy"
10310
+ children: copy.buy
10204
10311
  }
10205
10312
  ),
10206
10313
  /* @__PURE__ */ jsx62(
@@ -10210,7 +10317,7 @@ var CryptoTradeConverter = ({
10210
10317
  className: side === "sell" ? "ctc-seg__btn is-active is-sell" : "ctc-seg__btn is-sell",
10211
10318
  "aria-pressed": side === "sell",
10212
10319
  onClick: () => setSide("sell"),
10213
- children: "Sell"
10320
+ children: copy.sell
10214
10321
  }
10215
10322
  )
10216
10323
  ] }),
@@ -10221,7 +10328,7 @@ var CryptoTradeConverter = ({
10221
10328
  className: ["ctc-amount", conversion.error ? "is-invalid" : ""].filter(Boolean).join(" "),
10222
10329
  htmlFor: payId,
10223
10330
  children: [
10224
- /* @__PURE__ */ jsx62("span", { className: "ctc-amount__label", children: "You pay" }),
10331
+ /* @__PURE__ */ jsx62("span", { className: "ctc-amount__label", children: copy.youPay }),
10225
10332
  /* @__PURE__ */ jsxs58("span", { className: "ctc-amount__row", children: [
10226
10333
  /* @__PURE__ */ jsx62(
10227
10334
  "input",
@@ -10233,7 +10340,7 @@ var CryptoTradeConverter = ({
10233
10340
  autoComplete: "off",
10234
10341
  placeholder: "0.00",
10235
10342
  value: payValue,
10236
- "aria-label": "Primary trade amount",
10343
+ "aria-label": copy.primaryAmountAria,
10237
10344
  "aria-invalid": Boolean(conversion.error),
10238
10345
  "aria-describedby": statusId,
10239
10346
  onChange: (event) => {
@@ -10244,7 +10351,7 @@ var CryptoTradeConverter = ({
10244
10351
  ),
10245
10352
  /* @__PURE__ */ jsx62("span", { className: "ctc-amount__unit", children: labels.pay })
10246
10353
  ] }),
10247
- /* @__PURE__ */ jsx62("span", { className: "ctc-amount__meta", children: "Amount to spend" })
10354
+ /* @__PURE__ */ jsx62("span", { className: "ctc-amount__meta", children: copy.amountToSpend })
10248
10355
  ]
10249
10356
  }
10250
10357
  ),
@@ -10254,7 +10361,7 @@ var CryptoTradeConverter = ({
10254
10361
  className: ["ctc-amount", conversion.error ? "is-invalid" : ""].filter(Boolean).join(" "),
10255
10362
  htmlFor: receiveId,
10256
10363
  children: [
10257
- /* @__PURE__ */ jsx62("span", { className: "ctc-amount__label", children: "You receive" }),
10364
+ /* @__PURE__ */ jsx62("span", { className: "ctc-amount__label", children: copy.youReceive }),
10258
10365
  /* @__PURE__ */ jsxs58("span", { className: "ctc-amount__row", children: [
10259
10366
  /* @__PURE__ */ jsx62(
10260
10367
  "input",
@@ -10266,7 +10373,7 @@ var CryptoTradeConverter = ({
10266
10373
  autoComplete: "off",
10267
10374
  placeholder: "0.00",
10268
10375
  value: receiveValue,
10269
- "aria-label": "Converted trade amount",
10376
+ "aria-label": copy.convertedAmountAria,
10270
10377
  "aria-invalid": Boolean(conversion.error),
10271
10378
  "aria-describedby": `ctc-rate-${reactId}`,
10272
10379
  onChange: (event) => {
@@ -10309,7 +10416,37 @@ var CryptoTradeConverter = ({
10309
10416
 
10310
10417
  // templates/CryptoMarketsTable/CryptoMarketsTable.tsx
10311
10418
  import { useEffect as useEffect22, useId as useId8, useMemo as useMemo9, useRef as useRef24, useState as useState27 } from "react";
10419
+
10420
+ // templates/CryptoMarketsTable/CryptoMarketsTable.types.ts
10421
+ var DEFAULT_CRYPTO_MARKETS_TABLE_TITLE = "Explore the markets";
10422
+ var DEFAULT_CRYPTO_MARKETS_TABLE_LABELS = {
10423
+ spot: "Spot",
10424
+ futures: "Futures",
10425
+ live: "Live",
10426
+ connecting: "Connecting\u2026",
10427
+ marketTypeGroup: "Market type",
10428
+ quoteCurrencyTabs: "Quote currency",
10429
+ scrollRegion: "Market prices. Scroll horizontally to see all columns.",
10430
+ columnName: "Name",
10431
+ columnPrice: "Price",
10432
+ column1h: "1h %",
10433
+ column24h: "24h %",
10434
+ column7d: "7d chart",
10435
+ unsupportedBrowser: "Please use a current browser to view market prices.",
10436
+ loadError: "Unable to load market data. Retrying\u2026",
10437
+ loading: "Loading market data\u2026",
10438
+ emptyPairsTemplate: "No {quote} pairs on {kind} yet\u2026",
10439
+ sparklineAriaTemplate: "{name} available hourly price history, up to 7 days"
10440
+ };
10441
+
10442
+ // templates/CryptoMarketsTable/CryptoMarketsTable.tsx
10312
10443
  import { jsx as jsx63, jsxs as jsxs59 } from "react/jsx-runtime";
10444
+ function fillTemplate4(template, values) {
10445
+ return template.replace(/\{(\w+)\}/g, (_, key) => values[key] ?? "");
10446
+ }
10447
+ function mergeLabels3(overrides) {
10448
+ return { ...DEFAULT_CRYPTO_MARKETS_TABLE_LABELS, ...overrides };
10449
+ }
10313
10450
  function CoinAvatar2({ base, icon }) {
10314
10451
  const [failed, setFailed] = useState27(false);
10315
10452
  if (failed) {
@@ -10415,8 +10552,11 @@ var CryptoMarketsTable = ({
10415
10552
  initialQuoteCurrency = "USDT",
10416
10553
  apiOrigin,
10417
10554
  maxRows,
10555
+ title = DEFAULT_CRYPTO_MARKETS_TABLE_TITLE,
10556
+ labels: labelsProp,
10418
10557
  className
10419
10558
  }) => {
10559
+ const copy = mergeLabels3(labelsProp);
10420
10560
  const reactId = useId8().replace(/:/g, "");
10421
10561
  const [kind, setKind] = useState27(initialMarket);
10422
10562
  const [quote, setQuote] = useState27(initialQuoteCurrency.toUpperCase());
@@ -10463,7 +10603,14 @@ var CryptoMarketsTable = ({
10463
10603
  }
10464
10604
  };
10465
10605
  const statusTone = snapshot.status === "live" ? "live" : snapshot.status === "reconnecting" || snapshot.status === "stale" ? "warn" : snapshot.status === "error" || snapshot.status === "unsupported" ? "error" : "muted";
10466
- 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`;
10606
+ 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 });
10607
+ const columnLabels = [
10608
+ ["name", copy.columnName],
10609
+ ["last", copy.columnPrice],
10610
+ ["change1hPct", copy.column1h],
10611
+ ["change24hPct", copy.column24h],
10612
+ ["change7dPct", copy.column7d]
10613
+ ];
10467
10614
  return /* @__PURE__ */ jsx63(
10468
10615
  "section",
10469
10616
  {
@@ -10472,26 +10619,26 @@ var CryptoMarketsTable = ({
10472
10619
  "aria-labelledby": `cmt-heading-${reactId}`,
10473
10620
  children: /* @__PURE__ */ jsxs59("div", { className: "cmt-container", children: [
10474
10621
  /* @__PURE__ */ jsxs59("header", { className: "cmt-header", children: [
10475
- /* @__PURE__ */ jsx63("h2", { id: `cmt-heading-${reactId}`, className: "cmt-title", children: "Explore the markets" }),
10622
+ /* @__PURE__ */ jsx63("h2", { id: `cmt-heading-${reactId}`, className: "cmt-title", children: title }),
10476
10623
  /* @__PURE__ */ jsxs59("p", { className: `cmt-status cmt-status--${statusTone}`, role: "status", "aria-live": "polite", children: [
10477
10624
  /* @__PURE__ */ jsx63("span", { className: "cmt-status-dot", "aria-hidden": "true" }),
10478
- /* @__PURE__ */ jsx63("span", { children: snapshot.status === "live" ? "Live" : snapshot.statusMessage || "Connecting\u2026" })
10625
+ /* @__PURE__ */ jsx63("span", { children: snapshot.status === "live" ? copy.live : snapshot.statusMessage || copy.connecting })
10479
10626
  ] })
10480
10627
  ] }),
10481
10628
  /* @__PURE__ */ jsxs59("div", { className: "cmt-card", children: [
10482
10629
  /* @__PURE__ */ jsxs59("div", { className: "cmt-toolbar", children: [
10483
- /* @__PURE__ */ jsx63("div", { className: "cmt-seg", role: "group", "aria-label": "Market type", children: ["spot", "futures"].map((value) => /* @__PURE__ */ jsx63(
10630
+ /* @__PURE__ */ jsx63("div", { className: "cmt-seg", role: "group", "aria-label": copy.marketTypeGroup, children: ["spot", "futures"].map((value) => /* @__PURE__ */ jsx63(
10484
10631
  "button",
10485
10632
  {
10486
10633
  type: "button",
10487
10634
  className: kind === value ? "cmt-seg__btn is-active" : "cmt-seg__btn",
10488
10635
  "aria-pressed": kind === value,
10489
10636
  onClick: () => setKind(value),
10490
- children: value === "spot" ? "Spot" : "Futures"
10637
+ children: value === "spot" ? copy.spot : copy.futures
10491
10638
  },
10492
10639
  value
10493
10640
  )) }),
10494
- /* @__PURE__ */ jsx63("div", { className: "cmt-tabs", role: "tablist", "aria-label": "Quote currency", children: snapshot.quotes.map((q) => /* @__PURE__ */ jsx63(
10641
+ /* @__PURE__ */ jsx63("div", { className: "cmt-tabs", role: "tablist", "aria-label": copy.quoteCurrencyTabs, children: snapshot.quotes.map((q) => /* @__PURE__ */ jsx63(
10495
10642
  "button",
10496
10643
  {
10497
10644
  type: "button",
@@ -10511,15 +10658,9 @@ var CryptoMarketsTable = ({
10511
10658
  className: ["cmt-scroll", scrolled ? "is-scrolled" : ""].filter(Boolean).join(" "),
10512
10659
  tabIndex: 0,
10513
10660
  role: "region",
10514
- "aria-label": "Market prices. Scroll horizontally to see all columns.",
10661
+ "aria-label": copy.scrollRegion,
10515
10662
  children: /* @__PURE__ */ jsxs59("table", { className: "cmt-table", children: [
10516
- /* @__PURE__ */ jsx63("thead", { children: /* @__PURE__ */ jsx63("tr", { children: [
10517
- ["name", "Name"],
10518
- ["last", "Price"],
10519
- ["change1hPct", "1h %"],
10520
- ["change24hPct", "24h %"],
10521
- ["change7dPct", "7d chart"]
10522
- ].map(([key, label]) => {
10663
+ /* @__PURE__ */ jsx63("thead", { children: /* @__PURE__ */ jsx63("tr", { children: columnLabels.map(([key, label]) => {
10523
10664
  const active = sortKey === key;
10524
10665
  return /* @__PURE__ */ jsx63(
10525
10666
  "th",
@@ -10564,7 +10705,9 @@ var CryptoMarketsTable = ({
10564
10705
  {
10565
10706
  values: row.spark7d,
10566
10707
  up: (row.change7dPct ?? 0) >= 0,
10567
- label: `${row.name} available hourly price history, up to 7 days`,
10708
+ label: fillTemplate4(copy.sparklineAriaTemplate, {
10709
+ name: row.name
10710
+ }),
10568
10711
  reducedMotion
10569
10712
  }
10570
10713
  ) })
@@ -12221,10 +12364,16 @@ export {
12221
12364
  CryptoMarketsTable,
12222
12365
  CryptoTradeConverter,
12223
12366
  DEFAULT_AWARDS,
12367
+ DEFAULT_CRYPTO_MARKETS_TABLE_LABELS,
12368
+ DEFAULT_CRYPTO_MARKETS_TABLE_TITLE,
12369
+ DEFAULT_CRYPTO_TRADE_CONVERTER_LABELS,
12370
+ DEFAULT_CRYPTO_TRADE_CONVERTER_SUBTITLE,
12371
+ DEFAULT_CRYPTO_TRADE_CONVERTER_TITLE,
12224
12372
  DEFAULT_CULTURE_CARDS,
12225
12373
  DEFAULT_EXCHANGE_RATE_WS_URL,
12226
12374
  DEFAULT_SYMBOLS_ENDPOINT,
12227
12375
  DEFAULT_TRADE_HREF,
12376
+ DEFAULT_TRADING_CALCULATOR_LABELS,
12228
12377
  DayNightTransition,
12229
12378
  DocumentChips,
12230
12379
  DotLottieInView,