@thunderid/react 1.3.3 → 1.4.1
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/cjs/components/presentation/ChangeCredential/BaseChangeCredential.cjs +1 -1
- package/dist/cjs/components/presentation/auth/AcceptInvite/BaseAcceptInvite.cjs +1 -1
- package/dist/cjs/components/presentation/auth/AcceptInvite/BaseAcceptInvite.cjs.map +1 -1
- package/dist/cjs/components/presentation/auth/AuthOptionFactory.cjs +56 -17
- package/dist/cjs/components/presentation/auth/AuthOptionFactory.cjs.map +1 -1
- package/dist/cjs/components/presentation/auth/InviteUser/BaseInviteUser.cjs +21 -5
- package/dist/cjs/components/presentation/auth/InviteUser/BaseInviteUser.cjs.map +1 -1
- package/dist/cjs/components/presentation/auth/InviteUser/InviteUser.cjs +6 -0
- package/dist/cjs/components/presentation/auth/InviteUser/InviteUser.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/AuthFlowSelect/AuthFlowSelect.cjs +21 -0
- package/dist/cjs/components/primitives/AuthFlowSelect/AuthFlowSelect.cjs.map +1 -0
- package/dist/cjs/components/primitives/AuthFlowSelect/BaseAuthFlowSelect.cjs +32 -0
- package/dist/cjs/components/primitives/AuthFlowSelect/BaseAuthFlowSelect.cjs.map +1 -0
- 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/CopyableText/CopyableText.cjs +41 -21
- package/dist/cjs/components/primitives/CopyableText/CopyableText.cjs.map +1 -1
- package/dist/cjs/components/primitives/CopyableText/CopyableText.styles.cjs +5 -2
- package/dist/cjs/components/primitives/CopyableText/CopyableText.styles.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/Icons/Copy.cjs +33 -0
- package/dist/cjs/components/primitives/Icons/Copy.cjs.map +1 -0
- 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/PasswordField/PasswordField.cjs +2 -1
- package/dist/cjs/components/primitives/PasswordField/PasswordField.cjs.map +1 -1
- package/dist/cjs/components/primitives/Select/ResourceSelect.cjs +104 -0
- package/dist/cjs/components/primitives/Select/ResourceSelect.cjs.map +1 -0
- package/dist/cjs/components/primitives/Select/Select.cjs +80 -21
- package/dist/cjs/components/primitives/Select/Select.cjs.map +1 -1
- package/dist/cjs/components/primitives/Select/Select.styles.cjs +19 -3
- package/dist/cjs/components/primitives/Select/Select.styles.cjs.map +1 -1
- package/dist/cjs/components/primitives/Select/useSelectMessages.cjs +21 -0
- package/dist/cjs/components/primitives/Select/useSelectMessages.cjs.map +1 -0
- package/dist/cjs/components/primitives/TextField/TextField.cjs +30 -4
- package/dist/cjs/components/primitives/TextField/TextField.cjs.map +1 -1
- package/dist/cjs/components/primitives/TextField/TextField.styles.cjs +18 -5
- package/dist/cjs/components/primitives/TextField/TextField.styles.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/components/primitives/UserSelect/BaseUserSelect.cjs +33 -0
- package/dist/cjs/components/primitives/UserSelect/BaseUserSelect.cjs.map +1 -0
- package/dist/cjs/components/primitives/UserSelect/UserSelect.cjs +21 -0
- package/dist/cjs/components/primitives/UserSelect/UserSelect.cjs.map +1 -0
- package/dist/cjs/components/primitives/UserTypeSelect/BaseUserTypeSelect.cjs +31 -0
- package/dist/cjs/components/primitives/UserTypeSelect/BaseUserTypeSelect.cjs.map +1 -0
- package/dist/cjs/components/primitives/UserTypeSelect/UserTypeSelect.cjs +21 -0
- package/dist/cjs/components/primitives/UserTypeSelect/UserTypeSelect.cjs.map +1 -0
- 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 +1 -1
- package/dist/cjs/contexts/ThunderID/ThunderIDProvider.cjs.map +1 -1
- package/dist/cjs/hooks/useFetchAuthFlows.cjs +36 -0
- package/dist/cjs/hooks/useFetchAuthFlows.cjs.map +1 -0
- package/dist/cjs/hooks/useFetchUserTypes.cjs +34 -0
- package/dist/cjs/hooks/useFetchUserTypes.cjs.map +1 -0
- package/dist/cjs/hooks/useFetchUsers.cjs +35 -0
- package/dist/cjs/hooks/useFetchUsers.cjs.map +1 -0
- package/dist/cjs/hooks/useInfiniteOptions.cjs +129 -0
- package/dist/cjs/hooks/useInfiniteOptions.cjs.map +1 -0
- package/dist/cjs/hooks/useManagementEndpoint.cjs.map +1 -1
- package/dist/cjs/index.cjs +56 -0
- package/dist/cjs/utils/createAuthenticatedFetcher.cjs +1 -0
- package/dist/cjs/utils/createAuthenticatedFetcher.cjs.map +1 -1
- package/dist/components/presentation/ChangeCredential/BaseChangeCredential.js +1 -1
- package/dist/components/presentation/auth/AcceptInvite/BaseAcceptInvite.d.ts.map +1 -1
- package/dist/components/presentation/auth/AcceptInvite/BaseAcceptInvite.js +1 -1
- package/dist/components/presentation/auth/AcceptInvite/BaseAcceptInvite.js.map +1 -1
- package/dist/components/presentation/auth/AuthOptionFactory.d.ts +9 -1
- package/dist/components/presentation/auth/AuthOptionFactory.d.ts.map +1 -1
- package/dist/components/presentation/auth/AuthOptionFactory.js +56 -17
- package/dist/components/presentation/auth/AuthOptionFactory.js.map +1 -1
- package/dist/components/presentation/auth/InviteUser/BaseInviteUser.d.ts +18 -2
- package/dist/components/presentation/auth/InviteUser/BaseInviteUser.d.ts.map +1 -1
- package/dist/components/presentation/auth/InviteUser/BaseInviteUser.js +22 -6
- package/dist/components/presentation/auth/InviteUser/BaseInviteUser.js.map +1 -1
- package/dist/components/presentation/auth/InviteUser/InviteUser.d.ts +2 -1
- package/dist/components/presentation/auth/InviteUser/InviteUser.d.ts.map +1 -1
- package/dist/components/presentation/auth/InviteUser/InviteUser.js +6 -0
- package/dist/components/presentation/auth/InviteUser/InviteUser.js.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/AuthFlowSelect/AuthFlowSelect.d.ts +11 -0
- package/dist/components/primitives/AuthFlowSelect/AuthFlowSelect.d.ts.map +1 -0
- package/dist/components/primitives/AuthFlowSelect/AuthFlowSelect.js +19 -0
- package/dist/components/primitives/AuthFlowSelect/AuthFlowSelect.js.map +1 -0
- package/dist/components/primitives/AuthFlowSelect/BaseAuthFlowSelect.d.ts +14 -0
- package/dist/components/primitives/AuthFlowSelect/BaseAuthFlowSelect.d.ts.map +1 -0
- package/dist/components/primitives/AuthFlowSelect/BaseAuthFlowSelect.js +29 -0
- package/dist/components/primitives/AuthFlowSelect/BaseAuthFlowSelect.js.map +1 -0
- 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/CopyableText/CopyableText.d.ts +9 -0
- package/dist/components/primitives/CopyableText/CopyableText.d.ts.map +1 -1
- package/dist/components/primitives/CopyableText/CopyableText.js +40 -21
- package/dist/components/primitives/CopyableText/CopyableText.js.map +1 -1
- package/dist/components/primitives/CopyableText/CopyableText.styles.d.ts.map +1 -1
- package/dist/components/primitives/CopyableText/CopyableText.styles.js +5 -2
- package/dist/components/primitives/CopyableText/CopyableText.styles.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/Icons/Copy.d.ts +7 -0
- package/dist/components/primitives/Icons/Copy.d.ts.map +1 -0
- package/dist/components/primitives/Icons/Copy.js +31 -0
- package/dist/components/primitives/Icons/Copy.js.map +1 -0
- 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/PasswordField/PasswordField.d.ts +9 -1
- package/dist/components/primitives/PasswordField/PasswordField.d.ts.map +1 -1
- package/dist/components/primitives/PasswordField/PasswordField.js +2 -1
- package/dist/components/primitives/PasswordField/PasswordField.js.map +1 -1
- package/dist/components/primitives/Select/ResourceSelect.d.ts +36 -0
- package/dist/components/primitives/Select/ResourceSelect.d.ts.map +1 -0
- package/dist/components/primitives/Select/ResourceSelect.js +100 -0
- package/dist/components/primitives/Select/ResourceSelect.js.map +1 -0
- package/dist/components/primitives/Select/Select.d.ts +22 -13
- package/dist/components/primitives/Select/Select.d.ts.map +1 -1
- package/dist/components/primitives/Select/Select.js +80 -22
- package/dist/components/primitives/Select/Select.js.map +1 -1
- package/dist/components/primitives/Select/Select.styles.d.ts.map +1 -1
- package/dist/components/primitives/Select/Select.styles.js +19 -3
- package/dist/components/primitives/Select/Select.styles.js.map +1 -1
- package/dist/components/primitives/Select/__tests__/selectTestUtils.d.ts +6 -0
- package/dist/components/primitives/Select/__tests__/selectTestUtils.d.ts.map +1 -0
- package/dist/components/primitives/Select/useSelectMessages.d.ts +9 -0
- package/dist/components/primitives/Select/useSelectMessages.d.ts.map +1 -0
- package/dist/components/primitives/Select/useSelectMessages.js +18 -0
- package/dist/components/primitives/Select/useSelectMessages.js.map +1 -0
- package/dist/components/primitives/TextField/TextField.d.ts +8 -0
- package/dist/components/primitives/TextField/TextField.d.ts.map +1 -1
- package/dist/components/primitives/TextField/TextField.js +29 -4
- package/dist/components/primitives/TextField/TextField.js.map +1 -1
- package/dist/components/primitives/TextField/TextField.styles.d.ts +2 -1
- package/dist/components/primitives/TextField/TextField.styles.d.ts.map +1 -1
- package/dist/components/primitives/TextField/TextField.styles.js +18 -5
- package/dist/components/primitives/TextField/TextField.styles.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/components/primitives/UserSelect/BaseUserSelect.d.ts +15 -0
- package/dist/components/primitives/UserSelect/BaseUserSelect.d.ts.map +1 -0
- package/dist/components/primitives/UserSelect/BaseUserSelect.js +30 -0
- package/dist/components/primitives/UserSelect/BaseUserSelect.js.map +1 -0
- package/dist/components/primitives/UserSelect/UserSelect.d.ts +11 -0
- package/dist/components/primitives/UserSelect/UserSelect.d.ts.map +1 -0
- package/dist/components/primitives/UserSelect/UserSelect.js +19 -0
- package/dist/components/primitives/UserSelect/UserSelect.js.map +1 -0
- package/dist/components/primitives/UserTypeSelect/BaseUserTypeSelect.d.ts +14 -0
- package/dist/components/primitives/UserTypeSelect/BaseUserTypeSelect.d.ts.map +1 -0
- package/dist/components/primitives/UserTypeSelect/BaseUserTypeSelect.js +28 -0
- package/dist/components/primitives/UserTypeSelect/BaseUserTypeSelect.js.map +1 -0
- package/dist/components/primitives/UserTypeSelect/UserTypeSelect.d.ts +11 -0
- package/dist/components/primitives/UserTypeSelect/UserTypeSelect.d.ts.map +1 -0
- package/dist/components/primitives/UserTypeSelect/UserTypeSelect.js +19 -0
- package/dist/components/primitives/UserTypeSelect/UserTypeSelect.js.map +1 -0
- 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 +1 -1
- package/dist/contexts/ThunderID/ThunderIDProvider.js.map +1 -1
- package/dist/hooks/useFetchAuthFlows.d.ts +9 -0
- package/dist/hooks/useFetchAuthFlows.d.ts.map +1 -0
- package/dist/hooks/useFetchAuthFlows.js +33 -0
- package/dist/hooks/useFetchAuthFlows.js.map +1 -0
- package/dist/hooks/useFetchUserTypes.d.ts +8 -0
- package/dist/hooks/useFetchUserTypes.d.ts.map +1 -0
- package/dist/hooks/useFetchUserTypes.js +31 -0
- package/dist/hooks/useFetchUserTypes.js.map +1 -0
- package/dist/hooks/useFetchUsers.d.ts +8 -0
- package/dist/hooks/useFetchUsers.d.ts.map +1 -0
- package/dist/hooks/useFetchUsers.js +32 -0
- package/dist/hooks/useFetchUsers.js.map +1 -0
- package/dist/hooks/useInfiniteOptions.d.ts +31 -0
- package/dist/hooks/useInfiniteOptions.d.ts.map +1 -0
- package/dist/hooks/useInfiniteOptions.js +126 -0
- package/dist/hooks/useInfiniteOptions.js.map +1 -0
- package/dist/hooks/useManagementEndpoint.d.ts +1 -1
- package/dist/hooks/useManagementEndpoint.d.ts.map +1 -1
- package/dist/hooks/useManagementEndpoint.js.map +1 -1
- package/dist/index.d.ts +18 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -2
- package/dist/utils/createAuthenticatedFetcher.d.ts.map +1 -1
- package/dist/utils/createAuthenticatedFetcher.js +1 -0
- package/dist/utils/createAuthenticatedFetcher.js.map +1 -1
- package/package.json +4 -4
|
@@ -3,40 +3,98 @@ import useTheme_default from "../../../contexts/Theme/useTheme.js";
|
|
|
3
3
|
import FormControl_default from "../FormControl/FormControl.js";
|
|
4
4
|
import InputLabel_default from "../InputLabel/InputLabel.js";
|
|
5
5
|
import Select_styles_default from "./Select.styles.js";
|
|
6
|
-
import
|
|
6
|
+
import useSelectMessages_default from "./useSelectMessages.js";
|
|
7
|
+
import { SelectConstants, bem, normalizeSelectOptions, withVendorCSSClassPrefix } from "@thunderid/browser";
|
|
8
|
+
import { useEffect, useId, useMemo, useRef } from "react";
|
|
7
9
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
10
|
|
|
9
11
|
//#region src/components/primitives/Select/Select.tsx
|
|
10
|
-
const Select = ({ label, error, className, required, disabled, helperText, placeholder, options, style = {},...rest }) => {
|
|
12
|
+
const Select = ({ label, error, className, required, disabled, helperText, placeholder, options = void 0, emptyText = void 0, loading = false, onLoadMore = void 0, onChange = void 0, selectedOption = void 0, style = {},...rest }) => {
|
|
11
13
|
const { theme, colorScheme } = useTheme_default();
|
|
14
|
+
const messages = useSelectMessages_default();
|
|
12
15
|
const hasError = !!error;
|
|
16
|
+
const generatedId = useId();
|
|
17
|
+
const inputId = rest.id ?? generatedId;
|
|
18
|
+
const helperTextId = error || helperText ? `${inputId}-helper-text` : void 0;
|
|
19
|
+
const describedBy = [rest["aria-describedby"], helperTextId].filter(Boolean).join(" ") || void 0;
|
|
13
20
|
const styles = Select_styles_default(theme, colorScheme, disabled ?? false, hasError);
|
|
21
|
+
const items = useMemo(() => normalizeSelectOptions(options), [options]);
|
|
22
|
+
const selectRef = useRef(null);
|
|
23
|
+
const chosenValueRef = useRef("");
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
const current = selectRef.current?.value;
|
|
26
|
+
if (current !== void 0 && current !== SelectConstants.LOAD_MORE_OPTION_VALUE) chosenValueRef.current = current;
|
|
27
|
+
});
|
|
28
|
+
const handleChange = (event) => {
|
|
29
|
+
if (event.target.value !== SelectConstants.LOAD_MORE_OPTION_VALUE) {
|
|
30
|
+
chosenValueRef.current = event.target.value;
|
|
31
|
+
onChange?.(event);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
event.target.value = chosenValueRef.current;
|
|
35
|
+
onLoadMore?.();
|
|
36
|
+
};
|
|
14
37
|
const selectClassName = cx(withVendorCSSClassPrefix(bem("select", "input")), styles["select"], hasError && styles["selectError"], disabled && styles["selectDisabled"]);
|
|
38
|
+
const showsSelectedOption = selectedOption !== void 0 && rest.value !== void 0 && String(rest.value) === selectedOption.value && !items.some((item) => item.value === selectedOption.value);
|
|
39
|
+
let statusText;
|
|
40
|
+
if (loading) statusText = items.length === 0 ? messages.loading : messages.loadingMore;
|
|
41
|
+
else if (items.length === 0 && !onLoadMore) statusText = emptyText;
|
|
42
|
+
const hasStatus = onLoadMore !== void 0 || emptyText !== void 0 || loading;
|
|
43
|
+
const showsLoadMore = onLoadMore !== void 0 && (items.length > 0 || !loading);
|
|
15
44
|
return /* @__PURE__ */ jsxs(FormControl_default, {
|
|
16
45
|
error,
|
|
17
46
|
helperText,
|
|
47
|
+
helperTextId,
|
|
18
48
|
className: cx(withVendorCSSClassPrefix(bem("select")), className),
|
|
19
49
|
style,
|
|
20
|
-
children: [
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
50
|
+
children: [
|
|
51
|
+
label && /* @__PURE__ */ jsx(InputLabel_default, {
|
|
52
|
+
htmlFor: inputId,
|
|
53
|
+
required,
|
|
54
|
+
error: hasError,
|
|
55
|
+
children: label
|
|
56
|
+
}),
|
|
57
|
+
/* @__PURE__ */ jsxs("select", {
|
|
58
|
+
className: selectClassName,
|
|
59
|
+
disabled,
|
|
60
|
+
"aria-invalid": hasError,
|
|
61
|
+
"aria-required": required,
|
|
62
|
+
"aria-busy": loading || void 0,
|
|
63
|
+
...rest,
|
|
64
|
+
id: inputId,
|
|
65
|
+
"aria-describedby": describedBy,
|
|
66
|
+
ref: selectRef,
|
|
67
|
+
onChange: onLoadMore ? handleChange : onChange,
|
|
68
|
+
children: [
|
|
69
|
+
placeholder && /* @__PURE__ */ jsx("option", {
|
|
70
|
+
value: "",
|
|
71
|
+
disabled: Boolean(required),
|
|
72
|
+
children: placeholder
|
|
73
|
+
}),
|
|
74
|
+
showsSelectedOption && /* @__PURE__ */ jsx("option", {
|
|
75
|
+
value: selectedOption?.value,
|
|
76
|
+
children: selectedOption?.label
|
|
77
|
+
}),
|
|
78
|
+
items.map((option) => /* @__PURE__ */ jsx("option", {
|
|
79
|
+
value: option.value,
|
|
80
|
+
disabled: option.disabled,
|
|
81
|
+
className: styles["option"],
|
|
82
|
+
children: option.label
|
|
83
|
+
}, option.value)),
|
|
84
|
+
showsLoadMore && /* @__PURE__ */ jsx("option", {
|
|
85
|
+
value: SelectConstants.LOAD_MORE_OPTION_VALUE,
|
|
86
|
+
disabled: loading,
|
|
87
|
+
className: cx(withVendorCSSClassPrefix(bem("select", "load-more")), styles["option"], styles["loadMore"]),
|
|
88
|
+
children: loading ? messages.loadingMore : messages.loadMore
|
|
89
|
+
})
|
|
90
|
+
]
|
|
91
|
+
}),
|
|
92
|
+
hasStatus && /* @__PURE__ */ jsx("span", {
|
|
93
|
+
role: "status",
|
|
94
|
+
className: styles["status"],
|
|
95
|
+
children: statusText
|
|
96
|
+
})
|
|
97
|
+
]
|
|
40
98
|
});
|
|
41
99
|
};
|
|
42
100
|
var Select_default = Select;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Select.js","names":["Select: FC<SelectProps>","useTheme","styles: Record<string, string>","useStyles","selectClassName: string","FormControl","InputLabel"],"sources":["../../../../src/components/primitives/Select/Select.tsx"],"sourcesContent":["// Copyright 2024 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {withVendorCSSClassPrefix,
|
|
1
|
+
{"version":3,"file":"Select.js","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","selectClassName: string","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,kBAAU;CACpE,MAAMC,WAA2BC,2BAAmB;CACpD,MAAM,WAAW,CAAC,CAAC;CACnB,MAAMC,cAAsB,OAAO;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,sBAAU,OAAO,aAAa,YAAY,OAAO,SAAS;CACjG,MAAMC,QAAwB,cAAc,uBAAuB,QAAQ,EAAE,CAAC,QAAQ,CAAC;CACvF,MAAMC,YAAiD,OAA0B,KAAK;CACtF,MAAMC,iBAAoC,OAAe,GAAG;AAG5D,iBAAgB;EACd,MAAMC,UAA8B,UAAU,SAAS;AAEvD,MAAI,YAAY,UAAa,YAAY,gBAAgB,uBACvD,gBAAe,UAAU;GAE3B;CAEF,MAAM,gBAAgB,UAAgD;AACpE,MAAI,MAAM,OAAO,UAAU,gBAAgB,wBAAwB;AACjE,kBAAe,UAAU,MAAM,OAAO;AACtC,cAAW,MAAM;AAEjB;;AAGF,QAAM,OAAO,QAAQ,eAAe;AACpC,gBAAc;;CAGhB,MAAMC,kBAA0B,GAC9B,yBAAyB,IAAI,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,qBAACC;EACQ;EACK;EACE;EACd,WAAW,GAAG,yBAAyB,IAAI,SAAS,CAAC,EAAE,UAAU;EAC1D;;GAEN,SACC,oBAACC;IAAW,SAAS;IAAmB;IAAU,OAAO;cACtD;KACU;GAEf,qBAAC;IACC,WAAW;IACD;IACV,gBAAc;IACd,iBAAe;IACf,aAAW,WAAW;IACtB,GAAI;IACJ,IAAI;IACJ,oBAAkB;IAClB,KAAK;IACL,UAAU,aAAa,eAAe;;KAErC,eACC,oBAAC;MAAO,OAAM;MAAG,UAAU,QAAQ,SAAS;gBACzC;OACM;KAEV,uBAAuB,oBAAC;MAAO,OAAO,gBAAgB;gBAAQ,gBAAgB;OAAe;KAC7F,MAAM,KAAK,WACV,oBAAC;MAA0B,OAAO,OAAO;MAAO,UAAU,OAAO;MAAU,WAAW,OAAO;gBAC1F,OAAO;QADG,OAAO,MAEX,CACT;KACD,iBACC,oBAAC;MACC,OAAO,gBAAgB;MACvB,UAAU;MACV,WAAW,GAAG,yBAAyB,IAAI,UAAU,YAAY,CAAC,EAAE,OAAO,WAAW,OAAO,YAAY;gBAExG,UAAU,SAAS,cAAc,SAAS;OACpC;;KAEJ;GACR,aACC,oBAAC;IAAK,MAAK;IAAS,WAAW,OAAO;cACnC;KACI;;GAEG;;AAIlB,qBAAe"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Select.styles.d.ts","sourceRoot":"","sources":["../../../../src/components/primitives/Select/Select.styles.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,KAAK,EAAC,MAAM,oBAAoB,CAAC;AAIzC;;;;;;;GAOG;AACH,QAAA,MAAM,SAAS,GAAI,OAAO,KAAK,EAAE,aAAa,MAAM,EAAE,UAAU,OAAO,EAAE,UAAU,OAAO,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"Select.styles.d.ts","sourceRoot":"","sources":["../../../../src/components/primitives/Select/Select.styles.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,KAAK,EAAC,MAAM,oBAAoB,CAAC;AAIzC;;;;;;;GAOG;AACH,QAAA,MAAM,SAAS,GAAI,OAAO,KAAK,EAAE,aAAa,MAAM,EAAE,UAAU,OAAO,EAAE,UAAU,OAAO,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAkGpE,CAAC;AAE/C,eAAe,SAAS,CAAC"}
|
|
@@ -63,8 +63,7 @@ const useStyles = (theme, colorScheme, disabled, hasError) => useMemo(() => {
|
|
|
63
63
|
opacity: 0.6;
|
|
64
64
|
cursor: not-allowed;
|
|
65
65
|
`;
|
|
66
|
-
|
|
67
|
-
option: css`
|
|
66
|
+
const option = css`
|
|
68
67
|
padding: calc(${theme.vars.spacing.unit} / 2) ${theme.vars.spacing.unit};
|
|
69
68
|
color: ${theme.vars.colors.text.primary};
|
|
70
69
|
background-color: ${theme.vars.colors.background.surface};
|
|
@@ -77,10 +76,27 @@ const useStyles = (theme, colorScheme, disabled, hasError) => useMemo(() => {
|
|
|
77
76
|
background-color: ${theme.vars.colors.primary.main};
|
|
78
77
|
color: ${theme.vars.colors.primary.contrastText};
|
|
79
78
|
}
|
|
79
|
+
`;
|
|
80
|
+
const status = css`
|
|
81
|
+
display: block;
|
|
82
|
+
margin-top: calc(${theme.vars.spacing.unit} / 2);
|
|
83
|
+
font-size: ${theme.vars.typography.fontSizes.sm};
|
|
84
|
+
color: ${theme.vars.colors.text.secondary};
|
|
85
|
+
|
|
86
|
+
&:empty {
|
|
87
|
+
display: none;
|
|
88
|
+
}
|
|
89
|
+
`;
|
|
90
|
+
return {
|
|
91
|
+
loadMore: css`
|
|
92
|
+
color: ${theme.vars.colors.primary.main};
|
|
93
|
+
font-weight: ${theme.vars.typography.fontWeights.medium};
|
|
80
94
|
`,
|
|
95
|
+
option,
|
|
81
96
|
select,
|
|
82
97
|
selectDisabled,
|
|
83
|
-
selectError
|
|
98
|
+
selectError,
|
|
99
|
+
status
|
|
84
100
|
};
|
|
85
101
|
}, [
|
|
86
102
|
theme,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Select.styles.js","names":["select: string","selectError: string","selectDisabled: string"],"sources":["../../../../src/components/primitives/Select/Select.styles.ts"],"sourcesContent":["// Copyright 2024 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {Theme} from '@thunderid/browser';\nimport {useMemo} from 'react';\nimport {css} from '../../../styles/emotion';\n\n/**\n * Creates styles for the Select component using BEM methodology\n * @param theme - The theme object containing design tokens\n * @param colorScheme - The current color scheme (used for memoization)\n * @param disabled - Whether the component is disabled\n * @param hasError - Whether the component has an error\n * @returns Object containing CSS class names for component styling\n */\nconst useStyles = (theme: Theme, colorScheme: string, disabled: boolean, hasError: boolean): Record<string, string> =>\n useMemo(() => {\n const dropdownArrowColor: string = theme.colors.text.secondary.replace('#', '');\n const dropdownArrow = `data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23${dropdownArrowColor}%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E`;\n\n const select: string = css`\n width: 100%;\n padding: ${theme.vars.spacing.unit} calc(${theme.vars.spacing.unit} * 1.5);\n border: 1px solid ${hasError ? theme.vars.colors.error.main : theme.vars.colors.border};\n border-radius: ${theme.vars.components?.Field?.root?.borderRadius || theme.vars.borderRadius.medium};\n font-size: ${theme.vars.typography.fontSizes.md};\n font-family: ${theme.vars.typography.fontFamily};\n color: ${theme.vars.colors.text.primary};\n background-color: ${disabled ? theme.vars.colors.background.disabled : theme.vars.colors.background.surface};\n outline: none;\n transition:\n border-color 0.2s ease,\n box-shadow 0.2s ease;\n appearance: none;\n background-image: url('${dropdownArrow}');\n background-repeat: no-repeat;\n background-position: right 0.7em top 50%;\n background-size: 0.65em auto;\n cursor: ${disabled ? 'not-allowed' : 'pointer'};\n\n &:focus {\n border-color: ${hasError ? theme.vars.colors.error.main : theme.vars.colors.primary.main};\n box-shadow: 0 0 0 2px ${hasError ? `${theme.vars.colors.error.main}20` : `${theme.vars.colors.primary.main}20`};\n }\n\n &:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n &:hover:not(:disabled) {\n border-color: ${hasError ? theme.vars.colors.error.main : theme.vars.colors.primary.main};\n }\n `;\n\n const selectError: string = css`\n border-color: ${theme.vars.colors.error.main};\n\n &:focus {\n border-color: ${theme.vars.colors.error.main};\n box-shadow: 0 0 0 2px ${theme.vars.colors.error.main}20;\n }\n\n &:hover:not(:disabled) {\n border-color: ${theme.vars.colors.error.main};\n }\n `;\n\n const selectDisabled: string = css`\n background-color: ${theme.vars.colors.background.disabled};\n opacity: 0.6;\n cursor: not-allowed;\n `;\n\n const option: string = css`\n padding: calc(${theme.vars.spacing.unit} / 2) ${theme.vars.spacing.unit};\n color: ${theme.vars.colors.text.primary};\n background-color: ${theme.vars.colors.background.surface};\n\n &:hover {\n background-color: ${theme.vars.colors.action.hover};\n }\n\n &:checked {\n background-color: ${theme.vars.colors.primary.main};\n color: ${theme.vars.colors.primary.contrastText};\n }\n `;\n\n return {\n option,\n select,\n selectDisabled,\n selectError,\n };\n }, [theme, colorScheme, disabled, hasError]);\n\nexport default useStyles;\n"],"mappings":";;;;;;;;;;;;AAeA,MAAM,aAAa,OAAc,aAAqB,UAAmB,aACvE,cAAc;CAEZ,MAAM,gBAAgB,yKADa,MAAM,OAAO,KAAK,UAAU,QAAQ,KAAK,GAAG,CACmI;CAElN,MAAMA,SAAiB,GAAG;;iBAEb,MAAM,KAAK,QAAQ,KAAK,QAAQ,MAAM,KAAK,QAAQ,KAAK;0BAC/C,WAAW,MAAM,KAAK,OAAO,MAAM,OAAO,MAAM,KAAK,OAAO,OAAO;uBACtE,MAAM,KAAK,YAAY,OAAO,MAAM,gBAAgB,MAAM,KAAK,aAAa,OAAO;mBACvF,MAAM,KAAK,WAAW,UAAU,GAAG;qBACjC,MAAM,KAAK,WAAW,WAAW;eACvC,MAAM,KAAK,OAAO,KAAK,QAAQ;0BACpB,WAAW,MAAM,KAAK,OAAO,WAAW,WAAW,MAAM,KAAK,OAAO,WAAW,QAAQ;;;;;;+BAMnF,cAAc;;;;gBAI7B,WAAW,gBAAgB,UAAU;;;wBAG7B,WAAW,MAAM,KAAK,OAAO,MAAM,OAAO,MAAM,KAAK,OAAO,QAAQ,KAAK;gCACjE,WAAW,GAAG,MAAM,KAAK,OAAO,MAAM,KAAK,MAAM,GAAG,MAAM,KAAK,OAAO,QAAQ,KAAK,IAAI;;;;;;;;;wBAS/F,WAAW,MAAM,KAAK,OAAO,MAAM,OAAO,MAAM,KAAK,OAAO,QAAQ,KAAK;;;CAI7F,MAAMC,cAAsB,GAAG;sBACb,MAAM,KAAK,OAAO,MAAM,KAAK;;;wBAG3B,MAAM,KAAK,OAAO,MAAM,KAAK;gCACrB,MAAM,KAAK,OAAO,MAAM,KAAK;;;;wBAIrC,MAAM,KAAK,OAAO,MAAM,KAAK;;;CAIjD,MAAMC,iBAAyB,GAAG;0BACZ,MAAM,KAAK,OAAO,WAAW,SAAS;;;;
|
|
1
|
+
{"version":3,"file":"Select.styles.js","names":["select: string","selectError: string","selectDisabled: string","option: string","status: string"],"sources":["../../../../src/components/primitives/Select/Select.styles.ts"],"sourcesContent":["// Copyright 2024 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {Theme} from '@thunderid/browser';\nimport {useMemo} from 'react';\nimport {css} from '../../../styles/emotion';\n\n/**\n * Creates styles for the Select component using BEM methodology\n * @param theme - The theme object containing design tokens\n * @param colorScheme - The current color scheme (used for memoization)\n * @param disabled - Whether the component is disabled\n * @param hasError - Whether the component has an error\n * @returns Object containing CSS class names for component styling\n */\nconst useStyles = (theme: Theme, colorScheme: string, disabled: boolean, hasError: boolean): Record<string, string> =>\n useMemo(() => {\n const dropdownArrowColor: string = theme.colors.text.secondary.replace('#', '');\n const dropdownArrow = `data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23${dropdownArrowColor}%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E`;\n\n const select: string = css`\n width: 100%;\n padding: ${theme.vars.spacing.unit} calc(${theme.vars.spacing.unit} * 1.5);\n border: 1px solid ${hasError ? theme.vars.colors.error.main : theme.vars.colors.border};\n border-radius: ${theme.vars.components?.Field?.root?.borderRadius || theme.vars.borderRadius.medium};\n font-size: ${theme.vars.typography.fontSizes.md};\n font-family: ${theme.vars.typography.fontFamily};\n color: ${theme.vars.colors.text.primary};\n background-color: ${disabled ? theme.vars.colors.background.disabled : theme.vars.colors.background.surface};\n outline: none;\n transition:\n border-color 0.2s ease,\n box-shadow 0.2s ease;\n appearance: none;\n background-image: url('${dropdownArrow}');\n background-repeat: no-repeat;\n background-position: right 0.7em top 50%;\n background-size: 0.65em auto;\n cursor: ${disabled ? 'not-allowed' : 'pointer'};\n\n &:focus {\n border-color: ${hasError ? theme.vars.colors.error.main : theme.vars.colors.primary.main};\n box-shadow: 0 0 0 2px ${hasError ? `${theme.vars.colors.error.main}20` : `${theme.vars.colors.primary.main}20`};\n }\n\n &:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n &:hover:not(:disabled) {\n border-color: ${hasError ? theme.vars.colors.error.main : theme.vars.colors.primary.main};\n }\n `;\n\n const selectError: string = css`\n border-color: ${theme.vars.colors.error.main};\n\n &:focus {\n border-color: ${theme.vars.colors.error.main};\n box-shadow: 0 0 0 2px ${theme.vars.colors.error.main}20;\n }\n\n &:hover:not(:disabled) {\n border-color: ${theme.vars.colors.error.main};\n }\n `;\n\n const selectDisabled: string = css`\n background-color: ${theme.vars.colors.background.disabled};\n opacity: 0.6;\n cursor: not-allowed;\n `;\n\n const option: string = css`\n padding: calc(${theme.vars.spacing.unit} / 2) ${theme.vars.spacing.unit};\n color: ${theme.vars.colors.text.primary};\n background-color: ${theme.vars.colors.background.surface};\n\n &:hover {\n background-color: ${theme.vars.colors.action.hover};\n }\n\n &:checked {\n background-color: ${theme.vars.colors.primary.main};\n color: ${theme.vars.colors.primary.contrastText};\n }\n `;\n\n const status: string = css`\n display: block;\n margin-top: calc(${theme.vars.spacing.unit} / 2);\n font-size: ${theme.vars.typography.fontSizes.sm};\n color: ${theme.vars.colors.text.secondary};\n\n &:empty {\n display: none;\n }\n `;\n\n const loadMore: string = css`\n color: ${theme.vars.colors.primary.main};\n font-weight: ${theme.vars.typography.fontWeights.medium};\n `;\n\n return {\n loadMore,\n option,\n select,\n selectDisabled,\n selectError,\n status,\n };\n }, [theme, colorScheme, disabled, hasError]);\n\nexport default useStyles;\n"],"mappings":";;;;;;;;;;;;AAeA,MAAM,aAAa,OAAc,aAAqB,UAAmB,aACvE,cAAc;CAEZ,MAAM,gBAAgB,yKADa,MAAM,OAAO,KAAK,UAAU,QAAQ,KAAK,GAAG,CACmI;CAElN,MAAMA,SAAiB,GAAG;;iBAEb,MAAM,KAAK,QAAQ,KAAK,QAAQ,MAAM,KAAK,QAAQ,KAAK;0BAC/C,WAAW,MAAM,KAAK,OAAO,MAAM,OAAO,MAAM,KAAK,OAAO,OAAO;uBACtE,MAAM,KAAK,YAAY,OAAO,MAAM,gBAAgB,MAAM,KAAK,aAAa,OAAO;mBACvF,MAAM,KAAK,WAAW,UAAU,GAAG;qBACjC,MAAM,KAAK,WAAW,WAAW;eACvC,MAAM,KAAK,OAAO,KAAK,QAAQ;0BACpB,WAAW,MAAM,KAAK,OAAO,WAAW,WAAW,MAAM,KAAK,OAAO,WAAW,QAAQ;;;;;;+BAMnF,cAAc;;;;gBAI7B,WAAW,gBAAgB,UAAU;;;wBAG7B,WAAW,MAAM,KAAK,OAAO,MAAM,OAAO,MAAM,KAAK,OAAO,QAAQ,KAAK;gCACjE,WAAW,GAAG,MAAM,KAAK,OAAO,MAAM,KAAK,MAAM,GAAG,MAAM,KAAK,OAAO,QAAQ,KAAK,IAAI;;;;;;;;;wBAS/F,WAAW,MAAM,KAAK,OAAO,MAAM,OAAO,MAAM,KAAK,OAAO,QAAQ,KAAK;;;CAI7F,MAAMC,cAAsB,GAAG;sBACb,MAAM,KAAK,OAAO,MAAM,KAAK;;;wBAG3B,MAAM,KAAK,OAAO,MAAM,KAAK;gCACrB,MAAM,KAAK,OAAO,MAAM,KAAK;;;;wBAIrC,MAAM,KAAK,OAAO,MAAM,KAAK;;;CAIjD,MAAMC,iBAAyB,GAAG;0BACZ,MAAM,KAAK,OAAO,WAAW,SAAS;;;;CAK5D,MAAMC,SAAiB,GAAG;sBACR,MAAM,KAAK,QAAQ,KAAK,QAAQ,MAAM,KAAK,QAAQ,KAAK;eAC/D,MAAM,KAAK,OAAO,KAAK,QAAQ;0BACpB,MAAM,KAAK,OAAO,WAAW,QAAQ;;;4BAGnC,MAAM,KAAK,OAAO,OAAO,MAAM;;;;4BAI/B,MAAM,KAAK,OAAO,QAAQ,KAAK;iBAC1C,MAAM,KAAK,OAAO,QAAQ,aAAa;;;CAIpD,MAAMC,SAAiB,GAAG;;yBAEL,MAAM,KAAK,QAAQ,KAAK;mBAC9B,MAAM,KAAK,WAAW,UAAU,GAAG;eACvC,MAAM,KAAK,OAAO,KAAK,UAAU;;;;;;AAY5C,QAAO;EACL,UANuB,GAAG;eACjB,MAAM,KAAK,OAAO,QAAQ,KAAK;qBACzB,MAAM,KAAK,WAAW,YAAY,OAAO;;EAKxD;EACA;EACA;EACA;EACA;EACD;GACA;CAAC;CAAO;CAAa;CAAU;CAAS,CAAC;AAE9C,4BAAe"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const getSelectControl: () => HTMLSelectElement;
|
|
2
|
+
export declare const getAllSelectControls: () => HTMLSelectElement[];
|
|
3
|
+
export declare const readSelectedLabel: (control: HTMLElement) => string;
|
|
4
|
+
export declare const chooseOption: (control: HTMLElement, optionLabel: string) => void;
|
|
5
|
+
export declare const changeEventWith: (value: string) => unknown;
|
|
6
|
+
//# sourceMappingURL=selectTestUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selectTestUtils.d.ts","sourceRoot":"","sources":["../../../../../src/components/primitives/Select/__tests__/selectTestUtils.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,gBAAgB,QAAO,iBAAoE,CAAC;AAEzG,eAAO,MAAM,oBAAoB,QAAO,iBAAiB,EAAwD,CAAC;AAElH,eAAO,MAAM,iBAAiB,GAAI,SAAS,WAAW,KAAG,MACa,CAAC;AAEvE,eAAO,MAAM,YAAY,GAAI,SAAS,WAAW,EAAE,aAAa,MAAM,KAAG,IAUxE,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,OAAO,MAAM,KAAG,OAI/C,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SelectMessages } from '@thunderid/browser';
|
|
2
|
+
export type { SelectMessages };
|
|
3
|
+
/**
|
|
4
|
+
* The wording of `Select`: the active translations when there is an I18n provider, English otherwise, so `Select`
|
|
5
|
+
* works without a provider.
|
|
6
|
+
*/
|
|
7
|
+
declare const useSelectMessages: () => SelectMessages;
|
|
8
|
+
export default useSelectMessages;
|
|
9
|
+
//# sourceMappingURL=useSelectMessages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSelectMessages.d.ts","sourceRoot":"","sources":["../../../../src/components/primitives/Select/useSelectMessages.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAIvD,YAAY,EAAC,cAAc,EAAC,CAAC;AAE7B;;;GAGG;AACH,QAAA,MAAM,iBAAiB,QAAO,cAI7B,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import I18nContext_default from "../../../contexts/I18n/I18nContext.js";
|
|
2
|
+
import { getSelectMessages } from "@thunderid/browser";
|
|
3
|
+
import { useContext, useMemo } from "react";
|
|
4
|
+
|
|
5
|
+
//#region src/components/primitives/Select/useSelectMessages.ts
|
|
6
|
+
/**
|
|
7
|
+
* The wording of `Select`: the active translations when there is an I18n provider, English otherwise, so `Select`
|
|
8
|
+
* works without a provider.
|
|
9
|
+
*/
|
|
10
|
+
const useSelectMessages = () => {
|
|
11
|
+
const i18n = useContext(I18nContext_default);
|
|
12
|
+
return useMemo(() => getSelectMessages(i18n ? (key) => i18n.t(key) : void 0), [i18n]);
|
|
13
|
+
};
|
|
14
|
+
var useSelectMessages_default = useSelectMessages;
|
|
15
|
+
|
|
16
|
+
//#endregion
|
|
17
|
+
export { useSelectMessages_default as default };
|
|
18
|
+
//# sourceMappingURL=useSelectMessages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSelectMessages.js","names":["i18n: ContextType<typeof I18nContext>","I18nContext"],"sources":["../../../../src/components/primitives/Select/useSelectMessages.ts"],"sourcesContent":["// Copyright 2026 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {getSelectMessages} from '@thunderid/browser';\nimport type {SelectMessages} from '@thunderid/browser';\nimport {ContextType, useContext, useMemo} from 'react';\nimport I18nContext from '../../../contexts/I18n/I18nContext';\n\nexport type {SelectMessages};\n\n/**\n * The wording of `Select`: the active translations when there is an I18n provider, English otherwise, so `Select`\n * works without a provider.\n */\nconst useSelectMessages = (): SelectMessages => {\n const i18n: ContextType<typeof I18nContext> = useContext(I18nContext);\n\n return useMemo((): SelectMessages => getSelectMessages(i18n ? (key: string) => i18n.t(key) : undefined), [i18n]);\n};\n\nexport default useSelectMessages;\n"],"mappings":";;;;;;;;;AAcA,MAAM,0BAA0C;CAC9C,MAAMA,OAAwC,WAAWC,oBAAY;AAErE,QAAO,cAA8B,kBAAkB,QAAQ,QAAgB,KAAK,EAAE,IAAI,GAAG,OAAU,EAAE,CAAC,KAAK,CAAC;;AAGlH,gCAAe"}
|
|
@@ -8,10 +8,18 @@ export interface TextFieldProps extends Omit<InputHTMLAttributes<HTMLInputElemen
|
|
|
8
8
|
* Whether the field is disabled
|
|
9
9
|
*/
|
|
10
10
|
disabled?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Extra content shown inside the field, at its end after the end icon, such as an action button
|
|
13
|
+
*/
|
|
14
|
+
endAdornment?: ReactNode;
|
|
11
15
|
/**
|
|
12
16
|
* Icon to display at the end (right) of the input
|
|
13
17
|
*/
|
|
14
18
|
endIcon?: ReactNode;
|
|
19
|
+
/**
|
|
20
|
+
* Accessible name of the end icon when it is a button, that is when `onEndIconClick` is set
|
|
21
|
+
*/
|
|
22
|
+
endIconLabel?: string;
|
|
15
23
|
/**
|
|
16
24
|
* Error message to display below the input
|
|
17
25
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextField.d.ts","sourceRoot":"","sources":["../../../../src/components/primitives/TextField/TextField.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAC,EAAE,EAAE,mBAAmB,
|
|
1
|
+
{"version":3,"file":"TextField.d.ts","sourceRoot":"","sources":["../../../../src/components/primitives/TextField/TextField.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAC,EAAE,EAAE,mBAAmB,EAAiB,SAAS,EAAQ,MAAM,OAAO,CAAC;AAO/E,MAAM,WAAW,cAAe,SAAQ,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,WAAW,CAAC;IAC9F;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB;;OAEG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAcD,QAAA,MAAM,SAAS,EAAE,EAAE,CAAC,cAAc,CA8GjC,CAAC;AAEF,eAAe,SAAS,CAAC"}
|
|
@@ -4,23 +4,40 @@ import FormControl_default from "../FormControl/FormControl.js";
|
|
|
4
4
|
import InputLabel_default from "../InputLabel/InputLabel.js";
|
|
5
5
|
import TextField_styles_default from "./TextField.styles.js";
|
|
6
6
|
import { bem, withVendorCSSClassPrefix } from "@thunderid/browser";
|
|
7
|
+
import { useId } from "react";
|
|
7
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
9
|
|
|
9
10
|
//#region src/components/primitives/TextField/TextField.tsx
|
|
10
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Makes an element with `role="button"` respond to Enter and Space, as a native button does.
|
|
13
|
+
*/
|
|
14
|
+
const activateOnKey = (activate) => (event) => {
|
|
15
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
16
|
+
event.preventDefault();
|
|
17
|
+
activate();
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
const TextField = ({ label, error, required, className, disabled, helperText, startIcon, endAdornment, endIcon, endIconLabel = "End icon", onStartIconClick, onEndIconClick, type = "text", style = {},...rest }) => {
|
|
11
21
|
const { theme, colorScheme } = useTheme_default();
|
|
22
|
+
const generatedId = useId();
|
|
23
|
+
const inputId = rest.id ?? generatedId;
|
|
24
|
+
const helperTextId = error || helperText ? `${inputId}-helper-text` : void 0;
|
|
25
|
+
const describedBy = [rest["aria-describedby"], helperTextId].filter(Boolean).join(" ") || void 0;
|
|
12
26
|
const hasError = !!error;
|
|
13
|
-
const styles = TextField_styles_default(theme, colorScheme, disabled ?? false, hasError, !!startIcon, !!endIcon);
|
|
27
|
+
const styles = TextField_styles_default(theme, colorScheme, disabled ?? false, hasError, !!startIcon, !!endIcon, !!endAdornment);
|
|
14
28
|
const inputClassName = cx(withVendorCSSClassPrefix(bem("text-field", "input")), styles["input"], hasError && styles["inputError"], disabled && styles["inputDisabled"]);
|
|
15
29
|
const containerClassName = cx(withVendorCSSClassPrefix(bem("text-field", "container")), styles["inputContainer"]);
|
|
16
30
|
const startIconClassName = cx(withVendorCSSClassPrefix(bem("text-field", "start-icon")), styles["startIcon"]);
|
|
17
31
|
const endIconClassName = cx(withVendorCSSClassPrefix(bem("text-field", "end-icon")), styles["endIcon"]);
|
|
32
|
+
const endAdornmentClassName = cx(withVendorCSSClassPrefix(bem("text-field", "end-adornment")), styles["endAdornment"]);
|
|
18
33
|
return /* @__PURE__ */ jsxs(FormControl_default, {
|
|
19
34
|
error,
|
|
20
35
|
helperText,
|
|
36
|
+
helperTextId,
|
|
21
37
|
className: cx(withVendorCSSClassPrefix(bem("text-field")), className),
|
|
22
38
|
style,
|
|
23
39
|
children: [label && /* @__PURE__ */ jsx(InputLabel_default, {
|
|
40
|
+
htmlFor: inputId,
|
|
24
41
|
required,
|
|
25
42
|
error: hasError,
|
|
26
43
|
children: label
|
|
@@ -32,6 +49,7 @@ const TextField = ({ label, error, required, className, disabled, helperText, st
|
|
|
32
49
|
onClick: onStartIconClick,
|
|
33
50
|
role: onStartIconClick ? "button" : void 0,
|
|
34
51
|
tabIndex: onStartIconClick && !disabled ? 0 : void 0,
|
|
52
|
+
onKeyDown: onStartIconClick && !disabled ? activateOnKey(onStartIconClick) : void 0,
|
|
35
53
|
"aria-label": "Start icon",
|
|
36
54
|
children: startIcon
|
|
37
55
|
}),
|
|
@@ -41,15 +59,22 @@ const TextField = ({ label, error, required, className, disabled, helperText, st
|
|
|
41
59
|
disabled,
|
|
42
60
|
"aria-invalid": hasError,
|
|
43
61
|
"aria-required": required,
|
|
44
|
-
...rest
|
|
62
|
+
...rest,
|
|
63
|
+
id: inputId,
|
|
64
|
+
"aria-describedby": describedBy
|
|
45
65
|
}),
|
|
46
66
|
endIcon && /* @__PURE__ */ jsx("div", {
|
|
47
67
|
className: endIconClassName,
|
|
48
68
|
onClick: onEndIconClick,
|
|
49
69
|
role: onEndIconClick ? "button" : void 0,
|
|
50
70
|
tabIndex: onEndIconClick && !disabled ? 0 : void 0,
|
|
51
|
-
|
|
71
|
+
onKeyDown: onEndIconClick && !disabled ? activateOnKey(onEndIconClick) : void 0,
|
|
72
|
+
"aria-label": endIconLabel,
|
|
52
73
|
children: endIcon
|
|
74
|
+
}),
|
|
75
|
+
endAdornment && /* @__PURE__ */ jsx("div", {
|
|
76
|
+
className: endAdornmentClassName,
|
|
77
|
+
children: endAdornment
|
|
53
78
|
})
|
|
54
79
|
]
|
|
55
80
|
})]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextField.js","names":["TextField: FC<TextFieldProps>","useTheme","styles: Record<string, string>","useStyles","inputClassName: string","containerClassName: string","startIconClassName: string","endIconClassName: 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, 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 * Icon to display at the end (right) of the input\n */\n endIcon?: ReactNode;\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\nconst TextField: FC<TextFieldProps> = ({\n label,\n error,\n required,\n className,\n disabled,\n helperText,\n startIcon,\n endIcon,\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 styles: Record<string, string> = useStyles(\n theme,\n colorScheme,\n disabled ?? false,\n hasError,\n hasStartIcon,\n hasEndIcon,\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 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 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 aria-label
|
|
1
|
+
{"version":3,"file":"TextField.js","names":["TextField: FC<TextFieldProps>","useTheme","generatedId: string","inputId: string","helperTextId: string | undefined","describedBy: string | undefined","styles: Record<string, string>","useStyles","inputClassName: string","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,kBAAU;CACpE,MAAMC,cAAsB,OAAO;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,yBACrC,OACA,aACA,YAAY,OACZ,UAPmB,CAAC,CAAC,WACJ,CAAC,CAAC,SACG,CAAC,CAAC,aASzB;CAED,MAAMC,iBAAyB,GAC7B,yBAAyB,IAAI,cAAc,QAAQ,CAAC,EACpD,OAAO,UACP,YAAY,OAAO,eACnB,YAAY,OAAO,iBACpB;CAED,MAAMC,qBAA6B,GACjC,yBAAyB,IAAI,cAAc,YAAY,CAAC,EACxD,OAAO,kBACR;CAED,MAAMC,qBAA6B,GAAG,yBAAyB,IAAI,cAAc,aAAa,CAAC,EAAE,OAAO,aAAa;CAErH,MAAMC,mBAA2B,GAAG,yBAAyB,IAAI,cAAc,WAAW,CAAC,EAAE,OAAO,WAAW;CAE/G,MAAMC,wBAAgC,GACpC,yBAAyB,IAAI,cAAc,gBAAgB,CAAC,EAC5D,OAAO,gBACR;AAED,QACE,qBAACC;EACQ;EACK;EACE;EACd,WAAW,GAAG,yBAAyB,IAAI,aAAa,CAAC,EAAE,UAAU;EAC9D;aAEN,SACC,oBAACC;GAAW,SAAS;GAAmB;GAAU,OAAO;aACtD;IACU,EAEf,qBAAC;GAAI,WAAW;;IACb,aACC,oBAAC;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,oBAAC;KACC,WAAW;KACL;KACI;KACV,gBAAc;KACd,iBAAe;KACf,GAAI;KACJ,IAAI;KACJ,oBAAkB;MAClB;IACD,WACC,oBAAC;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,oBAAC;KAAI,WAAW;eAAwB;MAAmB;;IACxE;GACM;;AAIlB,wBAAe"}
|
|
@@ -7,8 +7,9 @@ import { Theme } from '@thunderid/browser';
|
|
|
7
7
|
* @param hasError - Whether the component has an error
|
|
8
8
|
* @param hasStartIcon - Whether the component has a start icon
|
|
9
9
|
* @param hasEndIcon - Whether the component has an end icon
|
|
10
|
+
* @param hasEndAdornment - Whether the component has extra content before the end icon
|
|
10
11
|
* @returns Object containing CSS class names for component styling
|
|
11
12
|
*/
|
|
12
|
-
declare const useStyles: (theme: Theme, colorScheme: string, disabled: boolean, hasError: boolean, hasStartIcon: boolean, hasEndIcon: boolean) => Record<string, string>;
|
|
13
|
+
declare const useStyles: (theme: Theme, colorScheme: string, disabled: boolean, hasError: boolean, hasStartIcon: boolean, hasEndIcon: boolean, hasEndAdornment?: boolean) => Record<string, string>;
|
|
13
14
|
export default useStyles;
|
|
14
15
|
//# sourceMappingURL=TextField.styles.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextField.styles.d.ts","sourceRoot":"","sources":["../../../../src/components/primitives/TextField/TextField.styles.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,KAAK,EAAC,MAAM,oBAAoB,CAAC;AAIzC
|
|
1
|
+
{"version":3,"file":"TextField.styles.d.ts","sourceRoot":"","sources":["../../../../src/components/primitives/TextField/TextField.styles.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,KAAK,EAAC,MAAM,oBAAoB,CAAC;AAIzC;;;;;;;;;;GAUG;AACH,QAAA,MAAM,SAAS,GACb,OAAO,KAAK,EACZ,aAAa,MAAM,EACnB,UAAU,OAAO,EACjB,UAAU,OAAO,EACjB,cAAc,OAAO,EACrB,YAAY,OAAO,EACnB,yBAAuB,KACtB,MAAM,CAAC,MAAM,EAAE,MAAM,CAkIiE,CAAC;AAE1F,eAAe,SAAS,CAAC"}
|
|
@@ -10,11 +10,14 @@ import { useMemo } from "react";
|
|
|
10
10
|
* @param hasError - Whether the component has an error
|
|
11
11
|
* @param hasStartIcon - Whether the component has a start icon
|
|
12
12
|
* @param hasEndIcon - Whether the component has an end icon
|
|
13
|
+
* @param hasEndAdornment - Whether the component has extra content before the end icon
|
|
13
14
|
* @returns Object containing CSS class names for component styling
|
|
14
15
|
*/
|
|
15
|
-
const useStyles = (theme, colorScheme, disabled, hasError, hasStartIcon, hasEndIcon) => useMemo(() => {
|
|
16
|
+
const useStyles = (theme, colorScheme, disabled, hasError, hasStartIcon, hasEndIcon, hasEndAdornment = false) => useMemo(() => {
|
|
16
17
|
const inlineStartPadding = hasStartIcon ? `calc(${theme.vars.spacing.unit} * 5)` : `calc(${theme.vars.spacing.unit} * 1.5)`;
|
|
17
|
-
|
|
18
|
+
let inlineEndPadding = `calc(${theme.vars.spacing.unit} * 1.5)`;
|
|
19
|
+
if (hasEndIcon && hasEndAdornment) inlineEndPadding = `calc(${theme.vars.spacing.unit} * 8)`;
|
|
20
|
+
else if (hasEndIcon || hasEndAdornment) inlineEndPadding = `calc(${theme.vars.spacing.unit} * 5)`;
|
|
18
21
|
const inputContainer = css`
|
|
19
22
|
position: relative;
|
|
20
23
|
display: flex;
|
|
@@ -102,11 +105,20 @@ const useStyles = (theme, colorScheme, disabled, hasError, hasStartIcon, hasEndI
|
|
|
102
105
|
${icon};
|
|
103
106
|
inset-inline-start: ${theme.vars.spacing.unit};
|
|
104
107
|
`;
|
|
105
|
-
|
|
106
|
-
endIcon: css`
|
|
108
|
+
const endIcon = css`
|
|
107
109
|
${icon};
|
|
110
|
+
inset-inline-end: calc(${theme.vars.spacing.unit} * ${hasEndAdornment ? 4.5 : 1});
|
|
111
|
+
`;
|
|
112
|
+
return {
|
|
113
|
+
endAdornment: css`
|
|
114
|
+
position: absolute;
|
|
115
|
+
display: flex;
|
|
116
|
+
align-items: center;
|
|
117
|
+
top: 50%;
|
|
118
|
+
transform: translateY(-50%);
|
|
108
119
|
inset-inline-end: ${theme.vars.spacing.unit};
|
|
109
120
|
`,
|
|
121
|
+
endIcon,
|
|
110
122
|
icon,
|
|
111
123
|
input,
|
|
112
124
|
inputContainer,
|
|
@@ -120,7 +132,8 @@ const useStyles = (theme, colorScheme, disabled, hasError, hasStartIcon, hasEndI
|
|
|
120
132
|
disabled,
|
|
121
133
|
hasError,
|
|
122
134
|
hasStartIcon,
|
|
123
|
-
hasEndIcon
|
|
135
|
+
hasEndIcon,
|
|
136
|
+
hasEndAdornment
|
|
124
137
|
]);
|
|
125
138
|
var TextField_styles_default = useStyles;
|
|
126
139
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextField.styles.js","names":["inlineStartPadding: string","
|
|
1
|
+
{"version":3,"file":"TextField.styles.js","names":["inlineStartPadding: string","inputContainer: string","input: string","inputError: string","inputDisabled: string","icon: string","startIcon: string","endIcon: string"],"sources":["../../../../src/components/primitives/TextField/TextField.styles.ts"],"sourcesContent":["// Copyright 2024 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {Theme} from '@thunderid/browser';\nimport {useMemo} from 'react';\nimport {css} from '../../../styles/emotion';\n\n/**\n * Creates styles for the TextField component using BEM methodology\n * @param theme - The theme object containing design tokens\n * @param colorScheme - The current color scheme (used for memoization)\n * @param disabled - Whether the component is disabled\n * @param hasError - Whether the component has an error\n * @param hasStartIcon - Whether the component has a start icon\n * @param hasEndIcon - Whether the component has an end icon\n * @param hasEndAdornment - Whether the component has extra content before the end icon\n * @returns Object containing CSS class names for component styling\n */\nconst useStyles = (\n theme: Theme,\n colorScheme: string,\n disabled: boolean,\n hasError: boolean,\n hasStartIcon: boolean,\n hasEndIcon: boolean,\n hasEndAdornment = false,\n): Record<string, string> =>\n useMemo(() => {\n const inlineStartPadding: string = hasStartIcon\n ? `calc(${theme.vars.spacing.unit} * 5)`\n : `calc(${theme.vars.spacing.unit} * 1.5)`;\n let inlineEndPadding = `calc(${theme.vars.spacing.unit} * 1.5)`;\n\n if (hasEndIcon && hasEndAdornment) {\n inlineEndPadding = `calc(${theme.vars.spacing.unit} * 8)`;\n } else if (hasEndIcon || hasEndAdornment) {\n inlineEndPadding = `calc(${theme.vars.spacing.unit} * 5)`;\n }\n\n const inputContainer: string = css`\n position: relative;\n display: flex;\n align-items: center;\n `;\n\n const input: string = css`\n width: 100%;\n padding-block: ${theme.vars.spacing.unit};\n padding-inline-start: ${inlineStartPadding};\n padding-inline-end: ${inlineEndPadding};\n border: 1px solid ${hasError ? theme.vars.colors.error.main : theme.vars.colors.border};\n border-radius: ${theme.vars.components?.Field?.root?.borderRadius || theme.vars.borderRadius.medium};\n font-size: ${theme.vars.typography.fontSizes.md};\n font-family: ${theme.vars.typography.fontFamily};\n color: ${theme.vars.colors.text.primary};\n background-color: ${disabled ? theme.vars.colors.background.disabled : theme.vars.colors.background.surface};\n outline: none;\n transition:\n border-color 0.2s ease,\n box-shadow 0.2s ease;\n\n &:focus {\n border-color: ${hasError ? theme.vars.colors.error.main : theme.vars.colors.primary.main};\n box-shadow: 0 0 0 2px ${hasError ? `${theme.vars.colors.error.main}20` : `${theme.vars.colors.primary.main}20`};\n }\n\n &:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n &:hover:not(:disabled) {\n border-color: ${hasError ? theme.vars.colors.error.main : theme.vars.colors.primary.main};\n }\n\n &::placeholder {\n color: ${theme.vars.colors.text.secondary};\n opacity: 0.7;\n }\n `;\n\n const inputError: string = css`\n border-color: ${theme.vars.colors.error.main};\n\n &:focus {\n border-color: ${theme.vars.colors.error.main};\n box-shadow: 0 0 0 2px ${theme.vars.colors.error.main}20;\n }\n\n &:hover:not(:disabled) {\n border-color: ${theme.vars.colors.error.main};\n }\n `;\n\n const inputDisabled: string = css`\n background-color: ${theme.vars.colors.background.disabled};\n opacity: 0.6;\n cursor: not-allowed;\n `;\n\n const icon: string = css`\n position: absolute;\n background: none;\n border: none;\n cursor: ${disabled ? 'not-allowed' : 'pointer'};\n padding: calc(${theme.vars.spacing.unit} / 2);\n display: flex;\n align-items: center;\n justify-content: center;\n color: ${theme.vars.colors.text.secondary};\n opacity: ${disabled ? 0.5 : 1};\n top: 50%;\n transform: translateY(-50%);\n transition:\n color 0.2s ease,\n opacity 0.2s ease;\n\n &:hover:not(:disabled) {\n color: ${theme.vars.colors.text.primary};\n }\n\n &:focus {\n outline: 2px solid ${theme.vars.colors.primary.main};\n outline-offset: 2px;\n }\n `;\n\n const startIcon: string = css`\n ${icon};\n inset-inline-start: ${theme.vars.spacing.unit};\n `;\n\n const endIcon: string = css`\n ${icon};\n inset-inline-end: calc(${theme.vars.spacing.unit} * ${hasEndAdornment ? 4.5 : 1});\n `;\n\n const endAdornment: string = css`\n position: absolute;\n display: flex;\n align-items: center;\n top: 50%;\n transform: translateY(-50%);\n inset-inline-end: ${theme.vars.spacing.unit};\n `;\n\n return {\n endAdornment,\n endIcon,\n icon,\n input,\n inputContainer,\n inputDisabled,\n inputError,\n startIcon,\n };\n }, [theme, colorScheme, disabled, hasError, hasStartIcon, hasEndIcon, hasEndAdornment]);\n\nexport default useStyles;\n"],"mappings":";;;;;;;;;;;;;;;AAkBA,MAAM,aACJ,OACA,aACA,UACA,UACA,cACA,YACA,kBAAkB,UAElB,cAAc;CACZ,MAAMA,qBAA6B,eAC/B,QAAQ,MAAM,KAAK,QAAQ,KAAK,SAChC,QAAQ,MAAM,KAAK,QAAQ,KAAK;CACpC,IAAI,mBAAmB,QAAQ,MAAM,KAAK,QAAQ,KAAK;AAEvD,KAAI,cAAc,gBAChB,oBAAmB,QAAQ,MAAM,KAAK,QAAQ,KAAK;UAC1C,cAAc,gBACvB,oBAAmB,QAAQ,MAAM,KAAK,QAAQ,KAAK;CAGrD,MAAMC,iBAAyB,GAAG;;;;;CAMlC,MAAMC,QAAgB,GAAG;;uBAEN,MAAM,KAAK,QAAQ,KAAK;8BACjB,mBAAmB;4BACrB,iBAAiB;0BACnB,WAAW,MAAM,KAAK,OAAO,MAAM,OAAO,MAAM,KAAK,OAAO,OAAO;uBACtE,MAAM,KAAK,YAAY,OAAO,MAAM,gBAAgB,MAAM,KAAK,aAAa,OAAO;mBACvF,MAAM,KAAK,WAAW,UAAU,GAAG;qBACjC,MAAM,KAAK,WAAW,WAAW;eACvC,MAAM,KAAK,OAAO,KAAK,QAAQ;0BACpB,WAAW,MAAM,KAAK,OAAO,WAAW,WAAW,MAAM,KAAK,OAAO,WAAW,QAAQ;;;;;;;wBAO1F,WAAW,MAAM,KAAK,OAAO,MAAM,OAAO,MAAM,KAAK,OAAO,QAAQ,KAAK;gCACjE,WAAW,GAAG,MAAM,KAAK,OAAO,MAAM,KAAK,MAAM,GAAG,MAAM,KAAK,OAAO,QAAQ,KAAK,IAAI;;;;;;;;;wBAS/F,WAAW,MAAM,KAAK,OAAO,MAAM,OAAO,MAAM,KAAK,OAAO,QAAQ,KAAK;;;;iBAIhF,MAAM,KAAK,OAAO,KAAK,UAAU;;;;CAK9C,MAAMC,aAAqB,GAAG;sBACZ,MAAM,KAAK,OAAO,MAAM,KAAK;;;wBAG3B,MAAM,KAAK,OAAO,MAAM,KAAK;gCACrB,MAAM,KAAK,OAAO,MAAM,KAAK;;;;wBAIrC,MAAM,KAAK,OAAO,MAAM,KAAK;;;CAIjD,MAAMC,gBAAwB,GAAG;0BACX,MAAM,KAAK,OAAO,WAAW,SAAS;;;;CAK5D,MAAMC,OAAe,GAAG;;;;gBAIZ,WAAW,gBAAgB,UAAU;sBAC/B,MAAM,KAAK,QAAQ,KAAK;;;;eAI/B,MAAM,KAAK,OAAO,KAAK,UAAU;iBAC/B,WAAW,KAAM,EAAE;;;;;;;;iBAQnB,MAAM,KAAK,OAAO,KAAK,QAAQ;;;;6BAInB,MAAM,KAAK,OAAO,QAAQ,KAAK;;;;CAKxD,MAAMC,YAAoB,GAAG;QACzB,KAAK;4BACe,MAAM,KAAK,QAAQ,KAAK;;CAGhD,MAAMC,UAAkB,GAAG;QACvB,KAAK;+BACkB,MAAM,KAAK,QAAQ,KAAK,KAAK,kBAAkB,MAAM,EAAE;;AAYlF,QAAO;EACL,cAV2B,GAAG;;;;;;0BAMV,MAAM,KAAK,QAAQ,KAAK;;EAK5C;EACA;EACA;EACA;EACA;EACA;EACA;EACD;GACA;CAAC;CAAO;CAAa;CAAU;CAAU;CAAc;CAAY;CAAgB,CAAC;AAEzF,+BAAe"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Toggle.d.ts","sourceRoot":"","sources":["../../../../src/components/primitives/Toggle/Toggle.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAC,EAAE,EAAE,mBAAmB,
|
|
1
|
+
{"version":3,"file":"Toggle.d.ts","sourceRoot":"","sources":["../../../../src/components/primitives/Toggle/Toggle.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAC,EAAE,EAAE,mBAAmB,EAAQ,MAAM,OAAO,CAAC;AAOrD;;GAEG;AACH,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC;IACpG,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,QAAA,MAAM,MAAM,EAAE,EAAE,CAAC,WAAW,CA+C3B,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
|
@@ -4,6 +4,7 @@ import Toggle_styles_default from "./Toggle.styles.js";
|
|
|
4
4
|
import FormControl_default from "../FormControl/FormControl.js";
|
|
5
5
|
import InputLabel_default from "../InputLabel/InputLabel.js";
|
|
6
6
|
import { bem, withVendorCSSClassPrefix } from "@thunderid/browser";
|
|
7
|
+
import { useId } from "react";
|
|
7
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
9
|
|
|
9
10
|
//#region src/components/primitives/Toggle/Toggle.tsx
|
|
@@ -20,10 +21,15 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
20
21
|
const Toggle = ({ label, error, className, required, helperText, style = {},...rest }) => {
|
|
21
22
|
const { theme, colorScheme } = useTheme_default();
|
|
22
23
|
const hasError = !!error;
|
|
24
|
+
const generatedId = useId();
|
|
25
|
+
const inputId = rest.id ?? generatedId;
|
|
26
|
+
const helperTextId = error || helperText ? `${inputId}-helper-text` : void 0;
|
|
27
|
+
const describedBy = [rest["aria-describedby"], helperTextId].filter(Boolean).join(" ") || void 0;
|
|
23
28
|
const styles = Toggle_styles_default(theme, colorScheme, hasError, !!required);
|
|
24
29
|
return /* @__PURE__ */ jsx(FormControl_default, {
|
|
25
30
|
error,
|
|
26
31
|
helperText,
|
|
32
|
+
helperTextId,
|
|
27
33
|
className: cx(withVendorCSSClassPrefix(bem("toggle")), className),
|
|
28
34
|
helperTextMarginLeft: `calc(${theme.vars.spacing.unit} * 5.5)`,
|
|
29
35
|
children: /* @__PURE__ */ jsxs("label", {
|
|
@@ -36,7 +42,9 @@ const Toggle = ({ label, error, className, required, helperText, style = {},...r
|
|
|
36
42
|
className: cx(withVendorCSSClassPrefix(bem("toggle", "input")), styles["input"]),
|
|
37
43
|
"aria-invalid": hasError,
|
|
38
44
|
"aria-required": required,
|
|
39
|
-
...rest
|
|
45
|
+
...rest,
|
|
46
|
+
id: inputId,
|
|
47
|
+
"aria-describedby": describedBy
|
|
40
48
|
}),
|
|
41
49
|
/* @__PURE__ */ jsx("div", {
|
|
42
50
|
className: cx(withVendorCSSClassPrefix(bem("toggle", "track")), styles["track"]),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Toggle.js","names":["Toggle: FC<ToggleProps>","useTheme","styles: Record<string, string>","useStyles","FormControl","InputLabel"],"sources":["../../../../src/components/primitives/Toggle/Toggle.tsx"],"sourcesContent":["// Copyright 2026 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 './Toggle.styles';\nimport useTheme from '../../../contexts/Theme/useTheme';\nimport {cx} from '../../../styles/emotion';\nimport FormControl from '../FormControl/FormControl';\nimport InputLabel from '../InputLabel/InputLabel';\n\n/**\n * Props for the Toggle component.\n */\nexport interface ToggleProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'className' | 'type'> {\n className?: string;\n error?: string;\n helperText?: string;\n label?: string;\n required?: boolean;\n}\n\n/**\n * A Toggle component that represents a boolean input. It is built on top of a hidden checkbox input\n * and styled to look like a switch.\n *\n * The component is wrapped in a FormControl to display error messages and helper text.\n * The label is associated with the input for accessibility.\n *\n * @param props - Props for the Toggle component\n * @returns A JSX element representing the Toggle\n */\nconst Toggle: FC<ToggleProps> = ({label, error, className, required, helperText, style = {}, ...rest}: ToggleProps) => {\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('toggle')), className)}\n helperTextMarginLeft={`calc(${theme.vars.spacing.unit} * 5.5)`}\n >\n <label style={style} className={cx(withVendorCSSClassPrefix(bem('toggle', 'container')), styles['container'])}>\n <input\n type=\"checkbox\"\n role=\"switch\"\n className={cx(withVendorCSSClassPrefix(bem('toggle', 'input')), styles['input'])}\n aria-invalid={hasError}\n aria-required={required}\n {...rest}\n />\n <div className={cx(withVendorCSSClassPrefix(bem('toggle', 'track')), styles['track'])}>\n <span className={cx(withVendorCSSClassPrefix(bem('toggle', 'thumb')), styles['thumb'])} />\n </div>\n {label && (\n <InputLabel\n required={required}\n error={hasError}\n variant=\"inline\"\n className={cx(withVendorCSSClassPrefix(bem('toggle', 'label')), styles['label'], styles['errorLabel'], {\n [withVendorCSSClassPrefix(bem('toggle', 'label', 'error'))]: hasError,\n })}\n >\n {label}\n </InputLabel>\n )}\n </label>\n </FormControl>\n );\n};\n\nexport default Toggle;\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"Toggle.js","names":["Toggle: FC<ToggleProps>","useTheme","generatedId: string","inputId: string","helperTextId: string | undefined","describedBy: string | undefined","styles: Record<string, string>","useStyles","FormControl","InputLabel"],"sources":["../../../../src/components/primitives/Toggle/Toggle.tsx"],"sourcesContent":["// Copyright 2026 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 './Toggle.styles';\nimport useTheme from '../../../contexts/Theme/useTheme';\nimport {cx} from '../../../styles/emotion';\nimport FormControl from '../FormControl/FormControl';\nimport InputLabel from '../InputLabel/InputLabel';\n\n/**\n * Props for the Toggle component.\n */\nexport interface ToggleProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'className' | 'type'> {\n className?: string;\n error?: string;\n helperText?: string;\n label?: string;\n required?: boolean;\n}\n\n/**\n * A Toggle component that represents a boolean input. It is built on top of a hidden checkbox input\n * and styled to look like a switch.\n *\n * The component is wrapped in a FormControl to display error messages and helper text.\n * The label is associated with the input for accessibility.\n *\n * @param props - Props for the Toggle component\n * @returns A JSX element representing the Toggle\n */\nconst Toggle: FC<ToggleProps> = ({label, error, className, required, helperText, style = {}, ...rest}: ToggleProps) => {\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('toggle')), className)}\n helperTextMarginLeft={`calc(${theme.vars.spacing.unit} * 5.5)`}\n >\n <label style={style} className={cx(withVendorCSSClassPrefix(bem('toggle', 'container')), styles['container'])}>\n <input\n type=\"checkbox\"\n role=\"switch\"\n className={cx(withVendorCSSClassPrefix(bem('toggle', 'input')), styles['input'])}\n aria-invalid={hasError}\n aria-required={required}\n {...rest}\n id={inputId}\n aria-describedby={describedBy}\n />\n <div className={cx(withVendorCSSClassPrefix(bem('toggle', 'track')), styles['track'])}>\n <span className={cx(withVendorCSSClassPrefix(bem('toggle', 'thumb')), styles['thumb'])} />\n </div>\n {label && (\n <InputLabel\n required={required}\n error={hasError}\n variant=\"inline\"\n className={cx(withVendorCSSClassPrefix(bem('toggle', 'label')), styles['label'], styles['errorLabel'], {\n [withVendorCSSClassPrefix(bem('toggle', 'label', 'error'))]: hasError,\n })}\n >\n {label}\n </InputLabel>\n )}\n </label>\n </FormControl>\n );\n};\n\nexport default Toggle;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAgCA,MAAMA,UAA2B,EAAC,OAAO,OAAO,WAAW,UAAU,YAAY,QAAQ,EAAE,CAAE,GAAG,WAAuB;CACrH,MAAM,EAAC,OAAO,gBAA4CC,kBAAU;CACpE,MAAM,WAAW,CAAC,CAAC;CACnB,MAAMC,cAAsB,OAAO;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,sBAAU,OAAO,aAAa,UAAU,CAAC,CAAC,SAAS;AAE1F,QACE,oBAACC;EACQ;EACK;EACE;EACd,WAAW,GAAG,yBAAyB,IAAI,SAAS,CAAC,EAAE,UAAU;EACjE,sBAAsB,QAAQ,MAAM,KAAK,QAAQ,KAAK;YAEtD,qBAAC;GAAa;GAAO,WAAW,GAAG,yBAAyB,IAAI,UAAU,YAAY,CAAC,EAAE,OAAO,aAAa;;IAC3G,oBAAC;KACC,MAAK;KACL,MAAK;KACL,WAAW,GAAG,yBAAyB,IAAI,UAAU,QAAQ,CAAC,EAAE,OAAO,SAAS;KAChF,gBAAc;KACd,iBAAe;KACf,GAAI;KACJ,IAAI;KACJ,oBAAkB;MAClB;IACF,oBAAC;KAAI,WAAW,GAAG,yBAAyB,IAAI,UAAU,QAAQ,CAAC,EAAE,OAAO,SAAS;eACnF,oBAAC,UAAK,WAAW,GAAG,yBAAyB,IAAI,UAAU,QAAQ,CAAC,EAAE,OAAO,SAAS,GAAI;MACtF;IACL,SACC,oBAACC;KACW;KACV,OAAO;KACP,SAAQ;KACR,WAAW,GAAG,yBAAyB,IAAI,UAAU,QAAQ,CAAC,EAAE,OAAO,UAAU,OAAO,eAAe,GACpG,yBAAyB,IAAI,UAAU,SAAS,QAAQ,CAAC,GAAG,UAC9D,CAAC;eAED;MACU;;IAET;GACI;;AAIlB,qBAAe"}
|