@charcoal-ui/react 3.0.0-beta.2 → 3.0.0-beta.4

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 (186) hide show
  1. package/dist/_lib/compat.d.ts +19 -1
  2. package/dist/_lib/compat.d.ts.map +1 -1
  3. package/dist/_lib/index.d.ts +7 -0
  4. package/dist/_lib/index.d.ts.map +1 -1
  5. package/dist/components/Button/index.d.ts +1 -2
  6. package/dist/components/Button/index.d.ts.map +1 -1
  7. package/dist/components/Button/index.story.d.ts +1 -2
  8. package/dist/components/Button/index.story.d.ts.map +1 -1
  9. package/dist/components/Button/index.test.d.ts +4 -0
  10. package/dist/components/Button/index.test.d.ts.map +1 -0
  11. package/dist/components/Checkbox/index.d.ts +2 -1
  12. package/dist/components/Checkbox/index.d.ts.map +1 -1
  13. package/dist/components/Checkbox/index.story.d.ts +2 -2
  14. package/dist/components/Checkbox/index.story.d.ts.map +1 -1
  15. package/dist/components/Clickable/index.d.ts +1 -1
  16. package/dist/components/Clickable/index.d.ts.map +1 -1
  17. package/dist/components/Clickable/index.story.d.ts +1 -2
  18. package/dist/components/Clickable/index.story.d.ts.map +1 -1
  19. package/dist/components/DropdownSelector/Divider.d.ts +3 -0
  20. package/dist/components/DropdownSelector/Divider.d.ts.map +1 -1
  21. package/dist/components/DropdownSelector/DropdownMenuItem.d.ts +7 -0
  22. package/dist/components/DropdownSelector/DropdownMenuItem.d.ts.map +1 -0
  23. package/dist/components/DropdownSelector/DropdownPopover.d.ts +8 -8
  24. package/dist/components/DropdownSelector/DropdownPopover.d.ts.map +1 -1
  25. package/dist/components/DropdownSelector/ListItem/index.d.ts +18 -0
  26. package/dist/components/DropdownSelector/ListItem/index.d.ts.map +1 -0
  27. package/dist/components/DropdownSelector/ListItem/index.story.d.ts +9 -0
  28. package/dist/components/DropdownSelector/ListItem/index.story.d.ts.map +1 -0
  29. package/dist/components/DropdownSelector/MenuItem/index.d.ts +11 -0
  30. package/dist/components/DropdownSelector/MenuItem/index.d.ts.map +1 -0
  31. package/dist/components/DropdownSelector/MenuItem/internals/handleFocusByKeyBoard.d.ts +9 -0
  32. package/dist/components/DropdownSelector/MenuItem/internals/handleFocusByKeyBoard.d.ts.map +1 -0
  33. package/dist/components/DropdownSelector/MenuItem/internals/useMenuItemHandleKeyDown.d.ts +10 -0
  34. package/dist/components/DropdownSelector/MenuItem/internals/useMenuItemHandleKeyDown.d.ts.map +1 -0
  35. package/dist/components/DropdownSelector/MenuItemGroup/index.d.ts +14 -0
  36. package/dist/components/DropdownSelector/MenuItemGroup/index.d.ts.map +1 -0
  37. package/dist/components/DropdownSelector/MenuList/MenuListContext.d.ts +10 -0
  38. package/dist/components/DropdownSelector/MenuList/MenuListContext.d.ts.map +1 -0
  39. package/dist/components/DropdownSelector/MenuList/index.d.ts +18 -0
  40. package/dist/components/DropdownSelector/MenuList/index.d.ts.map +1 -0
  41. package/dist/components/DropdownSelector/MenuList/index.story.d.ts +11 -0
  42. package/dist/components/DropdownSelector/MenuList/index.story.d.ts.map +1 -0
  43. package/dist/components/DropdownSelector/MenuList/internals/getValuesRecursive.d.ts +11 -0
  44. package/dist/components/DropdownSelector/MenuList/internals/getValuesRecursive.d.ts.map +1 -0
  45. package/dist/components/DropdownSelector/Popover/index.d.ts +17 -0
  46. package/dist/components/DropdownSelector/Popover/index.d.ts.map +1 -0
  47. package/dist/components/DropdownSelector/Popover/index.story.d.ts +9 -0
  48. package/dist/components/DropdownSelector/Popover/index.story.d.ts.map +1 -0
  49. package/dist/components/DropdownSelector/index.d.ts +3 -10
  50. package/dist/components/DropdownSelector/index.d.ts.map +1 -1
  51. package/dist/components/DropdownSelector/index.story.d.ts +4 -4
  52. package/dist/components/DropdownSelector/index.story.d.ts.map +1 -1
  53. package/dist/components/DropdownSelector/utils/findPreviewRecursive.d.ts +12 -0
  54. package/dist/components/DropdownSelector/utils/findPreviewRecursive.d.ts.map +1 -0
  55. package/dist/components/FieldLabel/index.d.ts +1 -1
  56. package/dist/components/FieldLabel/index.d.ts.map +1 -1
  57. package/dist/components/Icon/index.d.ts +1 -1
  58. package/dist/components/Icon/index.d.ts.map +1 -1
  59. package/dist/components/Icon/index.story.d.ts +2 -3
  60. package/dist/components/Icon/index.story.d.ts.map +1 -1
  61. package/dist/components/IconButton/index.d.ts +1 -2
  62. package/dist/components/IconButton/index.d.ts.map +1 -1
  63. package/dist/components/IconButton/index.story.d.ts +1 -2
  64. package/dist/components/IconButton/index.story.d.ts.map +1 -1
  65. package/dist/components/LoadingSpinner/index.d.ts +9 -8
  66. package/dist/components/LoadingSpinner/index.d.ts.map +1 -1
  67. package/dist/components/LoadingSpinner/index.story.d.ts +1 -2
  68. package/dist/components/LoadingSpinner/index.story.d.ts.map +1 -1
  69. package/dist/components/Modal/ModalPlumbing.d.ts.map +1 -1
  70. package/dist/components/Modal/index.d.ts +18 -27
  71. package/dist/components/Modal/index.d.ts.map +1 -1
  72. package/dist/components/Modal/index.story.d.ts +12 -2
  73. package/dist/components/Modal/index.story.d.ts.map +1 -1
  74. package/dist/components/MultiSelect/context.d.ts +1 -1
  75. package/dist/components/MultiSelect/context.d.ts.map +1 -1
  76. package/dist/components/MultiSelect/index.d.ts +18 -6
  77. package/dist/components/MultiSelect/index.d.ts.map +1 -1
  78. package/dist/components/MultiSelect/index.story.d.ts +21 -16
  79. package/dist/components/MultiSelect/index.story.d.ts.map +1 -1
  80. package/dist/components/Radio/index.d.ts +13 -6
  81. package/dist/components/Radio/index.d.ts.map +1 -1
  82. package/dist/components/Radio/index.story.d.ts +11 -8
  83. package/dist/components/Radio/index.story.d.ts.map +1 -1
  84. package/dist/components/SegmentedControl/RadioGroupContext.d.ts +1 -1
  85. package/dist/components/SegmentedControl/RadioGroupContext.d.ts.map +1 -1
  86. package/dist/components/SegmentedControl/index.d.ts +2 -1
  87. package/dist/components/SegmentedControl/index.d.ts.map +1 -1
  88. package/dist/components/SegmentedControl/index.story.d.ts +1 -2
  89. package/dist/components/SegmentedControl/index.story.d.ts.map +1 -1
  90. package/dist/components/Switch/index.d.ts +3 -2
  91. package/dist/components/Switch/index.d.ts.map +1 -1
  92. package/dist/components/Switch/index.story.d.ts +1 -2
  93. package/dist/components/Switch/index.story.d.ts.map +1 -1
  94. package/dist/components/TagItem/index.d.ts +3 -3
  95. package/dist/components/TagItem/index.d.ts.map +1 -1
  96. package/dist/components/TagItem/index.story.d.ts +2 -3
  97. package/dist/components/TagItem/index.story.d.ts.map +1 -1
  98. package/dist/components/TextArea/TextArea.story.d.ts +28 -0
  99. package/dist/components/TextArea/TextArea.story.d.ts.map +1 -0
  100. package/dist/components/TextArea/index.d.ts +21 -0
  101. package/dist/components/TextArea/index.d.ts.map +1 -0
  102. package/dist/components/TextField/TextField.story.d.ts +28 -0
  103. package/dist/components/TextField/TextField.story.d.ts.map +1 -0
  104. package/dist/components/TextField/index.d.ts +8 -30
  105. package/dist/components/TextField/index.d.ts.map +1 -1
  106. package/dist/core/CharcoalProvider.d.ts +1 -1
  107. package/dist/core/CharcoalProvider.d.ts.map +1 -1
  108. package/dist/core/ComponentAbstraction.d.ts +1 -1
  109. package/dist/core/ComponentAbstraction.d.ts.map +1 -1
  110. package/dist/index.cjs.js +1064 -771
  111. package/dist/index.cjs.js.map +1 -1
  112. package/dist/index.d.ts +5 -3
  113. package/dist/index.d.ts.map +1 -1
  114. package/dist/index.esm.js +1028 -750
  115. package/dist/index.esm.js.map +1 -1
  116. package/dist/styled.d.ts +13 -13
  117. package/package.json +7 -7
  118. package/src/_lib/compat.ts +20 -1
  119. package/src/_lib/index.ts +23 -0
  120. package/src/components/Button/__snapshots__/index.test.tsx.snap +385 -0
  121. package/src/components/Button/index.story.tsx +1 -1
  122. package/src/components/Button/index.test.tsx +24 -0
  123. package/src/components/Button/index.tsx +2 -2
  124. package/src/components/Checkbox/index.story.tsx +1 -1
  125. package/src/components/Checkbox/index.tsx +4 -2
  126. package/src/components/Clickable/index.story.tsx +0 -1
  127. package/src/components/Clickable/index.tsx +1 -1
  128. package/src/components/DropdownSelector/Divider.tsx +3 -0
  129. package/src/components/DropdownSelector/DropdownMenuItem.tsx +40 -0
  130. package/src/components/DropdownSelector/DropdownPopover.tsx +21 -42
  131. package/src/components/DropdownSelector/ListItem/index.story.tsx +51 -0
  132. package/src/components/DropdownSelector/ListItem/index.tsx +58 -0
  133. package/src/components/DropdownSelector/MenuItem/index.tsx +31 -0
  134. package/src/components/DropdownSelector/MenuItem/internals/handleFocusByKeyBoard.tsx +43 -0
  135. package/src/components/DropdownSelector/MenuItem/internals/useMenuItemHandleKeyDown.tsx +55 -0
  136. package/src/components/DropdownSelector/MenuItemGroup/index.tsx +42 -0
  137. package/src/components/DropdownSelector/MenuList/MenuListContext.ts +17 -0
  138. package/src/components/DropdownSelector/MenuList/index.story.tsx +51 -0
  139. package/src/components/DropdownSelector/MenuList/index.tsx +51 -0
  140. package/src/components/DropdownSelector/MenuList/internals/getValuesRecursive.tsx +35 -0
  141. package/src/components/DropdownSelector/Popover/index.story.tsx +65 -0
  142. package/src/components/DropdownSelector/Popover/index.tsx +69 -0
  143. package/src/components/DropdownSelector/index.story.tsx +56 -21
  144. package/src/components/DropdownSelector/index.tsx +19 -60
  145. package/src/components/DropdownSelector/utils/findPreviewRecursive.tsx +39 -0
  146. package/src/components/FieldLabel/index.tsx +1 -1
  147. package/src/components/Icon/index.story.tsx +0 -1
  148. package/src/components/Icon/index.tsx +1 -1
  149. package/src/components/IconButton/index.story.tsx +0 -1
  150. package/src/components/IconButton/index.tsx +2 -2
  151. package/src/components/LoadingSpinner/index.story.tsx +8 -2
  152. package/src/components/LoadingSpinner/index.tsx +44 -29
  153. package/src/components/Modal/ModalPlumbing.tsx +0 -1
  154. package/src/components/Modal/index.story.tsx +0 -1
  155. package/src/components/Modal/index.tsx +19 -12
  156. package/src/components/MultiSelect/context.ts +2 -2
  157. package/src/components/MultiSelect/index.story.tsx +26 -27
  158. package/src/components/MultiSelect/index.test.tsx +5 -23
  159. package/src/components/MultiSelect/index.tsx +83 -78
  160. package/src/components/Radio/index.story.tsx +7 -9
  161. package/src/components/Radio/index.test.tsx +3 -4
  162. package/src/components/Radio/index.tsx +24 -23
  163. package/src/components/SegmentedControl/RadioGroupContext.tsx +2 -1
  164. package/src/components/SegmentedControl/index.story.tsx +0 -1
  165. package/src/components/SegmentedControl/index.tsx +16 -5
  166. package/src/components/Switch/index.story.tsx +1 -1
  167. package/src/components/Switch/index.tsx +38 -32
  168. package/src/components/TagItem/index.story.tsx +0 -1
  169. package/src/components/TagItem/index.tsx +1 -6
  170. package/src/components/TextArea/TextArea.story.tsx +61 -0
  171. package/src/components/TextArea/index.tsx +246 -0
  172. package/src/components/TextField/{index.story.tsx → TextField.story.tsx} +6 -29
  173. package/src/components/TextField/index.tsx +148 -378
  174. package/src/components/a11y.test.tsx +0 -1
  175. package/src/core/CharcoalProvider.tsx +1 -1
  176. package/src/core/ComponentAbstraction.tsx +2 -1
  177. package/src/index.ts +8 -6
  178. package/dist/components/DropdownSelector/OptionItem.d.ts +0 -7
  179. package/dist/components/DropdownSelector/OptionItem.d.ts.map +0 -1
  180. package/dist/components/DropdownSelector/utils/focusIfHTMLLIElement.d.ts +0 -6
  181. package/dist/components/DropdownSelector/utils/focusIfHTMLLIElement.d.ts.map +0 -1
  182. package/dist/components/DropdownSelector/utils/handleFocusByKeyBoard.d.ts +0 -6
  183. package/dist/components/DropdownSelector/utils/handleFocusByKeyBoard.d.ts.map +0 -1
  184. package/src/components/DropdownSelector/OptionItem.tsx +0 -85
  185. package/src/components/DropdownSelector/utils/focusIfHTMLLIElement.tsx +0 -12
  186. package/src/components/DropdownSelector/utils/handleFocusByKeyBoard.tsx +0 -20
