@tanstack/hotkeys-devtools 0.1.3 → 0.3.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/dist/HotkeysContextProvider.js +13 -3
- package/dist/HotkeysContextProvider.js.map +1 -1
- package/dist/components/ActionButtons.js +7 -2
- package/dist/components/ActionButtons.js.map +1 -1
- package/dist/components/DetailsPanel.js +458 -215
- package/dist/components/DetailsPanel.js.map +1 -1
- package/dist/components/HotkeyList.js +294 -139
- package/dist/components/HotkeyList.js.map +1 -1
- package/dist/components/Shell.js +3 -1
- package/dist/components/Shell.js.map +1 -1
- package/package.json +2 -2
- package/src/HotkeysContextProvider.tsx +33 -5
- package/src/components/ActionButtons.tsx +18 -5
- package/src/components/DetailsPanel.tsx +500 -204
- package/src/components/HotkeyList.tsx +195 -2
- package/src/components/Shell.tsx +4 -1
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import { For, Show, createEffect, createMemo, createSignal, on } from 'solid-js'
|
|
2
2
|
import clsx from 'clsx'
|
|
3
|
-
import { formatForDisplay } from '@tanstack/hotkeys'
|
|
3
|
+
import { formatForDisplay, formatHotkeySequence } from '@tanstack/hotkeys'
|
|
4
4
|
import { useStyles } from '../styles/use-styles'
|
|
5
5
|
import { useHotkeysDevtoolsState } from '../HotkeysContextProvider'
|
|
6
|
-
import type {
|
|
6
|
+
import type {
|
|
7
|
+
ConflictBehavior,
|
|
8
|
+
HotkeyRegistration,
|
|
9
|
+
SequenceRegistrationView,
|
|
10
|
+
} from '@tanstack/hotkeys'
|
|
11
|
+
|
|
12
|
+
function sequenceKey(sequence: Array<string>): string {
|
|
13
|
+
return sequence.join('|')
|
|
14
|
+
}
|
|
7
15
|
|
|
8
16
|
type HotkeyListProps = {
|
|
9
17
|
selectedId: () => string | null
|
|
@@ -78,11 +86,38 @@ function findScopeConflicts(
|
|
|
78
86
|
)
|
|
79
87
|
}
|
|
80
88
|
|
|
89
|
+
function findSequenceTargetConflicts(
|
|
90
|
+
registration: SequenceRegistrationView,
|
|
91
|
+
all: Array<SequenceRegistrationView>,
|
|
92
|
+
): Array<SequenceRegistrationView> {
|
|
93
|
+
return all.filter(
|
|
94
|
+
(other) =>
|
|
95
|
+
other.id !== registration.id &&
|
|
96
|
+
sequenceKey(other.sequence) === sequenceKey(registration.sequence) &&
|
|
97
|
+
other.options.eventType === registration.options.eventType &&
|
|
98
|
+
other.target === registration.target,
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function findSequenceScopeConflicts(
|
|
103
|
+
registration: SequenceRegistrationView,
|
|
104
|
+
all: Array<SequenceRegistrationView>,
|
|
105
|
+
): Array<SequenceRegistrationView> {
|
|
106
|
+
return all.filter(
|
|
107
|
+
(other) =>
|
|
108
|
+
other.id !== registration.id &&
|
|
109
|
+
sequenceKey(other.sequence) === sequenceKey(registration.sequence) &&
|
|
110
|
+
other.options.eventType === registration.options.eventType &&
|
|
111
|
+
other.target !== registration.target,
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
|
|
81
115
|
export function HotkeyList(props: HotkeyListProps) {
|
|
82
116
|
const styles = useStyles()
|
|
83
117
|
const state = useHotkeysDevtoolsState()
|
|
84
118
|
|
|
85
119
|
const registrations = createMemo(() => state.registrations())
|
|
120
|
+
const sequenceRegistrations = createMemo(() => state.sequenceRegistrations())
|
|
86
121
|
|
|
87
122
|
return (
|
|
88
123
|
<>
|
|
@@ -243,6 +278,164 @@ export function HotkeyList(props: HotkeyListProps) {
|
|
|
243
278
|
}}
|
|
244
279
|
</For>
|
|
245
280
|
</div>
|
|
281
|
+
|
|
282
|
+
<div class={styles().panelHeader}>
|
|
283
|
+
Sequences ({sequenceRegistrations().length})
|
|
284
|
+
</div>
|
|
285
|
+
<div class={styles().hotkeyList}>
|
|
286
|
+
<For each={sequenceRegistrations()}>
|
|
287
|
+
{(reg) => {
|
|
288
|
+
const targetConflicts = () =>
|
|
289
|
+
findSequenceTargetConflicts(reg, sequenceRegistrations())
|
|
290
|
+
const scopeConflicts = () =>
|
|
291
|
+
findSequenceScopeConflicts(reg, sequenceRegistrations())
|
|
292
|
+
|
|
293
|
+
const hasTargetConflict = () => targetConflicts().length > 0
|
|
294
|
+
const hasScopeConflict = () => scopeConflicts().length > 0
|
|
295
|
+
|
|
296
|
+
const conflictBehavior = (): ConflictBehavior =>
|
|
297
|
+
reg.options.conflictBehavior ?? 'warn'
|
|
298
|
+
|
|
299
|
+
const targetConflictBadge = () => {
|
|
300
|
+
const behavior = conflictBehavior()
|
|
301
|
+
const c = targetConflicts()
|
|
302
|
+
if (behavior === 'allow') {
|
|
303
|
+
return {
|
|
304
|
+
style: 'badgeAllow' as const,
|
|
305
|
+
label: '~',
|
|
306
|
+
tooltip: `Allowed: ${c.length} other binding${c.length > 1 ? 's' : ''} on same sequence and target (conflictBehavior: allow)`,
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (behavior === 'error') {
|
|
310
|
+
return {
|
|
311
|
+
style: 'badgeError' as const,
|
|
312
|
+
label: '!',
|
|
313
|
+
tooltip: `Error: ${c.length} conflicting binding${c.length > 1 ? 's' : ''} on same sequence and target (conflictBehavior: error)`,
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return {
|
|
317
|
+
style: 'badgeConflict' as const,
|
|
318
|
+
label: '!',
|
|
319
|
+
tooltip: `Warning: ${c.length} other binding${c.length > 1 ? 's' : ''} on same sequence and target`,
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const scopeConflictTooltip = () => {
|
|
324
|
+
const c = scopeConflicts()
|
|
325
|
+
return `Info: ${c.length} binding${c.length > 1 ? 's' : ''} with same sequence on different target${c.length > 1 ? 's' : ''}`
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const enabled = () => reg.options.enabled !== false
|
|
329
|
+
|
|
330
|
+
const triggerCount = () =>
|
|
331
|
+
sequenceRegistrations().find((r) => r.id === reg.id)
|
|
332
|
+
?.triggerCount ?? reg.triggerCount
|
|
333
|
+
|
|
334
|
+
const [prevCount, setPrevCount] = createSignal(reg.triggerCount)
|
|
335
|
+
const [pulsing, setPulsing] = createSignal(false)
|
|
336
|
+
|
|
337
|
+
createEffect(
|
|
338
|
+
on(triggerCount, (current) => {
|
|
339
|
+
if (current > prevCount()) {
|
|
340
|
+
setPulsing(true)
|
|
341
|
+
setTimeout(() => setPulsing(false), 600)
|
|
342
|
+
}
|
|
343
|
+
setPrevCount(current)
|
|
344
|
+
}),
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
return (
|
|
348
|
+
<div
|
|
349
|
+
class={clsx(
|
|
350
|
+
styles().hotkeyRow,
|
|
351
|
+
props.selectedId() === reg.id && styles().hotkeyRowSelected,
|
|
352
|
+
pulsing() && styles().hotkeyRowTriggered,
|
|
353
|
+
)}
|
|
354
|
+
onClick={() => props.setSelectedId(reg.id)}
|
|
355
|
+
>
|
|
356
|
+
<span class={styles().hotkeyLabel}>
|
|
357
|
+
{formatHotkeySequence(reg.sequence)}
|
|
358
|
+
</span>
|
|
359
|
+
<Show when={triggerCount() > 0}>
|
|
360
|
+
<span class={styles().triggerCount}>x{triggerCount()}</span>
|
|
361
|
+
</Show>
|
|
362
|
+
<div class={styles().hotkeyBadges}>
|
|
363
|
+
{hasTargetConflict() && (
|
|
364
|
+
<span
|
|
365
|
+
class={clsx(
|
|
366
|
+
styles().badge,
|
|
367
|
+
styles()[targetConflictBadge().style],
|
|
368
|
+
styles().tooltip,
|
|
369
|
+
)}
|
|
370
|
+
>
|
|
371
|
+
{targetConflictBadge().label}
|
|
372
|
+
<span class={styles().tooltipText} data-tooltip>
|
|
373
|
+
{targetConflictBadge().tooltip}
|
|
374
|
+
</span>
|
|
375
|
+
</span>
|
|
376
|
+
)}
|
|
377
|
+
{hasScopeConflict() && (
|
|
378
|
+
<span
|
|
379
|
+
class={clsx(
|
|
380
|
+
styles().badge,
|
|
381
|
+
styles().badgeInfo,
|
|
382
|
+
styles().tooltip,
|
|
383
|
+
)}
|
|
384
|
+
>
|
|
385
|
+
i
|
|
386
|
+
<span class={styles().tooltipText} data-tooltip>
|
|
387
|
+
{scopeConflictTooltip()}
|
|
388
|
+
</span>
|
|
389
|
+
</span>
|
|
390
|
+
)}
|
|
391
|
+
<span
|
|
392
|
+
class={clsx(
|
|
393
|
+
styles().badge,
|
|
394
|
+
enabled()
|
|
395
|
+
? styles().badgeEnabled
|
|
396
|
+
: styles().badgeDisabled,
|
|
397
|
+
styles().tooltip,
|
|
398
|
+
)}
|
|
399
|
+
>
|
|
400
|
+
{enabled() ? 'on' : 'off'}
|
|
401
|
+
<span class={styles().tooltipText} data-tooltip>
|
|
402
|
+
{enabled()
|
|
403
|
+
? 'Sequence is enabled'
|
|
404
|
+
: 'Sequence is disabled'}
|
|
405
|
+
</span>
|
|
406
|
+
</span>
|
|
407
|
+
<span
|
|
408
|
+
class={clsx(
|
|
409
|
+
styles().badge,
|
|
410
|
+
(reg.options.eventType ?? 'keydown') === 'keydown'
|
|
411
|
+
? styles().badgeKeydown
|
|
412
|
+
: styles().badgeKeyup,
|
|
413
|
+
styles().tooltip,
|
|
414
|
+
)}
|
|
415
|
+
>
|
|
416
|
+
{reg.options.eventType ?? 'keydown'}
|
|
417
|
+
<span class={styles().tooltipText} data-tooltip>
|
|
418
|
+
Fires on {reg.options.eventType ?? 'keydown'} event
|
|
419
|
+
</span>
|
|
420
|
+
</span>
|
|
421
|
+
<span
|
|
422
|
+
class={clsx(
|
|
423
|
+
styles().badge,
|
|
424
|
+
styles().badgeTarget,
|
|
425
|
+
styles().tooltip,
|
|
426
|
+
)}
|
|
427
|
+
>
|
|
428
|
+
{getTargetLabel(reg.target)}
|
|
429
|
+
<span class={styles().tooltipText} data-tooltip>
|
|
430
|
+
{getTargetTooltip(reg.target)}
|
|
431
|
+
</span>
|
|
432
|
+
</span>
|
|
433
|
+
</div>
|
|
434
|
+
</div>
|
|
435
|
+
)
|
|
436
|
+
}}
|
|
437
|
+
</For>
|
|
438
|
+
</div>
|
|
246
439
|
</>
|
|
247
440
|
)
|
|
248
441
|
}
|
package/src/components/Shell.tsx
CHANGED
|
@@ -16,7 +16,10 @@ export function Shell() {
|
|
|
16
16
|
const selectedRegistration = createMemo(() => {
|
|
17
17
|
const id = selectedId()
|
|
18
18
|
if (!id) return null
|
|
19
|
-
|
|
19
|
+
const hotkey = state.registrations().find((r) => r.id === id)
|
|
20
|
+
if (hotkey) return hotkey
|
|
21
|
+
const sequence = state.sequenceRegistrations().find((r) => r.id === id)
|
|
22
|
+
return sequence ?? null
|
|
20
23
|
})
|
|
21
24
|
|
|
22
25
|
let dragStartX = 0
|