@datalayer/core 1.0.11 → 1.0.12

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.
@@ -11,6 +11,8 @@ export * from './useIAM';
11
11
  export * from './useId';
12
12
  export * from './useIsomorphicLayoutEffect';
13
13
  export * from './useJupyterLabTheme';
14
+ export * from './useHighZIndexPortal';
15
+ export * from './useKeyboardShortcuts';
14
16
  export * from './useKeyboardEscape';
15
17
  export * from './useLocation';
16
18
  export * from './useLocationHandles';
@@ -25,5 +27,6 @@ export * from './useUpload';
25
27
  export * from './useUser';
26
28
  export * from './useProjects';
27
29
  export * from './useProjectStore';
30
+ export * from './useMobile';
28
31
  export * from './useVisibilityObserver';
29
32
  export * from './useWindowSize';
@@ -16,6 +16,8 @@ export * from './useIAM';
16
16
  export * from './useId';
17
17
  export * from './useIsomorphicLayoutEffect';
18
18
  export * from './useJupyterLabTheme';
19
+ export * from './useHighZIndexPortal';
20
+ export * from './useKeyboardShortcuts';
19
21
  export * from './useKeyboardEscape';
20
22
  export * from './useLocation';
21
23
  export * from './useLocationHandles';
@@ -30,5 +32,6 @@ export * from './useUpload';
30
32
  export * from './useUser';
31
33
  export * from './useProjects';
32
34
  export * from './useProjectStore';
35
+ export * from './useMobile';
33
36
  export * from './useVisibilityObserver';
