@kolkrabbi/kol-framework 0.42.0 → 0.44.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/Layout.jsx +2 -2
- package/src/SideNav.jsx +1 -1
- package/src/index.js +4 -1
- package/src/useDragResize.js +0 -311
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-framework",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.44.0",
|
|
4
4
|
"description": "KOL app shell — sidenav, layout, theme toggle, footer, heroes + brand color layer. SideNav takes nav data as props so the tree stays app-local.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"react-router-dom": "^6.0.0 || ^7.0.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@kolkrabbi/kol-component": "^0.
|
|
24
|
+
"@kolkrabbi/kol-component": "^0.186.0",
|
|
25
25
|
"@kolkrabbi/kol-icons": "^0.25.0"
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
package/src/Layout.jsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Outlet, useLocation } from 'react-router-dom'
|
|
1
|
+
import { Link, Outlet, useLocation } from 'react-router-dom'
|
|
2
2
|
import ScrollToTop from './ScrollToTop'
|
|
3
3
|
import { ExitPreview } from '@kolkrabbi/kol-component'
|
|
4
4
|
|
|
@@ -35,7 +35,7 @@ export default function Layout() {
|
|
|
35
35
|
<main id="main" className="flex-1 min-w-0">
|
|
36
36
|
<Outlet />
|
|
37
37
|
</main>
|
|
38
|
-
{isClientSiteRoute && <ExitPreview />}
|
|
38
|
+
{isClientSiteRoute && <ExitPreview linkComponent={Link} />}
|
|
39
39
|
</div>
|
|
40
40
|
)
|
|
41
41
|
}
|
package/src/SideNav.jsx
CHANGED
|
@@ -4,7 +4,7 @@ import { createPortal } from 'react-dom'
|
|
|
4
4
|
import { Icon } from '@kolkrabbi/kol-icons'
|
|
5
5
|
import { Tooltip } from '@kolkrabbi/kol-component'
|
|
6
6
|
import ThemeToggle from './ThemeToggle'
|
|
7
|
-
import useDragResize from '
|
|
7
|
+
import { useDragResize } from '@kolkrabbi/kol-component'
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* SideNav — the grouped navigation rail.
|
package/src/index.js
CHANGED
|
@@ -10,7 +10,10 @@
|
|
|
10
10
|
export { default as PageLayout, default as AppShell } from './PageLayout.jsx'
|
|
11
11
|
export { ShellTocContext, ShellTocCollapsedContext } from './PageLayout.jsx'
|
|
12
12
|
export { default as SideNav } from './SideNav.jsx'
|
|
13
|
-
|
|
13
|
+
/* MOVED to kol-component 2026-09-03 (editor-set-is-behind-its-source): its
|
|
14
|
+
* EditorShell needs the same rails and cannot import this package. Re-exported
|
|
15
|
+
* here under the name it has always had — this specifier stays valid. */
|
|
16
|
+
export { useDragResize } from '@kolkrabbi/kol-component'
|
|
14
17
|
export { default as ShellHeader, HEADER_ICON } from './ShellHeader.jsx'
|
|
15
18
|
export { default as ThemeToggle } from './ThemeToggle.jsx'
|
|
16
19
|
export { useTheme, applyTheme, getInitialTheme, THEME_STORAGE_KEY, THEME_BOOT_SCRIPT } from './theme.js'
|
package/src/useDragResize.js
DELETED
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
import { useEffect, useMemo, useRef, useState } from 'react'
|
|
2
|
-
/* THE SAME GRAB AS THE SHELL RAIL (OneGrabGestureBothRails, kol-fxr 2026-08-30 —
|
|
3
|
-
* user: *"why dont we use the grab animation and other sidenav settings to be
|
|
4
|
-
* consistent?"*). fxr's /labs shows both rails at once and only the left one
|
|
5
|
-
* woke as the cursor neared it, because each package had built the gesture
|
|
6
|
-
* itself. The hook lives in kol-component, the one package both can reach —
|
|
7
|
-
* kol-shell dropped its framework peer in 0.16.0, so neither could import the
|
|
8
|
-
* other's. */
|
|
9
|
-
import { useGrabEdge } from '@kolkrabbi/kol-component'
|
|
10
|
-
|
|
11
|
-
/* Grab-edge resize + collapse for SideNav — THE single control since 0.17.0
|
|
12
|
-
* (user build order 2026-08-09, completing the SideNavGrabResize brief: the
|
|
13
|
-
* chip Button is gone in both states; the pill-marked edge does everything).
|
|
14
|
-
* Logic lifted from the hand-tested brand proto (kol-website _tmp
|
|
15
|
-
* useDragResizeProto.js), which forked this hook's 0.16.0 form.
|
|
16
|
-
*
|
|
17
|
-
* - CLICK TOGGLES expand↔collapse: pointerup with < 3px of travel is a
|
|
18
|
-
* click, never a resize — drags only start past the slop, so toggle
|
|
19
|
-
* presses can't jitter the rail.
|
|
20
|
-
* - DRAG resizes; live width goes to --kol-sidenav-w on :root so the grid,
|
|
21
|
-
* shell-header brand block and aside follow one number. Dragging under
|
|
22
|
-
* --kol-sidenav-snap stamps :root[data-sidenav="collapsed"].
|
|
23
|
-
* - SNAP-TO-DEFAULT: releasing within --kol-sidenav-snap-default of the
|
|
24
|
-
* stylesheet default clears the override — you land exactly on default,
|
|
25
|
-
* never 249px or 263px.
|
|
26
|
-
* - DOUBLE-CLICK RESET IS GONE — it cannot coexist with click-toggle (two
|
|
27
|
-
* clicks would toggle-toggle-reset). Home keeps the reset; the snap band
|
|
28
|
-
* covers pointer users.
|
|
29
|
-
* - Keyboard on the focused separator: arrows resize by --kol-sidenav-step
|
|
30
|
-
* (ArrowLeft past the snap collapses), Home resets, Enter/Space toggle.
|
|
31
|
-
* - Width + state survive reload ('kol-sidenav' keeps the consumers'
|
|
32
|
-
* existing 'collapsed'|'expanded' schema; width under its own key).
|
|
33
|
-
*
|
|
34
|
-
* Every value the gesture needs is a --<token>-* custom property in the
|
|
35
|
-
* consumer's CSS — no literals here except the click slop, which is a
|
|
36
|
-
* gesture constant, not chrome. Missing tokens leave the gesture inert.
|
|
37
|
-
*
|
|
38
|
-
* SIDE-AGNOSTIC since ThreeColumnEditorShell (kol-fxr, 2026-08-15): the name
|
|
39
|
-
* family and the drag direction are both arguments now, so a right-hand
|
|
40
|
-
* inspector rail reuses this gesture — pointer, keyboard, snap, collapse and
|
|
41
|
-
* persistence — instead of reimplementing it. The bullets above describe the
|
|
42
|
-
* DEFAULT ('kol-sidenav', side 'left'), which is byte-identical to 0.17.0. */
|
|
43
|
-
|
|
44
|
-
const CLICK_SLOP_PX = 3
|
|
45
|
-
|
|
46
|
-
const root = () => document.documentElement
|
|
47
|
-
|
|
48
|
-
/* Every name the gesture touches, derived from ONE token (ThreeColumnEditorShell,
|
|
49
|
-
* filed from kol-fxr 2026-08-15). The default token reproduces the hardcoded
|
|
50
|
-
* 0.17.0 names EXACTLY — 'kol-sidenav' → data-sidenav, --kol-sidenav-w,
|
|
51
|
-
* storage 'kol-sidenav'/'kol-sidenav-w' — so SideNav and every existing caller
|
|
52
|
-
* are untouched by this generalisation.
|
|
53
|
-
*
|
|
54
|
-
* The data-attribute drops the `kol-` prefix because that is what the shipped
|
|
55
|
-
* CSS already selects (`:root[data-sidenav="collapsed"]`), not a new scheme. */
|
|
56
|
-
export function buildNames(token) {
|
|
57
|
-
const base = token.replace(/^kol-/, '')
|
|
58
|
-
return {
|
|
59
|
-
stateKey: token,
|
|
60
|
-
widthKey: `${token}-w`,
|
|
61
|
-
collapsedAttr: `data-${base}`,
|
|
62
|
-
draggingAttr: `data-${base}-dragging`,
|
|
63
|
-
wVar: `--${token}-w`,
|
|
64
|
-
collapsedVar: `--${token}-w-collapsed`,
|
|
65
|
-
snapVar: `--${token}-snap`,
|
|
66
|
-
stepVar: `--${token}-step`,
|
|
67
|
-
snapDefaultVar: `--${token}-snap-default`,
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/* Resolve a length token to px, or null when it is absent/unparsable. */
|
|
72
|
-
function readVarPx(name) {
|
|
73
|
-
const raw = getComputedStyle(root()).getPropertyValue(name).trim()
|
|
74
|
-
const n = parseFloat(raw)
|
|
75
|
-
if (!raw || Number.isNaN(n)) return null
|
|
76
|
-
return raw.endsWith('rem') ? n * parseFloat(getComputedStyle(root()).fontSize) : n
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/* Imperative DOM writes — pointermove must never re-render the nav tree.
|
|
80
|
-
* React state syncs from the DOM at rest (release / key press / reset). */
|
|
81
|
-
const stampCollapsed = (n, on) => {
|
|
82
|
-
if (on) root().setAttribute(n.collapsedAttr, 'collapsed')
|
|
83
|
-
else root().removeAttribute(n.collapsedAttr)
|
|
84
|
-
}
|
|
85
|
-
const writeWidth = (n, px) => {
|
|
86
|
-
if (px == null) root().style.removeProperty(n.wVar)
|
|
87
|
-
else root().style.setProperty(n.wVar, `${px}px`)
|
|
88
|
-
}
|
|
89
|
-
const readBack = (n) => {
|
|
90
|
-
const inline = parseFloat(root().style.getPropertyValue(n.wVar))
|
|
91
|
-
return {
|
|
92
|
-
collapsed: root().getAttribute(n.collapsedAttr) === 'collapsed',
|
|
93
|
-
widthPx: Number.isNaN(inline) ? null : inline,
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/* @param ref the panel being resized — its measured width seeds the drag
|
|
98
|
-
* @param options { token, side }
|
|
99
|
-
* token — the CSS/storage name family. Default 'kol-sidenav' (SideNav).
|
|
100
|
-
* A right-hand inspector passes its own, e.g. 'kol-rail', so the two
|
|
101
|
-
* rails never share one :root variable and drag together.
|
|
102
|
-
* side — which EDGE the grab handle sits on. 'left' (default) is a rail on
|
|
103
|
-
* the left of the viewport whose handle is on its right edge, so
|
|
104
|
-
* rightward drag = wider. 'right' inverts both the pointer sign and
|
|
105
|
-
* the arrow keys.
|
|
106
|
-
* persistWidth — remember a dragged WIDTH across sessions. **Default false**
|
|
107
|
-
* (sidenav-drag-width-outranks-breakpoints, kol-client-olina
|
|
108
|
-
* 2026-09-03; user: *"why would you want that in persistent memory?
|
|
109
|
-
* and even if you did, shouldnt it be an opt in via prop? off by
|
|
110
|
-
* default"*).
|
|
111
|
-
*
|
|
112
|
-
* It used to be unconditional, and the width was replayed on boot as
|
|
113
|
-
* an INLINE custom property on `:root` — which outranks every
|
|
114
|
-
* stylesheet rule, so one drag on a laptop killed both shipped rungs
|
|
115
|
-
* (`--kol-sidenav-w: 264px` and the 320px `min-width:1536px` rule)
|
|
116
|
-
* permanently, on every machine, with no UI saying so and no selector
|
|
117
|
-
* a consumer could beat. Measured at 1600×900: a stored 210 rendered
|
|
118
|
-
* 210 and the 1536 rung did nothing. A one-off gesture had become a
|
|
119
|
-
* permanent global.
|
|
120
|
-
*
|
|
121
|
-
* Off, a drag still works and lasts the session; the rail follows the
|
|
122
|
-
* stylesheet and its breakpoints on the next boot. On, the old
|
|
123
|
-
* behaviour returns for an app that genuinely wants the rail
|
|
124
|
-
* remembered — and it is still an inline stamp, so an app opting in is
|
|
125
|
-
* choosing to outrank its own breakpoints. That precedence is worth
|
|
126
|
-
* inverting, but it is a bigger change than this ticket asked for.
|
|
127
|
-
*
|
|
128
|
-
* STATE is untouched: collapsed/expanded still persists via
|
|
129
|
-
* `stateKey`, unconditionally, as it always did. The ticket says the
|
|
130
|
-
* two are separate questions and only asks about the width. */
|
|
131
|
-
export default function useDragResize(ref, options = {}) {
|
|
132
|
-
const { token = 'kol-sidenav', side = 'left', defaultCollapsed = false, persistWidth = false } = options
|
|
133
|
-
/* -1 on a right-hand rail: the same rightward pointer travel that widens a
|
|
134
|
-
* left rail must NARROW a right one, because its handle faces the canvas. */
|
|
135
|
-
const dir = side === 'right' ? -1 : 1
|
|
136
|
-
const names = useMemo(() => buildNames(token), [token])
|
|
137
|
-
|
|
138
|
-
/* the handle's own ref, so the shared gesture can find it. Returned in
|
|
139
|
-
* `grabProps`, so a consumer already spreading those gets the wake, the
|
|
140
|
-
* travel and the dwell with no change. */
|
|
141
|
-
const grabRef = useRef(null)
|
|
142
|
-
useGrabEdge(grabRef)
|
|
143
|
-
const drag = useRef(null) // { startX, startW, snapPx, maxPx, moved } during a drag
|
|
144
|
-
const defaultPx = useRef(null)
|
|
145
|
-
const collapsedPx = useRef(null)
|
|
146
|
-
const [collapsed, setCollapsed] = useState(false)
|
|
147
|
-
const [widthPx, setWidthPx] = useState(null) // null = stylesheet default
|
|
148
|
-
|
|
149
|
-
const syncAndPersist = () => {
|
|
150
|
-
const { collapsed: c, widthPx: w } = readBack(names)
|
|
151
|
-
setCollapsed(c)
|
|
152
|
-
setWidthPx(w)
|
|
153
|
-
try {
|
|
154
|
-
localStorage.setItem(names.stateKey, c ? 'collapsed' : 'expanded')
|
|
155
|
-
/* width only when asked for; off, the key is not written AND any key a
|
|
156
|
-
* previous version left behind is cleared, so a consumer that bumps stops
|
|
157
|
-
* replaying a width it never opted into */
|
|
158
|
-
if (!persistWidth || w == null) localStorage.removeItem(names.widthKey)
|
|
159
|
-
else localStorage.setItem(names.widthKey, String(Math.round(w)))
|
|
160
|
-
} catch { /* storage blocked */ }
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const toggleCollapsed = () => {
|
|
164
|
-
const { collapsed: c } = readBack(names)
|
|
165
|
-
stampCollapsed(names, !c)
|
|
166
|
-
syncAndPersist()
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/* Boot: capture the stylesheet defaults BEFORE any inline override lands,
|
|
170
|
-
* then restore the persisted width/state. */
|
|
171
|
-
useEffect(() => {
|
|
172
|
-
defaultPx.current = readVarPx(names.wVar)
|
|
173
|
-
collapsedPx.current = readVarPx(names.collapsedVar)
|
|
174
|
-
let w = null
|
|
175
|
-
let stored = null
|
|
176
|
-
try {
|
|
177
|
-
/* off → never read it, and drop a key an earlier version wrote */
|
|
178
|
-
if (persistWidth) w = parseFloat(localStorage.getItem(names.widthKey)) || null
|
|
179
|
-
else localStorage.removeItem(names.widthKey)
|
|
180
|
-
stored = localStorage.getItem(names.stateKey)
|
|
181
|
-
} catch { /* storage blocked */ }
|
|
182
|
-
/* nothing stored → the consumer's boot state (RailSideNavPixelParity,
|
|
183
|
-
* 2026-08-28): an app rail boots collapsed, the brand sidebar boots open;
|
|
184
|
-
* the first drag or click persists and the default never speaks again */
|
|
185
|
-
const c = stored ? stored === 'collapsed' : !!defaultCollapsed
|
|
186
|
-
if (w) { writeWidth(names, w); setWidthPx(w) }
|
|
187
|
-
if (c) { stampCollapsed(names, true); setCollapsed(true) }
|
|
188
|
-
}, [names, defaultCollapsed, persistWidth])
|
|
189
|
-
|
|
190
|
-
useEffect(() => {
|
|
191
|
-
const onMove = (e) => {
|
|
192
|
-
if (!drag.current) return
|
|
193
|
-
const d = drag.current
|
|
194
|
-
const dx = e.clientX - d.startX
|
|
195
|
-
/* Below the slop the pointer is still a CLICK — resizing from the
|
|
196
|
-
* first pixel would jitter the rail on every toggle press. */
|
|
197
|
-
if (!d.moved) {
|
|
198
|
-
if (Math.abs(dx) < CLICK_SLOP_PX) return
|
|
199
|
-
d.moved = true
|
|
200
|
-
}
|
|
201
|
-
const next = d.startW + dx * dir
|
|
202
|
-
if (next < d.snapPx) {
|
|
203
|
-
stampCollapsed(names, true)
|
|
204
|
-
} else {
|
|
205
|
-
stampCollapsed(names, false)
|
|
206
|
-
writeWidth(names, Math.min(next, d.maxPx))
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
const onUp = () => {
|
|
210
|
-
if (!drag.current) return
|
|
211
|
-
const { moved } = drag.current
|
|
212
|
-
drag.current = null
|
|
213
|
-
root().removeAttribute(names.draggingAttr)
|
|
214
|
-
document.body.style.cursor = ''
|
|
215
|
-
document.body.style.userSelect = ''
|
|
216
|
-
if (!moved) { toggleCollapsed(); return } // a click, not a drag
|
|
217
|
-
/* Snap-to-default: release near the stylesheet default clears the
|
|
218
|
-
* override entirely. */
|
|
219
|
-
const { collapsed: c, widthPx: w } = readBack(names)
|
|
220
|
-
const band = readVarPx(names.snapDefaultVar) ?? readVarPx(names.stepVar) ?? 16
|
|
221
|
-
if (!c && w != null && defaultPx.current != null && Math.abs(w - defaultPx.current) <= band) {
|
|
222
|
-
writeWidth(names, null)
|
|
223
|
-
}
|
|
224
|
-
syncAndPersist()
|
|
225
|
-
}
|
|
226
|
-
window.addEventListener('pointermove', onMove)
|
|
227
|
-
window.addEventListener('pointerup', onUp)
|
|
228
|
-
return () => {
|
|
229
|
-
window.removeEventListener('pointermove', onMove)
|
|
230
|
-
window.removeEventListener('pointerup', onUp)
|
|
231
|
-
}
|
|
232
|
-
}, [names, dir])
|
|
233
|
-
|
|
234
|
-
const onPointerDown = (e) => {
|
|
235
|
-
const snapPx = readVarPx(names.snapVar)
|
|
236
|
-
if (defaultPx.current == null || snapPx == null) return // tokens absent → inert
|
|
237
|
-
e.preventDefault()
|
|
238
|
-
drag.current = {
|
|
239
|
-
startX: e.clientX,
|
|
240
|
-
startW: ref.current?.getBoundingClientRect().width ?? defaultPx.current,
|
|
241
|
-
snapPx,
|
|
242
|
-
/* mirror's ceiling (default × 3), resolved from the token not hardcoded */
|
|
243
|
-
maxPx: defaultPx.current * 3,
|
|
244
|
-
moved: false,
|
|
245
|
-
}
|
|
246
|
-
/* the grid's grid-template-columns ease would trail the pointer —
|
|
247
|
-
* kol-framework.css suspends it while this attribute is stamped */
|
|
248
|
-
root().setAttribute(names.draggingAttr, '')
|
|
249
|
-
document.body.style.cursor = 'col-resize'
|
|
250
|
-
document.body.style.userSelect = 'none'
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
const resetToDefault = () => {
|
|
254
|
-
stampCollapsed(names, false)
|
|
255
|
-
writeWidth(names, null)
|
|
256
|
-
syncAndPersist()
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
const onKeyDown = (e) => {
|
|
260
|
-
const snapPx = readVarPx(names.snapVar)
|
|
261
|
-
const stepPx = readVarPx(names.stepVar)
|
|
262
|
-
if (defaultPx.current == null || snapPx == null || stepPx == null) return
|
|
263
|
-
const { collapsed: c, widthPx: w } = readBack(names)
|
|
264
|
-
const current = w ?? defaultPx.current
|
|
265
|
-
/* The arrow that GROWS is the one pointing away from the rail's own edge —
|
|
266
|
-
* ArrowRight on a left rail, ArrowLeft on a right one. Same inversion the
|
|
267
|
-
* pointer gets, so keyboard and drag never disagree. */
|
|
268
|
-
const growKey = dir === 1 ? 'ArrowRight' : 'ArrowLeft'
|
|
269
|
-
const shrinkKey = dir === 1 ? 'ArrowLeft' : 'ArrowRight'
|
|
270
|
-
if (e.key === growKey) {
|
|
271
|
-
e.preventDefault()
|
|
272
|
-
if (c) stampCollapsed(names, false)
|
|
273
|
-
else writeWidth(names, Math.min(current + stepPx, defaultPx.current * 3))
|
|
274
|
-
syncAndPersist()
|
|
275
|
-
} else if (e.key === shrinkKey) {
|
|
276
|
-
e.preventDefault()
|
|
277
|
-
if (c) return
|
|
278
|
-
const next = current - stepPx
|
|
279
|
-
if (next < snapPx) stampCollapsed(names, true)
|
|
280
|
-
else writeWidth(names, next)
|
|
281
|
-
syncAndPersist()
|
|
282
|
-
} else if (e.key === 'Home') {
|
|
283
|
-
e.preventDefault()
|
|
284
|
-
resetToDefault()
|
|
285
|
-
} else if (e.key === 'Enter' || e.key === ' ') {
|
|
286
|
-
e.preventDefault()
|
|
287
|
-
toggleCollapsed()
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
return {
|
|
292
|
-
collapsed,
|
|
293
|
-
toggleCollapsed,
|
|
294
|
-
grabProps: {
|
|
295
|
-
ref: grabRef,
|
|
296
|
-
/* the pill is drawn by `.kol-rail-grab` (kol-animation.css) — the hook
|
|
297
|
-
* only supplies `is-near` and `--kol-rail-grab-y`. A consumer's own
|
|
298
|
-
* className, spread after this, still wins. */
|
|
299
|
-
className: 'kol-rail-grab',
|
|
300
|
-
role: 'separator',
|
|
301
|
-
'aria-orientation': 'vertical',
|
|
302
|
-
'aria-label': 'Resize navigation',
|
|
303
|
-
'aria-valuenow': Math.round(collapsed ? collapsedPx.current : (widthPx ?? defaultPx.current)) || undefined,
|
|
304
|
-
'aria-valuemin': collapsedPx.current == null ? undefined : Math.round(collapsedPx.current),
|
|
305
|
-
'aria-valuemax': defaultPx.current == null ? undefined : Math.round(defaultPx.current * 3),
|
|
306
|
-
tabIndex: 0,
|
|
307
|
-
onPointerDown,
|
|
308
|
-
onKeyDown,
|
|
309
|
-
},
|
|
310
|
-
}
|
|
311
|
-
}
|