@charcoal-ui/react 3.4.0 → 3.6.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.
Files changed (77) hide show
  1. package/dist/components/Button/index.d.ts.map +1 -1
  2. package/dist/components/Checkbox/index.d.ts.map +1 -1
  3. package/dist/components/Checkbox/index.story.d.ts +1 -0
  4. package/dist/components/Checkbox/index.story.d.ts.map +1 -1
  5. package/dist/components/DropdownSelector/DropdownMenuItem.d.ts.map +1 -1
  6. package/dist/components/DropdownSelector/ListItem/index.d.ts.map +1 -1
  7. package/dist/components/DropdownSelector/Popover/index.d.ts.map +1 -1
  8. package/dist/components/DropdownSelector/index.d.ts.map +1 -1
  9. package/dist/components/FieldLabel/index.d.ts.map +1 -1
  10. package/dist/components/IconButton/index.d.ts.map +1 -1
  11. package/dist/components/LoadingSpinner/index.d.ts.map +1 -1
  12. package/dist/components/Modal/ModalPlumbing.d.ts.map +1 -1
  13. package/dist/components/Modal/index.d.ts.map +1 -1
  14. package/dist/components/Modal/index.story.d.ts +1 -0
  15. package/dist/components/Modal/index.story.d.ts.map +1 -1
  16. package/dist/components/MultiSelect/index.d.ts.map +1 -1
  17. package/dist/components/MultiSelect/index.story.d.ts +2 -0
  18. package/dist/components/MultiSelect/index.story.d.ts.map +1 -1
  19. package/dist/components/Radio/index.d.ts.map +1 -1
  20. package/dist/components/SegmentedControl/index.d.ts.map +1 -1
  21. package/dist/components/Switch/index.d.ts.map +1 -1
  22. package/dist/components/TagItem/index.d.ts.map +1 -1
  23. package/dist/components/TextArea/index.d.ts.map +1 -1
  24. package/dist/components/TextField/TextField.story.d.ts.map +1 -1
  25. package/dist/components/TextField/index.d.ts +4 -0
  26. package/dist/components/TextField/index.d.ts.map +1 -1
  27. package/dist/components/TextField/useFocusWithClick.d.ts +3 -0
  28. package/dist/components/TextField/useFocusWithClick.d.ts.map +1 -0
  29. package/dist/index.cjs.js +935 -494
  30. package/dist/index.cjs.js.map +1 -1
  31. package/dist/index.esm.js +863 -422
  32. package/dist/index.esm.js.map +1 -1
  33. package/dist/styled.d.ts +4 -4
  34. package/package.json +9 -9
  35. package/src/components/Button/__snapshots__/index.story.storyshot +312 -592
  36. package/src/components/Button/index.tsx +50 -14
  37. package/src/components/Checkbox/__snapshots__/index.story.storyshot +209 -40
  38. package/src/components/Checkbox/__snapshots__/snapshot.test.tsx.snap +410 -80
  39. package/src/components/Checkbox/index.story.tsx +24 -0
  40. package/src/components/Checkbox/index.tsx +47 -17
  41. package/src/components/Clickable/__snapshots__/index.story.storyshot +4 -0
  42. package/src/components/Clickable/index.tsx +1 -0
  43. package/src/components/DropdownSelector/DropdownMenuItem.tsx +19 -3
  44. package/src/components/DropdownSelector/ListItem/__snapshots__/index.story.storyshot +3 -10
  45. package/src/components/DropdownSelector/ListItem/index.tsx +6 -4
  46. package/src/components/DropdownSelector/MenuItemGroup/index.tsx +1 -1
  47. package/src/components/DropdownSelector/MenuList/__snapshots__/index.story.storyshot +4 -13
  48. package/src/components/DropdownSelector/Popover/__snapshots__/index.story.storyshot +20 -37
  49. package/src/components/DropdownSelector/Popover/index.tsx +5 -8
  50. package/src/components/DropdownSelector/__snapshots__/index.story.storyshot +60 -191
  51. package/src/components/DropdownSelector/index.tsx +70 -20
  52. package/src/components/FieldLabel/index.tsx +77 -12
  53. package/src/components/IconButton/__snapshots__/index.story.storyshot +30 -54
  54. package/src/components/IconButton/index.tsx +51 -26
  55. package/src/components/LoadingSpinner/index.tsx +3 -6
  56. package/src/components/Modal/Dialog/index.tsx +1 -1
  57. package/src/components/Modal/ModalPlumbing.tsx +26 -5
  58. package/src/components/Modal/__snapshots__/index.story.storyshot +2454 -108
  59. package/src/components/Modal/index.story.tsx +27 -0
  60. package/src/components/Modal/index.tsx +19 -4
  61. package/src/components/MultiSelect/__snapshots__/index.story.storyshot +528 -25
  62. package/src/components/MultiSelect/index.story.tsx +60 -0
  63. package/src/components/MultiSelect/index.tsx +82 -22
  64. package/src/components/Radio/__snapshots__/index.story.storyshot +32 -49
  65. package/src/components/Radio/index.tsx +71 -23
  66. package/src/components/SegmentedControl/__snapshots__/index.story.storyshot +18 -19
  67. package/src/components/SegmentedControl/index.tsx +35 -15
  68. package/src/components/Switch/__snapshots__/index.story.storyshot +6 -18
  69. package/src/components/Switch/index.tsx +10 -15
  70. package/src/components/TagItem/__snapshots__/index.story.storyshot +39 -158
  71. package/src/components/TagItem/index.tsx +84 -19
  72. package/src/components/TextArea/__snapshots__/TextArea.story.storyshot +126 -321
  73. package/src/components/TextArea/index.tsx +38 -43
  74. package/src/components/TextField/TextField.story.tsx +35 -19
  75. package/src/components/TextField/__snapshots__/TextField.story.storyshot +309 -563
  76. package/src/components/TextField/index.tsx +85 -84
  77. package/src/components/TextField/useFocusWithClick.tsx +22 -0
