@kolkrabbi/kol-component 0.190.0 → 0.191.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/molecules/DocsToc.jsx +51 -14
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-component",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.191.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",
|
|
@@ -72,6 +72,7 @@ import useScrollSpy from '../hooks/useScrollSpy.js'
|
|
|
72
72
|
* @param {Element} root IntersectionObserver root passed to the spy
|
|
73
73
|
* @param {number} minors RAIL: graduations between one heading and the next — the dial's resolution, so literally how many clicks a heading is worth (default 10)
|
|
74
74
|
* @param {number} falloff RAIL: the gaussian's sigma in px, measured against the TICK pitch not the label pitch — at 10 graduations of 3px a section spans ~52px, so 14 crosses four or five clicks. Widen it and the whole column swells together, which is a column getting bigger rather than a lens moving over it (default 14)
|
|
75
|
+
* @param {'tick'|'label'|'off'} snap RAIL: what the lens locks to — `tick` (default) is the safecracker dial, locking to the nearest graduation and holding across its band; `label` locks to headings only, which is the ONLY sensible detent at `minors={0}`; `off` lets the lens follow the pointer continuously, which is the honest fisheye and reads better on a long rail with no graduations
|
|
75
76
|
* @param {'left'|'right'} position RAIL: which edge it pins to (default 'right')
|
|
76
77
|
* @param {number} minSections RAIL: render nothing under this many headings — an index of one is noise (default 2)
|
|
77
78
|
* @param {string} ariaLabel RAIL: the nav's accessible name (default 'On this page')
|
|
@@ -85,6 +86,7 @@ export default function DocsToc({
|
|
|
85
86
|
root = null,
|
|
86
87
|
minors = 10,
|
|
87
88
|
falloff = 14,
|
|
89
|
+
snap = 'tick',
|
|
88
90
|
position = 'right',
|
|
89
91
|
minSections = 2,
|
|
90
92
|
ariaLabel = 'On this page',
|
|
@@ -100,6 +102,7 @@ export default function DocsToc({
|
|
|
100
102
|
onNavigate={onNavigate}
|
|
101
103
|
minors={minors}
|
|
102
104
|
falloff={falloff}
|
|
105
|
+
snap={snap}
|
|
103
106
|
position={position}
|
|
104
107
|
minSections={minSections}
|
|
105
108
|
ariaLabel={ariaLabel}
|
|
@@ -145,7 +148,7 @@ export default function DocsToc({
|
|
|
145
148
|
* do not belong in the other's render path.
|
|
146
149
|
*
|
|
147
150
|
* The magnitudes go to the DOM as `--m`, never to state — see the header. */
|
|
148
|
-
function TocRail({ toc, activeId, onNavigate, minors, falloff, position, minSections, ariaLabel, className }) {
|
|
151
|
+
function TocRail({ toc, activeId, onNavigate, minors, falloff, snap, position, minSections, ariaLabel, className }) {
|
|
149
152
|
const navRef = useRef(null)
|
|
150
153
|
const centresRef = useRef([])
|
|
151
154
|
const frameRef = useRef(0)
|
|
@@ -166,19 +169,38 @@ function TocRail({ toc, activeId, onNavigate, minors, falloff, position, minSect
|
|
|
166
169
|
const nav = navRef.current
|
|
167
170
|
if (!nav) return
|
|
168
171
|
|
|
169
|
-
/* DETENT
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
+
/* THE DETENT. `tick` is the safecracker's dial — the lens locks to the
|
|
173
|
+
* nearest mark and holds across its whole band, so dragging down the rail
|
|
174
|
+
* is a run of discrete clicks rather than a smear. The three modes differ
|
|
175
|
+
* only in WHICH array is snapped against, which is why this is a prop and
|
|
176
|
+
* not three components (`docstoc-rail-tracking-and-snap-prop`, 2026-09-04):
|
|
177
|
+
*
|
|
178
|
+
* tick every graduation — the default, right for an index you aim with
|
|
179
|
+
* label the headings only — coarse at 10 minors, and the ONLY sensible
|
|
180
|
+
* detent at `minors={0}`, which the component already allows: no
|
|
181
|
+
* ticks plus a tick-snap is `label` by accident rather than choice
|
|
182
|
+
* off no snap at all — the honest fisheye, and better on a long rail
|
|
183
|
+
* with many headings and no graduations
|
|
184
|
+
*
|
|
185
|
+
* Majors are every (minors + 1)th child, because the render interleaves
|
|
186
|
+
* one heading with `minors` ticks and stops after the last heading. */
|
|
172
187
|
const marks = centresRef.current
|
|
173
|
-
const
|
|
188
|
+
const targets = snap === 'off'
|
|
174
189
|
? null
|
|
175
|
-
:
|
|
190
|
+
: snap === 'label'
|
|
191
|
+
? marks.filter((_, i) => i % (minors + 1) === 0)
|
|
192
|
+
: marks
|
|
193
|
+
const focus = y == null
|
|
194
|
+
? null
|
|
195
|
+
: !targets?.length
|
|
196
|
+
? y
|
|
197
|
+
: targets.reduce((best, c) => (Math.abs(c - y) < Math.abs(best - y) ? c : best), targets[0])
|
|
176
198
|
|
|
177
199
|
marks.forEach((c, i) => {
|
|
178
200
|
const m = focus == null ? 0 : Math.exp(-(((focus - c) / falloff) ** 2))
|
|
179
201
|
nav.children[i]?.style.setProperty('--m', m.toFixed(3))
|
|
180
202
|
})
|
|
181
|
-
}, [falloff])
|
|
203
|
+
}, [falloff, snap, minors])
|
|
182
204
|
|
|
183
205
|
const onMove = useCallback((e) => {
|
|
184
206
|
/* Reduced motion: the curve is the animation, so there is nothing to damp —
|
|
@@ -228,13 +250,28 @@ function TocRail({ toc, activeId, onNavigate, minors, falloff, position, minSect
|
|
|
228
250
|
data-active={active ? '' : undefined}
|
|
229
251
|
className="kol-toc-row"
|
|
230
252
|
>
|
|
231
|
-
{/*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
|
|
253
|
+
{/* `kol-helper-12`, and it is the FAULT LINE that says so, not
|
|
254
|
+
* taste (01-foundations/03-typography: *"can this string ever
|
|
255
|
+
* wrap? No (structurally single-line) → helper"*). This label is
|
|
256
|
+
* `white-space: nowrap` — a graduation on a ruler, which is the
|
|
257
|
+
* helper family's own listed use.
|
|
258
|
+
*
|
|
259
|
+
* The line-height is the functional half. `kol-mono-14` carries
|
|
260
|
+
* 18px leading on a 14px label: 4px this string never uses, and
|
|
261
|
+
* THIS component measures its rows — `measure()` reads
|
|
262
|
+
* `getBoundingClientRect()` on every child to place the detents,
|
|
263
|
+
* so leading inflates each row box and moves the label's centre
|
|
264
|
+
* off its own tick. A ramp built for wrapping text is the wrong
|
|
265
|
+
* tool for a mark on a ruler.
|
|
266
|
+
*
|
|
267
|
+
* It shipped as `kol-mono-14` in 0.188.0 because validate:rails
|
|
268
|
+
* R1 tested the class PREFIX rather than the rung's purpose and I
|
|
269
|
+
* took the gate's word over the type law. The filer had specified
|
|
270
|
+
* helper-12 deliberately (user: *"its a bit big and tight and
|
|
271
|
+
* bold"*, and then *"I just dont get the point of me desiging for
|
|
272
|
+
* the design be ignored?!"*). R1 now carries the exemption with
|
|
273
|
+
* this reason — see `docstoc-rail-tracking-and-snap-prop`. */}
|
|
274
|
+
<span className="kol-toc-label kol-helper-12 text-meta">{label}</span>
|
|
238
275
|
<span aria-hidden="true" className="kol-toc-rule" />
|
|
239
276
|
</a>
|
|
240
277
|
)
|