@spark-web/text-input 1.0.2 → 1.1.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.
@@ -1,22 +1,152 @@
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
+ display: "flex",
67
+ alignItems: "center",
68
+ justifyContent: "center",
69
+ style: {
70
+ minWidth: sizing.xxsmall
71
+ },
72
+ children: children
73
+ })
74
+ });
75
+ }
76
+
77
+ var wrappedContent = /*#__PURE__*/jsx(InputAdornmentContext.Provider, {
78
+ value: adornmentContext,
79
+ children: content
80
+ });
81
+
82
+ if (fieldLabel) {
83
+ return /*#__PURE__*/jsx(FieldAdornment, {
84
+ fieldLabel: fieldLabel,
85
+ children: wrappedContent
86
+ });
87
+ }
88
+
89
+ return wrappedContent;
90
+ };
91
+ /**
92
+ * Wrap the element with a field provider to override the parent field label.
93
+ * Only split-out from `InputAdornment` to avoid the conditional hook rule.
94
+ */
95
+
96
+ var FieldAdornment = function FieldAdornment(_ref2) {
97
+ var children = _ref2.children,
98
+ fieldLabel = _ref2.fieldLabel;
99
+ var parentFieldContext = useFieldContext();
100
+ var fieldContext = useMemo(function () {
101
+ return _objectSpread(_objectSpread({}, parentFieldContext), {}, {
102
+ accessibilityLabel: fieldLabel
103
+ });
104
+ }, [fieldLabel, parentFieldContext]);
105
+ return /*#__PURE__*/jsx(FieldContextProvider, {
106
+ value: fieldContext,
107
+ children: children
108
+ });
109
+ };
110
+
111
+ /**
112
+ * Map children for placement within the `TextInput` flex container. Ensures that
113
+ * placeholders are provided for unused placements.
114
+ */
115
+ var childrenToAdornments = function childrenToAdornments(children) {
116
+ var startAdornment = null;
117
+ var endAdornment = null;
118
+
119
+ if (!children) {
120
+ return {
121
+ startAdornment: startAdornment,
122
+ endAdornment: endAdornment
123
+ };
124
+ }
125
+
126
+ Children.forEach(children, function (child) {
127
+ if ( /*#__PURE__*/isValidElement(child)) {
128
+ if (child.props.placement === 'end') {
129
+ endAdornment = child;
130
+ }
131
+
132
+ if (child.props.placement === 'start') {
133
+ startAdornment = child;
134
+ }
135
+ }
136
+ });
137
+ return {
138
+ startAdornment: startAdornment,
139
+ endAdornment: endAdornment
140
+ };
141
+ };
142
+
143
+ var _excluded = ["children", "data"],
15
144
  _excluded2 = ["disabled", "invalid"];
16
145
 
17
146
  /** Organize and emphasize information quickly and effectively in a list of text elements. */
