@trackunit/react-form-components 1.7.34 → 1.7.37
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/index.cjs.js +352 -362
- package/index.esm.js +348 -356
- package/package.json +8 -8
- package/src/components/BaseInput/BaseInput.d.ts +1 -1
- package/src/components/{DateInput/DateInput.d.ts → DateField/DateBaseInput/DateBaseInput.d.ts} +3 -6
- package/src/components/DateField/DateField.d.ts +2 -2
- package/src/components/{EmailInput/EmailInput.d.ts → EmailField/EmailBaseInput/EmailBaseInput.d.ts} +5 -8
- package/src/components/FormGroup/FormGroup.d.ts +1 -1
- package/src/components/NumberField/NumberBaseInput/NumberBaseInput.d.ts +11 -0
- package/src/components/NumberField/NumberField.d.ts +2 -2
- package/src/components/PasswordField/PasswordBaseInput/PasswordBaseInput.d.ts +12 -0
- package/src/components/PasswordField/PasswordField.d.ts +2 -2
- package/src/components/{PhoneInput/PhoneInput.d.ts → PhoneField/PhoneBaseInput/PhoneBaseInput.d.ts} +4 -7
- package/src/components/PhoneField/PhoneField.d.ts +2 -2
- package/src/components/Search/Search.d.ts +2 -2
- package/src/components/{TextArea/TextArea.d.ts → TextAreaField/TextArea/TextAreaBaseInput.d.ts} +2 -5
- package/src/components/{TextArea/TextArea.variants.d.ts → TextAreaField/TextArea/TextAreaBaseInput.variants.d.ts} +1 -1
- package/src/components/TextAreaField/TextAreaField.d.ts +2 -2
- package/src/components/TextField/TextBaseInput/TextBaseInput.d.ts +11 -0
- package/src/components/TextField/TextField.d.ts +2 -2
- package/src/components/{UrlInput/UrlInput.d.ts → UrlField/UrlBaseInput/UrlBaseInput.d.ts} +4 -7
- package/src/components/UrlField/UrlField.d.ts +2 -2
- package/src/index.d.ts +10 -12
- package/src/components/EmailInput/index.d.ts +0 -1
- package/src/components/NumberInput/NumberInput.d.ts +0 -14
- package/src/components/PasswordField/index.d.ts +0 -1
- package/src/components/PasswordInput/PasswordInput.d.ts +0 -15
- package/src/components/PhoneInput/index.d.ts +0 -1
- package/src/components/TextArea/index.d.ts +0 -1
- package/src/components/TextAreaField/index.d.ts +0 -1
- package/src/components/TextInput/TextInput.d.ts +0 -14
- /package/src/components/{PhoneInput → PhoneField/PhoneBaseInput}/CountryCodeSelect.d.ts +0 -0
- /package/src/components/{PhoneInput → PhoneField/PhoneBaseInput}/CountryCodes.d.ts +0 -0
- /package/src/components/{PhoneInput → PhoneField/PhoneBaseInput}/PhoneInputValidationUtils.d.ts +0 -0
- /package/src/components/{PhoneInput → PhoneField/PhoneBaseInput}/PhoneNumberUtilities.d.ts +0 -0
package/index.cjs.js
CHANGED
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
4
|
var i18nLibraryTranslation = require('@trackunit/i18n-library-translation');
|
|
5
|
+
var polyfill = require('@js-temporal/polyfill');
|
|
5
6
|
var reactComponents = require('@trackunit/react-components');
|
|
6
|
-
var usehooksTs = require('usehooks-ts');
|
|
7
|
-
var cssClassVarianceUtilities = require('@trackunit/css-class-variance-utilities');
|
|
8
7
|
var uiDesignTokens = require('@trackunit/ui-design-tokens');
|
|
9
8
|
var react = require('react');
|
|
9
|
+
var cssClassVarianceUtilities = require('@trackunit/css-class-variance-utilities');
|
|
10
10
|
var stringTs = require('string-ts');
|
|
11
|
-
var
|
|
12
|
-
var polyfill = require('@js-temporal/polyfill');
|
|
11
|
+
var usehooksTs = require('usehooks-ts');
|
|
13
12
|
var parsePhoneNumberFromString = require('libphonenumber-js');
|
|
13
|
+
var sharedUtils = require('@trackunit/shared-utils');
|
|
14
14
|
var reactHookForm = require('react-hook-form');
|
|
15
15
|
var ReactSelect = require('react-select');
|
|
16
16
|
var ReactAsyncCreatableSelect = require('react-select/async-creatable');
|
|
@@ -99,82 +99,6 @@ const setupLibraryTranslations = () => {
|
|
|
99
99
|
i18nLibraryTranslation.registerTranslations(translations);
|
|
100
100
|
};
|
|
101
101
|
|
|
102
|
-
const cvaActionButton = cssClassVarianceUtilities.cvaMerge(["drop-shadow-none", "rounded-md"], {
|
|
103
|
-
variants: {
|
|
104
|
-
size: {
|
|
105
|
-
small: ["w-6", "h-6", "min-h-0"],
|
|
106
|
-
medium: ["w-6", "h-6", "min-h-0"],
|
|
107
|
-
large: ["w-8", "h-8"],
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
defaultVariants: {
|
|
111
|
-
size: "medium",
|
|
112
|
-
},
|
|
113
|
-
});
|
|
114
|
-
const cvaActionContainer = cssClassVarianceUtilities.cvaMerge(["flex", "items-center"], {
|
|
115
|
-
variants: {
|
|
116
|
-
size: {
|
|
117
|
-
//I just measured manually the top/bottom spacing
|
|
118
|
-
//when using the action button inside an input
|
|
119
|
-
//might need tweaking in the future
|
|
120
|
-
small: ["m-[1px]"],
|
|
121
|
-
medium: ["m-[3px]"],
|
|
122
|
-
large: ["m-[7px]"],
|
|
123
|
-
},
|
|
124
|
-
},
|
|
125
|
-
defaultVariants: {
|
|
126
|
-
size: "medium",
|
|
127
|
-
},
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* The ActionButton component is a wrapper over IconButton to perform an action when the onClick event is triggered.
|
|
132
|
-
*
|
|
133
|
-
* @param {ActionButtonProps} props - The props for the ActionButton component
|
|
134
|
-
* @returns {ReactElement} ActionButton component
|
|
135
|
-
*/
|
|
136
|
-
const ActionButton = ({ type, value, dataTestId, size, disabled, className, onClick }) => {
|
|
137
|
-
const [, copyToClipboard] = usehooksTs.useCopyToClipboard();
|
|
138
|
-
const getIconName = () => {
|
|
139
|
-
switch (type) {
|
|
140
|
-
case "PHONE_NUMBER":
|
|
141
|
-
return "PhoneArrowUpRight";
|
|
142
|
-
case "WEB_ADDRESS":
|
|
143
|
-
return "ArrowTopRightOnSquare";
|
|
144
|
-
case "EMAIL":
|
|
145
|
-
return "Envelope";
|
|
146
|
-
case "EDIT":
|
|
147
|
-
return "Pencil";
|
|
148
|
-
case "COPY":
|
|
149
|
-
default:
|
|
150
|
-
return "ClipboardDocument";
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
-
const buttonAction = () => {
|
|
154
|
-
switch (type) {
|
|
155
|
-
case "EMAIL":
|
|
156
|
-
return window.open(`mailto:${value}`);
|
|
157
|
-
case "WEB_ADDRESS":
|
|
158
|
-
if (value) {
|
|
159
|
-
return window.open(value, "_blank", "noopener,noreferrer");
|
|
160
|
-
}
|
|
161
|
-
return null;
|
|
162
|
-
case "PHONE_NUMBER":
|
|
163
|
-
return window.open(`tel:${value}`);
|
|
164
|
-
case "EDIT":
|
|
165
|
-
return value?.current?.click();
|
|
166
|
-
case "COPY":
|
|
167
|
-
// Typescript seems to be unable to detect RefObject
|
|
168
|
-
// as one of the members of the union RefObject | string | null which gives access to the `current` property
|
|
169
|
-
return copyToClipboard(value?.current?.value ?? "");
|
|
170
|
-
default:
|
|
171
|
-
return null;
|
|
172
|
-
}
|
|
173
|
-
};
|
|
174
|
-
const adjustedIconSize = size === "large" ? "medium" : size;
|
|
175
|
-
return (jsxRuntime.jsx("div", { className: cvaActionContainer({ className, size }), children: jsxRuntime.jsx(reactComponents.IconButton, { className: cvaActionButton({ size: adjustedIconSize }), dataTestId: dataTestId || "testIconButtonId", disabled: disabled, icon: jsxRuntime.jsx(reactComponents.Icon, { name: getIconName(), size: adjustedIconSize }), onClick: buttonAction, size: "small", variant: "secondary" }) }));
|
|
176
|
-
};
|
|
177
|
-
|
|
178
102
|
const cvaInputBase = cssClassVarianceUtilities.cvaMerge([
|
|
179
103
|
"component-baseInput-shadow",
|
|
180
104
|
"component-baseInput-border",
|
|
@@ -344,6 +268,82 @@ const AddonRenderer = ({ addon, dataTestId, className, fieldSize, position }) =>
|
|
|
344
268
|
return (jsxRuntime.jsx("div", { className: cvaInputAddon({ size: fieldSize, position, className }), "data-testid": dataTestId ? `${dataTestId}-addon${stringTs.titleCase(position)}` : null, children: addon }));
|
|
345
269
|
};
|
|
346
270
|
|
|
271
|
+
const cvaActionButton = cssClassVarianceUtilities.cvaMerge(["drop-shadow-none", "rounded-md"], {
|
|
272
|
+
variants: {
|
|
273
|
+
size: {
|
|
274
|
+
small: ["w-6", "h-6", "min-h-0"],
|
|
275
|
+
medium: ["w-6", "h-6", "min-h-0"],
|
|
276
|
+
large: ["w-8", "h-8"],
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
defaultVariants: {
|
|
280
|
+
size: "medium",
|
|
281
|
+
},
|
|
282
|
+
});
|
|
283
|
+
const cvaActionContainer = cssClassVarianceUtilities.cvaMerge(["flex", "items-center"], {
|
|
284
|
+
variants: {
|
|
285
|
+
size: {
|
|
286
|
+
//I just measured manually the top/bottom spacing
|
|
287
|
+
//when using the action button inside an input
|
|
288
|
+
//might need tweaking in the future
|
|
289
|
+
small: ["m-[1px]"],
|
|
290
|
+
medium: ["m-[3px]"],
|
|
291
|
+
large: ["m-[7px]"],
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
defaultVariants: {
|
|
295
|
+
size: "medium",
|
|
296
|
+
},
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* The ActionButton component is a wrapper over IconButton to perform an action when the onClick event is triggered.
|
|
301
|
+
*
|
|
302
|
+
* @param {ActionButtonProps} props - The props for the ActionButton component
|
|
303
|
+
* @returns {ReactElement} ActionButton component
|
|
304
|
+
*/
|
|
305
|
+
const ActionButton = ({ type, value, dataTestId, size, disabled, className, onClick }) => {
|
|
306
|
+
const [, copyToClipboard] = usehooksTs.useCopyToClipboard();
|
|
307
|
+
const getIconName = () => {
|
|
308
|
+
switch (type) {
|
|
309
|
+
case "PHONE_NUMBER":
|
|
310
|
+
return "PhoneArrowUpRight";
|
|
311
|
+
case "WEB_ADDRESS":
|
|
312
|
+
return "ArrowTopRightOnSquare";
|
|
313
|
+
case "EMAIL":
|
|
314
|
+
return "Envelope";
|
|
315
|
+
case "EDIT":
|
|
316
|
+
return "Pencil";
|
|
317
|
+
case "COPY":
|
|
318
|
+
default:
|
|
319
|
+
return "ClipboardDocument";
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
const buttonAction = () => {
|
|
323
|
+
switch (type) {
|
|
324
|
+
case "EMAIL":
|
|
325
|
+
return window.open(`mailto:${value}`);
|
|
326
|
+
case "WEB_ADDRESS":
|
|
327
|
+
if (value) {
|
|
328
|
+
return window.open(value, "_blank", "noopener,noreferrer");
|
|
329
|
+
}
|
|
330
|
+
return null;
|
|
331
|
+
case "PHONE_NUMBER":
|
|
332
|
+
return window.open(`tel:${value}`);
|
|
333
|
+
case "EDIT":
|
|
334
|
+
return value?.current?.click();
|
|
335
|
+
case "COPY":
|
|
336
|
+
// Typescript seems to be unable to detect RefObject
|
|
337
|
+
// as one of the members of the union RefObject | string | null which gives access to the `current` property
|
|
338
|
+
return copyToClipboard(value?.current?.value ?? "");
|
|
339
|
+
default:
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
const adjustedIconSize = size === "large" ? "medium" : size;
|
|
344
|
+
return (jsxRuntime.jsx("div", { className: cvaActionContainer({ className, size }), children: jsxRuntime.jsx(reactComponents.IconButton, { className: cvaActionButton({ size: adjustedIconSize }), dataTestId: dataTestId || "testIconButtonId", disabled: disabled, icon: jsxRuntime.jsx(reactComponents.Icon, { name: getIconName(), size: adjustedIconSize }), onClick: buttonAction, size: "small", variant: "secondary" }) }));
|
|
345
|
+
};
|
|
346
|
+
|
|
347
347
|
const GenericActionsRenderer = ({ genericAction, disabled, fieldSize, innerRef, tooltipLabel, }) => {
|
|
348
348
|
const [t] = useTranslation();
|
|
349
349
|
if (!genericAction) {
|
|
@@ -413,41 +413,260 @@ const SuffixRenderer = ({ suffix, isInvalid, isWarning, dataTestId, disabled, })
|
|
|
413
413
|
};
|
|
414
414
|
|
|
415
415
|
/**
|
|
416
|
-
* A base input component that can be used for text inputs, password inputs, etc.
|
|
417
|
-
* A reference to the input element is provided as the `ref` prop.
|
|
418
|
-
* Extends props from [React.InputHTMLAttributes](https://reactjs.org/docs/dom-elements.html#input)
|
|
416
|
+
* A base input component that can be used for text inputs, password inputs, etc.
|
|
417
|
+
* A reference to the input element is provided as the `ref` prop.
|
|
418
|
+
* Extends props from [React.InputHTMLAttributes](https://reactjs.org/docs/dom-elements.html#input)
|
|
419
|
+
*
|
|
420
|
+
* For specific input types make sure to use the corresponding input component.
|
|
421
|
+
* This is a base used by our other input components such as TextBaseInput, NumberBaseInput, PasswordBaseInput, etc.
|
|
422
|
+
*/
|
|
423
|
+
const BaseInput = ({ className, isInvalid, dataTestId, prefix, suffix, addonBefore, addonAfter, actions, fieldSize = "medium", nonInteractive = false, inputClassName, placeholder, isWarning, type, genericAction, style, ref, ...rest }) => {
|
|
424
|
+
// Derive final flags
|
|
425
|
+
const renderAsDisabled = Boolean(rest.disabled);
|
|
426
|
+
const renderAsReadonly = Boolean(rest.readOnly);
|
|
427
|
+
const beforeContainerRef = react.useRef(null);
|
|
428
|
+
const { width: beforeContainerWidth } = reactComponents.useGeometry(beforeContainerRef);
|
|
429
|
+
const afterContainerRef = react.useRef(null);
|
|
430
|
+
const { width: afterContainerWidth } = reactComponents.useGeometry(afterContainerRef);
|
|
431
|
+
// Keep a reference to the input element
|
|
432
|
+
const innerRef = react.useRef(null);
|
|
433
|
+
react.useImperativeHandle(ref, () => innerRef.current, []);
|
|
434
|
+
return (jsxRuntime.jsxs("div", { className: cvaInput$1({
|
|
435
|
+
disabled: renderAsDisabled,
|
|
436
|
+
invalid: isInvalid,
|
|
437
|
+
isWarning,
|
|
438
|
+
size: fieldSize,
|
|
439
|
+
className,
|
|
440
|
+
}), "data-testid": dataTestId ? `${dataTestId}-container` : undefined, style: style, children: [jsxRuntime.jsxs("div", { className: cvaAccessoriesContainer({ className: cvaInputItemPlacementManager({ position: "before" }) }), "data-testid": dataTestId ? `${dataTestId}-before-container` : undefined, ref: beforeContainerRef, children: [jsxRuntime.jsx(AddonRenderer, { addon: addonBefore, dataTestId: dataTestId, fieldSize: fieldSize, position: "before" }), jsxRuntime.jsx(PrefixRenderer, { dataTestId: dataTestId, disabled: renderAsDisabled, prefix: prefix, type: type })] }), jsxRuntime.jsx("input", { "aria-required": rest.required, className: cvaInputField({
|
|
441
|
+
readOnly: renderAsReadonly,
|
|
442
|
+
size: fieldSize,
|
|
443
|
+
disabled: renderAsDisabled,
|
|
444
|
+
className: cvaInputItemPlacementManager({ position: "span", className: inputClassName }),
|
|
445
|
+
}), "data-testid": dataTestId, placeholder: renderAsDisabled ? undefined : placeholder, ref: innerRef, style: {
|
|
446
|
+
paddingLeft: beforeContainerWidth ? `calc(${beforeContainerWidth}px + ${uiDesignTokens.themeSpacing[2]})` : undefined,
|
|
447
|
+
paddingRight: afterContainerWidth ? `calc(${afterContainerWidth}px + ${uiDesignTokens.themeSpacing[2]})` : undefined,
|
|
448
|
+
}, type: type, ...rest, disabled: renderAsDisabled, readOnly: renderAsReadonly || nonInteractive }), jsxRuntime.jsxs("div", { className: cvaAccessoriesContainer({ className: cvaInputItemPlacementManager({ position: "after" }) }), "data-testid": dataTestId ? `${dataTestId}-after-container` : undefined, ref: afterContainerRef, children: [jsxRuntime.jsx(LockReasonRenderer, { dataTestId: dataTestId + "-disabled", lockReason: rest.disabled }), jsxRuntime.jsx(LockReasonRenderer, { dataTestId: dataTestId + "-readonly", lockReason: rest.readOnly && !rest.disabled ? rest.readOnly : undefined }), jsxRuntime.jsx(GenericActionsRenderer, { fieldSize: fieldSize, genericAction: genericAction, innerRef: innerRef }), jsxRuntime.jsx(SuffixRenderer, { dataTestId: dataTestId, disabled: renderAsDisabled, isInvalid: isInvalid, isWarning: isWarning, suffix: suffix }), actions, jsxRuntime.jsx(AddonRenderer, { addon: addonAfter, dataTestId: dataTestId, fieldSize: fieldSize, position: "after" })] })] }));
|
|
449
|
+
};
|
|
450
|
+
BaseInput.displayName = "BaseInput";
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* A wrapper around BaseInput with a pop-up day picker.
|
|
454
|
+
*
|
|
455
|
+
* The value is formatted to an ISO date string (YYYY-MM-DD)
|
|
456
|
+
*
|
|
457
|
+
* NOTE: If shown with a label, please use the `DateField` component instead.
|
|
458
|
+
*/
|
|
459
|
+
const DateBaseInput = ({ min, max, defaultValue, value, ref, ...rest }) => {
|
|
460
|
+
const formatDateToInputString = (date) => date instanceof Date
|
|
461
|
+
? polyfill.Temporal.PlainDateTime.from({
|
|
462
|
+
year: date.getFullYear(),
|
|
463
|
+
month: date.getMonth() + 1,
|
|
464
|
+
day: date.getDate(),
|
|
465
|
+
})
|
|
466
|
+
.toPlainDate()
|
|
467
|
+
.toString()
|
|
468
|
+
: date;
|
|
469
|
+
// Chrome and Firefox need their default icon to have datepicker functionality.
|
|
470
|
+
const showIcon = !/Chrome/.test(navigator.userAgent) && !/Firefox/.test(navigator.userAgent);
|
|
471
|
+
return (jsxRuntime.jsx(BaseInput, { defaultValue: formatDateToInputString(defaultValue), max: formatDateToInputString(max), min: formatDateToInputString(min), ref: ref, suffix: showIcon ? jsxRuntime.jsx(reactComponents.Icon, { dataTestId: "calendar", name: "Calendar", size: "medium", type: "solid" }) : null, type: "date", value: formatDateToInputString(value), ...rest }));
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* A thin wrapper around the `BaseInput` component for number input fields.
|
|
476
|
+
*
|
|
477
|
+
* NOTE: If shown with a label, please use the `NumberField` component instead.
|
|
478
|
+
*/
|
|
479
|
+
const NumberBaseInput = ({ ref, ...rest }) => {
|
|
480
|
+
const inputElementRef = react.useRef(null);
|
|
481
|
+
const preventDefaultWheelEvent = react.useCallback((event) => {
|
|
482
|
+
const inputElement = inputElementRef.current;
|
|
483
|
+
const activeElement = document.activeElement;
|
|
484
|
+
if (inputElement && activeElement === inputElement) {
|
|
485
|
+
event.preventDefault();
|
|
486
|
+
}
|
|
487
|
+
}, []);
|
|
488
|
+
const forwardAndStoreInputRef = react.useCallback((node) => {
|
|
489
|
+
const previousNode = inputElementRef.current;
|
|
490
|
+
if (previousNode) {
|
|
491
|
+
previousNode.removeEventListener("wheel", preventDefaultWheelEvent);
|
|
492
|
+
}
|
|
493
|
+
inputElementRef.current = node;
|
|
494
|
+
if (node) {
|
|
495
|
+
// NOTE: Prevent the default browser behavior of changing the value via mouse wheel
|
|
496
|
+
node.addEventListener("wheel", preventDefaultWheelEvent, { passive: false });
|
|
497
|
+
}
|
|
498
|
+
if (typeof ref === "function") {
|
|
499
|
+
ref(node);
|
|
500
|
+
}
|
|
501
|
+
else if (ref && typeof ref === "object") {
|
|
502
|
+
ref.current = node;
|
|
503
|
+
}
|
|
504
|
+
}, [preventDefaultWheelEvent, ref]);
|
|
505
|
+
react.useEffect(() => {
|
|
506
|
+
return () => {
|
|
507
|
+
const element = inputElementRef.current;
|
|
508
|
+
if (element) {
|
|
509
|
+
element.removeEventListener("wheel", preventDefaultWheelEvent);
|
|
510
|
+
}
|
|
511
|
+
};
|
|
512
|
+
}, [preventDefaultWheelEvent]);
|
|
513
|
+
return jsxRuntime.jsx(BaseInput, { ref: forwardAndStoreInputRef, type: "number", ...rest, value: rest.value });
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* @param phoneNumber - a phone number as a string
|
|
518
|
+
* @returns {boolean} true if the phone number starts with a plus sign
|
|
519
|
+
* @example checkIfPhoneNumberHasPlus("123456789") // false
|
|
520
|
+
* checkIfPhoneNumberHasPlus("+123456789") // true
|
|
521
|
+
*/
|
|
522
|
+
const checkIfPhoneNumberHasPlus = (phoneNumber) => {
|
|
523
|
+
return phoneNumber.startsWith("+");
|
|
524
|
+
};
|
|
525
|
+
/**
|
|
526
|
+
* @param phoneNumber - a phone number as a string
|
|
527
|
+
* @returns {string|number} the phone number with a plus sign in front of it
|
|
528
|
+
* @example getPhoneNumberWithPlus("123456789") // "+123456789"
|
|
529
|
+
*/
|
|
530
|
+
const getPhoneNumberWithPlus = (phoneNumber) => {
|
|
531
|
+
const stringPhoneNumber = phoneNumber.toString();
|
|
532
|
+
return checkIfPhoneNumberHasPlus(stringPhoneNumber) ? stringPhoneNumber : `+${stringPhoneNumber}`;
|
|
533
|
+
};
|
|
534
|
+
/**
|
|
535
|
+
* Generates a flag emoji based on the given country code.
|
|
536
|
+
*
|
|
537
|
+
* @param {string} countryCode - The two-letter country code (ISO 3166-1 alpha-2).
|
|
538
|
+
* @returns {string} - The corresponding flag emoji for the given country code.
|
|
539
|
+
* @example getPhoneNumberWithPlus("DK") // "🇩🇰"
|
|
540
|
+
*/
|
|
541
|
+
const countryCodeToFlagEmoji = (countryCode) => {
|
|
542
|
+
let code = countryCode;
|
|
543
|
+
if (countryCode.startsWith("+")) {
|
|
544
|
+
code = getCountryAbbreviation(countryCode.substring(1));
|
|
545
|
+
}
|
|
546
|
+
else if (!isNaN(Number(countryCode))) {
|
|
547
|
+
code = getCountryAbbreviation(countryCode);
|
|
548
|
+
}
|
|
549
|
+
return parsePhoneNumberFromString.isSupportedCountry(code.toUpperCase())
|
|
550
|
+
? code.toUpperCase().replace(/./g, char => String.fromCodePoint(127397 + char.charCodeAt(0)))
|
|
551
|
+
: "";
|
|
552
|
+
};
|
|
553
|
+
/**
|
|
554
|
+
* Retrieves the ISO 3166-1 alpha-2 country code associated with a given international calling code.
|
|
555
|
+
*
|
|
556
|
+
* @param {string} callCode - The international calling code for a country.
|
|
557
|
+
* @returns {string} The abbreviation for a country or an empty string if no matching country is found.
|
|
558
|
+
* @example getCountryAbbreviation("45") // "DK"
|
|
559
|
+
* @example getCountryAbbreviation("+45") // "DK"
|
|
560
|
+
*/
|
|
561
|
+
const getCountryAbbreviation = (callCode) => {
|
|
562
|
+
let code = callCode;
|
|
563
|
+
if (callCode.startsWith("+")) {
|
|
564
|
+
code = callCode.substring(1);
|
|
565
|
+
}
|
|
566
|
+
return parsePhoneNumberFromString.getCountries().find(c => parsePhoneNumberFromString.getCountryCallingCode(c) === code) || "";
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
const DEFAULT_COUNTRY_CODE = undefined;
|
|
570
|
+
/**
|
|
571
|
+
* A component for inputting phone numbers with an optional action button for initiating a phone call.
|
|
572
|
+
*
|
|
573
|
+
* @param {string} [dataTestId] - The data test ID for the component.
|
|
574
|
+
* @param {string|number} [value] - The value of the input field. The value should include the country code as well.
|
|
575
|
+
* @param {boolean} [disabled=false] - Whether the component is disabled or not.
|
|
576
|
+
* @param {string} [fieldSize="medium"] - The size of the input field.
|
|
577
|
+
* @param {boolean} [disableAction=false] - Whether the action button is disabled or not.
|
|
578
|
+
*/
|
|
579
|
+
const PhoneBaseInput = ({ dataTestId, isInvalid, disabled = false, value, defaultValue, fieldSize = "medium", disableAction = false, onChange, readOnly, onFocus, onBlur, name, ref, ...rest }) => {
|
|
580
|
+
const [innerValue, setInnerValue] = react.useState(() => {
|
|
581
|
+
return (value?.toString() || defaultValue?.toString()) ?? "";
|
|
582
|
+
});
|
|
583
|
+
const fieldIsFocused = react.useRef(false);
|
|
584
|
+
const [countryCode, setCountryCode] = react.useState(DEFAULT_COUNTRY_CODE);
|
|
585
|
+
const determineCountry = react.useCallback((newValue) => {
|
|
586
|
+
const asYouType = new parsePhoneNumberFromString.AsYouType();
|
|
587
|
+
asYouType.input(newValue);
|
|
588
|
+
setCountryCode(asYouType.getCountry());
|
|
589
|
+
}, []);
|
|
590
|
+
const handleChange = react.useCallback(event => {
|
|
591
|
+
const newValue = event.target.value;
|
|
592
|
+
event.target.value = parsePhoneNumberFromString.parseIncompletePhoneNumber(newValue);
|
|
593
|
+
onChange?.(event);
|
|
594
|
+
setInnerValue(newValue);
|
|
595
|
+
determineCountry(newValue);
|
|
596
|
+
}, [onChange, determineCountry]);
|
|
597
|
+
const makePretty = react.useCallback((newValue) => {
|
|
598
|
+
const asYouType = new parsePhoneNumberFromString.AsYouType();
|
|
599
|
+
const pretty = asYouType.input(newValue);
|
|
600
|
+
setInnerValue(pretty);
|
|
601
|
+
setCountryCode(asYouType.getCountry());
|
|
602
|
+
}, []);
|
|
603
|
+
react.useEffect(() => {
|
|
604
|
+
if (!fieldIsFocused.current) {
|
|
605
|
+
makePretty(typeof value === "string" ? value : "");
|
|
606
|
+
}
|
|
607
|
+
}, [makePretty, value]);
|
|
608
|
+
const handleBlur = react.useCallback(event => {
|
|
609
|
+
const newValue = event.target.value;
|
|
610
|
+
makePretty(newValue);
|
|
611
|
+
onBlur?.(event);
|
|
612
|
+
fieldIsFocused.current = false;
|
|
613
|
+
}, [makePretty, onBlur]);
|
|
614
|
+
const handleFocus = react.useCallback(event => {
|
|
615
|
+
const newValue = event.target.value;
|
|
616
|
+
const noneFormattedValue = parsePhoneNumberFromString.parseIncompletePhoneNumber(newValue);
|
|
617
|
+
setInnerValue(noneFormattedValue);
|
|
618
|
+
onFocus?.(event);
|
|
619
|
+
fieldIsFocused.current = true;
|
|
620
|
+
}, [onFocus]);
|
|
621
|
+
return (jsxRuntime.jsx("div", { className: "grid grid-cols-1 gap-2", "data-testid": dataTestId ? `${dataTestId}-container` : null, children: jsxRuntime.jsx(BaseInput, { actions: !disableAction && innerValue && innerValue.length > 0 ? (jsxRuntime.jsx(ActionButton, { dataTestId: dataTestId ? `${dataTestId}-phoneIcon` : undefined, disabled: isInvalid, size: fieldSize ?? undefined, type: "PHONE_NUMBER", value: value?.toString() || "" })) : null, dataTestId: dataTestId ? `${dataTestId}-phoneNumberInput` : undefined, disabled: disabled, fieldSize: fieldSize, id: "phoneInput-number", isInvalid: isInvalid, name: name, onBlur: handleBlur, onChange: handleChange, onFocus: handleFocus, prefix: (countryCode && countryCodeToFlagEmoji(countryCode)) || undefined, readOnly: readOnly, ref: ref, type: "tel", value: innerValue, ...rest }) }));
|
|
622
|
+
};
|
|
623
|
+
|
|
624
|
+
const cvaTextAreaBaseInput = cssClassVarianceUtilities.cvaMerge([
|
|
625
|
+
cvaInputBase(),
|
|
626
|
+
"block",
|
|
627
|
+
"overflow-auto",
|
|
628
|
+
"appearance-none",
|
|
629
|
+
"px-3",
|
|
630
|
+
"py-2",
|
|
631
|
+
"text-base",
|
|
632
|
+
"text-slate-900",
|
|
633
|
+
"placeholder-slate-400",
|
|
634
|
+
"w-full",
|
|
635
|
+
"h-20",
|
|
636
|
+
"transition",
|
|
637
|
+
], {
|
|
638
|
+
variants: {
|
|
639
|
+
disabled: {
|
|
640
|
+
true: cvaInputBaseDisabled(),
|
|
641
|
+
false: "",
|
|
642
|
+
},
|
|
643
|
+
invalid: {
|
|
644
|
+
true: cvaInputBaseInvalid(),
|
|
645
|
+
false: "",
|
|
646
|
+
},
|
|
647
|
+
resize: {
|
|
648
|
+
both: "resize",
|
|
649
|
+
vertical: "resize-y",
|
|
650
|
+
horizontal: "resize-x",
|
|
651
|
+
none: "resize-none",
|
|
652
|
+
},
|
|
653
|
+
},
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* The TextArea is a base component, and should not be used very often.
|
|
658
|
+
* For most cases the TextAreaField is the correct component.
|
|
659
|
+
*/
|
|
660
|
+
const TextAreaBaseInput = ({ id, name, value, rows, disabled, placeholder, readOnly, tabIndex, onChange, onFocus, onBlur, maxLength, resize = "vertical", defaultValue, required, dataTestId, isInvalid, className, ref, ...rest }) => {
|
|
661
|
+
return (jsxRuntime.jsx("textarea", { className: cvaTextAreaBaseInput({ className, resize, invalid: isInvalid, disabled }), defaultValue: defaultValue, disabled: disabled, id: id, maxLength: maxLength, name: name, onBlur: onBlur, onFocus: onFocus, placeholder: placeholder, readOnly: readOnly, ref: ref, required: required, rows: rows, tabIndex: tabIndex, value: value, ...rest, "data-testid": dataTestId, onChange: onChange }));
|
|
662
|
+
};
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* A thin wrapper around the `BaseInput` component for text input fields.
|
|
419
666
|
*
|
|
420
|
-
*
|
|
421
|
-
* This is a base used by our other input components such as TextInput, NumberInput, PasswordInput, etc.
|
|
667
|
+
* NOTE: If shown with a label, please use the `TextField` component instead.
|
|
422
668
|
*/
|
|
423
|
-
const
|
|
424
|
-
// Derive final flags
|
|
425
|
-
const renderAsDisabled = Boolean(rest.disabled);
|
|
426
|
-
const renderAsReadonly = Boolean(rest.readOnly);
|
|
427
|
-
const beforeContainerRef = react.useRef(null);
|
|
428
|
-
const { width: beforeContainerWidth } = reactComponents.useGeometry(beforeContainerRef);
|
|
429
|
-
const afterContainerRef = react.useRef(null);
|
|
430
|
-
const { width: afterContainerWidth } = reactComponents.useGeometry(afterContainerRef);
|
|
431
|
-
// Keep a reference to the input element
|
|
432
|
-
const innerRef = react.useRef(null);
|
|
433
|
-
react.useImperativeHandle(ref, () => innerRef.current, []);
|
|
434
|
-
return (jsxRuntime.jsxs("div", { className: cvaInput$1({
|
|
435
|
-
disabled: renderAsDisabled,
|
|
436
|
-
invalid: isInvalid,
|
|
437
|
-
isWarning,
|
|
438
|
-
size: fieldSize,
|
|
439
|
-
className,
|
|
440
|
-
}), "data-testid": dataTestId ? `${dataTestId}-container` : undefined, style: style, children: [jsxRuntime.jsxs("div", { className: cvaAccessoriesContainer({ className: cvaInputItemPlacementManager({ position: "before" }) }), "data-testid": dataTestId ? `${dataTestId}-before-container` : undefined, ref: beforeContainerRef, children: [jsxRuntime.jsx(AddonRenderer, { addon: addonBefore, dataTestId: dataTestId, fieldSize: fieldSize, position: "before" }), jsxRuntime.jsx(PrefixRenderer, { dataTestId: dataTestId, disabled: renderAsDisabled, prefix: prefix, type: type })] }), jsxRuntime.jsx("input", { "aria-required": rest.required, className: cvaInputField({
|
|
441
|
-
readOnly: renderAsReadonly,
|
|
442
|
-
size: fieldSize,
|
|
443
|
-
disabled: renderAsDisabled,
|
|
444
|
-
className: cvaInputItemPlacementManager({ position: "span", className: inputClassName }),
|
|
445
|
-
}), "data-testid": dataTestId, placeholder: renderAsDisabled ? undefined : placeholder, ref: innerRef, style: {
|
|
446
|
-
paddingLeft: beforeContainerWidth ? `calc(${beforeContainerWidth}px + ${uiDesignTokens.themeSpacing[2]})` : undefined,
|
|
447
|
-
paddingRight: afterContainerWidth ? `calc(${afterContainerWidth}px + ${uiDesignTokens.themeSpacing[2]})` : undefined,
|
|
448
|
-
}, type: type, ...rest, disabled: renderAsDisabled, readOnly: renderAsReadonly || nonInteractive }), jsxRuntime.jsxs("div", { className: cvaAccessoriesContainer({ className: cvaInputItemPlacementManager({ position: "after" }) }), "data-testid": dataTestId ? `${dataTestId}-after-container` : undefined, ref: afterContainerRef, children: [jsxRuntime.jsx(LockReasonRenderer, { dataTestId: dataTestId + "-disabled", lockReason: rest.disabled }), jsxRuntime.jsx(LockReasonRenderer, { dataTestId: dataTestId + "-readonly", lockReason: rest.readOnly && !rest.disabled ? rest.readOnly : undefined }), jsxRuntime.jsx(GenericActionsRenderer, { fieldSize: fieldSize, genericAction: genericAction, innerRef: innerRef }), jsxRuntime.jsx(SuffixRenderer, { dataTestId: dataTestId, disabled: renderAsDisabled, isInvalid: isInvalid, isWarning: isWarning, suffix: suffix }), actions, jsxRuntime.jsx(AddonRenderer, { addon: addonAfter, dataTestId: dataTestId, fieldSize: fieldSize, position: "after" })] })] }));
|
|
449
|
-
};
|
|
450
|
-
BaseInput.displayName = "BaseInput";
|
|
669
|
+
const TextBaseInput = ({ ref, ...rest }) => jsxRuntime.jsx(BaseInput, { ref: ref, type: "text", ...rest });
|
|
451
670
|
|
|
452
671
|
/**
|
|
453
672
|
* Shared CVA for binary control items: Checkbox, RadioItem, ToggleSwitchOption
|
|
@@ -714,7 +933,7 @@ const FormGroup = ({ isInvalid, isWarning, helpText, helpAddon, tip, className,
|
|
|
714
933
|
const color = isInvalid ? "danger" : isWarning ? "warning" : null;
|
|
715
934
|
return color ? jsxRuntime.jsx(reactComponents.Icon, { color: color, name: "ExclamationTriangle", size: "small" }) : null;
|
|
716
935
|
}, [isInvalid, isWarning]);
|
|
717
|
-
return (jsxRuntime.jsxs("div", { className: cvaFormGroup({ className }), "data-testid": dataTestId, children: [jsxRuntime.jsxs("div", { className: cvaFormGroupContainerBefore(), children: [
|
|
936
|
+
return (jsxRuntime.jsxs("div", { className: cvaFormGroup({ className }), "data-testid": dataTestId, children: [label ? (jsxRuntime.jsxs("div", { className: cvaFormGroupContainerBefore(), children: [jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(Label, { className: "component-formGroup-font", dataTestId: dataTestId ? `${dataTestId}-label` : undefined, htmlFor: htmlFor, id: htmlFor + "-label", children: label }), required ? (jsxRuntime.jsx(reactComponents.Tooltip, { "data-testid": "required-asterisk", label: t("field.required.asterisk.tooltip"), children: "*" })) : null] }), tip ? (jsxRuntime.jsx(reactComponents.Tooltip, { className: "ml-1", dataTestId: dataTestId ? `${dataTestId}-tooltip` : undefined, label: tip, placement: "bottom" })) : null] })) : null, children, helpText || helpAddon ? (jsxRuntime.jsxs("div", { className: cvaFormGroupContainerAfter({ invalid: isInvalid, isWarning: isWarning }), children: [helpText ? (jsxRuntime.jsxs("div", { className: "flex gap-1", children: [validationStateIcon, jsxRuntime.jsx("span", { "data-testid": dataTestId ? `${dataTestId}-helpText` : undefined, children: helpText })] })) : undefined, helpAddon ? (jsxRuntime.jsx("span", { className: cvaHelpAddon(), "data-testid": dataTestId ? `${dataTestId}-helpAddon` : null, children: helpAddon })) : null] })) : null] }));
|
|
718
937
|
};
|
|
719
938
|
|
|
720
939
|
/**
|
|
@@ -840,29 +1059,6 @@ const ColorField = react.forwardRef(({ label, id, tip, helpText, errorMessage, h
|
|
|
840
1059
|
});
|
|
841
1060
|
ColorField.displayName = "ColorField";
|
|
842
1061
|
|
|
843
|
-
/**
|
|
844
|
-
* A wrapper around BaseInput with a pop-up day picker.
|
|
845
|
-
*
|
|
846
|
-
* The value is formatted to an ISO date string (YYYY-MM-DD)
|
|
847
|
-
*
|
|
848
|
-
* NOTE: If shown with a label, please use the `DateField` component instead.
|
|
849
|
-
*/
|
|
850
|
-
const DateInput = ({ min, max, defaultValue, value, ref, ...rest }) => {
|
|
851
|
-
const formatDateToInputString = (date) => date instanceof Date
|
|
852
|
-
? polyfill.Temporal.PlainDateTime.from({
|
|
853
|
-
year: date.getFullYear(),
|
|
854
|
-
month: date.getMonth() + 1,
|
|
855
|
-
day: date.getDate(),
|
|
856
|
-
})
|
|
857
|
-
.toPlainDate()
|
|
858
|
-
.toString()
|
|
859
|
-
: date;
|
|
860
|
-
// Chrome and Firefox need their default icon to have datepicker functionality.
|
|
861
|
-
const showIcon = !/Chrome/.test(navigator.userAgent) && !/Firefox/.test(navigator.userAgent);
|
|
862
|
-
return (jsxRuntime.jsx(BaseInput, { defaultValue: formatDateToInputString(defaultValue), max: formatDateToInputString(max), min: formatDateToInputString(min), ref: ref, suffix: showIcon ? jsxRuntime.jsx(reactComponents.Icon, { dataTestId: "calendar", name: "Calendar", size: "medium", type: "solid" }) : null, type: "date", value: formatDateToInputString(value), ...rest }));
|
|
863
|
-
};
|
|
864
|
-
DateInput.displayName = "DateInput";
|
|
865
|
-
|
|
866
1062
|
/**
|
|
867
1063
|
* The date field component is used for entering date values.
|
|
868
1064
|
*
|
|
@@ -873,7 +1069,7 @@ DateInput.displayName = "DateInput";
|
|
|
873
1069
|
const DateField = ({ label, id, tip, helpText, errorMessage, helpAddon, isInvalid, className, defaultValue, dataTestId, ref, ...rest }) => {
|
|
874
1070
|
const renderAsInvalid = isInvalid === undefined ? Boolean(errorMessage) : isInvalid;
|
|
875
1071
|
const htmlForId = id ? id : "dateField-" + sharedUtils.uuidv4();
|
|
876
|
-
return (jsxRuntime.jsx(FormGroup, { dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && errorMessage) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required ? !(rest.disabled || rest.readOnly) : false, tip: tip, children: jsxRuntime.jsx(
|
|
1072
|
+
return (jsxRuntime.jsx(FormGroup, { dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && errorMessage) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required ? !(rest.disabled || rest.readOnly) : false, tip: tip, children: jsxRuntime.jsx(DateBaseInput, { "aria-labelledby": htmlForId + "-label", defaultValue: defaultValue, id: htmlForId, isInvalid: renderAsInvalid, ref: ref, ...rest, className: className, dataTestId: dataTestId }) }));
|
|
877
1073
|
};
|
|
878
1074
|
DateField.displayName = "DateField";
|
|
879
1075
|
|
|
@@ -1028,8 +1224,8 @@ const validateEmailId = (emailId, required) => {
|
|
|
1028
1224
|
* A reference to the input element is provided as the `ref` prop.
|
|
1029
1225
|
* For specific input types make sure to use the corresponding input component.
|
|
1030
1226
|
*/
|
|
1031
|
-
const
|
|
1032
|
-
const [email, setEmail] = react.useState(
|
|
1227
|
+
const EmailBaseInput = ({ fieldSize = "medium", disabled = false, dataTestId, isInvalid = false, onChange, disableAction = false, ref, ...rest }) => {
|
|
1228
|
+
const [email, setEmail] = react.useState(rest.value?.toString() || rest.defaultValue?.toString());
|
|
1033
1229
|
const sendEmail = () => {
|
|
1034
1230
|
return window.open(`mailto:${email}`);
|
|
1035
1231
|
};
|
|
@@ -1041,7 +1237,6 @@ const EmailInput = ({ fieldSize = "medium", disabled = false, dataTestId, isInva
|
|
|
1041
1237
|
const renderAsInvalid = (email && !validateEmailAddress(email)) || isInvalid;
|
|
1042
1238
|
return (jsxRuntime.jsx(BaseInput, { actions: email && email.length > 0 ? (jsxRuntime.jsx(ActionButton, { dataTestId: dataTestId ? `${dataTestId}-emailIcon` : undefined, disabled: disableAction || isInvalid, onClick: sendEmail, size: fieldSize ?? undefined, type: "EMAIL", value: email })) : null, dataTestId: dataTestId, disabled: disabled, isInvalid: renderAsInvalid, onChange: handleChange, placeholder: rest.placeholder || "mail@example.com", ref: ref, type: "email", ...rest }));
|
|
1043
1239
|
};
|
|
1044
|
-
EmailInput.displayName = "EmailInput";
|
|
1045
1240
|
|
|
1046
1241
|
/**
|
|
1047
1242
|
* The EmailField component is used to enter email.
|
|
@@ -1069,7 +1264,7 @@ const EmailField = ({ label, id, tip, helpText, errorMessage, helpAddon, classNa
|
|
|
1069
1264
|
onChange(event);
|
|
1070
1265
|
}
|
|
1071
1266
|
}, [onChange]);
|
|
1072
|
-
return (jsxRuntime.jsx(FormGroup, { dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && error) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required ? !(rest.disabled || rest.readOnly) : false, tip: tip, children: jsxRuntime.jsx(
|
|
1267
|
+
return (jsxRuntime.jsx(FormGroup, { dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && error) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required ? !(rest.disabled || rest.readOnly) : false, tip: tip, children: jsxRuntime.jsx(EmailBaseInput, { "aria-labelledby": htmlForId + "-label", defaultValue: defaultValue, id: htmlForId, isInvalid: renderAsInvalid, onBlur: handleBlur, onChange: handleChange, ref: ref, value: value, ...rest, className: className, dataTestId: dataTestId }) }));
|
|
1073
1268
|
};
|
|
1074
1269
|
EmailField.displayName = "EmailField";
|
|
1075
1270
|
|
|
@@ -1123,49 +1318,6 @@ const validateNumber = (number, required = false, min, max) => {
|
|
|
1123
1318
|
return "INVALID_NUMBER";
|
|
1124
1319
|
};
|
|
1125
1320
|
|
|
1126
|
-
/**
|
|
1127
|
-
* A thin wrapper around the `BaseInput` component for number input fields.
|
|
1128
|
-
*
|
|
1129
|
-
* NOTE: If shown with a label, please use the `NumberField` component instead.
|
|
1130
|
-
*/
|
|
1131
|
-
const NumberInput = ({ ref, ...rest }) => {
|
|
1132
|
-
const inputElementRef = react.useRef(null);
|
|
1133
|
-
const preventDefaultWheelEvent = react.useCallback((event) => {
|
|
1134
|
-
const inputElement = inputElementRef.current;
|
|
1135
|
-
const activeElement = document.activeElement;
|
|
1136
|
-
if (inputElement && activeElement === inputElement) {
|
|
1137
|
-
event.preventDefault();
|
|
1138
|
-
}
|
|
1139
|
-
}, []);
|
|
1140
|
-
const forwardAndStoreInputRef = react.useCallback((node) => {
|
|
1141
|
-
const previousNode = inputElementRef.current;
|
|
1142
|
-
if (previousNode) {
|
|
1143
|
-
previousNode.removeEventListener("wheel", preventDefaultWheelEvent);
|
|
1144
|
-
}
|
|
1145
|
-
inputElementRef.current = node;
|
|
1146
|
-
if (node) {
|
|
1147
|
-
// NOTE: Prevent the default browser behavior of changing the value via mouse wheel
|
|
1148
|
-
node.addEventListener("wheel", preventDefaultWheelEvent, { passive: false });
|
|
1149
|
-
}
|
|
1150
|
-
if (typeof ref === "function") {
|
|
1151
|
-
ref(node);
|
|
1152
|
-
}
|
|
1153
|
-
else if (ref && typeof ref === "object") {
|
|
1154
|
-
ref.current = node;
|
|
1155
|
-
}
|
|
1156
|
-
}, [preventDefaultWheelEvent, ref]);
|
|
1157
|
-
react.useEffect(() => {
|
|
1158
|
-
return () => {
|
|
1159
|
-
const element = inputElementRef.current;
|
|
1160
|
-
if (element) {
|
|
1161
|
-
element.removeEventListener("wheel", preventDefaultWheelEvent);
|
|
1162
|
-
}
|
|
1163
|
-
};
|
|
1164
|
-
}, [preventDefaultWheelEvent]);
|
|
1165
|
-
return jsxRuntime.jsx(BaseInput, { ref: forwardAndStoreInputRef, type: "number", ...rest, value: rest.value });
|
|
1166
|
-
};
|
|
1167
|
-
NumberInput.displayName = "NumberInput";
|
|
1168
|
-
|
|
1169
1321
|
/**
|
|
1170
1322
|
* The number field component is used for entering numeric values and includes controls for incrementally increasing or decreasing the value.
|
|
1171
1323
|
*
|
|
@@ -1215,7 +1367,7 @@ const NumberField = ({ label, id, tip, helpText, errorMessage, helpAddon, isInva
|
|
|
1215
1367
|
onChange(event);
|
|
1216
1368
|
}
|
|
1217
1369
|
}, [onChange]);
|
|
1218
|
-
return (jsxRuntime.jsx(FormGroup, { dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && error) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required ? !(rest.disabled || rest.readOnly) : false, tip: tip, children: jsxRuntime.jsx(
|
|
1370
|
+
return (jsxRuntime.jsx(FormGroup, { dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && error) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required ? !(rest.disabled || rest.readOnly) : false, tip: tip, children: jsxRuntime.jsx(NumberBaseInput, { "aria-labelledby": htmlForId + "-label", id: htmlForId, isInvalid: renderAsInvalid, maxLength: maxLength, onBlur: handleBlur, onChange: handleChange, ref: ref, value: value, ...rest, className: className, dataTestId: dataTestId }) }));
|
|
1219
1371
|
};
|
|
1220
1372
|
NumberField.displayName = "NumberField";
|
|
1221
1373
|
|
|
@@ -1329,11 +1481,10 @@ OptionCard.displayName = "OptionCard";
|
|
|
1329
1481
|
*
|
|
1330
1482
|
* NOTE: If shown with a label, please use the `PasswordField` component instead.
|
|
1331
1483
|
*/
|
|
1332
|
-
const
|
|
1484
|
+
const PasswordBaseInput = ({ ref, fieldSize, ...rest }) => {
|
|
1333
1485
|
const [showPassword, setShowPassword] = react.useState(false);
|
|
1334
1486
|
return (jsxRuntime.jsx(BaseInput, { ref: ref, ...rest, actions: jsxRuntime.jsx("div", { className: cvaActionContainer({ size: fieldSize }), children: jsxRuntime.jsx(reactComponents.IconButton, { className: cvaActionButton({ size: fieldSize }), icon: jsxRuntime.jsx(reactComponents.Icon, { name: showPassword ? "EyeSlash" : "Eye", size: "small" }), onClick: () => setShowPassword(prevState => !prevState), size: "small", variant: "secondary" }) }), type: showPassword ? "text" : "password" }));
|
|
1335
1487
|
};
|
|
1336
|
-
PasswordInput.displayName = "PasswordInput";
|
|
1337
1488
|
|
|
1338
1489
|
/**
|
|
1339
1490
|
* Password fields enter a password or other confidential information. Characters are masked as they are typed.
|
|
@@ -1348,119 +1499,10 @@ const PasswordField = ({ id, label, tip, helpText, helpAddon, errorMessage, isIn
|
|
|
1348
1499
|
const handleChange = react.useCallback((event) => {
|
|
1349
1500
|
onChange?.(event);
|
|
1350
1501
|
}, [onChange]);
|
|
1351
|
-
return (jsxRuntime.jsx(FormGroup, { dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && errorMessage) || helpText, htmlFor: htmlFor, isInvalid: renderAsInvalid, label: label, required: rest.required ? !(rest.disabled || rest.readOnly) : false, tip: tip, children: jsxRuntime.jsx(
|
|
1502
|
+
return (jsxRuntime.jsx(FormGroup, { dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && errorMessage) || helpText, htmlFor: htmlFor, isInvalid: renderAsInvalid, label: label, required: rest.required ? !(rest.disabled || rest.readOnly) : false, tip: tip, children: jsxRuntime.jsx(PasswordBaseInput, { ...rest, "aria-labelledby": htmlFor + "-label", className: className, dataTestId: dataTestId, disabled: rest.readOnly, id: htmlFor, isInvalid: renderAsInvalid, maxLength: maxLength, onChange: handleChange, ref: ref, value: value }) }));
|
|
1352
1503
|
};
|
|
1353
1504
|
PasswordField.displayName = "PasswordField";
|
|
1354
1505
|
|
|
1355
|
-
/**
|
|
1356
|
-
* @param phoneNumber - a phone number as a string
|
|
1357
|
-
* @returns {boolean} true if the phone number starts with a plus sign
|
|
1358
|
-
* @example checkIfPhoneNumberHasPlus("123456789") // false
|
|
1359
|
-
* checkIfPhoneNumberHasPlus("+123456789") // true
|
|
1360
|
-
*/
|
|
1361
|
-
const checkIfPhoneNumberHasPlus = (phoneNumber) => {
|
|
1362
|
-
return phoneNumber.startsWith("+");
|
|
1363
|
-
};
|
|
1364
|
-
/**
|
|
1365
|
-
* @param phoneNumber - a phone number as a string
|
|
1366
|
-
* @returns {string|number} the phone number with a plus sign in front of it
|
|
1367
|
-
* @example getPhoneNumberWithPlus("123456789") // "+123456789"
|
|
1368
|
-
*/
|
|
1369
|
-
const getPhoneNumberWithPlus = (phoneNumber) => {
|
|
1370
|
-
const stringPhoneNumber = phoneNumber.toString();
|
|
1371
|
-
return checkIfPhoneNumberHasPlus(stringPhoneNumber) ? stringPhoneNumber : `+${stringPhoneNumber}`;
|
|
1372
|
-
};
|
|
1373
|
-
/**
|
|
1374
|
-
* Generates a flag emoji based on the given country code.
|
|
1375
|
-
*
|
|
1376
|
-
* @param {string} countryCode - The two-letter country code (ISO 3166-1 alpha-2).
|
|
1377
|
-
* @returns {string} - The corresponding flag emoji for the given country code.
|
|
1378
|
-
* @example getPhoneNumberWithPlus("DK") // "🇩🇰"
|
|
1379
|
-
*/
|
|
1380
|
-
const countryCodeToFlagEmoji = (countryCode) => {
|
|
1381
|
-
let code = countryCode;
|
|
1382
|
-
if (countryCode.startsWith("+")) {
|
|
1383
|
-
code = getCountryAbbreviation(countryCode.substring(1));
|
|
1384
|
-
}
|
|
1385
|
-
else if (!isNaN(Number(countryCode))) {
|
|
1386
|
-
code = getCountryAbbreviation(countryCode);
|
|
1387
|
-
}
|
|
1388
|
-
return parsePhoneNumberFromString.isSupportedCountry(code.toUpperCase())
|
|
1389
|
-
? code.toUpperCase().replace(/./g, char => String.fromCodePoint(127397 + char.charCodeAt(0)))
|
|
1390
|
-
: "";
|
|
1391
|
-
};
|
|
1392
|
-
/**
|
|
1393
|
-
* Retrieves the ISO 3166-1 alpha-2 country code associated with a given international calling code.
|
|
1394
|
-
*
|
|
1395
|
-
* @param {string} callCode - The international calling code for a country.
|
|
1396
|
-
* @returns {string} The abbreviation for a country or an empty string if no matching country is found.
|
|
1397
|
-
* @example getCountryAbbreviation("45") // "DK"
|
|
1398
|
-
* @example getCountryAbbreviation("+45") // "DK"
|
|
1399
|
-
*/
|
|
1400
|
-
const getCountryAbbreviation = (callCode) => {
|
|
1401
|
-
let code = callCode;
|
|
1402
|
-
if (callCode.startsWith("+")) {
|
|
1403
|
-
code = callCode.substring(1);
|
|
1404
|
-
}
|
|
1405
|
-
return parsePhoneNumberFromString.getCountries().find(c => parsePhoneNumberFromString.getCountryCallingCode(c) === code) || "";
|
|
1406
|
-
};
|
|
1407
|
-
|
|
1408
|
-
const DEFAULT_COUNTRY_CODE = undefined;
|
|
1409
|
-
/**
|
|
1410
|
-
* A component for inputting phone numbers with an optional action button for initiating a phone call.
|
|
1411
|
-
*
|
|
1412
|
-
* @param {string} [dataTestId] - The data test ID for the component.
|
|
1413
|
-
* @param {string|number} [value] - The value of the input field. The value should include the country code as well.
|
|
1414
|
-
* @param {boolean} [disabled=false] - Whether the component is disabled or not.
|
|
1415
|
-
* @param {string} [fieldSize="medium"] - The size of the input field.
|
|
1416
|
-
* @param {boolean} [disableAction=false] - Whether the action button is disabled or not.
|
|
1417
|
-
*/
|
|
1418
|
-
const PhoneInput = ({ dataTestId, isInvalid, disabled = false, value, defaultValue, fieldSize = "medium", disableAction = false, onChange, readOnly, onFocus, onBlur, name, ref, ...rest }) => {
|
|
1419
|
-
const [innerValue, setInnerValue] = react.useState(() => {
|
|
1420
|
-
return (value?.toString() || defaultValue?.toString()) ?? "";
|
|
1421
|
-
});
|
|
1422
|
-
const fieldIsFocused = react.useRef(false);
|
|
1423
|
-
const [countryCode, setCountryCode] = react.useState(DEFAULT_COUNTRY_CODE);
|
|
1424
|
-
const determineCountry = react.useCallback((newValue) => {
|
|
1425
|
-
const asYouType = new parsePhoneNumberFromString.AsYouType();
|
|
1426
|
-
asYouType.input(newValue);
|
|
1427
|
-
setCountryCode(asYouType.getCountry());
|
|
1428
|
-
}, []);
|
|
1429
|
-
const handleChange = react.useCallback(event => {
|
|
1430
|
-
const newValue = event.target.value;
|
|
1431
|
-
event.target.value = parsePhoneNumberFromString.parseIncompletePhoneNumber(newValue);
|
|
1432
|
-
onChange?.(event);
|
|
1433
|
-
setInnerValue(newValue);
|
|
1434
|
-
determineCountry(newValue);
|
|
1435
|
-
}, [onChange, determineCountry]);
|
|
1436
|
-
const makePretty = react.useCallback((newValue) => {
|
|
1437
|
-
const asYouType = new parsePhoneNumberFromString.AsYouType();
|
|
1438
|
-
const pretty = asYouType.input(newValue);
|
|
1439
|
-
setInnerValue(pretty);
|
|
1440
|
-
setCountryCode(asYouType.getCountry());
|
|
1441
|
-
}, []);
|
|
1442
|
-
react.useEffect(() => {
|
|
1443
|
-
if (!fieldIsFocused.current) {
|
|
1444
|
-
makePretty(typeof value === "string" ? value : "");
|
|
1445
|
-
}
|
|
1446
|
-
}, [makePretty, value]);
|
|
1447
|
-
const handleBlur = react.useCallback(event => {
|
|
1448
|
-
const newValue = event.target.value;
|
|
1449
|
-
makePretty(newValue);
|
|
1450
|
-
onBlur?.(event);
|
|
1451
|
-
fieldIsFocused.current = false;
|
|
1452
|
-
}, [makePretty, onBlur]);
|
|
1453
|
-
const handleFocus = react.useCallback(event => {
|
|
1454
|
-
const newValue = event.target.value;
|
|
1455
|
-
const noneFormattedValue = parsePhoneNumberFromString.parseIncompletePhoneNumber(newValue);
|
|
1456
|
-
setInnerValue(noneFormattedValue);
|
|
1457
|
-
onFocus?.(event);
|
|
1458
|
-
fieldIsFocused.current = true;
|
|
1459
|
-
}, [onFocus]);
|
|
1460
|
-
return (jsxRuntime.jsx("div", { className: "grid grid-cols-1 gap-2", "data-testid": dataTestId ? `${dataTestId}-container` : null, children: jsxRuntime.jsx(BaseInput, { actions: !disableAction && innerValue && innerValue.length > 0 ? (jsxRuntime.jsx(ActionButton, { dataTestId: dataTestId ? `${dataTestId}-phoneIcon` : undefined, disabled: isInvalid, size: fieldSize ?? undefined, type: "PHONE_NUMBER", value: value?.toString() || "" })) : null, dataTestId: dataTestId ? `${dataTestId}-phoneNumberInput` : undefined, disabled: disabled, fieldSize: fieldSize, id: "phoneInput-number", isInvalid: isInvalid, name: name, onBlur: handleBlur, onChange: handleChange, onFocus: handleFocus, prefix: (countryCode && countryCodeToFlagEmoji(countryCode)) || undefined, readOnly: readOnly, ref: ref, type: "tel", value: innerValue, ...rest }) }));
|
|
1461
|
-
};
|
|
1462
|
-
PhoneInput.displayName = "PhoneInput";
|
|
1463
|
-
|
|
1464
1506
|
/**
|
|
1465
1507
|
* Validates a phone number
|
|
1466
1508
|
*/
|
|
@@ -1560,7 +1602,7 @@ const PhoneField = ({ label, id, tip, helpText, isInvalid, errorMessage, value,
|
|
|
1560
1602
|
}
|
|
1561
1603
|
onBlur?.(event);
|
|
1562
1604
|
}, [errorMessage, onBlur, rest.required]);
|
|
1563
|
-
return (jsxRuntime.jsx(FormGroup, { className: className, dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && error) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required ? !(rest.disabled || rest.readOnly) : false, tip: tip, children: jsxRuntime.jsx(
|
|
1605
|
+
return (jsxRuntime.jsx(FormGroup, { className: className, dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && error) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required ? !(rest.disabled || rest.readOnly) : false, tip: tip, children: jsxRuntime.jsx(PhoneBaseInput, { "aria-labelledby": htmlForId + "-label", dataTestId: dataTestId, defaultValue: defaultValue, id: htmlForId, isInvalid: renderAsInvalid, name: name, onBlur: handleBlur, ref: ref, value: value, ...rest }) }));
|
|
1564
1606
|
};
|
|
1565
1607
|
PhoneField.displayName = "PhoneField";
|
|
1566
1608
|
|
|
@@ -1901,14 +1943,6 @@ const hasConsecutiveDays = (schedule) => {
|
|
|
1901
1943
|
return schedule.every(({ day }, index) => day === days[index]);
|
|
1902
1944
|
};
|
|
1903
1945
|
|
|
1904
|
-
/**
|
|
1905
|
-
* A thin wrapper around the `BaseInput` component for text input fields.
|
|
1906
|
-
*
|
|
1907
|
-
* NOTE: If shown with a label, please use the `TextField` component instead.
|
|
1908
|
-
*/
|
|
1909
|
-
const TextInput = ({ ref, ...rest }) => jsxRuntime.jsx(BaseInput, { ref: ref, type: "text", ...rest });
|
|
1910
|
-
TextInput.displayName = "TextInput";
|
|
1911
|
-
|
|
1912
1946
|
const cvaSearch = cssClassVarianceUtilities.cvaMerge([
|
|
1913
1947
|
"shadow-none",
|
|
1914
1948
|
"component-search-border",
|
|
@@ -1940,7 +1974,7 @@ const cvaSearch = cssClassVarianceUtilities.cvaMerge([
|
|
|
1940
1974
|
*/
|
|
1941
1975
|
const Search = ({ className, placeholder, value, widenInputOnFocus, hideBorderWhenNotInFocus = false, disabled, onKeyUp, onChange, onFocus, onBlur, name, onClear, dataTestId, autoComplete = "on", loading, inputClassName, iconName = "MagnifyingGlass", style, xMarkRef, ref, ...rest }) => {
|
|
1942
1976
|
const { t } = useTranslation();
|
|
1943
|
-
return (jsxRuntime.jsx(
|
|
1977
|
+
return (jsxRuntime.jsx(TextBaseInput, { ...rest, autoComplete: autoComplete, className: cvaSearch({ className, border: hideBorderWhenNotInFocus, widenOnFocus: widenInputOnFocus }), dataTestId: dataTestId, disabled: disabled, inputClassName: inputClassName, name: name, onBlur: onBlur, onChange: onChange, onFocus: onFocus, onKeyUp: onKeyUp, placeholder: placeholder ?? t("search.placeholder"), prefix: loading ? (jsxRuntime.jsx(reactComponents.Spinner, { centering: "centered", size: rest.fieldSize ?? undefined })) : (jsxRuntime.jsx(reactComponents.Icon, { name: iconName, size: rest.fieldSize ?? undefined })), ref: ref, suffix:
|
|
1944
1978
|
//only show the clear button if there is a value and the onClear function is provided
|
|
1945
1979
|
onClear && value ? (jsxRuntime.jsx("button", { className: "flex", "data-testid": dataTestId ? `${dataTestId}_suffix_component` : null, onClick: () => {
|
|
1946
1980
|
onClear();
|
|
@@ -2706,47 +2740,6 @@ const SelectField = ({ ref, ...props }) => {
|
|
|
2706
2740
|
};
|
|
2707
2741
|
SelectField.displayName = "SelectField";
|
|
2708
2742
|
|
|
2709
|
-
const cvaTextArea = cssClassVarianceUtilities.cvaMerge([
|
|
2710
|
-
cvaInputBase(),
|
|
2711
|
-
"block",
|
|
2712
|
-
"overflow-auto",
|
|
2713
|
-
"appearance-none",
|
|
2714
|
-
"px-3",
|
|
2715
|
-
"py-2",
|
|
2716
|
-
"text-base",
|
|
2717
|
-
"text-slate-900",
|
|
2718
|
-
"placeholder-slate-400",
|
|
2719
|
-
"w-full",
|
|
2720
|
-
"h-20",
|
|
2721
|
-
"transition",
|
|
2722
|
-
], {
|
|
2723
|
-
variants: {
|
|
2724
|
-
disabled: {
|
|
2725
|
-
true: cvaInputBaseDisabled(),
|
|
2726
|
-
false: "",
|
|
2727
|
-
},
|
|
2728
|
-
invalid: {
|
|
2729
|
-
true: cvaInputBaseInvalid(),
|
|
2730
|
-
false: "",
|
|
2731
|
-
},
|
|
2732
|
-
resize: {
|
|
2733
|
-
both: "resize",
|
|
2734
|
-
vertical: "resize-y",
|
|
2735
|
-
horizontal: "resize-x",
|
|
2736
|
-
none: "resize-none",
|
|
2737
|
-
},
|
|
2738
|
-
},
|
|
2739
|
-
});
|
|
2740
|
-
|
|
2741
|
-
/**
|
|
2742
|
-
* The TextArea is a base component, and should not be used very often.
|
|
2743
|
-
* For most cases the TextAreaField is the correct component.
|
|
2744
|
-
*/
|
|
2745
|
-
const TextArea = ({ id, name, value, rows, disabled, placeholder, readOnly, tabIndex, onChange, onFocus, onBlur, maxLength, resize = "vertical", defaultValue, required, dataTestId, isInvalid, className, ref, ...rest }) => {
|
|
2746
|
-
return (jsxRuntime.jsx("textarea", { className: cvaTextArea({ className, resize, invalid: isInvalid, disabled }), defaultValue: defaultValue, disabled: disabled, id: id, maxLength: maxLength, name: name, onBlur: onBlur, onFocus: onFocus, placeholder: placeholder, readOnly: readOnly, ref: ref, required: required, rows: rows, tabIndex: tabIndex, value: value, ...rest, "data-testid": dataTestId, onChange: onChange }));
|
|
2747
|
-
};
|
|
2748
|
-
TextArea.displayName = "TextArea";
|
|
2749
|
-
|
|
2750
2743
|
/**
|
|
2751
2744
|
* The TextLengthIndicator component shows a `{length}/{maxLength}` label.
|
|
2752
2745
|
* Used for TextFields to communicate a maximum allowed input length.
|
|
@@ -2772,7 +2765,7 @@ const TextAreaField = ({ id, label, tip, helpText, helpAddon, errorMessage, isIn
|
|
|
2772
2765
|
}
|
|
2773
2766
|
}, [onChange]);
|
|
2774
2767
|
return (jsxRuntime.jsx(FormGroup, { className: tailwindMerge.twMerge(className, "grid", "grid-rows-[auto_1fr_auto]"), dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon ||
|
|
2775
|
-
(typeof maxLength === "number" && jsxRuntime.jsx(TextLengthIndicator, { length: valueLength, maxLength: maxLength })), helpText: errorMessage || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required ? !(rest.disabled || rest.readOnly) : false, tip: tip, children: jsxRuntime.jsx(
|
|
2768
|
+
(typeof maxLength === "number" && jsxRuntime.jsx(TextLengthIndicator, { length: valueLength, maxLength: maxLength })), helpText: errorMessage || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required ? !(rest.disabled || rest.readOnly) : false, tip: tip, children: jsxRuntime.jsx(TextAreaBaseInput, { "aria-labelledby": htmlForId + "-label", id: htmlForId, isInvalid: renderAsInvalid, maxLength: maxLength, ref: ref, value: value, ...rest, className: "h-auto", dataTestId: dataTestId, onChange: handleChange }) }));
|
|
2776
2769
|
};
|
|
2777
2770
|
TextAreaField.displayName = "TextAreaField";
|
|
2778
2771
|
|
|
@@ -2790,7 +2783,7 @@ const TextField = ({ id, label, tip, helpText, helpAddon, errorMessage, isInvali
|
|
|
2790
2783
|
}
|
|
2791
2784
|
}, [onChange]);
|
|
2792
2785
|
return (jsxRuntime.jsx(FormGroup, { dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon ||
|
|
2793
|
-
(typeof maxLength === "number" && jsxRuntime.jsx(TextLengthIndicator, { length: valueLength, maxLength: maxLength })), helpText: (renderAsInvalid && errorMessage) || helpText, htmlFor: htmlFor, isInvalid: renderAsInvalid, isWarning: isWarning, label: label, required: rest.required ? !(rest.disabled || rest.readOnly) : false, tip: tip, children: jsxRuntime.jsx(
|
|
2786
|
+
(typeof maxLength === "number" && jsxRuntime.jsx(TextLengthIndicator, { length: valueLength, maxLength: maxLength })), helpText: (renderAsInvalid && errorMessage) || helpText, htmlFor: htmlFor, isInvalid: renderAsInvalid, isWarning: isWarning, label: label, required: rest.required ? !(rest.disabled || rest.readOnly) : false, tip: tip, children: jsxRuntime.jsx(TextBaseInput, { "aria-labelledby": htmlFor + "-label", id: htmlFor, isInvalid: renderAsInvalid, isWarning: isWarning, maxLength: maxLength, ref: ref, value: value, ...rest, className: className, dataTestId: dataTestId, onChange: handleChange }) }));
|
|
2794
2787
|
};
|
|
2795
2788
|
TextField.displayName = "TextField";
|
|
2796
2789
|
|
|
@@ -3014,12 +3007,11 @@ const validateUrl = (url, required) => {
|
|
|
3014
3007
|
*
|
|
3015
3008
|
* NOTE: If shown with a label, please use the `UrlField` component instead.
|
|
3016
3009
|
*/
|
|
3017
|
-
const
|
|
3010
|
+
const UrlBaseInput = ({ dataTestId, isInvalid, disabled = false, fieldSize = "medium", disableAction = false, value, defaultValue, ref, ...rest }) => {
|
|
3018
3011
|
const [url, setUrl] = react.useState(value?.toString() || defaultValue?.toString());
|
|
3019
3012
|
const renderAsInvalid = (url && typeof url === "string" && !validateUrlAddress(url)) || isInvalid;
|
|
3020
3013
|
return (jsxRuntime.jsx(BaseInput, { dataTestId: dataTestId ? `${dataTestId}-url-input` : undefined, disabled: disabled, id: "url-input", isInvalid: renderAsInvalid, onChange: e => setUrl(e.target.value), placeholder: rest.placeholder || "https://www.example.com", ref: ref, type: "url", value: url, ...rest, actions: !disableAction && (jsxRuntime.jsx(ActionButton, { dataTestId: (dataTestId && `${dataTestId}-url-input-Icon`) || "url-input-action-icon", disabled: renderAsInvalid || Boolean(disabled) || disableAction, size: fieldSize ?? undefined, type: "WEB_ADDRESS", value: url })) }));
|
|
3021
3014
|
};
|
|
3022
|
-
UrlInput.displayName = "UrlField";
|
|
3023
3015
|
|
|
3024
3016
|
/**
|
|
3025
3017
|
* The UrlField component is used to enter url.
|
|
@@ -3041,7 +3033,7 @@ const UrlField = ({ label, id, tip, helpText, errorMessage, helpAddon, className
|
|
|
3041
3033
|
setRenderAsInvalid(!!validateUrl(newValue, rest.required));
|
|
3042
3034
|
onBlur?.(event);
|
|
3043
3035
|
}, [onBlur, rest.required]);
|
|
3044
|
-
return (jsxRuntime.jsx(FormGroup, { dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: renderAsInvalid ? error : helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required ? !(rest.disabled || rest.readOnly) : false, tip: tip, children: jsxRuntime.jsx(
|
|
3036
|
+
return (jsxRuntime.jsx(FormGroup, { dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: renderAsInvalid ? error : helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required ? !(rest.disabled || rest.readOnly) : false, tip: tip, children: jsxRuntime.jsx(UrlBaseInput, { "aria-labelledby": htmlForId + "-label", id: htmlForId, isInvalid: renderAsInvalid, onBlur: handleBlur, ref: ref, value: value || defaultValue, ...rest, className: className, dataTestId: dataTestId }) }));
|
|
3045
3037
|
};
|
|
3046
3038
|
UrlField.displayName = "UrlField";
|
|
3047
3039
|
|
|
@@ -3166,25 +3158,24 @@ exports.ColorField = ColorField;
|
|
|
3166
3158
|
exports.CreatableSelect = CreatableSelect;
|
|
3167
3159
|
exports.CreatableSelectField = CreatableSelectField;
|
|
3168
3160
|
exports.DEFAULT_TIME = DEFAULT_TIME;
|
|
3161
|
+
exports.DateBaseInput = DateBaseInput;
|
|
3169
3162
|
exports.DateField = DateField;
|
|
3170
|
-
exports.DateInput = DateInput;
|
|
3171
3163
|
exports.DropZone = DropZone;
|
|
3172
3164
|
exports.DropZoneDefaultLabel = DropZoneDefaultLabel;
|
|
3173
3165
|
exports.EMAIL_REGEX = EMAIL_REGEX;
|
|
3174
3166
|
exports.EmailField = EmailField;
|
|
3175
|
-
exports.EmailInput = EmailInput;
|
|
3176
3167
|
exports.FormFieldSelectAdapter = FormFieldSelectAdapter;
|
|
3177
3168
|
exports.FormGroup = FormGroup;
|
|
3178
3169
|
exports.Label = Label;
|
|
3179
3170
|
exports.MultiSelectMenuItem = MultiSelectMenuItem;
|
|
3171
|
+
exports.NumberBaseInput = NumberBaseInput;
|
|
3180
3172
|
exports.NumberField = NumberField;
|
|
3181
|
-
exports.NumberInput = NumberInput;
|
|
3182
3173
|
exports.OptionCard = OptionCard;
|
|
3174
|
+
exports.PasswordBaseInput = PasswordBaseInput;
|
|
3183
3175
|
exports.PasswordField = PasswordField;
|
|
3184
|
-
exports.
|
|
3176
|
+
exports.PhoneBaseInput = PhoneBaseInput;
|
|
3185
3177
|
exports.PhoneField = PhoneField;
|
|
3186
3178
|
exports.PhoneFieldWithController = PhoneFieldWithController;
|
|
3187
|
-
exports.PhoneInput = PhoneInput;
|
|
3188
3179
|
exports.RadioGroup = RadioGroup;
|
|
3189
3180
|
exports.RadioItem = RadioItem;
|
|
3190
3181
|
exports.Schedule = Schedule;
|
|
@@ -3192,10 +3183,10 @@ exports.Search = Search;
|
|
|
3192
3183
|
exports.Select = Select;
|
|
3193
3184
|
exports.SelectField = SelectField;
|
|
3194
3185
|
exports.SingleSelectMenuItem = SingleSelectMenuItem;
|
|
3195
|
-
exports.
|
|
3186
|
+
exports.TextAreaBaseInput = TextAreaBaseInput;
|
|
3196
3187
|
exports.TextAreaField = TextAreaField;
|
|
3188
|
+
exports.TextBaseInput = TextBaseInput;
|
|
3197
3189
|
exports.TextField = TextField;
|
|
3198
|
-
exports.TextInput = TextInput;
|
|
3199
3190
|
exports.TimeRange = TimeRange;
|
|
3200
3191
|
exports.TimeRangeField = TimeRangeField;
|
|
3201
3192
|
exports.ToggleSwitch = ToggleSwitch;
|
|
@@ -3203,7 +3194,6 @@ exports.ToggleSwitchOption = ToggleSwitchOption;
|
|
|
3203
3194
|
exports.UploadField = UploadField;
|
|
3204
3195
|
exports.UploadInput = UploadInput;
|
|
3205
3196
|
exports.UrlField = UrlField;
|
|
3206
|
-
exports.UrlInput = UrlInput;
|
|
3207
3197
|
exports.checkIfPhoneNumberHasPlus = checkIfPhoneNumberHasPlus;
|
|
3208
3198
|
exports.countryCodeToFlagEmoji = countryCodeToFlagEmoji;
|
|
3209
3199
|
exports.cvaAccessoriesContainer = cvaAccessoriesContainer;
|