@kolkrabbi/kol-component 0.157.0 → 0.159.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.
- package/package.json +1 -1
- package/src/atoms/Button.jsx +3 -1
- package/src/atoms/Input.jsx +38 -5
- package/src/hooks/glyphLadders.js +3 -3
- package/src/molecules/ContentCard.jsx +4 -1
- package/src/molecules/ContentRow.jsx +5 -2
- package/src/molecules/ContentText.jsx +6 -2
- package/src/molecules/Dropdown.jsx +2 -1
- package/src/molecules/Stepper.jsx +27 -9
- package/src/organisms/SectionNewsletter.jsx +9 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-component",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.159.0",
|
|
4
4
|
"description": "KOL design-system components — atoms through organisms, emitting canonical kol-* classes. Pairs with @kolkrabbi/kol-theme for styling.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
package/src/atoms/Button.jsx
CHANGED
|
@@ -13,7 +13,7 @@ import { glyphSize } from '../hooks/glyphLadders.js'
|
|
|
13
13
|
* @param {Object} props
|
|
14
14
|
* @param {ReactNode} props.children - Button content
|
|
15
15
|
* @param {'primary'|'secondary'|'accent'|'outline'|'ghost'|'nav'|'danger'|'grey'|'control'} props.variant - Visual variant. `danger` is the destructive treatment (--ui-error fill); `nav` is the chrome rung — transparent, oq-64 ink, one step brighter than `ghost`; `control` is an alias for `ghost` (legacy call-sites).
|
|
16
|
-
* @param {'sm'|'md'|'lg'} props.size - Button size (default: 'md')
|
|
16
|
+
* @param {'xs'|'sm'|'md'|'lg'} props.size - Button size (default: 'md'); xs is the panel rung (ControlsXsRung, 2026-09-01) — kol-mono-8 in a 22px shell (20 icon-only), opt-in by prop
|
|
17
17
|
* @param {string} props.iconLeft - Icon name to display on the left
|
|
18
18
|
* @param {string} props.iconRight - Icon name to display on the right
|
|
19
19
|
* @param {string} props.iconLeftHover - Icon to show on hover (left position)
|
|
@@ -98,6 +98,8 @@ const Button = ({
|
|
|
98
98
|
// Add size class — pairs the padding rule with its mono type class.
|
|
99
99
|
const sizeClass = size === 'sm'
|
|
100
100
|
? 'kol-btn-sm kol-mono-12'
|
|
101
|
+
: size === 'xs'
|
|
102
|
+
? 'kol-btn-xs kol-mono-8'
|
|
101
103
|
: size === 'lg'
|
|
102
104
|
? 'kol-btn-lg kol-mono-16'
|
|
103
105
|
: 'kol-btn-md kol-mono-14'
|
package/src/atoms/Input.jsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react'
|
|
1
2
|
import { Icon } from '@kolkrabbi/kol-icons'
|
|
2
3
|
import { toneClass } from '../utilities/tone.js'
|
|
3
4
|
import { glyphSize } from '../hooks/glyphLadders.js'
|
|
@@ -11,6 +12,13 @@ import { glyphSize } from '../hooks/glyphLadders.js'
|
|
|
11
12
|
* secondary, always subordinate to filled)
|
|
12
13
|
* tone="inverse" — the dark chip (`fg-ab-24`) for a washed plane
|
|
13
14
|
* (ControlToneInverse, 2026-08-27); same prop on ViewToggle · Dropdown
|
|
15
|
+
* size="xs" — the panel rung (ControlsXsRung, 2026-09-01):
|
|
16
|
+
* kol-mono-8 in a 22px shell; opt-in by prop
|
|
17
|
+
* onCommit — `(trimmed) => void` on blur / Enter (Escape
|
|
18
|
+
* restores): the rack commits a module name or a
|
|
19
|
+
* scope expression, not every keystroke. With it
|
|
20
|
+
* the field keeps a local draft seeded from
|
|
21
|
+
* `value`; `onChange` still fires live if given.
|
|
14
22
|
* variant="ghost" — legacy alias, resolves to outline
|
|
15
23
|
* variant="property" — the Figma property field (PropertyField,
|
|
16
24
|
* 2026-08-12): filled chrome, dim `affordance`
|
|
@@ -46,12 +54,13 @@ import { glyphSize } from '../hooks/glyphLadders.js'
|
|
|
46
54
|
* Input owns prefix/suffix/icon layout + the inner <input> styling.
|
|
47
55
|
*/
|
|
48
56
|
|
|
49
|
-
const SIZE_TYPE = { sm: 'kol-mono-12', md: 'kol-mono-14', lg: 'kol-mono-16' }
|
|
57
|
+
const SIZE_TYPE = { xs: 'kol-mono-8', sm: 'kol-mono-12', md: 'kol-mono-14', lg: 'kol-mono-16' }
|
|
50
58
|
|
|
51
59
|
export default function Input({
|
|
52
60
|
type = 'text',
|
|
53
61
|
value,
|
|
54
62
|
onChange,
|
|
63
|
+
onCommit,
|
|
55
64
|
variant = 'filled',
|
|
56
65
|
tone = 'default',
|
|
57
66
|
size = 'md',
|
|
@@ -100,6 +109,26 @@ export default function Input({
|
|
|
100
109
|
* `${len}ch` hugs the value with no probe element, and stays number-safe
|
|
101
110
|
* where the HTML `size` attr is ignored (<input type="number">). +2px keeps
|
|
102
111
|
* the caret from clipping at the end. Tracks `value` → controlled only. */
|
|
112
|
+
/* commit-on-blur/Enter (ControlsXsRung, 2026-09-01 — kol-monitor's panel
|
|
113
|
+
* TextInput collapsing onto this atom): a local draft, seeded from `value`,
|
|
114
|
+
* committed trimmed; Escape restores. Only when `onCommit` is given. */
|
|
115
|
+
const [draft, setDraft] = useState(value ?? '')
|
|
116
|
+
/* the ref mirrors the draft so a blur that lands in the same tick as the
|
|
117
|
+
* last keystroke commits what was typed, not the last RENDER's draft */
|
|
118
|
+
const draftRef = useRef(value ?? '')
|
|
119
|
+
useEffect(() => { if (onCommit) { setDraft(value ?? ''); draftRef.current = value ?? '' } }, [value, onCommit])
|
|
120
|
+
const commitProps = onCommit
|
|
121
|
+
? {
|
|
122
|
+
value: draft,
|
|
123
|
+
onChange: (e) => { draftRef.current = e.target.value; setDraft(e.target.value); onChange?.(e) },
|
|
124
|
+
onBlur: () => onCommit(String(draftRef.current).trim()),
|
|
125
|
+
onKeyDown: (e) => {
|
|
126
|
+
if (e.key === 'Enter') e.currentTarget.blur()
|
|
127
|
+
if (e.key === 'Escape') { draftRef.current = value ?? ''; setDraft(value ?? ''); e.currentTarget.blur() }
|
|
128
|
+
},
|
|
129
|
+
}
|
|
130
|
+
: null
|
|
131
|
+
|
|
103
132
|
const propertyLen = Math.max(String(value ?? placeholder ?? '').length, 1)
|
|
104
133
|
|
|
105
134
|
/* Pin inner input height to the typography token's line-height. Without
|
|
@@ -108,7 +137,7 @@ export default function Input({
|
|
|
108
137
|
* from the font's ascender+descender (font-metric), not strictly from
|
|
109
138
|
* CSS line-height. Result: kol-control-sm ends up 26.5px instead of 26.
|
|
110
139
|
* h-4 / h-[18px] / h-[22px] match the kol-mono-12 / -14 / -16 line-heights. */
|
|
111
|
-
const heightCls = size === 'sm' ? 'h-4' : size === 'md' ? 'h-[18px]' : 'h-[22px]'
|
|
140
|
+
const heightCls = size === 'xs' ? 'h-3' : size === 'sm' ? 'h-4' : size === 'md' ? 'h-[18px]' : 'h-[22px]'
|
|
112
141
|
|
|
113
142
|
const inputCls = [
|
|
114
143
|
'min-w-0 bg-transparent border-none outline-none text-auto',
|
|
@@ -149,9 +178,11 @@ export default function Input({
|
|
|
149
178
|
* Controlled + no onChange = deliberate display-only → readOnly. */}
|
|
150
179
|
<input
|
|
151
180
|
type={type}
|
|
152
|
-
{...(
|
|
153
|
-
?
|
|
154
|
-
:
|
|
181
|
+
{...(commitProps
|
|
182
|
+
? commitProps
|
|
183
|
+
: value !== undefined
|
|
184
|
+
? { value: value ?? '', onChange, readOnly: !onChange || undefined }
|
|
185
|
+
: { onChange })}
|
|
155
186
|
placeholder={placeholder}
|
|
156
187
|
disabled={disabled}
|
|
157
188
|
spellCheck={false}
|
|
@@ -159,6 +190,8 @@ export default function Input({
|
|
|
159
190
|
className={inputCls}
|
|
160
191
|
style={isProperty ? { width: `calc(${propertyLen}ch + 2px)` } : undefined}
|
|
161
192
|
{...inputProps}
|
|
193
|
+
/* the commit pair LAST, so nothing spread above it can shadow onBlur / onKeyDown */
|
|
194
|
+
{...(commitProps || {})}
|
|
162
195
|
/>
|
|
163
196
|
{unit !== undefined && (
|
|
164
197
|
<span aria-hidden="true" className="text-meta shrink-0">{unit}</span>
|
|
@@ -24,10 +24,10 @@
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
/** Icon alone in a pinned square. Pairs with squares 28 · 32 · 36. */
|
|
27
|
-
export const SOLO = { sm: 16, md: 20, lg: 24 }
|
|
27
|
+
export const SOLO = { xs: 12, sm: 16, md: 20, lg: 24 }
|
|
28
28
|
|
|
29
29
|
/** Icon beside a label, inside the rung's line box. */
|
|
30
|
-
export const ADJACENT = { sm: 14, md: 16, lg: 18 }
|
|
30
|
+
export const ADJACENT = { xs: 10, sm: 14, md: 16, lg: 18 }
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Indicator glyph — a caret/chevron that DECORATES a control rather than
|
|
@@ -37,7 +37,7 @@ export const ADJACENT = { sm: 14, md: 16, lg: 18 }
|
|
|
37
37
|
* Dropdown's caret each hand-typed their number (Dropdown took the ADJACENT
|
|
38
38
|
* rung, the oversize the user called): two transcriptions, the folklore
|
|
39
39
|
* threshold. */
|
|
40
|
-
export const INDICATOR = { sm: 12, md: 14, lg: 16 }
|
|
40
|
+
export const INDICATOR = { xs: 8, sm: 12, md: 14, lg: 16 }
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
43
|
* Resolve a glyph size. `solo` picks the ladder; `size` indexes it.
|
|
@@ -142,6 +142,9 @@ export default function ContentCard({
|
|
|
142
142
|
hero = false,
|
|
143
143
|
label,
|
|
144
144
|
pad,
|
|
145
|
+
/* the tag chip follows the BOX (CardTagsNoVisibleFill, 2026-09-01): a solid
|
|
146
|
+
* surface fill keeps `tertiary`, a plain or washed box takes `primary` */
|
|
147
|
+
tagVariant,
|
|
145
148
|
media,
|
|
146
149
|
ratio,
|
|
147
150
|
fit,
|
|
@@ -222,7 +225,7 @@ export default function ContentCard({
|
|
|
222
225
|
zIndex: box.layout === 'canvas' ? 1 : undefined,
|
|
223
226
|
}}
|
|
224
227
|
>
|
|
225
|
-
{hasText && <ContentText variant={variant} form={isHero ? 'hero' : 'card'} {...textSlots} />}
|
|
228
|
+
{hasText && <ContentText variant={variant} form={isHero ? 'hero' : 'card'} tagVariant={tagVariant ?? (/surface-/.test(box.bg ?? '') ? 'tertiary' : 'primary')} {...textSlots} />}
|
|
226
229
|
{/* ABSOLUTE, not a flex sibling: the plate's height moves with the title
|
|
227
230
|
* and the meta, so a laid-out stack would stretch or drift with it. The
|
|
228
231
|
* inset reads the SAME pad token the plate uses, so the icons sit the
|
|
@@ -33,6 +33,8 @@ import ContentText from './ContentText.jsx'
|
|
|
33
33
|
* and takes a band through `footer` instead.
|
|
34
34
|
* @param {boolean} selected
|
|
35
35
|
* @param {Function} onClick
|
|
36
|
+
* @param {string} tagVariant the tag chip (CardTagsNoVisibleFill, 2026-09-01): default follows the BOX — a solid
|
|
37
|
+
* surface fill keeps `tertiary` (the /work row it was minted for), a plain or washed box takes `primary`
|
|
36
38
|
* text slots + *Class seams forwarded to ContentText.
|
|
37
39
|
*/
|
|
38
40
|
|
|
@@ -151,6 +153,7 @@ export default function ContentRow({
|
|
|
151
153
|
onClick,
|
|
152
154
|
href,
|
|
153
155
|
onNavigate,
|
|
156
|
+
tagVariant,
|
|
154
157
|
className = '',
|
|
155
158
|
...text
|
|
156
159
|
}) {
|
|
@@ -222,7 +225,7 @@ export default function ContentRow({
|
|
|
222
225
|
* (a rendered alphabet, a waveform, a sparkline) is never the family's. */
|
|
223
226
|
const inner = box.column ? (
|
|
224
227
|
<>
|
|
225
|
-
<ContentText variant={variant} form="row" className="w-full" {...text} />
|
|
228
|
+
<ContentText variant={variant} form="row" className="w-full" tagVariant={tagVariant ?? (/surface-/.test(box.bg ?? '') ? 'tertiary' : 'primary')} {...text} />
|
|
226
229
|
{footer}
|
|
227
230
|
</>
|
|
228
231
|
) : (
|
|
@@ -250,7 +253,7 @@ export default function ContentRow({
|
|
|
250
253
|
* ruled it on screen): the two lines stack to 34 inside a 40 content box,
|
|
251
254
|
* and pushing them fully apart puts the ascenders hard against the thumb's
|
|
252
255
|
* top and bottom edges. It is the ROW's ruling, so the row passes it. */}
|
|
253
|
-
<ContentText variant={variant} form="row" className={`flex-1 ${box.height != null ? 'py-[2px]' : ''}`.trim()} {...text} />
|
|
256
|
+
<ContentText variant={variant} form="row" className={`flex-1 ${box.height != null ? 'py-[2px]' : ''}`.trim()} tagVariant={tagVariant ?? (/surface-/.test(box.bg ?? '') ? 'tertiary' : 'primary')} {...text} />
|
|
254
257
|
{/* `specs` rides the trailing edge on EVERY variant — year · material ·
|
|
255
258
|
* edition is a content difference, not a geometry one, and minting a
|
|
256
259
|
* seventh page-named box for it is the exact mistake §1 of the ticket is
|
|
@@ -24,6 +24,10 @@
|
|
|
24
24
|
* @param {ReactNode} size default · article (file size / read length)
|
|
25
25
|
* @param {ReactNode} meta work only
|
|
26
26
|
* @param {number} gap inner line gap in px (defaults per variant/form)
|
|
27
|
+
* @param {string} tagVariant the Tag variant the `tags` slot draws (default `primary` — a soft ink wash that reads as a chip
|
|
28
|
+
* on a plain surface; CardTagsNoVisibleFill, kol-website 2026-09-01: `tertiary` is surface-primary with no border and was
|
|
29
|
+
* INVISIBLE on every card that sits on surface-primary). ContentCard / ContentRow pass `tertiary` for a box with a
|
|
30
|
+
* solid surface fill — the /work row the variant was minted for — and `primary` otherwise
|
|
27
31
|
* @param {string} titleClass … eyebrowClass (alias `kickerClass`), bodyClass, detailClass,
|
|
28
32
|
* dateClass, sizeClass, metaClass — full class overrides
|
|
29
33
|
*/
|
|
@@ -203,7 +207,7 @@ export default function ContentText({
|
|
|
203
207
|
variant: variantProp = 'file',
|
|
204
208
|
form = 'card',
|
|
205
209
|
title, body, eyebrow, kicker, detail, date, size, meta, tags,
|
|
206
|
-
gap, clamp,
|
|
210
|
+
gap, clamp, tagVariant = 'primary',
|
|
207
211
|
titleClass, bodyClass, eyebrowClass, kickerClass, detailClass, dateClass, sizeClass, metaClass, tagsClass,
|
|
208
212
|
className = '',
|
|
209
213
|
}) {
|
|
@@ -244,7 +248,7 @@ export default function ContentText({
|
|
|
244
248
|
if (slot === 'tags')
|
|
245
249
|
return v.map((t, i) =>
|
|
246
250
|
typeof t === 'string' || typeof t === 'number' ? (
|
|
247
|
-
<Tag key={i} variant=
|
|
251
|
+
<Tag key={i} variant={tagVariant} size="sm">{t}</Tag>
|
|
248
252
|
) : t
|
|
249
253
|
)
|
|
250
254
|
return v.map((item, i) => <span key={i}>{item}</span>)
|
|
@@ -32,7 +32,8 @@ import { indicatorSize } from '../hooks/glyphLadders.js'
|
|
|
32
32
|
* (lg ≥1024 / md ≥768 / sm below) is gone.
|
|
33
33
|
*/
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
/* xs (ControlsXsRung, 2026-09-01): the panel rung, opt-in by prop — the default stays sm (2026-07-28 law) */
|
|
36
|
+
const SIZE_TYPE = { xs: 'kol-mono-8', sm: 'kol-mono-12', md: 'kol-mono-14', lg: 'kol-mono-16' }
|
|
36
37
|
/* Caret size comes from the INDICATOR ladder (glyphLadders.js) — the private
|
|
37
38
|
* map that lived here was a transcription of ADJACENT, which is the wrong
|
|
38
39
|
* ladder for a decoration: it put a caret one rung HEAVIER than the label
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Stepper — number input + chevron buttons, built on the .kol-control shell.
|
|
3
3
|
*
|
|
4
|
-
* size="sm" (default) / "md" / "lg" — matched padding + type class.
|
|
5
|
-
* Chevron scale follows the size: 8 / 10 / 12 px each, stacked.
|
|
4
|
+
* size="xs" / "sm" (default) / "md" / "lg" — matched padding + type class.
|
|
5
|
+
* Chevron scale follows the size: 6 / 8 / 10 / 12 px each, stacked. xs is the
|
|
6
|
+
* panel rung (ControlsXsRung, kol-monitor 2026-09-01).
|
|
7
|
+
*
|
|
8
|
+
* options — step through a LIST instead of a number range (the rack's
|
|
9
|
+
* ‹ value › Selector, as a variant of this on the ladder; user: "selector
|
|
10
|
+
* could be a variant of ours if we make it follow the size ladder"). `value`
|
|
11
|
+
* is one of the options, the field is read-only, the chevrons step and WRAP
|
|
12
|
+
* at both ends, and `onChange` reports the option in the same event shape
|
|
13
|
+
* as the number path — `{ target: { value: option } }`.
|
|
6
14
|
*
|
|
7
15
|
* Loaded from `00-kol/chevron-{up,down}.svg` via the Icon registry — first
|
|
8
16
|
* stroke icons in the kol curated set. For a plain number input without
|
|
@@ -10,8 +18,8 @@
|
|
|
10
18
|
*/
|
|
11
19
|
import { Icon } from '@kolkrabbi/kol-icons'
|
|
12
20
|
|
|
13
|
-
const SIZE_TYPE = { sm: 'kol-mono-12', md: 'kol-mono-14', lg: 'kol-mono-16' }
|
|
14
|
-
const CHEVRON_SIZE = { sm: 8, md: 10, lg: 12 }
|
|
21
|
+
const SIZE_TYPE = { xs: 'kol-mono-8', sm: 'kol-mono-12', md: 'kol-mono-14', lg: 'kol-mono-16' }
|
|
22
|
+
const CHEVRON_SIZE = { xs: 6, sm: 8, md: 10, lg: 12 }
|
|
15
23
|
|
|
16
24
|
export default function Stepper({
|
|
17
25
|
value,
|
|
@@ -19,18 +27,27 @@ export default function Stepper({
|
|
|
19
27
|
min,
|
|
20
28
|
max,
|
|
21
29
|
step = 1,
|
|
30
|
+
options,
|
|
22
31
|
size = 'sm',
|
|
23
32
|
className = '',
|
|
24
33
|
style = {},
|
|
25
34
|
...props
|
|
26
35
|
}) {
|
|
36
|
+
/* the list path: wrap at both ends, report the option */
|
|
37
|
+
const stepList = (dir) => {
|
|
38
|
+
const i = options.indexOf(value)
|
|
39
|
+
const n = i < 0 ? 0 : (i + dir + options.length) % options.length
|
|
40
|
+
onChange?.({ target: { value: options[n] } })
|
|
41
|
+
}
|
|
27
42
|
const handleIncrement = () => {
|
|
43
|
+
if (options) return stepList(1)
|
|
28
44
|
const newValue = Number(value) + step
|
|
29
45
|
if (max !== undefined && newValue > max) return
|
|
30
46
|
onChange?.({ target: { value: newValue } })
|
|
31
47
|
}
|
|
32
48
|
|
|
33
49
|
const handleDecrement = () => {
|
|
50
|
+
if (options) return stepList(-1)
|
|
34
51
|
const newValue = Number(value) - step
|
|
35
52
|
if (min !== undefined && newValue < min) return
|
|
36
53
|
onChange?.({ target: { value: newValue } })
|
|
@@ -64,12 +81,13 @@ export default function Stepper({
|
|
|
64
81
|
return (
|
|
65
82
|
<div className={shellCls} style={style}>
|
|
66
83
|
<input
|
|
67
|
-
type=
|
|
84
|
+
type={options ? 'text' : 'number'}
|
|
85
|
+
readOnly={options ? true : undefined}
|
|
68
86
|
value={value ?? ''}
|
|
69
|
-
onChange={handleInputChange}
|
|
70
|
-
min={min}
|
|
71
|
-
max={max}
|
|
72
|
-
step={step}
|
|
87
|
+
onChange={options ? undefined : handleInputChange}
|
|
88
|
+
min={options ? undefined : min}
|
|
89
|
+
max={options ? undefined : max}
|
|
90
|
+
step={options ? undefined : step}
|
|
73
91
|
className="w-full min-w-0 bg-transparent border-none outline-none text-auto hide-number-spinners"
|
|
74
92
|
style={{ paddingRight: `${inputPaddingRight}px` }}
|
|
75
93
|
{...props}
|
|
@@ -160,9 +160,17 @@ export default function SectionNewsletter({
|
|
|
160
160
|
{/* `w-full`: SectionText centres its children as flex items, so
|
|
161
161
|
* without it the form shrink-wraps and the Input's `w-full` has
|
|
162
162
|
* nothing to fill — it rendered at its intrinsic ~190px */}
|
|
163
|
+
{/* ON BUTTONGROUP'S LADDER (NewsletterFormGapOffLadder, kol-website
|
|
164
|
+
* 2026-09-01; user: "the gap between input and button in newsletter
|
|
165
|
+
* should be the same as button group"): `gap-2 sm:gap-4` — 8 stacked,
|
|
166
|
+
* 16 in the row — the pair ButtonGroup ruled on 08-31. This form was
|
|
167
|
+
* `gap-4 sm:gap-3`, inverted against it. The `pt-6` went too: the
|
|
168
|
+
* SectionText above already spaces its children by `gap-6`, so the
|
|
169
|
+
* form sat 48 under the body where every other section's actions sit
|
|
170
|
+
* 24. */}
|
|
163
171
|
<form
|
|
164
172
|
onSubmit={handleSubmit}
|
|
165
|
-
className="flex w-full flex-col gap-
|
|
173
|
+
className="flex w-full flex-col gap-2 sm:flex-row sm:items-center sm:justify-center sm:gap-4"
|
|
166
174
|
>
|
|
167
175
|
<Input
|
|
168
176
|
id={emailId}
|