18
147
  var TextInput = /*#__PURE__*/forwardRef(function (_ref, forwardedRef) {
19
- var data = _ref.data,
148
+ var children = _ref.children,
149
+ data = _ref.data,
20
150
  consumerProps = _objectWithoutProperties(_ref, _excluded);
21
151
 
22
152
  var _useFieldContext = useFieldContext(),
@@ -24,28 +154,48 @@ var TextInput = /*#__PURE__*/forwardRef(function (_ref, forwardedRef) {
24
154
  invalid = _useFieldContext.invalid,
25
155
  a11yProps = _objectWithoutProperties(_useFieldContext, _excluded2);
26
156
 
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
- ,
157
+ var _childrenToAdornments = childrenToAdornments(children),
158
+ startAdornment = _childrenToAdornments.startAdornment,
159
+ endAdornment = _childrenToAdornments.endAdornment;
160
+
161
+ return /*#__PURE__*/jsxs(Box, {
36
162
  background: disabled ? 'inputDisabled' : 'input',
37
163
  border: invalid ? 'critical' : 'field',
38
164
  borderRadius: "small",
165
+ display: "inline-flex",
166
+ alignItems: "center",
167
+ flexDirection: "row",
39
168
  height: "medium",
40
- paddingX: "medium",
41
- className: css(inputStyles)
42
- }, data ? buildDataAttributes(data) : null), a11yProps), consumerProps));
169
+ marginY: "none",
170
+ className: css(useInput({
171
+ disabled: disabled,
172
+ invalid: invalid
173
+ })),
174
+ children: [startAdornment, /*#__PURE__*/jsx(Box, _objectSpread(_objectSpread({
175
+ as: "input",
176
+ "aria-invalid": invalid || undefined,
177
+ ref: forwardedRef,
178
+ disabled: disabled // Styles
179
+ ,
180
+ flex: 1,
181
+ height: "medium",
182
+ paddingX: "medium",
183
+ paddingLeft: startAdornment ? 'none' : 'medium',
184
+ paddingRight: endAdornment ? 'none' : 'medium',
185
+ className: css(useInput({
186
+ disabled: disabled,
187
+ invalid: invalid,
188
+ isNested: true
189
+ })),
190
+ data: data
191
+ }, a11yProps), consumerProps)), endAdornment]
192
+ });
43
193
  });