@@ -1,331 +1,176 @@
1
1
  import { useTextField } from '@react-aria/textfield'
2
2
  import { useVisuallyHidden } from '@react-aria/visually-hidden'
3
- import React, {
4
- ReactNode,
5
- useCallback,
6
- useEffect,
7
- useRef,
8
- useState,
9
- } from 'react'
10
- import styled, { css } from 'styled-components'
3
+ import { ReactNode, useCallback, useEffect, useRef, useState } from 'react'
4
+ import * as React from 'react'
5
+ import styled from 'styled-components'
11
6
  import FieldLabel, { FieldLabelProps } from '../FieldLabel'
12
- import { createTheme } from '@charcoal-ui/styled'
13
-
14
- const theme = createTheme(styled)
7
+ import { countCodePointsInString, mergeRefs } from '../../_lib'
8
+ import { theme } from '../../styled'
9
+ import { ReactAreaUseTextFieldCompat } from '../../_lib/compat'
10
+
11
+ type DOMProps = Omit<
12
+ React.InputHTMLAttributes<HTMLInputElement>,
13
+ // react-ariaのhookは、onChangeが`(v: string) => void`想定
14
+ | 'onChange'
15
+
16
+ // RDFa Attributeとかぶる
17
+ // https://github.com/DefinitelyTyped/DefinitelyTyped/blob/58d57ca87ac7be0d306c0844dc254e90c150bd0d/types/react/index.d.ts#L1951
18
+ | 'prefix'
19
+
20
+ // ReactAreaUseTextFieldCompatに書いてあるような事情で、ここにあるイベントハンドラの型をゆるめる
21
+ | keyof ReactAreaUseTextFieldCompat
22
+ >
23
+
24
+ export interface TextFieldProps
25
+ extends Pick<FieldLabelProps, 'label' | 'requiredText' | 'subLabel'>,
26
+ DOMProps,
27
+ ReactAreaUseTextFieldCompat {
28
+ readonly prefix?: ReactNode
29
+ readonly suffix?: ReactNode
15
30
 
16
- interface TextFieldBaseProps
17
- extends Pick<FieldLabelProps, 'label' | 'requiredText' | 'subLabel'> {
18
- readonly className?: string
31
+ // <input> 要素は number とか string[] もありうるが、今はこれしか想定してない
19
32
  readonly defaultValue?: string
20
33
  readonly value?: string
21
34
  readonly onChange?: (value: string) => void
35
+
36
+ // react-ariaの型定義のせいでHTMLInputElementにできない
22
37
  readonly onKeyDown?: (event: React.KeyboardEvent<Element>) => void
23
38
  readonly onFocus?: (event: React.FocusEvent<Element>) => void
24
39
  readonly onBlur?: (event: React.FocusEvent<Element>) => void
40
+
25
41
  readonly showCount?: boolean
26
42
  readonly showLabel?: boolean
27
- readonly placeholder?: string
28
43
  readonly assistiveText?: string
29
- readonly disabled?: boolean
30
- readonly required?: boolean
31
44
  readonly invalid?: boolean
32
- readonly maxLength?: number
33
- /**
34
- * tab-indexがー1かどうか
35
- */
36
- readonly excludeFromTabOrder?: boolean
37
- }
38
-
39
- export interface SingleLineTextFieldProps extends TextFieldBaseProps {
40
- readonly autoHeight?: never
41
- readonly multiline?: false
42
- readonly rows?: never
43
- readonly type?: string
44
- readonly prefix?: ReactNode
45
- readonly suffix?: ReactNode
46
- }
47
-
48
- export interface MultiLineTextFieldProps extends TextFieldBaseProps {
49
- readonly autoHeight?: boolean
50
- readonly multiline: true
51
- readonly rows?: number
52
- readonly type?: never
53
- readonly prefix?: never
54
- readonly suffix?: never
55
- }
56
-
57
- export type TextFieldProps = SingleLineTextFieldProps | MultiLineTextFieldProps
58
- type TextFieldElement = HTMLInputElement & HTMLTextAreaElement
59
-
60
- function mergeRefs<T>(...refs: React.Ref<T>[]): React.RefCallback<T> {
61
- return (value) => {
62
- for (const ref of refs) {
63
- if (typeof ref === 'function') {
64
- ref(value)
65
- } else if (ref !== null) {
66
- ;(ref as React.MutableRefObject<T | null>).current = value
67
- }
68
- }
69
- }
70
- }
71
-
72
- function countCodePointsInString(string: string) {
73
- // [...string] とするとproduction buildで動かなくなる
74
- // cf. https://twitter.com/f_subal/status/1497214727511891972
75
- return Array.from(string).length
76
45
  }
77
46
 
78
- const TextField = React.forwardRef<TextFieldElement, TextFieldProps>(
79
- function TextField(props, ref) {
80
- return props.multiline !== undefined && props.multiline ? (
81
- <MultiLineTextField ref={ref} {...props} />
82
- ) : (
83
- <SingleLineTextField ref={ref} {...props} />
47
+ const TextField = React.forwardRef<HTMLInputElement, TextFieldProps>(
48
+ function SingleLineTextFieldInner({ onChange, ...props }, forwardRef) {
49
+ const {
50
+ className,
51
+ showLabel = false,
52
+ showCount = false,
53
+ label,
54
+ requiredText,
55
+ subLabel,
56
+ disabled = false,
57
+ required,
58
+ invalid = false,
59
+ assistiveText,
60
+ maxLength,
61
+ prefix = null,
62
+ suffix = null,
63
+ } = props
64
+
65
+ const { visuallyHiddenProps } = useVisuallyHidden()
66
+ const ariaRef = useRef<HTMLInputElement>(null)
67
+ const prefixRef = useRef<HTMLSpanElement>(null)
68
+ const suffixRef = useRef<HTMLSpanElement>(null)
69
+ const [count, setCount] = useState(
70
+ countCodePointsInString(props.value ?? '')
84
71
  )
85
- }
86
- )
87
-
88
- export default TextField
89
-
90
- const SingleLineTextField = React.forwardRef<
91
- HTMLInputElement,
92
- SingleLineTextFieldProps
93
- >(function SingleLineTextFieldInner({ onChange, ...props }, forwardRef) {
94
- const {
95
- className,
96
- showLabel = false,
97
- showCount = false,
98
- label,
99
- requiredText,
100
- subLabel,
101
- disabled = false,
102
- required,
103
- invalid = false,
104
- assistiveText,
105
- maxLength,
106
- prefix = null,
107
- suffix = null,
108
- } = props
109
-
110
- const { visuallyHiddenProps } = useVisuallyHidden()
111
- const ariaRef = useRef<HTMLInputElement>(null)
112
- const prefixRef = useRef<HTMLSpanElement>(null)
113
- const suffixRef = useRef<HTMLSpanElement>(null)
114
- const [count, setCount] = useState(countCodePointsInString(props.value ?? ''))
115
- const [prefixWidth, setPrefixWidth] = useState(0)
116
- const [suffixWidth, setSuffixWidth] = useState(0)
117
-
118
- const nonControlled = props.value === undefined
119
- const handleChange = useCallback(
120
- (value: string) => {
121
- const count = countCodePointsInString(value)
122
- if (maxLength !== undefined && count > maxLength) {
123
- return
124
- }
125
- if (nonControlled) {
126
- setCount(count)
127
- }
128
- onChange?.(value)
129
- },
130
- [maxLength, nonControlled, onChange]
131
- )
132
-
133
- useEffect(() => {
134
- setCount(countCodePointsInString(props.value ?? ''))
135
- }, [props.value])
136
-
137
- const { inputProps, labelProps, descriptionProps, errorMessageProps } =
138
- useTextField(
139
- {
140
- inputElementType: 'input',
141
- isDisabled: disabled,
142
- isRequired: required,
143
- validationState: invalid ? 'invalid' : 'valid',
144
- description: !invalid && assistiveText,
145
- errorMessage: invalid && assistiveText,
146
- onChange: handleChange,
147
- ...props,
72
+ const [prefixWidth, setPrefixWidth] = useState(0)
73
+ const [suffixWidth, setSuffixWidth] = useState(0)
74
+
75
+ const nonControlled = props.value === undefined
76
+ const handleChange = useCallback(
77
+ (value: string) => {
78
+ const count = countCodePointsInString(value)
79
+ if (maxLength !== undefined && count > maxLength) {
80
+ return
81
+ }
82
+ if (nonControlled) {
83
+ setCount(count)
84
+ }
85
+ onChange?.(value)
148
86
  },
149
- ariaRef
87
+ [maxLength, nonControlled, onChange]
150
88
  )
151
89
 
152
- useEffect(() => {
153
- const prefixObserver = new ResizeObserver((entries) => {
154
- setPrefixWidth(entries[0].contentRect.width)
155
- })
156
- const suffixObserver = new ResizeObserver((entries) => {
157
- setSuffixWidth(entries[0].contentRect.width)
158
- })
159
-
160
- if (prefixRef.current !== null) {
161
- prefixObserver.observe(prefixRef.current)
162
- }
163
- if (suffixRef.current !== null) {
164
- suffixObserver.observe(suffixRef.current)
165
- }
166
-
167
- return () => {
168
- suffixObserver.disconnect()
169
- prefixObserver.disconnect()
170
- }
171
- }, [])
172
-
173
- return (
174
- <TextFieldRoot className={className} isDisabled={disabled}>
175
- <TextFieldLabel
176
- label={label}
177
- requiredText={requiredText}
178
- required={required}
179
- subLabel={subLabel}
180
- {...labelProps}
181
- {...(!showLabel ? visuallyHiddenProps : {})}
182
- />
183
- <StyledInputContainer>
184
- <PrefixContainer ref={prefixRef}>
185
- <Affix>{prefix}</Affix>
186
- </PrefixContainer>
187
- <StyledInput
188
- ref={mergeRefs(forwardRef, ariaRef)}
189
- invalid={invalid}
190
- extraLeftPadding={prefixWidth}
191
- extraRightPadding={suffixWidth}
192
- {...inputProps}
193
- />
194
- <SuffixContainer ref={suffixRef}>
195
- <Affix>{suffix}</Affix>
196
- {showCount && (
197
- <SingleLineCounter>
198
- {maxLength !== undefined ? `${count}/${maxLength}` : count}
199
- </SingleLineCounter>
200
- )}
201
- </SuffixContainer>
202
- </StyledInputContainer>
203
- {assistiveText != null && assistiveText.length !== 0 && (
204
- <AssistiveText
205
- invalid={invalid}
206
- {...(invalid ? errorMessageProps : descriptionProps)}
207
- >
208
- {assistiveText}
209
- </AssistiveText>
210
- )}
211
- </TextFieldRoot>
212
- )
213
- })
214
-
215
- const MultiLineTextField = React.forwardRef<
216
- HTMLTextAreaElement,
217
- MultiLineTextFieldProps
218
- >(function MultiLineTextFieldInner({ onChange, ...props }, forwardRef) {
219
- const {
220
- className,
221
- showCount = false,
222
- showLabel = false,
223
- label,
224
- requiredText,
225
- subLabel,
226
- disabled = false,
227
- required,
228
- invalid = false,
229
- assistiveText,
230
- maxLength,
231
- autoHeight = false,
232
- rows: initialRows = 4,
233
- } = props
234
-
235
- const { visuallyHiddenProps } = useVisuallyHidden()
236
- const textareaRef = useRef<HTMLTextAreaElement>(null)
237
- const ariaRef = useRef<HTMLTextAreaElement>(null)
238
- const [count, setCount] = useState(countCodePointsInString(props.value ?? ''))
239
- const [rows, setRows] = useState(initialRows)
240
-
241
- const syncHeight = useCallback(
242
- (textarea: HTMLTextAreaElement) => {
243
- const rows = (`${textarea.value}\n`.match(/\n/gu)?.length ?? 0) || 1
244
- setRows(initialRows <= rows ? rows : initialRows)
245
- },
246
- [initialRows]
247
- )
248
-
249
- const nonControlled = props.value === undefined
250
- const handleChange = useCallback(
251
- (value: string) => {
252
- const count = countCodePointsInString(value)
253
- if (maxLength !== undefined && count > maxLength) {
254
- return
255
- }
256
- if (nonControlled) {
257
- setCount(count)
90
+ useEffect(() => {
91
+ setCount(countCodePointsInString(props.value ?? ''))
92
+ }, [props.value])
93
+
94
+ const { inputProps, labelProps, descriptionProps, errorMessageProps } =
95
+ useTextField(
96
+ {
97
+ inputElementType: 'input',
98
+ isDisabled: disabled,
99
+ isRequired: required,
100
+ validationState: invalid ? 'invalid' : 'valid',
101
+ description: !invalid && assistiveText,
102
+ errorMessage: invalid && assistiveText,
103
+ onChange: handleChange,
104
+ ...props,
105
+ },
106
+ ariaRef
107
+ )
108
+
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)
258
119
  }
259
- if (autoHeight && textareaRef.current !== null) {
260
- syncHeight(textareaRef.current)
120
+ if (suffixRef.current !== null) {
121
+ suffixObserver.observe(suffixRef.current)
261
122
  }
262
- onChange?.(value)
263
- },
264
- [autoHeight, maxLength, nonControlled, onChange, syncHeight]
265
- )
266
-
267
- useEffect(() => {
268
- setCount(countCodePointsInString(props.value ?? ''))
269
- }, [props.value])
270
-
271
- const { inputProps, labelProps, descriptionProps, errorMessageProps } =
272
- useTextField(
273
- {
274
- inputElementType: 'textarea',
275
- isDisabled: disabled,
276
- isRequired: required,
277
- validationState: invalid ? 'invalid' : 'valid',
278
- description: !invalid && assistiveText,
279
- errorMessage: invalid && assistiveText,
280
- onChange: handleChange,
281
- ...props,
282
- },
283
- ariaRef
284
- )
285
123
 
286
- useEffect(() => {
287
- if (autoHeight && textareaRef.current !== null) {
288
- syncHeight(textareaRef.current)
289
- }
290
- }, [autoHeight, syncHeight])
291
-
292
- return (
293
- <TextFieldRoot className={className} isDisabled={disabled}>
294
- <TextFieldLabel
295
- label={label}
296
- requiredText={requiredText}
297
- required={required}
298
- subLabel={subLabel}
299
- {...labelProps}
300
- {...(!showLabel ? visuallyHiddenProps : {})}
301
- />
302
- <StyledTextareaContainer
303
- invalid={invalid}
304
- rows={showCount ? rows + 1 : rows}
305
- >
306
- <StyledTextarea
307
- ref={mergeRefs(textareaRef, forwardRef, ariaRef)}
308
- rows={rows}
309
- noBottomPadding={showCount}
310
- {...inputProps}
124
+ return () => {
125
+ suffixObserver.disconnect()
126
+ prefixObserver.disconnect()
127
+ }
128
+ }, [])
129
+
130
+ return (
131
+ <TextFieldRoot className={className} isDisabled={disabled}>
132
+ <TextFieldLabel
133
+ label={label}
134
+ requiredText={requiredText}
135
+ required={required}
136
+ subLabel={subLabel}
137
+ {...labelProps}
138
+ {...(!showLabel ? visuallyHiddenProps : {})}
311
139
  />
312
- {showCount && (
313
- <MultiLineCounter>
314
- {maxLength !== undefined ? `${count}/${maxLength}` : count}
315
- </MultiLineCounter>
140
+ <StyledInputContainer>
141
+ <PrefixContainer ref={prefixRef}>
142
+ <Affix>{prefix}</Affix>
143
+ </PrefixContainer>
144
+ <StyledInput
145
+ ref={mergeRefs(forwardRef, ariaRef)}
146
+ invalid={invalid}
147
+ extraLeftPadding={prefixWidth}
148
+ extraRightPadding={suffixWidth}
149
+ {...inputProps}
150
+ />
151
+ <SuffixContainer ref={suffixRef}>
152
+ <Affix>{suffix}</Affix>
153
+ {showCount && (
154
+ <SingleLineCounter>
155
+ {maxLength !== undefined ? `${count}/${maxLength}` : count}
156
+ </SingleLineCounter>
157
+ )}
158
+ </SuffixContainer>
159
+ </StyledInputContainer>
160
+ {assistiveText != null && assistiveText.length !== 0 && (
161
+ <AssistiveText
162
+ invalid={invalid}
163
+ {...(invalid ? errorMessageProps : descriptionProps)}
164
+ >
165
+ {assistiveText}
166
+ </AssistiveText>
316
167
  )}
317
- </StyledTextareaContainer>
318
- {assistiveText != null && assistiveText.length !== 0 && (
319
- <AssistiveText
320
- invalid={invalid}
321
- {...(invalid ? errorMessageProps : descriptionProps)}
322
- >
323
- {assistiveText}
324
- </AssistiveText>
325
- )}
326
- </TextFieldRoot>
327
- )
328
- })
168
+ </TextFieldRoot>
169
+ )
170
+ }
171
+ )
172
+
173
+ export default TextField
329
174
 
330
175
  const TextFieldRoot = styled.div<{ isDisabled: boolean }>`
331
176
  display: flex;
@@ -405,85 +250,10 @@ const StyledInput = styled.input<{
405
250
  }
406
251
  `
407
252
 
408
- const StyledTextareaContainer = styled.div<{ rows: number; invalid: boolean }>`
409
- position: relative;
410
- overflow: hidden;
411
- padding: 0 8px;
412
-
413
- ${(p) =>
414
- theme((o) => [
415
- o.bg.surface3.hover,
416
- p.invalid && o.outline.assertive,
417
- o.font.text2,
418
- o.borderRadius(4),
419
- ])}
420
-
421
- /**
422
- * FIXME: o.outline.default を &:focus-within 内に書いてると、外れるときに transition が効かない
423
- * 本来 o.outline.default.focus と書けば足してくれるような transition の内容を一旦明示している
424
- * o.outline.default.focusWithin のようなものがあればこの行は不要になるはず
425
- */
426
- transition: box-shadow 0.2s;
427
-
428
- &:focus-within {
429
- ${(p) =>
430
- theme((o) => (p.invalid ? o.outline.assertive : o.outline.default))}
431
- }
432
-
433
- ${({ rows }) => css`
434
- height: calc(22px * ${rows} + 18px);
435
- `};
436
- `
437
-
438
- const StyledTextarea = styled.textarea<{ noBottomPadding: boolean }>`
439
- border: none;
440
- outline: none;
441
- resize: none;
442
- font-family: inherit;
443
- color: inherit;
444
-
445
- /* Prevent zooming for iOS Safari */
446
- transform-origin: top left;
447
- transform: scale(0.875);
448
- width: calc(100% / 0.875);
449
- font-size: calc(14px / 0.875);
450
- line-height: calc(22px / 0.875);
451
- padding: calc(9px / 0.875) 0 ${(p) => (p.noBottomPadding ? 0 : '')};
452
-
453
- ${({ rows = 1 }) => css`
454
- height: calc(22px / 0.875 * ${rows});
455
- `};
456
-
457
- /* Display box-shadow for iOS Safari */
458
- appearance: none;
459
-
460
- background: none;
461
-
462
- &::placeholder {
463
- ${theme((o) => o.font.text3)}
464
- }
465
-
466
- /* Hide scrollbar for Chrome, Safari and Opera */
467
- &::-webkit-scrollbar {
468
- display: none;
469
- }
470
- /* Hide scrollbar for IE, Edge and Firefox */
471
- -ms-overflow-style: none; /* IE and Edge */
472
- scrollbar-width: none; /* Firefox */
473
- `
474
-
475
253
  const SingleLineCounter = styled.span`
476
254
  ${theme((o) => [o.typography(14).preserveHalfLeading, o.font.text3])}
477
255
  `
478
256
 
479
- const MultiLineCounter = styled.span`
480
- position: absolute;
481
- bottom: 9px;
482
- right: 8px;
483
-
484
- ${theme((o) => [o.typography(14).preserveHalfLeading, o.font.text3])}
485
- `
486
-
487
257
  const AssistiveText = styled.p<{ invalid: boolean }>`
488
258
  ${(p) =>
489
259
  theme((o) => [
@@ -1,7 +1,6 @@
1
1
  import path from 'path'
2
2
  import glob from 'glob'
3
3
  import { axe, toHaveNoViolations } from 'jest-axe'
4
- import React from 'react'
5
4
  import { render } from '@testing-library/react'
6
5
  import { ThemeProvider } from 'styled-components'
7
6
  import { Story } from '../_lib/compat'
@@ -1,4 +1,4 @@
1
- import React from 'react'
1
+ import * as React from 'react'
2
2
  import { ThemeProvider } from 'styled-components'
3
3
  import ComponentAbstraction, { Components } from './ComponentAbstraction'
4
4
  import { TokenInjector } from '@charcoal-ui/styled'
@@ -1,4 +1,5 @@
1
- import React, { useContext } from 'react'
1
+ import { useContext } from 'react'
2
+ import * as React from 'react'
2
3
 
3
4
  export type LinkProps = {
4
5
  /**
package/src/index.ts CHANGED
@@ -35,9 +35,8 @@ export { default as Switch, type SwitchProps } from './components/Switch'
35
35
  export {
36
36
  default as TextField,
37
37
  type TextFieldProps,
38
- type SingleLineTextFieldProps,
39
- type MultiLineTextFieldProps,
40
38
  } from './components/TextField'
39
+ export { default as TextArea, type TextAreaProps } from './components/TextArea'
41
40
  export { default as Icon, type IconProps } from './components/Icon'
42
41
  export { default as Modal, type ModalProps } from './components/Modal'
43
42
  export {
@@ -52,13 +51,16 @@ export {
52
51
  } from './components/LoadingSpinner'
53
52
  export {
54
53
  default as DropdownSelector,
55
- type DropdownSelectorOption,
56
54
  type DropdownSelectorProps,
57
55
  } from './components/DropdownSelector'
58
56
  export {
59
- OptionItem,
60
- type OptionItemProps,
61
- } from './components/DropdownSelector/OptionItem'
57
+ default as DropdownMenuItem,
58
+ type DropdownMenuItemProps,
59
+ } from './components/DropdownSelector/DropdownMenuItem'
60
+ export {
61
+ default as MenuItemGroup,
62
+ type MenuItemGroupProps,
63
+ } from './components/DropdownSelector/MenuItemGroup'
62
64
  export {
63
65
  default as SegmentedControl,
64
66
  type SegmentedControlProps,
@@ -1,7 +0,0 @@
1
- import { ReactNode } from 'react';
2
- export type OptionItemProps = {
3
- children?: ReactNode;
4
- value: string;
5
- };
6
- export declare function OptionItem(props: OptionItemProps): JSX.Element;
7
- //# sourceMappingURL=OptionItem.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"OptionItem.d.ts","sourceRoot":"","sources":["../../../src/components/DropdownSelector/OptionItem.tsx"],"names":[],"mappings":"AAAA,OAAc,EAAE,SAAS,EAAc,MAAM,OAAO,CAAA;AAQpD,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,eA2ChD"}
@@ -1,6 +0,0 @@
1
- /**
2
- * li要素ならフォーカスしてスクロールスクロール領域に見えるように親要素をスクロールする
3
- * @param element
4
- */
5
- export declare function focusIfHTMLLIElement(element: Node | null | undefined): void;
6
- //# sourceMappingURL=focusIfHTMLLIElement.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"focusIfHTMLLIElement.d.ts","sourceRoot":"","sources":["../../../../src/components/DropdownSelector/utils/focusIfHTMLLIElement.tsx"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,IAAI,GAAG,IAAI,GAAG,SAAS,QAKpE"}
@@ -1,6 +0,0 @@
1
- /**
2
- * スクロールスクロール領域に見えるように親要素をスクロールする
3
- * @param element
4
- */
5
- export declare function handleFocusByKeyBoard(element: HTMLElement): void;
6
- //# sourceMappingURL=handleFocusByKeyBoard.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"handleFocusByKeyBoard.d.ts","sourceRoot":"","sources":["../../../../src/components/DropdownSelector/utils/handleFocusByKeyBoard.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,WAAW,QAczD"}