@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.
@@ -1,22 +1,151 @@
1
- import _slicedToArray from '@babel/runtime/helpers/esm/slicedToArray';
2
1
  import _objectSpread from '@babel/runtime/helpers/esm/objectSpread2';
2
+ import { Box } from '@spark-web/box';
3
+ import { useFieldContext, FieldContextProvider } from '@spark-web/field';
4
+ import { useTheme } from '@spark-web/theme';
5
+ import { useMemo, createContext, Children, isValidElement, forwardRef } from 'react';
6
+ import { jsx, jsxs } from 'react/jsx-runtime';
7
+ import _slicedToArray from '@babel/runtime/helpers/esm/slicedToArray';
3
8
  import _objectWithoutProperties from '@babel/runtime/helpers/esm/objectWithoutProperties';
4
9
  import { css } from '@emotion/css';
5
10
  import { useFocusRing } from '@spark-web/a11y';
6
- import { Box } from '@spark-web/box';
7
- import { useFieldContext } from '@spark-web/field';
8
11
  import { useText } from '@spark-web/text';
9
- import { useTheme } from '@spark-web/theme';
10
- import { buildDataAttributes } from '@spark-web/utils/internal';
11
- import { forwardRef } from 'react';
12
- import { jsx } from 'react/jsx-runtime';
13
12
 
14
- var _excluded = ["data"],
13
+ /**
14
+ * Components like the `SelectInput` may subscribe to the adornment context and
15
+ * change their appearance or behaviour.
16
+ */
17
+ var InputAdornmentContext = /*#__PURE__*/createContext(null);
18
+ var placementToPadding = {
19
+ start: {
20
+ paddingLeft: 'medium',
21
+ paddingRight: 'xsmall'
22
+ },
23
+ end: {
24
+ paddingLeft: 'xsmall',
25
+ paddingRight: 'medium'
26
+ }
27
+ };
28
+
29
+ /**
30
+ * Places an element at the "start" or "end" of the input, only one adornment
31
+ * may be provided for each placement. By default, the adornment element will be
32
+ * wrapped to provide alignment and spacing, use the "raw" property to opt-out
33
+ * of this behaviour.
34
+ *
35
+ * @example
36
+ * <TextInput>
37
+ * <InputAdornment placement="start">
38
+ * <Text tone="placeholder">$</Text>
39
+ * </InputAdornment>
40
+ * </TextInput>
41
+ */
42
+ var InputAdornment = function InputAdornment(_ref) {
43
+ var children = _ref.children,
44
+ fieldLabel = _ref.fieldLabel,
45
+ placement = _ref.placement,
46
+ raw = _ref.raw;
47
+
48
+ var _useTheme = useTheme(),
49
+ sizing = _useTheme.sizing;
50
+
51
+ var adornmentContext = useMemo(function () {
52
+ return {
53
+ placement: placement
54
+ };
55
+ }, [placement]);
56
+ var _placementToPadding$p = placementToPadding[placement],
57
+ paddingLeft = _placementToPadding$p.paddingLeft,
58
+ paddingRight = _placementToPadding$p.paddingRight;
59
+ var content = children;
60
+
61
+ if (!raw) {
62
+ content = /*#__PURE__*/jsx(Box, {
63
+ paddingLeft: paddingLeft,
64
+ paddingRight: paddingRight,
65
+ children: /*#__PURE__*/jsx(Box, {
66
+ alignItems: "center",
67
+ justifyContent: "center",
68
+ style: {
69
+ minWidth: sizing.xxsmall
70
+ },
71
+ children: children
72
+ })
73
+ });
74
+ }
75
+
76
+ var wrappedContent = /*#__PURE__*/jsx(InputAdornmentContext.Provider, {
77
+ value: adornmentContext,
78
+ children: content
79
+ });
80
+
81
+ if (fieldLabel) {
82
+ return /*#__PURE__*/jsx(FieldAdornment, {
83
+ fieldLabel: fieldLabel,
84
+ children: wrappedContent
85
+ });
86
+ }
87
+
88
+ return wrappedContent;
89
+ };
90
+ /**
91
+ * Wrap the element with a field provider to override the parent field label.
92
+ * Only split-out from `InputAdornment` to avoid the conditional hook rule.
93
+ */
94
+
95
+ var FieldAdornment = function FieldAdornment(_ref2) {
96
+ var children = _ref2.children,
97
+ fieldLabel = _ref2.fieldLabel;
98
+ var parentFieldContext = useFieldContext();
99
+ var fieldContext = useMemo(function () {
100
+ return _objectSpread(_objectSpread({}, parentFieldContext), {}, {
101
+ accessibilityLabel: fieldLabel
102
+ });
103
+ }, [fieldLabel, parentFieldContext]);
104
+ return /*#__PURE__*/jsx(FieldContextProvider, {
105
+ value: fieldContext,
106
+ children: children
107
+ });
108
+ };
109
+
110
+ /**
111
+ * Map children for placement within the `TextInput` flex container. Ensures that
112
+ * placeholders are provided for unused placements.
113
+ */
114
+ var childrenToAdornments = function childrenToAdornments(children) {
115
+ var startAdornment = null;
116
+ var endAdornment = null;
117
+
118
+ if (!children) {
119
+ return {
120
+ startAdornment: startAdornment,
121
+ endAdornment: endAdornment
122
+ };
123
+ }
124
+
125
+ Children.forEach(children, function (child) {
126
+ if ( /*#__PURE__*/isValidElement(child)) {
127
+ if (child.props.placement === 'end') {
128
+ endAdornment = child;
129
+ }
130
+
131
+ if (child.props.placement === 'start') {
132
+ startAdornment = child;
133
+ }
134
+ }
135
+ });
136
+ return {
137
+ startAdornment: startAdornment,
138
+ endAdornment: endAdornment
139
+ };
140
+ };
141
+
142
+ var _excluded = ["children", "data"],
15
143
  _excluded2 = ["disabled", "invalid"];
