@mrshmllw/smores-react 16.4.0 → 16.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/Dropdown/Dropdown.d.ts +5 -1
- package/dist/Dropdown/Dropdown.js +57 -48
- package/dist/Dropdown/Dropdown.js.map +1 -1
- package/dist/fields/Field/Field.d.ts +2 -0
- package/dist/fields/Field/Field.js.map +1 -1
- package/dist/fields/components/InternalField.d.ts +5 -1
- package/dist/fields/components/InternalField.js +8 -3
- package/dist/fields/components/InternalField.js.map +1 -1
- package/dist/fields/components/fieldStyles.d.ts +17 -0
- package/dist/fields/components/fieldStyles.js +39 -0
- package/dist/fields/components/fieldStyles.js.map +1 -0
- package/package.json +1 -1
|
@@ -18,7 +18,11 @@ export interface Props extends CommonFieldProps {
|
|
|
18
18
|
frontIcon?: Icons;
|
|
19
19
|
iconComponent?: ReactNode;
|
|
20
20
|
fallbackStyle?: boolean;
|
|
21
|
-
|
|
21
|
+
/** Accessible name, for when there is no visible `label` */
|
|
22
|
+
'aria-label'?: string;
|
|
23
|
+
/** Lets the browser autofill the field, e.g. `honorific-prefix` */
|
|
24
|
+
autoComplete?: string;
|
|
25
|
+
onSelect?: (element: string) => void;
|
|
22
26
|
onBlur?: (e: FocusEvent<HTMLSelectElement>) => void;
|
|
23
27
|
}
|
|
24
28
|
type TruncateProps = {
|
|
@@ -4,18 +4,21 @@ import styled, { css, useTheme } from 'styled-components';
|
|
|
4
4
|
import { Box } from '../Box';
|
|
5
5
|
import { Field } from '../fields/Field';
|
|
6
6
|
import { InputLeadingIconContainer, StyledFrontIcon, } from '../fields/components/CommonInput';
|
|
7
|
+
import { fieldDisabledStyle, fieldFocusAttrs, fieldFocusRing, } from '../fields/components/fieldStyles';
|
|
7
8
|
import { useUniqueId } from '../utils/id';
|
|
8
9
|
import { useControllableState } from '../utils/useControlledState';
|
|
9
10
|
import { IconContainer } from '../sharedStyles/shared.styles';
|
|
10
|
-
import { faChevronDown } from '@awesome.me/kit-46ca99185c/icons/classic/
|
|
11
|
+
import { faChevronDown } from '@awesome.me/kit-46ca99185c/icons/classic/solid';
|
|
11
12
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
12
|
-
export const Dropdown = forwardRef(function Dropdown({ id: idProp, placeholder, showDefaultOption = false, customDefaultOption, name, value: valueProp, defaultValue, disabled = false, list, onSelect, error = false, onInputChange, onBlur, frontIcon, iconComponent, fallbackStyle, ...fieldProps }, ref) {
|
|
13
|
+
export const Dropdown = forwardRef(function Dropdown({ id: idProp, placeholder, showDefaultOption = false, customDefaultOption, name, value: valueProp, defaultValue, disabled = false, list, onSelect, error = false, onInputChange, onBlur, frontIcon, iconComponent, fallbackStyle, required, 'aria-label': ariaLabel, autoComplete, ...fieldProps }, ref) {
|
|
13
14
|
const theme = useTheme();
|
|
14
15
|
const [value, setValue] = useControllableState({
|
|
15
16
|
initialState: defaultValue,
|
|
16
17
|
stateProp: valueProp,
|
|
17
18
|
});
|
|
18
19
|
const id = useUniqueId(idProp);
|
|
20
|
+
const assistiveTextId = `${id}-assistive-text`;
|
|
21
|
+
const errorMsgId = `${id}-error`;
|
|
19
22
|
const hasOptGroups = list.findIndex((item) => !!item.optionGroupLabel) !== -1;
|
|
20
23
|
const dropdownItemsGroups = useMemo(() => {
|
|
21
24
|
const itemsPerGroupLabel = new Map();
|
|
@@ -33,64 +36,70 @@ export const Dropdown = forwardRef(function Dropdown({ id: idProp, placeholder,
|
|
|
33
36
|
}
|
|
34
37
|
return customDefaultOption ?? 'Select an option';
|
|
35
38
|
};
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
// Only reference messages that the field actually renders.
|
|
40
|
+
const describedBy = [
|
|
41
|
+
fieldProps.assistiveText &&
|
|
42
|
+
(!fieldProps.renderAsTitle || fieldProps.label) &&
|
|
43
|
+
assistiveTextId,
|
|
44
|
+
error && fieldProps.errorMsg && errorMsgId,
|
|
45
|
+
]
|
|
46
|
+
.filter(Boolean)
|
|
47
|
+
.join(' ') || undefined;
|
|
48
|
+
const iconToRender = iconComponent ? (_jsx(InputLeadingIconContainer, { $size: 16, $iconColor: theme.color.text.base, children: iconComponent })) : frontIcon ? (_jsx(StyledFrontIcon, { render: frontIcon, color: "color.icon.subtle" })) : null;
|
|
49
|
+
return (_jsx(Field, { ...fieldProps, htmlFor: id, error: error, required: required, assistiveTextId: assistiveTextId, errorMsgId: errorMsgId, children: _jsxs(Box, { flex: true, alignItems: "center", style: { position: 'relative' }, children: [iconToRender, _jsxs(StyledSelect, { id: id, disabled: disabled || list.length < 1, onChange: (event) => {
|
|
38
50
|
const value = event.currentTarget.value;
|
|
39
51
|
onSelect?.(value);
|
|
40
52
|
onInputChange?.(event);
|
|
41
53
|
setValue(value);
|
|
42
|
-
}, $error: error, ref: ref, onBlur: onBlur, name: name,
|
|
54
|
+
}, $error: error, ref: ref, onBlur: onBlur, name: name, autoComplete: autoComplete, "aria-label": ariaLabel, "aria-invalid": error || undefined, "aria-required": required || undefined, "aria-describedby": describedBy, $frontIcon: !!iconToRender, $fallbackStyle: fallbackStyle, value: value ?? '', children: [_jsx("option", { value: "", hidden: !showDefaultOption, disabled: true, children: defaultOptionLabel() }), dropdownItemsGroups.map((groupItems, i) => hasOptGroups ? (_jsx("optgroup", { label: groupItems[0].optionGroupLabel ?? 'Other', children: groupItems.map((el, j) => (_jsx("option", { value: el.value, children: el.label }, `${i}-${j}`))) }, i)) : (groupItems.map((el, j) => (_jsx("option", { value: el.value, children: el.label }, j)))))] }), _jsx(Caret, { children: _jsx(IconContainer, { $size: 16, children: _jsx(FontAwesomeIcon, { icon: faChevronDown, color: theme.color.icon.subtle }) }) })] }) }));
|
|
43
55
|
});
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
56
|
+
// Figma's 32px chevron slot sits 12px from the edge, 16px from the text
|
|
57
|
+
const CHEVRON_INSET = 20;
|
|
58
|
+
const StyledSelect = styled.select.attrs(fieldFocusAttrs)(({ theme, $error, $frontIcon, $fallbackStyle, value }) => {
|
|
59
|
+
const borderColor = (color) => $error ? theme.color.feedback.negative[200] : color;
|
|
60
|
+
return css `
|
|
61
|
+
appearance: none;
|
|
62
|
+
width: 100%;
|
|
63
|
+
height: 48px;
|
|
64
|
+
margin: 0;
|
|
65
|
+
padding: 12px 60px 12px ${$frontIcon ? '42px' : '12px'};
|
|
66
|
+
border: 2px solid ${borderColor(theme.color.border.subtle)};
|
|
67
|
+
border-radius: 12px;
|
|
68
|
+
background-color: ${$fallbackStyle
|
|
69
|
+
? theme.color.surface.base[300]
|
|
70
|
+
: theme.color.background['000']};
|
|
71
|
+
color: ${value === '' ? theme.color.text.subtle : theme.color.text.base};
|
|
72
|
+
font-family: inherit;
|
|
73
|
+
font-size: 16px;
|
|
74
|
+
line-height: 20px;
|
|
75
|
+
text-overflow: ellipsis;
|
|
76
|
+
cursor: pointer;
|
|
77
|
+
outline: none;
|
|
66
78
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
`}
|
|
79
|
+
option,
|
|
80
|
+
optgroup {
|
|
81
|
+
color: ${theme.color.text.base};
|
|
82
|
+
}
|
|
72
83
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
84
|
+
// Hover mustn't override the focus border while the pointer is over a
|
|
85
|
+
// focused field.
|
|
86
|
+
&:hover:not(:disabled):not(:focus) {
|
|
87
|
+
border-color: ${borderColor(theme.color.border.base)};
|
|
88
|
+
}
|
|
77
89
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
:not(:disabled) {
|
|
81
|
-
&:hover,
|
|
82
|
-
&:focus,
|
|
83
|
-
&:focus-visible,
|
|
84
|
-
&:checked {
|
|
85
|
-
border-color: ${({ theme }) => theme.color.icon.nonEssential};
|
|
90
|
+
&:focus {
|
|
91
|
+
border-color: ${borderColor(theme.color.border.contrast)};
|
|
86
92
|
}
|
|
87
|
-
|
|
88
|
-
|
|
93
|
+
|
|
94
|
+
${fieldFocusRing}
|
|
95
|
+
${fieldDisabledStyle}
|
|
96
|
+
`;
|
|
97
|
+
});
|
|
89
98
|
const Caret = styled.div `
|
|
90
99
|
position: absolute;
|
|
91
100
|
z-index: 1;
|
|
92
|
-
right:
|
|
101
|
+
right: ${CHEVRON_INSET}px;
|
|
102
|
+
display: flex;
|
|
93
103
|
pointer-events: none;
|
|
94
|
-
color: ${({ theme }) => theme.color.icon.nonEssential};
|
|
95
104
|
`;
|
|
96
105
|
//# sourceMappingURL=Dropdown.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Dropdown.js","sourceRoot":"","sources":["../../src/Dropdown/Dropdown.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n FocusEvent,\n FormEvent,\n ForwardedRef,\n forwardRef,\n ReactNode,\n useMemo,\n} from 'react'\nimport styled, { css, useTheme } from 'styled-components'\n\nimport { Box } from '../Box'\nimport { Icons } from '../Icon'\n\nimport { Field } from '../fields/Field'\nimport { CommonFieldProps } from '../fields/commonFieldTypes'\nimport {\n InputLeadingIconContainer,\n StyledFrontIcon,\n} from '../fields/components/CommonInput'\nimport { useUniqueId } from '../utils/id'\nimport { useControllableState } from '../utils/useControlledState'\nimport { IconContainer } from '../sharedStyles/shared.styles'\nimport { faChevronDown } from '@awesome.me/kit-46ca99185c/icons/classic/regular'\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome'\n\nexport type DropdownItem = {\n optionGroupLabel?: string\n label: string\n value: string\n}\n\nexport interface Props extends CommonFieldProps {\n placeholder?: string\n showDefaultOption?: boolean\n customDefaultOption?: string\n name?: string\n value?: string | null\n defaultValue?: string\n disabled?: boolean\n list: DropdownItem[]\n frontIcon?: Icons\n iconComponent?: ReactNode\n fallbackStyle?: boolean\n onSelect: (element: string) => void\n onBlur?: (e: FocusEvent<HTMLSelectElement>) => void\n}\n\ntype TruncateProps =\n | {\n onSelect: (e: string) => void\n onInputChange?: (e: FormEvent<HTMLSelectElement>) => void\n }\n | {\n onSelect?: (e: string) => void\n onInputChange: (e: FormEvent<HTMLSelectElement>) => void\n }\n\nexport type DropdownProps = Props & TruncateProps\n\nexport const Dropdown = forwardRef(function Dropdown(\n {\n id: idProp,\n placeholder,\n showDefaultOption = false,\n customDefaultOption,\n name,\n value: valueProp,\n defaultValue,\n disabled = false,\n list,\n onSelect,\n error = false,\n onInputChange,\n onBlur,\n frontIcon,\n iconComponent,\n fallbackStyle,\n ...fieldProps\n }: DropdownProps,\n ref: ForwardedRef<HTMLSelectElement>,\n) {\n const theme = useTheme()\n const [value, setValue] = useControllableState({\n initialState: defaultValue,\n stateProp: valueProp,\n })\n const id = useUniqueId(idProp)\n const hasOptGroups = list.findIndex((item) => !!item.optionGroupLabel) !== -1\n\n const dropdownItemsGroups = useMemo(() => {\n const itemsPerGroupLabel = new Map<string, DropdownItem[]>()\n\n list.forEach((item) => {\n const key = item.optionGroupLabel ?? ''\n const group = itemsPerGroupLabel.get(key) ?? []\n\n group.push(item)\n itemsPerGroupLabel.set(key, group)\n })\n\n return Array.from(itemsPerGroupLabel.values())\n }, [list])\n\n const defaultOptionLabel = () => {\n if (!showDefaultOption) {\n return placeholder\n }\n return customDefaultOption ?? 'Select an option'\n }\n\n const iconToRender = iconComponent ? (\n <InputLeadingIconContainer $size={16} $iconColor={theme.color.text.base}>\n {iconComponent}\n </InputLeadingIconContainer>\n ) : frontIcon ? (\n <StyledFrontIcon $disabled={disabled} render={frontIcon} color=\"sesame\" />\n ) : null\n\n return (\n <Field {...fieldProps} htmlFor={id} error={error}>\n <Box flex alignItems=\"center\" style={{ position: 'relative' }}>\n {iconToRender}\n <StyledSelect\n id={id}\n disabled={disabled || list.length < 1}\n onChange={(event) => {\n const value = event.currentTarget.value\n\n onSelect?.(value)\n onInputChange?.(event)\n setValue(value)\n }}\n $error={error}\n ref={ref}\n onBlur={onBlur}\n name={name}\n $frontIcon={frontIcon}\n $fallbackStyle={fallbackStyle}\n value={value ?? ''}\n >\n {hasOptGroups ? (\n <optgroup label={defaultOptionLabel()}>\n <option value=\"\" hidden={!showDefaultOption} disabled>\n {defaultOptionLabel()}\n </option>\n </optgroup>\n ) : (\n <option value=\"\" hidden={!showDefaultOption} disabled>\n {defaultOptionLabel()}\n </option>\n )}\n\n {dropdownItemsGroups.map((groupItems, i) =>\n hasOptGroups ? (\n <optgroup\n key={i}\n label={groupItems[0].optionGroupLabel ?? 'Other'}\n >\n {groupItems.map((el, j) => (\n <option key={`${i}-${j}`} value={el.value}>\n {el.label}\n </option>\n ))}\n </optgroup>\n ) : (\n groupItems.map((el, j) => (\n <option key={j} value={el.value}>\n {el.label}\n </option>\n ))\n ),\n )}\n </StyledSelect>\n <Caret>\n <IconContainer $size={20}>\n <FontAwesomeIcon\n icon={faChevronDown}\n color={theme.color.illustration.neutral[400]}\n />\n </IconContainer>\n </Caret>\n </Box>\n </Field>\n )\n})\n\nconst resetSelect = css`\n border: none;\n border-radius: 0;\n font-size: 16px;\n appearance: none; /* remove default arrow */\n outline: none;\n`\n\ninterface SSelect {\n $error: boolean\n $frontIcon?: string\n $fallbackStyle?: boolean\n value?: string | null\n}\n\nconst StyledSelect = styled.select<SSelect>`\n ${resetSelect}\n width: 100%;\n height: 32px;\n color: ${({ value, theme }) =>\n value === '' ? theme.color.text.subtle : theme.color.text.base};\n cursor: pointer;\n background-color: ${({ $fallbackStyle, theme }) =>\n $fallbackStyle\n ? theme.color.surface.base[300]\n : theme.color.background['000']};\n\n border-radius: 12px;\n padding: 18px 14px;\n border: 2px solid\n ${({ $error, theme }) =>\n $error ? theme.color.feedback.negative[200] : theme.color.border.subtle};\n height: auto;\n\n ${({ $frontIcon }) =>\n $frontIcon &&\n $frontIcon != '' &&\n `\n padding-left: 42px;\n `}\n\n &:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n }\n\n ${({ $error }) =>\n !$error &&\n css`\n :not(:disabled) {\n &:hover,\n &:focus,\n &:focus-visible,\n &:checked {\n border-color: ${({ theme }) => theme.color.icon.nonEssential};\n }\n `}\n`\n\nconst Caret = styled.div`\n position: absolute;\n z-index: 1;\n right: 15px;\n pointer-events: none;\n color: ${({ theme }) => theme.color.icon.nonEssential};\n`\n"]}
|
|
1
|
+
{"version":3,"file":"Dropdown.js","sourceRoot":"","sources":["../../src/Dropdown/Dropdown.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n FocusEvent,\n FormEvent,\n ForwardedRef,\n forwardRef,\n ReactNode,\n useMemo,\n} from 'react'\nimport styled, { css, useTheme } from 'styled-components'\n\nimport { Box } from '../Box'\nimport { Icons } from '../Icon'\n\nimport { Field } from '../fields/Field'\nimport { CommonFieldProps } from '../fields/commonFieldTypes'\nimport {\n InputLeadingIconContainer,\n StyledFrontIcon,\n} from '../fields/components/CommonInput'\nimport {\n fieldDisabledStyle,\n fieldFocusAttrs,\n fieldFocusRing,\n} from '../fields/components/fieldStyles'\nimport { useUniqueId } from '../utils/id'\nimport { useControllableState } from '../utils/useControlledState'\nimport { IconContainer } from '../sharedStyles/shared.styles'\nimport { faChevronDown } from '@awesome.me/kit-46ca99185c/icons/classic/solid'\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome'\n\nexport type DropdownItem = {\n optionGroupLabel?: string\n label: string\n value: string\n}\n\nexport interface Props extends CommonFieldProps {\n placeholder?: string\n showDefaultOption?: boolean\n customDefaultOption?: string\n name?: string\n value?: string | null\n defaultValue?: string\n disabled?: boolean\n list: DropdownItem[]\n frontIcon?: Icons\n iconComponent?: ReactNode\n fallbackStyle?: boolean\n /** Accessible name, for when there is no visible `label` */\n 'aria-label'?: string\n /** Lets the browser autofill the field, e.g. `honorific-prefix` */\n autoComplete?: string\n onSelect?: (element: string) => void\n onBlur?: (e: FocusEvent<HTMLSelectElement>) => void\n}\n\ntype TruncateProps =\n | {\n onSelect: (e: string) => void\n onInputChange?: (e: FormEvent<HTMLSelectElement>) => void\n }\n | {\n onSelect?: (e: string) => void\n onInputChange: (e: FormEvent<HTMLSelectElement>) => void\n }\n\nexport type DropdownProps = Props & TruncateProps\n\nexport const Dropdown = forwardRef(function Dropdown(\n {\n id: idProp,\n placeholder,\n showDefaultOption = false,\n customDefaultOption,\n name,\n value: valueProp,\n defaultValue,\n disabled = false,\n list,\n onSelect,\n error = false,\n onInputChange,\n onBlur,\n frontIcon,\n iconComponent,\n fallbackStyle,\n required,\n 'aria-label': ariaLabel,\n autoComplete,\n ...fieldProps\n }: DropdownProps,\n ref: ForwardedRef<HTMLSelectElement>,\n) {\n const theme = useTheme()\n const [value, setValue] = useControllableState({\n initialState: defaultValue,\n stateProp: valueProp,\n })\n const id = useUniqueId(idProp)\n const assistiveTextId = `${id}-assistive-text`\n const errorMsgId = `${id}-error`\n const hasOptGroups = list.findIndex((item) => !!item.optionGroupLabel) !== -1\n\n const dropdownItemsGroups = useMemo(() => {\n const itemsPerGroupLabel = new Map<string, DropdownItem[]>()\n\n list.forEach((item) => {\n const key = item.optionGroupLabel ?? ''\n const group = itemsPerGroupLabel.get(key) ?? []\n\n group.push(item)\n itemsPerGroupLabel.set(key, group)\n })\n\n return Array.from(itemsPerGroupLabel.values())\n }, [list])\n\n const defaultOptionLabel = () => {\n if (!showDefaultOption) {\n return placeholder\n }\n return customDefaultOption ?? 'Select an option'\n }\n\n // Only reference messages that the field actually renders.\n const describedBy =\n [\n fieldProps.assistiveText &&\n (!fieldProps.renderAsTitle || fieldProps.label) &&\n assistiveTextId,\n error && fieldProps.errorMsg && errorMsgId,\n ]\n .filter(Boolean)\n .join(' ') || undefined\n\n const iconToRender = iconComponent ? (\n <InputLeadingIconContainer $size={16} $iconColor={theme.color.text.base}>\n {iconComponent}\n </InputLeadingIconContainer>\n ) : frontIcon ? (\n <StyledFrontIcon render={frontIcon} color=\"color.icon.subtle\" />\n ) : null\n\n return (\n <Field\n {...fieldProps}\n htmlFor={id}\n error={error}\n required={required}\n assistiveTextId={assistiveTextId}\n errorMsgId={errorMsgId}\n >\n <Box flex alignItems=\"center\" style={{ position: 'relative' }}>\n {iconToRender}\n <StyledSelect\n id={id}\n disabled={disabled || list.length < 1}\n onChange={(event) => {\n const value = event.currentTarget.value\n\n onSelect?.(value)\n onInputChange?.(event)\n setValue(value)\n }}\n $error={error}\n ref={ref}\n onBlur={onBlur}\n name={name}\n autoComplete={autoComplete}\n aria-label={ariaLabel}\n aria-invalid={error || undefined}\n aria-required={required || undefined}\n aria-describedby={describedBy}\n $frontIcon={!!iconToRender}\n $fallbackStyle={fallbackStyle}\n value={value ?? ''}\n >\n <option value=\"\" hidden={!showDefaultOption} disabled>\n {defaultOptionLabel()}\n </option>\n\n {dropdownItemsGroups.map((groupItems, i) =>\n hasOptGroups ? (\n <optgroup\n key={i}\n label={groupItems[0].optionGroupLabel ?? 'Other'}\n >\n {groupItems.map((el, j) => (\n <option key={`${i}-${j}`} value={el.value}>\n {el.label}\n </option>\n ))}\n </optgroup>\n ) : (\n groupItems.map((el, j) => (\n <option key={j} value={el.value}>\n {el.label}\n </option>\n ))\n ),\n )}\n </StyledSelect>\n <Caret>\n <IconContainer $size={16}>\n <FontAwesomeIcon\n icon={faChevronDown}\n color={theme.color.icon.subtle}\n />\n </IconContainer>\n </Caret>\n </Box>\n </Field>\n )\n})\n\ninterface SSelect {\n $error: boolean\n $frontIcon: boolean\n $fallbackStyle?: boolean\n value?: string | null\n}\n\n// Figma's 32px chevron slot sits 12px from the edge, 16px from the text\nconst CHEVRON_INSET = 20\n\nconst StyledSelect = styled.select.attrs(fieldFocusAttrs)<SSelect>(\n ({ theme, $error, $frontIcon, $fallbackStyle, value }) => {\n const borderColor = (color: string) =>\n $error ? theme.color.feedback.negative[200] : color\n\n return css`\n appearance: none;\n width: 100%;\n height: 48px;\n margin: 0;\n padding: 12px 60px 12px ${$frontIcon ? '42px' : '12px'};\n border: 2px solid ${borderColor(theme.color.border.subtle)};\n border-radius: 12px;\n background-color: ${\n $fallbackStyle\n ? theme.color.surface.base[300]\n : theme.color.background['000']\n };\n color: ${value === '' ? theme.color.text.subtle : theme.color.text.base};\n font-family: inherit;\n font-size: 16px;\n line-height: 20px;\n text-overflow: ellipsis;\n cursor: pointer;\n outline: none;\n\n option,\n optgroup {\n color: ${theme.color.text.base};\n }\n\n // Hover mustn't override the focus border while the pointer is over a\n // focused field.\n &:hover:not(:disabled):not(:focus) {\n border-color: ${borderColor(theme.color.border.base)};\n }\n\n &:focus {\n border-color: ${borderColor(theme.color.border.contrast)};\n }\n\n ${fieldFocusRing}\n ${fieldDisabledStyle}\n `\n },\n)\n\nconst Caret = styled.div`\n position: absolute;\n z-index: 1;\n right: ${CHEVRON_INSET}px;\n display: flex;\n pointer-events: none;\n`\n"]}
|
|
@@ -3,5 +3,7 @@ import { CommonFieldProps } from '../commonFieldTypes';
|
|
|
3
3
|
export interface FieldProps extends CommonFieldProps {
|
|
4
4
|
htmlFor?: string;
|
|
5
5
|
children: ReactNode;
|
|
6
|
+
assistiveTextId?: string;
|
|
7
|
+
errorMsgId?: string;
|
|
6
8
|
}
|
|
7
9
|
export declare const Field: ({ children, ...fieldProps }: FieldProps) => import("react").JSX.Element;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Field.js","sourceRoot":"","sources":["../../../src/fields/Field/Field.tsx"],"names":[],"mappings":";AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;
|
|
1
|
+
{"version":3,"file":"Field.js","sourceRoot":"","sources":["../../../src/fields/Field/Field.tsx"],"names":[],"mappings":";AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAS3D,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,EAAE,QAAQ,EAAE,GAAG,UAAU,EAAc,EAAE,EAAE;IAC/D,OAAO,CACL,KAAC,aAAa,IAAC,SAAS,EAAC,OAAO,KAAK,UAAU,YAC5C,QAAQ,GACK,CACjB,CAAA;AACH,CAAC,CAAA","sourcesContent":["import { ReactNode } from 'react'\n\nimport { CommonFieldProps } from '../commonFieldTypes'\nimport { InternalField } from '../components/InternalField'\n\nexport interface FieldProps extends CommonFieldProps {\n htmlFor?: string\n children: ReactNode\n assistiveTextId?: string\n errorMsgId?: string\n}\n\nexport const Field = ({ children, ...fieldProps }: FieldProps) => {\n return (\n <InternalField fieldType=\"field\" {...fieldProps}>\n {children}\n </InternalField>\n )\n}\n"]}
|
|
@@ -5,6 +5,10 @@ interface InternalFieldProps extends CommonFieldProps {
|
|
|
5
5
|
className?: string;
|
|
6
6
|
htmlFor?: string;
|
|
7
7
|
fieldType: 'field' | 'fieldset';
|
|
8
|
+
/** id for the assistive text, so a control can reference it with `aria-describedby` */
|
|
9
|
+
assistiveTextId?: string;
|
|
10
|
+
/** id for the error message, so a control can reference it with `aria-describedby` */
|
|
11
|
+
errorMsgId?: string;
|
|
8
12
|
}
|
|
9
|
-
export declare const InternalField: ({ children, fieldType, renderAsTitle, htmlFor, className, label, assistiveText, error, errorMsg, required, completed, ...marginProps }: InternalFieldProps) => import("react").JSX.Element;
|
|
13
|
+
export declare const InternalField: ({ children, fieldType, renderAsTitle, htmlFor, className, label, assistiveText, error, errorMsg, required, completed, assistiveTextId, errorMsgId, ...marginProps }: InternalFieldProps) => import("react").JSX.Element;
|
|
10
14
|
export {};
|
|
@@ -3,12 +3,17 @@ import styled, { css } from 'styled-components';
|
|
|
3
3
|
import { Box } from '../../Box';
|
|
4
4
|
import { Icon } from '../../Icon';
|
|
5
5
|
import { Text } from '../../Text';
|
|
6
|
-
|
|
6
|
+
import { markPointerFocus } from './fieldStyles';
|
|
7
|
+
export const InternalField = ({ children, fieldType, renderAsTitle, htmlFor, className, label, assistiveText, error, errorMsg, required, completed, assistiveTextId, errorMsgId, ...marginProps }) => {
|
|
7
8
|
const labelTag = fieldType === 'field' ? 'label' : 'legend';
|
|
9
|
+
// Clicking a label focuses its field, which shouldn't show the focus ring.
|
|
10
|
+
const handleLabelPointerDown = htmlFor
|
|
11
|
+
? () => markPointerFocus(document.getElementById(htmlFor))
|
|
12
|
+
: undefined;
|
|
8
13
|
const textColor = error ? 'color.feedback.negative.200' : 'color.text.subtle';
|
|
9
|
-
return (_jsxs(Container, { forwardedAs: fieldType === 'field' ? 'div' : 'fieldset', className: className, ...marginProps, children: [label && (_jsx(_Fragment, { children: renderAsTitle ? (_jsxs(Box, { mb: "space.200", children: [_jsx(Text, { tag: labelTag, typo: "heading-small", htmlFor: htmlFor, children: label }), assistiveText && (_jsx(Text, { tag: "p", color: "
|
|
14
|
+
return (_jsxs(Container, { forwardedAs: fieldType === 'field' ? 'div' : 'fieldset', className: className, ...marginProps, children: [label && (_jsx(_Fragment, { children: renderAsTitle ? (_jsxs(Box, { mb: "space.200", children: [_jsx(Text, { tag: labelTag, typo: "heading-small", htmlFor: htmlFor, onPointerDown: handleLabelPointerDown, children: label }), assistiveText && (_jsx(Text, { tag: "p", color: "color.text.subtle", mt: "space.050", id: assistiveTextId, children: assistiveText }))] })) : (_jsxs(Text, { tag: labelTag, typo: "label", color: textColor, htmlFor: htmlFor, onPointerDown: handleLabelPointerDown, mb: "space.050", children: [label, required && (_jsx(Text, { tag: "span", typo: "body-small", color: "color.feedback.negative.200", children: "*" }))] })) })), _jsx(Box, { children: children }), fieldType === 'field' && assistiveText && !renderAsTitle && (_jsx(Text, { tag: labelTag, typo: "caption", color: textColor, mt: "space.050", id: assistiveTextId, children: assistiveText })), error &&
|
|
10
15
|
errorMsg &&
|
|
11
|
-
(typeof errorMsg === 'string' ? (_jsxs(Box, { flex: true, alignItems: "center", mt: "space.100", gap: "space.050", children: [_jsx(Icon, { render: "warning", size: 16, color: "color.feedback.negative.200" }), _jsx(Text, { tag: "span", typo: "caption", color: "color.feedback.negative.200", children: errorMsg })] })) : (_jsx(Box, { mt: "space.100", children: errorMsg }))), completed !== undefined && (_jsx(AnimationWrapper, { $displayStatus: completed, $isError: !!(error && errorMsg), children: _jsxs(StatusWrapper, { mt: 'space.100', children: [_jsx(Icon, { render: "included", size: 16, color: "color.feedback.positive.200" }), _jsx(Text, { typo: "caption", color: "color.feedback.positive.200", children: "Complete" })] }) }))] }));
|
|
16
|
+
(typeof errorMsg === 'string' ? (_jsxs(Box, { flex: true, alignItems: "center", mt: "space.100", gap: "space.050", id: errorMsgId, children: [_jsx(Icon, { render: "warning", size: 16, color: "color.feedback.negative.200" }), _jsx(Text, { tag: "span", typo: "caption", color: "color.feedback.negative.200", children: errorMsg })] })) : (_jsx(Box, { mt: "space.100", id: errorMsgId, children: errorMsg }))), completed !== undefined && (_jsx(AnimationWrapper, { $displayStatus: completed, $isError: !!(error && errorMsg), children: _jsxs(StatusWrapper, { mt: 'space.100', children: [_jsx(Icon, { render: "included", size: 16, color: "color.feedback.positive.200" }), _jsx(Text, { typo: "caption", color: "color.feedback.positive.200", children: "Complete" })] }) }))] }));
|
|
12
17
|
};
|
|
13
18
|
const AnimationWrapper = styled(Box) `
|
|
14
19
|
width: 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InternalField.js","sourceRoot":"","sources":["../../../src/fields/components/InternalField.tsx"],"names":[],"mappings":";AACA,OAAO,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAA;AAE/C,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAC/B,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"InternalField.js","sourceRoot":"","sources":["../../../src/fields/components/InternalField.tsx"],"names":[],"mappings":"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","sourcesContent":["import { ReactNode } from 'react'\nimport styled, { css } from 'styled-components'\n\nimport { Box } from '../../Box'\nimport { Icon } from '../../Icon'\nimport { Text } from '../../Text'\nimport { CommonFieldProps } from '../commonFieldTypes'\nimport { markPointerFocus } from './fieldStyles'\n\ninterface InternalFieldProps extends CommonFieldProps {\n children: ReactNode\n className?: string\n htmlFor?: string\n fieldType: 'field' | 'fieldset'\n /** id for the assistive text, so a control can reference it with `aria-describedby` */\n assistiveTextId?: string\n /** id for the error message, so a control can reference it with `aria-describedby` */\n errorMsgId?: string\n}\n\nexport const InternalField = ({\n children,\n fieldType,\n renderAsTitle,\n htmlFor,\n className,\n label,\n assistiveText,\n error,\n errorMsg,\n required,\n completed,\n assistiveTextId,\n errorMsgId,\n ...marginProps\n}: InternalFieldProps) => {\n const labelTag = fieldType === 'field' ? 'label' : 'legend'\n // Clicking a label focuses its field, which shouldn't show the focus ring.\n const handleLabelPointerDown = htmlFor\n ? () => markPointerFocus(document.getElementById(htmlFor))\n : undefined\n\n const textColor = error ? 'color.feedback.negative.200' : 'color.text.subtle'\n return (\n <Container\n forwardedAs={fieldType === 'field' ? 'div' : 'fieldset'}\n className={className}\n {...marginProps}\n >\n {label && (\n <>\n {renderAsTitle ? (\n <Box mb=\"space.200\">\n <Text\n tag={labelTag}\n typo=\"heading-small\"\n htmlFor={htmlFor}\n onPointerDown={handleLabelPointerDown}\n >\n {label}\n </Text>\n\n {assistiveText && (\n <Text\n tag=\"p\"\n color=\"color.text.subtle\"\n mt=\"space.050\"\n id={assistiveTextId}\n >\n {assistiveText}\n </Text>\n )}\n </Box>\n ) : (\n <Text\n tag={labelTag}\n typo=\"label\"\n color={textColor}\n htmlFor={htmlFor}\n onPointerDown={handleLabelPointerDown}\n mb=\"space.050\"\n >\n {label}\n {required && (\n <Text\n tag=\"span\"\n typo=\"body-small\"\n color=\"color.feedback.negative.200\"\n >\n *\n </Text>\n )}\n </Text>\n )}\n </>\n )}\n\n <Box>{children}</Box>\n {fieldType === 'field' && assistiveText && !renderAsTitle && (\n <Text\n tag={labelTag}\n typo=\"caption\"\n color={textColor}\n mt=\"space.050\"\n id={assistiveTextId}\n >\n {assistiveText}\n </Text>\n )}\n\n {error &&\n errorMsg &&\n (typeof errorMsg === 'string' ? (\n <Box\n flex\n alignItems=\"center\"\n mt=\"space.100\"\n gap=\"space.050\"\n id={errorMsgId}\n >\n <Icon\n render=\"warning\"\n size={16}\n color=\"color.feedback.negative.200\"\n />\n <Text tag=\"span\" typo=\"caption\" color=\"color.feedback.negative.200\">\n {errorMsg}\n </Text>\n </Box>\n ) : (\n <Box mt=\"space.100\" id={errorMsgId}>\n {errorMsg}\n </Box>\n ))}\n\n {/* When completed is false, whitespace is rendered */}\n {completed !== undefined && (\n <AnimationWrapper\n $displayStatus={completed}\n $isError={!!(error && errorMsg)}\n >\n <StatusWrapper mt={'space.100'}>\n <Icon\n render=\"included\"\n size={16}\n color=\"color.feedback.positive.200\"\n />\n <Text typo=\"caption\" color=\"color.feedback.positive.200\">\n Complete\n </Text>\n </StatusWrapper>\n </AnimationWrapper>\n )}\n </Container>\n )\n}\n\nconst AnimationWrapper = styled(Box)<{\n $displayStatus: boolean\n $isError: boolean\n}>`\n width: 0;\n overflow: hidden;\n\n ${({ $displayStatus }) =>\n $displayStatus &&\n css`\n transition: width 0.6s ease-in;\n width: 100%;\n `}\n\n /* This enables animation to appear when previous state is error */\n ${({ $isError }) =>\n $isError &&\n css`\n height: 0;\n `}\n`\n\nconst StatusWrapper = styled(Box)`\n display: flex;\n align-items: center;\n gap: ${({ theme }) => theme.space['050']};\n`\n\nconst Container = styled(Box)`\n display: flex;\n flex-direction: column;\n position: relative;\n width: 100%;\n\n // In case, the element is a 'fieldset', we remove the border\n border: 0;\n`\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { FocusEvent, FocusEventHandler, PointerEvent, PointerEventHandler } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Browsers treat text fields as `:focus-visible` even when they are clicked,
|
|
4
|
+
* so fields flag focus that comes from a pointer and only show the focus ring
|
|
5
|
+
* for keyboard focus.
|
|
6
|
+
*/
|
|
7
|
+
export declare const markPointerFocus: (element: HTMLElement | null) => void;
|
|
8
|
+
/** Pass to styled-components `.attrs`. Callers' own handlers still run. */
|
|
9
|
+
export declare const fieldFocusAttrs: <T extends HTMLElement>({ onPointerDown, onBlurCapture, }: {
|
|
10
|
+
onPointerDown?: PointerEventHandler<T>;
|
|
11
|
+
onBlurCapture?: FocusEventHandler<T>;
|
|
12
|
+
}) => {
|
|
13
|
+
onPointerDown: (event: PointerEvent<T>) => void;
|
|
14
|
+
onBlurCapture: (event: FocusEvent<T>) => void;
|
|
15
|
+
};
|
|
16
|
+
export declare const fieldFocusRing: import("styled-components").RuleSet<object>;
|
|
17
|
+
export declare const fieldDisabledStyle: import("styled-components").RuleSet<object>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { css } from 'styled-components';
|
|
2
|
+
/**
|
|
3
|
+
* Browsers treat text fields as `:focus-visible` even when they are clicked,
|
|
4
|
+
* so fields flag focus that comes from a pointer and only show the focus ring
|
|
5
|
+
* for keyboard focus.
|
|
6
|
+
*/
|
|
7
|
+
export const markPointerFocus = (element) => {
|
|
8
|
+
if (element)
|
|
9
|
+
element.dataset.pointerFocus = '';
|
|
10
|
+
};
|
|
11
|
+
/** Pass to styled-components `.attrs`. Callers' own handlers still run. */
|
|
12
|
+
export const fieldFocusAttrs = ({ onPointerDown, onBlurCapture, }) => ({
|
|
13
|
+
onPointerDown: (event) => {
|
|
14
|
+
markPointerFocus(event.currentTarget);
|
|
15
|
+
onPointerDown?.(event);
|
|
16
|
+
},
|
|
17
|
+
onBlurCapture: (event) => {
|
|
18
|
+
delete event.currentTarget.dataset.pointerFocus;
|
|
19
|
+
onBlurCapture?.(event);
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
export const fieldFocusRing = css `
|
|
23
|
+
&:focus-visible:not([data-pointer-focus]) {
|
|
24
|
+
outline: 2px solid ${({ theme }) => theme.color.focus.onLight};
|
|
25
|
+
outline-offset: 2px;
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
28
|
+
export const fieldDisabledStyle = css `
|
|
29
|
+
&:disabled {
|
|
30
|
+
border-color: ${({ theme }) => theme.color.feedback.inactive[100]};
|
|
31
|
+
background-color: ${({ theme }) => theme.color.feedback.inactive[50]};
|
|
32
|
+
color: ${({ theme }) => theme.color.text.subtle};
|
|
33
|
+
cursor: not-allowed;
|
|
34
|
+
/* Safari fades disabled fields on top of these colours */
|
|
35
|
+
opacity: 1;
|
|
36
|
+
-webkit-text-fill-color: currentColor;
|
|
37
|
+
}
|
|
38
|
+
`;
|
|
39
|
+
//# sourceMappingURL=fieldStyles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fieldStyles.js","sourceRoot":"","sources":["../../../src/fields/components/fieldStyles.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAA;AAEvC;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,OAA2B,EAAE,EAAE;IAC9D,IAAI,OAAO;QAAE,OAAO,CAAC,OAAO,CAAC,YAAY,GAAG,EAAE,CAAA;AAChD,CAAC,CAAA;AAED,2EAA2E;AAC3E,MAAM,CAAC,MAAM,eAAe,GAAG,CAAwB,EACrD,aAAa,EACb,aAAa,GAId,EAAE,EAAE,CAAC,CAAC;IACL,aAAa,EAAE,CAAC,KAAsB,EAAE,EAAE;QACxC,gBAAgB,CAAC,KAAK,CAAC,aAAa,CAAC,CAAA;QACrC,aAAa,EAAE,CAAC,KAAK,CAAC,CAAA;IACxB,CAAC;IACD,aAAa,EAAE,CAAC,KAAoB,EAAE,EAAE;QACtC,OAAO,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,YAAY,CAAA;QAC/C,aAAa,EAAE,CAAC,KAAK,CAAC,CAAA;IACxB,CAAC;CACF,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,CAAA;;yBAER,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO;;;CAGhE,CAAA;AAED,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,CAAA;;oBAEjB,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC;wBAC7C,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;aAC3D,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM;;;;;;CAMlD,CAAA","sourcesContent":["import {\n FocusEvent,\n FocusEventHandler,\n PointerEvent,\n PointerEventHandler,\n} from 'react'\nimport { css } from 'styled-components'\n\n/**\n * Browsers treat text fields as `:focus-visible` even when they are clicked,\n * so fields flag focus that comes from a pointer and only show the focus ring\n * for keyboard focus.\n */\nexport const markPointerFocus = (element: HTMLElement | null) => {\n if (element) element.dataset.pointerFocus = ''\n}\n\n/** Pass to styled-components `.attrs`. Callers' own handlers still run. */\nexport const fieldFocusAttrs = <T extends HTMLElement>({\n onPointerDown,\n onBlurCapture,\n}: {\n onPointerDown?: PointerEventHandler<T>\n onBlurCapture?: FocusEventHandler<T>\n}) => ({\n onPointerDown: (event: PointerEvent<T>) => {\n markPointerFocus(event.currentTarget)\n onPointerDown?.(event)\n },\n onBlurCapture: (event: FocusEvent<T>) => {\n delete event.currentTarget.dataset.pointerFocus\n onBlurCapture?.(event)\n },\n})\n\nexport const fieldFocusRing = css`\n &:focus-visible:not([data-pointer-focus]) {\n outline: 2px solid ${({ theme }) => theme.color.focus.onLight};\n outline-offset: 2px;\n }\n`\n\nexport const fieldDisabledStyle = css`\n &:disabled {\n border-color: ${({ theme }) => theme.color.feedback.inactive[100]};\n background-color: ${({ theme }) => theme.color.feedback.inactive[50]};\n color: ${({ theme }) => theme.color.text.subtle};\n cursor: not-allowed;\n /* Safari fades disabled fields on top of these colours */\n opacity: 1;\n -webkit-text-fill-color: currentColor;\n }\n`\n"]}
|