@kolkrabbi/kol-component 0.124.0 → 0.126.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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-component",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.126.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",
|
|
@@ -61,6 +61,19 @@ const Dropdown = ({
|
|
|
61
61
|
* Both write variables on the panel that `.kol-dd-list` reads. */
|
|
62
62
|
maxRows = 10,
|
|
63
63
|
rowHeight,
|
|
64
|
+
/* `onOptionHover(value | null)` (DropdownOptionHoverPreview, kol-mirror
|
|
65
|
+
* 2026-08-28): the pointer entering a row reports its value, leaving reports
|
|
66
|
+
* `null` — so a picker over a VISUAL setting can preview the hovered option
|
|
67
|
+
* live and revert on leave. mirror's blend-mode picker applies the hovered
|
|
68
|
+
* mode to the composite: you scrub 16 modes against the actual image instead
|
|
69
|
+
* of committing to each one to look at it. The same case is every picker over
|
|
70
|
+
* easing curves, palettes, filters, fonts — the DS owns the panel, only the
|
|
71
|
+
* consumer knows what to preview, so this reports and nothing else.
|
|
72
|
+
*
|
|
73
|
+
* `null` fires ON CLOSE too, and that half is load-bearing: a panel dismissed
|
|
74
|
+
* while a row is hovered would otherwise leave the consumer previewing
|
|
75
|
+
* forever. It never fires while closed — a closed dropdown has no rows. */
|
|
76
|
+
onOptionHover,
|
|
64
77
|
defaultOpen = false,
|
|
65
78
|
className = ''
|
|
66
79
|
}) => {
|
|
@@ -96,6 +109,21 @@ const Dropdown = ({
|
|
|
96
109
|
setIsOpen(false)
|
|
97
110
|
}
|
|
98
111
|
|
|
112
|
+
/* the hover report, and the close that ends it. `hovered` is a ref, not
|
|
113
|
+
* state — the preview is the consumer's business and re-rendering the panel
|
|
114
|
+
* on every row crossed would be a render per pointermove for nothing. */
|
|
115
|
+
const hovered = useRef(null)
|
|
116
|
+
const reportHover = (v) => {
|
|
117
|
+
if (hovered.current === v) return
|
|
118
|
+
hovered.current = v
|
|
119
|
+
onOptionHover?.(v)
|
|
120
|
+
}
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
if (isOpen || hovered.current == null) return
|
|
123
|
+
hovered.current = null
|
|
124
|
+
onOptionHover?.(null)
|
|
125
|
+
}, [isOpen]) // eslint-disable-line react-hooks/exhaustive-deps
|
|
126
|
+
|
|
99
127
|
const currentOption = options.find((opt) => opt.value === value) || options[0]
|
|
100
128
|
|
|
101
129
|
/* A clamped list (Popover caps the panel to the viewport) can open with the
|
|
@@ -174,6 +202,8 @@ const Dropdown = ({
|
|
|
174
202
|
* brighten. The check mark is what marks the current value. */
|
|
175
203
|
hover={false}
|
|
176
204
|
height={rowHeight}
|
|
205
|
+
onPointerEnter={onOptionHover ? () => reportHover(option.value) : undefined}
|
|
206
|
+
onPointerLeave={onOptionHover ? () => reportHover(null) : undefined}
|
|
177
207
|
onClick={() => handleSelect(option)}
|
|
178
208
|
shortcut={isActive ? <Icon name="check" size={11} /> : undefined}
|
|
179
209
|
>
|
|
@@ -99,12 +99,14 @@ export function MenuItem({
|
|
|
99
99
|
* - children — main label, flex-1.
|
|
100
100
|
* - shortcut — trailing content (text shortcut hint, ✓ marker, or icon).
|
|
101
101
|
*/
|
|
102
|
-
export function MenuDropdownItem({ onClick, disabled, prefix, iconLeft, shortcut, hover = true, height, children }) {
|
|
102
|
+
export function MenuDropdownItem({ onClick, onPointerEnter, onPointerLeave, disabled, prefix, iconLeft, shortcut, hover = true, height, children }) {
|
|
103
103
|
return (
|
|
104
104
|
<button
|
|
105
105
|
type="button"
|
|
106
106
|
data-menu-item
|
|
107
107
|
onClick={onClick}
|
|
108
|
+
onPointerEnter={onPointerEnter}
|
|
109
|
+
onPointerLeave={onPointerLeave}
|
|
108
110
|
disabled={disabled}
|
|
109
111
|
role="menuitem"
|
|
110
112
|
style={height != null ? { height } : undefined}
|
package/src/utilities/motion.js
CHANGED
|
@@ -46,20 +46,32 @@ export const SPRING = {
|
|
|
46
46
|
* near / sleep the pill wakes within 20px of the line and sleeps only past
|
|
47
47
|
* 40 — hovering the line itself flapped the class every frame
|
|
48
48
|
* and restarted the fade, so the hysteresis is not optional
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
49
|
+
* stick DWELL, not a grid (user ruling 2026-08-28: "I don't like the
|
|
50
|
+
* snapping of the grabber, it's too far — let's make it come to
|
|
51
|
+
* the cursor but have some sticky time where it lands, so it's
|
|
52
|
+
* not constantly jerking"). The pill targets the pointer's own
|
|
53
|
+
* position on the line and re-targets only once the pointer is
|
|
54
|
+
* `stick` px away: it lands ON you, holds while you move inside
|
|
55
|
+
* the radius, then travels to where you are now.
|
|
56
|
+
*
|
|
57
|
+
* It was four marks at 20/40/60/80 % of the edge until this
|
|
58
|
+
* ruling — on a ~900px rail that is ~180px apart, so every
|
|
59
|
+
* re-target was a long throw to a point the pointer was not at,
|
|
60
|
+
* and the dwell read as the pill refusing to come to you and
|
|
61
|
+
* then lurching. The earlier "snap to something so it has
|
|
62
|
+
* somewhere to stick to" is served by dwell; the grid was the
|
|
63
|
+
* wrong model for it.
|
|
64
|
+
* travel the chase. 1.1s, down from 2.8 with the grid: the throw now
|
|
65
|
+
* covers a fraction of the distance, and a long tween over a
|
|
66
|
+
* short throw reads as lag rather than weight
|
|
54
67
|
* snap the width tween on release
|
|
55
68
|
* slop under this, a pointerdown/up is a CLICK, not a drag
|
|
56
69
|
*/
|
|
57
70
|
export const GRAB = {
|
|
58
71
|
near: 20,
|
|
59
72
|
sleep: 40,
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
travel: { duration: 2.8, ease: EASE.outGsap },
|
|
73
|
+
stick: 90,
|
|
74
|
+
travel: { duration: 1.1, ease: EASE.outGsap },
|
|
63
75
|
snap: { duration: 0.5, ease: EASE.outGsap },
|
|
64
76
|
slop: 3,
|
|
65
77
|
}
|