44
- TextInput.displayName = 'TextInput'; // Styled components
45
- // ------------------------------
46
-
47
- function useInput(_ref2) {
48
- var disabled = _ref2.disabled;
194
+ TextInput.displayName = 'TextInput';
195
+ var useInput = function useInput(_ref2) {
196
+ var disabled = _ref2.disabled,
197
+ _ref2$isNested = _ref2.isNested,
198
+ isNested = _ref2$isNested === void 0 ? false : _ref2$isNested;
49
199
  var theme = useTheme();
50
200
  var focusRingStyles = useFocusRing({
51
201
  always: true
@@ -61,16 +211,25 @@ function useInput(_ref2) {
61
211
  typographyStyles = _textStyles[0],
62
212
  responsiveStyles = _textStyles[1];
63
213
 
64
- return _objectSpread(_objectSpread(_objectSpread({}, typographyStyles), responsiveStyles), {}, {
214
+ return _objectSpread(_objectSpread(_objectSpread({}, typographyStyles), responsiveStyles), isNested ? {
215
+ ':focus': {
216
+ // This removes the nested input outline visibility since
217
+ // the wrapper will be outlined, but still visibly focusable
218
+ // to windows high contrast mode users.
219
+ // @see https://tailwindcss.com/docs/outline-style#removing-outlines
220
+ outline: '2px solid transparent',
221
+ outlineOffset: '2px'
222
+ }
223
+ } : {
65
224
  ':enabled': {
66
- '&:hover': {
67
- borderColor: theme.border.color.fieldHover
68
- },
69
225
  '&:focus': _objectSpread(_objectSpread({}, focusRingStyles), {}, {
70
226
  borderColor: theme.border.color.fieldAccent
71
227
  })
72
- }
228
+ },
229
+ ':focus-within': _objectSpread(_objectSpread({}, focusRingStyles), {}, {
230
+ borderColor: theme.border.color.fieldAccent
231
+ })
73
232
  });
74
- }
233
+ };
75
234
 
76
- export { TextInput, useInput };
235
+ export { InputAdornment, TextInput, useInput };
package/package.json CHANGED
@@ -1,23 +1,29 @@
1
1
  {
2
2
  "name": "@spark-web/text-input",
3
+ "version": "1.1.1",
3
4
  "license": "MIT",
4
- "version": "1.0.2",
5
5
  "main": "dist/spark-web-text-input.cjs.js",
6
6
  "module": "dist/spark-web-text-input.esm.js",
7
- "devDependencies": {
8
- "@types/react": "^17.0.12"
9
- },
7
+ "files": [
8
+ "dist"
9
+ ],
10
10
  "dependencies": {
11
11
  "@babel/runtime": "^7.14.6",
12
12
  "@emotion/css": "^11.7.1",
13
- "@spark-web/a11y": "^1.0.2",
14
- "@spark-web/box": "^1.0.2",
15
- "@spark-web/field": "^1.0.2",
16
- "@spark-web/text": "^1.0.2",
17
- "@spark-web/theme": "^2.0.1",
18
- "@spark-web/utils": "^1.0.2",
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"
19
+ },
20
+ "devDependencies": {
21
+ "@types/react": "^17.0.12",
19
22
  "react": "^17.0.2"
20
23
  },
24
+ "peerDependencies": {
25
+ "react": ">=17.0.2"
26
+ },
21
27
  "engines": {
22
28
  "node": ">= 14.13"
23
29
  }
package/CHANGELOG.md DELETED
@@ -1,57 +0,0 @@
1
- # @spark-web/text-input
2
-
3
- ## 1.0.2
4
-
5
- ### Patch Changes
6
-
7
- - [#40](https://github.com/brighte-labs/spark-web/pull/40)
8
- [`062c8ab`](https://github.com/brighte-labs/spark-web/commit/062c8ab8c7b4120f8d14c269b5f7801288c678ca)
9
- Thanks [@lukebennett88](https://github.com/lukebennett88)! - Add
10
- @babel/transform-runtime
11
-
12
- - Updated dependencies
13
- [[`062c8ab`](https://github.com/brighte-labs/spark-web/commit/062c8ab8c7b4120f8d14c269b5f7801288c678ca)]:
14
- - @spark-web/a11y@1.0.2
15
- - @spark-web/box@1.0.2
16
- - @spark-web/field@1.0.2
17
- - @spark-web/text@1.0.2
18
- - @spark-web/theme@2.0.1
19
- - @spark-web/utils@1.0.2
20
-
21
- ## 1.0.1
22
-
23
- ### Patch Changes
24
-
25
- - [#36](https://github.com/brighte-labs/spark-web/pull/36)
26
- [`8546f8f`](https://github.com/brighte-labs/spark-web/commit/8546f8f05daaa79ea3ff954c6c4928a7a2d0622d)
27
- Thanks [@lukebennett88](https://github.com/lukebennett88)! - Update Babel
28
- config
29
-
30
- - Updated dependencies
31
- [[`aebff30`](https://github.com/brighte-labs/spark-web/commit/aebff30c86cb0a9db22b545c46159ce0d1c14afb),
32
- [`8546f8f`](https://github.com/brighte-labs/spark-web/commit/8546f8f05daaa79ea3ff954c6c4928a7a2d0622d)]:
33
- - @spark-web/theme@2.0.0
34
- - @spark-web/a11y@1.0.1
35
- - @spark-web/box@1.0.1
36
- - @spark-web/field@1.0.1
37
- - @spark-web/text@1.0.1
38
- - @spark-web/utils@1.0.1
39
-
40
- ## 1.0.0
41
-
42
- ### Major Changes
43
-
44
- - [#27](https://github.com/brighte-labs/spark-web/pull/27)
45
- [`4c8e398`](https://github.com/brighte-labs/spark-web/commit/4c8e3988f8a59d3dab60a6b67b1128b6ff2a5f2c)
46
- Thanks [@JedWatson](https://github.com/JedWatson)! - Initial Version
47
-
48
- ### Patch Changes
49
-
50
- - Updated dependencies
51
- [[`4c8e398`](https://github.com/brighte-labs/spark-web/commit/4c8e3988f8a59d3dab60a6b67b1128b6ff2a5f2c)]:
52
- - @spark-web/a11y@1.0.0
53
- - @spark-web/box@1.0.0
54
- - @spark-web/field@1.0.0
55
- - @spark-web/text@1.0.0
56
- - @spark-web/theme@1.0.0
57
- - @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';