@@ -2,11 +2,11 @@ import { useTextField } from '@react-aria/textfield'
2
2
  import { useVisuallyHidden } from '@react-aria/visually-hidden'
3
3
  import { ReactNode, useCallback, useEffect, useRef, useState } from 'react'
4
4
  import * as React from 'react'
5
- import styled from 'styled-components'
5
+ import styled, { css } from 'styled-components'
6
6
  import FieldLabel, { FieldLabelProps } from '../FieldLabel'
7
7
  import { countCodePointsInString, mergeRefs } from '../../_lib'
8
- import { theme } from '../../styled'
9
8
  import { ReactAreaUseTextFieldCompat } from '../../_lib/compat'
9
+ import { useFocusWithClick } from './useFocusWithClick'
10
10
 
11
11
  type DOMProps = Omit<
12
12
  React.InputHTMLAttributes<HTMLInputElement>,
@@ -64,13 +64,9 @@ const TextField = React.forwardRef<HTMLInputElement, TextFieldProps>(
64
64
 
65
65
  const { visuallyHiddenProps } = useVisuallyHidden()
66
66
  const ariaRef = useRef<HTMLInputElement>(null)
67
- const prefixRef = useRef<HTMLSpanElement>(null)
68
- const suffixRef = useRef<HTMLSpanElement>(null)
69
67
  const [count, setCount] = useState(
70
68
  countCodePointsInString(props.value ?? '')
71
69
  )
72
- const [prefixWidth, setPrefixWidth] = useState(0)
73
- const [suffixWidth, setSuffixWidth] = useState(0)
74
70
 
75
71
  const nonControlled = props.value === undefined
76
72
  const handleChange = useCallback(
@@ -106,26 +102,9 @@ const TextField = React.forwardRef<HTMLInputElement, TextFieldProps>(
106
102
  ariaRef
107
103
  )
108
104
 
109
- useEffect(() => {
110
- const prefixObserver = new ResizeObserver((entries) => {
111
- setPrefixWidth(entries[0].contentRect.width)
112
- })
113
- const suffixObserver = new ResizeObserver((entries) => {
114
- setSuffixWidth(entries[0].contentRect.width)
115
- })
116
-
117
- if (prefixRef.current !== null) {
118
- prefixObserver.observe(prefixRef.current)
119
- }
120
- if (suffixRef.current !== null) {
121
- suffixObserver.observe(suffixRef.current)
122
- }
123
-
124
- return () => {
125
- suffixObserver.disconnect()
126
- prefixObserver.disconnect()
127
- }
128
- }, [])
105
+ const containerRef = useRef(null)
106
+
107
+ useFocusWithClick(containerRef, ariaRef)
129
108
 
130
109
  return (
131
110
  <TextFieldRoot className={className} isDisabled={disabled}>
@@ -137,25 +116,29 @@ const TextField = React.forwardRef<HTMLInputElement, TextFieldProps>(
137
116
  {...labelProps}
138
117
  {...(!showLabel ? visuallyHiddenProps : {})}
139
118
  />
140
- <StyledInputContainer>
141
- <PrefixContainer ref={prefixRef}>
142
- <Affix>{prefix}</Affix>
143
- </PrefixContainer>
119
+ <StyledInputContainer
120
+ ref={containerRef}
121
+ invalid={invalid}
122
+ aria-disabled={disabled === true ? true : undefined}
123
+ hasPrefix={prefix != null}
124
+ hasSuffix={suffix != null || showCount}
125
+ >
126
+ {prefix && <PrefixContainer>{prefix}</PrefixContainer>}
144
127
  <StyledInput
145
128
  ref={mergeRefs(forwardRef, ariaRef)}
146
129
  invalid={invalid}
147
- extraLeftPadding={prefixWidth}
148
- extraRightPadding={suffixWidth}
149
130
  {...inputProps}
150
131
  />
151
- <SuffixContainer ref={suffixRef}>
152
- <Affix>{suffix}</Affix>
153
- {showCount && (
154
- <SingleLineCounter>
155
- {maxLength !== undefined ? `${count}/${maxLength}` : count}
156
- </SingleLineCounter>
157
- )}
158
- </SuffixContainer>
132
+ {(suffix || showCount) && (
133
+ <SuffixContainer>
134
+ {suffix}
135
+ {showCount && (
136
+ <SingleLineCounter>
137
+ {maxLength !== undefined ? `${count}/${maxLength}` : count}
138
+ </SingleLineCounter>
139
+ )}
140
+ </SuffixContainer>
141
+ )}
159
142
  </StyledInputContainer>
160
143
  {assistiveText != null && assistiveText.length !== 0 && (
161
144
  <AssistiveText
@@ -179,44 +162,68 @@ const TextFieldRoot = styled.div<{ isDisabled: boolean }>`
179
162
  ${(p) => p.isDisabled && { opacity: p.theme.elementEffect.disabled.opacity }}
180
163
  `
181
164
 
182
- const TextFieldLabel = styled(FieldLabel)`
183
- ${theme((o) => o.margin.bottom(8))}
165
+ export const TextFieldLabel = styled(FieldLabel)`
166
+ margin-bottom: 8px;
184
167
  `
185
168
 
186
- const StyledInputContainer = styled.div`
187
- height: 40px;
169
+ const StyledInputContainer = styled.div<{
170
+ invalid: boolean
171
+ hasPrefix: boolean
172
+ hasSuffix: boolean
173
+ }>`
188
174
  display: grid;
189
- position: relative;
175
+ grid-template-columns: ${(p) =>
176
+ [p.hasPrefix && 'auto', '1fr', p.hasSuffix && 'auto']
177
+ .filter(Boolean)
178
+ .join(' ')};
179
+ height: 40px;
180
+ transition: 0.2s background-color, 0.2s box-shadow;
181
+ color: var(--charcoal-text2);
182
+ background-color: var(--charcoal-surface3);
183
+ border-radius: 4px;
184
+ gap: 4px;
185
+ padding: 0 8px;
186
+ line-height: 22px;
187
+ font-size: 14px;
188
+
189
+ :not(:disabled):not([aria-disabled]):hover,
190
+ [aria-disabled='false']:hover {
191
+ background-color: var(--charcoal-surface3-hover);
192
+ }
193
+
194
+ :not(:disabled):not([aria-disabled]):active,
195
+ [aria-disabled='false']:active {
196
+ outline: none;
197
+ box-shadow: 0 0 0 4px
198
+ ${(p) => (p.invalid ? `rgba(255,43,0,0.32)` : `rgba(0, 150, 250, 0.32);`)};
199
+ }
200
+
201
+ :focus-within {
202
+ outline: none;
203
+ box-shadow: 0 0 0 4px
204
+ ${(p) => (p.invalid ? `rgba(255,43,0,0.32)` : `rgba(0, 150, 250, 0.32);`)};
205
+ }
206
+
207
+ ${(p) =>
208
+ p.invalid &&
209
+ css`
210
+ box-shadow: 0 0 0 4px rgba(255, 43, 0, 0.32);
211
+ `}
190
212
  `
191
213
 
192
- const PrefixContainer = styled.span`
193
- position: absolute;
194
- top: 50%;
195
- left: 8px;
196
- transform: translateY(-50%);
197
- z-index: 1;
214
+ const PrefixContainer = styled.div`
215
+ display: flex;
216
+ align-items: center;
198
217
  `
199
218
 
200
219
  const SuffixContainer = styled.span`
201
- position: absolute;
202
- top: 50%;
203
- right: 8px;
204
- transform: translateY(-50%);
205
-
206
220
  display: flex;
221
+ align-items: center;
207
222
  gap: 8px;
208
223
  `
209
224
 
210
- const Affix = styled.span`
211
- user-select: none;
212
-
213
- ${theme((o) => [o.typography(14).preserveHalfLeading, o.font.text2])}
214
- `
215
-
216
225
  const StyledInput = styled.input<{
217
226
  invalid: boolean
218
- extraLeftPadding: number
219
- extraRightPadding: number
220
227
  }>`
221
228
  border: none;
222
229
  box-sizing: border-box;
@@ -230,36 +237,30 @@ const StyledInput = styled.input<{
230
237
  height: calc(100% / 0.875);
231
238
  font-size: calc(14px / 0.875);
232
239
  line-height: calc(22px / 0.875);
233
- padding-left: calc((8px + ${(p) => p.extraLeftPadding}px) / 0.875);
234
- padding-right: calc((8px + ${(p) => p.extraRightPadding}px) / 0.875);
240
+ padding-left: 0;
241
+ padding-right: 0;
235
242
  border-radius: calc(4px / 0.875);
236
243
 
237
244
  /* Display box-shadow for iOS Safari */
238
245
  appearance: none;
246
+ background: transparent;
239
247
 
240
- ${(p) =>
241
- theme((o) => [
242
- o.bg.surface3.hover,
243
- o.outline.default.focus,
244
- p.invalid && o.outline.assertive,
245
- o.font.text2,
246
- ])}
247
-
248
+ color: var(--charcoal-text2);
248
249
  &::placeholder {
249
- ${theme((o) => o.font.text3)}
250
+ color: var(--charcoal-text3);
250
251
  }
251
252
  `
252
253
 
253
254
  const SingleLineCounter = styled.span`
254
- ${theme((o) => [o.typography(14).preserveHalfLeading, o.font.text3])}
255
+ line-height: 22px;
256
+ font-size: 14px;
257
+ color: var(--charcoal-text3);
255
258
  `
256
259
 
257
- const AssistiveText = styled.p<{ invalid: boolean }>`
258
- ${(p) =>
259
- theme((o) => [
260
- o.typography(14),
261
- o.margin.top(8),
262
- o.margin.bottom(0),
263
- o.font[p.invalid ? 'assertive' : 'text1'],
264
- ])}
260
+ export const AssistiveText = styled.p<{ invalid: boolean }>`
261
+ font-size: 14px;
262
+ line-height: 22px;
263
+ margin-top: 4px;
264
+ margin-bottom: -4px;
265
+ color: ${(p) => `var(--charcoal-${p.invalid ? `assertive` : `text2`})`};
265
266
  `
@@ -0,0 +1,22 @@
1
+ import { useEffect } from 'react'
2
+ import * as React from 'react'
3
+
4
+ export function useFocusWithClick(
5
+ containerRef: React.RefObject<HTMLDivElement>,
6
+ inputRef: React.RefObject<HTMLInputElement | HTMLTextAreaElement>
7
+ ) {
8
+ useEffect(() => {
9
+ const el = containerRef.current
10
+ if (el) {
11
+ const handleDown = (e: MouseEvent) => {
12
+ if (e.target !== inputRef.current) {
13
+ inputRef.current?.focus()
14
+ }
15
+ }
16
+ el.addEventListener('click', handleDown)
17
+ return () => {
18
+ el.removeEventListener('click', handleDown)
19
+ }
20
+ }
21
+ })
22
+ }