@thunderid/react 1.4.0 → 1.5.0
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/ThunderIDReactClient.d.ts +5 -0
- package/dist/ThunderIDReactClient.d.ts.map +1 -1
- package/dist/ThunderIDReactClient.js +11 -1
- package/dist/ThunderIDReactClient.js.map +1 -1
- package/dist/cjs/ThunderIDReactClient.cjs +11 -1
- package/dist/cjs/ThunderIDReactClient.cjs.map +1 -1
- package/dist/cjs/components/presentation/auth/SignIn/BaseSignIn.cjs +1 -0
- package/dist/cjs/components/presentation/auth/SignIn/BaseSignIn.cjs.map +1 -1
- package/dist/cjs/components/presentation/auth/SignIn/SignIn.cjs +2 -3
- package/dist/cjs/components/presentation/auth/SignIn/SignIn.cjs.map +1 -1
- package/dist/cjs/components/presentation/auth/SignUp/BaseSignUp.cjs +1 -0
- package/dist/cjs/components/presentation/auth/SignUp/BaseSignUp.cjs.map +1 -1
- package/dist/cjs/components/primitives/AffixedField/AffixedField.cjs +11 -1
- package/dist/cjs/components/primitives/AffixedField/AffixedField.cjs.map +1 -1
- package/dist/cjs/components/primitives/Checkbox/Checkbox.cjs +11 -1
- package/dist/cjs/components/primitives/Checkbox/Checkbox.cjs.map +1 -1
- package/dist/cjs/components/primitives/DatePicker/DatePicker.cjs +11 -1
- package/dist/cjs/components/primitives/DatePicker/DatePicker.cjs.map +1 -1
- package/dist/cjs/components/primitives/FormControl/FormControl.cjs +2 -1
- package/dist/cjs/components/primitives/FormControl/FormControl.cjs.map +1 -1
- package/dist/cjs/components/primitives/MultiInput/MultiInput.cjs +11 -2
- package/dist/cjs/components/primitives/MultiInput/MultiInput.cjs.map +1 -1
- package/dist/cjs/components/primitives/OtpField/OtpField.cjs +9 -0
- package/dist/cjs/components/primitives/OtpField/OtpField.cjs.map +1 -1
- package/dist/cjs/components/primitives/Select/Select.cjs +8 -0
- package/dist/cjs/components/primitives/Select/Select.cjs.map +1 -1
- package/dist/cjs/components/primitives/TextField/TextField.cjs +11 -1
- package/dist/cjs/components/primitives/TextField/TextField.cjs.map +1 -1
- package/dist/cjs/components/primitives/Toggle/Toggle.cjs +10 -1
- package/dist/cjs/components/primitives/Toggle/Toggle.cjs.map +1 -1
- package/dist/cjs/contexts/Theme/ThemeProvider.cjs +1 -1
- package/dist/cjs/contexts/Theme/ThemeProvider.cjs.map +1 -1
- package/dist/cjs/contexts/ThunderID/ThunderIDContext.cjs.map +1 -1
- package/dist/cjs/contexts/ThunderID/ThunderIDProvider.cjs +6 -1
- package/dist/cjs/contexts/ThunderID/ThunderIDProvider.cjs.map +1 -1
- package/dist/components/presentation/auth/SignIn/BaseSignIn.d.ts +4 -0
- package/dist/components/presentation/auth/SignIn/BaseSignIn.d.ts.map +1 -1
- package/dist/components/presentation/auth/SignIn/BaseSignIn.js +1 -0
- package/dist/components/presentation/auth/SignIn/BaseSignIn.js.map +1 -1
- package/dist/components/presentation/auth/SignIn/SignIn.d.ts.map +1 -1
- package/dist/components/presentation/auth/SignIn/SignIn.js +2 -3
- package/dist/components/presentation/auth/SignIn/SignIn.js.map +1 -1
- package/dist/components/presentation/auth/SignUp/BaseSignUp.d.ts +4 -0
- package/dist/components/presentation/auth/SignUp/BaseSignUp.d.ts.map +1 -1
- package/dist/components/presentation/auth/SignUp/BaseSignUp.js +1 -0
- package/dist/components/presentation/auth/SignUp/BaseSignUp.js.map +1 -1
- package/dist/components/primitives/AffixedField/AffixedField.d.ts.map +1 -1
- package/dist/components/primitives/AffixedField/AffixedField.js +11 -1
- package/dist/components/primitives/AffixedField/AffixedField.js.map +1 -1
- package/dist/components/primitives/Checkbox/Checkbox.d.ts.map +1 -1
- package/dist/components/primitives/Checkbox/Checkbox.js +10 -1
- package/dist/components/primitives/Checkbox/Checkbox.js.map +1 -1
- package/dist/components/primitives/DatePicker/DatePicker.d.ts.map +1 -1
- package/dist/components/primitives/DatePicker/DatePicker.js +10 -1
- package/dist/components/primitives/DatePicker/DatePicker.js.map +1 -1
- package/dist/components/primitives/FormControl/FormControl.d.ts +4 -0
- package/dist/components/primitives/FormControl/FormControl.d.ts.map +1 -1
- package/dist/components/primitives/FormControl/FormControl.js +2 -1
- package/dist/components/primitives/FormControl/FormControl.js.map +1 -1
- package/dist/components/primitives/MultiInput/MultiInput.d.ts.map +1 -1
- package/dist/components/primitives/MultiInput/MultiInput.js +12 -3
- package/dist/components/primitives/MultiInput/MultiInput.js.map +1 -1
- package/dist/components/primitives/OtpField/OtpField.d.ts.map +1 -1
- package/dist/components/primitives/OtpField/OtpField.js +10 -1
- package/dist/components/primitives/OtpField/OtpField.js.map +1 -1
- package/dist/components/primitives/Select/Select.d.ts.map +1 -1
- package/dist/components/primitives/Select/Select.js +9 -1
- package/dist/components/primitives/Select/Select.js.map +1 -1
- package/dist/components/primitives/TextField/TextField.d.ts.map +1 -1
- package/dist/components/primitives/TextField/TextField.js +10 -1
- package/dist/components/primitives/TextField/TextField.js.map +1 -1
- package/dist/components/primitives/Toggle/Toggle.d.ts.map +1 -1
- package/dist/components/primitives/Toggle/Toggle.js +9 -1
- package/dist/components/primitives/Toggle/Toggle.js.map +1 -1
- package/dist/contexts/Theme/ThemeProvider.d.ts.map +1 -1
- package/dist/contexts/Theme/ThemeProvider.js +1 -1
- package/dist/contexts/Theme/ThemeProvider.js.map +1 -1
- package/dist/contexts/ThunderID/ThunderIDContext.d.ts +4 -3
- package/dist/contexts/ThunderID/ThunderIDContext.d.ts.map +1 -1
- package/dist/contexts/ThunderID/ThunderIDContext.js.map +1 -1
- package/dist/contexts/ThunderID/ThunderIDProvider.d.ts.map +1 -1
- package/dist/contexts/ThunderID/ThunderIDProvider.js +6 -1
- package/dist/contexts/ThunderID/ThunderIDProvider.js.map +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AffixedField.cjs","names":["AffixedField: FC<AffixedFieldProps>","useTheme","hasError: boolean","prefixList: PrefixOption[]","hasPrefix: boolean","hasFixedPrefix: boolean","styles: Record<string, string>","useStyles","prefixDescriptionId: string","containerClassName: string","cx","inputClassName: string","selectedValue: string","FormControl","InputLabel"],"sources":["../../../../../src/components/primitives/AffixedField/AffixedField.tsx"],"sourcesContent":["// Copyright 2026 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {PrefixOption, bem, withVendorCSSClassPrefix} from '@thunderid/browser';\nimport {ChangeEvent, FC, InputHTMLAttributes, ReactElement, useId} from 'react';\nimport useStyles from './AffixedField.styles';\nimport useTheme from '../../../contexts/Theme/useTheme';\nimport {cx} from '../../../styles/emotion';\nimport FormControl from '../FormControl/FormControl';\nimport InputLabel from '../InputLabel/InputLabel';\n\nexport interface AffixedFieldProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'className' | 'prefix'> {\n /** Additional CSS class names applied to the outer form control. */\n className?: string;\n /** Whether the field is disabled. */\n disabled?: boolean;\n /** Error message to render below the field. */\n error?: string;\n /** Helper text to render below the field. */\n helperText?: string;\n /** Label rendered above the field. */\n label?: string;\n /**\n * Called when the user changes the selected prefix. Only fires when `prefixes` is an\n * array with two or more entries and the user picks a different one.\n */\n onPrefixChange?: (prefixValue: string) => void;\n /**\n * Currently selected prefix `value` (must match one of the resolved prefix entries'\n * `value`). Required when `prefixes` resolves to a non-empty list. Ignored otherwise.\n */\n prefixValue?: string;\n /**\n * Leading affix. `string` renders as a fixed span; `PrefixOption[]` renders as a fixed\n * span (single entry) or dropdown (multiple entries). Empty or undefined renders a\n * plain input with no leading element.\n */\n prefixes?: string | PrefixOption[];\n /** Whether the field is required. */\n required?: boolean;\n}\n\n/**\n * Converts the polymorphic `prefixes` prop into a uniform list. A string is treated\n * as a single fixed entry so the rest of the component can iterate a single shape.\n */\nconst normalizePrefixes = (prefixes: string | PrefixOption[] | undefined): PrefixOption[] => {\n if (typeof prefixes === 'string') {\n return prefixes.length > 0 ? [{label: prefixes, value: prefixes}] : [];\n }\n return prefixes ?? [];\n};\n\nconst AffixedField: FC<AffixedFieldProps> = ({\n label,\n error,\n required,\n className,\n disabled,\n helperText,\n prefixes,\n prefixValue,\n onPrefixChange,\n type = 'text',\n style = {},\n ...rest\n}: AffixedFieldProps) => {\n const {theme, colorScheme}: ReturnType<typeof useTheme> = useTheme();\n const hasError: boolean = !!error;\n const prefixList: PrefixOption[] = normalizePrefixes(prefixes);\n const hasPrefix: boolean = prefixList.length > 0;\n const hasFixedPrefix: boolean = prefixList.length === 1;\n const styles: Record<string, string> = useStyles(theme, colorScheme, disabled ?? false, hasError, hasPrefix);\n // Stable id for the visually-hidden prefix announcement; only referenced when a\n // fixed prefix is present, but generated unconditionally so the hook order is stable.\n const prefixDescriptionId: string = `${useId()}-prefix`;\n\n const containerClassName: string = cx(\n withVendorCSSClassPrefix(bem('affixed-field', 'container')),\n styles['inputContainer'],\n );\n\n const inputClassName: string = cx(withVendorCSSClassPrefix(bem('affixed-field', 'input')), styles['input']);\n\n const renderPrefix = (): ReactElement | null => {\n if (prefixList.length === 0) return null;\n\n if (prefixList.length === 1) {\n return (\n <>\n <span\n className={cx(withVendorCSSClassPrefix(bem('affixed-field', 'prefix-span')), styles['prefixSpan'])}\n aria-hidden=\"true\"\n >\n {prefixList[0].label}\n </span>\n {/* Assistive-tech-only mirror of the fixed prefix; wired to the input via aria-describedby. */}\n <span id={prefixDescriptionId} className={styles['visuallyHidden']}>\n {prefixList[0].label}\n </span>\n </>\n );\n }\n\n const selectedValue: string = prefixValue ?? prefixList[0].value;\n return (\n <select\n className={cx(withVendorCSSClassPrefix(bem('affixed-field', 'prefix-select')), styles['prefixSelect'])}\n value={selectedValue}\n disabled={disabled}\n onChange={(e: ChangeEvent<HTMLSelectElement>): void => onPrefixChange?.(e.target.value)}\n aria-label=\"Prefix\"\n >\n {prefixList.map((p: PrefixOption) => (\n <option key={p.value} value={p.value}>\n {p.label}\n </option>\n ))}\n </select>\n );\n };\n\n return (\n <FormControl\n error={error}\n helperText={helperText}\n className={cx(withVendorCSSClassPrefix(bem('affixed-field')), className)}\n style={style}\n >\n {label && (\n <InputLabel required={required} error={hasError}>\n {label}\n </InputLabel>\n )}\n <div className={containerClassName}>\n {renderPrefix()}\n <input\n className={inputClassName}\n type={type}\n disabled={disabled}\n aria-invalid={hasError}\n aria-required={required}\n {...rest}\n aria-describedby={\n [rest['aria-describedby'], hasFixedPrefix ? prefixDescriptionId : undefined].filter(Boolean).join(' ')
|
|
1
|
+
{"version":3,"file":"AffixedField.cjs","names":["AffixedField: FC<AffixedFieldProps>","useTheme","hasError: boolean","prefixList: PrefixOption[]","hasPrefix: boolean","hasFixedPrefix: boolean","styles: Record<string, string>","useStyles","prefixDescriptionId: string","generatedId: string","inputId: string","helperTextId: string | undefined","containerClassName: string","cx","inputClassName: string","selectedValue: string","FormControl","InputLabel"],"sources":["../../../../../src/components/primitives/AffixedField/AffixedField.tsx"],"sourcesContent":["// Copyright 2026 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {PrefixOption, bem, withVendorCSSClassPrefix} from '@thunderid/browser';\nimport {ChangeEvent, FC, InputHTMLAttributes, ReactElement, useId} from 'react';\nimport useStyles from './AffixedField.styles';\nimport useTheme from '../../../contexts/Theme/useTheme';\nimport {cx} from '../../../styles/emotion';\nimport FormControl from '../FormControl/FormControl';\nimport InputLabel from '../InputLabel/InputLabel';\n\nexport interface AffixedFieldProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'className' | 'prefix'> {\n /** Additional CSS class names applied to the outer form control. */\n className?: string;\n /** Whether the field is disabled. */\n disabled?: boolean;\n /** Error message to render below the field. */\n error?: string;\n /** Helper text to render below the field. */\n helperText?: string;\n /** Label rendered above the field. */\n label?: string;\n /**\n * Called when the user changes the selected prefix. Only fires when `prefixes` is an\n * array with two or more entries and the user picks a different one.\n */\n onPrefixChange?: (prefixValue: string) => void;\n /**\n * Currently selected prefix `value` (must match one of the resolved prefix entries'\n * `value`). Required when `prefixes` resolves to a non-empty list. Ignored otherwise.\n */\n prefixValue?: string;\n /**\n * Leading affix. `string` renders as a fixed span; `PrefixOption[]` renders as a fixed\n * span (single entry) or dropdown (multiple entries). Empty or undefined renders a\n * plain input with no leading element.\n */\n prefixes?: string | PrefixOption[];\n /** Whether the field is required. */\n required?: boolean;\n}\n\n/**\n * Converts the polymorphic `prefixes` prop into a uniform list. A string is treated\n * as a single fixed entry so the rest of the component can iterate a single shape.\n */\nconst normalizePrefixes = (prefixes: string | PrefixOption[] | undefined): PrefixOption[] => {\n if (typeof prefixes === 'string') {\n return prefixes.length > 0 ? [{label: prefixes, value: prefixes}] : [];\n }\n return prefixes ?? [];\n};\n\nconst AffixedField: FC<AffixedFieldProps> = ({\n label,\n error,\n required,\n className,\n disabled,\n helperText,\n prefixes,\n prefixValue,\n onPrefixChange,\n type = 'text',\n style = {},\n ...rest\n}: AffixedFieldProps) => {\n const {theme, colorScheme}: ReturnType<typeof useTheme> = useTheme();\n const hasError: boolean = !!error;\n const prefixList: PrefixOption[] = normalizePrefixes(prefixes);\n const hasPrefix: boolean = prefixList.length > 0;\n const hasFixedPrefix: boolean = prefixList.length === 1;\n const styles: Record<string, string> = useStyles(theme, colorScheme, disabled ?? false, hasError, hasPrefix);\n // Stable id for the visually-hidden prefix announcement; only referenced when a\n // fixed prefix is present, but generated unconditionally so the hook order is stable.\n const prefixDescriptionId: string = `${useId()}-prefix`;\n const generatedId: string = useId();\n const inputId: string = rest.id ?? generatedId;\n const helperTextId: string | undefined = error || helperText ? `${inputId}-helper-text` : undefined;\n\n const containerClassName: string = cx(\n withVendorCSSClassPrefix(bem('affixed-field', 'container')),\n styles['inputContainer'],\n );\n\n const inputClassName: string = cx(withVendorCSSClassPrefix(bem('affixed-field', 'input')), styles['input']);\n\n const renderPrefix = (): ReactElement | null => {\n if (prefixList.length === 0) return null;\n\n if (prefixList.length === 1) {\n return (\n <>\n <span\n className={cx(withVendorCSSClassPrefix(bem('affixed-field', 'prefix-span')), styles['prefixSpan'])}\n aria-hidden=\"true\"\n >\n {prefixList[0].label}\n </span>\n {/* Assistive-tech-only mirror of the fixed prefix; wired to the input via aria-describedby. */}\n <span id={prefixDescriptionId} className={styles['visuallyHidden']}>\n {prefixList[0].label}\n </span>\n </>\n );\n }\n\n const selectedValue: string = prefixValue ?? prefixList[0].value;\n return (\n <select\n className={cx(withVendorCSSClassPrefix(bem('affixed-field', 'prefix-select')), styles['prefixSelect'])}\n value={selectedValue}\n disabled={disabled}\n onChange={(e: ChangeEvent<HTMLSelectElement>): void => onPrefixChange?.(e.target.value)}\n aria-label=\"Prefix\"\n >\n {prefixList.map((p: PrefixOption) => (\n <option key={p.value} value={p.value}>\n {p.label}\n </option>\n ))}\n </select>\n );\n };\n\n return (\n <FormControl\n error={error}\n helperText={helperText}\n helperTextId={helperTextId}\n className={cx(withVendorCSSClassPrefix(bem('affixed-field')), className)}\n style={style}\n >\n {label && (\n <InputLabel htmlFor={inputId} required={required} error={hasError}>\n {label}\n </InputLabel>\n )}\n <div className={containerClassName}>\n {renderPrefix()}\n <input\n className={inputClassName}\n type={type}\n disabled={disabled}\n aria-invalid={hasError}\n aria-required={required}\n {...rest}\n id={inputId}\n aria-describedby={\n [rest['aria-describedby'], hasFixedPrefix ? prefixDescriptionId : undefined, helperTextId]\n .filter(Boolean)\n .join(' ') || undefined\n }\n />\n </div>\n </FormControl>\n );\n};\n\nexport default AffixedField;\n"],"mappings":";;;;;;;;;;;;;;;;;;AA8CA,MAAM,qBAAqB,aAAkE;AAC3F,KAAI,OAAO,aAAa,SACtB,QAAO,SAAS,SAAS,IAAI,CAAC;EAAC,OAAO;EAAU,OAAO;EAAS,CAAC,GAAG,EAAE;AAExE,QAAO,YAAY,EAAE;;AAGvB,MAAMA,gBAAuC,EAC3C,OACA,OACA,UACA,WACA,UACA,YACA,UACA,aACA,gBACA,OAAO,QACP,QAAQ,EAAE,CACV,GAAG,WACoB;CACvB,MAAM,EAAC,OAAO,gBAA4CC,0BAAU;CACpE,MAAMC,WAAoB,CAAC,CAAC;CAC5B,MAAMC,aAA6B,kBAAkB,SAAS;CAC9D,MAAMC,YAAqB,WAAW,SAAS;CAC/C,MAAMC,iBAA0B,WAAW,WAAW;CACtD,MAAMC,SAAiCC,oCAAU,OAAO,aAAa,YAAY,OAAO,UAAU,UAAU;CAG5G,MAAMC,sBAA8B,qBAAU,CAAC;CAC/C,MAAMC,gCAA6B;CACnC,MAAMC,UAAkB,KAAK,MAAM;CACnC,MAAMC,eAAmC,SAAS,aAAa,GAAG,QAAQ,gBAAgB;CAE1F,MAAMC,qBAA6BC,kGACJ,iBAAiB,YAAY,CAAC,EAC3D,OAAO,kBACR;CAED,MAAMC,iBAAyBD,kGAAgC,iBAAiB,QAAQ,CAAC,EAAE,OAAO,SAAS;CAE3G,MAAM,qBAA0C;AAC9C,MAAI,WAAW,WAAW,EAAG,QAAO;AAEpC,MAAI,WAAW,WAAW,EACxB,QACE,qFACE,2CAAC;GACC,WAAWA,kGAAgC,iBAAiB,cAAc,CAAC,EAAE,OAAO,cAAc;GAClG,eAAY;aAEX,WAAW,GAAG;IACV,EAEP,2CAAC;GAAK,IAAI;GAAqB,WAAW,OAAO;aAC9C,WAAW,GAAG;IACV,IACN;EAIP,MAAME,gBAAwB,eAAe,WAAW,GAAG;AAC3D,SACE,2CAAC;GACC,WAAWF,kGAAgC,iBAAiB,gBAAgB,CAAC,EAAE,OAAO,gBAAgB;GACtG,OAAO;GACG;GACV,WAAW,MAA4C,iBAAiB,EAAE,OAAO,MAAM;GACvF,cAAW;aAEV,WAAW,KAAK,MACf,2CAAC;IAAqB,OAAO,EAAE;cAC5B,EAAE;MADQ,EAAE,MAEN,CACT;IACK;;AAIb,QACE,4CAACG;EACQ;EACK;EACE;EACd,WAAWH,kGAAgC,gBAAgB,CAAC,EAAE,UAAU;EACjE;aAEN,SACC,2CAACI;GAAW,SAAS;GAAmB;GAAU,OAAO;aACtD;IACU,EAEf,4CAAC;GAAI,WAAW;cACb,cAAc,EACf,2CAAC;IACC,WAAW;IACL;IACI;IACV,gBAAc;IACd,iBAAe;IACf,GAAI;IACJ,IAAI;IACJ,oBACE;KAAC,KAAK;KAAqB,iBAAiB,sBAAsB;KAAW;KAAa,CACvF,OAAO,QAAQ,CACf,KAAK,IAAI,IAAI;KAElB;IACE;GACM;;AAIlB,2BAAe"}
|
|
@@ -6,6 +6,8 @@ const require_InputLabel = require('../InputLabel/InputLabel.cjs');
|
|
|
6
6
|
const require_Checkbox_styles = require('./Checkbox.styles.cjs');
|
|
7
7
|
let __thunderid_browser = require("@thunderid/browser");
|
|
8
8
|
__thunderid_browser = require_rolldown_runtime.__toESM(__thunderid_browser);
|
|
9
|
+
let react = require("react");
|
|
10
|
+
react = require_rolldown_runtime.__toESM(react);
|
|
9
11
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
12
|
react_jsx_runtime = require_rolldown_runtime.__toESM(react_jsx_runtime);
|
|
11
13
|
|
|
@@ -13,10 +15,15 @@ react_jsx_runtime = require_rolldown_runtime.__toESM(react_jsx_runtime);
|
|
|
13
15
|
const Checkbox = ({ label, error, className, required, helperText, style = {},...rest }) => {
|
|
14
16
|
const { theme, colorScheme } = require_useTheme.default();
|
|
15
17
|
const hasError = !!error;
|
|
18
|
+
const generatedId = (0, react.useId)();
|
|
19
|
+
const inputId = rest.id ?? generatedId;
|
|
20
|
+
const helperTextId = error || helperText ? `${inputId}-helper-text` : void 0;
|
|
21
|
+
const describedBy = [rest["aria-describedby"], helperTextId].filter(Boolean).join(" ") || void 0;
|
|
16
22
|
const styles = require_Checkbox_styles.default(theme, colorScheme, hasError, !!required);
|
|
17
23
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_FormControl.default, {
|
|
18
24
|
error,
|
|
19
25
|
helperText,
|
|
26
|
+
helperTextId,
|
|
20
27
|
className: require_emotion.cx((0, __thunderid_browser.withVendorCSSClassPrefix)((0, __thunderid_browser.bem)("checkbox")), className),
|
|
21
28
|
helperTextMarginLeft: `calc(${theme.vars.spacing.unit} * 3.5)`,
|
|
22
29
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -27,8 +34,11 @@ const Checkbox = ({ label, error, className, required, helperText, style = {},..
|
|
|
27
34
|
className: require_emotion.cx((0, __thunderid_browser.withVendorCSSClassPrefix)((0, __thunderid_browser.bem)("checkbox", "input")), styles["input"], styles["errorInput"], { [(0, __thunderid_browser.withVendorCSSClassPrefix)((0, __thunderid_browser.bem)("checkbox", "input", "error"))]: hasError }),
|
|
28
35
|
"aria-invalid": hasError,
|
|
29
36
|
"aria-required": required,
|
|
30
|
-
...rest
|
|
37
|
+
...rest,
|
|
38
|
+
id: inputId,
|
|
39
|
+
"aria-describedby": describedBy
|
|
31
40
|
}), label && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_InputLabel.default, {
|
|
41
|
+
htmlFor: inputId,
|
|
32
42
|
required,
|
|
33
43
|
error: hasError,
|
|
34
44
|
variant: "inline",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Checkbox.cjs","names":["Checkbox: FC<CheckboxProps>","useTheme","styles: Record<string, string>","useStyles","FormControl","cx","InputLabel"],"sources":["../../../../../src/components/primitives/Checkbox/Checkbox.tsx"],"sourcesContent":["// Copyright 2024 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {withVendorCSSClassPrefix, bem} from '@thunderid/browser';\nimport {FC, InputHTMLAttributes} from 'react';\nimport useStyles from './Checkbox.styles';\nimport useTheme from '../../../contexts/Theme/useTheme';\nimport {cx} from '../../../styles/emotion';\nimport FormControl from '../FormControl/FormControl';\nimport InputLabel from '../InputLabel/InputLabel';\n\nexport interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'className' | 'type'> {\n /**\n * Additional CSS class names\n */\n className?: string;\n /**\n * Error message to display below the checkbox\n */\n error?: string;\n /**\n * Helper text to display below the checkbox\n */\n helperText?: string;\n /**\n * Label text to display next to the checkbox\n */\n label?: string;\n /**\n * Whether the field is required\n */\n required?: boolean;\n}\n\nconst Checkbox: FC<CheckboxProps> = ({\n label,\n error,\n className,\n required,\n helperText,\n style = {},\n ...rest\n}: CheckboxProps) => {\n const {theme, colorScheme}: ReturnType<typeof useTheme> = useTheme();\n const hasError = !!error;\n const styles: Record<string, string> = useStyles(theme, colorScheme, hasError, !!required);\n\n return (\n <FormControl\n error={error}\n helperText={helperText}\n className={cx(withVendorCSSClassPrefix(bem('checkbox')), className)}\n helperTextMarginLeft={`calc(${theme.vars.spacing.unit} * 3.5)`}\n >\n <div style={style} className={cx(withVendorCSSClassPrefix(bem('checkbox', 'container')), styles['container'])}>\n <input\n type=\"checkbox\"\n className={cx(withVendorCSSClassPrefix(bem('checkbox', 'input')), styles['input'], styles['errorInput'], {\n [withVendorCSSClassPrefix(bem('checkbox', 'input', 'error'))]: hasError,\n })}\n aria-invalid={hasError}\n aria-required={required}\n {...rest}\n />\n {label && (\n <InputLabel\n required={required}\n error={hasError}\n variant=\"inline\"\n className={cx(withVendorCSSClassPrefix(bem('checkbox', 'label')), styles['label'], styles['errorLabel'], {\n [withVendorCSSClassPrefix(bem('checkbox', 'label', 'error'))]: hasError,\n })}\n >\n {label}\n </InputLabel>\n )}\n </div>\n </FormControl>\n );\n};\n\nexport default Checkbox;\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"Checkbox.cjs","names":["Checkbox: FC<CheckboxProps>","useTheme","generatedId: string","inputId: string","helperTextId: string | undefined","describedBy: string | undefined","styles: Record<string, string>","useStyles","FormControl","cx","InputLabel"],"sources":["../../../../../src/components/primitives/Checkbox/Checkbox.tsx"],"sourcesContent":["// Copyright 2024 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {withVendorCSSClassPrefix, bem} from '@thunderid/browser';\nimport {FC, InputHTMLAttributes, useId} from 'react';\nimport useStyles from './Checkbox.styles';\nimport useTheme from '../../../contexts/Theme/useTheme';\nimport {cx} from '../../../styles/emotion';\nimport FormControl from '../FormControl/FormControl';\nimport InputLabel from '../InputLabel/InputLabel';\n\nexport interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'className' | 'type'> {\n /**\n * Additional CSS class names\n */\n className?: string;\n /**\n * Error message to display below the checkbox\n */\n error?: string;\n /**\n * Helper text to display below the checkbox\n */\n helperText?: string;\n /**\n * Label text to display next to the checkbox\n */\n label?: string;\n /**\n * Whether the field is required\n */\n required?: boolean;\n}\n\nconst Checkbox: FC<CheckboxProps> = ({\n label,\n error,\n className,\n required,\n helperText,\n style = {},\n ...rest\n}: CheckboxProps) => {\n const {theme, colorScheme}: ReturnType<typeof useTheme> = useTheme();\n const hasError = !!error;\n const generatedId: string = useId();\n const inputId: string = rest.id ?? generatedId;\n const helperTextId: string | undefined = error || helperText ? `${inputId}-helper-text` : undefined;\n const describedBy: string | undefined =\n [rest['aria-describedby'], helperTextId].filter(Boolean).join(' ') || undefined;\n const styles: Record<string, string> = useStyles(theme, colorScheme, hasError, !!required);\n\n return (\n <FormControl\n error={error}\n helperText={helperText}\n helperTextId={helperTextId}\n className={cx(withVendorCSSClassPrefix(bem('checkbox')), className)}\n helperTextMarginLeft={`calc(${theme.vars.spacing.unit} * 3.5)`}\n >\n <div style={style} className={cx(withVendorCSSClassPrefix(bem('checkbox', 'container')), styles['container'])}>\n <input\n type=\"checkbox\"\n className={cx(withVendorCSSClassPrefix(bem('checkbox', 'input')), styles['input'], styles['errorInput'], {\n [withVendorCSSClassPrefix(bem('checkbox', 'input', 'error'))]: hasError,\n })}\n aria-invalid={hasError}\n aria-required={required}\n {...rest}\n id={inputId}\n aria-describedby={describedBy}\n />\n {label && (\n <InputLabel\n htmlFor={inputId}\n required={required}\n error={hasError}\n variant=\"inline\"\n className={cx(withVendorCSSClassPrefix(bem('checkbox', 'label')), styles['label'], styles['errorLabel'], {\n [withVendorCSSClassPrefix(bem('checkbox', 'label', 'error'))]: hasError,\n })}\n >\n {label}\n </InputLabel>\n )}\n </div>\n </FormControl>\n );\n};\n\nexport default Checkbox;\n"],"mappings":";;;;;;;;;;;;;;AAkCA,MAAMA,YAA+B,EACnC,OACA,OACA,WACA,UACA,YACA,QAAQ,EAAE,CACV,GAAG,WACgB;CACnB,MAAM,EAAC,OAAO,gBAA4CC,0BAAU;CACpE,MAAM,WAAW,CAAC,CAAC;CACnB,MAAMC,gCAA6B;CACnC,MAAMC,UAAkB,KAAK,MAAM;CACnC,MAAMC,eAAmC,SAAS,aAAa,GAAG,QAAQ,gBAAgB;CAC1F,MAAMC,cACJ,CAAC,KAAK,qBAAqB,aAAa,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI,IAAI;CACxE,MAAMC,SAAiCC,gCAAU,OAAO,aAAa,UAAU,CAAC,CAAC,SAAS;AAE1F,QACE,2CAACC;EACQ;EACK;EACE;EACd,WAAWC,kGAAgC,WAAW,CAAC,EAAE,UAAU;EACnE,sBAAsB,QAAQ,MAAM,KAAK,QAAQ,KAAK;YAEtD,4CAAC;GAAW;GAAO,WAAWA,kGAAgC,YAAY,YAAY,CAAC,EAAE,OAAO,aAAa;cAC3G,2CAAC;IACC,MAAK;IACL,WAAWA,kGAAgC,YAAY,QAAQ,CAAC,EAAE,OAAO,UAAU,OAAO,eAAe,kFACzE,YAAY,SAAS,QAAQ,CAAC,GAAG,UAChE,CAAC;IACF,gBAAc;IACd,iBAAe;IACf,GAAI;IACJ,IAAI;IACJ,oBAAkB;KAClB,EACD,SACC,2CAACC;IACC,SAAS;IACC;IACV,OAAO;IACP,SAAQ;IACR,WAAWD,kGAAgC,YAAY,QAAQ,CAAC,EAAE,OAAO,UAAU,OAAO,eAAe,kFACzE,YAAY,SAAS,QAAQ,CAAC,GAAG,UAChE,CAAC;cAED;KACU;IAEX;GACM;;AAIlB,uBAAe"}
|
|
@@ -6,6 +6,8 @@ const require_InputLabel = require('../InputLabel/InputLabel.cjs');
|
|
|
6
6
|
const require_DatePicker_styles = require('./DatePicker.styles.cjs');
|
|
7
7
|
let __thunderid_browser = require("@thunderid/browser");
|
|
8
8
|
__thunderid_browser = require_rolldown_runtime.__toESM(__thunderid_browser);
|
|
9
|
+
let react = require("react");
|
|
10
|
+
react = require_rolldown_runtime.__toESM(react);
|
|
9
11
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
12
|
react_jsx_runtime = require_rolldown_runtime.__toESM(react_jsx_runtime);
|
|
11
13
|
|
|
@@ -13,13 +15,19 @@ react_jsx_runtime = require_rolldown_runtime.__toESM(react_jsx_runtime);
|
|
|
13
15
|
const DatePicker = ({ label, error, className, required, disabled, helperText, dateFormat = "yyyy-MM-dd", style = {},...rest }) => {
|
|
14
16
|
const { theme, colorScheme } = require_useTheme.default();
|
|
15
17
|
const hasError = !!error;
|
|
18
|
+
const generatedId = (0, react.useId)();
|
|
19
|
+
const inputId = rest.id ?? generatedId;
|
|
20
|
+
const helperTextId = error || helperText ? `${inputId}-helper-text` : void 0;
|
|
21
|
+
const describedBy = [rest["aria-describedby"], helperTextId].filter(Boolean).join(" ") || void 0;
|
|
16
22
|
const styles = require_DatePicker_styles.default(theme, colorScheme, hasError, !!disabled);
|
|
17
23
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_FormControl.default, {
|
|
18
24
|
error,
|
|
19
25
|
helperText,
|
|
26
|
+
helperTextId,
|
|
20
27
|
className: require_emotion.cx((0, __thunderid_browser.withVendorCSSClassPrefix)((0, __thunderid_browser.bem)("date-picker")), className),
|
|
21
28
|
style,
|
|
22
29
|
children: [label && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_InputLabel.default, {
|
|
30
|
+
htmlFor: inputId,
|
|
23
31
|
required,
|
|
24
32
|
error: hasError,
|
|
25
33
|
className: require_emotion.cx((0, __thunderid_browser.withVendorCSSClassPrefix)((0, __thunderid_browser.bem)("date-picker", "label")), styles["label"]),
|
|
@@ -35,7 +43,9 @@ const DatePicker = ({ label, error, className, required, disabled, helperText, d
|
|
|
35
43
|
disabled,
|
|
36
44
|
"aria-invalid": hasError,
|
|
37
45
|
"aria-required": required,
|
|
38
|
-
...rest
|
|
46
|
+
...rest,
|
|
47
|
+
id: inputId,
|
|
48
|
+
"aria-describedby": describedBy
|
|
39
49
|
})]
|
|
40
50
|
});
|
|
41
51
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DatePicker.cjs","names":["DatePicker: FC<DatePickerProps>","useTheme","styles: Record<string, string>","useStyles","FormControl","cx","InputLabel"],"sources":["../../../../../src/components/primitives/DatePicker/DatePicker.tsx"],"sourcesContent":["// Copyright 2024 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {withVendorCSSClassPrefix, bem} from '@thunderid/browser';\nimport {FC, InputHTMLAttributes} from 'react';\nimport useStyles from './DatePicker.styles';\nimport useTheme from '../../../contexts/Theme/useTheme';\nimport {cx} from '../../../styles/emotion';\nimport FormControl from '../FormControl/FormControl';\nimport InputLabel from '../InputLabel/InputLabel';\n\nexport interface DatePickerProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'className' | 'type'> {\n /**\n * Additional CSS class names\n */\n className?: string;\n /**\n * Custom date format for the regex pattern\n */\n dateFormat?: string;\n /**\n * Whether the field is disabled\n */\n disabled?: boolean;\n /**\n * Error message to display below the input\n */\n error?: string;\n /**\n * Helper text to display below the input\n */\n helperText?: string;\n /**\n * Label text to display above the input\n */\n label?: string;\n /**\n * Whether the field is required\n */\n required?: boolean;\n}\n\nconst DatePicker: FC<DatePickerProps> = ({\n label,\n error,\n className,\n required,\n disabled,\n helperText,\n dateFormat = 'yyyy-MM-dd',\n style = {},\n ...rest\n}: DatePickerProps) => {\n const {theme, colorScheme}: ReturnType<typeof useTheme> = useTheme();\n const hasError = !!error;\n const styles: Record<string, string> = useStyles(theme, colorScheme, hasError, !!disabled);\n\n return (\n <FormControl\n error={error}\n helperText={helperText}\n className={cx(withVendorCSSClassPrefix(bem('date-picker')), className)}\n style={style}\n >\n {label && (\n <InputLabel\n required={required}\n error={hasError}\n className={cx(withVendorCSSClassPrefix(bem('date-picker', 'label')), styles['label'])}\n >\n {label}\n </InputLabel>\n )}\n <input\n type=\"date\"\n pattern=\"\\d{4}-\\d{2}-\\d{2}\"\n placeholder={dateFormat}\n className={cx(\n withVendorCSSClassPrefix(bem('date-picker', 'input')),\n styles['input'],\n styles['errorInput'],\n styles['disabledInput'],\n {\n [withVendorCSSClassPrefix(bem('date-picker', 'input', 'error'))]: hasError,\n [withVendorCSSClassPrefix(bem('date-picker', 'input', 'disabled'))]: disabled,\n },\n )}\n disabled={disabled}\n aria-invalid={hasError}\n aria-required={required}\n {...rest}\n />\n </FormControl>\n );\n};\n\nexport default DatePicker;\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"DatePicker.cjs","names":["DatePicker: FC<DatePickerProps>","useTheme","generatedId: string","inputId: string","helperTextId: string | undefined","describedBy: string | undefined","styles: Record<string, string>","useStyles","FormControl","cx","InputLabel"],"sources":["../../../../../src/components/primitives/DatePicker/DatePicker.tsx"],"sourcesContent":["// Copyright 2024 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {withVendorCSSClassPrefix, bem} from '@thunderid/browser';\nimport {FC, InputHTMLAttributes, useId} from 'react';\nimport useStyles from './DatePicker.styles';\nimport useTheme from '../../../contexts/Theme/useTheme';\nimport {cx} from '../../../styles/emotion';\nimport FormControl from '../FormControl/FormControl';\nimport InputLabel from '../InputLabel/InputLabel';\n\nexport interface DatePickerProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'className' | 'type'> {\n /**\n * Additional CSS class names\n */\n className?: string;\n /**\n * Custom date format for the regex pattern\n */\n dateFormat?: string;\n /**\n * Whether the field is disabled\n */\n disabled?: boolean;\n /**\n * Error message to display below the input\n */\n error?: string;\n /**\n * Helper text to display below the input\n */\n helperText?: string;\n /**\n * Label text to display above the input\n */\n label?: string;\n /**\n * Whether the field is required\n */\n required?: boolean;\n}\n\nconst DatePicker: FC<DatePickerProps> = ({\n label,\n error,\n className,\n required,\n disabled,\n helperText,\n dateFormat = 'yyyy-MM-dd',\n style = {},\n ...rest\n}: DatePickerProps) => {\n const {theme, colorScheme}: ReturnType<typeof useTheme> = useTheme();\n const hasError = !!error;\n const generatedId: string = useId();\n const inputId: string = rest.id ?? generatedId;\n const helperTextId: string | undefined = error || helperText ? `${inputId}-helper-text` : undefined;\n const describedBy: string | undefined =\n [rest['aria-describedby'], helperTextId].filter(Boolean).join(' ') || undefined;\n const styles: Record<string, string> = useStyles(theme, colorScheme, hasError, !!disabled);\n\n return (\n <FormControl\n error={error}\n helperText={helperText}\n helperTextId={helperTextId}\n className={cx(withVendorCSSClassPrefix(bem('date-picker')), className)}\n style={style}\n >\n {label && (\n <InputLabel\n htmlFor={inputId}\n required={required}\n error={hasError}\n className={cx(withVendorCSSClassPrefix(bem('date-picker', 'label')), styles['label'])}\n >\n {label}\n </InputLabel>\n )}\n <input\n type=\"date\"\n pattern=\"\\d{4}-\\d{2}-\\d{2}\"\n placeholder={dateFormat}\n className={cx(\n withVendorCSSClassPrefix(bem('date-picker', 'input')),\n styles['input'],\n styles['errorInput'],\n styles['disabledInput'],\n {\n [withVendorCSSClassPrefix(bem('date-picker', 'input', 'error'))]: hasError,\n [withVendorCSSClassPrefix(bem('date-picker', 'input', 'disabled'))]: disabled,\n },\n )}\n disabled={disabled}\n aria-invalid={hasError}\n aria-required={required}\n {...rest}\n id={inputId}\n aria-describedby={describedBy}\n />\n </FormControl>\n );\n};\n\nexport default DatePicker;\n"],"mappings":";;;;;;;;;;;;;;AA0CA,MAAMA,cAAmC,EACvC,OACA,OACA,WACA,UACA,UACA,YACA,aAAa,cACb,QAAQ,EAAE,CACV,GAAG,WACkB;CACrB,MAAM,EAAC,OAAO,gBAA4CC,0BAAU;CACpE,MAAM,WAAW,CAAC,CAAC;CACnB,MAAMC,gCAA6B;CACnC,MAAMC,UAAkB,KAAK,MAAM;CACnC,MAAMC,eAAmC,SAAS,aAAa,GAAG,QAAQ,gBAAgB;CAC1F,MAAMC,cACJ,CAAC,KAAK,qBAAqB,aAAa,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI,IAAI;CACxE,MAAMC,SAAiCC,kCAAU,OAAO,aAAa,UAAU,CAAC,CAAC,SAAS;AAE1F,QACE,4CAACC;EACQ;EACK;EACE;EACd,WAAWC,kGAAgC,cAAc,CAAC,EAAE,UAAU;EAC/D;aAEN,SACC,2CAACC;GACC,SAAS;GACC;GACV,OAAO;GACP,WAAWD,kGAAgC,eAAe,QAAQ,CAAC,EAAE,OAAO,SAAS;aAEpF;IACU,EAEf,2CAAC;GACC,MAAK;GACL,SAAQ;GACR,aAAa;GACb,WAAWA,kGACoB,eAAe,QAAQ,CAAC,EACrD,OAAO,UACP,OAAO,eACP,OAAO,kBACP;oFACgC,eAAe,SAAS,QAAQ,CAAC,GAAG;oFACpC,eAAe,SAAS,WAAW,CAAC,GAAG;IACtE,CACF;GACS;GACV,gBAAc;GACd,iBAAe;GACf,GAAI;GACJ,IAAI;GACJ,oBAAkB;IAClB;GACU;;AAIlB,yBAAe"}
|
|
@@ -9,13 +9,14 @@ let react_jsx_runtime = require("react/jsx-runtime");
|
|
|
9
9
|
react_jsx_runtime = require_rolldown_runtime.__toESM(react_jsx_runtime);
|
|
10
10
|
|
|
11
11
|
//#region src/components/primitives/FormControl/FormControl.tsx
|
|
12
|
-
const FormControl = ({ children, error, helperText, className, id, helperTextAlign = "left", helperTextMarginLeft }) => {
|
|
12
|
+
const FormControl = ({ children, error, helperText, helperTextId, className, id, helperTextAlign = "left", helperTextMarginLeft }) => {
|
|
13
13
|
const { theme, colorScheme } = require_useTheme.default();
|
|
14
14
|
const styles = require_FormControl_styles.default(theme, colorScheme, helperTextAlign, helperTextMarginLeft, !!error);
|
|
15
15
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
16
16
|
id,
|
|
17
17
|
className: require_emotion.cx((0, __thunderid_browser.withVendorCSSClassPrefix)((0, __thunderid_browser.bem)("form-control")), styles["formControl"], className),
|
|
18
18
|
children: [children, (error || helperText) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Typography.default, {
|
|
19
|
+
id: helperTextId,
|
|
19
20
|
variant: "caption",
|
|
20
21
|
color: error ? "error" : "textSecondary",
|
|
21
22
|
className: require_emotion.cx((0, __thunderid_browser.withVendorCSSClassPrefix)((0, __thunderid_browser.bem)("form-control", "helper-text")), styles["helperText"], {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormControl.cjs","names":["FormControl: FC<FormControlProps>","useTheme","styles: Record<string, string>","useStyles","cx","Typography"],"sources":["../../../../../src/components/primitives/FormControl/FormControl.tsx"],"sourcesContent":["// Copyright 2025 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {bem, withVendorCSSClassPrefix} from '@thunderid/browser';\nimport {CSSProperties, FC, ReactNode} from 'react';\nimport useStyles from './FormControl.styles';\nimport useTheme from '../../../contexts/Theme/useTheme';\nimport {cx} from '../../../styles/emotion';\nimport Typography from '../Typography/Typography';\n\nexport type FormControlHelperTextAlign = 'left' | 'center';\n\nexport interface FormControlProps {\n /**\n * The content to be wrapped by the form control\n */\n children: ReactNode;\n /**\n * Additional CSS class names\n */\n className?: string;\n /**\n * Error message to display below the content\n */\n error?: string;\n /**\n * Helper text to display below the content\n */\n helperText?: string;\n /**\n * Custom alignment for helper text (default: left, center for OTP)\n */\n helperTextAlign?: FormControlHelperTextAlign;\n /**\n * Custom margin left for helper text (for components like Checkbox)\n */\n helperTextMarginLeft?: string;\n /**\n * HTML id attribute\n */\n id?: string;\n /**\n * Custom container style\n */\n style?: CSSProperties;\n}\n\nconst FormControl: FC<FormControlProps> = ({\n children,\n error,\n helperText,\n className,\n id,\n helperTextAlign = 'left',\n helperTextMarginLeft,\n}: FormControlProps) => {\n const {theme, colorScheme}: ReturnType<typeof useTheme> = useTheme();\n const styles: Record<string, string> = useStyles(theme, colorScheme, helperTextAlign, helperTextMarginLeft, !!error);\n\n return (\n <div id={id} className={cx(withVendorCSSClassPrefix(bem('form-control')), styles['formControl'], className)}>\n {children}\n {(error || helperText) && (\n <Typography\n variant=\"caption\"\n color={error ? 'error' : 'textSecondary'}\n className={cx(withVendorCSSClassPrefix(bem('form-control', 'helper-text')), styles['helperText'], {\n [withVendorCSSClassPrefix(bem('form-control', 'helper-text', 'error'))]: !!error,\n [styles['helperTextError']]: !!error,\n })}\n >\n {error || helperText}\n </Typography>\n )}\n </div>\n );\n};\n\nexport default FormControl;\n"],"mappings":";;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"FormControl.cjs","names":["FormControl: FC<FormControlProps>","useTheme","styles: Record<string, string>","useStyles","cx","Typography"],"sources":["../../../../../src/components/primitives/FormControl/FormControl.tsx"],"sourcesContent":["// Copyright 2025 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {bem, withVendorCSSClassPrefix} from '@thunderid/browser';\nimport {CSSProperties, FC, ReactNode} from 'react';\nimport useStyles from './FormControl.styles';\nimport useTheme from '../../../contexts/Theme/useTheme';\nimport {cx} from '../../../styles/emotion';\nimport Typography from '../Typography/Typography';\n\nexport type FormControlHelperTextAlign = 'left' | 'center';\n\nexport interface FormControlProps {\n /**\n * The content to be wrapped by the form control\n */\n children: ReactNode;\n /**\n * Additional CSS class names\n */\n className?: string;\n /**\n * Error message to display below the content\n */\n error?: string;\n /**\n * Helper text to display below the content\n */\n helperText?: string;\n /**\n * id of the helper text element, so the field it describes can reference it with `aria-describedby`\n */\n helperTextId?: string;\n /**\n * Custom alignment for helper text (default: left, center for OTP)\n */\n helperTextAlign?: FormControlHelperTextAlign;\n /**\n * Custom margin left for helper text (for components like Checkbox)\n */\n helperTextMarginLeft?: string;\n /**\n * HTML id attribute\n */\n id?: string;\n /**\n * Custom container style\n */\n style?: CSSProperties;\n}\n\nconst FormControl: FC<FormControlProps> = ({\n children,\n error,\n helperText,\n helperTextId,\n className,\n id,\n helperTextAlign = 'left',\n helperTextMarginLeft,\n}: FormControlProps) => {\n const {theme, colorScheme}: ReturnType<typeof useTheme> = useTheme();\n const styles: Record<string, string> = useStyles(theme, colorScheme, helperTextAlign, helperTextMarginLeft, !!error);\n\n return (\n <div id={id} className={cx(withVendorCSSClassPrefix(bem('form-control')), styles['formControl'], className)}>\n {children}\n {(error || helperText) && (\n <Typography\n id={helperTextId}\n variant=\"caption\"\n color={error ? 'error' : 'textSecondary'}\n className={cx(withVendorCSSClassPrefix(bem('form-control', 'helper-text')), styles['helperText'], {\n [withVendorCSSClassPrefix(bem('form-control', 'helper-text', 'error'))]: !!error,\n [styles['helperTextError']]: !!error,\n })}\n >\n {error || helperText}\n </Typography>\n )}\n </div>\n );\n};\n\nexport default FormControl;\n"],"mappings":";;;;;;;;;;;AAmDA,MAAMA,eAAqC,EACzC,UACA,OACA,YACA,cACA,WACA,IACA,kBAAkB,QAClB,2BACsB;CACtB,MAAM,EAAC,OAAO,gBAA4CC,0BAAU;CACpE,MAAMC,SAAiCC,mCAAU,OAAO,aAAa,iBAAiB,sBAAsB,CAAC,CAAC,MAAM;AAEpH,QACE,4CAAC;EAAQ;EAAI,WAAWC,kGAAgC,eAAe,CAAC,EAAE,OAAO,gBAAgB,UAAU;aACxG,WACC,SAAS,eACT,2CAACC;GACC,IAAI;GACJ,SAAQ;GACR,OAAO,QAAQ,UAAU;GACzB,WAAWD,kGAAgC,gBAAgB,cAAc,CAAC,EAAE,OAAO,eAAe;oFAClE,gBAAgB,eAAe,QAAQ,CAAC,GAAG,CAAC,CAAC;KAC1E,OAAO,qBAAqB,CAAC,CAAC;IAChC,CAAC;aAED,SAAS;IACC;GAEX;;AAIV,0BAAe"}
|
|
@@ -17,6 +17,8 @@ react_jsx_runtime = require_rolldown_runtime.__toESM(react_jsx_runtime);
|
|
|
17
17
|
//#region src/components/primitives/MultiInput/MultiInput.tsx
|
|
18
18
|
const MultiInput = ({ label, error, required, className, disabled, helperText, placeholder = "Enter value", values = [], onChange, type = "text", fieldType = "STRING", startIcon, endIcon, minFields = 1, maxFields, style = {} }) => {
|
|
19
19
|
const { theme, colorScheme } = require_useTheme.default();
|
|
20
|
+
const inputId = (0, react.useId)();
|
|
21
|
+
const helperTextId = error || helperText ? `${inputId}-description` : void 0;
|
|
20
22
|
const canAddMore = !maxFields || values.length < maxFields;
|
|
21
23
|
const canRemove = values.length > minFields;
|
|
22
24
|
const styles = require_MultiInput_styles.default(theme, colorScheme, !!disabled, !!error, canAddMore, canRemove);
|
|
@@ -58,10 +60,13 @@ const MultiInput = ({ label, error, required, className, disabled, helperText, p
|
|
|
58
60
|
onEndIconClick();
|
|
59
61
|
}
|
|
60
62
|
};
|
|
63
|
+
const finalEndIcon = attachedEndIcon || endIcon;
|
|
61
64
|
const commonProps = {
|
|
65
|
+
"aria-describedby": helperTextId,
|
|
62
66
|
disabled,
|
|
63
|
-
endIcon:
|
|
67
|
+
endIcon: finalEndIcon,
|
|
64
68
|
error,
|
|
69
|
+
id: inputId,
|
|
65
70
|
onChange: handleInputChange,
|
|
66
71
|
onEndIconClick,
|
|
67
72
|
onKeyDown: handleKeyDown,
|
|
@@ -88,7 +93,9 @@ const MultiInput = ({ label, error, required, className, disabled, helperText, p
|
|
|
88
93
|
endIcon,
|
|
89
94
|
error,
|
|
90
95
|
fieldType,
|
|
91
|
-
type
|
|
96
|
+
type,
|
|
97
|
+
inputId,
|
|
98
|
+
helperTextId
|
|
92
99
|
]);
|
|
93
100
|
const [currentInputValue, setCurrentInputValue] = (0, react.useState)("");
|
|
94
101
|
const handleInputSubmit = (0, react.useCallback)(() => {
|
|
@@ -100,9 +107,11 @@ const MultiInput = ({ label, error, required, className, disabled, helperText, p
|
|
|
100
107
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_FormControl.default, {
|
|
101
108
|
error,
|
|
102
109
|
helperText,
|
|
110
|
+
helperTextId,
|
|
103
111
|
className: require_emotion.cx((0, __thunderid_browser.withVendorCSSClassPrefix)((0, __thunderid_browser.bem)("multi-input")), className),
|
|
104
112
|
style,
|
|
105
113
|
children: [label && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_InputLabel.default, {
|
|
114
|
+
htmlFor: inputId,
|
|
106
115
|
required,
|
|
107
116
|
error: !!error,
|
|
108
117
|
children: label
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MultiInput.cjs","names":["MultiInput: FC<MultiInputProps>","useTheme","canAddMore: boolean","canRemove: boolean","styles: Record<string, string>","useStyles","PlusIcon: FC<IconProps>","cx","BinIcon: FC<IconProps>","handleAddValue: ReturnType<typeof useCallback>","handleRemoveValue: ReturnType<typeof useCallback>","renderInputField: ReturnType<typeof useCallback>","commonProps: Record<string, unknown>","DatePicker","Checkbox","TextField","handleInputSubmit: ReturnType<typeof useCallback>","FormControl","InputLabel"],"sources":["../../../../../src/components/primitives/MultiInput/MultiInput.tsx"],"sourcesContent":["// Copyright 2025 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {withVendorCSSClassPrefix, bem} from '@thunderid/browser';\nimport {CSSProperties, FC, KeyboardEvent, ReactElement, ReactNode, useCallback, useState} from 'react';\nimport useStyles from './MultiInput.styles';\nimport useTheme from '../../../contexts/Theme/useTheme';\nimport {cx} from '../../../styles/emotion';\nimport Checkbox from '../Checkbox/Checkbox';\nimport DatePicker from '../DatePicker/DatePicker';\nimport FormControl from '../FormControl/FormControl';\nimport InputLabel from '../InputLabel/InputLabel';\nimport TextField from '../TextField/TextField';\n\nexport type MultiInputType = 'text' | 'email' | 'tel' | 'url' | 'password' | 'date' | 'boolean';\nexport type MultiInputFieldType = 'STRING' | 'DATE_TIME' | 'BOOLEAN';\n\nexport interface MultiInputProps {\n /**\n * Additional CSS class names\n */\n className?: string;\n /**\n * Whether the field is disabled\n */\n disabled?: boolean;\n /**\n * Icon to display at the end (right) of each input (in addition to add/remove buttons)\n */\n endIcon?: ReactNode;\n /**\n * Error message to display below the inputs\n */\n error?: string;\n /**\n * Field type for different input components\n */\n fieldType?: MultiInputFieldType;\n /**\n * Helper text to display below the inputs\n */\n helperText?: string;\n /**\n * Label text to display above the inputs\n */\n label?: string;\n /**\n * Maximum number of fields to allow (default: unlimited)\n */\n maxFields?: number;\n /**\n * Minimum number of fields to show (default: 1)\n */\n minFields?: number;\n /**\n * Callback when values change\n */\n onChange: (values: string[]) => void;\n /**\n * Placeholder text for input fields\n */\n placeholder?: string;\n /**\n * Whether the field is required\n */\n required?: boolean;\n /**\n * Icon to display at the start (left) of each input\n */\n startIcon?: ReactNode;\n /**\n * Custom style object\n */\n style?: CSSProperties;\n /**\n * Input type\n */\n type?: MultiInputType;\n /**\n * Array of values\n */\n values: string[];\n}\n\ninterface IconProps {\n iconClassName?: string;\n}\n\nconst MultiInput: FC<MultiInputProps> = ({\n label,\n error,\n required,\n className,\n disabled,\n helperText,\n placeholder = 'Enter value',\n values = [],\n onChange,\n type = 'text',\n fieldType = 'STRING',\n startIcon,\n endIcon,\n minFields = 1,\n maxFields,\n style = {},\n}: MultiInputProps) => {\n const {theme, colorScheme}: ReturnType<typeof useTheme> = useTheme();\n const canAddMore: boolean = !maxFields || values.length < maxFields;\n const canRemove: boolean = values.length > minFields;\n const styles: Record<string, string> = useStyles(theme, colorScheme, !!disabled, !!error, canAddMore, canRemove);\n\n const PlusIcon: FC<IconProps> = ({iconClassName}: IconProps): ReactElement => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" className={cx(styles['icon'], iconClassName)}>\n <path d=\"M12 5v14M5 12h14\" />\n </svg>\n );\n\n const BinIcon: FC<IconProps> = ({iconClassName}: IconProps): ReactElement => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" className={cx(styles['icon'], iconClassName)}>\n <path d=\"M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6h14ZM10 11v6M14 11v6\" />\n </svg>\n );\n\n const handleAddValue: ReturnType<typeof useCallback> = useCallback(\n (newValue: string) => {\n if (newValue.trim() !== '' && (!maxFields || values.length < maxFields)) {\n onChange([...values, newValue.trim()]);\n }\n },\n [values, onChange, maxFields],\n );\n\n const handleRemoveValue: ReturnType<typeof useCallback> = useCallback(\n (index: number) => {\n if (values.length > minFields) {\n const updatedValues: string[] = values.filter((_: string, i: number) => i !== index);\n onChange(updatedValues);\n }\n },\n [values, onChange, minFields],\n );\n\n const renderInputField: ReturnType<typeof useCallback> = useCallback(\n (\n value: string,\n onValueChange: (value: string) => void,\n attachedEndIcon?: ReactNode,\n onEndIconClick?: () => void,\n ): ReactElement => {\n const handleInputChange = (e: any): void => {\n const newValue: string = e.target ? e.target.value : e;\n onValueChange(newValue);\n };\n\n const handleKeyDown = (e: KeyboardEvent): void => {\n if (e.key === 'Enter' && onEndIconClick) {\n e.preventDefault();\n onEndIconClick();\n }\n };\n\n const finalEndIcon: ReactNode = attachedEndIcon || endIcon;\n\n const commonProps: Record<string, unknown> = {\n disabled,\n endIcon: finalEndIcon,\n error,\n onChange: handleInputChange,\n onEndIconClick,\n onKeyDown: handleKeyDown,\n placeholder,\n startIcon,\n value,\n };\n\n switch (fieldType) {\n case 'DATE_TIME':\n return <DatePicker {...commonProps} />;\n case 'BOOLEAN':\n return (\n <Checkbox\n {...commonProps}\n checked={value === 'true' || Boolean(value)}\n onChange={(e: any): void => onValueChange(e.target.checked ? 'true' : 'false')}\n />\n );\n default:\n return <TextField {...commonProps} type={type} />;\n }\n },\n [placeholder, disabled, startIcon, endIcon, error, fieldType, type],\n );\n\n // State for the current input value\n const [currentInputValue, setCurrentInputValue] = useState('');\n\n const handleInputSubmit: ReturnType<typeof useCallback> = useCallback((): void => {\n if (currentInputValue.trim() !== '') {\n handleAddValue(currentInputValue);\n setCurrentInputValue('');\n }\n }, [currentInputValue, handleAddValue]);\n\n return (\n <FormControl\n error={error}\n helperText={helperText}\n className={cx(withVendorCSSClassPrefix(bem('multi-input')), className)}\n style={style}\n >\n {label && (\n <InputLabel required={required} error={!!error}>\n {label}\n </InputLabel>\n )}\n <div className={cx(withVendorCSSClassPrefix(bem('multi-input', 'container')), styles['container'])}>\n {/* Input field at the top */}\n <div className={cx(withVendorCSSClassPrefix(bem('multi-input', 'input-row')), styles['inputRow'])}>\n <div className={cx(withVendorCSSClassPrefix(bem('multi-input', 'input-wrapper')), styles['inputWrapper'])}>\n {renderInputField(\n currentInputValue,\n setCurrentInputValue,\n canAddMore ? <PlusIcon iconClassName={styles['plusIcon']} /> : undefined,\n canAddMore ? handleInputSubmit : undefined,\n )}\n </div>\n </div>\n\n {/* List of added items */}\n {values.length > 0 && (\n <div className={cx(withVendorCSSClassPrefix(bem('multi-input', 'list-container')), styles['listContainer'])}>\n {values.map((value: string, index: number) => (\n <div\n key={index}\n className={cx(withVendorCSSClassPrefix(bem('multi-input', 'list-item')), styles['listItem'])}\n >\n <span\n className={cx(withVendorCSSClassPrefix(bem('multi-input', 'list-item-text')), styles['listItemText'])}\n >\n {value}\n </span>\n {canRemove && (\n <button\n type=\"button\"\n onClick={(): void => handleRemoveValue(index)}\n disabled={disabled}\n className={cx(\n withVendorCSSClassPrefix(bem('multi-input', 'remove-button')),\n styles['removeButton'],\n )}\n title=\"Remove value\"\n >\n <BinIcon iconClassName={styles['icon']} />\n </button>\n )}\n </div>\n ))}\n </div>\n )}\n </div>\n </FormControl>\n );\n};\n\nexport default MultiInput;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MultiInput.cjs","names":["MultiInput: FC<MultiInputProps>","useTheme","inputId: string","helperTextId: string | undefined","canAddMore: boolean","canRemove: boolean","styles: Record<string, string>","useStyles","PlusIcon: FC<IconProps>","cx","BinIcon: FC<IconProps>","handleAddValue: ReturnType<typeof useCallback>","handleRemoveValue: ReturnType<typeof useCallback>","renderInputField: ReturnType<typeof useCallback>","finalEndIcon: ReactNode","commonProps: Record<string, unknown>","DatePicker","Checkbox","TextField","handleInputSubmit: ReturnType<typeof useCallback>","FormControl","InputLabel"],"sources":["../../../../../src/components/primitives/MultiInput/MultiInput.tsx"],"sourcesContent":["// Copyright 2025 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {withVendorCSSClassPrefix, bem} from '@thunderid/browser';\nimport {CSSProperties, FC, KeyboardEvent, ReactElement, ReactNode, useCallback, useId, useState} from 'react';\nimport useStyles from './MultiInput.styles';\nimport useTheme from '../../../contexts/Theme/useTheme';\nimport {cx} from '../../../styles/emotion';\nimport Checkbox from '../Checkbox/Checkbox';\nimport DatePicker from '../DatePicker/DatePicker';\nimport FormControl from '../FormControl/FormControl';\nimport InputLabel from '../InputLabel/InputLabel';\nimport TextField from '../TextField/TextField';\n\nexport type MultiInputType = 'text' | 'email' | 'tel' | 'url' | 'password' | 'date' | 'boolean';\nexport type MultiInputFieldType = 'STRING' | 'DATE_TIME' | 'BOOLEAN';\n\nexport interface MultiInputProps {\n /**\n * Additional CSS class names\n */\n className?: string;\n /**\n * Whether the field is disabled\n */\n disabled?: boolean;\n /**\n * Icon to display at the end (right) of each input (in addition to add/remove buttons)\n */\n endIcon?: ReactNode;\n /**\n * Error message to display below the inputs\n */\n error?: string;\n /**\n * Field type for different input components\n */\n fieldType?: MultiInputFieldType;\n /**\n * Helper text to display below the inputs\n */\n helperText?: string;\n /**\n * Label text to display above the inputs\n */\n label?: string;\n /**\n * Maximum number of fields to allow (default: unlimited)\n */\n maxFields?: number;\n /**\n * Minimum number of fields to show (default: 1)\n */\n minFields?: number;\n /**\n * Callback when values change\n */\n onChange: (values: string[]) => void;\n /**\n * Placeholder text for input fields\n */\n placeholder?: string;\n /**\n * Whether the field is required\n */\n required?: boolean;\n /**\n * Icon to display at the start (left) of each input\n */\n startIcon?: ReactNode;\n /**\n * Custom style object\n */\n style?: CSSProperties;\n /**\n * Input type\n */\n type?: MultiInputType;\n /**\n * Array of values\n */\n values: string[];\n}\n\ninterface IconProps {\n iconClassName?: string;\n}\n\nconst MultiInput: FC<MultiInputProps> = ({\n label,\n error,\n required,\n className,\n disabled,\n helperText,\n placeholder = 'Enter value',\n values = [],\n onChange,\n type = 'text',\n fieldType = 'STRING',\n startIcon,\n endIcon,\n minFields = 1,\n maxFields,\n style = {},\n}: MultiInputProps) => {\n const {theme, colorScheme}: ReturnType<typeof useTheme> = useTheme();\n const inputId: string = useId();\n // The inner field renders its own helper text with `${inputId}-helper-text`, so this one needs a distinct id.\n const helperTextId: string | undefined = error || helperText ? `${inputId}-description` : undefined;\n const canAddMore: boolean = !maxFields || values.length < maxFields;\n const canRemove: boolean = values.length > minFields;\n const styles: Record<string, string> = useStyles(theme, colorScheme, !!disabled, !!error, canAddMore, canRemove);\n\n const PlusIcon: FC<IconProps> = ({iconClassName}: IconProps): ReactElement => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" className={cx(styles['icon'], iconClassName)}>\n <path d=\"M12 5v14M5 12h14\" />\n </svg>\n );\n\n const BinIcon: FC<IconProps> = ({iconClassName}: IconProps): ReactElement => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" className={cx(styles['icon'], iconClassName)}>\n <path d=\"M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6h14ZM10 11v6M14 11v6\" />\n </svg>\n );\n\n const handleAddValue: ReturnType<typeof useCallback> = useCallback(\n (newValue: string) => {\n if (newValue.trim() !== '' && (!maxFields || values.length < maxFields)) {\n onChange([...values, newValue.trim()]);\n }\n },\n [values, onChange, maxFields],\n );\n\n const handleRemoveValue: ReturnType<typeof useCallback> = useCallback(\n (index: number) => {\n if (values.length > minFields) {\n const updatedValues: string[] = values.filter((_: string, i: number) => i !== index);\n onChange(updatedValues);\n }\n },\n [values, onChange, minFields],\n );\n\n const renderInputField: ReturnType<typeof useCallback> = useCallback(\n (\n value: string,\n onValueChange: (value: string) => void,\n attachedEndIcon?: ReactNode,\n onEndIconClick?: () => void,\n ): ReactElement => {\n const handleInputChange = (e: any): void => {\n const newValue: string = e.target ? e.target.value : e;\n onValueChange(newValue);\n };\n\n const handleKeyDown = (e: KeyboardEvent): void => {\n if (e.key === 'Enter' && onEndIconClick) {\n e.preventDefault();\n onEndIconClick();\n }\n };\n\n const finalEndIcon: ReactNode = attachedEndIcon || endIcon;\n\n const commonProps: Record<string, unknown> = {\n 'aria-describedby': helperTextId,\n disabled,\n endIcon: finalEndIcon,\n error,\n id: inputId,\n onChange: handleInputChange,\n onEndIconClick,\n onKeyDown: handleKeyDown,\n placeholder,\n startIcon,\n value,\n };\n\n switch (fieldType) {\n case 'DATE_TIME':\n return <DatePicker {...commonProps} />;\n case 'BOOLEAN':\n return (\n <Checkbox\n {...commonProps}\n checked={value === 'true' || Boolean(value)}\n onChange={(e: any): void => onValueChange(e.target.checked ? 'true' : 'false')}\n />\n );\n default:\n return <TextField {...commonProps} type={type} />;\n }\n },\n [placeholder, disabled, startIcon, endIcon, error, fieldType, type, inputId, helperTextId],\n );\n\n // State for the current input value\n const [currentInputValue, setCurrentInputValue] = useState('');\n\n const handleInputSubmit: ReturnType<typeof useCallback> = useCallback((): void => {\n if (currentInputValue.trim() !== '') {\n handleAddValue(currentInputValue);\n setCurrentInputValue('');\n }\n }, [currentInputValue, handleAddValue]);\n\n return (\n <FormControl\n error={error}\n helperText={helperText}\n helperTextId={helperTextId}\n className={cx(withVendorCSSClassPrefix(bem('multi-input')), className)}\n style={style}\n >\n {label && (\n <InputLabel htmlFor={inputId} required={required} error={!!error}>\n {label}\n </InputLabel>\n )}\n <div className={cx(withVendorCSSClassPrefix(bem('multi-input', 'container')), styles['container'])}>\n {/* Input field at the top */}\n <div className={cx(withVendorCSSClassPrefix(bem('multi-input', 'input-row')), styles['inputRow'])}>\n <div className={cx(withVendorCSSClassPrefix(bem('multi-input', 'input-wrapper')), styles['inputWrapper'])}>\n {renderInputField(\n currentInputValue,\n setCurrentInputValue,\n canAddMore ? <PlusIcon iconClassName={styles['plusIcon']} /> : undefined,\n canAddMore ? handleInputSubmit : undefined,\n )}\n </div>\n </div>\n\n {/* List of added items */}\n {values.length > 0 && (\n <div className={cx(withVendorCSSClassPrefix(bem('multi-input', 'list-container')), styles['listContainer'])}>\n {values.map((value: string, index: number) => (\n <div\n key={index}\n className={cx(withVendorCSSClassPrefix(bem('multi-input', 'list-item')), styles['listItem'])}\n >\n <span\n className={cx(withVendorCSSClassPrefix(bem('multi-input', 'list-item-text')), styles['listItemText'])}\n >\n {value}\n </span>\n {canRemove && (\n <button\n type=\"button\"\n onClick={(): void => handleRemoveValue(index)}\n disabled={disabled}\n className={cx(\n withVendorCSSClassPrefix(bem('multi-input', 'remove-button')),\n styles['removeButton'],\n )}\n title=\"Remove value\"\n >\n <BinIcon iconClassName={styles['icon']} />\n </button>\n )}\n </div>\n ))}\n </div>\n )}\n </div>\n </FormControl>\n );\n};\n\nexport default MultiInput;\n"],"mappings":"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"}
|
|
@@ -17,6 +17,9 @@ const OtpField = ({ label, error, className, required, disabled, helperText, len
|
|
|
17
17
|
const styles = require_OtpField_styles.default(theme, colorScheme, !!disabled, !!error, length);
|
|
18
18
|
const [otp, setOtp] = (0, react.useState)(Array(length).fill(""));
|
|
19
19
|
const inputRefs = (0, react.useRef)([]);
|
|
20
|
+
const fieldId = (0, react.useId)();
|
|
21
|
+
const labelId = `${fieldId}-label`;
|
|
22
|
+
const helperTextId = error || helperText ? `${fieldId}-helper-text` : void 0;
|
|
20
23
|
(0, react.useEffect)(() => {
|
|
21
24
|
inputRefs.current = inputRefs.current.slice(0, length);
|
|
22
25
|
}, [length]);
|
|
@@ -86,14 +89,19 @@ const OtpField = ({ label, error, className, required, disabled, helperText, len
|
|
|
86
89
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_FormControl.default, {
|
|
87
90
|
error,
|
|
88
91
|
helperText,
|
|
92
|
+
helperTextId,
|
|
89
93
|
className: require_emotion.cx((0, __thunderid_browser.withVendorCSSClassPrefix)((0, __thunderid_browser.bem)("otp-field")), className),
|
|
90
94
|
helperTextAlign: "center",
|
|
91
95
|
style,
|
|
92
96
|
children: [label && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_InputLabel.default, {
|
|
97
|
+
id: labelId,
|
|
93
98
|
required,
|
|
94
99
|
error: !!error,
|
|
95
100
|
children: label
|
|
96
101
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
102
|
+
role: "group",
|
|
103
|
+
"aria-labelledby": label ? labelId : void 0,
|
|
104
|
+
"aria-describedby": helperTextId,
|
|
97
105
|
className: require_emotion.cx((0, __thunderid_browser.withVendorCSSClassPrefix)((0, __thunderid_browser.bem)("otp-field", "input-container")), styles["inputContainer"]),
|
|
98
106
|
children: Array.from({ length }, (_, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
99
107
|
ref: (el) => {
|
|
@@ -117,6 +125,7 @@ const OtpField = ({ label, error, className, required, disabled, helperText, len
|
|
|
117
125
|
"aria-label": `${label || "OTP"} digit ${index + 1}`,
|
|
118
126
|
"aria-invalid": !!error,
|
|
119
127
|
"aria-required": required,
|
|
128
|
+
"aria-describedby": helperTextId,
|
|
120
129
|
autoComplete: "one-time-code"
|
|
121
130
|
}, index))
|
|
122
131
|
})]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OtpField.cjs","names":["OtpField: FC<OtpInputProps>","useTheme","styles: Record<string, string>","useStyles","inputRefs: MutableRefObject<HTMLInputElement[]>","newOtp: string[]","newValue: string","otpValue: string","rawData: string","pastedData: string","nextEmptyIndex: number","focusIndex: number","FormControl","cx","InputLabel"],"sources":["../../../../../src/components/primitives/OtpField/OtpField.tsx"],"sourcesContent":["// Copyright 2024 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {withVendorCSSClassPrefix, bem} from '@thunderid/browser';\nimport {\n ClipboardEvent,\n FC,\n KeyboardEvent,\n ChangeEvent,\n MutableRefObject,\n useRef,\n useEffect,\n useState,\n CSSProperties,\n} from 'react';\nimport useStyles from './OtpField.styles';\nimport useTheme from '../../../contexts/Theme/useTheme';\nimport {cx} from '../../../styles/emotion';\nimport FormControl from '../FormControl/FormControl';\nimport InputLabel from '../InputLabel/InputLabel';\n\nexport type OtpFieldType = 'text' | 'number' | 'password';\n\nexport interface OtpInputProps {\n /**\n * Auto focus the first input on mount\n */\n autoFocus?: boolean;\n /**\n * Additional CSS class names\n */\n className?: string;\n /**\n * Whether the field is disabled\n */\n disabled?: boolean;\n /**\n * Error message to display below the OTP input\n */\n error?: string;\n /**\n * Helper text to display below the OTP input\n */\n helperText?: string;\n /**\n * Label text to display above the OTP input\n */\n label?: string;\n /**\n * Number of OTP input fields\n */\n length?: number;\n /**\n * Callback function called when OTP value changes\n */\n onChange?: (event: {target: {value: string}}) => void;\n /**\n * Callback function called when OTP input is complete\n */\n onComplete?: (value: string) => void;\n /**\n * Pattern for numeric input validation\n */\n pattern?: string;\n /**\n * Placeholder character for each input field\n */\n placeholder?: string;\n /**\n * Whether the field is required\n */\n required?: boolean;\n /**\n * Custom container style\n */\n style?: CSSProperties;\n /**\n * Type of input (text, number, password)\n */\n type?: OtpFieldType;\n /**\n * Upper-case entered characters before emitting them\n */\n uppercase?: boolean;\n /**\n * Current OTP value\n */\n value?: string;\n}\n\nconst OtpField: FC<OtpInputProps> = ({\n label,\n error,\n className,\n required,\n disabled,\n helperText,\n length = 6,\n value = '',\n onChange,\n onComplete,\n type = 'text',\n placeholder = '',\n style = {},\n autoFocus = false,\n pattern,\n uppercase = false,\n}: OtpInputProps) => {\n const {theme, colorScheme}: ReturnType<typeof useTheme> = useTheme();\n const styles: Record<string, string> = useStyles(theme, colorScheme, !!disabled, !!error, length);\n const [otp, setOtp] = useState<string[]>(Array(length).fill(''));\n const inputRefs: MutableRefObject<HTMLInputElement[]> = useRef<HTMLInputElement[]>([]);\n\n useEffect(() => {\n inputRefs.current = inputRefs.current.slice(0, length);\n }, [length]);\n\n useEffect(() => {\n if (value) {\n const newOtp: string[] = value.split('').slice(0, length);\n while (newOtp.length < length) {\n newOtp.push('');\n }\n setOtp(newOtp);\n } else {\n setOtp(Array(length).fill(''));\n }\n }, [value, length]);\n\n useEffect(() => {\n if (autoFocus && inputRefs.current[0]) {\n inputRefs.current[0].focus();\n }\n }, [autoFocus]);\n\n const handleChange = (index: number, event: ChangeEvent<HTMLInputElement>): void => {\n const newValue: string = uppercase ? event.target.value.toUpperCase() : event.target.value;\n\n if (newValue.length > 1) return;\n\n if (type === 'number' && newValue && !/^\\d$/.test(newValue)) return;\n\n if (pattern && newValue && !new RegExp(pattern).test(newValue)) return;\n\n const newOtp: string[] = [...otp];\n newOtp[index] = newValue;\n setOtp(newOtp);\n\n const otpValue: string = newOtp.join('');\n\n onChange?.({target: {value: otpValue}});\n\n if (newValue && index < length - 1) {\n inputRefs.current[index + 1]?.focus();\n }\n\n if (newOtp.every((digit: string) => digit !== '') && onComplete) {\n onComplete(otpValue);\n }\n };\n\n const handleKeyDown = (index: number, event: KeyboardEvent<HTMLInputElement>): void => {\n if (event.key === 'Backspace') {\n if (!otp[index] && index > 0) {\n const newOtp: string[] = [...otp];\n\n newOtp[index - 1] = '';\n setOtp(newOtp);\n inputRefs.current[index - 1]?.focus();\n onChange?.({target: {value: newOtp.join('')}});\n } else if (otp[index]) {\n const newOtp: string[] = [...otp];\n\n newOtp[index] = '';\n setOtp(newOtp);\n onChange?.({target: {value: newOtp.join('')}});\n }\n } else if (event.key === 'ArrowLeft' && index > 0) {\n inputRefs.current[index - 1]?.focus();\n } else if (event.key === 'ArrowRight' && index < length - 1) {\n inputRefs.current[index + 1]?.focus();\n } else if (event.key === 'Enter') {\n event.preventDefault();\n\n if (otp.every((digit: string) => digit !== '') && onComplete) {\n onComplete(otp.join(''));\n }\n }\n };\n\n const handlePaste = (event: ClipboardEvent<HTMLInputElement>): void => {\n event.preventDefault();\n\n const rawData: string = event.clipboardData.getData('text');\n // Filter first and let the copy loop below bound the result, so surrounding text such as\n // \"Your code is 123456\" does not push the code itself past the cut-off.\n const pastedData: string = uppercase ? rawData.toUpperCase() : rawData;\n\n let validData = '';\n\n Array.from(pastedData).forEach((char: string) => {\n if (type === 'number' && !/^\\d$/.test(char)) return;\n if (pattern && !new RegExp(pattern).test(char)) return;\n validData += char;\n });\n\n const newOtp: string[] = Array(length).fill('');\n\n for (let i = 0; i < Math.min(validData.length, length); i += 1) {\n newOtp[i] = validData[i];\n }\n\n setOtp(newOtp);\n onChange?.({target: {value: newOtp.join('')}});\n\n const nextEmptyIndex: number = newOtp.findIndex((digit: string) => digit === '');\n const focusIndex: number = nextEmptyIndex !== -1 ? nextEmptyIndex : length - 1;\n\n inputRefs.current[focusIndex]?.focus();\n\n if (newOtp.every((digit: string) => digit !== '') && onComplete) {\n onComplete(newOtp.join(''));\n }\n };\n\n return (\n <FormControl\n error={error}\n helperText={helperText}\n className={cx(withVendorCSSClassPrefix(bem('otp-field')), className)}\n helperTextAlign=\"center\"\n style={style}\n >\n {label && (\n <InputLabel required={required} error={!!error}>\n {label}\n </InputLabel>\n )}\n <div className={cx(withVendorCSSClassPrefix(bem('otp-field', 'input-container')), styles['inputContainer'])}>\n {Array.from({length}, (_: unknown, index: number) => (\n <input\n key={index}\n ref={(el: HTMLInputElement | null): void => {\n if (el) inputRefs.current[index] = el;\n }}\n type={type === 'password' ? 'password' : 'text'}\n inputMode={type === 'number' ? 'numeric' : 'text'}\n value={otp[index] || ''}\n onChange={(event: ChangeEvent<HTMLInputElement>): void => handleChange(index, event)}\n onKeyDown={(event: KeyboardEvent<HTMLInputElement>): void => handleKeyDown(index, event)}\n onPaste={handlePaste}\n className={cx(withVendorCSSClassPrefix(bem('otp-field', 'input')), styles['input'], {\n [withVendorCSSClassPrefix(bem('otp-field', 'input', 'error'))]: !!error,\n [styles['inputError']]: !!error,\n [withVendorCSSClassPrefix(bem('otp-field', 'input', 'disabled'))]: !!disabled,\n [styles['inputDisabled']]: !!disabled,\n })}\n maxLength={1}\n placeholder={placeholder}\n disabled={disabled}\n aria-label={`${label || 'OTP'} digit ${index + 1}`}\n aria-invalid={!!error}\n aria-required={required}\n autoComplete=\"one-time-code\"\n />\n ))}\n </div>\n </FormControl>\n );\n};\n\nexport default OtpField;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"OtpField.cjs","names":["OtpField: FC<OtpInputProps>","useTheme","styles: Record<string, string>","useStyles","inputRefs: MutableRefObject<HTMLInputElement[]>","fieldId: string","helperTextId: string | undefined","newOtp: string[]","newValue: string","otpValue: string","rawData: string","pastedData: string","nextEmptyIndex: number","focusIndex: number","FormControl","cx","InputLabel"],"sources":["../../../../../src/components/primitives/OtpField/OtpField.tsx"],"sourcesContent":["// Copyright 2024 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {withVendorCSSClassPrefix, bem} from '@thunderid/browser';\nimport {\n ClipboardEvent,\n FC,\n KeyboardEvent,\n ChangeEvent,\n MutableRefObject,\n useRef,\n useEffect,\n useState,\n CSSProperties,\n useId,\n} from 'react';\nimport useStyles from './OtpField.styles';\nimport useTheme from '../../../contexts/Theme/useTheme';\nimport {cx} from '../../../styles/emotion';\nimport FormControl from '../FormControl/FormControl';\nimport InputLabel from '../InputLabel/InputLabel';\n\nexport type OtpFieldType = 'text' | 'number' | 'password';\n\nexport interface OtpInputProps {\n /**\n * Auto focus the first input on mount\n */\n autoFocus?: boolean;\n /**\n * Additional CSS class names\n */\n className?: string;\n /**\n * Whether the field is disabled\n */\n disabled?: boolean;\n /**\n * Error message to display below the OTP input\n */\n error?: string;\n /**\n * Helper text to display below the OTP input\n */\n helperText?: string;\n /**\n * Label text to display above the OTP input\n */\n label?: string;\n /**\n * Number of OTP input fields\n */\n length?: number;\n /**\n * Callback function called when OTP value changes\n */\n onChange?: (event: {target: {value: string}}) => void;\n /**\n * Callback function called when OTP input is complete\n */\n onComplete?: (value: string) => void;\n /**\n * Pattern for numeric input validation\n */\n pattern?: string;\n /**\n * Placeholder character for each input field\n */\n placeholder?: string;\n /**\n * Whether the field is required\n */\n required?: boolean;\n /**\n * Custom container style\n */\n style?: CSSProperties;\n /**\n * Type of input (text, number, password)\n */\n type?: OtpFieldType;\n /**\n * Upper-case entered characters before emitting them\n */\n uppercase?: boolean;\n /**\n * Current OTP value\n */\n value?: string;\n}\n\nconst OtpField: FC<OtpInputProps> = ({\n label,\n error,\n className,\n required,\n disabled,\n helperText,\n length = 6,\n value = '',\n onChange,\n onComplete,\n type = 'text',\n placeholder = '',\n style = {},\n autoFocus = false,\n pattern,\n uppercase = false,\n}: OtpInputProps) => {\n const {theme, colorScheme}: ReturnType<typeof useTheme> = useTheme();\n const styles: Record<string, string> = useStyles(theme, colorScheme, !!disabled, !!error, length);\n const [otp, setOtp] = useState<string[]>(Array(length).fill(''));\n const inputRefs: MutableRefObject<HTMLInputElement[]> = useRef<HTMLInputElement[]>([]);\n const fieldId: string = useId();\n const labelId = `${fieldId}-label`;\n const helperTextId: string | undefined = error || helperText ? `${fieldId}-helper-text` : undefined;\n\n useEffect(() => {\n inputRefs.current = inputRefs.current.slice(0, length);\n }, [length]);\n\n useEffect(() => {\n if (value) {\n const newOtp: string[] = value.split('').slice(0, length);\n while (newOtp.length < length) {\n newOtp.push('');\n }\n setOtp(newOtp);\n } else {\n setOtp(Array(length).fill(''));\n }\n }, [value, length]);\n\n useEffect(() => {\n if (autoFocus && inputRefs.current[0]) {\n inputRefs.current[0].focus();\n }\n }, [autoFocus]);\n\n const handleChange = (index: number, event: ChangeEvent<HTMLInputElement>): void => {\n const newValue: string = uppercase ? event.target.value.toUpperCase() : event.target.value;\n\n if (newValue.length > 1) return;\n\n if (type === 'number' && newValue && !/^\\d$/.test(newValue)) return;\n\n if (pattern && newValue && !new RegExp(pattern).test(newValue)) return;\n\n const newOtp: string[] = [...otp];\n newOtp[index] = newValue;\n setOtp(newOtp);\n\n const otpValue: string = newOtp.join('');\n\n onChange?.({target: {value: otpValue}});\n\n if (newValue && index < length - 1) {\n inputRefs.current[index + 1]?.focus();\n }\n\n if (newOtp.every((digit: string) => digit !== '') && onComplete) {\n onComplete(otpValue);\n }\n };\n\n const handleKeyDown = (index: number, event: KeyboardEvent<HTMLInputElement>): void => {\n if (event.key === 'Backspace') {\n if (!otp[index] && index > 0) {\n const newOtp: string[] = [...otp];\n\n newOtp[index - 1] = '';\n setOtp(newOtp);\n inputRefs.current[index - 1]?.focus();\n onChange?.({target: {value: newOtp.join('')}});\n } else if (otp[index]) {\n const newOtp: string[] = [...otp];\n\n newOtp[index] = '';\n setOtp(newOtp);\n onChange?.({target: {value: newOtp.join('')}});\n }\n } else if (event.key === 'ArrowLeft' && index > 0) {\n inputRefs.current[index - 1]?.focus();\n } else if (event.key === 'ArrowRight' && index < length - 1) {\n inputRefs.current[index + 1]?.focus();\n } else if (event.key === 'Enter') {\n event.preventDefault();\n\n if (otp.every((digit: string) => digit !== '') && onComplete) {\n onComplete(otp.join(''));\n }\n }\n };\n\n const handlePaste = (event: ClipboardEvent<HTMLInputElement>): void => {\n event.preventDefault();\n\n const rawData: string = event.clipboardData.getData('text');\n // Filter first and let the copy loop below bound the result, so surrounding text such as\n // \"Your code is 123456\" does not push the code itself past the cut-off.\n const pastedData: string = uppercase ? rawData.toUpperCase() : rawData;\n\n let validData = '';\n\n Array.from(pastedData).forEach((char: string) => {\n if (type === 'number' && !/^\\d$/.test(char)) return;\n if (pattern && !new RegExp(pattern).test(char)) return;\n validData += char;\n });\n\n const newOtp: string[] = Array(length).fill('');\n\n for (let i = 0; i < Math.min(validData.length, length); i += 1) {\n newOtp[i] = validData[i];\n }\n\n setOtp(newOtp);\n onChange?.({target: {value: newOtp.join('')}});\n\n const nextEmptyIndex: number = newOtp.findIndex((digit: string) => digit === '');\n const focusIndex: number = nextEmptyIndex !== -1 ? nextEmptyIndex : length - 1;\n\n inputRefs.current[focusIndex]?.focus();\n\n if (newOtp.every((digit: string) => digit !== '') && onComplete) {\n onComplete(newOtp.join(''));\n }\n };\n\n return (\n <FormControl\n error={error}\n helperText={helperText}\n helperTextId={helperTextId}\n className={cx(withVendorCSSClassPrefix(bem('otp-field')), className)}\n helperTextAlign=\"center\"\n style={style}\n >\n {label && (\n <InputLabel id={labelId} required={required} error={!!error}>\n {label}\n </InputLabel>\n )}\n <div\n role=\"group\"\n aria-labelledby={label ? labelId : undefined}\n aria-describedby={helperTextId}\n className={cx(withVendorCSSClassPrefix(bem('otp-field', 'input-container')), styles['inputContainer'])}\n >\n {Array.from({length}, (_: unknown, index: number) => (\n <input\n key={index}\n ref={(el: HTMLInputElement | null): void => {\n if (el) inputRefs.current[index] = el;\n }}\n type={type === 'password' ? 'password' : 'text'}\n inputMode={type === 'number' ? 'numeric' : 'text'}\n value={otp[index] || ''}\n onChange={(event: ChangeEvent<HTMLInputElement>): void => handleChange(index, event)}\n onKeyDown={(event: KeyboardEvent<HTMLInputElement>): void => handleKeyDown(index, event)}\n onPaste={handlePaste}\n className={cx(withVendorCSSClassPrefix(bem('otp-field', 'input')), styles['input'], {\n [withVendorCSSClassPrefix(bem('otp-field', 'input', 'error'))]: !!error,\n [styles['inputError']]: !!error,\n [withVendorCSSClassPrefix(bem('otp-field', 'input', 'disabled'))]: !!disabled,\n [styles['inputDisabled']]: !!disabled,\n })}\n maxLength={1}\n placeholder={placeholder}\n disabled={disabled}\n aria-label={`${label || 'OTP'} digit ${index + 1}`}\n aria-invalid={!!error}\n aria-required={required}\n aria-describedby={helperTextId}\n autoComplete=\"one-time-code\"\n />\n ))}\n </div>\n </FormControl>\n );\n};\n\nexport default OtpField;\n"],"mappings":"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"}
|
|
@@ -17,6 +17,10 @@ const Select = ({ label, error, className, required, disabled, helperText, place
|
|
|
17
17
|
const { theme, colorScheme } = require_useTheme.default();
|
|
18
18
|
const messages = require_useSelectMessages.default();
|
|
19
19
|
const hasError = !!error;
|
|
20
|
+
const generatedId = (0, react.useId)();
|
|
21
|
+
const inputId = rest.id ?? generatedId;
|
|
22
|
+
const helperTextId = error || helperText ? `${inputId}-helper-text` : void 0;
|
|
23
|
+
const describedBy = [rest["aria-describedby"], helperTextId].filter(Boolean).join(" ") || void 0;
|
|
20
24
|
const styles = require_Select_styles.default(theme, colorScheme, disabled ?? false, hasError);
|
|
21
25
|
const items = (0, react.useMemo)(() => (0, __thunderid_browser.normalizeSelectOptions)(options), [options]);
|
|
22
26
|
const selectRef = (0, react.useRef)(null);
|
|
@@ -44,10 +48,12 @@ const Select = ({ label, error, className, required, disabled, helperText, place
|
|
|
44
48
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_FormControl.default, {
|
|
45
49
|
error,
|
|
46
50
|
helperText,
|
|
51
|
+
helperTextId,
|
|
47
52
|
className: require_emotion.cx((0, __thunderid_browser.withVendorCSSClassPrefix)((0, __thunderid_browser.bem)("select")), className),
|
|
48
53
|
style,
|
|
49
54
|
children: [
|
|
50
55
|
label && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_InputLabel.default, {
|
|
56
|
+
htmlFor: inputId,
|
|
51
57
|
required,
|
|
52
58
|
error: hasError,
|
|
53
59
|
children: label
|
|
@@ -59,6 +65,8 @@ const Select = ({ label, error, className, required, disabled, helperText, place
|
|
|
59
65
|
"aria-required": required,
|
|
60
66
|
"aria-busy": loading || void 0,
|
|
61
67
|
...rest,
|
|
68
|
+
id: inputId,
|
|
69
|
+
"aria-describedby": describedBy,
|
|
62
70
|
ref: selectRef,
|
|
63
71
|
onChange: onLoadMore ? handleChange : onChange,
|
|
64
72
|
children: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Select.cjs","names":["Select: FC<SelectProps>","useTheme","messages: SelectMessages","useSelectMessages","styles: Record<string, string>","useStyles","items: SelectOption[]","selectRef: RefObject<HTMLSelectElement | null>","chosenValueRef: RefObject<string>","current: string | undefined","SelectConstants","selectClassName: string","cx","showsSelectedOption: boolean","statusText: string | undefined","hasStatus: boolean","showsLoadMore: boolean","FormControl","InputLabel"],"sources":["../../../../../src/components/primitives/Select/Select.tsx"],"sourcesContent":["// Copyright 2024 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {bem, normalizeSelectOptions, SelectConstants, withVendorCSSClassPrefix} from '@thunderid/browser';\nimport type {SelectOption, SelectOptionInput} from '@thunderid/browser';\nimport {ChangeEvent, FC, RefObject, SelectHTMLAttributes, useEffect, useMemo, useRef} from 'react';\nimport useStyles from './Select.styles';\nimport useSelectMessages, {SelectMessages} from './useSelectMessages';\nimport useTheme from '../../../contexts/Theme/useTheme';\nimport {cx} from '../../../styles/emotion';\nimport FormControl from '../FormControl/FormControl';\nimport InputLabel from '../InputLabel/InputLabel';\n\nexport type {SelectOption, SelectOptionInput};\n\nexport interface SelectProps extends Omit<SelectHTMLAttributes<HTMLSelectElement>, 'className'> {\n /**\n * Additional CSS class names\n */\n className?: string;\n /**\n * Whether the field is disabled\n */\n disabled?: boolean;\n /**\n * Text shown below the select when it has no options, nothing is loading and there is nothing more to load\n */\n emptyText?: string;\n /**\n * Error message to display below the select\n */\n error?: string;\n /**\n * Helper text to display below the select\n */\n helperText?: string;\n /**\n * Label text to display above the select\n */\n label?: string;\n /**\n * Whether options are being fetched. The load more option waits, and a select with no options shows a loading message\n */\n loading?: boolean;\n /**\n * Called when the load more option, listed after the last one, is chosen. Choosing it does not change the selection.\n * The option is listed only when this is set, so pass it only while more options may be available\n */\n onLoadMore?: () => void;\n /**\n * The options to display: strings, numbers, or `{label, value, disabled?}` objects\n */\n options?: readonly (SelectOptionInput | null | undefined)[] | null;\n /**\n * Placeholder text for the default/empty option. While the field is not required, choosing it clears the selection\n */\n placeholder?: string;\n /**\n * Whether the field is required\n */\n required?: boolean;\n /**\n * Label of the selected option while `value` is not among `options`, such as a value loaded from saved data\n */\n selectedOption?: SelectOption;\n}\n\nconst Select: FC<SelectProps> = ({\n label,\n error,\n className,\n required,\n disabled,\n helperText,\n placeholder,\n options = undefined,\n emptyText = undefined,\n loading = false,\n onLoadMore = undefined,\n onChange = undefined,\n selectedOption = undefined,\n style = {},\n ...rest\n}: SelectProps) => {\n const {theme, colorScheme}: ReturnType<typeof useTheme> = useTheme();\n const messages: SelectMessages = useSelectMessages();\n const hasError = !!error;\n const styles: Record<string, string> = useStyles(theme, colorScheme, disabled ?? false, hasError);\n const items: SelectOption[] = useMemo(() => normalizeSelectOptions(options), [options]);\n const selectRef: RefObject<HTMLSelectElement | null> = useRef<HTMLSelectElement>(null);\n const chosenValueRef: RefObject<string> = useRef<string>('');\n\n // The value to put back when the load more option is chosen, since choosing it is not a selection.\n useEffect(() => {\n const current: string | undefined = selectRef.current?.value;\n\n if (current !== undefined && current !== SelectConstants.LOAD_MORE_OPTION_VALUE) {\n chosenValueRef.current = current;\n }\n });\n\n const handleChange = (event: ChangeEvent<HTMLSelectElement>): void => {\n if (event.target.value !== SelectConstants.LOAD_MORE_OPTION_VALUE) {\n chosenValueRef.current = event.target.value;\n onChange?.(event);\n\n return;\n }\n\n event.target.value = chosenValueRef.current;\n onLoadMore?.();\n };\n\n const selectClassName: string = cx(\n withVendorCSSClassPrefix(bem('select', 'input')),\n styles['select'],\n hasError && styles['selectError'],\n disabled && styles['selectDisabled'],\n );\n\n const showsSelectedOption: boolean =\n selectedOption !== undefined &&\n rest.value !== undefined &&\n String(rest.value) === selectedOption.value &&\n !items.some((item: SelectOption) => item.value === selectedOption.value);\n\n let statusText: string | undefined;\n\n if (loading) {\n statusText = items.length === 0 ? messages.loading : messages.loadingMore;\n } else if (items.length === 0 && !onLoadMore) {\n statusText = emptyText;\n }\n\n const hasStatus: boolean = onLoadMore !== undefined || emptyText !== undefined || loading;\n const showsLoadMore: boolean = onLoadMore !== undefined && (items.length > 0 || !loading);\n\n return (\n <FormControl\n error={error}\n helperText={helperText}\n className={cx(withVendorCSSClassPrefix(bem('select')), className)}\n style={style}\n >\n {label && (\n <InputLabel required={required} error={hasError}>\n {label}\n </InputLabel>\n )}\n <select\n className={selectClassName}\n disabled={disabled}\n aria-invalid={hasError}\n aria-required={required}\n aria-busy={loading || undefined}\n {...rest}\n ref={selectRef}\n onChange={onLoadMore ? handleChange : onChange}\n >\n {placeholder && (\n <option value=\"\" disabled={Boolean(required)}>\n {placeholder}\n </option>\n )}\n {showsSelectedOption && <option value={selectedOption?.value}>{selectedOption?.label}</option>}\n {items.map((option: SelectOption) => (\n <option key={option.value} value={option.value} disabled={option.disabled} className={styles['option']}>\n {option.label}\n </option>\n ))}\n {showsLoadMore && (\n <option\n value={SelectConstants.LOAD_MORE_OPTION_VALUE}\n disabled={loading}\n className={cx(withVendorCSSClassPrefix(bem('select', 'load-more')), styles['option'], styles['loadMore'])}\n >\n {loading ? messages.loadingMore : messages.loadMore}\n </option>\n )}\n </select>\n {hasStatus && (\n <span role=\"status\" className={styles['status']}>\n {statusText}\n </span>\n )}\n </FormControl>\n );\n};\n\nexport default Select;\n"],"mappings":";;;;;;;;;;;;;;;AAmEA,MAAMA,UAA2B,EAC/B,OACA,OACA,WACA,UACA,UACA,YACA,aACA,UAAU,QACV,YAAY,QACZ,UAAU,OACV,aAAa,QACb,WAAW,QACX,iBAAiB,QACjB,QAAQ,EAAE,CACV,GAAG,WACc;CACjB,MAAM,EAAC,OAAO,gBAA4CC,0BAAU;CACpE,MAAMC,WAA2BC,mCAAmB;CACpD,MAAM,WAAW,CAAC,CAAC;CACnB,MAAMC,SAAiCC,8BAAU,OAAO,aAAa,YAAY,OAAO,SAAS;CACjG,MAAMC,iFAA6D,QAAQ,EAAE,CAAC,QAAQ,CAAC;CACvF,MAAMC,8BAA2E,KAAK;CACtF,MAAMC,mCAAmD,GAAG;AAG5D,4BAAgB;EACd,MAAMC,UAA8B,UAAU,SAAS;AAEvD,MAAI,YAAY,UAAa,YAAYC,oCAAgB,uBACvD,gBAAe,UAAU;GAE3B;CAEF,MAAM,gBAAgB,UAAgD;AACpE,MAAI,MAAM,OAAO,UAAUA,oCAAgB,wBAAwB;AACjE,kBAAe,UAAU,MAAM,OAAO;AACtC,cAAW,MAAM;AAEjB;;AAGF,QAAM,OAAO,QAAQ,eAAe;AACpC,gBAAc;;CAGhB,MAAMC,kBAA0BC,kGACD,UAAU,QAAQ,CAAC,EAChD,OAAO,WACP,YAAY,OAAO,gBACnB,YAAY,OAAO,kBACpB;CAED,MAAMC,sBACJ,mBAAmB,UACnB,KAAK,UAAU,UACf,OAAO,KAAK,MAAM,KAAK,eAAe,SACtC,CAAC,MAAM,MAAM,SAAuB,KAAK,UAAU,eAAe,MAAM;CAE1E,IAAIC;AAEJ,KAAI,QACF,cAAa,MAAM,WAAW,IAAI,SAAS,UAAU,SAAS;UACrD,MAAM,WAAW,KAAK,CAAC,WAChC,cAAa;CAGf,MAAMC,YAAqB,eAAe,UAAa,cAAc,UAAa;CAClF,MAAMC,gBAAyB,eAAe,WAAc,MAAM,SAAS,KAAK,CAAC;AAEjF,QACE,4CAACC;EACQ;EACK;
|
|
1
|
+
{"version":3,"file":"Select.cjs","names":["Select: FC<SelectProps>","useTheme","messages: SelectMessages","useSelectMessages","generatedId: string","inputId: string","helperTextId: string | undefined","describedBy: string | undefined","styles: Record<string, string>","useStyles","items: SelectOption[]","selectRef: RefObject<HTMLSelectElement | null>","chosenValueRef: RefObject<string>","current: string | undefined","SelectConstants","selectClassName: string","cx","showsSelectedOption: boolean","statusText: string | undefined","hasStatus: boolean","showsLoadMore: boolean","FormControl","InputLabel"],"sources":["../../../../../src/components/primitives/Select/Select.tsx"],"sourcesContent":["// Copyright 2024 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {bem, normalizeSelectOptions, SelectConstants, withVendorCSSClassPrefix} from '@thunderid/browser';\nimport type {SelectOption, SelectOptionInput} from '@thunderid/browser';\nimport {ChangeEvent, FC, RefObject, SelectHTMLAttributes, useEffect, useId, useMemo, useRef} from 'react';\nimport useStyles from './Select.styles';\nimport useSelectMessages, {SelectMessages} from './useSelectMessages';\nimport useTheme from '../../../contexts/Theme/useTheme';\nimport {cx} from '../../../styles/emotion';\nimport FormControl from '../FormControl/FormControl';\nimport InputLabel from '../InputLabel/InputLabel';\n\nexport type {SelectOption, SelectOptionInput};\n\nexport interface SelectProps extends Omit<SelectHTMLAttributes<HTMLSelectElement>, 'className'> {\n /**\n * Additional CSS class names\n */\n className?: string;\n /**\n * Whether the field is disabled\n */\n disabled?: boolean;\n /**\n * Text shown below the select when it has no options, nothing is loading and there is nothing more to load\n */\n emptyText?: string;\n /**\n * Error message to display below the select\n */\n error?: string;\n /**\n * Helper text to display below the select\n */\n helperText?: string;\n /**\n * Label text to display above the select\n */\n label?: string;\n /**\n * Whether options are being fetched. The load more option waits, and a select with no options shows a loading message\n */\n loading?: boolean;\n /**\n * Called when the load more option, listed after the last one, is chosen. Choosing it does not change the selection.\n * The option is listed only when this is set, so pass it only while more options may be available\n */\n onLoadMore?: () => void;\n /**\n * The options to display: strings, numbers, or `{label, value, disabled?}` objects\n */\n options?: readonly (SelectOptionInput | null | undefined)[] | null;\n /**\n * Placeholder text for the default/empty option. While the field is not required, choosing it clears the selection\n */\n placeholder?: string;\n /**\n * Whether the field is required\n */\n required?: boolean;\n /**\n * Label of the selected option while `value` is not among `options`, such as a value loaded from saved data\n */\n selectedOption?: SelectOption;\n}\n\nconst Select: FC<SelectProps> = ({\n label,\n error,\n className,\n required,\n disabled,\n helperText,\n placeholder,\n options = undefined,\n emptyText = undefined,\n loading = false,\n onLoadMore = undefined,\n onChange = undefined,\n selectedOption = undefined,\n style = {},\n ...rest\n}: SelectProps) => {\n const {theme, colorScheme}: ReturnType<typeof useTheme> = useTheme();\n const messages: SelectMessages = useSelectMessages();\n const hasError = !!error;\n const generatedId: string = useId();\n const inputId: string = rest.id ?? generatedId;\n const helperTextId: string | undefined = error || helperText ? `${inputId}-helper-text` : undefined;\n const describedBy: string | undefined =\n [rest['aria-describedby'], helperTextId].filter(Boolean).join(' ') || undefined;\n const styles: Record<string, string> = useStyles(theme, colorScheme, disabled ?? false, hasError);\n const items: SelectOption[] = useMemo(() => normalizeSelectOptions(options), [options]);\n const selectRef: RefObject<HTMLSelectElement | null> = useRef<HTMLSelectElement>(null);\n const chosenValueRef: RefObject<string> = useRef<string>('');\n\n // The value to put back when the load more option is chosen, since choosing it is not a selection.\n useEffect(() => {\n const current: string | undefined = selectRef.current?.value;\n\n if (current !== undefined && current !== SelectConstants.LOAD_MORE_OPTION_VALUE) {\n chosenValueRef.current = current;\n }\n });\n\n const handleChange = (event: ChangeEvent<HTMLSelectElement>): void => {\n if (event.target.value !== SelectConstants.LOAD_MORE_OPTION_VALUE) {\n chosenValueRef.current = event.target.value;\n onChange?.(event);\n\n return;\n }\n\n event.target.value = chosenValueRef.current;\n onLoadMore?.();\n };\n\n const selectClassName: string = cx(\n withVendorCSSClassPrefix(bem('select', 'input')),\n styles['select'],\n hasError && styles['selectError'],\n disabled && styles['selectDisabled'],\n );\n\n const showsSelectedOption: boolean =\n selectedOption !== undefined &&\n rest.value !== undefined &&\n String(rest.value) === selectedOption.value &&\n !items.some((item: SelectOption) => item.value === selectedOption.value);\n\n let statusText: string | undefined;\n\n if (loading) {\n statusText = items.length === 0 ? messages.loading : messages.loadingMore;\n } else if (items.length === 0 && !onLoadMore) {\n statusText = emptyText;\n }\n\n const hasStatus: boolean = onLoadMore !== undefined || emptyText !== undefined || loading;\n const showsLoadMore: boolean = onLoadMore !== undefined && (items.length > 0 || !loading);\n\n return (\n <FormControl\n error={error}\n helperText={helperText}\n helperTextId={helperTextId}\n className={cx(withVendorCSSClassPrefix(bem('select')), className)}\n style={style}\n >\n {label && (\n <InputLabel htmlFor={inputId} required={required} error={hasError}>\n {label}\n </InputLabel>\n )}\n <select\n className={selectClassName}\n disabled={disabled}\n aria-invalid={hasError}\n aria-required={required}\n aria-busy={loading || undefined}\n {...rest}\n id={inputId}\n aria-describedby={describedBy}\n ref={selectRef}\n onChange={onLoadMore ? handleChange : onChange}\n >\n {placeholder && (\n <option value=\"\" disabled={Boolean(required)}>\n {placeholder}\n </option>\n )}\n {showsSelectedOption && <option value={selectedOption?.value}>{selectedOption?.label}</option>}\n {items.map((option: SelectOption) => (\n <option key={option.value} value={option.value} disabled={option.disabled} className={styles['option']}>\n {option.label}\n </option>\n ))}\n {showsLoadMore && (\n <option\n value={SelectConstants.LOAD_MORE_OPTION_VALUE}\n disabled={loading}\n className={cx(withVendorCSSClassPrefix(bem('select', 'load-more')), styles['option'], styles['loadMore'])}\n >\n {loading ? messages.loadingMore : messages.loadMore}\n </option>\n )}\n </select>\n {hasStatus && (\n <span role=\"status\" className={styles['status']}>\n {statusText}\n </span>\n )}\n </FormControl>\n );\n};\n\nexport default Select;\n"],"mappings":";;;;;;;;;;;;;;;AAmEA,MAAMA,UAA2B,EAC/B,OACA,OACA,WACA,UACA,UACA,YACA,aACA,UAAU,QACV,YAAY,QACZ,UAAU,OACV,aAAa,QACb,WAAW,QACX,iBAAiB,QACjB,QAAQ,EAAE,CACV,GAAG,WACc;CACjB,MAAM,EAAC,OAAO,gBAA4CC,0BAAU;CACpE,MAAMC,WAA2BC,mCAAmB;CACpD,MAAM,WAAW,CAAC,CAAC;CACnB,MAAMC,gCAA6B;CACnC,MAAMC,UAAkB,KAAK,MAAM;CACnC,MAAMC,eAAmC,SAAS,aAAa,GAAG,QAAQ,gBAAgB;CAC1F,MAAMC,cACJ,CAAC,KAAK,qBAAqB,aAAa,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI,IAAI;CACxE,MAAMC,SAAiCC,8BAAU,OAAO,aAAa,YAAY,OAAO,SAAS;CACjG,MAAMC,iFAA6D,QAAQ,EAAE,CAAC,QAAQ,CAAC;CACvF,MAAMC,8BAA2E,KAAK;CACtF,MAAMC,mCAAmD,GAAG;AAG5D,4BAAgB;EACd,MAAMC,UAA8B,UAAU,SAAS;AAEvD,MAAI,YAAY,UAAa,YAAYC,oCAAgB,uBACvD,gBAAe,UAAU;GAE3B;CAEF,MAAM,gBAAgB,UAAgD;AACpE,MAAI,MAAM,OAAO,UAAUA,oCAAgB,wBAAwB;AACjE,kBAAe,UAAU,MAAM,OAAO;AACtC,cAAW,MAAM;AAEjB;;AAGF,QAAM,OAAO,QAAQ,eAAe;AACpC,gBAAc;;CAGhB,MAAMC,kBAA0BC,kGACD,UAAU,QAAQ,CAAC,EAChD,OAAO,WACP,YAAY,OAAO,gBACnB,YAAY,OAAO,kBACpB;CAED,MAAMC,sBACJ,mBAAmB,UACnB,KAAK,UAAU,UACf,OAAO,KAAK,MAAM,KAAK,eAAe,SACtC,CAAC,MAAM,MAAM,SAAuB,KAAK,UAAU,eAAe,MAAM;CAE1E,IAAIC;AAEJ,KAAI,QACF,cAAa,MAAM,WAAW,IAAI,SAAS,UAAU,SAAS;UACrD,MAAM,WAAW,KAAK,CAAC,WAChC,cAAa;CAGf,MAAMC,YAAqB,eAAe,UAAa,cAAc,UAAa;CAClF,MAAMC,gBAAyB,eAAe,WAAc,MAAM,SAAS,KAAK,CAAC;AAEjF,QACE,4CAACC;EACQ;EACK;EACE;EACd,WAAWL,kGAAgC,SAAS,CAAC,EAAE,UAAU;EAC1D;;GAEN,SACC,2CAACM;IAAW,SAAS;IAAmB;IAAU,OAAO;cACtD;KACU;GAEf,4CAAC;IACC,WAAW;IACD;IACV,gBAAc;IACd,iBAAe;IACf,aAAW,WAAW;IACtB,GAAI;IACJ,IAAI;IACJ,oBAAkB;IAClB,KAAK;IACL,UAAU,aAAa,eAAe;;KAErC,eACC,2CAAC;MAAO,OAAM;MAAG,UAAU,QAAQ,SAAS;gBACzC;OACM;KAEV,uBAAuB,2CAAC;MAAO,OAAO,gBAAgB;gBAAQ,gBAAgB;OAAe;KAC7F,MAAM,KAAK,WACV,2CAAC;MAA0B,OAAO,OAAO;MAAO,UAAU,OAAO;MAAU,WAAW,OAAO;gBAC1F,OAAO;QADG,OAAO,MAEX,CACT;KACD,iBACC,2CAAC;MACC,OAAOR,oCAAgB;MACvB,UAAU;MACV,WAAWE,kGAAgC,UAAU,YAAY,CAAC,EAAE,OAAO,WAAW,OAAO,YAAY;gBAExG,UAAU,SAAS,cAAc,SAAS;OACpC;;KAEJ;GACR,aACC,2CAAC;IAAK,MAAK;IAAS,WAAW,OAAO;cACnC;KACI;;GAEG;;AAIlB,qBAAe"}
|
|
@@ -6,6 +6,8 @@ const require_InputLabel = require('../InputLabel/InputLabel.cjs');
|
|
|
6
6
|
const require_TextField_styles = require('./TextField.styles.cjs');
|
|
7
7
|
let __thunderid_browser = require("@thunderid/browser");
|
|
8
8
|
__thunderid_browser = require_rolldown_runtime.__toESM(__thunderid_browser);
|
|
9
|
+
let react = require("react");
|
|
10
|
+
react = require_rolldown_runtime.__toESM(react);
|
|
9
11
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
12
|
react_jsx_runtime = require_rolldown_runtime.__toESM(react_jsx_runtime);
|
|
11
13
|
|
|
@@ -21,6 +23,10 @@ const activateOnKey = (activate) => (event) => {
|
|
|
21
23
|
};
|
|
22
24
|
const TextField = ({ label, error, required, className, disabled, helperText, startIcon, endAdornment, endIcon, endIconLabel = "End icon", onStartIconClick, onEndIconClick, type = "text", style = {},...rest }) => {
|
|
23
25
|
const { theme, colorScheme } = require_useTheme.default();
|
|
26
|
+
const generatedId = (0, react.useId)();
|
|
27
|
+
const inputId = rest.id ?? generatedId;
|
|
28
|
+
const helperTextId = error || helperText ? `${inputId}-helper-text` : void 0;
|
|
29
|
+
const describedBy = [rest["aria-describedby"], helperTextId].filter(Boolean).join(" ") || void 0;
|
|
24
30
|
const hasError = !!error;
|
|
25
31
|
const styles = require_TextField_styles.default(theme, colorScheme, disabled ?? false, hasError, !!startIcon, !!endIcon, !!endAdornment);
|
|
26
32
|
const inputClassName = require_emotion.cx((0, __thunderid_browser.withVendorCSSClassPrefix)((0, __thunderid_browser.bem)("text-field", "input")), styles["input"], hasError && styles["inputError"], disabled && styles["inputDisabled"]);
|
|
@@ -31,9 +37,11 @@ const TextField = ({ label, error, required, className, disabled, helperText, st
|
|
|
31
37
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_FormControl.default, {
|
|
32
38
|
error,
|
|
33
39
|
helperText,
|
|
40
|
+
helperTextId,
|
|
34
41
|
className: require_emotion.cx((0, __thunderid_browser.withVendorCSSClassPrefix)((0, __thunderid_browser.bem)("text-field")), className),
|
|
35
42
|
style,
|
|
36
43
|
children: [label && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_InputLabel.default, {
|
|
44
|
+
htmlFor: inputId,
|
|
37
45
|
required,
|
|
38
46
|
error: hasError,
|
|
39
47
|
children: label
|
|
@@ -55,7 +63,9 @@ const TextField = ({ label, error, required, className, disabled, helperText, st
|
|
|
55
63
|
disabled,
|
|
56
64
|
"aria-invalid": hasError,
|
|
57
65
|
"aria-required": required,
|
|
58
|
-
...rest
|
|
66
|
+
...rest,
|
|
67
|
+
id: inputId,
|
|
68
|
+
"aria-describedby": describedBy
|
|
59
69
|
}),
|
|
60
70
|
endIcon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
61
71
|
className: endIconClassName,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextField.cjs","names":["TextField: FC<TextFieldProps>","useTheme","styles: Record<string, string>","useStyles","inputClassName: string","cx","containerClassName: string","startIconClassName: string","endIconClassName: string","endAdornmentClassName: string","FormControl","InputLabel"],"sources":["../../../../../src/components/primitives/TextField/TextField.tsx"],"sourcesContent":["// Copyright 2024 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {withVendorCSSClassPrefix, bem} from '@thunderid/browser';\nimport {FC, InputHTMLAttributes, KeyboardEvent, ReactNode} from 'react';\nimport useStyles from './TextField.styles';\nimport useTheme from '../../../contexts/Theme/useTheme';\nimport {cx} from '../../../styles/emotion';\nimport FormControl from '../FormControl/FormControl';\nimport InputLabel from '../InputLabel/InputLabel';\n\nexport interface TextFieldProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'className'> {\n /**\n * Additional CSS class names\n */\n className?: string;\n /**\n * Whether the field is disabled\n */\n disabled?: boolean;\n /**\n * Extra content shown inside the field, at its end after the end icon, such as an action button\n */\n endAdornment?: ReactNode;\n /**\n * Icon to display at the end (right) of the input\n */\n endIcon?: ReactNode;\n /**\n * Accessible name of the end icon when it is a button, that is when `onEndIconClick` is set\n */\n endIconLabel?: string;\n /**\n * Error message to display below the input\n */\n error?: string;\n /**\n * Helper text to display below the input\n */\n helperText?: string;\n /**\n * Label text to display above the input\n */\n label?: string;\n /**\n * Click handler for the end icon\n */\n onEndIconClick?: () => void;\n /**\n * Click handler for the start icon\n */\n onStartIconClick?: () => void;\n /**\n * Whether the field is required\n */\n required?: boolean;\n /**\n * Icon to display at the start (left) of the input\n */\n startIcon?: ReactNode;\n}\n\n/**\n * Makes an element with `role=\"button\"` respond to Enter and Space, as a native button does.\n */\nconst activateOnKey =\n (activate: () => void) =>\n (event: KeyboardEvent<HTMLDivElement>): void => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n activate();\n }\n };\n\nconst TextField: FC<TextFieldProps> = ({\n label,\n error,\n required,\n className,\n disabled,\n helperText,\n startIcon,\n endAdornment,\n endIcon,\n endIconLabel = 'End icon',\n onStartIconClick,\n onEndIconClick,\n type = 'text',\n style = {},\n ...rest\n}: TextFieldProps) => {\n const {theme, colorScheme}: ReturnType<typeof useTheme> = useTheme();\n const hasError = !!error;\n const hasStartIcon = !!startIcon;\n const hasEndIcon = !!endIcon;\n const hasEndAdornment = !!endAdornment;\n const styles: Record<string, string> = useStyles(\n theme,\n colorScheme,\n disabled ?? false,\n hasError,\n hasStartIcon,\n hasEndIcon,\n hasEndAdornment,\n );\n\n const inputClassName: string = cx(\n withVendorCSSClassPrefix(bem('text-field', 'input')),\n styles['input'],\n hasError && styles['inputError'],\n disabled && styles['inputDisabled'],\n );\n\n const containerClassName: string = cx(\n withVendorCSSClassPrefix(bem('text-field', 'container')),\n styles['inputContainer'],\n );\n\n const startIconClassName: string = cx(withVendorCSSClassPrefix(bem('text-field', 'start-icon')), styles['startIcon']);\n\n const endIconClassName: string = cx(withVendorCSSClassPrefix(bem('text-field', 'end-icon')), styles['endIcon']);\n\n const endAdornmentClassName: string = cx(\n withVendorCSSClassPrefix(bem('text-field', 'end-adornment')),\n styles['endAdornment'],\n );\n\n return (\n <FormControl\n error={error}\n helperText={helperText}\n className={cx(withVendorCSSClassPrefix(bem('text-field')), className)}\n style={style}\n >\n {label && (\n <InputLabel required={required} error={hasError}>\n {label}\n </InputLabel>\n )}\n <div className={containerClassName}>\n {startIcon && (\n <div\n className={startIconClassName}\n onClick={onStartIconClick}\n role={onStartIconClick ? 'button' : undefined}\n tabIndex={onStartIconClick && !disabled ? 0 : undefined}\n onKeyDown={onStartIconClick && !disabled ? activateOnKey(onStartIconClick) : undefined}\n aria-label=\"Start icon\"\n >\n {startIcon}\n </div>\n )}\n <input\n className={inputClassName}\n type={type}\n disabled={disabled}\n aria-invalid={hasError}\n aria-required={required}\n {...rest}\n />\n {endIcon && (\n <div\n className={endIconClassName}\n onClick={onEndIconClick}\n role={onEndIconClick ? 'button' : undefined}\n tabIndex={onEndIconClick && !disabled ? 0 : undefined}\n onKeyDown={onEndIconClick && !disabled ? activateOnKey(onEndIconClick) : undefined}\n aria-label={endIconLabel}\n >\n {endIcon}\n </div>\n )}\n {endAdornment && <div className={endAdornmentClassName}>{endAdornment}</div>}\n </div>\n </FormControl>\n );\n};\n\nexport default TextField;\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"TextField.cjs","names":["TextField: FC<TextFieldProps>","useTheme","generatedId: string","inputId: string","helperTextId: string | undefined","describedBy: string | undefined","styles: Record<string, string>","useStyles","inputClassName: string","cx","containerClassName: string","startIconClassName: string","endIconClassName: string","endAdornmentClassName: string","FormControl","InputLabel"],"sources":["../../../../../src/components/primitives/TextField/TextField.tsx"],"sourcesContent":["// Copyright 2024 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {withVendorCSSClassPrefix, bem} from '@thunderid/browser';\nimport {FC, InputHTMLAttributes, KeyboardEvent, ReactNode, useId} from 'react';\nimport useStyles from './TextField.styles';\nimport useTheme from '../../../contexts/Theme/useTheme';\nimport {cx} from '../../../styles/emotion';\nimport FormControl from '../FormControl/FormControl';\nimport InputLabel from '../InputLabel/InputLabel';\n\nexport interface TextFieldProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'className'> {\n /**\n * Additional CSS class names\n */\n className?: string;\n /**\n * Whether the field is disabled\n */\n disabled?: boolean;\n /**\n * Extra content shown inside the field, at its end after the end icon, such as an action button\n */\n endAdornment?: ReactNode;\n /**\n * Icon to display at the end (right) of the input\n */\n endIcon?: ReactNode;\n /**\n * Accessible name of the end icon when it is a button, that is when `onEndIconClick` is set\n */\n endIconLabel?: string;\n /**\n * Error message to display below the input\n */\n error?: string;\n /**\n * Helper text to display below the input\n */\n helperText?: string;\n /**\n * Label text to display above the input\n */\n label?: string;\n /**\n * Click handler for the end icon\n */\n onEndIconClick?: () => void;\n /**\n * Click handler for the start icon\n */\n onStartIconClick?: () => void;\n /**\n * Whether the field is required\n */\n required?: boolean;\n /**\n * Icon to display at the start (left) of the input\n */\n startIcon?: ReactNode;\n}\n\n/**\n * Makes an element with `role=\"button\"` respond to Enter and Space, as a native button does.\n */\nconst activateOnKey =\n (activate: () => void) =>\n (event: KeyboardEvent<HTMLDivElement>): void => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n activate();\n }\n };\n\nconst TextField: FC<TextFieldProps> = ({\n label,\n error,\n required,\n className,\n disabled,\n helperText,\n startIcon,\n endAdornment,\n endIcon,\n endIconLabel = 'End icon',\n onStartIconClick,\n onEndIconClick,\n type = 'text',\n style = {},\n ...rest\n}: TextFieldProps) => {\n const {theme, colorScheme}: ReturnType<typeof useTheme> = useTheme();\n const generatedId: string = useId();\n const inputId: string = rest.id ?? generatedId;\n const helperTextId: string | undefined = error || helperText ? `${inputId}-helper-text` : undefined;\n const describedBy: string | undefined =\n [rest['aria-describedby'], helperTextId].filter(Boolean).join(' ') || undefined;\n const hasError = !!error;\n const hasStartIcon = !!startIcon;\n const hasEndIcon = !!endIcon;\n const hasEndAdornment = !!endAdornment;\n const styles: Record<string, string> = useStyles(\n theme,\n colorScheme,\n disabled ?? false,\n hasError,\n hasStartIcon,\n hasEndIcon,\n hasEndAdornment,\n );\n\n const inputClassName: string = cx(\n withVendorCSSClassPrefix(bem('text-field', 'input')),\n styles['input'],\n hasError && styles['inputError'],\n disabled && styles['inputDisabled'],\n );\n\n const containerClassName: string = cx(\n withVendorCSSClassPrefix(bem('text-field', 'container')),\n styles['inputContainer'],\n );\n\n const startIconClassName: string = cx(withVendorCSSClassPrefix(bem('text-field', 'start-icon')), styles['startIcon']);\n\n const endIconClassName: string = cx(withVendorCSSClassPrefix(bem('text-field', 'end-icon')), styles['endIcon']);\n\n const endAdornmentClassName: string = cx(\n withVendorCSSClassPrefix(bem('text-field', 'end-adornment')),\n styles['endAdornment'],\n );\n\n return (\n <FormControl\n error={error}\n helperText={helperText}\n helperTextId={helperTextId}\n className={cx(withVendorCSSClassPrefix(bem('text-field')), className)}\n style={style}\n >\n {label && (\n <InputLabel htmlFor={inputId} required={required} error={hasError}>\n {label}\n </InputLabel>\n )}\n <div className={containerClassName}>\n {startIcon && (\n <div\n className={startIconClassName}\n onClick={onStartIconClick}\n role={onStartIconClick ? 'button' : undefined}\n tabIndex={onStartIconClick && !disabled ? 0 : undefined}\n onKeyDown={onStartIconClick && !disabled ? activateOnKey(onStartIconClick) : undefined}\n aria-label=\"Start icon\"\n >\n {startIcon}\n </div>\n )}\n <input\n className={inputClassName}\n type={type}\n disabled={disabled}\n aria-invalid={hasError}\n aria-required={required}\n {...rest}\n id={inputId}\n aria-describedby={describedBy}\n />\n {endIcon && (\n <div\n className={endIconClassName}\n onClick={onEndIconClick}\n role={onEndIconClick ? 'button' : undefined}\n tabIndex={onEndIconClick && !disabled ? 0 : undefined}\n onKeyDown={onEndIconClick && !disabled ? activateOnKey(onEndIconClick) : undefined}\n aria-label={endIconLabel}\n >\n {endIcon}\n </div>\n )}\n {endAdornment && <div className={endAdornmentClassName}>{endAdornment}</div>}\n </div>\n </FormControl>\n );\n};\n\nexport default TextField;\n"],"mappings":";;;;;;;;;;;;;;;;;AAiEA,MAAM,iBACH,cACA,UAA+C;AAC9C,KAAI,MAAM,QAAQ,WAAW,MAAM,QAAQ,KAAK;AAC9C,QAAM,gBAAgB;AACtB,YAAU;;;AAIhB,MAAMA,aAAiC,EACrC,OACA,OACA,UACA,WACA,UACA,YACA,WACA,cACA,SACA,eAAe,YACf,kBACA,gBACA,OAAO,QACP,QAAQ,EAAE,CACV,GAAG,WACiB;CACpB,MAAM,EAAC,OAAO,gBAA4CC,0BAAU;CACpE,MAAMC,gCAA6B;CACnC,MAAMC,UAAkB,KAAK,MAAM;CACnC,MAAMC,eAAmC,SAAS,aAAa,GAAG,QAAQ,gBAAgB;CAC1F,MAAMC,cACJ,CAAC,KAAK,qBAAqB,aAAa,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI,IAAI;CACxE,MAAM,WAAW,CAAC,CAAC;CAInB,MAAMC,SAAiCC,iCACrC,OACA,aACA,YAAY,OACZ,UAPmB,CAAC,CAAC,WACJ,CAAC,CAAC,SACG,CAAC,CAAC,aASzB;CAED,MAAMC,iBAAyBC,kGACA,cAAc,QAAQ,CAAC,EACpD,OAAO,UACP,YAAY,OAAO,eACnB,YAAY,OAAO,iBACpB;CAED,MAAMC,qBAA6BD,kGACJ,cAAc,YAAY,CAAC,EACxD,OAAO,kBACR;CAED,MAAME,qBAA6BF,kGAAgC,cAAc,aAAa,CAAC,EAAE,OAAO,aAAa;CAErH,MAAMG,mBAA2BH,kGAAgC,cAAc,WAAW,CAAC,EAAE,OAAO,WAAW;CAE/G,MAAMI,wBAAgCJ,kGACP,cAAc,gBAAgB,CAAC,EAC5D,OAAO,gBACR;AAED,QACE,4CAACK;EACQ;EACK;EACE;EACd,WAAWL,kGAAgC,aAAa,CAAC,EAAE,UAAU;EAC9D;aAEN,SACC,2CAACM;GAAW,SAAS;GAAmB;GAAU,OAAO;aACtD;IACU,EAEf,4CAAC;GAAI,WAAW;;IACb,aACC,2CAAC;KACC,WAAW;KACX,SAAS;KACT,MAAM,mBAAmB,WAAW;KACpC,UAAU,oBAAoB,CAAC,WAAW,IAAI;KAC9C,WAAW,oBAAoB,CAAC,WAAW,cAAc,iBAAiB,GAAG;KAC7E,cAAW;eAEV;MACG;IAER,2CAAC;KACC,WAAW;KACL;KACI;KACV,gBAAc;KACd,iBAAe;KACf,GAAI;KACJ,IAAI;KACJ,oBAAkB;MAClB;IACD,WACC,2CAAC;KACC,WAAW;KACX,SAAS;KACT,MAAM,iBAAiB,WAAW;KAClC,UAAU,kBAAkB,CAAC,WAAW,IAAI;KAC5C,WAAW,kBAAkB,CAAC,WAAW,cAAc,eAAe,GAAG;KACzE,cAAY;eAEX;MACG;IAEP,gBAAgB,2CAAC;KAAI,WAAW;eAAwB;MAAmB;;IACxE;GACM;;AAIlB,wBAAe"}
|