@spark-web/text-input 1.0.3 → 1.1.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.
@@ -0,0 +1,12 @@
1
+ import type { ReactElement } from 'react';
2
+ import type { InputAdornmentProps } from './InputAdornment';
3
+ export declare type AdornmentChild = ReactElement<InputAdornmentProps> | null | undefined;
4
+ export declare type AdornmentsAsChildren = AdornmentChild | [AdornmentChild, AdornmentChild];
5
+ /**
6
+ * Map children for placement within the `TextInput` flex container. Ensures that
7
+ * placeholders are provided for unused placements.
8
+ */
9
+ export declare const childrenToAdornments: (children?: AdornmentsAsChildren) => {
10
+ startAdornment: ReactElement<InputAdornmentProps> | null;
11
+ endAdornment: ReactElement<InputAdornmentProps> | null;
12
+ };
@@ -1,2 +1,4 @@
1
+ export { InputAdornment } from './InputAdornment';
1
2
  export { TextInput, useInput } from './TextInput';
3
+ export type { AdornmentChild } from './childrenToAdornments';
2
4
  export type { TextInputProps, UseInputProps } from './TextInput';
@@ -2,25 +2,154 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var _slicedToArray = require('@babel/runtime/helpers/slicedToArray');
6
5
  var _objectSpread = require('@babel/runtime/helpers/objectSpread2');
7
- var _objectWithoutProperties = require('@babel/runtime/helpers/objectWithoutProperties');
8
- var css = require('@emotion/css');
9
- var a11y = require('@spark-web/a11y');
10
6
  var box = require('@spark-web/box');
11
7
  var field = require('@spark-web/field');
12
- var text = require('@spark-web/text');
13
8
  var theme = require('@spark-web/theme');
14
- var internal = require('@spark-web/utils/internal');
15
9
  var react = require('react');
16
10
  var jsxRuntime = require('react/jsx-runtime');
11
+ var _slicedToArray = require('@babel/runtime/helpers/slicedToArray');
12
+ var _objectWithoutProperties = require('@babel/runtime/helpers/objectWithoutProperties');
13
+ var css = require('@emotion/css');
14
+ var a11y = require('@spark-web/a11y');
15
+ var text = require('@spark-web/text');
16
+
17
+ /**
18
+ * Components like the `SelectInput` may subscribe to the adornment context and
19
+ * change their appearance or behaviour.
20
+ */
21
+ var InputAdornmentContext = /*#__PURE__*/react.createContext(null);
22
+ var placementToPadding = {
23
+ start: {
24
+ paddingLeft: 'medium',
25
+ paddingRight: 'xsmall'
26
+ },
27
+ end: {
28
+ paddingLeft: 'xsmall',
29
+ paddingRight: 'medium'
30
+ }
31
+ };
32
+
33
+ /**
34
+ * Places an element at the "start" or "end" of the input, only one adornment
35
+ * may be provided for each placement. By default, the adornment element will be
36
+ * wrapped to provide alignment and spacing, use the "raw" property to opt-out
37
+ * of this behaviour.
38
+ *
39
+ * @example
40
+ * <TextInput>
41
+ * <InputAdornment placement="start">
42
+ * <Text tone="placeholder">$</Text>
43
+ * </InputAdornment>
44
+ * </TextInput>
45
+ */
46
+ var InputAdornment = function InputAdornment(_ref) {
47
+ var children = _ref.children,
48
+ fieldLabel = _ref.fieldLabel,
49
+ placement = _ref.placement,
50
+ raw = _ref.raw;
51
+
52
+ var _useTheme = theme.useTheme(),
53
+ sizing = _useTheme.sizing;
54
+
55
+ var adornmentContext = react.useMemo(function () {
56
+ return {
57
+ placement: placement
58
+ };
59
+ }, [placement]);
60
+ var _placementToPadding$p = placementToPadding[placement],
61
+ paddingLeft = _placementToPadding$p.paddingLeft,
62
+ paddingRight = _placementToPadding$p.paddingRight;
63
+ var content = children;
64
+
65
+ if (!raw) {
66
+ content = /*#__PURE__*/jsxRuntime.jsx(box.Box, {
67
+ paddingLeft: paddingLeft,
68
+ paddingRight: paddingRight,
69
+ children: /*#__PURE__*/jsxRuntime.jsx(box.Box, {
70
+ alignItems: "center",
71
+ justifyContent: "center",
72
+ style: {
73
+ minWidth: sizing.xxsmall
74
+ },
75
+ children: children
76
+ })
77
+ });
78
+ }
79
+
80
+ var wrappedContent = /*#__PURE__*/jsxRuntime.jsx(InputAdornmentContext.Provider, {
81
+ value: adornmentContext,
82
+ children: content
83
+ });
84
+
85
+ if (fieldLabel) {
86
+ return /*#__PURE__*/jsxRuntime.jsx(FieldAdornment, {
87
+ fieldLabel: fieldLabel,
88
+ children: wrappedContent
89
+ });
90
+ }
91
+
92
+ return wrappedContent;
93
+ };
94
+ /**
95
+ * Wrap the element with a field provider to override the parent field label.
96
+ * Only split-out from `InputAdornment` to avoid the conditional hook rule.
97
+ */
98
+
99
+ var FieldAdornment = function FieldAdornment(_ref2) {
100
+ var children = _ref2.children,
101
+ fieldLabel = _ref2.fieldLabel;
102
+ var parentFieldContext = field.useFieldContext();
103
+ var fieldContext = react.useMemo(function () {
104
+ return _objectSpread(_objectSpread({}, parentFieldContext), {}, {
105
+ accessibilityLabel: fieldLabel
106
+ });
107
+ }, [fieldLabel, parentFieldContext]);
108
+ return /*#__PURE__*/jsxRuntime.jsx(field.FieldContextProvider, {
109
+ value: fieldContext,
110
+ children: children
111
+ });
112
+ };
17
113
 
