@kolkrabbi/kol-component 0.140.0 → 0.142.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.140.0",
3
+ "version": "0.142.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/index.js CHANGED
@@ -152,6 +152,9 @@ export { parseFrontmatter } from './utilities/frontmatter.js'
152
152
  export { readCover } from './utilities/id3.js'
153
153
  export { kindOf, extOf, isSystemFile, KINDS, KIND_LABEL, DEFAULT_KINDS, isSegment, groupSegments, groupVariants, posterFor, partition } from './utilities/mediaKinds.js'
154
154
  export { nearestRatio, RATIOS } from './utilities/ratios.js'
155
+ /* the grab gesture — shared so kol-shell's NavRail and kol-framework's
156
+ * useDragResize wear ONE implementation (OneGrabGestureBothRails, 2026-08-30) */
157
+ export { default as useGrabEdge } from './utilities/useGrabEdge.js'
155
158
  export { default as markdownToHtml, inlineToHtml } from './utilities/markdownToHtml.js'
156
159
  export { default as RecordManager } from './organisms/RecordManager.jsx'
157
160
  export { default as SpectrumGrid } from './organisms/SpectrumGrid.jsx'
@@ -87,8 +87,16 @@ const BOX = {
87
87
  * variant NAME — so the two boxes agreed on every number and still disagreed
88
88
  * about the vertical spread. Deriving one box from another while a second map
89
89
  * is keyed by name is the drift the derive was meant to end. */
90
- bg: 'var(--kol-surface-secondary)', frame: 'transparent', frameHover: 'var(--kol-fg-08)',
91
- hover: 'color-mix(in srgb, var(--kol-surface-on-primary) 1%, transparent)' },
90
+ /* NO `hover` KEY (TypefaceRowHoverBg, kol-website 2026-08-30, the third
91
+ * pass on this row). `showcase` has `frameHover` and no `hover` at all, so a
92
+ * work row answers a pointer with a border step alone; this row also washed
93
+ * its background and read as a different interaction from every other listing.
94
+ *
95
+ * The three passes are the lesson: the ask was "make it look like /work" and
96
+ * each ticket named only the state someone had looked at — rest colours, then
97
+ * the derive's collateral, then this. When a user says make X look like Y,
98
+ * diff EVERY state: rest, hover, selected, focus. */
99
+ bg: 'var(--kol-surface-secondary)', frame: 'transparent', frameHover: 'var(--kol-fg-08)' },
92
100
  }
93
101
 
94
102
  /* `default` → `file` (user 2026-08-29). The variant was named after being the
@@ -0,0 +1,61 @@
1
+ import { useEffect } from 'react'
2
+ import { GRAB } from './motion.js'
3
+ import gsap from 'gsap'
4
+
5
+ /**
6
+ * useGrabEdge — the proximity-wake grab pill, for ANY resizable edge.
7
+ *
8
+ * Lifted out of kol-shell's `NavRail` 2026-08-30 (OneGrabGestureBothRails,
9
+ * kol-fxr — user: *"why dont we use the grab animation and other sidenav
10
+ * settings to be consistent?"*). fxr's `/labs` renders two rails on one screen,
11
+ * the shell's on the left and a params rail on the right, and only the left one
12
+ * came alive as the cursor neared it. Two implementations of one gesture in two
13
+ * packages, and neither was consumer code, so no consumer could converge them.
14
+ *
15
+ * It lives in kol-component because that is the only package BOTH rails can
16
+ * reach: kol-shell dropped its kol-framework peer in 0.16.0, so shell cannot
17
+ * import framework's hook and framework cannot import shell's. `gsap` and the
18
+ * `GRAB` tuning constants were already here.
19
+ *
20
+ * WHAT IT DOES. Watches the pointer; within `GRAB.near` of the handle's centre
21
+ * line it adds `is-near` (hysteresis out at `GRAB.sleep`, so the pill does not
22
+ * flicker on the boundary) and travels the pill along the edge to meet the
23
+ * cursor — bipolar from the middle across `GRAB.range` of the height, so it
24
+ * never rides up under a logomark. `GRAB.stick` is the dwell: a move smaller
25
+ * than that leaves the pill where it is, which is what stops it twitching.
26
+ *
27
+ * The element needs the `.kol-rail-grab` class for the drawing (kol-animation.css);
28
+ * this hook only supplies `is-near` and `--kol-rail-grab-y`.
29
+ *
30
+ * @param {React.RefObject<HTMLElement>} ref the grab handle
31
+ */
32
+ export default function useGrabEdge(ref) {
33
+ useEffect(() => {
34
+ let raf = 0
35
+ const onMove = ({ clientX, clientY }) => {
36
+ if (raf) return
37
+ raf = requestAnimationFrame(() => {
38
+ raf = 0
39
+ const h = ref.current
40
+ if (!h) return
41
+ const r = h.getBoundingClientRect()
42
+ const dist = Math.abs(clientX - (r.left + r.width / 2))
43
+ const near = dist <= GRAB.near || (h.classList.contains('is-near') && dist <= GRAB.sleep)
44
+ h.classList.toggle('is-near', near)
45
+ if (!near) return
46
+ /* the travel band: bipolar from the middle, GRAB.range of the height */
47
+ const edge = (r.height * (1 - GRAB.range)) / 2
48
+ const along = Math.min(Math.max(clientY - r.top, edge), r.height - edge)
49
+ if (h.dataset.grabSeeded && Math.abs(along - Number(h.dataset.grabTarget)) < GRAB.stick) return
50
+ h.dataset.grabTarget = String(along)
51
+ const vars = { '--kol-rail-grab-y': `${along}px` }
52
+ /* the CSS fallback is 50%, a percentage — nothing to tween from, so the
53
+ * first sighting sets and every move after tweens */
54
+ if (h.dataset.grabSeeded) gsap.to(h, { ...vars, ...GRAB.travel, overwrite: 'auto' })
55
+ else { gsap.set(h, vars); h.dataset.grabSeeded = '1' }
56
+ })
57
+ }
58
+ window.addEventListener('pointermove', onMove)
59
+ return () => { window.removeEventListener('pointermove', onMove); cancelAnimationFrame(raf) }
60
+ }, [ref])
61
+ }