@kolkrabbi/kol-component 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/atoms/Button.jsx +9 -5
- package/src/atoms/Input.jsx +9 -6
- package/src/atoms/Textarea.jsx +36 -5
- package/src/atoms/ToggleSwitch.jsx +31 -4
- package/src/molecules/Dropdown.jsx +50 -96
- package/src/molecules/Slider.jsx +4 -14
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-component",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.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",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@floating-ui/react": "^0.27.19",
|
|
26
26
|
"embla-carousel-react": "^8.6.0",
|
|
27
|
-
"@kolkrabbi/kol-icons": "0.
|
|
27
|
+
"@kolkrabbi/kol-icons": "0.5.0"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
30
|
"react": "^18.3.0 || ^19.0.0",
|
package/src/atoms/Button.jsx
CHANGED
|
@@ -82,11 +82,15 @@ const Button = ({
|
|
|
82
82
|
|
|
83
83
|
// Add kol-btn-animate class if animateIcon is true to disable default hover states
|
|
84
84
|
const animateClass = animateIcon ? 'kol-btn-animate' : ''
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const
|
|
85
|
+
// `selected` is a legacy alias of `pressed` — both express the aria-pressed
|
|
86
|
+
// toggle-on state and render through .kol-btn-pressed. (The old
|
|
87
|
+
// .kol-btn-selected class never had a CSS rule, so `selected` was a no-op.)
|
|
88
|
+
// Resolve to one flag so the quiet-drop and the pressed fill agree.
|
|
89
|
+
const isPressed = pressed !== undefined ? pressed : selected
|
|
90
|
+
const quietClass = quiet && !isPressed ? 'kol-btn-quiet' : ''
|
|
91
|
+
const pressedClass = isPressed ? 'kol-btn-pressed' : ''
|
|
92
|
+
|
|
93
|
+
const combinedClass = `kol-btn ${variantClass} ${sizeClass} ${animateClass} ${quietClass} ${pressedClass} ${className}`.trim().replace(/\s+/g, ' ')
|
|
90
94
|
|
|
91
95
|
// Render icon with optional hover state
|
|
92
96
|
const renderIcon = (iconName, iconHoverName) => {
|
package/src/atoms/Input.jsx
CHANGED
|
@@ -3,11 +3,11 @@ import { Icon } from '@kolkrabbi/kol-icons'
|
|
|
3
3
|
/**
|
|
4
4
|
* Input — single-input atom built on the .kol-control shell.
|
|
5
5
|
*
|
|
6
|
-
* variant="filled" (default) — persistent solid bg
|
|
7
|
-
* variant="
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* variant="filled" (default) — persistent solid bg (= button primary chrome)
|
|
7
|
+
* variant="outline" — bordered, transparent bg — THE secondary
|
|
8
|
+
* treatment (2026-07-08 chrome law: one
|
|
9
|
+
* secondary, always subordinate to filled)
|
|
10
|
+
* variant="ghost" — legacy alias, resolves to outline
|
|
11
11
|
*
|
|
12
12
|
* size="sm" / "md" (default) / "lg" — matched padding + type class
|
|
13
13
|
*
|
|
@@ -57,9 +57,12 @@ export default function Input({
|
|
|
57
57
|
const fixedChars = typeof chars === 'number'
|
|
58
58
|
const resolvedIconSize = iconSize ?? ICON_SIZE[size] ?? 14
|
|
59
59
|
|
|
60
|
+
// ghost folds into outline (2026-07-08 chrome law): one secondary treatment.
|
|
61
|
+
const resolvedVariant = variant === 'ghost' ? 'outline' : variant
|
|
62
|
+
|
|
60
63
|
const shellCls = [
|
|
61
64
|
'kol-control',
|
|
62
|
-
`kol-control--${
|
|
65
|
+
`kol-control--${resolvedVariant}`,
|
|
63
66
|
`kol-control-${size}`,
|
|
64
67
|
SIZE_TYPE[size],
|
|
65
68
|
'cursor-text',
|
package/src/atoms/Textarea.jsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
|
|
1
2
|
import { Icon } from '@kolkrabbi/kol-icons'
|
|
2
3
|
|
|
3
4
|
/**
|
|
@@ -11,8 +12,10 @@ import { Icon } from '@kolkrabbi/kol-icons'
|
|
|
11
12
|
* size="sm" / "md" (default) / "lg" — padding + type class
|
|
12
13
|
*
|
|
13
14
|
* Default rows = 3 (kept short — long content gets scrollbars instead of
|
|
14
|
-
* dominating the panel).
|
|
15
|
-
*
|
|
15
|
+
* dominating the panel). Resize is real (2026-07-08): native resize is
|
|
16
|
+
* OFF (Firefox's built-in grip cannot be hidden any other way) and the
|
|
17
|
+
* kol-icon-set-v1 `resize-grip` icon IS the drag handle — corner drag,
|
|
18
|
+
* both axes, min 120×40. One grip, every browser.
|
|
16
19
|
*
|
|
17
20
|
* Controlled OR uncontrolled:
|
|
18
21
|
* - pass `value` + `onChange` for controlled,
|
|
@@ -34,9 +37,12 @@ export default function Textarea({
|
|
|
34
37
|
className = '',
|
|
35
38
|
...props
|
|
36
39
|
}) {
|
|
40
|
+
// ghost folds into outline (2026-07-08 chrome law): one secondary treatment.
|
|
41
|
+
const resolvedVariant = variant === 'ghost' ? 'outline' : variant
|
|
42
|
+
|
|
37
43
|
const wrapperCls = [
|
|
38
44
|
'kol-control',
|
|
39
|
-
`kol-control--${
|
|
45
|
+
`kol-control--${resolvedVariant}`,
|
|
40
46
|
'kol-control--textarea',
|
|
41
47
|
`kol-control-${size}`,
|
|
42
48
|
SIZE_TYPE[size],
|
|
@@ -62,8 +68,33 @@ export default function Textarea({
|
|
|
62
68
|
style={{ resize: 'none' }}
|
|
63
69
|
{...props}
|
|
64
70
|
/>
|
|
65
|
-
<span
|
|
66
|
-
|
|
71
|
+
<span
|
|
72
|
+
aria-hidden="true"
|
|
73
|
+
className="kol-textarea-resize-icon"
|
|
74
|
+
onPointerDown={(e) => {
|
|
75
|
+
// The grip IS the resize handle — corner drag, both axes
|
|
76
|
+
// (min 120×40). Width lands on the shell, height on the textarea.
|
|
77
|
+
const shell = e.currentTarget.parentElement
|
|
78
|
+
const ta = shell?.querySelector('textarea')
|
|
79
|
+
if (!shell || !ta) return
|
|
80
|
+
e.preventDefault()
|
|
81
|
+
const startX = e.clientX
|
|
82
|
+
const startY = e.clientY
|
|
83
|
+
const startW = shell.offsetWidth
|
|
84
|
+
const startH = ta.offsetHeight
|
|
85
|
+
const move = (ev) => {
|
|
86
|
+
shell.style.width = `${Math.max(startW + ev.clientX - startX, 120)}px`
|
|
87
|
+
ta.style.height = `${Math.max(startH + ev.clientY - startY, 40)}px`
|
|
88
|
+
}
|
|
89
|
+
const up = () => {
|
|
90
|
+
window.removeEventListener('pointermove', move)
|
|
91
|
+
window.removeEventListener('pointerup', up)
|
|
92
|
+
}
|
|
93
|
+
window.addEventListener('pointermove', move)
|
|
94
|
+
window.addEventListener('pointerup', up)
|
|
95
|
+
}}
|
|
96
|
+
>
|
|
97
|
+
<Icon name="resize-grip" size={12} />
|
|
67
98
|
</span>
|
|
68
99
|
</label>
|
|
69
100
|
)
|
|
@@ -1,11 +1,30 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* ToggleSwitch — bare by default (2026-07-08 chrome law rewrite).
|
|
5
|
+
*
|
|
6
|
+
* variant="bare" (default) — label + track, no box
|
|
7
|
+
* variant="primary" — filled shell (surface-secondary), button geometry
|
|
8
|
+
* variant="outline" — bordered shell (border-oq-16), button geometry
|
|
9
|
+
*
|
|
10
|
+
* size="sm" / "md" (default) / "lg" — shells match button heights
|
|
11
|
+
* (26/32/40); the track scales with size in all variants.
|
|
12
|
+
*
|
|
13
|
+
* Legacy aliases: 'plain' → bare · 'default' (the old boxed look) → outline.
|
|
14
|
+
* Type via kol-mono-{12,14,16}; no auto-uppercase — casing is authored at
|
|
15
|
+
* the call site (KOL no-auto-casing rule).
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const SIZE_TYPE = { sm: 'kol-mono-12', md: 'kol-mono-14', lg: 'kol-mono-16' }
|
|
19
|
+
const LEGACY_VARIANTS = { plain: 'bare', default: 'outline' }
|
|
20
|
+
|
|
3
21
|
const ToggleSwitch = ({
|
|
4
22
|
label,
|
|
5
23
|
checked = false,
|
|
6
24
|
onChange,
|
|
7
25
|
onToggle,
|
|
8
|
-
variant = '
|
|
26
|
+
variant = 'bare',
|
|
27
|
+
size = 'md',
|
|
9
28
|
className = '',
|
|
10
29
|
hint,
|
|
11
30
|
...props
|
|
@@ -15,12 +34,20 @@ const ToggleSwitch = ({
|
|
|
15
34
|
if (onChange) onChange(!checked)
|
|
16
35
|
}
|
|
17
36
|
|
|
18
|
-
const
|
|
37
|
+
const resolvedVariant = LEGACY_VARIANTS[variant] || variant
|
|
38
|
+
|
|
39
|
+
const cls = [
|
|
40
|
+
'toggle-switch',
|
|
41
|
+
`toggle-switch--${resolvedVariant}`,
|
|
42
|
+
`toggle-switch--${size}`,
|
|
43
|
+
SIZE_TYPE[size],
|
|
44
|
+
className,
|
|
45
|
+
].filter(Boolean).join(' ')
|
|
19
46
|
|
|
20
47
|
return (
|
|
21
48
|
<button
|
|
22
49
|
type="button"
|
|
23
|
-
className={
|
|
50
|
+
className={cls}
|
|
24
51
|
data-state={checked ? 'on' : 'off'}
|
|
25
52
|
onClick={handleClick}
|
|
26
53
|
aria-pressed={checked}
|
|
@@ -29,7 +56,7 @@ const ToggleSwitch = ({
|
|
|
29
56
|
<span className="toggle-switch-label">
|
|
30
57
|
{label}
|
|
31
58
|
{hint ? (
|
|
32
|
-
<span className="ml-2 opacity-60
|
|
59
|
+
<span className="ml-2 opacity-60 kol-helper-10">
|
|
33
60
|
{hint}
|
|
34
61
|
</span>
|
|
35
62
|
) : null}
|
|
@@ -3,20 +3,34 @@ import { Icon } from '@kolkrabbi/kol-icons'
|
|
|
3
3
|
import { MenuDropdownItem } from './MenuItem.jsx'
|
|
4
4
|
import { PopoverPanel, usePopover } from '../atoms/Popover.jsx'
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Dropdown — trigger IS button chrome (2026-07-08 chrome law).
|
|
8
|
+
*
|
|
9
|
+
* The trigger emits `kol-btn kol-btn-{variant} kol-btn-{size}` so it renders
|
|
10
|
+
* pixel-identical to a Button of the same variant/size — fills, hover,
|
|
11
|
+
* active, focus ring all come from the button rules in kol-theme.
|
|
12
|
+
* `.kol-dd-*` classes only add trigger layout + the open/panel fusion.
|
|
13
|
+
*
|
|
14
|
+
* variant="primary" (default) — filled trigger; open panel continues the
|
|
15
|
+
* same fill (one piece: no border, no gap, hairline divider inside)
|
|
16
|
+
* variant="outline" — bordered trigger; open panel carries the
|
|
17
|
+
* same border, trigger's bottom edge acts as the divider
|
|
18
|
+
*
|
|
19
|
+
* Legacy aliases (pre-law variants, kept so call-sites don't break):
|
|
20
|
+
* default → primary · subtle → primary · minimal → outline
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const SIZE_TYPE = { sm: 'kol-mono-12', md: 'kol-mono-14', lg: 'kol-mono-16' }
|
|
24
|
+
const ICON_SIZE = { sm: 14, md: 16, lg: 18 }
|
|
25
|
+
|
|
26
|
+
const LEGACY_VARIANTS = { default: 'primary', subtle: 'primary', minimal: 'outline' }
|
|
13
27
|
|
|
14
28
|
const Dropdown = ({
|
|
15
29
|
options = [],
|
|
16
30
|
value,
|
|
17
31
|
onChange,
|
|
18
32
|
size,
|
|
19
|
-
variant = '
|
|
33
|
+
variant = 'primary',
|
|
20
34
|
defaultOpen = false,
|
|
21
35
|
className = ''
|
|
22
36
|
}) => {
|
|
@@ -24,15 +38,17 @@ const Dropdown = ({
|
|
|
24
38
|
const [resolvedSize, setResolvedSize] = useState('md')
|
|
25
39
|
const [dropdownWidth, setDropdownWidth] = useState('100px')
|
|
26
40
|
|
|
41
|
+
const resolvedVariant = LEGACY_VARIANTS[variant] || variant
|
|
42
|
+
|
|
27
43
|
/* Floating-ui popover. `flip: false` keeps the panel below the button
|
|
28
|
-
* — the seamless
|
|
29
|
-
*
|
|
44
|
+
* — the seamless fused edge between trigger and panel assumes the panel
|
|
45
|
+
* sits below; flipping above would visually disconnect them.
|
|
30
46
|
* `matchReferenceWidth: true` pins panel min-width to the button. */
|
|
31
47
|
const popover = usePopover({
|
|
32
48
|
open: isOpen,
|
|
33
49
|
onOpenChange: setIsOpen,
|
|
34
50
|
placement: 'bottom-start',
|
|
35
|
-
offset:
|
|
51
|
+
offset: -1,
|
|
36
52
|
flip: false,
|
|
37
53
|
matchReferenceWidth: true,
|
|
38
54
|
role: 'listbox',
|
|
@@ -67,63 +83,23 @@ const Dropdown = ({
|
|
|
67
83
|
}
|
|
68
84
|
}, [size])
|
|
69
85
|
|
|
70
|
-
// Width management
|
|
86
|
+
// Width management — 100px mobile, 140px tablet, 180px desktop
|
|
71
87
|
useEffect(() => {
|
|
72
88
|
const updateWidth = () => {
|
|
73
89
|
if (typeof window === 'undefined') return
|
|
74
90
|
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
setDropdownWidth('180px')
|
|
82
|
-
} else if (window.innerWidth >= 768) {
|
|
83
|
-
setDropdownWidth('140px')
|
|
84
|
-
} else {
|
|
85
|
-
setDropdownWidth('100px')
|
|
86
|
-
}
|
|
91
|
+
if (window.innerWidth >= 1024) {
|
|
92
|
+
setDropdownWidth('180px')
|
|
93
|
+
} else if (window.innerWidth >= 768) {
|
|
94
|
+
setDropdownWidth('140px')
|
|
95
|
+
} else {
|
|
96
|
+
setDropdownWidth('100px')
|
|
87
97
|
}
|
|
88
98
|
}
|
|
89
99
|
updateWidth()
|
|
90
100
|
window.addEventListener('resize', updateWidth)
|
|
91
101
|
return () => window.removeEventListener('resize', updateWidth)
|
|
92
|
-
}, [
|
|
93
|
-
|
|
94
|
-
const metrics = SIZE_MAP[resolvedSize] || SIZE_MAP.md
|
|
95
|
-
|
|
96
|
-
// Variant-specific styles
|
|
97
|
-
const variantStyles = {
|
|
98
|
-
default: {
|
|
99
|
-
border: '1px solid var(--kol-border-default)',
|
|
100
|
-
borderRadius: isOpen
|
|
101
|
-
? `${metrics.radius}px ${metrics.radius}px 0 0`
|
|
102
|
-
: `${metrics.radius}px`,
|
|
103
|
-
backgroundColor: 'var(--kol-surface-primary)',
|
|
104
|
-
padding: `${metrics.paddingY}px ${metrics.paddingX}px`
|
|
105
|
-
},
|
|
106
|
-
minimal: {
|
|
107
|
-
border: 'none',
|
|
108
|
-
borderRadius: '0',
|
|
109
|
-
backgroundColor: 'transparent',
|
|
110
|
-
padding: '0',
|
|
111
|
-
height: '24px',
|
|
112
|
-
display: 'flex',
|
|
113
|
-
alignItems: 'center'
|
|
114
|
-
},
|
|
115
|
-
subtle: {
|
|
116
|
-
/* 1px transparent border so total height matches Button + Input + the
|
|
117
|
-
* other shared-control atoms (which have a transparent 1px border for
|
|
118
|
-
* hover/focus consistency). Without this, subtle is 2px shorter. */
|
|
119
|
-
border: '1px solid transparent',
|
|
120
|
-
borderRadius: isOpen ? '4px 4px 0 0' : '4px',
|
|
121
|
-
backgroundColor: 'var(--kol-surface-secondary)',
|
|
122
|
-
padding: `${metrics.paddingY}px ${metrics.paddingX}px`
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const styles = variantStyles[variant] || variantStyles.default
|
|
102
|
+
}, [])
|
|
127
103
|
|
|
128
104
|
const handleSelect = (option) => {
|
|
129
105
|
onChange?.(option.value)
|
|
@@ -132,11 +108,20 @@ const Dropdown = ({
|
|
|
132
108
|
|
|
133
109
|
const currentOption = options.find((opt) => opt.value === value) || options[0]
|
|
134
110
|
|
|
111
|
+
const triggerCls = [
|
|
112
|
+
'kol-btn',
|
|
113
|
+
`kol-btn-${resolvedVariant}`,
|
|
114
|
+
`kol-btn-${resolvedSize}`,
|
|
115
|
+
SIZE_TYPE[resolvedSize],
|
|
116
|
+
'kol-dd-trigger',
|
|
117
|
+
isOpen && 'kol-dd-trigger--open',
|
|
118
|
+
].filter(Boolean).join(' ')
|
|
119
|
+
|
|
135
120
|
return (
|
|
136
121
|
<div
|
|
137
122
|
className={`relative block ${className}`}
|
|
138
123
|
style={{
|
|
139
|
-
...(
|
|
124
|
+
...(dropdownWidth && !className.includes('w-full') && {
|
|
140
125
|
width: dropdownWidth,
|
|
141
126
|
minWidth: dropdownWidth
|
|
142
127
|
})
|
|
@@ -146,26 +131,15 @@ const Dropdown = ({
|
|
|
146
131
|
ref={popover.refs.setReference}
|
|
147
132
|
{...popover.getReferenceProps()}
|
|
148
133
|
type="button"
|
|
149
|
-
className={
|
|
150
|
-
style={{
|
|
151
|
-
border: styles.border,
|
|
152
|
-
borderRadius: styles.borderRadius,
|
|
153
|
-
backgroundColor: styles.backgroundColor,
|
|
154
|
-
color: 'var(--kol-surface-on-primary)',
|
|
155
|
-
padding: styles.padding,
|
|
156
|
-
transition: 'background-color 0.2s, color 0.2s, border-color 0.2s',
|
|
157
|
-
...(variant === 'minimal' && {
|
|
158
|
-
height: styles.height,
|
|
159
|
-
}),
|
|
160
|
-
}}
|
|
134
|
+
className={triggerCls}
|
|
161
135
|
aria-haspopup="listbox"
|
|
162
136
|
aria-expanded={isOpen}
|
|
163
137
|
data-state={isOpen ? 'open' : 'closed'}
|
|
164
138
|
>
|
|
165
|
-
<span
|
|
139
|
+
<span>{currentOption?.label}</span>
|
|
166
140
|
<Icon
|
|
167
141
|
name="chevron-down"
|
|
168
|
-
size={
|
|
142
|
+
size={ICON_SIZE[resolvedSize]}
|
|
169
143
|
className="ml-auto"
|
|
170
144
|
style={{
|
|
171
145
|
transform: isOpen ? 'rotate(180deg)' : 'rotate(0deg)',
|
|
@@ -178,29 +152,9 @@ const Dropdown = ({
|
|
|
178
152
|
popover={popover}
|
|
179
153
|
panel={false}
|
|
180
154
|
focus={false}
|
|
181
|
-
|
|
182
|
-
backgroundColor: variant === 'minimal'
|
|
183
|
-
? 'var(--kol-surface-primary)'
|
|
184
|
-
: styles.backgroundColor,
|
|
185
|
-
color: 'var(--kol-surface-on-primary)',
|
|
186
|
-
border: variant === 'subtle' ? 'none' : styles.border,
|
|
187
|
-
borderRadius: variant === 'minimal'
|
|
188
|
-
? '0'
|
|
189
|
-
: (variant === 'subtle'
|
|
190
|
-
? '0 0 4px 4px'
|
|
191
|
-
: `0 0 ${metrics.panelRadius}px ${metrics.panelRadius}px`),
|
|
192
|
-
}}
|
|
155
|
+
className={`kol-dd-panel kol-dd-panel--${resolvedVariant}`}
|
|
193
156
|
>
|
|
194
|
-
{
|
|
195
|
-
<div style={{ padding: `0 ${metrics.paddingX}px` }}>
|
|
196
|
-
<div
|
|
197
|
-
style={{
|
|
198
|
-
height: '1px',
|
|
199
|
-
backgroundColor: 'var(--kol-border-default)'
|
|
200
|
-
}}
|
|
201
|
-
/>
|
|
202
|
-
</div>
|
|
203
|
-
)}
|
|
157
|
+
{resolvedVariant === 'primary' && <div className="kol-dd-div" />}
|
|
204
158
|
|
|
205
159
|
<div className="flex max-h-[300px] flex-col items-stretch overflow-y-auto" role="listbox">
|
|
206
160
|
{options.map((option) => {
|
package/src/molecules/Slider.jsx
CHANGED
|
@@ -4,11 +4,9 @@ import Input from '../atoms/Input.jsx'
|
|
|
4
4
|
/**
|
|
5
5
|
* Slider — range slider with label and an editable value readout.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* inline controls). PRESERVED from web.
|
|
11
|
-
* subtle — filled rounded chip; for inspector-style controls.
|
|
7
|
+
* One bare inline row: label · track · editable readout. (Bordered
|
|
8
|
+
* `default` and chip `subtle` variants retired 2026-07-08 — minimal is
|
|
9
|
+
* the only slider.)
|
|
12
10
|
*
|
|
13
11
|
* The value readout is an editable <Input> (type a value, commit on
|
|
14
12
|
* blur / Enter, revert on Escape). Track color is exposed as the
|
|
@@ -21,7 +19,6 @@ import Input from '../atoms/Input.jsx'
|
|
|
21
19
|
* @param {number} props.max - Maximum value
|
|
22
20
|
* @param {number} props.value - Current value
|
|
23
21
|
* @param {Function} props.onChange - Change handler
|
|
24
|
-
* @param {'default'|'minimal'|'subtle'} props.variant - Visual variant (default: 'default')
|
|
25
22
|
* @param {string} props.className - Additional wrapper classes
|
|
26
23
|
* @param {number} props.displayWidth - Width of the value readout, in characters (default: 6)
|
|
27
24
|
* @param {string} props.fontSize - Font size for label/value (e.g., '11px')
|
|
@@ -34,7 +31,6 @@ const Slider = ({
|
|
|
34
31
|
max = 100,
|
|
35
32
|
value = 0,
|
|
36
33
|
onChange,
|
|
37
|
-
variant = 'default',
|
|
38
34
|
className = '',
|
|
39
35
|
displayWidth = 6,
|
|
40
36
|
fontSize,
|
|
@@ -47,12 +43,6 @@ const Slider = ({
|
|
|
47
43
|
}
|
|
48
44
|
}
|
|
49
45
|
|
|
50
|
-
const variantClass = variant === 'minimal'
|
|
51
|
-
? 'control-slider-minimal'
|
|
52
|
-
: variant === 'subtle'
|
|
53
|
-
? 'control-slider-subtle'
|
|
54
|
-
: 'control-slider'
|
|
55
|
-
|
|
56
46
|
const decimals = useMemo(() => {
|
|
57
47
|
if (formatValue) return null
|
|
58
48
|
if (!Number.isFinite(step)) return 0
|
|
@@ -94,7 +84,7 @@ const Slider = ({
|
|
|
94
84
|
}
|
|
95
85
|
|
|
96
86
|
return (
|
|
97
|
-
<div className={
|
|
87
|
+
<div className={`control-slider gap-3 shadow-none ${className}`}>
|
|
98
88
|
{label && (
|
|
99
89
|
<label className="kol-helper-12 whitespace-nowrap shrink-0 w-fit" style={fontSize ? { fontSize } : undefined}>
|
|
100
90
|
{label}
|