34
37
  export * from './useWindowSize';
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Ensure Primer portal root has a high z-index so overlays render above chat.
3
+ */
4
+ export declare function useHighZIndexPortal(): void;
@@ -0,0 +1,36 @@
1
+ /*
2
+ * Copyright (c) 2023-2025 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ import { useEffect } from 'react';
6
+ const PRIMER_PORTAL_ROOT_ID = 'primer-portal-root';
7
+ /**
8
+ * Ensure Primer portal root has a high z-index so overlays render above chat.
9
+ */
10
+ export function useHighZIndexPortal() {
11
+ useEffect(() => {
12
+ const setPortalZIndex = () => {
13
+ const portalRoot = document.getElementById(PRIMER_PORTAL_ROOT_ID);
14
+ if (portalRoot) {
15
+ portalRoot.style.zIndex = '9999';
16
+ return true;
17
+ }
18
+ return false;
19
+ };
20
+ if (setPortalZIndex()) {
21
+ return;
22
+ }
23
+ const observer = new MutationObserver(() => {
24
+ if (setPortalZIndex()) {
25
+ observer.disconnect();
26
+ }
27
+ });
28
+ observer.observe(document.body, {
29
+ childList: true,
30
+ subtree: true,
31
+ });
32
+ return () => {
33
+ observer.disconnect();
34
+ };
35
+ }, []);
36
+ }
@@ -0,0 +1,23 @@
1
+ export interface KeyboardShortcut {
2
+ key: string;
3
+ ctrlOrCmd?: boolean;
4
+ shift?: boolean;
5
+ alt?: boolean;
6
+ handler: () => void;
7
+ description?: string;
8
+ allowInInput?: boolean;
9
+ }
10
+ export interface UseKeyboardShortcutsOptions {
11
+ enabled?: boolean;
12
+ shortcuts: KeyboardShortcut[];
13
+ }
14
+ export declare function useKeyboardShortcuts({ enabled, shortcuts, }: UseKeyboardShortcutsOptions): void;
15
+ export declare function useChatKeyboardShortcuts({ onToggle, onNewChat, onClear, onFocusInput, enabled, }: {
16
+ onToggle?: () => void;
17
+ onNewChat?: () => void;
18
+ onClear?: () => void;
19
+ onFocusInput?: () => void;
20
+ enabled?: boolean;
21
+ }): void;
22
+ export declare function getShortcutDisplay(shortcut: KeyboardShortcut): string;
23
+ export default useKeyboardShortcuts;
@@ -0,0 +1,124 @@
1
+ /*
2
+ * Copyright (c) 2023-2025 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ import { useEffect, useCallback } from 'react';
6
+ function isMacOS() {
7
+ if (typeof navigator === 'undefined')
8
+ return false;
9
+ return /Mac|iPod|iPhone|iPad/.test(navigator.platform);
10
+ }
11
+ export function useKeyboardShortcuts({ enabled = true, shortcuts, }) {
12
+ const handleKeyDown = useCallback((event) => {
13
+ if (!enabled)
14
+ return;
15
+ const target = event.target;
16
+ const isInInput = target.tagName === 'INPUT' ||
17
+ target.tagName === 'SELECT' ||
18
+ target.tagName === 'TEXTAREA' ||
19
+ target.isContentEditable;
20
+ for (const shortcut of shortcuts) {
21
+ if (isInInput && !shortcut.allowInInput) {
22
+ if (shortcut.key !== 'Escape') {
23
+ continue;
24
+ }
25
+ }
26
+ if (event.key.toLowerCase() !== shortcut.key.toLowerCase()) {
27
+ continue;
28
+ }
29
+ const ctrlOrCmdPressed = isMacOS() ? event.metaKey : event.ctrlKey;
30
+ if (shortcut.ctrlOrCmd && !ctrlOrCmdPressed) {
31
+ continue;
32
+ }
33
+ if (!shortcut.ctrlOrCmd &&
34
+ ctrlOrCmdPressed &&
35
+ shortcut.key !== 'Escape') {
36
+ continue;
37
+ }
38
+ if (shortcut.shift && !event.shiftKey) {
39
+ continue;
40
+ }
41
+ if (shortcut.alt && !event.altKey) {
42
+ continue;
43
+ }
44
+ event.preventDefault();
45
+ event.stopPropagation();
46
+ shortcut.handler();
47
+ break;
48
+ }
49
+ }, [enabled, shortcuts]);
50
+ useEffect(() => {
51
+ if (!enabled)
52
+ return;
53
+ document.addEventListener('keydown', handleKeyDown);
54
+ return () => {
55
+ document.removeEventListener('keydown', handleKeyDown);
56
+ };
57
+ }, [enabled, handleKeyDown]);
58
+ }
59
+ export function useChatKeyboardShortcuts({ onToggle, onNewChat, onClear, onFocusInput, enabled = true, }) {
60
+ const shortcuts = [];
61
+ if (onToggle) {
62
+ shortcuts.push({
63
+ key: 'k',
64
+ ctrlOrCmd: true,
65
+ handler: onToggle,
66
+ description: 'Toggle chat sidebar',
67
+ });
68
+ }
69
+ if (onNewChat) {
70
+ shortcuts.push({
71
+ key: 'n',
72
+ ctrlOrCmd: true,
73
+ shift: true,
74
+ handler: onNewChat,
75
+ description: 'Start new chat',
76
+ });
77
+ }
78
+ if (onClear) {
79
+ shortcuts.push({
80
+ key: 'Backspace',
81
+ ctrlOrCmd: true,
82
+ shift: true,
83
+ handler: onClear,
84
+ description: 'Clear chat messages',
85
+ });
86
+ }
87
+ if (onFocusInput) {
88
+ shortcuts.push({
89
+ key: '/',
90
+ handler: onFocusInput,
91
+ description: 'Focus chat input',
92
+ });
93
+ }
94
+ if (onToggle) {
95
+ shortcuts.push({
96
+ key: 'Escape',
97
+ handler: onToggle,
98
+ description: 'Close chat sidebar',
99
+ allowInInput: true,
100
+ });
101
+ }
102
+ useKeyboardShortcuts({ enabled, shortcuts });
103
+ }
104
+ export function getShortcutDisplay(shortcut) {
105
+ const parts = [];
106
+ const isMac = isMacOS();
107
+ if (shortcut.ctrlOrCmd) {
108
+ parts.push(isMac ? '⌘' : 'Ctrl');
109
+ }
110
+ if (shortcut.shift) {
111
+ parts.push(isMac ? '⇧' : 'Shift');
112
+ }
113
+ if (shortcut.alt) {
114
+ parts.push(isMac ? '⌥' : 'Alt');
115
+ }
116
+ let keyDisplay = shortcut.key.toUpperCase();
117
+ if (shortcut.key === 'Escape')
118
+ keyDisplay = 'Esc';
119
+ if (shortcut.key === 'Backspace')
120
+ keyDisplay = isMac ? '⌫' : 'Del';
121
+ parts.push(keyDisplay);
122
+ return parts.join(isMac ? '' : '+');
123
+ }
124
+ export default useKeyboardShortcuts;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datalayer/core",
3
- "version": "1.0.11",
3
+ "version": "1.0.12",
4
4
  "type": "module",
5
5
  "workspaces": [
6
6
  ".",