@adaptabletools/adaptable 23.0.5-canary.1 → 23.0.6

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.
Files changed (35) hide show
  1. package/index.css +10 -1
  2. package/package.json +1 -1
  3. package/src/AdaptableOptions/AdaptableOptions.d.ts +9 -0
  4. package/src/AdaptableOptions/DefaultAdaptableOptions.d.ts +17 -0
  5. package/src/AdaptableOptions/DefaultAdaptableOptions.js +7 -46
  6. package/src/AdaptableOptions/EditOptions.d.ts +10 -2
  7. package/src/AdaptableOptions/SettingsPanelOptions.d.ts +3 -28
  8. package/src/AdaptableOptions/WizardOptions.d.ts +26 -0
  9. package/src/AdaptableOptions/WizardOptions.js +1 -0
  10. package/src/AdaptableState/Common/CustomWindowConfig.d.ts +2 -14
  11. package/src/AdaptableState/Common/Window.d.ts +33 -0
  12. package/src/AdaptableState/Common/Window.js +1 -0
  13. package/src/AdaptableState/InitialState.d.ts +2 -1
  14. package/src/AdaptableState/PlusMinusState.d.ts +12 -2
  15. package/src/Strategy/PlusMinusModule.js +5 -3
  16. package/src/Utilities/Extensions/ObjectExtensions.d.ts +16 -0
  17. package/src/Utilities/Extensions/ObjectExtensions.js +41 -0
  18. package/src/Utilities/Helpers/PlusMinusHelper.d.ts +62 -9
  19. package/src/Utilities/Helpers/PlusMinusHelper.js +180 -25
  20. package/src/View/Components/Popups/AdaptablePopup/AdaptablePopup.js +5 -1
  21. package/src/View/Components/Popups/AdaptablePopup/AdaptablePopupDialog.js +7 -3
  22. package/src/View/Components/Popups/Utilities.d.ts +11 -0
  23. package/src/View/Components/Popups/Utilities.js +8 -0
  24. package/src/View/PlusMinus/Wizard/KeyComboCaptureInput.d.ts +30 -0
  25. package/src/View/PlusMinus/Wizard/KeyComboCaptureInput.js +161 -0
  26. package/src/View/PlusMinus/Wizard/PlusMinusSettingsWizardSection.js +30 -60
  27. package/src/View/Wizard/OnePageWizards.js +27 -2
  28. package/src/agGrid/AdaptableAgGrid.js +2 -0
  29. package/src/env.js +2 -2
  30. package/src/migration/DeprecatedStateKeys.d.ts +37 -0
  31. package/src/migration/DeprecatedStateKeys.js +42 -0
  32. package/src/migration/VersionUpgrade23.d.ts +9 -0
  33. package/src/migration/VersionUpgrade23.js +1 -2
  34. package/src/types.d.ts +4 -2
  35. package/tsconfig.esm.tsbuildinfo +1 -1
@@ -1,5 +1,12 @@
1
+ import { eventMatchesKeyboardShortcut } from '@infinite-table/infinite-react';
1
2
  const DEFAULT_INCREMENT_KEY = '+';
2
3
  const DEFAULT_DECREMENT_KEY = '-';
