@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.
- package/index.css +10 -1
- package/package.json +1 -1
- package/src/AdaptableOptions/AdaptableOptions.d.ts +9 -0
- package/src/AdaptableOptions/DefaultAdaptableOptions.d.ts +17 -0
- package/src/AdaptableOptions/DefaultAdaptableOptions.js +7 -46
- package/src/AdaptableOptions/EditOptions.d.ts +10 -2
- package/src/AdaptableOptions/SettingsPanelOptions.d.ts +3 -28
- package/src/AdaptableOptions/WizardOptions.d.ts +26 -0
- package/src/AdaptableOptions/WizardOptions.js +1 -0
- package/src/AdaptableState/Common/CustomWindowConfig.d.ts +2 -14
- package/src/AdaptableState/Common/Window.d.ts +33 -0
- package/src/AdaptableState/Common/Window.js +1 -0
- package/src/AdaptableState/InitialState.d.ts +2 -1
- package/src/AdaptableState/PlusMinusState.d.ts +12 -2
- package/src/Strategy/PlusMinusModule.js +5 -3
- package/src/Utilities/Extensions/ObjectExtensions.d.ts +16 -0
- package/src/Utilities/Extensions/ObjectExtensions.js +41 -0
- package/src/Utilities/Helpers/PlusMinusHelper.d.ts +62 -9
- package/src/Utilities/Helpers/PlusMinusHelper.js +180 -25
- package/src/View/Components/Popups/AdaptablePopup/AdaptablePopup.js +5 -1
- package/src/View/Components/Popups/AdaptablePopup/AdaptablePopupDialog.js +7 -3
- package/src/View/Components/Popups/Utilities.d.ts +11 -0
- package/src/View/Components/Popups/Utilities.js +8 -0
- package/src/View/PlusMinus/Wizard/KeyComboCaptureInput.d.ts +30 -0
- package/src/View/PlusMinus/Wizard/KeyComboCaptureInput.js +161 -0
- package/src/View/PlusMinus/Wizard/PlusMinusSettingsWizardSection.js +30 -60
- package/src/View/Wizard/OnePageWizards.js +27 -2
- package/src/agGrid/AdaptableAgGrid.js +2 -0
- package/src/env.js +2 -2
- package/src/migration/DeprecatedStateKeys.d.ts +37 -0
- package/src/migration/DeprecatedStateKeys.js +42 -0
- package/src/migration/VersionUpgrade23.d.ts +9 -0
- package/src/migration/VersionUpgrade23.js +1 -2
- package/src/types.d.ts +4 -2
- 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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
|
22
|
+
return normalizedIncrement === normalizedDecrement;
|
|
17
23
|
}
|
|
18
|
-
export function
|
|
19
|
-
|
|
24
|
+
export function plusMinusEventMatchesTrigger(event, configuredKey) {
|
|
25
|
+
const trigger = (configuredKey ?? '').trim();
|
|
26
|
+
if (!trigger) {
|
|
20
27
|
return false;
|
|
21
28
|
}
|
|
22
|
-
|
|
23
|
-
|
|
29
|
+
const pressedKey = event?.key;
|
|
30
|
+
if (!pressedKey) {
|
|
31
|
+
return false;
|
|
24
32
|
}
|
|
25
|
-
if (
|
|
26
|
-
return
|
|
33
|
+
if (!isPlusMinusCombination(trigger)) {
|
|
34
|
+
return pressedKey.length === 1 && pressedKey.toLowerCase() === trigger.toLowerCase();
|
|
27
35
|
}
|
|
28
|
-
return
|
|
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
|
|
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
|
-
|
|
182
|
+
const trigger = (configuredKey ?? '').trim();
|
|
183
|
+
if (!trigger) {
|
|
41
184
|
return;
|
|
42
185
|
}
|
|
43
|
-
if (
|
|
44
|
-
lookup.
|
|
186
|
+
if (!isPlusMinusCombination(trigger)) {
|
|
187
|
+
lookup.effectiveKeysLower.add(trigger.toLowerCase());
|
|
188
|
+
return;
|
|
45
189
|
}
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
53
|
-
|
|
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
|
-
|
|
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
|
|
24
|
-
position
|
|
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
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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((
|
|
66
|
-
const
|
|
67
|
-
|
|
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
|
-
|
|
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
|
};
|