@charcoal-ui/react 3.0.0-beta.3 → 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 (86) hide show
  1. package/dist/_lib/compat.d.ts +18 -0
  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/Checkbox/index.d.ts +1 -0
  6. package/dist/components/Checkbox/index.d.ts.map +1 -1
  7. package/dist/components/Checkbox/index.story.d.ts +1 -0
  8. package/dist/components/Checkbox/index.story.d.ts.map +1 -1
  9. package/dist/components/DropdownSelector/index.d.ts.map +1 -1
  10. package/dist/components/LoadingSpinner/index.d.ts +8 -6
  11. package/dist/components/LoadingSpinner/index.d.ts.map +1 -1
  12. package/dist/components/LoadingSpinner/index.story.d.ts +1 -2
  13. package/dist/components/LoadingSpinner/index.story.d.ts.map +1 -1
  14. package/dist/components/Modal/index.d.ts +17 -26
  15. package/dist/components/Modal/index.d.ts.map +1 -1
  16. package/dist/components/Modal/index.story.d.ts +12 -2
  17. package/dist/components/Modal/index.story.d.ts.map +1 -1
  18. package/dist/components/MultiSelect/index.d.ts +14 -1
  19. package/dist/components/MultiSelect/index.d.ts.map +1 -1
  20. package/dist/components/MultiSelect/index.story.d.ts +14 -2
  21. package/dist/components/MultiSelect/index.story.d.ts.map +1 -1
  22. package/dist/components/Radio/index.d.ts +12 -5
  23. package/dist/components/Radio/index.d.ts.map +1 -1
  24. package/dist/components/Radio/index.story.d.ts +10 -6
  25. package/dist/components/Radio/index.story.d.ts.map +1 -1
  26. package/dist/components/SegmentedControl/index.d.ts +1 -0
  27. package/dist/components/SegmentedControl/index.d.ts.map +1 -1
  28. package/dist/components/Switch/index.d.ts +2 -1
  29. package/dist/components/Switch/index.d.ts.map +1 -1
  30. package/dist/components/Switch/index.story.d.ts +1 -2
  31. package/dist/components/Switch/index.story.d.ts.map +1 -1
  32. package/dist/components/TextArea/index.d.ts +3 -10
  33. package/dist/components/TextArea/index.d.ts.map +1 -1
  34. package/dist/components/TextField/TextField.story.d.ts +4 -5
  35. package/dist/components/TextField/TextField.story.d.ts.map +1 -1
  36. package/dist/components/TextField/index.d.ts +6 -29
  37. package/dist/components/TextField/index.d.ts.map +1 -1
  38. package/dist/components/TextField/index.story.d.ts +5 -4
  39. package/dist/components/TextField/index.story.d.ts.map +1 -1
  40. package/dist/index.cjs.js +636 -594
  41. package/dist/index.cjs.js.map +1 -1
  42. package/dist/index.d.ts +2 -1
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.esm.js +604 -563
  45. package/dist/index.esm.js.map +1 -1
  46. package/package.json +6 -6
  47. package/src/_lib/compat.ts +19 -0
  48. package/src/_lib/index.ts +23 -0
  49. package/src/components/Checkbox/index.story.tsx +1 -0
  50. package/src/components/Checkbox/index.tsx +2 -1
  51. package/src/components/DropdownSelector/DropdownMenuItem.tsx +1 -1
  52. package/src/components/DropdownSelector/ListItem/index.story.tsx +1 -1
  53. package/src/components/DropdownSelector/ListItem/index.tsx +1 -1
  54. package/src/components/DropdownSelector/MenuItem/index.tsx +0 -1
  55. package/src/components/DropdownSelector/MenuItem/internals/useMenuItemHandleKeyDown.tsx +1 -1
  56. package/src/components/DropdownSelector/MenuItemGroup/index.tsx +0 -1
  57. package/src/components/DropdownSelector/MenuList/index.story.tsx +0 -1
  58. package/src/components/DropdownSelector/MenuList/index.tsx +1 -1
  59. package/src/components/DropdownSelector/MenuList/internals/getValuesRecursive.tsx +1 -1
  60. package/src/components/DropdownSelector/Popover/index.story.tsx +1 -1
  61. package/src/components/DropdownSelector/Popover/index.tsx +1 -1
  62. package/src/components/DropdownSelector/index.tsx +16 -14
  63. package/src/components/DropdownSelector/utils/findPreviewRecursive.tsx +2 -1
  64. package/src/components/LoadingSpinner/index.story.tsx +7 -1
  65. package/src/components/LoadingSpinner/index.tsx +27 -11
  66. package/src/components/Modal/index.tsx +18 -12
  67. package/src/components/MultiSelect/index.story.tsx +16 -4
  68. package/src/components/MultiSelect/index.tsx +70 -60
  69. package/src/components/Radio/index.story.tsx +7 -8
  70. package/src/components/Radio/index.test.tsx +3 -3
  71. package/src/components/Radio/index.tsx +23 -23
  72. package/src/components/SegmentedControl/index.tsx +6 -1
  73. package/src/components/Switch/index.tsx +37 -32
  74. package/src/components/TextArea/TextArea.story.tsx +61 -0
  75. package/src/components/TextArea/index.tsx +246 -0
  76. package/src/components/TextField/{index.story.tsx → TextField.story.tsx} +6 -28
  77. package/src/components/TextField/index.tsx +146 -371
  78. package/src/index.ts +1 -2
  79. package/dist/components/DropdownSelector/OptionItem.d.ts +0 -7
  80. package/dist/components/DropdownSelector/OptionItem.d.ts.map +0 -1
  81. package/dist/components/DropdownSelector/utils/focusIfHTMLLIElement.d.ts +0 -6
  82. package/dist/components/DropdownSelector/utils/focusIfHTMLLIElement.d.ts.map +0 -1
  83. package/dist/components/DropdownSelector/utils/handleFocusByKeyBoard.d.ts +0 -6
  84. package/dist/components/DropdownSelector/utils/handleFocusByKeyBoard.d.ts.map +0 -1
  85. package/dist/types/CustomJSXElement.d.ts +0 -3
  86. package/dist/types/CustomJSXElement.d.ts.map +0 -1
