@wise/dynamic-flow-client-internal 5.30.0 → 5.31.0-experimental-a2135d6

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/build/main.mjs CHANGED
@@ -6768,15 +6768,108 @@ function ChipsTabsRendererComponent({ margin, tabs, control, tags }) {
6768
6768
 
6769
6769
  // ../renderers/src/TextInputRenderer.tsx
6770
6770
  import { InputGroup as InputGroup5 } from "@transferwise/components";
6771
+ import { useState as useState21 } from "react";
6771
6772
 
6772
6773
  // ../renderers/src/components/VariableTextInput.tsx
6773
6774
  import {
6775
+ IconButton as IconButton4,
6774
6776
  Input as Input6,
6775
6777
  InputWithDisplayFormat,
6776
6778
  PhoneNumberInput,
6777
6779
  TextArea,
6778
6780
  TextareaWithDisplayFormat
6779
6781
  } from "@transferwise/components";
6782
+ import { Eye, EyeShut } from "@transferwise/icons";
6783
+ import { useIntl as useIntl21 } from "react-intl";
6784
+ import { useLayoutEffect as useLayoutEffect3, useRef as useRef5, useState as useState20 } from "react";
6785
+
6786
+ // ../renderers/src/messages/ssn-input.messages.ts
6787
+ import { defineMessages as defineMessages15 } from "react-intl";
6788
+ var ssn_input_messages_default = defineMessages15({
6789
+ showValue: {
6790
+ id: "df.wise.ssnInput.showValue",
6791
+ defaultMessage: "Show value",
6792
+ description: "Accessible label for showing a masked SSN or SIN value"
6793
+ },
6794
+ hideValue: {
6795
+ id: "df.wise.ssnInput.hideValue",
6796
+ defaultMessage: "Hide value",
6797
+ description: "Accessible label for hiding an SSN or SIN value"
6798
+ }
6799
+ });
6800
+
6801
+ // ../renderers/src/components/ssnInputUtils.ts
6802
+ function formatSsnValue(rawValue, displayFormat, isVisible) {
6803
+ if (!displayFormat) {
6804
+ return isVisible ? rawValue : "\u2022".repeat(rawValue.length);
6805
+ }
6806
+ const formattedValue = displayFormat.split("").reduce(
6807
+ ({ display, digitsUsed }, character) => {
6808
+ if (character === "*") {
6809
+ return digitsUsed < rawValue.length ? {
6810
+ display: `${display}${isVisible ? rawValue[digitsUsed] : "\u2022"}`,
6811
+ digitsUsed: digitsUsed + 1
6812
+ } : { display, digitsUsed };
6813
+ }
6814
+ return digitsUsed > 0 && digitsUsed < rawValue.length ? { display: `${display}${character}`, digitsUsed } : { display, digitsUsed };
6815
+ },
6816
+ { display: "", digitsUsed: 0 }
6817
+ ).display;
6818
+ const displayFormatDigitCount = displayFormat.split("").filter((character) => character === "*").length;
6819
+ const extraValue = rawValue.slice(displayFormatDigitCount);
6820
+ return `${formattedValue}${isVisible ? extraValue : "\u2022".repeat(extraValue.length)}`;
6821
+ }
6822
+ function getRawOffset(displayValue, displayOffset, rawValueLength) {
6823
+ return Math.min(
6824
+ displayValue.slice(0, displayOffset).split("").filter((character) => character === "\u2022" || /\d/.test(character)).length,
6825
+ rawValueLength
6826
+ );
6827
+ }
6828
+ function replaceSsnSelection(rawValue, displayValue, insertedValue, start, end) {
6829
+ const startOffset = getRawOffset(displayValue, start, rawValue.length);
6830
+ const endOffset = getRawOffset(displayValue, end, rawValue.length);
6831
+ if (!insertedValue) {
6832
+ return void 0;
6833
+ }
6834
+ return {
6835
+ value: `${rawValue.slice(0, startOffset)}${insertedValue}${rawValue.slice(endOffset)}`,
6836
+ caret: startOffset + insertedValue.length
6837
+ };
6838
+ }
6839
+ function deleteSsnSelection(rawValue, displayValue, start, end, backward) {
6840
+ let startOffset = getRawOffset(displayValue, start, rawValue.length);
6841
+ let endOffset = getRawOffset(displayValue, end, rawValue.length);
6842
+ if (startOffset === endOffset) {
6843
+ if (backward) {
6844
+ startOffset = Math.max(0, startOffset - 1);
6845
+ } else {
6846
+ endOffset = Math.min(rawValue.length, endOffset + 1);
6847
+ }
6848
+ }
6849
+ return {
6850
+ value: `${rawValue.slice(0, startOffset)}${rawValue.slice(endOffset)}`,
6851
+ caret: startOffset
6852
+ };
6853
+ }
6854
+ function getDisplayOffset(displayValue, rawOffset) {
6855
+ if (rawOffset === 0) {
6856
+ return 0;
6857
+ }
6858
+ let displayOffset = 0;
6859
+ let digitsSeen = 0;
6860
+ for (const character of displayValue) {
6861
+ if (character === "\u2022" || /\d/.test(character)) {
6862
+ digitsSeen += 1;
6863
+ if (digitsSeen === rawOffset) {
6864
+ return displayOffset + 1;
6865
+ }
6866
+ }
6867
+ displayOffset += 1;
6868
+ }
6869
+ return displayOffset;
6870
+ }
6871
+
6872
+ // ../renderers/src/components/VariableTextInput.tsx
6780
6873
  import { jsx as jsx100 } from "react/jsx-runtime";
6781
6874
  var commonKeys = [
6782
6875
  "autoComplete",
@@ -6792,13 +6885,15 @@ var commonKeys = [
6792
6885
  "value"
6793
6886
  ];
6794
6887
  function VariableTextInput(inputProps) {
6795
- const { id, value, control, onChange } = inputProps;
6888
+ const { id, value, control, onChange, isSsnVisible } = inputProps;
6796
6889
  const commonProps = __spreadProps(__spreadValues({}, pick(inputProps, ...commonKeys)), { name: id });
6797
6890
  switch (control) {
6798
6891
  case "email":
6799
6892
  return /* @__PURE__ */ jsx100(TextInput, __spreadProps(__spreadValues({}, commonProps), { type: "email", onChange }));
6800
6893
  case "password":
6801
6894
  return /* @__PURE__ */ jsx100(TextInput, __spreadProps(__spreadValues({}, commonProps), { type: "password", onChange }));
6895
+ case "ssn-input":
6896
+ return /* @__PURE__ */ jsx100(SsnInput, __spreadProps(__spreadValues({}, commonProps), { isVisible: isSsnVisible, onChange }));
6802
6897
  case "numeric": {
6803
6898
  const numericProps = __spreadProps(__spreadValues({}, commonProps), { type: "number", onWheel });
6804
6899
  return /* @__PURE__ */ jsx100(
@@ -6818,65 +6913,198 @@ function VariableTextInput(inputProps) {
6818
6913
  }
6819
6914
  }
6820
6915
  }
6916
+ function SsnInputToggle({
6917
+ isVisible,
6918
+ onClick
6919
+ }) {
6920
+ const { formatMessage } = useIntl21();
6921
+ return /* @__PURE__ */ jsx100(
6922
+ IconButton4,
6923
+ {
6924
+ priority: "minimal",
6925
+ size: 24,
6926
+ "aria-label": formatMessage(isVisible ? ssn_input_messages_default.hideValue : ssn_input_messages_default.showValue),
6927
+ "aria-pressed": isVisible,
6928
+ onClick,
6929
+ children: isVisible ? /* @__PURE__ */ jsx100(EyeShut, { size: 24 }) : /* @__PURE__ */ jsx100(Eye, { size: 24 })
6930
+ }
6931
+ );
6932
+ }
6821
6933
  function TextInput(props) {
6822
- const _a = props, { control, displayFormat, onChange } = _a, commonProps = __objRest(_a, ["control", "displayFormat", "onChange"]);
6934
+ const _a = props, { control, displayFormat, onChange, type } = _a, commonProps = __objRest(_a, ["control", "displayFormat", "onChange", "type"]);
6823
6935
  const InputWithPattern = control === "textarea" ? TextareaWithDisplayFormat : InputWithDisplayFormat;
6824
6936
  const InputWithoutPattern = control === "textarea" ? TextArea : Input6;
6825
- return displayFormat ? /* @__PURE__ */ jsx100(InputWithPattern, __spreadProps(__spreadValues({ displayPattern: displayFormat }, commonProps), { onChange })) : /* @__PURE__ */ jsx100(InputWithoutPattern, __spreadProps(__spreadValues({}, commonProps), { onChange: (e) => onChange(e.target.value) }));
6937
+ return displayFormat ? /* @__PURE__ */ jsx100(InputWithPattern, __spreadProps(__spreadValues({ displayPattern: displayFormat }, commonProps), { onChange })) : /* @__PURE__ */ jsx100(InputWithoutPattern, __spreadProps(__spreadValues({}, commonProps), { type, onChange: (e) => onChange(e.target.value) }));
6938
+ }
6939
+ function SsnInput(props) {
6940
+ const _a = props, { displayFormat = "", isVisible = false, onChange, value = "" } = _a, commonProps = __objRest(_a, ["displayFormat", "isVisible", "onChange", "value"]);
6941
+ const [editVersion, setEditVersion] = useState20(0);
6942
+ const inputRef = useRef5(null);
6943
+ const selection = useRef5({ start: 0, end: 0 });
6944
+ const pendingCaret = useRef5(null);
6945
+ const rawValue = value.replace(/\D/g, "");
6946
+ const formattedValue = formatSsnValue(rawValue, displayFormat, isVisible);
6947
+ const scheduleCaret = (rawOffset) => {
6948
+ pendingCaret.current = rawOffset;
6949
+ setEditVersion((version) => version + 1);
6950
+ };
6951
+ const replaceSelection = (insertedValue, start, end) => {
6952
+ const result = replaceSsnSelection(rawValue, formattedValue, insertedValue, start, end);
6953
+ if (!result) {
6954
+ return;
6955
+ }
6956
+ scheduleCaret(result.caret);
6957
+ onChange(result.value);
6958
+ };
6959
+ const deleteSelection = (start, end, backward) => {
6960
+ const result = deleteSsnSelection(rawValue, formattedValue, start, end, backward);
6961
+ scheduleCaret(result.caret);
6962
+ onChange(result.value);
6963
+ };
6964
+ useLayoutEffect3(() => {
6965
+ if (pendingCaret.current === null || !inputRef.current) {
6966
+ return;
6967
+ }
6968
+ inputRef.current.setSelectionRange(
6969
+ getDisplayOffset(formattedValue, pendingCaret.current),
6970
+ getDisplayOffset(formattedValue, pendingCaret.current)
6971
+ );
6972
+ pendingCaret.current = null;
6973
+ }, [editVersion, formattedValue, rawValue]);
6974
+ return /* @__PURE__ */ jsx100(
6975
+ "input",
6976
+ __spreadProps(__spreadValues({
6977
+ ref: inputRef
6978
+ }, commonProps), {
6979
+ className: "form-control np-form-control np-input np-input--shape-rectangle",
6980
+ type: "text",
6981
+ inputMode: "numeric",
6982
+ value: formattedValue,
6983
+ onChange: (event) => {
6984
+ const nextValue = event.currentTarget.value;
6985
+ const inputType = event.nativeEvent.inputType;
6986
+ const start = selection.current.start;
6987
+ const end = selection.current.end;
6988
+ if (isVisible || /^\d+$/.test(nextValue)) {
6989
+ const nextRawValue = nextValue.replace(/\D/g, "");
6990
+ scheduleCaret(nextRawValue.length);
6991
+ onChange(nextRawValue);
6992
+ } else if ((inputType == null ? void 0 : inputType.startsWith("delete")) || nextValue.length < formattedValue.length) {
6993
+ deleteSelection(start, end, inputType === "deleteContentBackward");
6994
+ } else {
6995
+ replaceSelection(nextValue.replace(/\D/g, ""), start, end);
6996
+ }
6997
+ },
6998
+ onSelect: (event) => {
6999
+ var _a2, _b;
7000
+ selection.current = {
7001
+ start: (_a2 = event.currentTarget.selectionStart) != null ? _a2 : 0,
7002
+ end: (_b = event.currentTarget.selectionEnd) != null ? _b : 0
7003
+ };
7004
+ },
7005
+ onKeyDown: (event) => {
7006
+ var _a2, _b;
7007
+ selection.current = {
7008
+ start: (_a2 = event.currentTarget.selectionStart) != null ? _a2 : 0,
7009
+ end: (_b = event.currentTarget.selectionEnd) != null ? _b : 0
7010
+ };
7011
+ if (/^\d$/.test(event.key)) {
7012
+ event.preventDefault();
7013
+ replaceSelection(event.key, selection.current.start, selection.current.end);
7014
+ } else if (event.key === "Backspace") {
7015
+ event.preventDefault();
7016
+ deleteSelection(selection.current.start, selection.current.end, true);
7017
+ } else if (event.key === "Delete") {
7018
+ event.preventDefault();
7019
+ deleteSelection(selection.current.start, selection.current.end, false);
7020
+ }
7021
+ },
7022
+ onPaste: (event) => {
7023
+ var _a2, _b;
7024
+ event.preventDefault();
7025
+ selection.current = {
7026
+ start: (_a2 = event.currentTarget.selectionStart) != null ? _a2 : 0,
7027
+ end: (_b = event.currentTarget.selectionEnd) != null ? _b : 0
7028
+ };
7029
+ const pastedValue = event.clipboardData.getData("text").replace(/\D/g, "");
7030
+ replaceSelection(pastedValue, selection.current.start, selection.current.end);
7031
+ }
7032
+ })
7033
+ );
6826
7034
  }
6827
7035
 
6828
7036
  // ../renderers/src/TextInputRenderer.tsx
6829
7037
  import { jsx as jsx101 } from "react/jsx-runtime";
6830
7038
  var TextInputRenderer = {
6831
7039
  canRenderType: "input-text",
6832
- render: (props) => {
6833
- const _a = props, {
7040
+ render: (props) => /* @__PURE__ */ jsx101(TextInputRendererComponent, __spreadValues({}, props))
7041
+ };
7042
+ function TextInputRendererComponent(props) {
7043
+ const [isSsnVisible, setIsSsnVisible] = useState21(false);
7044
+ const _a = props, {
7045
+ id,
7046
+ title,
7047
+ description,
7048
+ help,
7049
+ media,
7050
+ validationState,
7051
+ value: initialValue,
7052
+ onChange
7053
+ } = _a, rest = __objRest(_a, [
7054
+ "id",
7055
+ "title",
7056
+ "description",
7057
+ "help",
7058
+ "media",
7059
+ "validationState",
7060
+ "value",
7061
+ "onChange"
7062
+ ]);
7063
+ const value = initialValue != null ? initialValue : "";
7064
+ const isSsnInput = props.control === "ssn-input";
7065
+ const inputProps = __spreadProps(__spreadValues({}, rest), {
7066
+ value,
7067
+ id,
7068
+ isSsnVisible,
7069
+ onChange: (newValue) => {
7070
+ if ((value != null ? value : "") !== (newValue != null ? newValue : "")) {
7071
+ onChange(newValue);
7072
+ }
7073
+ }
7074
+ });
7075
+ return /* @__PURE__ */ jsx101(
7076
+ FieldInput_default,
7077
+ {
6834
7078
  id,
6835
- title,
7079
+ label: title,
6836
7080
  description,
7081
+ validation: validationState,
7082
+ inlineAlert: props.inlineAlert,
7083
+ loadingState: props.fieldLoadingState,
6837
7084
  help,
6838
- media,
6839
- validationState,
6840
- value: initialValue,
6841
- onChange
6842
- } = _a, rest = __objRest(_a, [
6843
- "id",
6844
- "title",
6845
- "description",
6846
- "help",
6847
- "media",
6848
- "validationState",
6849
- "value",
6850
- "onChange"
6851
- ]);
6852
- const value = initialValue != null ? initialValue : "";
6853
- const inputProps = __spreadProps(__spreadValues({}, rest), {
6854
- value,
6855
- id,
6856
- onChange: (newValue) => {
6857
- if ((value != null ? value : "") !== (newValue != null ? newValue : "")) {
6858
- onChange(newValue);
7085
+ features: props.features,
7086
+ control: props.control,
7087
+ tags: props.tags,
7088
+ children: /* @__PURE__ */ jsx101(
7089
+ InputGroup5,
7090
+ {
7091
+ addonStart: getInputGroupAddonStart(media),
7092
+ addonEnd: isSsnInput ? {
7093
+ interactive: true,
7094
+ content: /* @__PURE__ */ jsx101(
7095
+ SsnInputToggle,
7096
+ {
7097
+ isVisible: isSsnVisible,
7098
+ onClick: () => setIsSsnVisible((visible) => !visible)
7099
+ }
7100
+ )
7101
+ } : void 0,
7102
+ children: /* @__PURE__ */ jsx101(VariableTextInput, __spreadValues({}, inputProps))
6859
7103
  }
6860
- }
6861
- });
6862
- return /* @__PURE__ */ jsx101(
6863
- FieldInput_default,
6864
- {
6865
- id,
6866
- label: title,
6867
- description,
6868
- validation: validationState,
6869
- inlineAlert: props.inlineAlert,
6870
- loadingState: props.fieldLoadingState,
6871
- help,
6872
- features: props.features,
6873
- control: props.control,
6874
- tags: props.tags,
6875
- children: /* @__PURE__ */ jsx101(InputGroup5, { addonStart: getInputGroupAddonStart(media), children: /* @__PURE__ */ jsx101(VariableTextInput, __spreadValues({}, inputProps)) })
6876
- }
6877
- );
6878
- }
6879
- };
7104
+ )
7105
+ }
7106
+ );
7107
+ }
6880
7108
  var TextInputRenderer_default = TextInputRenderer;
6881
7109
 
6882
7110
  // ../renderers/src/UpdatableRenderer.tsx
@@ -7034,7 +7262,7 @@ var LargeUploadRenderer = {
7034
7262
  // ../renderers/src/UpsellRenderer.tsx
7035
7263
  var import_classnames35 = __toESM(require_classnames());
7036
7264
  import { Nudge } from "@transferwise/components";
7037
- import { useState as useState20 } from "react";
7265
+ import { useState as useState22 } from "react";
7038
7266
  import { jsx as jsx104 } from "react/jsx-runtime";
7039
7267
  var UpsellRenderer = {
7040
7268
  canRenderType: "upsell",
@@ -7042,7 +7270,7 @@ var UpsellRenderer = {
7042
7270
  };
7043
7271
  function UpsellRendererComponent(props) {
7044
7272
  const { text, callToAction, media, margin, onDismiss, control, tags } = props;
7045
- const [isVisible, setIsVisible] = useState20(true);
7273
+ const [isVisible, setIsVisible] = useState22(true);
7046
7274
  return isVisible ? /* @__PURE__ */ jsx104(
7047
7275
  Nudge,
7048
7276
  {
@@ -7272,11 +7500,11 @@ var customEventsToAnalytics = {
7272
7500
 
7273
7501
  // src/dynamicFlow/useOnCopy.tsx
7274
7502
  import { useCallback as useCallback2 } from "react";
7275
- import { useIntl as useIntl21 } from "react-intl";
7503
+ import { useIntl as useIntl22 } from "react-intl";
7276
7504
 
7277
7505
  // src/dynamicFlow/messages.ts
7278
- import { defineMessages as defineMessages15 } from "react-intl";
7279
- var messages_default = defineMessages15({
7506
+ import { defineMessages as defineMessages16 } from "react-intl";
7507
+ var messages_default = defineMessages16({
7280
7508
  copied: {
7281
7509
  id: "df.wise.CopyFeedback.copy",
7282
7510
  defaultMessage: "Copied to clipboard",
@@ -7291,7 +7519,7 @@ var messages_default = defineMessages15({
7291
7519
 
7292
7520
  // src/dynamicFlow/useOnCopy.tsx
7293
7521
  var useOnCopy = () => {
7294
- const { formatMessage } = useIntl21();
7522
+ const { formatMessage } = useIntl22();
7295
7523
  const createSnackBar = useSnackBarIfAvailable();
7296
7524
  return useCallback2(
7297
7525
  (copiedContent) => {
@@ -7307,9 +7535,9 @@ var useOnCopy = () => {
7307
7535
 
7308
7536
  // src/dynamicFlow/useWiseHttpClient.tsx
7309
7537
  import { useCallback as useCallback3 } from "react";
7310
- import { useIntl as useIntl22 } from "react-intl";
7538
+ import { useIntl as useIntl23 } from "react-intl";
7311
7539
  var useWiseHttpClient = (httpClient) => {
7312
- const { locale } = useIntl22();
7540
+ const { locale } = useIntl23();
7313
7541
  return useCallback3(
7314
7542
  async (input, init = {}) => {
7315
7543
  const headers = new Headers(init.headers);
@@ -7585,6 +7813,8 @@ var en_default = {
7585
7813
  "df.wise.group.currencies-with-account-details": "Currencies with account details",
7586
7814
  "df.wise.group.popular": "Popular",
7587
7815
  "df.wise.group.recent": "Recent",
7816
+ "df.wise.ssnInput.hideValue": "Hide value",
7817
+ "df.wise.ssnInput.showValue": "Show value",
7588
7818
  "df.wise.step.scrollToBottom": "Scroll to bottom"
7589
7819
  };
7590
7820