18
- var _excluded = ["data"],
114
+ /**
115
+ * Map children for placement within the `TextInput` flex container. Ensures that
116
+ * placeholders are provided for unused placements.
117
+ */
118
+ var childrenToAdornments = function childrenToAdornments(children) {
119
+ var startAdornment = null;
120
+ var endAdornment = null;
121
+
122
+ if (!children) {
123
+ return {
124
+ startAdornment: startAdornment,
125
+ endAdornment: endAdornment
126
+ };
127
+ }
128
+
129
+ react.Children.forEach(children, function (child) {
130
+ if ( /*#__PURE__*/react.isValidElement(child)) {
131
+ if (child.props.placement === 'end') {
132
+ endAdornment = child;
133
+ }
134
+
135
+ if (child.props.placement === 'start') {
136
+ startAdornment = child;
137
+ }
138
+ }
139
+ });
140
+ return {
141
+ startAdornment: startAdornment,
142
+ endAdornment: endAdornment
143
+ };
144
+ };
145
+
146
+ var _excluded = ["children", "data"],
19
147
  _excluded2 = ["disabled", "invalid"];
20
148
 
21
149
  /** Organize and emphasize information quickly and effectively in a list of text elements. */
22
150
  var TextInput = /*#__PURE__*/react.forwardRef(function (_ref, forwardedRef) {
23
- var data = _ref.data,
151
+ var children = _ref.children,
152
+ data = _ref.data,
24
153
  consumerProps = _objectWithoutProperties(_ref, _excluded);
25
154
 
26
155
  var _useFieldContext = field.useFieldContext(),
@@ -28,28 +157,47 @@ var TextInput = /*#__PURE__*/react.forwardRef(function (_ref, forwardedRef) {
28
157
  invalid = _useFieldContext.invalid,
29
158
  a11yProps = _objectWithoutProperties(_useFieldContext, _excluded2);
30
159
 
31
- var inputStyles = useInput({
32
- disabled: disabled,
33
- invalid: invalid
34
- });
35
- return /*#__PURE__*/jsxRuntime.jsx(box.Box, _objectSpread(_objectSpread(_objectSpread({
36
- as: "input",
37
- disabled: disabled,
38
- ref: forwardedRef // styles
39
- ,
160
+ var _childrenToAdornments = childrenToAdornments(children),
161
+ startAdornment = _childrenToAdornments.startAdornment,
162
+ endAdornment = _childrenToAdornments.endAdornment;
163
+
164
+ return /*#__PURE__*/jsxRuntime.jsxs(box.Box, {
40
165
  background: disabled ? 'inputDisabled' : 'input',
41
166
  border: invalid ? 'critical' : 'field',
42
167
  borderRadius: "small",
168
+ display: "inline-flex",
169
+ alignItems: "center",
170
+ flexDirection: "row",
43
171
  height: "medium",
44
- paddingX: "medium",
45
- className: css.css(inputStyles)
46
- }, data ? internal.buildDataAttributes(data) : null), a11yProps), consumerProps));
172
+ marginY: "none",
173
+ className: css.css(useInput({
174
+ disabled: disabled,
175
+ invalid: invalid
176
+ })),
177
+ children: [startAdornment, /*#__PURE__*/jsxRuntime.jsx(box.Box, _objectSpread(_objectSpread({
178
+ as: "input",
179
+ ref: forwardedRef,
180
+ disabled: disabled // Styles
181
+ ,
182
+ flex: 1,
183
+ height: "medium",
184
+ paddingX: "medium",
185
+ paddingLeft: startAdornment ? 'none' : 'medium',
186
+ paddingRight: endAdornment ? 'none' : 'medium',
187
+ className: css.css(useInput({
188
+ disabled: disabled,
189
+ invalid: invalid,
190
+ isNested: true
191
+ })),
192
+ data: data
193
+ }, a11yProps), consumerProps)), endAdornment]
194
+ });
47
195
  });
48
- TextInput.displayName = 'TextInput'; // Styled components
49
- // ------------------------------
50
-
51
- function useInput(_ref2) {
52
- var disabled = _ref2.disabled;
196
+ TextInput.displayName = 'TextInput';
197
+ var useInput = function useInput(_ref2) {
198
+ var disabled = _ref2.disabled,
199
+ _ref2$isNested = _ref2.isNested,
200
+ isNested = _ref2$isNested === void 0 ? false : _ref2$isNested;
53
201
  var theme$1 = theme.useTheme();
54
202
  var focusRingStyles = a11y.useFocusRing({
55
203
  always: true
@@ -65,17 +213,27 @@ function useInput(_ref2) {
65
213
  typographyStyles = _textStyles[0],
66
214
  responsiveStyles = _textStyles[1];
67
215
 
68
- return _objectSpread(_objectSpread(_objectSpread({}, typographyStyles), responsiveStyles), {}, {
216
+ return _objectSpread(_objectSpread(_objectSpread({}, typographyStyles), responsiveStyles), isNested ? {
217
+ ':focus': {
218
+ // This removes the nested input outline visibility since
219
+ // the wrapper will be outlined, but still visibly focusable
220
+ // to windows high contrast mode users.
221
+ // @see https://tailwindcss.com/docs/outline-style#removing-outlines
222
+ outline: '2px solid transparent',
223
+ outlineOffset: '2px'
224
+ }
225
+ } : {
69
226
  ':enabled': {
70
- '&:hover': {
71
- borderColor: theme$1.border.color.fieldHover
72
- },
73
227
  '&:focus': _objectSpread(_objectSpread({}, focusRingStyles), {}, {
74
228
  borderColor: theme$1.border.color.fieldAccent
75
229
  })
76
- }
230
+ },
231
+ ':focus-within': _objectSpread(_objectSpread({}, focusRingStyles), {}, {
232
+ borderColor: theme$1.border.color.fieldAccent
233
+ })
77
234
  });
78
- }
235
+ };
79
236
 
237
+ exports.InputAdornment = InputAdornment;
80
238
  exports.TextInput = TextInput;
81
239
  exports.useInput = useInput;
@@ -2,25 +2,154 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var _slicedToArray = require('@babel/runtime/helpers/slicedToArray');
6
5
  var _objectSpread = require('@babel/runtime/helpers/objectSpread2');
7
- var _objectWithoutProperties = require('@babel/runtime/helpers/objectWithoutProperties');
8
- var css = require('@emotion/css');
9
- var a11y = require('@spark-web/a11y');
10
6
  var box = require('@spark-web/box');
11
7
  var field = require('@spark-web/field');
12
- var text = require('@spark-web/text');
13
8
  var theme = require('@spark-web/theme');
14
- var internal = require('@spark-web/utils/internal');
15
9
  var react = require('react');
16
10
  var jsxRuntime = require('react/jsx-runtime');
11
+ var _slicedToArray = require('@babel/runtime/helpers/slicedToArray');
12
+ var _objectWithoutProperties = require('@babel/runtime/helpers/objectWithoutProperties');
13
+ var css = require('@emotion/css');
14
+ var a11y = require('@spark-web/a11y');
15
+ var text = require('@spark-web/text');
16
+
17
+ /**
18
+ * Components like the `SelectInput` may subscribe to the adornment context and
19
+ * change their appearance or behaviour.
20
+ */
21
+ var InputAdornmentContext = /*#__PURE__*/react.createContext(null);
22
+ var placementToPadding = {
23
+ start: {
24
+ paddingLeft: 'medium',
25
+ paddingRight: 'xsmall'
26
+ },
27
+ end: {
28
+ paddingLeft: 'xsmall',
29
+ paddingRight: 'medium'
30
+ }
31
+ };
32
+
33
+ /**
34
+ * Places an element at the "start" or "end" of the input, only one adornment
35
+ * may be provided for each placement. By default, the adornment element will be
36
+ * wrapped to provide alignment and spacing, use the "raw" property to opt-out
37
+ * of this behaviour.
38
+ *
39
+ * @example
40
+ * <TextInput>
41
+ * <InputAdornment placement="start">
42
+ * <Text tone="placeholder">$</Text>
43
+ * </InputAdornment>
44
+ * </TextInput>
45
+ */
46
+ var InputAdornment = function InputAdornment(_ref) {
47
+ var children = _ref.children,
48
+ fieldLabel = _ref.fieldLabel,
49
+ placement = _ref.placement,
50
+ raw = _ref.raw;
51
+
52
+ var _useTheme = theme.useTheme(),
53
+ sizing = _useTheme.sizing;
54
+
55
+ var adornmentContext = react.useMemo(function () {
56
+ return {
57
+ placement: placement
58
+ };
59
+ }, [placement]);
60
+ var _placementToPadding$p = placementToPadding[placement],
61
+ paddingLeft = _placementToPadding$p.paddingLeft,
62
+ paddingRight = _placementToPadding$p.paddingRight;
63
+ var content = children;
64
+
65
+ if (!raw) {
66
+ content = /*#__PURE__*/jsxRuntime.jsx(box.Box, {
67
+ paddingLeft: paddingLeft,
68
+ paddingRight: paddingRight,
69
+ children: /*#__PURE__*/jsxRuntime.jsx(box.Box, {
70
+ alignItems: "center",
71
+ justifyContent: "center",
72
+ style: {
73
+ minWidth: sizing.xxsmall
74
+ },
75
+ children: children
76
+ })
77
+ });
78
+ }
79
+
80
+ var wrappedContent = /*#__PURE__*/jsxRuntime.jsx(InputAdornmentContext.Provider, {
81
+ value: adornmentContext,
82
+ children: content
83
+ });
84
+
85
+ if (fieldLabel) {
86
+ return /*#__PURE__*/jsxRuntime.jsx(FieldAdornment, {
87
+ fieldLabel: fieldLabel,
88
+ children: wrappedContent
89
+ });
90
+ }
91
+
92
+ return wrappedContent;
93
+ };
94
+ /**
95
+ * Wrap the element with a field provider to override the parent field label.
96
+ * Only split-out from `InputAdornment` to avoid the conditional hook rule.
97
+ */
98
+
99
+ var FieldAdornment = function FieldAdornment(_ref2) {
100
+ var children = _ref2.children,
101
+ fieldLabel = _ref2.fieldLabel;
102
+ var parentFieldContext = field.useFieldContext();
103
+ var fieldContext = react.useMemo(function () {
104
+ return _objectSpread(_objectSpread({}, parentFieldContext), {}, {
105
+ accessibilityLabel: fieldLabel
106
+ });
107
+ }, [fieldLabel, parentFieldContext]);
108
+ return /*#__PURE__*/jsxRuntime.jsx(field.FieldContextProvider, {
109
+ value: fieldContext,
110
+ children: children
111
+ });
112
+ };
17
113
 
18
- var _excluded = ["data"],
114
+ /**
115
+ * Map children for placement within the `TextInput` flex container. Ensures that
116
+ * placeholders are provided for unused placements.
117
+ */
118
+ var childrenToAdornments = function childrenToAdornments(children) {
119
+ var startAdornment = null;
120
+ var endAdornment = null;
121
+
122
+ if (!children) {
123
+ return {
124
+ startAdornment: startAdornment,
125
+ endAdornment: endAdornment
126
+ };
127
+ }
128
+
129
+ react.Children.forEach(children, function (child) {
130
+ if ( /*#__PURE__*/react.isValidElement(child)) {
131
+ if (child.props.placement === 'end') {
132
+ endAdornment = child;
133
+ }
134
+
135
+ if (child.props.placement === 'start') {
136
+ startAdornment = child;
137
+ }
138
+ }
139
+ });
140
+ return {
141
+ startAdornment: startAdornment,
142
+ endAdornment: endAdornment
143
+ };
144
+ };
145
+
146
+ var _excluded = ["children", "data"],
19
147
  _excluded2 = ["disabled", "invalid"];
20
148
 
21
149
  /** Organize and emphasize information quickly and effectively in a list of text elements. */
22
150
  var TextInput = /*#__PURE__*/react.forwardRef(function (_ref, forwardedRef) {
23
- var data = _ref.data,
151
+ var children = _ref.children,
152
+ data = _ref.data,
24
153
  consumerProps = _objectWithoutProperties(_ref, _excluded);
25
154
 
26
155
  var _useFieldContext = field.useFieldContext(),
@@ -28,28 +157,47 @@ var TextInput = /*#__PURE__*/react.forwardRef(function (_ref, forwardedRef) {
28
157
  invalid = _useFieldContext.invalid,
29
158
  a11yProps = _objectWithoutProperties(_useFieldContext, _excluded2);
30
159
 
31
- var inputStyles = useInput({
32
- disabled: disabled,
33
- invalid: invalid
34
- });
35
- return /*#__PURE__*/jsxRuntime.jsx(box.Box, _objectSpread(_objectSpread(_objectSpread({
36
- as: "input",
37
- disabled: disabled,
38
- ref: forwardedRef // styles
39
- ,
160
+ var _childrenToAdornments = childrenToAdornments(children),
161
+ startAdornment = _childrenToAdornments.startAdornment,
162
+ endAdornment = _childrenToAdornments.endAdornment;
163
+
164
+ return /*#__PURE__*/jsxRuntime.jsxs(box.Box, {
40
165
  background: disabled ? 'inputDisabled' : 'input',
41
166
  border: invalid ? 'critical' : 'field',
42
167
  borderRadius: "small",
168
+ display: "inline-flex",
169
+ alignItems: "center",
170
+ flexDirection: "row",
43
171
  height: "medium",
44
- paddingX: "medium",
45
- className: css.css(inputStyles)
46
- }, data ? internal.buildDataAttributes(data) : null), a11yProps), consumerProps));
172
+ marginY: "none",
173
+ className: css.css(useInput({
174
+ disabled: disabled,
175
+ invalid: invalid
176
+ })),
177
+ children: [startAdornment, /*#__PURE__*/jsxRuntime.jsx(box.Box, _objectSpread(_objectSpread({
178
+ as: "input",
179
+ ref: forwardedRef,
180
+ disabled: disabled // Styles
181
+ ,
182
+ flex: 1,
183
+ height: "medium",
184
+ paddingX: "medium",
185
+ paddingLeft: startAdornment ? 'none' : 'medium',
186
+ paddingRight: endAdornment ? 'none' : 'medium',
187
+ className: css.css(useInput({
188
+ disabled: disabled,
189
+ invalid: invalid,
190
+ isNested: true
191
+ })),
192
+ data: data
193
+ }, a11yProps), consumerProps)), endAdornment]
194
+ });
47
195
  });
48
- TextInput.displayName = 'TextInput'; // Styled components
49
- // ------------------------------
50
-
51
- function useInput(_ref2) {
52
- var disabled = _ref2.disabled;
196
+ TextInput.displayName = 'TextInput';
197
+ var useInput = function useInput(_ref2) {
198
+ var disabled = _ref2.disabled,
199
+ _ref2$isNested = _ref2.isNested,
200
+ isNested = _ref2$isNested === void 0 ? false : _ref2$isNested;
53
201
  var theme$1 = theme.useTheme();
54
202
  var focusRingStyles = a11y.useFocusRing({
55
203
  always: true
@@ -65,17 +213,27 @@ function useInput(_ref2) {
65
213
  typographyStyles = _textStyles[0],
66
214
  responsiveStyles = _textStyles[1];
67
215
 
68
- return _objectSpread(_objectSpread(_objectSpread({}, typographyStyles), responsiveStyles), {}, {
216
+ return _objectSpread(_objectSpread(_objectSpread({}, typographyStyles), responsiveStyles), isNested ? {
217
+ ':focus': {
218
+ // This removes the nested input outline visibility since
219
+ // the wrapper will be outlined, but still visibly focusable
220
+ // to windows high contrast mode users.
221
+ // @see https://tailwindcss.com/docs/outline-style#removing-outlines
222
+ outline: '2px solid transparent',
223
+ outlineOffset: '2px'
224
+ }
225
+ } : {
69
226
  ':enabled': {
70
- '&:hover': {
71
- borderColor: theme$1.border.color.fieldHover
72
- },
73
227
  '&:focus': _objectSpread(_objectSpread({}, focusRingStyles), {}, {
74
228
  borderColor: theme$1.border.color.fieldAccent
75
229
  })
76
- }
230
+ },
231
+ ':focus-within': _objectSpread(_objectSpread({}, focusRingStyles), {}, {
232
+ borderColor: theme$1.border.color.fieldAccent
233
+ })
77
234
  });
78
- }
235
+ };
79
236
 
237
+ exports.InputAdornment = InputAdornment;
80
238
  exports.TextInput = TextInput;
81
239
  exports.useInput = useInput;