4
+ const MODIFIER_TOKENS = new Set(['cmd', 'meta', 'ctrl', 'control', 'alt', 'shift']);
5
+ const MODIFIER_ALIASES = { control: 'ctrl', meta: 'cmd' };
6
+ const KEY_ALIASES = { esc: 'escape' };
7
+ function isPlusMinusCombination(trigger) {
8
+ return trigger.length > 1;
9
+ }
3
10
  export function resolvePlusMinusTriggerKeysForNudge(nudge, globalOptions) {
4
11
  const incrementKey = nudge.IncrementKey?.trim() || plusMinusIncrementKeyFromOptionsOnly(globalOptions);
5
12
  const decrementKey = nudge.DecrementKey?.trim() || plusMinusDecrementKeyFromOptionsOnly(globalOptions);
@@ -7,25 +14,32 @@ export function resolvePlusMinusTriggerKeysForNudge(nudge, globalOptions) {
7
14
  }
8
15
  export function plusMinusResolvedKeysConflict(nudge, globalOptions) {
9
16
  const { incrementKey, decrementKey } = resolvePlusMinusTriggerKeysForNudge(nudge, globalOptions);
10
- if (incrementKey === decrementKey) {
11
- return true;
12
- }
13
- if (incrementKey.length === 1 && decrementKey.length === 1) {
14
- return incrementKey.toLowerCase() === decrementKey.toLowerCase();
17
+ const normalizedIncrement = normalizePlusMinusTrigger(incrementKey);
18
+ const normalizedDecrement = normalizePlusMinusTrigger(decrementKey);
19
+ if (!normalizedIncrement || !normalizedDecrement) {
20
+ return false;
15
21
  }
16
- return false;
22
+ return normalizedIncrement === normalizedDecrement;
17
23
  }
18
- export function plusMinusKeyMatches(actualKey, configuredKey) {
19
- if (!configuredKey) {
24
+ export function plusMinusEventMatchesTrigger(event, configuredKey) {
25
+ const trigger = (configuredKey ?? '').trim();
26
+ if (!trigger) {
20
27
  return false;
21
28
  }
22
- if (actualKey === configuredKey) {
23
- return true;
29
+ const pressedKey = event?.key;
30
+ if (!pressedKey) {
31
+ return false;
24
32
  }
25
- if (configuredKey.length === 1 && actualKey.length === 1) {
26
- return actualKey.toLowerCase() === configuredKey.toLowerCase();
33
+ if (!isPlusMinusCombination(trigger)) {
34
+ return pressedKey.length === 1 && pressedKey.toLowerCase() === trigger.toLowerCase();
27
35
  }
28
- return false;
36
+ return eventMatchesKeyboardShortcut({
37
+ key: pressedKey,
38
+ metaKey: !!event.metaKey,
39
+ altKey: !!event.altKey,
40
+ ctrlKey: !!event.ctrlKey,
41
+ shiftKey: !!event.shiftKey,
42
+ }, trigger);
29
43
  }
30
44
  export function plusMinusIncrementKeyFromOptionsOnly(globalOptions) {
31
45
  return globalOptions?.incrementKey?.trim() || DEFAULT_INCREMENT_KEY;
@@ -33,24 +47,165 @@ export function plusMinusIncrementKeyFromOptionsOnly(globalOptions) {
33
47
  export function plusMinusDecrementKeyFromOptionsOnly(globalOptions) {
34
48
  return globalOptions?.decrementKey?.trim() || DEFAULT_DECREMENT_KEY;
35
49
  }
50
+ export function normalizePlusMinusTrigger(trigger) {
51
+ const value = (trigger ?? '').trim();
52
+ if (!value) {
53
+ return '';
54
+ }
55
+ if (!isPlusMinusCombination(value)) {
56
+ return value.toLowerCase();
57
+ }
58
+ const parts = value
59
+ .split('+')
60
+ .map((part) => part.trim().toLowerCase())
61
+ .filter(Boolean);
62
+ const modifiers = [];
63
+ const keys = [];
64
+ for (const part of parts) {
65
+ if (MODIFIER_TOKENS.has(part)) {
66
+ modifiers.push(MODIFIER_ALIASES[part] ?? part);
67
+ }
68
+ else {
69
+ keys.push(KEY_ALIASES[part] ?? part);
70
+ }
71
+ }
72
+ modifiers.sort();
73
+ return [...modifiers, ...keys].join('+');
74
+ }
36
75
  export function plusMinusTriggerSameAsBaseline(candidate, baseline) {
37
- return plusMinusKeyMatches(candidate, baseline) && plusMinusKeyMatches(baseline, candidate);
76
+ return normalizePlusMinusTrigger(candidate) === normalizePlusMinusTrigger(baseline);
77
+ }
78
+ const MODIFIER_KEY_NAMES = new Set([
79
+ 'Shift',
80
+ 'Control',
81
+ 'Alt',
82
+ 'Meta',
83
+ 'OS',
84
+ 'AltGraph',
85
+ 'CapsLock',
86
+ 'NumLock',
87
+ 'ScrollLock',
88
+ 'Hyper',
89
+ 'Super',
90
+ 'Fn',
91
+ 'FnLock',
92
+ 'Dead',
93
+ ]);
94
+ function activeModifierTokens(event) {
95
+ const tokens = [];
96
+ if (event.ctrlKey) {
97
+ tokens.push('ctrl');
98
+ }
99
+ if (event.altKey) {
100
+ tokens.push('alt');
101
+ }
102
+ if (event.metaKey) {
103
+ tokens.push('cmd');
104
+ }
105
+ if (event.shiftKey) {
106
+ tokens.push('shift');
107
+ }
108
+ return tokens;
109
+ }
110
+ export function capturePlusMinusTriggerFromEvent(event) {
111
+ const key = event?.key;
112
+ const modifiers = activeModifierTokens(event);
113
+ if (!key || MODIFIER_KEY_NAMES.has(key)) {
114
+ return { kind: 'pending', modifiers };
115
+ }
116
+ const isPrintable = key.length === 1;
117
+ const keyPart = isPrintable ? key.toLowerCase() : key;
118
+ if (!modifiers.length) {
119
+ return { kind: 'captured', trigger: keyPart };
120
+ }
121
+ const shiftOnly = modifiers.length === 1 && modifiers[0] === 'shift';
122
+ const isLetter = isPrintable && /^[a-z]$/.test(keyPart);
123
+ if (isPrintable && shiftOnly && !isLetter) {
124
+ return { kind: 'captured', trigger: keyPart };
125
+ }
126
+ if (keyPart.includes('+')) {
127
+ return { kind: 'invalid', reason: 'This key cannot be combined with modifiers.' };
128
+ }
129
+ return { kind: 'captured', trigger: [...modifiers, keyPart].join('+') };
130
+ }
131
+ const KEY_DISPLAY_LABELS = {
132
+ ctrl: 'Ctrl',
133
+ cmd: 'Cmd',
134
+ alt: 'Alt',
135
+ shift: 'Shift',
136
+ enter: 'Enter',
137
+ escape: 'Esc',
138
+ esc: 'Esc',
139
+ tab: 'Tab',
140
+ space: 'Space',
141
+ backspace: 'Backspace',
142
+ delete: 'Delete',
143
+ insert: 'Insert',
144
+ arrowup: '↑',
145
+ arrowdown: '↓',
146
+ arrowleft: '←',
147
+ arrowright: '→',
148
+ pageup: 'Page Up',
149
+ pagedown: 'Page Down',
150
+ home: 'Home',
151
+ end: 'End',
152
+ };
153
+ export function plusMinusKeyLabel(token) {
154
+ const value = (token ?? '').trim();
155
+ if (!value) {
156
+ return '';
157
+ }
158
+ const lower = value.toLowerCase();
159
+ if (KEY_DISPLAY_LABELS[lower]) {
160
+ return KEY_DISPLAY_LABELS[lower];
161
+ }
162
+ if (value.length === 1) {
163
+ return value;
164
+ }
165
+ return value.charAt(0).toUpperCase() + value.slice(1);
166
+ }
167
+ export function plusMinusTriggerDisplayParts(trigger) {
168
+ const value = (trigger ?? '').trim();
169
+ if (!value) {
170
+ return [];
171
+ }
172
+ if (!isPlusMinusCombination(value)) {
173
+ return [plusMinusKeyLabel(value)];
174
+ }
175
+ return value
176
+ .split('+')
177
+ .map((part) => part.trim())
178
+ .filter(Boolean)
179
+ .map(plusMinusKeyLabel);
38
180
  }
39
181
  function addConfiguredKeyToLookup(lookup, configuredKey) {
40
- if (!configuredKey) {
182
+ const trigger = (configuredKey ?? '').trim();
183
+ if (!trigger) {
41
184
  return;
42
185
  }
43
- if (configuredKey.length === 1) {
44
- lookup.singleCharLower.add(configuredKey.toLowerCase());
186
+ if (!isPlusMinusCombination(trigger)) {
187
+ lookup.effectiveKeysLower.add(trigger.toLowerCase());
188
+ return;
45
189
  }
46
- else {
47
- lookup.exactKeys.add(configuredKey);
190
+ const parts = trigger
191
+ .split('+')
192
+ .map((part) => part.trim().toLowerCase())
193
+ .filter(Boolean);
194
+ for (const part of parts) {
195
+ if (MODIFIER_TOKENS.has(part)) {
196
+ continue;
197
+ }
198
+ if (part === '*') {
199
+ lookup.matchAnyKey = true;
200
+ continue;
201
+ }
202
+ lookup.effectiveKeysLower.add(KEY_ALIASES[part] ?? part);
48
203
  }
49
204
  }
50
205
  export function buildPlusMinusTriggerKeyLookup(nudges, globalOptions) {
51
206
  const lookup = {
52
- singleCharLower: new Set(),
53
- exactKeys: new Set(),
207
+ effectiveKeysLower: new Set(),
208
+ matchAnyKey: false,
54
209
  };
55
210
  for (const nudge of nudges) {
56
211
  const { incrementKey, decrementKey } = resolvePlusMinusTriggerKeysForNudge(nudge, globalOptions);
@@ -60,11 +215,11 @@ export function buildPlusMinusTriggerKeyLookup(nudges, globalOptions) {
60
215
  return lookup;
61
216
  }
62
217
  export function plusMinusKeyCouldTrigger(keyEventString, lookup) {
218
+ if (lookup.matchAnyKey) {
219
+ return true;
220
+ }
63
221
  if (!keyEventString) {
64
222
  return false;
65
223
  }
66
- if (keyEventString.length === 1) {
67
- return lookup.singleCharLower.has(keyEventString.toLowerCase());
68
- }
69
- return lookup.exactKeys.has(keyEventString);
224
+ return lookup.effectiveKeysLower.has(keyEventString.toLowerCase());
70
225
  }
@@ -16,9 +16,9 @@ import { Box } from '../../../../components/Flex';
16
16
  export const AdaptablePopup = (props) => {
17
17
  const adaptable = useAdaptable();
18
18
  const settingsPanelOptions = adaptable.adaptableOptions.settingsPanelOptions;
19
+ const wizardOptions = adaptable.adaptableOptions.wizardOptions;
19
20
  const settingsPanelTitle = adaptable.ModuleService.getModuleFriendlyName('SettingsPanel');
20
21
  const menuItems = useMenuItems();
21
- const isWindowModal = settingsPanelOptions.popupType === 'window';
22
22
  const modalContainer = resolveContainerElement(adaptable.adaptableOptions?.containerOptions?.modalContainer, props.api.internalApi.buildBaseContext());
23
23
  let friendlyName = null;
24
24
  let isStandalone = false;
@@ -54,6 +54,10 @@ export const AdaptablePopup = (props) => {
54
54
  activeItem = moduleInfo.ModuleName;
55
55
  moduleViewContent = (_jsx(AdaptablePopupBody, { api: props.api, module: module, componentName: props.componentName, moduleParams: props.moduleParams, onClearParams: props.onClearParams, onHide: props.onHide }));
56
56
  }
57
+ const effectivePopupType = isStandalone
58
+ ? wizardOptions?.popupType ?? settingsPanelOptions.popupType
59
+ : settingsPanelOptions.popupType;
60
+ const isWindowModal = effectivePopupType === 'window';
57
61
  const baseClassName = 'ab-Adaptable-Popup';
58
62
  const className = join(baseClassName, `${baseClassName}--${isWindowModal ? 'window' : 'modal'}`, isStandalone ? `${baseClassName}--action-popup` : `${baseClassName}--settings-popup`);
59
63
  return (_jsx(PopupContext.Provider, { value: {
@@ -11,17 +11,21 @@ const WindowDialog = (props) => {
11
11
  const adaptable = useAdaptable();
12
12
  const dispatch = useDispatch();
13
13
  const settingsPanelOptions = adaptable.adaptableOptions.settingsPanelOptions;
14
+ const wizardOptions = adaptable.adaptableOptions.wizardOptions;
14
15
  let popupSettings = useSelector((state) => state?.Internal?.SettingsPanel?.[settingsPanelOptionsKey]);
15
16
  const handleWindowSettings = (settings) => {
16
17
  dispatch(SettingsPanelSet(settingsPanelOptionsKey, settings));
17
18
  };
18
19
  if (!popupSettings) {
19
20
  const size = isActionModule
20
- ? getActionPanelSize()
21
+ ? wizardOptions?.size ?? getActionPanelSize()
21
22
  : settingsPanelOptions.size ?? getSettingsPanelSize();
23
+ const position = isActionModule
24
+ ? wizardOptions?.position ?? getMiddlePosition(size)
25
+ : settingsPanelOptions.position ?? getMiddlePosition(size);
22
26
  popupSettings = {
23
- size: size,
24
- position: settingsPanelOptions.position ?? getMiddlePosition(size),
27
+ size,
28
+ position,
25
29
  };
26
30
  }
27
31
  const windowModalProps = {
@@ -13,6 +13,17 @@ export declare const getSettingsPanelSize: () => {
13
13
  height: number;
14
14
  width: number;
15
15
  };
16
+ /**
17
+ * Default size for the One Page Wizard when it renders as a window (i.e.
18
+ * `wizardOptions.popupType === 'window'` and the user has not provided an
19
+ * explicit `wizardOptions.size`). Mirrors the historical modal sizing of
20
+ * 90vh tall and 90vw wide, capped at 1200px wide so the wizard stays
21
+ * readable on very large monitors.
22
+ */
23
+ export declare const getWizardSize: () => {
24
+ height: number;
25
+ width: number;
26
+ };
16
27
  export declare const getWindowPopupSize: () => {
17
28
  height: number;
18
29
  width: number;
@@ -23,6 +23,14 @@ export const getSettingsPanelSize = () => {
23
23
  width: clamp(width * 0.9, 0, 800),
24
24
  };
25
25
  };
26
+ export const getWizardSize = () => {
27
+ const height = window.innerHeight;
28
+ const width = window.innerWidth;
29
+ return {
30
+ height: height * 0.9,
31
+ width: clamp(width * 0.9, 0, 1200),
32
+ };
33
+ };
26
34
  export const getWindowPopupSize = () => {
27
35
  return getSettingsPanelSize();
28
36
  };
@@ -0,0 +1,30 @@
1
+ import * as React from 'react';
2
+ export interface KeyComboCaptureInputProps {
3
+ /** Resolved trigger to display when not recording (e.g. `+` or `shift+Enter`). */
4
+ value: string;
5
+ /** When true, `value` comes from the default/global setting (shown as a hint). */
6
+ isUsingDefault?: boolean;
7
+ /** Hint shown when there is no value and the field is not recording. */
8
+ placeholder?: string;
9
+ disabled?: boolean;
10
+ 'data-name'?: string;
11
+ /** Called with a complete, valid trigger once the user records one. */
12
+ onCapture: (trigger: string) => void;
13
+ /** Reset back to the default/global trigger (clears the per-nudge override). */
14
+ onReset?: () => void;
15
+ /**
16
+ * Optional validation run on a captured trigger before committing.
17
+ * Return an error message to reject the capture (e.g. increment/decrement clash).
18
+ */
19
+ validateCapture?: (trigger: string) => string | undefined;
20
+ }
21
+ /**
22
+ * VS Code-style keyboard shortcut recorder. While focused the field shows the
23
+ * current value and a "Hit Enter to edit" hint; pressing Enter enters edit mode
24
+ * (the value is cleared) where the next key press / combination is detected from
25
+ * the event. A valid press is persisted and edit mode ends; incomplete
26
+ * (modifier-only) and unrepresentable presses are rejected, and an optional
27
+ * `validateCapture` guards against clashes. The revert button restores the
28
+ * default once a custom value has been set.
29
+ */
30
+ export declare const KeyComboCaptureInput: React.FunctionComponent<KeyComboCaptureInputProps>;
@@ -0,0 +1,161 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from 'react';
3
+ import { cn } from '../../../lib/utils';
4
+ import { Box, Flex } from '../../../components/Flex';
5
+ import SimpleButton from '../../../components/SimpleButton';
6
+ import { capturePlusMinusTriggerFromEvent, plusMinusKeyLabel, plusMinusTriggerDisplayParts, } from '../../../Utilities/Helpers/PlusMinusHelper';
7
+ import { targetOwn } from '../../../components/twUtils';
8
+ const Kbd = ({ children }) => (_jsx(Box, { as: "kbd", className: "twa:px-2 twa:py-[2px] twa:rounded-standard twa:bg-primary twa:text-2 twa:leading-none twa:font-medium twa:whitespace-nowrap", children: children }));
9
+ const renderTokens = (tokens) => tokens.map((token, index) => (_jsxs(React.Fragment, { children: [index > 0 ? _jsx(Box, { className: "twa:opacity-50 twa:text-2", children: "+" }) : null, _jsx(Kbd, { children: token })] }, `${token}-${index}`)));
10
+ export const KeyComboCaptureInput = (props) => {
11
+ const { value, isUsingDefault, placeholder, disabled, onCapture, onReset, validateCapture } = props;
12
+ const ref = React.useRef(null);
13
+ const [focused, setFocused] = React.useState(false);
14
+ const [editing, setEditing] = React.useState(false);
15
+ const [pendingModifiers, setPendingModifiers] = React.useState([]);
16
+ const [staged, setStaged] = React.useState(undefined);
17
+ const [error, setError] = React.useState(undefined);
18
+ const stopEditing = React.useCallback(() => {
19
+ setEditing(false);
20
+ setPendingModifiers([]);
21
+ setStaged(undefined);
22
+ }, []);
23
+ const handleKeyDown = (event) => {
24
+ if (disabled) {
25
+ return;
26
+ }
27
+ if (!editing) {
28
+ if (event.key === 'Enter') {
29
+ event.preventDefault();
30
+ event.stopPropagation();
31
+ setError(undefined);
32
+ setPendingModifiers([]);
33
+ setStaged(undefined);
34
+ setEditing(true);
35
+ }
36
+ return;
37
+ }
38
+ if (event.key === 'Escape') {
39
+ event.preventDefault();
40
+ event.stopPropagation();
41
+ setError(undefined);
42
+ stopEditing();
43
+ return;
44
+ }
45
+ if (event.key === 'Tab') {
46
+ if (staged && !validateCapture?.(staged)) {
47
+ onCapture(staged);
48
+ }
49
+ stopEditing();
50
+ setError(undefined);
51
+ return;
52
+ }
53
+ event.preventDefault();
54
+ event.stopPropagation();
55
+ const noModifiers = !event.ctrlKey && !event.altKey && !event.metaKey && !event.shiftKey;
56
+ if (event.key === 'Enter' && noModifiers) {
57
+ if (!staged) {
58
+ return;
59
+ }
60
+ const validationError = validateCapture?.(staged);
61
+ if (validationError) {
62
+ setError(validationError);
63
+ return;
64
+ }
65
+ const confirmed = staged;
66
+ setError(undefined);
67
+ stopEditing();
68
+ onCapture(confirmed);
69
+ return;
70
+ }
71
+ const result = capturePlusMinusTriggerFromEvent({
72
+ key: event.key,
73
+ ctrlKey: event.ctrlKey,
74
+ altKey: event.altKey,
75
+ metaKey: event.metaKey,
76
+ shiftKey: event.shiftKey,
77
+ });
78
+ if (result.kind === 'pending') {
79
+ setPendingModifiers(result.modifiers);
80
+ setError(undefined);
81
+ return;
82
+ }
83
+ if (result.kind === 'invalid') {
84
+ setPendingModifiers([]);
85
+ setStaged(undefined);
86
+ setError(result.reason);
87
+ return;
88
+ }
89
+ const validationError = validateCapture?.(result.trigger);
90
+ if (validationError) {
91
+ setPendingModifiers([]);
92
+ setStaged(undefined);
93
+ setError(validationError);
94
+ return;
95
+ }
96
+ setPendingModifiers([]);
97
+ setError(undefined);
98
+ setStaged(result.trigger);
99
+ };
100
+ const handleReset = (event) => {
101
+ event.preventDefault();
102
+ event.stopPropagation();
103
+ setError(undefined);
104
+ stopEditing();
105
+ onReset?.();
106
+ };
107
+ const handleEnterEdit = (event) => {
108
+ event.preventDefault();
109
+ event.stopPropagation();
110
+ setError(undefined);
111
+ setPendingModifiers([]);
112
+ setStaged(undefined);
113
+ setEditing(true);
114
+ ref.current?.focus();
115
+ };
116
+ const valueParts = plusMinusTriggerDisplayParts(value);
117
+ let content;
118
+ if (editing) {
119
+ if (staged) {
120
+ content = (_jsx(Flex, { className: "twa:items-center twa:gap-1", children: renderTokens(plusMinusTriggerDisplayParts(staged)) }));
121
+ }
122
+ else if (pendingModifiers.length) {
123
+ content = (_jsxs(Flex, { className: "twa:items-center twa:gap-1", children: [renderTokens(pendingModifiers.map(plusMinusKeyLabel)), _jsx(Box, { className: "twa:opacity-50 twa:text-2", children: "+ \u2026" })] }));
124
+ }
125
+ else {
126
+ content = _jsx(Box, { className: "twa:opacity-60 twa:text-2", children: "Press a key combination\u2026" });
127
+ }
128
+ }
129
+ else if (valueParts.length) {
130
+ content = (_jsxs(Flex, { className: "twa:items-center twa:gap-1", children: [renderTokens(valueParts), isUsingDefault ? (_jsx(Box, { className: "twa:opacity-60 twa:text-2 twa:ml-1", children: "(default)" })) : null] }));
131
+ }
132
+ else {
133
+ content = _jsx(Box, { className: "twa:opacity-60 twa:text-2", children: placeholder ?? 'Hit Enter to edit' });
134
+ }
135
+ return (_jsxs(Flex, { flexDirection: "column", className: "twa:gap-1 twa:max-w-[300px] twa:w-full", children: [_jsxs(Flex, { ref: ref, role: "button", tabIndex: disabled ? -1 : 0, "data-name": props['data-name'], "aria-label": "Record keyboard shortcut", onFocus: () => {
136
+ if (!disabled) {
137
+ setFocused(true);
138
+ }
139
+ }, onBlur: (event) => {
140
+ if (ref.current && event.relatedTarget && ref.current.contains(event.relatedTarget)) {
141
+ return;
142
+ }
143
+ if (editing && staged && !validateCapture?.(staged)) {
144
+ onCapture(staged);
145
+ }
146
+ setFocused(false);
147
+ stopEditing();
148
+ setError(undefined);
149
+ }, onClick: () => ref.current?.focus(), onKeyDown: handleKeyDown, className: cn('twa:min-h-input twa:rounded-input twa:px-2 twa:items-center twa:gap-2 twa:w-full', 'twa:border-input twa:border', targetOwn.focusWithinOutline, disabled
150
+ ? 'twa:opacity-60 twa:cursor-not-allowed'
151
+ : 'twa:cursor-pointer twa:bg-input-background', editing ? 'twa:border-accent' : '', error ? 'twa:border-destructive' : ''), children: [_jsx(Flex, { className: "twa:flex-1 twa:flex-wrap twa:items-center twa:gap-1 twa:py-1", children: content }), !disabled && !editing ? (onReset && !isUsingDefault ? (_jsx(SimpleButton, { variant: "text", icon: "undo", tooltip: "Revert to default", "data-name": "plus-minus-reset-key", tabIndex: -1, onMouseDown: handleReset, onKeyDown: (event) => {
152
+ if (event.key === 'Enter') {
153
+ handleReset(event);
154
+ ref.current?.focus();
155
+ }
156
+ } })) : (_jsx(SimpleButton, { variant: "text", icon: "edit", tooltip: "Edit keyboard shortcut", "data-name": "plus-minus-edit-key", tabIndex: -1, onMouseDown: handleEnterEdit, onKeyDown: (event) => {
157
+ if (event.key === 'Enter') {
158
+ handleEnterEdit(event);
159
+ }
160
+ } }))) : null] }), _jsx(Box, { "data-name": error ? 'key-capture-error' : 'key-capture-hint', className: cn('twa:text-2', error ? 'twa:text-destructive' : 'twa:opacity-60', !focused && !error ? 'twa:invisible' : ''), children: error ? error : editing ? 'ENTER to confirm, ESCAPE to cancel' : 'Hit Enter to edit' })] }));
161
+ };
@@ -5,7 +5,8 @@ import { SummaryText, useOnePageAdaptableWizardContext, } from '../../Wizard/One
5
5
  import { Tag } from '../../../components/Tag';
6
6
  import { Box, Flex } from '../../../components/Flex';
7
7
  import { Card } from '../../../components/Card';
8
- import { plusMinusDecrementKeyFromOptionsOnly, plusMinusIncrementKeyFromOptionsOnly, plusMinusResolvedKeysConflict, plusMinusTriggerSameAsBaseline, resolvePlusMinusTriggerKeysForNudge, } from '../../../Utilities/Helpers/PlusMinusHelper';
8
+ import { normalizePlusMinusTrigger, plusMinusDecrementKeyFromOptionsOnly, plusMinusIncrementKeyFromOptionsOnly, plusMinusResolvedKeysConflict, plusMinusTriggerSameAsBaseline, resolvePlusMinusTriggerKeysForNudge, } from '../../../Utilities/Helpers/PlusMinusHelper';
9
+ import { KeyComboCaptureInput } from './KeyComboCaptureInput';
9
10
  export const PlusMinusSettingsSummary = () => {
10
11
  const { data, api } = useOnePageAdaptableWizardContext();
11
12
  const { incrementKey, decrementKey } = resolvePlusMinusTriggerKeysForNudge(data, api.optionsApi.getEditOptions().plusMinusOptions ?? {});
@@ -36,42 +37,35 @@ export const PlusMinusSettingsWizardSection = (props) => {
36
37
  const pmOpts = api.optionsApi.getEditOptions().plusMinusOptions ?? {};
37
38
  const incrementBaseline = plusMinusIncrementKeyFromOptionsOnly(pmOpts);
38
39
  const decrementBaseline = plusMinusDecrementKeyFromOptionsOnly(pmOpts);
39
- const [incrementEdit, setIncrementEdit] = React.useState(undefined);
40
- const [decrementEdit, setDecrementEdit] = React.useState(undefined);
41
- React.useEffect(() => {
42
- setIncrementEdit(undefined);
43
- setDecrementEdit(undefined);
44
- }, [data.Uuid]);
45
- const incrementDisplayValue = incrementEdit !== undefined
46
- ? incrementEdit
47
- : data.IncrementKey?.trim()
48
- ? (data.IncrementKey ?? '').trim()
49
- : incrementBaseline;
50
- const decrementDisplayValue = decrementEdit !== undefined
51
- ? decrementEdit
52
- : data.DecrementKey?.trim()
53
- ? (data.DecrementKey ?? '').trim()
54
- : decrementBaseline;
55
- const commitIncrementKey = React.useCallback((raw) => {
56
- const trimmed = raw.trim();
57
- const next = trimmed === '' || plusMinusTriggerSameAsBaseline(trimmed, incrementBaseline)
58
- ? undefined
59
- : trimmed;
60
- const prev = data.IncrementKey?.trim() || undefined;
61
- if (next !== prev) {
62
- props.onChange({ ...data, IncrementKey: next });
63
- }
40
+ const incrementUsingDefault = !data.IncrementKey?.trim();
41
+ const decrementUsingDefault = !data.DecrementKey?.trim();
42
+ const incrementDisplayValue = incrementUsingDefault
43
+ ? incrementBaseline
44
+ : (data.IncrementKey ?? '').trim();
45
+ const decrementDisplayValue = decrementUsingDefault
46
+ ? decrementBaseline
47
+ : (data.DecrementKey ?? '').trim();
48
+ const commitIncrementKey = React.useCallback((trigger) => {
49
+ const next = plusMinusTriggerSameAsBaseline(trigger, incrementBaseline) ? undefined : trigger;
50
+ props.onChange({ ...data, IncrementKey: next });
64
51
  }, [data, incrementBaseline, props.onChange]);
65
- const commitDecrementKey = React.useCallback((raw) => {
66
- const trimmed = raw.trim();
67
- const next = trimmed === '' || plusMinusTriggerSameAsBaseline(trimmed, decrementBaseline)
68
- ? undefined
69
- : trimmed;
70
- const prev = data.DecrementKey?.trim() || undefined;
71
- if (next !== prev) {
72
- props.onChange({ ...data, DecrementKey: next });
73
- }
52
+ const commitDecrementKey = React.useCallback((trigger) => {
53
+ const next = plusMinusTriggerSameAsBaseline(trigger, decrementBaseline) ? undefined : trigger;
54
+ props.onChange({ ...data, DecrementKey: next });
74
55
  }, [data, decrementBaseline, props.onChange]);
56
+ const resetIncrementKey = React.useCallback(() => {
57
+ props.onChange({ ...data, IncrementKey: undefined });
58
+ }, [data, props.onChange]);
59
+ const resetDecrementKey = React.useCallback(() => {
60
+ props.onChange({ ...data, DecrementKey: undefined });
61
+ }, [data, props.onChange]);
62
+ const SAME_KEYS_MESSAGE = 'Increment and decrement keys cannot be the same.';
63
+ const validateIncrementCapture = React.useCallback((trigger) => normalizePlusMinusTrigger(trigger) === normalizePlusMinusTrigger(decrementDisplayValue)
64
+ ? SAME_KEYS_MESSAGE
65
+ : undefined, [decrementDisplayValue]);
66
+ const validateDecrementCapture = React.useCallback((trigger) => normalizePlusMinusTrigger(trigger) === normalizePlusMinusTrigger(incrementDisplayValue)
67
+ ? SAME_KEYS_MESSAGE
68
+ : undefined, [incrementDisplayValue]);
75
69
  const handleNameChange = (event) => {
76
70
  props.onChange({
77
71
  ...data,
@@ -85,29 +79,5 @@ export const PlusMinusSettingsWizardSection = (props) => {
85
79
  NudgeValue: isNaN(newValue) ? '' : newValue,
86
80
  });
87
81
  };
88
- const handleIncrementKeyFocus = () => {
89
- setIncrementEdit(data.IncrementKey?.trim() ? (data.IncrementKey ?? '').trim() : incrementBaseline);
90
- };
91
- const handleIncrementKeyChange = (event) => {
92
- const raw = event.target.value;
93
- setIncrementEdit(raw);
94
- commitIncrementKey(raw);
95
- };
96
- const handleIncrementKeyBlur = (event) => {
97
- commitIncrementKey(event.target.value);
98
- setIncrementEdit(undefined);
99
- };
100
- const handleDecrementKeyFocus = () => {
101
- setDecrementEdit(data.DecrementKey?.trim() ? (data.DecrementKey ?? '').trim() : decrementBaseline);
102
- };
103
- const handleDecrementKeyChange = (event) => {
104
- const raw = event.target.value;
105
- setDecrementEdit(raw);
106
- commitDecrementKey(raw);
107
- };
108
- const handleDecrementKeyBlur = (event) => {
109
- commitDecrementKey(event.target.value);
110
- setDecrementEdit(undefined);
111
- };
112
- return (_jsx(Box, { "data-name": "plus-minus-column-settings", children: _jsxs(Flex, { flexDirection: "column", className: "twa:gap-3 twa:p-3", children: [_jsxs(Card, { shadow: false, children: [_jsxs(Card.Title, { children: [_jsx(Box, { className: "twa:font-medium", children: "Name" }), _jsx(Box, { className: "twa:text-xs twa:opacity-70 twa:font-normal twa:max-w-[520px]", children: "Provide a unique name for the Plus/Minus Nudge" })] }), _jsx(Card.Body, { className: "twa:p-1", children: _jsx(Input, { "data-name": "plus-minus-name", className: "twa:max-w-[300px] twa:w-full", onChange: handleNameChange, placeholder: "Enter Name", value: data.Name ?? '' }) })] }), _jsxs(Card, { shadow: false, children: [_jsxs(Card.Title, { children: [_jsx(Box, { className: "twa:font-medium", children: "Nudge Value" }), _jsx(Box, { className: "twa:text-xs twa:opacity-70 twa:font-normal twa:max-w-[520px]", children: "Amount added to or subtracted from the cell value when Plus/Minus is used" })] }), _jsx(Card.Body, { className: "twa:p-1", children: _jsx(Input, { "data-name": "nudge-value", className: "twa:max-w-[300px] twa:w-full", onChange: handleNudgeValueChange, placeholder: "Enter Number", type: "number", value: data.NudgeValue ?? '' }) })] }), _jsxs(Card, { shadow: false, children: [_jsxs(Card.Title, { children: [_jsx(Box, { className: "twa:font-medium", children: "Trigger Keys" }), _jsx(Box, { className: "twa:text-xs twa:opacity-70 twa:font-normal twa:max-w-[520px]", children: "Keyboard keys that trigger increment and decrement" })] }), _jsx(Card.Body, { className: "twa:p-1", children: _jsxs(Flex, { flexDirection: "column", className: "twa:gap-3", children: [_jsxs(Box, { children: [_jsx(Box, { className: "twa:text-2 twa:mb-1 twa:opacity-80", children: "Increment key" }), _jsx(Input, { "data-name": "plus-minus-increment-key", className: "twa:max-w-[300px] twa:w-full", onBlur: handleIncrementKeyBlur, onChange: handleIncrementKeyChange, onFocus: handleIncrementKeyFocus, value: incrementDisplayValue })] }), _jsxs(Box, { children: [_jsx(Box, { className: "twa:text-2 twa:mb-1 twa:opacity-80", children: "Decrement key" }), _jsx(Input, { "data-name": "plus-minus-decrement-key", className: "twa:max-w-[300px] twa:w-full", onBlur: handleDecrementKeyBlur, onChange: handleDecrementKeyChange, onFocus: handleDecrementKeyFocus, value: decrementDisplayValue })] })] }) })] })] }) }));
82
+ return (_jsx(Box, { "data-name": "plus-minus-column-settings", children: _jsxs(Flex, { flexDirection: "column", className: "twa:gap-3 twa:p-3", children: [_jsxs(Card, { shadow: false, children: [_jsxs(Card.Title, { children: [_jsx(Box, { className: "twa:font-medium", children: "Name" }), _jsx(Box, { className: "twa:text-xs twa:opacity-70 twa:font-normal twa:max-w-[520px]", children: "Provide a unique name for the Plus / Minus Nudge" })] }), _jsx(Card.Body, { className: "twa:p-1", children: _jsx(Input, { "data-name": "plus-minus-name", className: "twa:max-w-[300px] twa:w-full", onChange: handleNameChange, placeholder: "Enter Name", value: data.Name ?? '' }) })] }), _jsxs(Card, { shadow: false, children: [_jsxs(Card.Title, { children: [_jsx(Box, { className: "twa:font-medium", children: "Nudge Value" }), _jsx(Box, { className: "twa:text-xs twa:opacity-70 twa:font-normal twa:max-w-[520px]", children: "Amount added to or subtracted from the cell value when Plus / Minus is used" })] }), _jsx(Card.Body, { className: "twa:p-1", children: _jsx(Input, { "data-name": "nudge-value", className: "twa:max-w-[300px] twa:w-full", onChange: handleNudgeValueChange, placeholder: "Enter Number", type: "number", value: data.NudgeValue ?? '' }) })] }), _jsxs(Card, { shadow: false, children: [_jsxs(Card.Title, { children: [_jsx(Box, { className: "twa:font-medium", children: "Trigger Keys" }), _jsx(Box, { className: "twa:text-xs twa:opacity-70 twa:font-normal twa:max-w-[520px]", children: "Click a field and press the keys that should trigger increment / decrement." })] }), _jsx(Card.Body, { className: "twa:p-1", children: _jsxs(Flex, { flexDirection: "column", className: "twa:gap-3", children: [_jsxs(Box, { children: [_jsx(Box, { className: "twa:text-2 twa:mb-1 twa:opacity-80", children: "Increment key" }), _jsx(KeyComboCaptureInput, { "data-name": "plus-minus-increment-key", value: incrementDisplayValue, isUsingDefault: incrementUsingDefault, onCapture: commitIncrementKey, onReset: resetIncrementKey, validateCapture: validateIncrementCapture })] }), _jsxs(Box, { children: [_jsx(Box, { className: "twa:text-2 twa:mb-1 twa:opacity-80", children: "Decrement key" }), _jsx(KeyComboCaptureInput, { "data-name": "plus-minus-decrement-key", value: decrementDisplayValue, isUsingDefault: decrementUsingDefault, onCapture: commitDecrementKey, onReset: resetDecrementKey, validateCapture: validateDecrementCapture })] })] }) })] })] }) }));
113
83
  };