@@ -1,4 +1,4 @@
1
- import { useCallback, useContext } from 'react'
1
+ import { memo, forwardRef, useCallback, useContext } from 'react'
2
2
  import * as React from 'react'
3
3
  import styled from 'styled-components'
4
4
  import warning from 'warning'
@@ -7,22 +7,20 @@ import { px } from '@charcoal-ui/utils'
7
7
 
8
8
  export type RadioProps = React.PropsWithChildren<{
9
9
  value: string
10
- forceChecked?: boolean
11
10
  disabled?: boolean
11
+ className?: string
12
12
  }>
13
13
 
14
- export default function Radio({
15
- value,
16
- forceChecked = false,
17
- disabled = false,
18
- children,
19
- }: RadioProps) {
14
+ const Radio = forwardRef<HTMLInputElement, RadioProps>(function RadioInner(
15
+ { value, disabled = false, children, className },
16
+ ref
17
+ ) {
20
18
  const {
21
19
  name,
22
20
  selected,
23
21
  disabled: isParentDisabled,
24
22
  readonly,
25
- hasError,
23
+ invalid,
26
24
  onChange,
27
25
  } = useContext(RadioGroupContext)
28
26
 
@@ -44,19 +42,22 @@ export default function Radio({
44
42
  )
45
43
 
46
44
  return (
47
- <RadioRoot aria-disabled={isDisabled || isReadonly}>
45
+ <RadioRoot aria-disabled={isDisabled || isReadonly} className={className}>
48
46
  <RadioInput
49
47
  name={name}
50
48
  value={value}
51
- checked={forceChecked || isSelected}
52
- hasError={hasError}
49
+ checked={isSelected}
50
+ invalid={invalid}
53
51
  onChange={handleChange}
54
52
  disabled={isDisabled || isReadonly}
53
+ ref={ref}
55
54
  />
56
55
  {children != null && <RadioLabel>{children}</RadioLabel>}
57
56
  </RadioRoot>
58
57
  )
59
- }
58
+ })
59
+
60
+ export default memo(Radio)
60
61
 
61
62
  const RadioRoot = styled.label`
62
63
  display: grid;
@@ -69,7 +70,7 @@ const RadioRoot = styled.label`
69
70
  `
70
71
 
71
72
  export const RadioInput = styled.input.attrs({ type: 'radio' })<{
72
- hasError?: boolean
73
+ invalid?: boolean
73
74
  }>`
74
75
  /** Make prior to browser default style */
75
76
  &[type='radio'] {
@@ -82,14 +83,13 @@ export const RadioInput = styled.input.attrs({ type: 'radio' })<{
82
83
 
83
84
  width: 20px;
84
85
  height: 20px;
85
-
86
86
  cursor: pointer;
87
87
 
88
- ${({ hasError = false }) =>
88
+ ${({ invalid = false }) =>
89
89
  theme((o) => [
90
90
  o.borderRadius('oval'),
91
91
  o.bg.surface1.hover.press,
92
- hasError && o.outline.assertive,
92
+ invalid && o.outline.assertive,
93
93
  ])};
94
94
 
95
95
  &:not(:checked) {
@@ -131,7 +131,7 @@ export type RadioGroupProps = React.PropsWithChildren<{
131
131
  onChange(next: string): void
132
132
  disabled?: boolean
133
133
  readonly?: boolean
134
- hasError?: boolean
134
+ invalid?: boolean
135
135
  }>
136
136
 
137
137
  // TODO: use (or polyfill) flex gap
@@ -146,7 +146,7 @@ interface RadioGroupContext {
146
146
  selected?: string
147
147
  disabled: boolean
148
148
  readonly: boolean
149
- hasError: boolean
149
+ invalid: boolean
150
150
  onChange: (next: string) => void
151
151
  }
152
152
 
@@ -155,7 +155,7 @@ const RadioGroupContext = React.createContext<RadioGroupContext>({
155
155
  selected: undefined,
156
156
  disabled: false,
157
157
  readonly: false,
158
- hasError: false,
158
+ invalid: false,
159
159
  onChange() {
160
160
  throw new Error(
161
161
  'Cannot find onChange() handler. Perhaps you forgot to wrap with <RadioGroup> ?'
@@ -171,7 +171,7 @@ export function RadioGroup({
171
171
  onChange,
172
172
  disabled,
173
173
  readonly,
174
- hasError,
174
+ invalid,
175
175
  children,
176
176
  }: RadioGroupProps) {
177
177
  const handleChange = useCallback(
@@ -188,7 +188,7 @@ export function RadioGroup({
188
188
  selected: value,
189
189
  disabled: disabled ?? false,
190
190
  readonly: readonly ?? false,
191
- hasError: hasError ?? false,
191
+ invalid: invalid ?? false,
192
192
  onChange: handleChange,
193
193
  }}
194
194
  >
@@ -196,7 +196,7 @@ export function RadioGroup({
196
196
  role="radiogroup"
197
197
  aria-orientation="vertical"
198
198
  aria-label={label}
199
- aria-invalid={hasError}
199
+ aria-invalid={invalid}
200
200
  className={className}
201
201
  >
202
202
  {children}
@@ -25,6 +25,7 @@ export type SegmentedControlProps = {
25
25
  readonly disabled?: boolean
26
26
  readonly readonly?: boolean
27
27
  readonly required?: boolean
28
+ readonly className?: string
28
29
 
29
30
  readonly value?: string
30
31
  readonly defaultValue?: string
@@ -55,7 +56,11 @@ const SegmentedControl = forwardRef<HTMLDivElement, SegmentedControlProps>(
55
56
  }, [props.data])
56
57
 
57
58
  return (
58
- <SegmentedControlRoot ref={ref} {...radioGroupProps}>
59
+ <SegmentedControlRoot
60
+ ref={ref}
61
+ {...radioGroupProps}
62
+ className={props.className}
63
+ >
59
64
  <RadioProvider value={state}>
60
65
  {segmentedControlItems.map((item) => (
61
66
  <Segmented
@@ -1,11 +1,12 @@
1
1
  import { useSwitch } from '@react-aria/switch'
2
2
  import type { AriaSwitchProps } from '@react-types/switch'
3
- import { useRef, useMemo } from 'react'
3
+ import { useMemo, memo, forwardRef } from 'react'
4
4
  import * as React from 'react'
5
5
  import { useToggleState } from 'react-stately'
6
6
  import styled from 'styled-components'
7
7
  import { theme } from '../../styled'
8
8
  import { disabledSelector } from '@charcoal-ui/utils'
9
+ import { useObjectRef } from '@react-aria/utils'
9
10
 
10
11
  export type SwitchProps = {
11
12
  name: string
@@ -24,37 +25,41 @@ export type SwitchProps = {
24
25
  }
25
26
  )
26
27
 
27
- export default function SwitchCheckbox(props: SwitchProps) {
28
- const { disabled, className } = props
29
-
30
- const ariaSwitchProps: AriaSwitchProps = useMemo(
31
- () => ({
32
- ...props,
33
-
34
- // children がいない場合は aria-label をつけないといけない
35
- 'aria-label': 'children' in props ? undefined : props.label,
36
- isDisabled: props.disabled,
37
- isSelected: props.checked,
38
- }),
39
- [props]
40
- )
41
-
42
- const state = useToggleState(ariaSwitchProps)
43
- const ref = useRef<HTMLInputElement>(null)
44
- const {
45
- inputProps: { className: _className, type: _type, ...rest },
46
- } = useSwitch(ariaSwitchProps, state, ref)
47
-
48
- return (
49
- <Label className={className} aria-disabled={disabled}>
50
- <SwitchInput {...rest} ref={ref} />
51
- {'children' in props ? (
52
- // eslint-disable-next-line react/destructuring-assignment
53
- <LabelInner>{props.children}</LabelInner>
54
- ) : undefined}
55
- </Label>
56
- )
57
- }
28
+ const SwitchCheckbox = forwardRef<HTMLInputElement, SwitchProps>(
29
+ function SwitchCheckboxInner(props, external) {
30
+ const { disabled, className } = props
31
+
32
+ const ariaSwitchProps: AriaSwitchProps = useMemo(
33
+ () => ({
34
+ ...props,
35
+
36
+ // children がいない場合は aria-label をつけないといけない
37
+ 'aria-label': 'children' in props ? undefined : props.label,
38
+ isDisabled: props.disabled,
39
+ isSelected: props.checked,
40
+ }),
41
+ [props]
42
+ )
43
+
44
+ const state = useToggleState(ariaSwitchProps)
45
+ const ref = useObjectRef<HTMLInputElement>(external)
46
+ const {
47
+ inputProps: { className: _className, type: _type, ...rest },
48
+ } = useSwitch(ariaSwitchProps, state, ref)
49
+
50
+ return (
51
+ <Label className={className} aria-disabled={disabled}>
52
+ <SwitchInput {...rest} ref={ref} />
53
+ {'children' in props ? (
54
+ // eslint-disable-next-line react/destructuring-assignment
55
+ <LabelInner>{props.children}</LabelInner>
56
+ ) : undefined}
57
+ </Label>
58
+ )
59
+ }
60
+ )
61
+
62
+ export default memo(SwitchCheckbox)
58
63
 
59
64
  const Label = styled.label`
60
65
  display: inline-grid;
@@ -0,0 +1,61 @@
1
+ import { action } from '@storybook/addon-actions'
2
+ import styled from 'styled-components'
3
+ import { Story } from '../../_lib/compat'
4
+ import Clickable from '../Clickable'
5
+ import TextArea, { TextAreaProps } from '.'
6
+ import { px } from '@charcoal-ui/utils'
7
+
8
+ export default {
9
+ title: 'TextArea',
10
+ component: TextArea,
11
+ argTypes: {},
12
+ args: {
13
+ showLabel: false,
14
+ label: 'Label',
15
+ assistiveText: '',
16
+ disabled: false,
17
+ required: false,
18
+ invalid: false,
19
+ },
20
+ }
21
+
22
+ const Container = styled.div`
23
+ display: grid;
24
+ gap: ${({ theme }) => px(theme.spacing[24])};
25
+ `
26
+
27
+ const Template: Story<Partial<TextAreaProps>> = (args) => (
28
+ <Container>
29
+ <TextArea
30
+ label="Label"
31
+ requiredText="*必須"
32
+ subLabel={
33
+ <Clickable onClick={action('label-click')}>Text Link</Clickable>
34
+ }
35
+ placeholder="Text Area"
36
+ {...args}
37
+ />
38
+ </Container>
39
+ )
40
+
41
+ export const Default = Template.bind({})
42
+
43
+ export const HasLabel = Template.bind({})
44
+ HasLabel.args = {
45
+ showLabel: true,
46
+ assistiveText: 'Assistive text',
47
+ required: true,
48
+ }
49
+
50
+ export const HasCount = Template.bind({})
51
+ HasCount.args = {
52
+ showCount: true,
53
+ maxLength: 100,
54
+ }
55
+
56
+ export const AutoHeight: Story<Partial<TextAreaProps>> = (args) => (
57
+ <TextArea label="Label" placeholder="TextArea" {...args} />
58
+ )
59
+ AutoHeight.args = {
60
+ autoHeight: true,
61
+ }
@@ -0,0 +1,246 @@
1
+ import { useTextField } from '@react-aria/textfield'
2
+ import { useVisuallyHidden } from '@react-aria/visually-hidden'
3
+ import { forwardRef, useCallback, useEffect, useRef, useState } from 'react'
4
+ import styled, { css } from 'styled-components'
5
+ import FieldLabel, { FieldLabelProps } from '../FieldLabel'
6
+ import { countCodePointsInString, mergeRefs } from '../../_lib'
7
+ import { ReactAreaUseTextFieldCompat } from '../../_lib/compat'
8
+ import { theme } from '../../styled'
9
+
10
+ type DOMProps = Omit<
11
+ React.TextareaHTMLAttributes<HTMLTextAreaElement>,
12
+ // react-ariaのhookは、onChangeが`(v: string) => void`想定
13
+ | 'onChange'
14
+ // ReactAreaUseTextFieldCompatに書いてあるような事情で、ここにあるイベントハンドラの型をゆるめる
15
+ | keyof ReactAreaUseTextFieldCompat
16
+ >
17
+
18
+ export interface TextAreaProps
19
+ extends Pick<FieldLabelProps, 'label' | 'requiredText' | 'subLabel'>,
20
+ DOMProps,
21
+ ReactAreaUseTextFieldCompat {
22
+ readonly autoHeight?: boolean
23
+ readonly rows?: number
24
+
25
+ // <input> 要素は number とか string[] もありうるが、今はこれしか想定してない
26
+ readonly defaultValue?: string
27
+ readonly value?: string
28
+ readonly onChange?: (value: string) => void
29
+
30
+ // react-ariaの型定義のせいでHTMLTextAreaElementにできない
31
+ readonly onKeyDown?: (event: React.KeyboardEvent<Element>) => void
32
+ readonly onFocus?: (event: React.FocusEvent<Element>) => void
33
+ readonly onBlur?: (event: React.FocusEvent<Element>) => void
34
+
35
+ readonly showCount?: boolean
36
+ readonly showLabel?: boolean
37
+ readonly assistiveText?: string
38
+ readonly invalid?: boolean
39
+ }
40
+
41
+ const TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(
42
+ function TextAreaInner({ onChange, ...props }, forwardRef) {
43
+ const {
44
+ className,
45
+ showCount = false,
46
+ showLabel = false,
47
+ label,
48
+ requiredText,
49
+ subLabel,
50
+ disabled = false,
51
+ required,
52
+ invalid = false,
53
+ assistiveText,
54
+ maxLength,
55
+ autoHeight = false,
56
+ rows: initialRows = 4,
57
+ } = props
58
+
59
+ const { visuallyHiddenProps } = useVisuallyHidden()
60
+ const textareaRef = useRef<HTMLTextAreaElement>(null)
61
+ const ariaRef = useRef<HTMLTextAreaElement>(null)
62
+ const [count, setCount] = useState(
63
+ countCodePointsInString(props.value ?? '')
64
+ )
65
+ const [rows, setRows] = useState(initialRows)
66
+
67
+ const syncHeight = useCallback(
68
+ (textarea: HTMLTextAreaElement) => {
69
+ const rows = (`${textarea.value}\n`.match(/\n/gu)?.length ?? 0) || 1
70
+ setRows(initialRows <= rows ? rows : initialRows)
71
+ },
72
+ [initialRows]
73
+ )
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
+ if (autoHeight && textareaRef.current !== null) {
86
+ syncHeight(textareaRef.current)
87
+ }
88
+ onChange?.(value)
89
+ },
90
+ [autoHeight, maxLength, nonControlled, onChange, syncHeight]
91
+ )
92
+
93
+ useEffect(() => {
94
+ setCount(countCodePointsInString(props.value ?? ''))
95
+ }, [props.value])
96
+
97
+ const { inputProps, labelProps, descriptionProps, errorMessageProps } =
98
+ useTextField(
99
+ {
100
+ inputElementType: 'textarea',
101
+ isDisabled: disabled,
102
+ isRequired: required,
103
+ validationState: invalid ? 'invalid' : 'valid',
104
+ description: !invalid && assistiveText,
105
+ errorMessage: invalid && assistiveText,
106
+ onChange: handleChange,
107
+ ...props,
108
+ },
109
+ ariaRef
110
+ )
111
+
112
+ useEffect(() => {
113
+ if (autoHeight && textareaRef.current !== null) {
114
+ syncHeight(textareaRef.current)
115
+ }
116
+ }, [autoHeight, syncHeight])
117
+
118
+ return (
119
+ <TextFieldRoot className={className} isDisabled={disabled}>
120
+ <TextFieldLabel
121
+ label={label}
122
+ requiredText={requiredText}
123
+ required={required}
124
+ subLabel={subLabel}
125
+ {...labelProps}
126
+ {...(!showLabel ? visuallyHiddenProps : {})}
127
+ />
128
+ <StyledTextareaContainer
129
+ invalid={invalid}
130
+ rows={showCount ? rows + 1 : rows}
131
+ >
132
+ <StyledTextarea
133
+ ref={mergeRefs(textareaRef, forwardRef, ariaRef)}
134
+ rows={rows}
135
+ noBottomPadding={showCount}
136
+ {...inputProps}
137
+ />
138
+ {showCount && (
139
+ <MultiLineCounter>
140
+ {maxLength !== undefined ? `${count}/${maxLength}` : count}
141
+ </MultiLineCounter>
142
+ )}
143
+ </StyledTextareaContainer>
144
+ {assistiveText != null && assistiveText.length !== 0 && (
145
+ <AssistiveText
146
+ invalid={invalid}
147
+ {...(invalid ? errorMessageProps : descriptionProps)}
148
+ >
149
+ {assistiveText}
150
+ </AssistiveText>
151
+ )}
152
+ </TextFieldRoot>
153
+ )
154
+ }
155
+ )
156
+
157
+ export default TextArea
158
+
159
+ const TextFieldRoot = styled.div<{ isDisabled: boolean }>`
160
+ display: flex;
161
+ flex-direction: column;
162
+
163
+ ${(p) => p.isDisabled && { opacity: p.theme.elementEffect.disabled.opacity }}
164
+ `
165
+
166
+ const TextFieldLabel = styled(FieldLabel)`
167
+ ${theme((o) => o.margin.bottom(8))}
168
+ `
169
+
170
+ const StyledTextareaContainer = styled.div<{ rows: number; invalid: boolean }>`
171
+ position: relative;
172
+ overflow: hidden;
173
+ padding: 0 8px;
174
+
175
+ ${(p) =>
176
+ theme((o) => [
177
+ o.bg.surface3.hover,
178
+ p.invalid && o.outline.assertive,
179
+ o.font.text2,
180
+ o.borderRadius(4),
181
+ ])}
182
+
183
+ &:focus-within {
184
+ ${(p) =>
185
+ theme((o) => (p.invalid ? o.outline.assertive : o.outline.default))}
186
+ }
187
+
188
+ ${({ rows }) => css`
189
+ height: calc(22px * ${rows} + 18px);
190
+ `};
191
+ `
192
+
193
+ const StyledTextarea = styled.textarea<{ noBottomPadding: boolean }>`
194
+ border: none;
195
+ outline: none;
196
+ resize: none;
197
+ font-family: inherit;
198
+ color: inherit;
199
+
200
+ /* Prevent zooming for iOS Safari */
201
+ transform-origin: top left;
202
+ transform: scale(0.875);
203
+ width: calc(100% / 0.875);
204
+ font-size: calc(14px / 0.875);
205
+ line-height: calc(22px / 0.875);
206
+ padding: calc(9px / 0.875) 0 ${(p) => (p.noBottomPadding ? 0 : '')};
207
+
208
+ ${({ rows = 1 }) => css`
209
+ height: calc(22px / 0.875 * ${rows});
210
+ `};
211
+
212
+ /* Display box-shadow for iOS Safari */
213
+ appearance: none;
214
+
215
+ background: none;
216
+
217
+ &::placeholder {
218
+ ${theme((o) => o.font.text3)}
219
+ }
220
+
221
+ /* Hide scrollbar for Chrome, Safari and Opera */
222
+ &::-webkit-scrollbar {
223
+ display: none;
224
+ }
225
+ /* Hide scrollbar for IE, Edge and Firefox */
226
+ -ms-overflow-style: none; /* IE and Edge */
227
+ scrollbar-width: none; /* Firefox */
228
+ `
229
+
230
+ const MultiLineCounter = styled.span`
231
+ position: absolute;
232
+ bottom: 9px;
233
+ right: 8px;
234
+
235
+ ${theme((o) => [o.typography(14).preserveHalfLeading, o.font.text3])}
236
+ `
237
+
238
+ const AssistiveText = styled.p<{ invalid: boolean }>`
239
+ ${(p) =>
240
+ theme((o) => [
241
+ o.typography(14),
242
+ o.margin.top(8),
243
+ o.margin.bottom(0),
244
+ o.font[p.invalid ? 'assertive' : 'text1'],
245
+ ])}
246
+ `
@@ -2,11 +2,7 @@ import { action } from '@storybook/addon-actions'
2
2
  import styled from 'styled-components'
3
3
  import { Story } from '../../_lib/compat'
4
4
  import Clickable from '../Clickable'
5
- import TextField, {
6
- MultiLineTextFieldProps,
7
- SingleLineTextFieldProps,
8
- TextFieldProps,
9
- } from '.'
5
+ import TextField, { TextFieldProps } from '.'
10
6
  import { px } from '@charcoal-ui/utils'
11
7
  import IconButton from '../IconButton'
12
8
 
@@ -37,19 +33,8 @@ const Template: Story<Partial<TextFieldProps>> = (args) => (
37
33
  subLabel={
38
34
  <Clickable onClick={action('label-click')}>Text Link</Clickable>
39
35
  }
40
- placeholder="Single Line"
41
- {...(args as Partial<SingleLineTextFieldProps>)}
42
- multiline={false}
43
- />
44
- <TextField
45
- label="Label"
46
- requiredText="*必須"
47
- subLabel={
48
- <Clickable onClick={action('label-click')}>Text Link</Clickable>
49
- }
50
- placeholder="Multi Line"
51
- {...(args as Partial<MultiLineTextFieldProps>)}
52
- multiline
36
+ placeholder="TextField"
37
+ {...args}
53
38
  />
54
39
  </Container>
55
40
  )
@@ -69,7 +54,7 @@ HasCount.args = {
69
54
  maxLength: 100,
70
55
  }
71
56
 
72
- export const HasAffix: Story<Partial<SingleLineTextFieldProps>> = (args) => (
57
+ export const HasAffix: Story<Partial<TextFieldProps>> = (args) => (
73
58
  <TextField label="Label" placeholder="path/to/your/file" {...args} />
74
59
  )
75
60
  HasAffix.args = {
@@ -79,14 +64,7 @@ HasAffix.args = {
79
64
  suffix: '.png',
80
65
  }
81
66
 
82
- export const AutoHeight: Story<Partial<MultiLineTextFieldProps>> = (args) => (
83
- <TextField label="Label" placeholder="Multi Line" {...args} multiline />
84
- )
85
- AutoHeight.args = {
86
- autoHeight: true,
87
- }
88
-
89
- export const PrefixIcon: Story<Partial<SingleLineTextFieldProps>> = (args) => (
67
+ export const PrefixIcon: Story<Partial<TextFieldProps>> = (args) => (
90
68
  <TextField
91
69
  label="Label"
92
70
  placeholder="Icon prefix"
@@ -101,7 +79,7 @@ export const PrefixIcon: Story<Partial<SingleLineTextFieldProps>> = (args) => (
101
79
  )
102
80
 
103
81
  const PrefixIconWrap = styled.div`
104
- color: ${({ theme }) => theme.color.text4};
82
+ color: ${({ theme }) => theme.color.text3};
105
83
  margin-top: 2px;
106
84
  margin-right: 4px;
107
85
  `