16
144
 
17
145
  /** Organize and emphasize information quickly and effectively in a list of text elements. */
18
146
  var TextInput = /*#__PURE__*/forwardRef(function (_ref, forwardedRef) {
19
- var data = _ref.data,
147
+ var children = _ref.children,
148
+ data = _ref.data,
20
149
  consumerProps = _objectWithoutProperties(_ref, _excluded);
21
150
 
22
151
  var _useFieldContext = useFieldContext(),
@@ -24,28 +153,47 @@ var TextInput = /*#__PURE__*/forwardRef(function (_ref, forwardedRef) {
24
153
  invalid = _useFieldContext.invalid,
25
154
  a11yProps = _objectWithoutProperties(_useFieldContext, _excluded2);
26
155
 
27
- var inputStyles = useInput({
28
- disabled: disabled,
29
- invalid: invalid
30
- });
31
- return /*#__PURE__*/jsx(Box, _objectSpread(_objectSpread(_objectSpread({
32
- as: "input",
33
- disabled: disabled,
34
- ref: forwardedRef // styles
35
- ,
156
+ var _childrenToAdornments = childrenToAdornments(children),
157
+ startAdornment = _childrenToAdornments.startAdornment,
158
+ endAdornment = _childrenToAdornments.endAdornment;
159
+
160
+ return /*#__PURE__*/jsxs(Box, {
36
161
  background: disabled ? 'inputDisabled' : 'input',
37
162
  border: invalid ? 'critical' : 'field',
38
163
  borderRadius: "small",
164
+ display: "inline-flex",
165
+ alignItems: "center",
166
+ flexDirection: "row",
39
167
  height: "medium",
40
- paddingX: "medium",
41
- className: css(inputStyles)
42
- }, data ? buildDataAttributes(data) : null), a11yProps), consumerProps));
168
+ marginY: "none",
169
+ className: css(useInput({
170
+ disabled: disabled,
171
+ invalid: invalid
172
+ })),
173
+ children: [startAdornment, /*#__PURE__*/jsx(Box, _objectSpread(_objectSpread({
174
+ as: "input",
175
+ ref: forwardedRef,
176
+ disabled: disabled // Styles
177
+ ,
178
+ flex: 1,
179
+ height: "medium",
180
+ paddingX: "medium",
181
+ paddingLeft: startAdornment ? 'none' : 'medium',
182
+ paddingRight: endAdornment ? 'none' : 'medium',
183
+ className: css(useInput({
184
+ disabled: disabled,
185
+ invalid: invalid,
186
+ isNested: true
187
+ })),
188
+ data: data
189
+ }, a11yProps), consumerProps)), endAdornment]
190
+ });
43
191
  });
44
- TextInput.displayName = 'TextInput'; // Styled components
45
- // ------------------------------
46
-
47
- function useInput(_ref2) {
48
- var disabled = _ref2.disabled;
192
+ TextInput.displayName = 'TextInput';
193
+ var useInput = function useInput(_ref2) {
194
+ var disabled = _ref2.disabled,
195
+ _ref2$isNested = _ref2.isNested,
196
+ isNested = _ref2$isNested === void 0 ? false : _ref2$isNested;
49
197
  var theme = useTheme();
50
198
  var focusRingStyles = useFocusRing({
51
199
  always: true
@@ -61,16 +209,25 @@ function useInput(_ref2) {
61
209
  typographyStyles = _textStyles[0],
62
210
  responsiveStyles = _textStyles[1];
63
211
 
64
- return _objectSpread(_objectSpread(_objectSpread({}, typographyStyles), responsiveStyles), {}, {
212
+ return _objectSpread(_objectSpread(_objectSpread({}, typographyStyles), responsiveStyles), isNested ? {
213
+ ':focus': {
214
+ // This removes the nested input outline visibility since
215
+ // the wrapper will be outlined, but still visibly focusable
216
+ // to windows high contrast mode users.
217
+ // @see https://tailwindcss.com/docs/outline-style#removing-outlines
218
+ outline: '2px solid transparent',
219
+ outlineOffset: '2px'
220
+ }
221
+ } : {
65
222
  ':enabled': {
66
- '&:hover': {
67
- borderColor: theme.border.color.fieldHover
68
- },
69
223
  '&:focus': _objectSpread(_objectSpread({}, focusRingStyles), {}, {
70
224
  borderColor: theme.border.color.fieldAccent
71
225
  })
72
- }
226
+ },
227
+ ':focus-within': _objectSpread(_objectSpread({}, focusRingStyles), {}, {
228
+ borderColor: theme.border.color.fieldAccent
229
+ })
73
230
  });
74
- }
231
+ };
75
232
 
76
- export { TextInput, useInput };
233
+ export { InputAdornment, TextInput, useInput };
package/package.json CHANGED
@@ -1,18 +1,21 @@
1
1
  {
2
2
  "name": "@spark-web/text-input",
3
- "version": "1.0.3",
3
+ "version": "1.1.0",
4
4
  "license": "MIT",
5
5
  "main": "dist/spark-web-text-input.cjs.js",
6
6
  "module": "dist/spark-web-text-input.esm.js",
7
+ "files": [
8
+ "dist"
9
+ ],
7
10
  "dependencies": {
8
11
  "@babel/runtime": "^7.14.6",
9
12
  "@emotion/css": "^11.7.1",
10
- "@spark-web/a11y": "^1.0.3",
11
- "@spark-web/box": "^1.0.3",
12
- "@spark-web/field": "^1.0.3",
13
- "@spark-web/text": "^1.0.3",
14
- "@spark-web/theme": "^2.0.2",
15
- "@spark-web/utils": "^1.1.1"
13
+ "@spark-web/a11y": "^1.0.4",
14
+ "@spark-web/box": "^1.0.4",
15
+ "@spark-web/field": "^1.1.0",
16
+ "@spark-web/text": "^1.0.4",
17
+ "@spark-web/theme": "^3.0.0",
18
+ "@spark-web/utils": "^1.1.2"
16
19
  },
17
20
  "devDependencies": {
18
21
  "@types/react": "^17.0.12",
package/CHANGELOG.md DELETED
@@ -1,75 +0,0 @@
1
- # @spark-web/text-input
2
-
3
- ## 1.0.3
4
-
5
- ### Patch Changes
6
-
7
- - [#42](https://github.com/brighte-labs/spark-web/pull/42)
8
- [`435779a`](https://github.com/brighte-labs/spark-web/commit/435779aa42bd635bbf43e1fd41724c666402caa2)
9
- Thanks [@lukebennett88](https://github.com/lukebennett88)! - Prevent multiple
10
- versions of React
11
-
12
- - Updated dependencies
13
- [[`435779a`](https://github.com/brighte-labs/spark-web/commit/435779aa42bd635bbf43e1fd41724c666402caa2)]:
14
- - @spark-web/a11y@1.0.3
15
- - @spark-web/box@1.0.3
16
- - @spark-web/field@1.0.3
17
- - @spark-web/text@1.0.3
18
- - @spark-web/theme@2.0.2
19
- - @spark-web/utils@1.1.1
20
-
21
- ## 1.0.2
22
-
23
- ### Patch Changes
24
-
25
- - [#40](https://github.com/brighte-labs/spark-web/pull/40)
26
- [`062c8ab`](https://github.com/brighte-labs/spark-web/commit/062c8ab8c7b4120f8d14c269b5f7801288c678ca)
27
- Thanks [@lukebennett88](https://github.com/lukebennett88)! - Add
28
- @babel/transform-runtime
29
-
30
- - Updated dependencies
31
- [[`062c8ab`](https://github.com/brighte-labs/spark-web/commit/062c8ab8c7b4120f8d14c269b5f7801288c678ca)]:
32
- - @spark-web/a11y@1.0.2
33
- - @spark-web/box@1.0.2
34
- - @spark-web/field@1.0.2
35
- - @spark-web/text@1.0.2
36
- - @spark-web/theme@2.0.1
37
- - @spark-web/utils@1.0.2
38
-
39
- ## 1.0.1
40
-
41
- ### Patch Changes
42
-
43
- - [#36](https://github.com/brighte-labs/spark-web/pull/36)
44
- [`8546f8f`](https://github.com/brighte-labs/spark-web/commit/8546f8f05daaa79ea3ff954c6c4928a7a2d0622d)
45
- Thanks [@lukebennett88](https://github.com/lukebennett88)! - Update Babel
46
- config
47
-
48
- - Updated dependencies
49
- [[`aebff30`](https://github.com/brighte-labs/spark-web/commit/aebff30c86cb0a9db22b545c46159ce0d1c14afb),
50
- [`8546f8f`](https://github.com/brighte-labs/spark-web/commit/8546f8f05daaa79ea3ff954c6c4928a7a2d0622d)]:
51
- - @spark-web/theme@2.0.0
52
- - @spark-web/a11y@1.0.1
53
- - @spark-web/box@1.0.1
54
- - @spark-web/field@1.0.1
55
- - @spark-web/text@1.0.1
56
- - @spark-web/utils@1.0.1
57
-
58
- ## 1.0.0
59
-
60
- ### Major Changes
61
-
62
- - [#27](https://github.com/brighte-labs/spark-web/pull/27)
63
- [`4c8e398`](https://github.com/brighte-labs/spark-web/commit/4c8e3988f8a59d3dab60a6b67b1128b6ff2a5f2c)
64
- Thanks [@JedWatson](https://github.com/JedWatson)! - Initial Version
65
-
66
- ### Patch Changes
67
-
68
- - Updated dependencies
69
- [[`4c8e398`](https://github.com/brighte-labs/spark-web/commit/4c8e3988f8a59d3dab60a6b67b1128b6ff2a5f2c)]:
70
- - @spark-web/a11y@1.0.0
71
- - @spark-web/box@1.0.0
72
- - @spark-web/field@1.0.0
73
- - @spark-web/text@1.0.0
74
- - @spark-web/theme@1.0.0
75
- - @spark-web/utils@1.0.0
@@ -1,35 +0,0 @@
1
- import { Field } from '@spark-web/field';
2
- import { InformationCircleIcon } from '@spark-web/icon';
3
- import { Inline } from '@spark-web/inline';
4
- import { Stack } from '@spark-web/stack';
5
- import { Text } from '@spark-web/text';
6
- import type { ComponentMeta, ComponentStory } from '@storybook/react';
7
-
8
- import type { TextInputProps } from './TextInput';
9
- import { TextInput } from './TextInput';
10
-
11
- export default {
12
- title: 'Forms / TextInput',
13
- component: TextInput,
14
- } as ComponentMeta<typeof TextInput>;
15
-
16
- const TextInputStory: ComponentStory<typeof TextInput> = (
17
- args: TextInputProps
18
- ) => (
19
- <Stack gap="large">
20
- <Inline gap="xsmall" alignY="center">
21
- <InformationCircleIcon tone="info" size="xsmall" />
22
- <Text weight="medium" tone="info" baseline={false}>
23
- {`Must be used inside of a <Field/>`}
24
- </Text>
25
- </Inline>
26
- <Field label="Text input">
27
- <TextInput {...args} />
28
- </Field>
29
- </Stack>
30
- );
31
- export const Default = TextInputStory.bind({});
32
-
33
- Default.args = {
34
- displayName: 'Display name',
35
- } as TextInputProps;
package/src/TextInput.tsx DELETED
@@ -1,95 +0,0 @@
1
- import { css } from '@emotion/css';
2
- import { useFocusRing } from '@spark-web/a11y';
3
- import { Box } from '@spark-web/box';
4
- import type { FieldContextType } from '@spark-web/field';
5
- import { useFieldContext } from '@spark-web/field';
6
- import { useText } from '@spark-web/text';
7
- import { useTheme } from '@spark-web/theme';
8
- import type { DataAttributeMap } from '@spark-web/utils/internal';
9
- import { buildDataAttributes } from '@spark-web/utils/internal';
10
- import type { AllHTMLAttributes } from 'react';
11
- import { forwardRef } from 'react';
12
-
13
- const validTypes = {
14
- text: 'text',
15
- password: 'password',
16
- email: 'email',
17
- search: 'search',
18
- number: 'number',
19
- tel: 'tel',
20
- url: 'url',
21
- };
22
-
23
- type NativeInputProps = Pick<
24
- AllHTMLAttributes<HTMLInputElement>,
25
- 'onBlur' | 'onFocus' | 'onChange' | 'placeholder' | 'value'
26
- >;
27
-
28
- export type TextInputProps = {
29
- /** Map of data attributes. */
30
- data?: DataAttributeMap;
31
- /**
32
- * How an input behaves varies considerably depending on the value of its type
33
- * attribute. If this attribute is not specified, the default type "text".
34
- */
35
- type?: keyof typeof validTypes;
36
- } & NativeInputProps;
37
-
38
- /** Organize and emphasize information quickly and effectively in a list of text elements. */
39
- export const TextInput = forwardRef<HTMLInputElement, TextInputProps>(
40
- ({ data, ...consumerProps }, forwardedRef) => {
41
- const { disabled, invalid, ...a11yProps } = useFieldContext();
42
- const inputStyles = useInput({ disabled, invalid });
43
-
44
- return (
45
- <Box
46
- as="input"
47
- disabled={disabled}
48
- ref={forwardedRef}
49
- // styles
50
- background={disabled ? 'inputDisabled' : 'input'}
51
- border={invalid ? 'critical' : 'field'}
52
- borderRadius="small"
53
- height="medium"
54
- paddingX="medium"
55
- className={css(inputStyles)}
56
- {...(data ? buildDataAttributes(data) : null)}
57
- {...a11yProps}
58
- {...consumerProps}
59
- />
60
- );
61
- }
62
- );
63
- TextInput.displayName = 'TextInput';
64
-
65
- // Styled components
66
- // ------------------------------
67
-
68
- export type UseInputProps = Pick<FieldContextType, 'disabled' | 'invalid'>;
69
-
70
- export function useInput({ disabled }: UseInputProps) {
71
- const theme = useTheme();
72
- const focusRingStyles = useFocusRing({ always: true });
73
- const textStyles = useText({
74
- baseline: false,
75
- tone: disabled ? 'disabled' : 'neutral',
76
- size: 'standard',
77
- weight: 'regular',
78
- });
79
-
80
- const [typographyStyles, responsiveStyles] = textStyles;
81
-
82
- return {
83
- ...typographyStyles,
84
- ...responsiveStyles,
85
- ':enabled': {
86
- '&:hover': {
87
- borderColor: theme.border.color.fieldHover,
88
- },
89
- '&:focus': {
90
- ...focusRingStyles,
91
- borderColor: theme.border.color.fieldAccent,
92
- },
93
- },
94
- };
95
- }
package/src/index.ts DELETED
@@ -1,5 +0,0 @@
1
- export { TextInput, useInput } from './TextInput';
2
-
3
- // types
4
-
5
- export type { TextInputProps, UseInputProps } from './TextInput';