impact-nova 1.3.0 → 1.5.2
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/README.md +49 -0
- package/dist/components/layout/dashboard-layout.d.ts +15 -1
- package/dist/components/ui/accordion-nested-list/accordion-nested-list.js +65 -64
- package/dist/components/ui/accordion.js +38 -34
- package/dist/components/ui/ag-grid-react/cell-renderers/badge-cell-renderer.js +1 -1
- package/dist/components/ui/ag-grid-react/cell-renderers/cell-renderer-utils.d.ts +58 -0
- package/dist/components/ui/ag-grid-react/cell-renderers/cell-renderer-utils.js +104 -0
- package/dist/components/ui/ag-grid-react/cell-renderers/editors/input-cell-editor.js +62 -54
- package/dist/components/ui/ag-grid-react/cell-renderers/editors/split-cell-editor.d.ts +4 -6
- package/dist/components/ui/ag-grid-react/cell-renderers/editors/split-cell-editor.js +54 -68
- package/dist/components/ui/ag-grid-react/cell-renderers/index.d.ts +3 -1
- package/dist/components/ui/ag-grid-react/cell-renderers/index.js +33 -27
- package/dist/components/ui/ag-grid-react/cell-renderers/input-display-renderer.d.ts +3 -0
- package/dist/components/ui/ag-grid-react/cell-renderers/input-display-renderer.js +28 -12
- package/dist/components/ui/ag-grid-react/cell-renderers/link-with-batch-cell-renderer.d.ts +2 -0
- package/dist/components/ui/ag-grid-react/cell-renderers/link-with-batch-cell-renderer.js +45 -44
- package/dist/components/ui/ag-grid-react/cell-renderers/split-cell-renderer.d.ts +4 -6
- package/dist/components/ui/ag-grid-react/cell-renderers/split-cell-renderer.js +23 -37
- package/dist/components/ui/ag-grid-react/cell-renderers/types.d.ts +33 -17
- package/dist/components/ui/ag-grid-react/editable-utils.d.ts +27 -0
- package/dist/components/ui/ag-grid-react/editable-utils.js +62 -0
- package/dist/components/ui/ag-grid-react/headers/advanced-filter/advanced-filter-dialog.js +99 -98
- package/dist/components/ui/ag-grid-react/headers/advanced-filter/column-filter-section.js +110 -143
- package/dist/components/ui/ag-grid-react/headers/column-menu/column-settings-menu.js +72 -69
- package/dist/components/ui/ag-grid-react/headers/components/header-info.js +36 -35
- package/dist/components/ui/ag-grid-react/headers/components/info-modal.js +14 -12
- package/dist/components/ui/ag-grid-react/headers/custom-header.js +111 -110
- package/dist/components/ui/ag-grid-react/headers/header-search-input.js +204 -203
- package/dist/components/ui/alert.js +62 -58
- package/dist/components/ui/breadcrumb.js +81 -74
- package/dist/components/ui/calendar.js +354 -354
- package/dist/components/ui/chart/chart.js +63 -62
- package/dist/components/ui/chips.js +42 -38
- package/dist/components/ui/command-palette/command-palette-context.d.ts +52 -0
- package/dist/components/ui/command-palette/command-palette-context.js +110 -0
- package/dist/components/ui/command-palette/command-palette.d.ts +67 -0
- package/dist/components/ui/command-palette/command-palette.js +402 -0
- package/dist/components/ui/command-palette/index.d.ts +23 -0
- package/dist/components/ui/command-palette/index.js +44 -0
- package/dist/components/ui/command-palette/kbd.d.ts +28 -0
- package/dist/components/ui/command-palette/kbd.js +52 -0
- package/dist/components/ui/command-palette/shortcut-registry.d.ts +68 -0
- package/dist/components/ui/command-palette/shortcut-registry.js +183 -0
- package/dist/components/ui/command-palette/shortcut-scope-provider.d.ts +55 -0
- package/dist/components/ui/command-palette/shortcut-scope-provider.js +55 -0
- package/dist/components/ui/command-palette/shortcut-settings.d.ts +27 -0
- package/dist/components/ui/command-palette/shortcut-settings.js +266 -0
- package/dist/components/ui/command-palette/use-browser-shortcuts.d.ts +32 -0
- package/dist/components/ui/command-palette/use-browser-shortcuts.js +48 -0
- package/dist/components/ui/command-palette/use-global-shortcut.d.ts +3 -0
- package/dist/components/ui/command-palette/use-global-shortcut.js +7 -0
- package/dist/components/ui/command-palette/use-shortcut.d.ts +47 -0
- package/dist/components/ui/command-palette/use-shortcut.js +49 -0
- package/dist/components/ui/command-palette/utils.d.ts +119 -0
- package/dist/components/ui/command-palette/utils.js +248 -0
- package/dist/components/ui/data-table/data-table-column-list.js +87 -86
- package/dist/components/ui/data-table/data-table-format-options.js +45 -44
- package/dist/components/ui/data-table/data-table-view-options.js +39 -38
- package/dist/components/ui/date-picker/date-picker.js +89 -87
- package/dist/components/ui/date-picker/date-range-picker.js +140 -138
- package/dist/components/ui/date-picker/month-picker.js +82 -81
- package/dist/components/ui/date-picker/month-range-picker.js +108 -105
- package/dist/components/ui/date-picker/multi-date-picker.js +68 -66
- package/dist/components/ui/date-picker/multi-month-picker.js +59 -58
- package/dist/components/ui/date-picker/multi-week-picker.js +80 -78
- package/dist/components/ui/date-picker/week-picker.js +117 -115
- package/dist/components/ui/date-picker/week-range-picker.js +166 -164
- package/dist/components/ui/dialog.js +78 -73
- package/dist/components/ui/drawer.js +71 -66
- package/dist/components/ui/file-upload.js +131 -127
- package/dist/components/ui/filter-panel/filter-panel.js +98 -94
- package/dist/components/ui/filter-strip/filter-strip.js +95 -91
- package/dist/components/ui/filter-strip/filter-summary.js +91 -90
- package/dist/components/ui/header.js +57 -53
- package/dist/components/ui/horizontal-scroller/horizontal-scroller.js +78 -76
- package/dist/components/ui/loader.js +17 -16
- package/dist/components/ui/nested-list/components/NestedListHeader.d.ts +1 -0
- package/dist/components/ui/nested-list/components/NestedListHeader.js +51 -48
- package/dist/components/ui/nested-list/components/SortableItem.js +60 -59
- package/dist/components/ui/nested-list/nested-list.js +184 -182
- package/dist/components/ui/notification-panel/notification-panel.js +60 -53
- package/dist/components/ui/popover.js +45 -40
- package/dist/components/ui/prompt.js +90 -86
- package/dist/components/ui/select/select.js +206 -209
- package/dist/components/ui/sheet.d.ts +1 -0
- package/dist/components/ui/sheet.js +50 -48
- package/dist/components/ui/sidebar.js +273 -267
- package/dist/components/ui/stepper.js +75 -63
- package/dist/components/ui/tag.js +48 -44
- package/dist/components/ui/toast.js +46 -41
- package/dist/i18n/ImpactNovaI18nContext.d.ts +21 -0
- package/dist/i18n/ImpactNovaI18nContext.js +76 -0
- package/dist/i18n/defaultMessages.d.ts +231 -0
- package/dist/i18n/defaultMessages.js +206 -0
- package/dist/i18n/getDateFnsLocale.d.ts +11 -0
- package/dist/i18n/getDateFnsLocale.js +21 -0
- package/dist/i18n/index.d.ts +5 -0
- package/dist/i18n/locales/de.d.ts +2 -0
- package/dist/i18n/locales/de.js +206 -0
- package/dist/i18n/locales/es.d.ts +2 -0
- package/dist/i18n/locales/es.js +206 -0
- package/dist/i18n/locales/hi.d.ts +2 -0
- package/dist/i18n/locales/hi.js +206 -0
- package/dist/i18n/locales/index.d.ts +4 -0
- package/dist/i18n/locales/kn.d.ts +2 -0
- package/dist/i18n/locales/kn.js +206 -0
- package/dist/icons/assets/boxAdd.svg.js +5 -0
- package/dist/icons/assets/boxed.svg.js +5 -0
- package/dist/icons/assets/trolley.svg.js +5 -0
- package/dist/icons/assets/unlocked.svg.js +5 -0
- package/dist/icons/index.d.ts +4 -0
- package/dist/icons/index.js +179 -171
- package/dist/impact-nova.css +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +232 -172
- package/package.json +36 -4
- package/dist/components/ui/ag-grid-react/cell-renderers/types.js +0 -74
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { isReservedShortcut as f, keybindingsEqual as m, matchesKeybinding as u, scopePriority as o, deserialiseOverrides as g, serialiseOverrides as v } from "./utils.js";
|
|
2
|
+
const l = "impact-nova-shortcuts", h = "impact-nova-recent-commands", p = 10;
|
|
3
|
+
class C {
|
|
4
|
+
commands = /* @__PURE__ */ new Map();
|
|
5
|
+
overrides = /* @__PURE__ */ new Map();
|
|
6
|
+
recentCommandIds = [];
|
|
7
|
+
listeners = /* @__PURE__ */ new Set();
|
|
8
|
+
activeScopes = /* @__PURE__ */ new Set();
|
|
9
|
+
activeInstance = null;
|
|
10
|
+
constructor() {
|
|
11
|
+
this.activeScopes.add("global"), this.loadOverrides(), this.loadRecentCommands();
|
|
12
|
+
}
|
|
13
|
+
// ─── Instance Management ────────────────────────────────────────────────
|
|
14
|
+
setActiveInstance(e) {
|
|
15
|
+
this.activeInstance !== e && (this.activeInstance = e, this.emit({ type: "scope-change" }));
|
|
16
|
+
}
|
|
17
|
+
getActiveInstance() {
|
|
18
|
+
return this.activeInstance;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Get all unique instance identifiers currently registered.
|
|
22
|
+
*/
|
|
23
|
+
getRegisteredInstances() {
|
|
24
|
+
const e = /* @__PURE__ */ new Set();
|
|
25
|
+
for (const t of this.commands.values())
|
|
26
|
+
t.instance && e.add(t.instance);
|
|
27
|
+
return Array.from(e);
|
|
28
|
+
}
|
|
29
|
+
// ─── Registration ───────────────────────────────────────────────────────
|
|
30
|
+
register(e) {
|
|
31
|
+
this.commands.set(e.id, e);
|
|
32
|
+
const t = this.overrides.get(e.id);
|
|
33
|
+
t && (e.keybinding = t), this.emit({ type: "register", commandId: e.id });
|
|
34
|
+
}
|
|
35
|
+
unregister(e) {
|
|
36
|
+
this.commands.delete(e), this.emit({ type: "unregister", commandId: e });
|
|
37
|
+
}
|
|
38
|
+
// ─── Keybinding Management ─────────────────────────────────────────────
|
|
39
|
+
getEffectiveKeybinding(e) {
|
|
40
|
+
const t = this.commands.get(e);
|
|
41
|
+
if (t)
|
|
42
|
+
return t.keybinding ?? t.defaultKeybinding;
|
|
43
|
+
}
|
|
44
|
+
updateKeybinding(e, t) {
|
|
45
|
+
const s = this.commands.get(e);
|
|
46
|
+
if (!s) return { success: !1 };
|
|
47
|
+
if (!s.customisable) return { success: !1 };
|
|
48
|
+
if (f(t))
|
|
49
|
+
return { success: !1, reserved: !0 };
|
|
50
|
+
const n = this.findConflict(e, t, s.scope);
|
|
51
|
+
return n ? { success: !1, conflict: n } : (s.keybinding = t, this.overrides.set(e, t), this.saveOverrides(), this.emit({ type: "update-keybinding", commandId: e }), { success: !0 });
|
|
52
|
+
}
|
|
53
|
+
resetKeybinding(e) {
|
|
54
|
+
const t = this.commands.get(e);
|
|
55
|
+
t && (t.keybinding = void 0, this.overrides.delete(e), this.saveOverrides(), this.emit({ type: "reset-keybinding", commandId: e }));
|
|
56
|
+
}
|
|
57
|
+
// ─── Conflict Detection ────────────────────────────────────────────────
|
|
58
|
+
findConflict(e, t, s) {
|
|
59
|
+
for (const [n, r] of this.commands) {
|
|
60
|
+
if (n === e || r.scope !== s) continue;
|
|
61
|
+
const c = this.getEffectiveKeybinding(n);
|
|
62
|
+
if (c && m(c, t))
|
|
63
|
+
return {
|
|
64
|
+
conflictingCommandId: n,
|
|
65
|
+
conflictingLabel: r.label,
|
|
66
|
+
scope: r.scope
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
// ─── Scope Management ──────────────────────────────────────────────────
|
|
72
|
+
pushScope(e) {
|
|
73
|
+
this.activeScopes.add(e), this.emit({ type: "scope-change" });
|
|
74
|
+
}
|
|
75
|
+
popScope(e) {
|
|
76
|
+
e !== "global" && (this.activeScopes.delete(e), this.emit({ type: "scope-change" }));
|
|
77
|
+
}
|
|
78
|
+
getActiveScopes() {
|
|
79
|
+
return Array.from(this.activeScopes);
|
|
80
|
+
}
|
|
81
|
+
getHighestActiveScope() {
|
|
82
|
+
let e = "global";
|
|
83
|
+
for (const t of this.activeScopes)
|
|
84
|
+
o(t) > o(e) && (e = t);
|
|
85
|
+
return e;
|
|
86
|
+
}
|
|
87
|
+
// ─── Command Queries ───────────────────────────────────────────────────
|
|
88
|
+
getCommand(e) {
|
|
89
|
+
return this.commands.get(e);
|
|
90
|
+
}
|
|
91
|
+
getAllCommands() {
|
|
92
|
+
return Array.from(this.commands.values());
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Get commands visible in the command palette.
|
|
96
|
+
* Instance-scoped commands are filtered (only active instance shows unless
|
|
97
|
+
* there's only one instance, in which case it always shows).
|
|
98
|
+
* When multiple instances exist, labels are auto-prefixed with the instance name.
|
|
99
|
+
*/
|
|
100
|
+
getPaletteCommands() {
|
|
101
|
+
const t = this.getRegisteredInstances().length > 1;
|
|
102
|
+
return this.getAllCommands().filter((s) => !(s.hidden || s.when && !s.when() || s.instance && t && this.activeInstance && s.instance !== this.activeInstance));
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Get commands for the settings panel (all registered, including hidden).
|
|
106
|
+
*/
|
|
107
|
+
getSettingsCommands() {
|
|
108
|
+
return this.getAllCommands();
|
|
109
|
+
}
|
|
110
|
+
// ─── Keyboard Event Handling ───────────────────────────────────────────
|
|
111
|
+
/**
|
|
112
|
+
* Handle a keyboard event. Finds the highest-priority matching command
|
|
113
|
+
* and executes its handler.
|
|
114
|
+
*
|
|
115
|
+
* @returns true if a command was executed, false otherwise
|
|
116
|
+
*/
|
|
117
|
+
handleKeyEvent(e) {
|
|
118
|
+
const t = this.activeScopes.has("modal"), n = this.getRegisteredInstances().length > 1;
|
|
119
|
+
let r = null, c = -1;
|
|
120
|
+
for (const i of this.commands.values()) {
|
|
121
|
+
if (i.passive) continue;
|
|
122
|
+
const a = this.getEffectiveKeybinding(i.id);
|
|
123
|
+
if (a && !(t && i.scope !== "modal") && !(i.instance && n && i.instance !== this.activeInstance) && !(i.when && !i.when()) && u(e, a)) {
|
|
124
|
+
const d = o(i.scope);
|
|
125
|
+
d > c && (r = i, c = d);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return r ? (e.preventDefault(), e.stopPropagation(), r.handler(), this.addRecentCommand(r.id), !0) : !1;
|
|
129
|
+
}
|
|
130
|
+
// ─── Recent Commands ───────────────────────────────────────────────────
|
|
131
|
+
getRecentCommandIds() {
|
|
132
|
+
return [...this.recentCommandIds];
|
|
133
|
+
}
|
|
134
|
+
addRecentCommand(e) {
|
|
135
|
+
this.recentCommandIds = [
|
|
136
|
+
e,
|
|
137
|
+
...this.recentCommandIds.filter((t) => t !== e)
|
|
138
|
+
].slice(0, p), this.saveRecentCommands();
|
|
139
|
+
}
|
|
140
|
+
// ─── Event System ─────────────────────────────────────────────────────
|
|
141
|
+
subscribe(e) {
|
|
142
|
+
return this.listeners.add(e), () => {
|
|
143
|
+
this.listeners.delete(e);
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
emit(e) {
|
|
147
|
+
this.listeners.forEach((t) => t(e));
|
|
148
|
+
}
|
|
149
|
+
// ─── Persistence ──────────────────────────────────────────────────────
|
|
150
|
+
loadOverrides() {
|
|
151
|
+
if (!(typeof localStorage > "u"))
|
|
152
|
+
try {
|
|
153
|
+
const e = localStorage.getItem(l);
|
|
154
|
+
e && (this.overrides = g(e));
|
|
155
|
+
} catch {
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
saveOverrides() {
|
|
159
|
+
if (!(typeof localStorage > "u"))
|
|
160
|
+
try {
|
|
161
|
+
localStorage.setItem(l, v(this.overrides));
|
|
162
|
+
} catch {
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
loadRecentCommands() {
|
|
166
|
+
if (!(typeof localStorage > "u"))
|
|
167
|
+
try {
|
|
168
|
+
const e = localStorage.getItem(h);
|
|
169
|
+
e && (this.recentCommandIds = JSON.parse(e));
|
|
170
|
+
} catch {
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
saveRecentCommands() {
|
|
174
|
+
if (!(typeof localStorage > "u"))
|
|
175
|
+
try {
|
|
176
|
+
localStorage.setItem(h, JSON.stringify(this.recentCommandIds));
|
|
177
|
+
} catch {
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
export {
|
|
182
|
+
C as ShortcutRegistry
|
|
183
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ShortcutScope } from './utils';
|
|
2
|
+
/**
|
|
3
|
+
* ShortcutScopeProvider — Scope boundary component with instance awareness.
|
|
4
|
+
*
|
|
5
|
+
* When mounted, it pushes a scope onto the registry. When unmounted,
|
|
6
|
+
* it pops the scope. This allows sections of the UI to own their shortcuts.
|
|
7
|
+
*
|
|
8
|
+
* When `instance` is provided, the provider wraps its children in a
|
|
9
|
+
* container div that detects focus events. When anything inside the
|
|
10
|
+
* container receives focus (click or tab), the instance becomes "active"
|
|
11
|
+
* in the registry, and only that instance's commands are dispatched.
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* // Basic scope (no instance — all commands fire)
|
|
15
|
+
* <ShortcutScopeProvider scope="module" name="planning">
|
|
16
|
+
* <PlanningModule />
|
|
17
|
+
* </ShortcutScopeProvider>
|
|
18
|
+
*
|
|
19
|
+
* // Instance-aware (for multi-table pages)
|
|
20
|
+
* <ShortcutScopeProvider scope="page" instance="sales-table" label="Sales Table">
|
|
21
|
+
* <DataTable ... />
|
|
22
|
+
* </ShortcutScopeProvider>
|
|
23
|
+
*
|
|
24
|
+
* <ShortcutScopeProvider scope="page" instance="inventory-table" label="Inventory Table">
|
|
25
|
+
* <DataTable ... />
|
|
26
|
+
* </ShortcutScopeProvider>
|
|
27
|
+
*/
|
|
28
|
+
import * as React from 'react';
|
|
29
|
+
export interface ShortcutScopeProviderProps {
|
|
30
|
+
/** The scope level to activate */
|
|
31
|
+
scope: ShortcutScope;
|
|
32
|
+
/** Human-readable name for debugging */
|
|
33
|
+
name?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Unique instance identifier. When provided, enables focus-based
|
|
36
|
+
* dispatch so that only the focused instance's commands fire.
|
|
37
|
+
*/
|
|
38
|
+
instance?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Human-readable label for the instance (shown in command palette
|
|
41
|
+
* when multiple instances are present, e.g. "Sales Table").
|
|
42
|
+
*/
|
|
43
|
+
label?: string;
|
|
44
|
+
/** Optional className for the wrapper div (only used when instance is set) */
|
|
45
|
+
className?: string;
|
|
46
|
+
children: React.ReactNode;
|
|
47
|
+
}
|
|
48
|
+
export declare const ShortcutInstanceContext: React.Context<{
|
|
49
|
+
instance?: string;
|
|
50
|
+
label?: string;
|
|
51
|
+
}>;
|
|
52
|
+
export declare function ShortcutScopeProvider({ scope, instance, label, className, children, }: ShortcutScopeProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
53
|
+
export declare namespace ShortcutScopeProvider {
|
|
54
|
+
var displayName: string;
|
|
55
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { jsx as c, Fragment as m } from "react/jsx-runtime";
|
|
2
|
+
import * as a from "react";
|
|
3
|
+
import { useCommandPalette as f } from "./command-palette-context.js";
|
|
4
|
+
import { cn as l } from "../../../lib/utils.js";
|
|
5
|
+
const d = a.createContext(null);
|
|
6
|
+
function v({
|
|
7
|
+
scope: t,
|
|
8
|
+
instance: e,
|
|
9
|
+
label: o,
|
|
10
|
+
className: n,
|
|
11
|
+
children: u
|
|
12
|
+
}) {
|
|
13
|
+
const { pushScope: r, popScope: p, setActiveInstance: s, getActiveInstance: i } = f();
|
|
14
|
+
return a.useEffect(() => (r(t), () => {
|
|
15
|
+
p(t);
|
|
16
|
+
}), [t, r, p]), e ? /* @__PURE__ */ c(d.Provider, { value: { instance: e, label: o }, children: /* @__PURE__ */ c(
|
|
17
|
+
C,
|
|
18
|
+
{
|
|
19
|
+
instance: e,
|
|
20
|
+
className: n,
|
|
21
|
+
setActiveInstance: s,
|
|
22
|
+
getActiveInstance: i,
|
|
23
|
+
children: u
|
|
24
|
+
}
|
|
25
|
+
) }) : /* @__PURE__ */ c(m, { children: u });
|
|
26
|
+
}
|
|
27
|
+
v.displayName = "ShortcutScopeProvider";
|
|
28
|
+
function C({
|
|
29
|
+
instance: t,
|
|
30
|
+
className: e,
|
|
31
|
+
setActiveInstance: o,
|
|
32
|
+
getActiveInstance: n,
|
|
33
|
+
children: u
|
|
34
|
+
}) {
|
|
35
|
+
const r = a.useCallback(() => {
|
|
36
|
+
o(t);
|
|
37
|
+
}, [t, o]);
|
|
38
|
+
return a.useEffect(() => () => {
|
|
39
|
+
queueMicrotask(() => {
|
|
40
|
+
});
|
|
41
|
+
}, [t]), /* @__PURE__ */ c(
|
|
42
|
+
"div",
|
|
43
|
+
{
|
|
44
|
+
onFocusCapture: r,
|
|
45
|
+
onClickCapture: r,
|
|
46
|
+
className: l("relative", e),
|
|
47
|
+
"data-shortcut-instance": t,
|
|
48
|
+
children: u
|
|
49
|
+
}
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
export {
|
|
53
|
+
d as ShortcutInstanceContext,
|
|
54
|
+
v as ShortcutScopeProvider
|
|
55
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ShortcutScope } from './utils';
|
|
2
|
+
/**
|
|
3
|
+
* ShortcutSettings — Keyboard shortcut customisation panel built with AG Grid.
|
|
4
|
+
*
|
|
5
|
+
* Features:
|
|
6
|
+
* - AG Grid with search, sort, and filter on every column
|
|
7
|
+
* - Source column with set filter (All / User / Browser / AG Grid / System)
|
|
8
|
+
* - Click keybinding cell → "recording mode" (press new keys)
|
|
9
|
+
* - Non-customisable rows are read-only
|
|
10
|
+
* - Conflict detection with inline warning
|
|
11
|
+
* - Reset individual overrides to defaults
|
|
12
|
+
* - Persists changes to localStorage immediately
|
|
13
|
+
*/
|
|
14
|
+
import * as React from 'react';
|
|
15
|
+
export interface ShortcutSettingsProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
16
|
+
/** Filter to specific scopes */
|
|
17
|
+
scopes?: ShortcutScope[];
|
|
18
|
+
/** Filter to specific sources */
|
|
19
|
+
sources?: Array<'system' | 'user' | 'ag-grid' | 'browser'>;
|
|
20
|
+
/** Optional render prop to hoist the recording indicator/conflict message up to a parent toolbar */
|
|
21
|
+
renderStatus?: (node: React.ReactNode | null) => React.ReactNode;
|
|
22
|
+
}
|
|
23
|
+
declare function ShortcutSettings({ className, scopes, sources, renderStatus, ...props }: ShortcutSettingsProps): import("react/jsx-runtime").JSX.Element;
|
|
24
|
+
declare namespace ShortcutSettings {
|
|
25
|
+
var displayName: string;
|
|
26
|
+
}
|
|
27
|
+
export { ShortcutSettings };
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { jsx as r, jsxs as h, Fragment as N } from "react/jsx-runtime";
|
|
2
|
+
import * as n from "react";
|
|
3
|
+
import { User as L, Monitor as F, AlertTriangle as I, Lock as R, RotateCcw as U } from "lucide-react";
|
|
4
|
+
import { cn as p } from "../../../lib/utils.js";
|
|
5
|
+
import { ModuleRegistry as _, AllCommunityModule as K } from "ag-grid-community";
|
|
6
|
+
import { AllEnterpriseModule as j } from "ag-grid-enterprise";
|
|
7
|
+
import { DataTable as z, DataTableContent as B } from "../data-table/data-table.js";
|
|
8
|
+
import { AG_CELL_NO_PADDING as S } from "../ag-grid-react/cell-renderers/index.js";
|
|
9
|
+
import { Kbd as O } from "./kbd.js";
|
|
10
|
+
import { useCommandPalette as $ } from "./command-palette-context.js";
|
|
11
|
+
import { keybindingToString as H, keybindingFromEvent as V, isReservedShortcut as W } from "./utils.js";
|
|
12
|
+
import { BadgeCellRenderer as q } from "../ag-grid-react/cell-renderers/badge-cell-renderer.js";
|
|
13
|
+
_.registerModules([K, j]);
|
|
14
|
+
const J = {
|
|
15
|
+
system: "System",
|
|
16
|
+
user: "User",
|
|
17
|
+
"ag-grid": "AG Grid",
|
|
18
|
+
browser: "Browser"
|
|
19
|
+
};
|
|
20
|
+
function Q(l) {
|
|
21
|
+
const e = l.data;
|
|
22
|
+
return e ? /* @__PURE__ */ h("div", { className: "flex items-center gap-2 min-w-0 h-full", children: [
|
|
23
|
+
!e.customisable && /* @__PURE__ */ r(R, { className: "h-3 w-3 text-[#9ca3af] shrink-0" }),
|
|
24
|
+
/* @__PURE__ */ h("div", { className: "flex flex-col min-w-0 gap-0", children: [
|
|
25
|
+
/* @__PURE__ */ r("span", { className: "truncate font-medium text-[#374151] text-xs leading-tight", children: e.command }),
|
|
26
|
+
e.description && /* @__PURE__ */ r("span", { className: "truncate text-[10px] text-[#9ca3af] leading-tight", children: e.description })
|
|
27
|
+
] })
|
|
28
|
+
] }) : null;
|
|
29
|
+
}
|
|
30
|
+
function X(l) {
|
|
31
|
+
const e = l.data;
|
|
32
|
+
if (!e) return null;
|
|
33
|
+
const c = l.recordingCommandId === e.id;
|
|
34
|
+
return /* @__PURE__ */ r("div", { className: p(
|
|
35
|
+
"w-full h-full flex items-center ag-cell-inner-padding",
|
|
36
|
+
e.customisable ? "in-ag-editable-cell-highlight" : ""
|
|
37
|
+
), children: /* @__PURE__ */ r(
|
|
38
|
+
"div",
|
|
39
|
+
{
|
|
40
|
+
className: p(
|
|
41
|
+
"flex items-center justify-between w-full h-7 rounded-[8px] px-1.5 group/row relative",
|
|
42
|
+
e.customisable ? "bg-white" : "bg-transparent",
|
|
43
|
+
c && "ring-1 ring-[#4259ee] border border-[#4259ee]"
|
|
44
|
+
),
|
|
45
|
+
children: c ? /* @__PURE__ */ r("span", { className: "text-xs text-[#4259ee] font-medium animate-pulse", children: "Press desired shortcut..." }) : /* @__PURE__ */ h(N, { children: [
|
|
46
|
+
e.keybinding ? /* @__PURE__ */ r(
|
|
47
|
+
"button",
|
|
48
|
+
{
|
|
49
|
+
onClick: (a) => {
|
|
50
|
+
a.stopPropagation(), e.customisable && l.onStartRecording(e.id);
|
|
51
|
+
},
|
|
52
|
+
disabled: !e.customisable,
|
|
53
|
+
className: p(
|
|
54
|
+
"flex items-center justify-end h-full w-full text-right",
|
|
55
|
+
e.customisable ? "cursor-pointer" : "cursor-default opacity-70"
|
|
56
|
+
),
|
|
57
|
+
children: /* @__PURE__ */ r(O, { keybinding: e.keybinding, size: "sm" })
|
|
58
|
+
}
|
|
59
|
+
) : e.customisable ? /* @__PURE__ */ r(
|
|
60
|
+
"button",
|
|
61
|
+
{
|
|
62
|
+
onClick: (a) => {
|
|
63
|
+
a.stopPropagation(), l.onStartRecording(e.id);
|
|
64
|
+
},
|
|
65
|
+
className: "flex items-center justify-end h-full w-full text-xs text-[#9ca3af] hover:text-[#4259ee] transition-colors cursor-pointer text-right",
|
|
66
|
+
children: "Add shortcut"
|
|
67
|
+
}
|
|
68
|
+
) : /* @__PURE__ */ r("span", { className: "text-xs text-transparent", children: "--" }),
|
|
69
|
+
e.hasOverride && e.customisable && !c && /* @__PURE__ */ r(
|
|
70
|
+
"button",
|
|
71
|
+
{
|
|
72
|
+
onClick: (a) => {
|
|
73
|
+
a.stopPropagation(), l.onReset(e.id);
|
|
74
|
+
},
|
|
75
|
+
className: "absolute right-1 p-0.5 rounded bg-white hover:bg-[#f3f4f6] hover:text-[#ef4444] cursor-pointer opacity-0 group-hover/row:opacity-100 transition-opacity z-10",
|
|
76
|
+
title: "Reset to default",
|
|
77
|
+
children: /* @__PURE__ */ r(U, { className: "h-3 w-3" })
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
] })
|
|
81
|
+
}
|
|
82
|
+
) });
|
|
83
|
+
}
|
|
84
|
+
function Y(l) {
|
|
85
|
+
const e = l.data;
|
|
86
|
+
return e ? /* @__PURE__ */ r("div", { className: "flex items-center h-full", children: /* @__PURE__ */ r("span", { className: "text-xs text-[#6b7280] capitalize", children: e.scope }) }) : null;
|
|
87
|
+
}
|
|
88
|
+
function Z({
|
|
89
|
+
className: l,
|
|
90
|
+
scopes: e,
|
|
91
|
+
sources: c,
|
|
92
|
+
renderStatus: a,
|
|
93
|
+
...k
|
|
94
|
+
}) {
|
|
95
|
+
const {
|
|
96
|
+
getSettingsCommands: E,
|
|
97
|
+
getEffectiveKeybinding: M,
|
|
98
|
+
updateKeybinding: g,
|
|
99
|
+
resetKeybinding: b,
|
|
100
|
+
version: A
|
|
101
|
+
} = $(), [o, f] = n.useState(null), [d, i] = n.useState(null), G = n.useMemo(() => {
|
|
102
|
+
let s = E();
|
|
103
|
+
return e && (s = s.filter((t) => e.includes(t.scope))), c && (s = s.filter((t) => t.source && c.includes(t.source))), s.map((t) => {
|
|
104
|
+
const u = M(t.id), m = t.source ?? "user", C = J[m] ?? t.source ?? "User", v = C.toLowerCase() === "user";
|
|
105
|
+
return {
|
|
106
|
+
id: t.id,
|
|
107
|
+
command: t.label,
|
|
108
|
+
description: t.description || "",
|
|
109
|
+
category: t.category || "General",
|
|
110
|
+
keybinding: u,
|
|
111
|
+
keybindingDisplay: u ? H(u) : "",
|
|
112
|
+
scope: t.scope,
|
|
113
|
+
source: C,
|
|
114
|
+
sourceColor: v ? "primary" : "neutral",
|
|
115
|
+
sourceIcon: v ? /* @__PURE__ */ r(L, { className: "h-3 w-3" }) : /* @__PURE__ */ r(F, { className: "h-3 w-3" }),
|
|
116
|
+
customisable: t.customisable !== !1,
|
|
117
|
+
passive: t.passive === !0,
|
|
118
|
+
hasOverride: t.keybinding !== void 0,
|
|
119
|
+
_def: t
|
|
120
|
+
};
|
|
121
|
+
});
|
|
122
|
+
}, [A, e, c]), x = n.useCallback((s) => {
|
|
123
|
+
f(s), i(null);
|
|
124
|
+
}, []), y = n.useCallback((s) => {
|
|
125
|
+
b(s), i(null);
|
|
126
|
+
}, [b]);
|
|
127
|
+
n.useEffect(() => {
|
|
128
|
+
if (!o) return;
|
|
129
|
+
const s = (t) => {
|
|
130
|
+
if (t.preventDefault(), t.stopPropagation(), t.key === "Escape") {
|
|
131
|
+
f(null), i(null);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const u = V(t);
|
|
135
|
+
if (!u) return;
|
|
136
|
+
if (W(u)) {
|
|
137
|
+
i({ type: "reserved", text: "This shortcut is reserved by the browser and cannot be used" }), f(null), setTimeout(() => i(null), 3e3);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const m = g(o, u);
|
|
141
|
+
m.success ? (f(null), i(null)) : m.conflict ? (i({
|
|
142
|
+
type: "conflict",
|
|
143
|
+
text: `Conflicts with "${m.conflict.conflictingLabel}" in ${m.conflict.scope} scope`
|
|
144
|
+
}), f(null), setTimeout(() => i(null), 3e3)) : m.reserved && (i({ type: "reserved", text: "This shortcut is reserved by the browser" }), f(null), setTimeout(() => i(null), 3e3));
|
|
145
|
+
};
|
|
146
|
+
return window.addEventListener("keydown", s, !0), () => window.removeEventListener("keydown", s, !0);
|
|
147
|
+
}, [o, g]);
|
|
148
|
+
const P = n.useMemo(() => [
|
|
149
|
+
{
|
|
150
|
+
field: "command",
|
|
151
|
+
headerName: "Command",
|
|
152
|
+
flex: 1,
|
|
153
|
+
minWidth: 200,
|
|
154
|
+
filter: "agTextColumnFilter",
|
|
155
|
+
cellRenderer: Q,
|
|
156
|
+
filterValueGetter: (s) => {
|
|
157
|
+
const t = s.data;
|
|
158
|
+
return t ? `${t.command} ${t.description}` : "";
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
field: "category",
|
|
163
|
+
headerName: "Category",
|
|
164
|
+
width: 140,
|
|
165
|
+
filter: "agSetColumnFilter"
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
field: "keybindingDisplay",
|
|
169
|
+
headerName: "Shortcut",
|
|
170
|
+
width: 180,
|
|
171
|
+
type: "rightAligned",
|
|
172
|
+
filter: "agTextColumnFilter",
|
|
173
|
+
cellRenderer: X,
|
|
174
|
+
cellClass: S,
|
|
175
|
+
cellRendererParams: {
|
|
176
|
+
recordingCommandId: o,
|
|
177
|
+
onStartRecording: x,
|
|
178
|
+
onReset: y
|
|
179
|
+
},
|
|
180
|
+
sortable: !0
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
field: "scope",
|
|
184
|
+
headerName: "Scope",
|
|
185
|
+
width: 100,
|
|
186
|
+
filter: "agSetColumnFilter",
|
|
187
|
+
cellRenderer: Y
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
field: "source",
|
|
191
|
+
headerName: "Source",
|
|
192
|
+
width: 130,
|
|
193
|
+
filter: "agSetColumnFilter",
|
|
194
|
+
cellClass: S,
|
|
195
|
+
cellRenderer: q,
|
|
196
|
+
cellRendererParams: {
|
|
197
|
+
variant: "subtle",
|
|
198
|
+
colorField: "sourceColor",
|
|
199
|
+
isIcon: !0,
|
|
200
|
+
iconField: "sourceIcon",
|
|
201
|
+
size: "small"
|
|
202
|
+
},
|
|
203
|
+
headerComponentParams: {
|
|
204
|
+
isSearchable: !0,
|
|
205
|
+
advanceSearchEnabled: !0,
|
|
206
|
+
selectOptions: [
|
|
207
|
+
{ label: "System", value: "System" },
|
|
208
|
+
{ label: "User", value: "User" },
|
|
209
|
+
{ label: "AG Grid", value: "AG Grid" },
|
|
210
|
+
{ label: "Browser", value: "Browser" }
|
|
211
|
+
],
|
|
212
|
+
isMultiSelect: !0
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
], [o, x, y]), T = n.useMemo(() => ({
|
|
216
|
+
filter: {
|
|
217
|
+
filterModel: {
|
|
218
|
+
source: {
|
|
219
|
+
filterType: "set",
|
|
220
|
+
values: ["System", "User", "AG Grid"]
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}), []), D = n.useMemo(() => ({
|
|
225
|
+
sortable: !0,
|
|
226
|
+
resizable: !0,
|
|
227
|
+
filter: !0,
|
|
228
|
+
headerComponentParams: {
|
|
229
|
+
isSearchable: !0,
|
|
230
|
+
advanceSearchEnabled: !0
|
|
231
|
+
}
|
|
232
|
+
}), []), w = d || o ? /* @__PURE__ */ h("div", { className: p(
|
|
233
|
+
"px-3 py-1.5 text-[10px] md:text-sm font-medium rounded-md whitespace-nowrap flex items-center gap-2",
|
|
234
|
+
d?.type === "conflict" ? "bg-[#fef3c7] text-[#92400e]" : d?.type === "reserved" ? "bg-[#fee2e2] text-[#991b1b]" : o ? "bg-[#eff6ff] text-[#1e40af] animate-[pulse_1.5s_ease-in-out_infinite]" : ""
|
|
235
|
+
), children: [
|
|
236
|
+
d?.type === "conflict" ? /* @__PURE__ */ r(I, { className: "h-3.5 w-3.5 shrink-0" }) : d?.type === "reserved" ? /* @__PURE__ */ r(R, { className: "h-3.5 w-3.5 shrink-0" }) : o ? /* @__PURE__ */ r("span", { className: "shrink-0 text-base", children: "⌨️" }) : null,
|
|
237
|
+
/* @__PURE__ */ r("span", { children: d ? d.text : /* @__PURE__ */ h(N, { children: [
|
|
238
|
+
/* @__PURE__ */ r("strong", { children: "Recording:" }),
|
|
239
|
+
" Press shortcut or ",
|
|
240
|
+
/* @__PURE__ */ r("strong", { children: "Esc" })
|
|
241
|
+
] }) })
|
|
242
|
+
] }) : null;
|
|
243
|
+
return /* @__PURE__ */ h(z, { className: p("flex flex-col w-full h-full border-t-0", l), ...k, children: [
|
|
244
|
+
a ? a(w) : w,
|
|
245
|
+
/* @__PURE__ */ r(
|
|
246
|
+
B,
|
|
247
|
+
{
|
|
248
|
+
rowData: G,
|
|
249
|
+
columnDefs: P,
|
|
250
|
+
defaultColDef: D,
|
|
251
|
+
initialState: T,
|
|
252
|
+
rowHeight: 44,
|
|
253
|
+
animateRows: !1,
|
|
254
|
+
rowSelection: "single",
|
|
255
|
+
suppressCellFocus: !0,
|
|
256
|
+
getRowId: (s) => s.data.id,
|
|
257
|
+
suppressRowClickSelection: !0,
|
|
258
|
+
domLayout: "normal"
|
|
259
|
+
}
|
|
260
|
+
)
|
|
261
|
+
] });
|
|
262
|
+
}
|
|
263
|
+
Z.displayName = "ShortcutSettings";
|
|
264
|
+
export {
|
|
265
|
+
Z as ShortcutSettings
|
|
266
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useBrowserShortcuts — Registers all browser-reserved shortcuts as passive,
|
|
3
|
+
* non-customisable commands so they appear in the command palette and settings
|
|
4
|
+
* panel. This lets users discover what shortcuts are browser-reserved and why
|
|
5
|
+
* they can't be overridden.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* // Show all browser shortcuts
|
|
9
|
+
* useBrowserShortcuts();
|
|
10
|
+
*
|
|
11
|
+
* // Hide DevTools shortcuts from the palette
|
|
12
|
+
* useBrowserShortcuts({ exclude: ['devtools'] });
|
|
13
|
+
*
|
|
14
|
+
* // Hide entire categories
|
|
15
|
+
* useBrowserShortcuts({ exclude: ['System', 'Address Bar'] });
|
|
16
|
+
*
|
|
17
|
+
* // Hide specific shortcuts by their label (partial match, case-insensitive)
|
|
18
|
+
* useBrowserShortcuts({ exclude: ['devtools', 'caret browsing'] });
|
|
19
|
+
*/
|
|
20
|
+
export interface UseBrowserShortcutsOptions {
|
|
21
|
+
/**
|
|
22
|
+
* Patterns to exclude from the palette. Each pattern is matched against
|
|
23
|
+
* the shortcut's label AND category (case-insensitive, partial match).
|
|
24
|
+
*
|
|
25
|
+
* Examples:
|
|
26
|
+
* - `['devtools']` hides all DevTools-related shortcuts
|
|
27
|
+
* - `['System']` hides entire System category
|
|
28
|
+
* - `['devtools', 'caret browsing', 'page source']` hides specific items
|
|
29
|
+
*/
|
|
30
|
+
exclude?: string[];
|
|
31
|
+
}
|
|
32
|
+
export declare function useBrowserShortcuts(options?: UseBrowserShortcutsOptions): void;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as u from "react";
|
|
2
|
+
import { useCommandPalette as f } from "./command-palette-context.js";
|
|
3
|
+
import { BROWSER_SHORTCUTS as d } from "./utils.js";
|
|
4
|
+
function m(r) {
|
|
5
|
+
const s = r.split("+"), e = { key: "" };
|
|
6
|
+
for (const t of s)
|
|
7
|
+
t === "meta" ? e.meta = !0 : t === "ctrl" ? e.ctrl = !0 : t === "alt" ? e.alt = !0 : t === "shift" ? e.shift = !0 : e.key = t;
|
|
8
|
+
return e;
|
|
9
|
+
}
|
|
10
|
+
function b(r) {
|
|
11
|
+
const { registerCommand: s, unregisterCommand: e } = f(), t = r?.exclude;
|
|
12
|
+
u.useEffect(() => {
|
|
13
|
+
const c = [];
|
|
14
|
+
for (const o of d) {
|
|
15
|
+
if (t?.length) {
|
|
16
|
+
const a = `${o.label} ${o.category}`.toLowerCase();
|
|
17
|
+
if (t.some(
|
|
18
|
+
(l) => a.includes(l.toLowerCase())
|
|
19
|
+
)) continue;
|
|
20
|
+
}
|
|
21
|
+
const n = `browser.${o.shortcut.replace(/\+/g, "-")}`;
|
|
22
|
+
c.push(n);
|
|
23
|
+
const i = {
|
|
24
|
+
id: n,
|
|
25
|
+
label: o.label,
|
|
26
|
+
description: `Browser shortcut (${o.category})`,
|
|
27
|
+
category: o.category,
|
|
28
|
+
scope: "global",
|
|
29
|
+
defaultKeybinding: m(o.shortcut),
|
|
30
|
+
customisable: !1,
|
|
31
|
+
hidden: !1,
|
|
32
|
+
handler: () => {
|
|
33
|
+
},
|
|
34
|
+
// no-op — browser handles these
|
|
35
|
+
source: "browser",
|
|
36
|
+
passive: !0
|
|
37
|
+
};
|
|
38
|
+
s(i);
|
|
39
|
+
}
|
|
40
|
+
return () => {
|
|
41
|
+
for (const o of c)
|
|
42
|
+
e(o);
|
|
43
|
+
};
|
|
44
|
+
}, [s, e, JSON.stringify(t)]);
|
|
45
|
+
}
|
|
46
|
+
export {
|
|
47
|
+
b as useBrowserShortcuts
|
|
48
|
+
};
|