@kolkrabbi/kol-component 0.3.0 → 0.4.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/QuantityInput.jsx +76 -114
- package/src/index.js +0 -1
- package/src/molecules/MenuPopover.jsx +4 -27
- package/src/atoms/QuantityStepper.jsx +0 -149
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-component",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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",
|
|
@@ -1,9 +1,31 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react'
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* QuantityInput — a compact integer quantity picker with two control layouts.
|
|
5
|
+
*
|
|
6
|
+
* controls="chevron" (default) — value with a stacked up/down chevron pair
|
|
7
|
+
* on the right, inside a full-width field.
|
|
8
|
+
* controls="split" — a − value + pill (minus and plus flank the
|
|
9
|
+
* value). The former `QuantityStepper`.
|
|
10
|
+
*
|
|
11
|
+
* Display-only value (not a text-editable input) — for an editable number
|
|
12
|
+
* field with bump chevrons use `Stepper`. Size auto-adapts to the viewport
|
|
13
|
+
* (sm < 768 ≤ md < 1024 ≤ lg) unless `size` pins it. Controlled: `value` +
|
|
14
|
+
* `(next) => onChange`, clamped to [min, max].
|
|
15
|
+
*
|
|
16
|
+
* @param {number} value current quantity (default 1)
|
|
17
|
+
* @param {Function} onChange (next) => void
|
|
18
|
+
* @param {number} min lower bound (default 1)
|
|
19
|
+
* @param {number} max upper bound (default 99)
|
|
20
|
+
* @param {string} controls 'chevron' (default) | 'split'
|
|
21
|
+
* @param {string} size 'sm' | 'md' | 'lg' — pins the responsive size
|
|
22
|
+
* @param {string} className extra classes on the root
|
|
23
|
+
*/
|
|
24
|
+
|
|
3
25
|
const SIZE_MAP = {
|
|
4
26
|
sm: { fontSize: 11, paddingY: 12, paddingX: 24, radius: 20, icon: 10 },
|
|
5
27
|
md: { fontSize: 12, paddingY: 14, paddingX: 24, radius: 22, icon: 12 },
|
|
6
|
-
lg: { fontSize: 14, paddingY: 16, paddingX: 24, radius: 24, icon: 14 }
|
|
28
|
+
lg: { fontSize: 14, paddingY: 16, paddingX: 24, radius: 24, icon: 14 },
|
|
7
29
|
}
|
|
8
30
|
|
|
9
31
|
const QuantityInput = ({
|
|
@@ -11,33 +33,21 @@ const QuantityInput = ({
|
|
|
11
33
|
onChange,
|
|
12
34
|
min = 1,
|
|
13
35
|
max = 99,
|
|
36
|
+
controls = 'chevron',
|
|
14
37
|
size,
|
|
15
|
-
className = ''
|
|
38
|
+
className = '',
|
|
16
39
|
}) => {
|
|
17
40
|
const [resolvedSize, setResolvedSize] = useState('md')
|
|
18
41
|
const [componentWidth, setComponentWidth] = useState('180px')
|
|
19
42
|
|
|
20
43
|
useEffect(() => {
|
|
21
44
|
const determineSize = () => {
|
|
22
|
-
if (size) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (typeof window === 'undefined') {
|
|
28
|
-
setResolvedSize('md')
|
|
29
|
-
return
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (window.innerWidth >= 1024) {
|
|
33
|
-
setResolvedSize('lg')
|
|
34
|
-
} else if (window.innerWidth >= 768) {
|
|
35
|
-
setResolvedSize('md')
|
|
36
|
-
} else {
|
|
37
|
-
setResolvedSize('sm')
|
|
38
|
-
}
|
|
45
|
+
if (size) { setResolvedSize(size); return }
|
|
46
|
+
if (typeof window === 'undefined') { setResolvedSize('md'); return }
|
|
47
|
+
if (window.innerWidth >= 1024) setResolvedSize('lg')
|
|
48
|
+
else if (window.innerWidth >= 768) setResolvedSize('md')
|
|
49
|
+
else setResolvedSize('sm')
|
|
39
50
|
}
|
|
40
|
-
|
|
41
51
|
determineSize()
|
|
42
52
|
window.addEventListener('resize', determineSize)
|
|
43
53
|
return () => window.removeEventListener('resize', determineSize)
|
|
@@ -46,14 +56,9 @@ const QuantityInput = ({
|
|
|
46
56
|
useEffect(() => {
|
|
47
57
|
const updateWidth = () => {
|
|
48
58
|
if (typeof window === 'undefined') return
|
|
49
|
-
|
|
50
|
-
if (window.innerWidth >=
|
|
51
|
-
|
|
52
|
-
} else if (window.innerWidth >= 768) {
|
|
53
|
-
setComponentWidth('140px')
|
|
54
|
-
} else {
|
|
55
|
-
setComponentWidth('100px')
|
|
56
|
-
}
|
|
59
|
+
if (window.innerWidth >= 1024) setComponentWidth('180px')
|
|
60
|
+
else if (window.innerWidth >= 768) setComponentWidth('140px')
|
|
61
|
+
else setComponentWidth('100px')
|
|
57
62
|
}
|
|
58
63
|
updateWidth()
|
|
59
64
|
window.addEventListener('resize', updateWidth)
|
|
@@ -61,108 +66,65 @@ const QuantityInput = ({
|
|
|
61
66
|
}, [])
|
|
62
67
|
|
|
63
68
|
const metrics = SIZE_MAP[resolvedSize] || SIZE_MAP.md
|
|
69
|
+
const increment = () => { if (value < max) onChange?.(value + 1) }
|
|
70
|
+
const decrement = () => { if (value > min) onChange?.(value - 1) }
|
|
64
71
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
72
|
+
/* controls="split" — the − value + pill (formerly QuantityStepper). */
|
|
73
|
+
if (controls === 'split') {
|
|
74
|
+
const btn = (disabled) => ({
|
|
75
|
+
padding: `${metrics.paddingY}px 12px`,
|
|
76
|
+
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
77
|
+
backgroundColor: 'transparent', border: 'none',
|
|
78
|
+
color: 'var(--kol-surface-on-primary)',
|
|
79
|
+
cursor: disabled ? 'not-allowed' : 'pointer',
|
|
80
|
+
opacity: disabled ? 0.3 : 1,
|
|
81
|
+
transition: 'opacity 0.15s',
|
|
82
|
+
fontFamily: 'var(--kol-font-family-mono)',
|
|
83
|
+
fontSize: `${metrics.fontSize}px`, lineHeight: '120%',
|
|
84
|
+
})
|
|
85
|
+
return (
|
|
86
|
+
<div
|
|
87
|
+
className={`flex items-center justify-center ${className}`}
|
|
88
|
+
style={{
|
|
89
|
+
width: componentWidth, minWidth: componentWidth,
|
|
90
|
+
border: '1px solid var(--kol-border-default)',
|
|
91
|
+
borderRadius: `${metrics.radius}px`,
|
|
92
|
+
backgroundColor: 'var(--kol-surface-primary)',
|
|
93
|
+
}}
|
|
94
|
+
>
|
|
95
|
+
<button type="button" onClick={decrement} disabled={value <= min} style={btn(value <= min)} aria-label="Decrease quantity">−</button>
|
|
96
|
+
<span style={{ minWidth: '24px', textAlign: 'center', color: 'var(--kol-surface-on-primary)', fontFamily: 'var(--kol-font-family-mono)', fontSize: `${metrics.fontSize}px`, lineHeight: '120%' }}>{value}</span>
|
|
97
|
+
<button type="button" onClick={increment} disabled={value >= max} style={btn(value >= max)} aria-label="Increase quantity">+</button>
|
|
98
|
+
</div>
|
|
99
|
+
)
|
|
71
100
|
}
|
|
72
101
|
|
|
102
|
+
/* controls="chevron" (default) — value + stacked chevron pair on the right. */
|
|
73
103
|
return (
|
|
74
|
-
<div
|
|
75
|
-
className={`relative block ${className}`}
|
|
76
|
-
>
|
|
77
|
-
{/* Border wrapper - EXPLICIT 42.5px height */}
|
|
104
|
+
<div className={`relative block ${className}`}>
|
|
78
105
|
<div
|
|
79
106
|
className="w-full flex items-center"
|
|
80
107
|
style={{
|
|
81
|
-
position: 'relative',
|
|
82
|
-
height: '42.5px',
|
|
108
|
+
position: 'relative', height: '42.5px',
|
|
83
109
|
border: '1px solid var(--kol-border-default)',
|
|
84
110
|
borderRadius: `${metrics.radius}px`,
|
|
85
111
|
backgroundColor: 'var(--kol-surface-primary)',
|
|
86
112
|
color: 'var(--kol-surface-on-primary)',
|
|
87
|
-
paddingLeft: `${metrics.paddingX}px`,
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
lineHeight: '120%',
|
|
91
|
-
fontFamily: 'var(--kol-font-family-mono)',
|
|
92
|
-
boxSizing: 'border-box'
|
|
113
|
+
paddingLeft: `${metrics.paddingX}px`, paddingRight: `${metrics.paddingX}px`,
|
|
114
|
+
fontSize: `${metrics.fontSize}px`, lineHeight: '120%',
|
|
115
|
+
fontFamily: 'var(--kol-font-family-mono)', boxSizing: 'border-box',
|
|
93
116
|
}}
|
|
94
117
|
>
|
|
95
118
|
<span>{value}</span>
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
position: 'absolute',
|
|
101
|
-
right: `${metrics.paddingX}px`,
|
|
102
|
-
top: '50%',
|
|
103
|
-
transform: 'translateY(-50%)',
|
|
104
|
-
display: 'flex',
|
|
105
|
-
flexDirection: 'column'
|
|
106
|
-
}}
|
|
107
|
-
>
|
|
108
|
-
<button
|
|
109
|
-
type="button"
|
|
110
|
-
onClick={increment}
|
|
111
|
-
disabled={value >= max}
|
|
112
|
-
style={{
|
|
113
|
-
backgroundColor: 'transparent',
|
|
114
|
-
border: 'none',
|
|
115
|
-
padding: '0',
|
|
116
|
-
cursor: value >= max ? 'not-allowed' : 'pointer',
|
|
117
|
-
opacity: value >= max ? 0.3 : 1,
|
|
118
|
-
lineHeight: 0,
|
|
119
|
-
display: 'block'
|
|
120
|
-
}}
|
|
121
|
-
aria-label="Increase quantity"
|
|
122
|
-
>
|
|
123
|
-
<svg
|
|
124
|
-
width={metrics.icon}
|
|
125
|
-
height={metrics.icon}
|
|
126
|
-
viewBox="0 0 12 12"
|
|
127
|
-
fill="none"
|
|
128
|
-
>
|
|
129
|
-
<path
|
|
130
|
-
d="m3 7 3-3 3 3"
|
|
131
|
-
stroke="currentColor"
|
|
132
|
-
strokeWidth="1.25"
|
|
133
|
-
strokeLinecap="round"
|
|
134
|
-
strokeLinejoin="round"
|
|
135
|
-
/>
|
|
119
|
+
<div style={{ position: 'absolute', right: `${metrics.paddingX}px`, top: '50%', transform: 'translateY(-50%)', display: 'flex', flexDirection: 'column' }}>
|
|
120
|
+
<button type="button" onClick={increment} disabled={value >= max} style={{ backgroundColor: 'transparent', border: 'none', padding: '0', cursor: value >= max ? 'not-allowed' : 'pointer', opacity: value >= max ? 0.3 : 1, lineHeight: 0, display: 'block' }} aria-label="Increase quantity">
|
|
121
|
+
<svg width={metrics.icon} height={metrics.icon} viewBox="0 0 12 12" fill="none">
|
|
122
|
+
<path d="m3 7 3-3 3 3" stroke="currentColor" strokeWidth="1.25" strokeLinecap="round" strokeLinejoin="round" />
|
|
136
123
|
</svg>
|
|
137
124
|
</button>
|
|
138
|
-
<button
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
disabled={value <= min}
|
|
142
|
-
style={{
|
|
143
|
-
backgroundColor: 'transparent',
|
|
144
|
-
border: 'none',
|
|
145
|
-
padding: '0',
|
|
146
|
-
cursor: value <= min ? 'not-allowed' : 'pointer',
|
|
147
|
-
opacity: value <= min ? 0.3 : 1,
|
|
148
|
-
lineHeight: 0,
|
|
149
|
-
display: 'block'
|
|
150
|
-
}}
|
|
151
|
-
aria-label="Decrease quantity"
|
|
152
|
-
>
|
|
153
|
-
<svg
|
|
154
|
-
width={metrics.icon}
|
|
155
|
-
height={metrics.icon}
|
|
156
|
-
viewBox="0 0 12 12"
|
|
157
|
-
fill="none"
|
|
158
|
-
>
|
|
159
|
-
<path
|
|
160
|
-
d="m3 5 3 3 3-3"
|
|
161
|
-
stroke="currentColor"
|
|
162
|
-
strokeWidth="1.25"
|
|
163
|
-
strokeLinecap="round"
|
|
164
|
-
strokeLinejoin="round"
|
|
165
|
-
/>
|
|
125
|
+
<button type="button" onClick={decrement} disabled={value <= min} style={{ backgroundColor: 'transparent', border: 'none', padding: '0', cursor: value <= min ? 'not-allowed' : 'pointer', opacity: value <= min ? 0.3 : 1, lineHeight: 0, display: 'block' }} aria-label="Decrease quantity">
|
|
126
|
+
<svg width={metrics.icon} height={metrics.icon} viewBox="0 0 12 12" fill="none">
|
|
127
|
+
<path d="m3 5 3 3 3-3" stroke="currentColor" strokeWidth="1.25" strokeLinecap="round" strokeLinejoin="round" />
|
|
166
128
|
</svg>
|
|
167
129
|
</button>
|
|
168
130
|
</div>
|
package/src/index.js
CHANGED
|
@@ -41,7 +41,6 @@ export { usePopover, PopoverPanel, Tooltip } from './atoms/Popover.jsx'
|
|
|
41
41
|
export { default as PriceDisplay } from './atoms/PriceDisplay.jsx'
|
|
42
42
|
export { default as ProsePreview } from './atoms/ProsePreview.jsx'
|
|
43
43
|
export { default as QuantityInput } from './atoms/QuantityInput.jsx'
|
|
44
|
-
export { default as QuantityStepper } from './atoms/QuantityStepper.jsx'
|
|
45
44
|
export { default as RotaryDial } from './atoms/RotaryDial.jsx'
|
|
46
45
|
export { default as SearchInput } from './atoms/SearchInput.jsx'
|
|
47
46
|
export { default as Section } from './atoms/Section.jsx'
|
|
@@ -10,34 +10,11 @@ import { MenuItem as MenuTrigger } from './MenuItem.jsx'
|
|
|
10
10
|
* fixed positioning, MenuItem on floating-ui (portal, auto-flip, focus
|
|
11
11
|
* management). One implementation now: MenuItem. This alias keeps existing
|
|
12
12
|
* call-sites working; migrate imports to MenuItem. Removal in the next major.
|
|
13
|
+
*
|
|
14
|
+
* Compose the rows with the exported `MenuDropdownItem` / `MenuDropdownDivider`
|
|
15
|
+
* / `MenuDropdownNest` from MenuItem.jsx — this file no longer ships its own
|
|
16
|
+
* duplicate row/divider components (they were dead: never barrel-exported).
|
|
13
17
|
*/
|
|
14
18
|
export function MenuPopover(props) {
|
|
15
19
|
return <MenuTrigger {...props} />
|
|
16
20
|
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* MenuItem — action row inside a MenuPopover. Renders as a button so it
|
|
20
|
-
* picks up disabled state, focus, and keyboard activation. The popover
|
|
21
|
-
* closes automatically when an item is clicked (via the wrapper's
|
|
22
|
-
* delegate click — the data-menu-item attr marks rows for that match).
|
|
23
|
-
*/
|
|
24
|
-
export function MenuItem({ onClick, disabled, shortcut, iconLeft, children }) {
|
|
25
|
-
return (
|
|
26
|
-
<button
|
|
27
|
-
type="button"
|
|
28
|
-
data-menu-item
|
|
29
|
-
onClick={onClick}
|
|
30
|
-
disabled={disabled}
|
|
31
|
-
role="menuitem"
|
|
32
|
-
className="w-full kol-helper-12 px-3 h-8 inline-flex items-center gap-2 text-meta hover:text-emphasis hover:bg-fg-08 disabled:opacity-40 disabled:cursor-not-allowed text-left"
|
|
33
|
-
>
|
|
34
|
-
{iconLeft && <span className="shrink-0 w-4 inline-flex items-center justify-center text-meta">{iconLeft}</span>}
|
|
35
|
-
<span className="flex-1">{children}</span>
|
|
36
|
-
{shortcut && <span className="kol-helper-10 text-subtle shrink-0">{shortcut}</span>}
|
|
37
|
-
</button>
|
|
38
|
-
)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function MenuDivider() {
|
|
42
|
-
return <div className="border-t border-fg-08 my-1" />
|
|
43
|
-
}
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react'
|
|
2
|
-
|
|
3
|
-
// Match Dropdown SIZE_MAP for consistent height
|
|
4
|
-
const SIZE_MAP = {
|
|
5
|
-
sm: { fontSize: 11, paddingY: 12, paddingX: 24, radius: 20 },
|
|
6
|
-
md: { fontSize: 12, paddingY: 14, paddingX: 24, radius: 22 },
|
|
7
|
-
lg: { fontSize: 14, paddingY: 16, paddingX: 24, radius: 24 }
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const QuantityStepper = ({
|
|
11
|
-
value = 1,
|
|
12
|
-
onChange,
|
|
13
|
-
min = 1,
|
|
14
|
-
max = 10,
|
|
15
|
-
size,
|
|
16
|
-
className = ''
|
|
17
|
-
}) => {
|
|
18
|
-
const [resolvedSize, setResolvedSize] = useState('md')
|
|
19
|
-
const [componentWidth, setComponentWidth] = useState('180px')
|
|
20
|
-
|
|
21
|
-
// Responsive size
|
|
22
|
-
useEffect(() => {
|
|
23
|
-
const determineSize = () => {
|
|
24
|
-
if (size) {
|
|
25
|
-
setResolvedSize(size)
|
|
26
|
-
return
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (typeof window === 'undefined') {
|
|
30
|
-
setResolvedSize('md')
|
|
31
|
-
return
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (window.innerWidth >= 1024) {
|
|
35
|
-
setResolvedSize('lg')
|
|
36
|
-
} else if (window.innerWidth >= 768) {
|
|
37
|
-
setResolvedSize('md')
|
|
38
|
-
} else {
|
|
39
|
-
setResolvedSize('sm')
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
determineSize()
|
|
44
|
-
window.addEventListener('resize', determineSize)
|
|
45
|
-
return () => window.removeEventListener('resize', determineSize)
|
|
46
|
-
}, [size])
|
|
47
|
-
|
|
48
|
-
// Width management - match Dropdown
|
|
49
|
-
useEffect(() => {
|
|
50
|
-
const updateWidth = () => {
|
|
51
|
-
if (typeof window === 'undefined') return
|
|
52
|
-
|
|
53
|
-
if (window.innerWidth >= 1024) {
|
|
54
|
-
setComponentWidth('180px')
|
|
55
|
-
} else if (window.innerWidth >= 768) {
|
|
56
|
-
setComponentWidth('140px')
|
|
57
|
-
} else {
|
|
58
|
-
setComponentWidth('100px')
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
updateWidth()
|
|
62
|
-
window.addEventListener('resize', updateWidth)
|
|
63
|
-
return () => window.removeEventListener('resize', updateWidth)
|
|
64
|
-
}, [])
|
|
65
|
-
|
|
66
|
-
const metrics = SIZE_MAP[resolvedSize] || SIZE_MAP.md
|
|
67
|
-
|
|
68
|
-
const decrement = () => {
|
|
69
|
-
if (value > min) onChange?.(value - 1)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const increment = () => {
|
|
73
|
-
if (value < max) onChange?.(value + 1)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const buttonStyle = {
|
|
77
|
-
padding: `${metrics.paddingY}px 12px`,
|
|
78
|
-
display: 'flex',
|
|
79
|
-
alignItems: 'center',
|
|
80
|
-
justifyContent: 'center',
|
|
81
|
-
backgroundColor: 'transparent',
|
|
82
|
-
border: 'none',
|
|
83
|
-
color: 'var(--kol-surface-on-primary)',
|
|
84
|
-
cursor: 'pointer',
|
|
85
|
-
transition: 'opacity 0.15s',
|
|
86
|
-
fontFamily: 'var(--kol-font-family-mono)',
|
|
87
|
-
fontSize: `${metrics.fontSize}px`,
|
|
88
|
-
lineHeight: '120%'
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const disabledStyle = {
|
|
92
|
-
opacity: 0.3,
|
|
93
|
-
cursor: 'not-allowed'
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return (
|
|
97
|
-
<div
|
|
98
|
-
className={`flex items-center justify-center ${className}`}
|
|
99
|
-
style={{
|
|
100
|
-
width: componentWidth,
|
|
101
|
-
minWidth: componentWidth,
|
|
102
|
-
border: '1px solid var(--kol-border-default)',
|
|
103
|
-
borderRadius: `${metrics.radius}px`,
|
|
104
|
-
backgroundColor: 'var(--kol-surface-primary)'
|
|
105
|
-
}}
|
|
106
|
-
>
|
|
107
|
-
<button
|
|
108
|
-
type="button"
|
|
109
|
-
onClick={decrement}
|
|
110
|
-
disabled={value <= min}
|
|
111
|
-
style={{
|
|
112
|
-
...buttonStyle,
|
|
113
|
-
...(value <= min ? disabledStyle : {})
|
|
114
|
-
}}
|
|
115
|
-
aria-label="Decrease quantity"
|
|
116
|
-
>
|
|
117
|
-
−
|
|
118
|
-
</button>
|
|
119
|
-
|
|
120
|
-
<span
|
|
121
|
-
style={{
|
|
122
|
-
minWidth: '24px',
|
|
123
|
-
textAlign: 'center',
|
|
124
|
-
color: 'var(--kol-surface-on-primary)',
|
|
125
|
-
fontFamily: 'var(--kol-font-family-mono)',
|
|
126
|
-
fontSize: `${metrics.fontSize}px`,
|
|
127
|
-
lineHeight: '120%'
|
|
128
|
-
}}
|
|
129
|
-
>
|
|
130
|
-
{value}
|
|
131
|
-
</span>
|
|
132
|
-
|
|
133
|
-
<button
|
|
134
|
-
type="button"
|
|
135
|
-
onClick={increment}
|
|
136
|
-
disabled={value >= max}
|
|
137
|
-
style={{
|
|
138
|
-
...buttonStyle,
|
|
139
|
-
...(value >= max ? disabledStyle : {})
|
|
140
|
-
}}
|
|
141
|
-
aria-label="Increase quantity"
|
|
142
|
-
>
|
|
143
|
-
+
|
|
144
|
-
</button>
|
|
145
|
-
</div>
|
|
146
|
-
)
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export default QuantityStepper
|