@via-profit/ui-kit 0.2.6 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Autocomplete/AutocompleteContainer.js +47 -35
- package/Autocomplete/index.js +9 -7
- package/Autocomplete2/Autocomplete2.js +1 -1
- package/CHANGELOG.md +38 -0
- package/Calendar/CalendarComponent.js +54 -51
- package/Calendar/use-calendar.d.ts +3 -2
- package/Calendar/use-calendar.js +31 -24
- package/ClickOutside/index.js +12 -1
- package/Color/ColorParser.d.ts +9 -0
- package/Color/ColorParser.js +52 -12
- package/DatePicker/index.d.ts +4 -0
- package/DatePicker/index.js +28 -14
- package/DatePicker/useDatePickerFormat.js +20 -3
- package/MaskedField/useMasked.js +17 -6
- package/Menu/MenuContainer.d.ts +3 -3
- package/Menu/MenuContainer.js +26 -35
- package/Menu/VirtualizedList.js +1 -3
- package/Modal/BaseModal/ModalWrapper.js +65 -38
- package/Modal/BaseModal/context.js +6 -0
- package/Modal/BaseModal/index.js +0 -8
- package/Popper/Popper.d.ts +2 -2
- package/Popper/Popper.js +9 -7
- package/Popper/usePopper.js +79 -46
- package/Selectbox/SelectboxMenu.d.ts +1 -1
- package/Selectbox/index.js +11 -27
- package/Swiper/Swiper.js +17 -5
- package/TextArea/index.js +6 -14
- package/TextField/index.js +6 -18
- package/package.json +1 -1
- package/utils/TabManager.d.ts +7 -1
- package/utils/TabManager.js +39 -45
package/Popper/usePopper.js
CHANGED
|
@@ -44,10 +44,9 @@ const findScrollableAncestor = (element, includeHidden = false) => {
|
|
|
44
44
|
return window;
|
|
45
45
|
};
|
|
46
46
|
exports.findScrollableAncestor = findScrollableAncestor;
|
|
47
|
+
const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? react_1.default.useLayoutEffect : react_1.default.useEffect;
|
|
47
48
|
const usePopper = (props) => {
|
|
48
49
|
const { anchorElement, anchorPos = 'auto', positionStrategy = 'fixed', autoFlip = false, offset = 0, viewportMargin = 10, isOpen = false, alternativePlacements, } = props;
|
|
49
|
-
const isMountedRef = react_1.default.useRef(false);
|
|
50
|
-
const isPositioningRef = react_1.default.useRef(false);
|
|
51
50
|
const [actualPlacement, setActualPlacement] = react_1.default.useState(anchorPos);
|
|
52
51
|
const [style, setStyle] = react_1.default.useState(null);
|
|
53
52
|
const [isVisible, setIsVisible] = react_1.default.useState(false);
|
|
@@ -59,12 +58,15 @@ const usePopper = (props) => {
|
|
|
59
58
|
}
|
|
60
59
|
return (0, exports.findScrollableAncestor)(anchorElement);
|
|
61
60
|
}, [anchorElement]);
|
|
62
|
-
//
|
|
61
|
+
// Reset actualPlacement only when the preferred placement itself changes.
|
|
62
|
+
// Comparing with actualPlacement here would immediately revert every flip.
|
|
63
|
+
const prevAnchorPosRef = react_1.default.useRef(anchorPos);
|
|
63
64
|
react_1.default.useEffect(() => {
|
|
64
|
-
if (
|
|
65
|
+
if (prevAnchorPosRef.current !== anchorPos) {
|
|
66
|
+
prevAnchorPosRef.current = anchorPos;
|
|
65
67
|
setActualPlacement(anchorPos);
|
|
66
68
|
}
|
|
67
|
-
}, [
|
|
69
|
+
}, [anchorPos]);
|
|
68
70
|
const getPositionStyle = react_1.default.useCallback((props) => {
|
|
69
71
|
var _a;
|
|
70
72
|
const { place, anchorRect, popperRect, forViewportCheck = false } = props;
|
|
@@ -383,25 +385,56 @@ const usePopper = (props) => {
|
|
|
383
385
|
}
|
|
384
386
|
const anchorRect = anchorElement.getBoundingClientRect();
|
|
385
387
|
const popperRect = popperRef.current.getBoundingClientRect();
|
|
386
|
-
const { style, placement
|
|
387
|
-
|
|
388
|
-
setActualPlacement(placement);
|
|
389
|
-
}
|
|
388
|
+
const { style, placement } = getPreferredPlacements(anchorPos, anchorRect, popperRect);
|
|
389
|
+
setActualPlacement(placement);
|
|
390
390
|
setStyle(style);
|
|
391
391
|
}, [anchorElement, anchorPos, getPreferredPlacements]);
|
|
392
|
+
const frameRef = react_1.default.useRef(null);
|
|
393
|
+
const scheduleUpdate = react_1.default.useCallback(() => {
|
|
394
|
+
if (frameRef.current !== null) {
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
frameRef.current = window.requestAnimationFrame(() => {
|
|
398
|
+
frameRef.current = null;
|
|
399
|
+
calculatePosition();
|
|
400
|
+
});
|
|
401
|
+
}, [calculatePosition]);
|
|
402
|
+
// Cancel the pending frame on unmount
|
|
403
|
+
react_1.default.useEffect(() => () => {
|
|
404
|
+
if (frameRef.current !== null) {
|
|
405
|
+
window.cancelAnimationFrame(frameRef.current);
|
|
406
|
+
frameRef.current = null;
|
|
407
|
+
}
|
|
408
|
+
}, []);
|
|
392
409
|
/**
|
|
393
|
-
*
|
|
410
|
+
* Close popper logic
|
|
394
411
|
*/
|
|
395
412
|
react_1.default.useEffect(() => {
|
|
396
413
|
if (!isOpen) {
|
|
397
414
|
setIsVisible(false);
|
|
398
415
|
setStyle(null);
|
|
399
416
|
}
|
|
400
|
-
|
|
417
|
+
}, [isOpen]);
|
|
418
|
+
/**
|
|
419
|
+
* Open popper logic.
|
|
420
|
+
* Runs after every render because the popper node can appear later than `isOpen`
|
|
421
|
+
* becomes true (e.g. the portal is rendered only after the client mount)
|
|
422
|
+
*/
|
|
423
|
+
useIsomorphicLayoutEffect(() => {
|
|
424
|
+
if (isOpen && !isVisible && anchorElement && popperRef.current) {
|
|
401
425
|
calculatePosition();
|
|
402
426
|
setIsVisible(true);
|
|
403
427
|
}
|
|
404
|
-
}
|
|
428
|
+
});
|
|
429
|
+
/**
|
|
430
|
+
* Recalculate position when the positioning params are changed
|
|
431
|
+
*/
|
|
432
|
+
react_1.default.useEffect(() => {
|
|
433
|
+
if (isOpen && isVisible) {
|
|
434
|
+
calculatePosition();
|
|
435
|
+
}
|
|
436
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
437
|
+
}, [calculatePosition]);
|
|
405
438
|
/**
|
|
406
439
|
* The anchor element changes
|
|
407
440
|
*/
|
|
@@ -409,12 +442,7 @@ const usePopper = (props) => {
|
|
|
409
442
|
if (!isOpen || !anchorElement) {
|
|
410
443
|
return;
|
|
411
444
|
}
|
|
412
|
-
const observer = new MutationObserver(
|
|
413
|
-
window.requestAnimationFrame(() => {
|
|
414
|
-
// console.log('The anchor element was change');
|
|
415
|
-
calculatePosition();
|
|
416
|
-
});
|
|
417
|
-
});
|
|
445
|
+
const observer = new MutationObserver(scheduleUpdate);
|
|
418
446
|
observer.observe(anchorElement, {
|
|
419
447
|
attributes: true,
|
|
420
448
|
childList: true,
|
|
@@ -424,36 +452,47 @@ const usePopper = (props) => {
|
|
|
424
452
|
return () => {
|
|
425
453
|
observer.disconnect();
|
|
426
454
|
};
|
|
427
|
-
}, [isOpen, anchorElement,
|
|
455
|
+
}, [isOpen, anchorElement, scheduleUpdate]);
|
|
428
456
|
/**
|
|
429
|
-
*
|
|
457
|
+
* The size of the anchor or popper changes
|
|
430
458
|
*/
|
|
431
459
|
react_1.default.useEffect(() => {
|
|
432
|
-
if (!isOpen || !
|
|
460
|
+
if (!isOpen || !isVisible || typeof ResizeObserver === 'undefined') {
|
|
433
461
|
return;
|
|
434
462
|
}
|
|
435
|
-
const
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
});
|
|
439
|
-
};
|
|
440
|
-
// parent scroll listener
|
|
441
|
-
if (scrollableAncestor instanceof HTMLElement) {
|
|
442
|
-
scrollableAncestor.addEventListener('scroll', handleScroll, { passive: true });
|
|
463
|
+
const observer = new ResizeObserver(scheduleUpdate);
|
|
464
|
+
if (anchorElement) {
|
|
465
|
+
observer.observe(anchorElement);
|
|
443
466
|
}
|
|
444
|
-
|
|
445
|
-
|
|
467
|
+
if (popperRef.current) {
|
|
468
|
+
observer.observe(popperRef.current);
|
|
446
469
|
}
|
|
447
470
|
// eslint-disable-next-line consistent-return
|
|
448
471
|
return () => {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
472
|
+
observer.disconnect();
|
|
473
|
+
};
|
|
474
|
+
}, [isOpen, isVisible, anchorElement, scheduleUpdate]);
|
|
475
|
+
/**
|
|
476
|
+
* On scroll of any ancestor (captured on window) - recalculate position
|
|
477
|
+
*/
|
|
478
|
+
react_1.default.useEffect(() => {
|
|
479
|
+
if (!isOpen) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
const handleScroll = (event) => {
|
|
483
|
+
var _a;
|
|
484
|
+
// Scrolling inside the popper does not move it
|
|
485
|
+
if (event.target instanceof Node && ((_a = popperRef.current) === null || _a === void 0 ? void 0 : _a.contains(event.target))) {
|
|
486
|
+
return;
|
|
454
487
|
}
|
|
488
|
+
scheduleUpdate();
|
|
489
|
+
};
|
|
490
|
+
window.addEventListener('scroll', handleScroll, { passive: true, capture: true });
|
|
491
|
+
// eslint-disable-next-line consistent-return
|
|
492
|
+
return () => {
|
|
493
|
+
window.removeEventListener('scroll', handleScroll, { capture: true });
|
|
455
494
|
};
|
|
456
|
-
}, [isOpen,
|
|
495
|
+
}, [isOpen, scheduleUpdate]);
|
|
457
496
|
/**
|
|
458
497
|
* On window resize - recalculate position
|
|
459
498
|
*/
|
|
@@ -461,18 +500,12 @@ const usePopper = (props) => {
|
|
|
461
500
|
if (!isOpen) {
|
|
462
501
|
return;
|
|
463
502
|
}
|
|
464
|
-
|
|
465
|
-
window.requestAnimationFrame(() => {
|
|
466
|
-
calculatePosition();
|
|
467
|
-
});
|
|
468
|
-
};
|
|
469
|
-
// parent scroll listener
|
|
470
|
-
window.addEventListener('resize', handleResize);
|
|
503
|
+
window.addEventListener('resize', scheduleUpdate);
|
|
471
504
|
// eslint-disable-next-line consistent-return
|
|
472
505
|
return () => {
|
|
473
|
-
window.removeEventListener('resize',
|
|
506
|
+
window.removeEventListener('resize', scheduleUpdate);
|
|
474
507
|
};
|
|
475
|
-
}, [
|
|
508
|
+
}, [scheduleUpdate, isOpen]);
|
|
476
509
|
return {
|
|
477
510
|
actualPlacement,
|
|
478
511
|
style,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { MenuProps, MenuRef } from '../Menu';
|
|
3
|
-
import { OnChange } from '
|
|
3
|
+
import type { OnChange } from './index';
|
|
4
4
|
export interface SelectbosMenuProps<T, Multiple extends boolean | undefined = undefined> extends MenuProps<T, Multiple> {
|
|
5
5
|
readonly onChange?: OnChange<T, Multiple>;
|
|
6
6
|
}
|
package/Selectbox/index.js
CHANGED
|
@@ -60,31 +60,14 @@ const DEFAULT_OVERRIDES = {
|
|
|
60
60
|
ButtonWrapper: SelectboxButtonWrapper_1.default,
|
|
61
61
|
Container: SelectboxContainer_1.default,
|
|
62
62
|
};
|
|
63
|
-
const generateGuid = () => {
|
|
64
|
-
const u = Date.now().toString(16) + Math.random().toString(16) + '0'.repeat(16);
|
|
65
|
-
return [
|
|
66
|
-
u.substring(0, 8),
|
|
67
|
-
u.substring(8, 12),
|
|
68
|
-
'4000-8' + u.substring(13, 16),
|
|
69
|
-
u.substring(16, 28),
|
|
70
|
-
].join('-');
|
|
71
|
-
};
|
|
72
63
|
const Selectbox = React.forwardRef((props, ref) => {
|
|
73
|
-
const { items, value, multiple, isOpen = false, isLoading = false, anchorPos = 'bottom-fill', fullWidth = false, children, onChange, selectedItemToString, getOptionSelected, onRequestOpen = () => undefined, onRequestClose = () => undefined, overrides, id, label,
|
|
64
|
+
const { items, value, multiple, isOpen = false, isLoading = false, anchorPos = 'bottom-fill', fullWidth = false, children, onChange, selectedItemToString, getOptionSelected, onRequestOpen = () => undefined, onRequestClose = () => undefined, overrides, id, label, requiredAsterisk, error, errorText, notSetLabel = 'Not set', autoFlip = true, positionStrategy } = props, nativeButtonProps = __rest(props, ["items", "value", "multiple", "isOpen", "isLoading", "anchorPos", "fullWidth", "children", "onChange", "selectedItemToString", "getOptionSelected", "onRequestOpen", "onRequestClose", "overrides", "id", "label", "requiredAsterisk", "error", "errorText", "notSetLabel", "autoFlip", "positionStrategy"]);
|
|
74
65
|
const menuRef = React.useRef(null);
|
|
75
66
|
const [anchorElement, setAnchorElement] = React.useState(null);
|
|
76
67
|
const overridesMap = React.useMemo(() => (Object.assign(Object.assign({}, DEFAULT_OVERRIDES), overrides)), [overrides]);
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
return generateGuid();
|
|
82
|
-
}, [id]);
|
|
83
|
-
React.useEffect(() => {
|
|
84
|
-
if (isOpen && menuRef.current) {
|
|
85
|
-
menuRef.current.scrollToFirstSelected();
|
|
86
|
-
}
|
|
87
|
-
}, [isOpen]);
|
|
68
|
+
// useId is stable between server and client renders
|
|
69
|
+
const generatedID = React.useId();
|
|
70
|
+
const inputID = typeof id === 'string' ? id : generatedID;
|
|
88
71
|
const renderValueAsString = React.useCallback(() => {
|
|
89
72
|
if ((!multiple && !value) || (multiple && value.length === 0)) {
|
|
90
73
|
return notSetLabel;
|
|
@@ -128,11 +111,15 @@ const Selectbox = React.forwardRef((props, ref) => {
|
|
|
128
111
|
}
|
|
129
112
|
}, [onRequestClose, anchorElement]);
|
|
130
113
|
React.useEffect(() => {
|
|
114
|
+
// A closed selectbox has nothing to close
|
|
115
|
+
if (!isOpen) {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
131
118
|
window.document.addEventListener(ClickOutside_1.mouseEventMap.onMouseDown, handleMouseDown);
|
|
132
119
|
return () => {
|
|
133
120
|
window.document.removeEventListener(ClickOutside_1.mouseEventMap.onMouseDown, handleMouseDown);
|
|
134
121
|
};
|
|
135
|
-
}, [handleMouseDown]);
|
|
122
|
+
}, [handleMouseDown, isOpen]);
|
|
136
123
|
const buttonProps = React.useMemo(() => (Object.assign({ fullWidth,
|
|
137
124
|
error,
|
|
138
125
|
isOpen,
|
|
@@ -141,15 +128,12 @@ const Selectbox = React.forwardRef((props, ref) => {
|
|
|
141
128
|
htmlFor: inputID,
|
|
142
129
|
error,
|
|
143
130
|
}), [inputID, error]);
|
|
144
|
-
if (!(items === null || items === void 0 ? void 0 : items.length) && !isLoading) {
|
|
145
|
-
return null;
|
|
146
|
-
}
|
|
147
131
|
return (React.createElement(overridesMap.Container, null,
|
|
148
132
|
label != null && (React.createElement(overridesMap.Label, Object.assign({}, labelProps),
|
|
149
133
|
label,
|
|
150
|
-
requiredAsterisk != null && (React.createElement(overridesMap.Asterisk, null, typeof requiredAsterisk === 'boolean' ? '*' : requiredAsterisk)))),
|
|
134
|
+
requiredAsterisk != null && requiredAsterisk !== false && (React.createElement(overridesMap.Asterisk, null, typeof requiredAsterisk === 'boolean' ? '*' : requiredAsterisk)))),
|
|
151
135
|
React.createElement(overridesMap.ButtonWrapper, { fullWidth: fullWidth, error: error, isOpen: isOpen },
|
|
152
|
-
React.createElement(overridesMap.Button, Object.assign({}, buttonProps, { onClick: handleButtonClick, ref: setRefs }), renderValueAsString())),
|
|
136
|
+
React.createElement(overridesMap.Button, Object.assign({}, buttonProps, { id: inputID, onClick: handleButtonClick, ref: setRefs }), renderValueAsString())),
|
|
153
137
|
React.createElement(overridesMap.ErrorText, { error: error }, errorText),
|
|
154
138
|
React.createElement(SelectboxMenu_1.default, { multiple: multiple, items: items, value: value, isOpen: isOpen, anchorPos: anchorPos, positionStrategy: positionStrategy, autoFlip: autoFlip, alternativePlacements: ['bottom-fill', 'top-fill'], anchorElement: anchorElement, getOptionSelected: getOptionSelected, onChange: onChange, onRequestClose: onRequestClose, ref: menuRef }, children)));
|
|
155
139
|
});
|
package/Swiper/Swiper.js
CHANGED
|
@@ -106,7 +106,6 @@ exports.Swiper = React.forwardRef((props, ref) => {
|
|
|
106
106
|
const isAnimating = React.useRef(false);
|
|
107
107
|
const autoplayTimer = React.useRef();
|
|
108
108
|
const mounted = React.useRef(true);
|
|
109
|
-
const normalizeTimeout = React.useRef();
|
|
110
109
|
// #region Real Index
|
|
111
110
|
const realIndex = React.useMemo(() => {
|
|
112
111
|
if (!infinite) {
|
|
@@ -167,7 +166,9 @@ exports.Swiper = React.forwardRef((props, ref) => {
|
|
|
167
166
|
indexRef.current = index;
|
|
168
167
|
}, [index]);
|
|
169
168
|
const goToIndex = React.useCallback((i) => {
|
|
170
|
-
|
|
169
|
+
// In the infinite mode the index is normalized after the transition, so wait for it.
|
|
170
|
+
// In the regular mode the transition is simply retargeted
|
|
171
|
+
if (infinite && isAnimating.current)
|
|
171
172
|
return;
|
|
172
173
|
let targetIndex = i;
|
|
173
174
|
if (infinite) {
|
|
@@ -278,11 +279,13 @@ exports.Swiper = React.forwardRef((props, ref) => {
|
|
|
278
279
|
else {
|
|
279
280
|
setIndex(nextIndex);
|
|
280
281
|
}
|
|
281
|
-
|
|
282
|
+
// A click without movement does not start a transition, so transitionend never comes
|
|
283
|
+
// and the flag would block autoplay and goToIndex forever
|
|
284
|
+
isAnimating.current = nextIndex !== index || (snap && offset !== 0);
|
|
282
285
|
dragging.current = false;
|
|
283
286
|
setIsDragging(false);
|
|
284
287
|
e.target.releasePointerCapture(e.pointerId);
|
|
285
|
-
}, [threshold, dragThreshold, index, maxIndex, snap, infinite, total]);
|
|
288
|
+
}, [threshold, dragThreshold, index, maxIndex, snap, infinite, total, offset]);
|
|
286
289
|
const onLostPointerCapture = React.useCallback((e) => {
|
|
287
290
|
if (dragging.current) {
|
|
288
291
|
onPointerUp(e);
|
|
@@ -347,8 +350,17 @@ exports.Swiper = React.forwardRef((props, ref) => {
|
|
|
347
350
|
isAnimating.current = false;
|
|
348
351
|
normalizeIndex(); // <-- вот здесь
|
|
349
352
|
};
|
|
353
|
+
const handleTransitionCancel = (e) => {
|
|
354
|
+
if (e.propertyName !== 'transform')
|
|
355
|
+
return;
|
|
356
|
+
isAnimating.current = false;
|
|
357
|
+
};
|
|
350
358
|
track.addEventListener('transitionend', handleTransitionEnd);
|
|
351
|
-
|
|
359
|
+
track.addEventListener('transitioncancel', handleTransitionCancel);
|
|
360
|
+
return () => {
|
|
361
|
+
track.removeEventListener('transitionend', handleTransitionEnd);
|
|
362
|
+
track.removeEventListener('transitioncancel', handleTransitionCancel);
|
|
363
|
+
};
|
|
352
364
|
}, [normalizeIndex]);
|
|
353
365
|
// #region Mount
|
|
354
366
|
React.useEffect(() => {
|
package/TextArea/index.js
CHANGED
|
@@ -48,19 +48,9 @@ const TextAreaIconWrapper_1 = __importDefault(require("./TextAreaIconWrapper"));
|
|
|
48
48
|
const TextArea = (props, ref) => {
|
|
49
49
|
const { error, fullWidth, inputRef, label, id, errorText, className, style, endIcon, startIcon, requiredAsterisk, overrides, onChange, onFocus, onBlur } = props, nativeInputProps = __rest(props, ["error", "fullWidth", "inputRef", "label", "id", "errorText", "className", "style", "endIcon", "startIcon", "requiredAsterisk", "overrides", "onChange", "onFocus", "onBlur"]);
|
|
50
50
|
const [focused, setFocused] = React.useState(false);
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
const u = Date.now().toString(16) + Math.random().toString(16) + '0'.repeat(16);
|
|
56
|
-
const guid = [
|
|
57
|
-
u.substring(0, 8),
|
|
58
|
-
u.substring(8, 12),
|
|
59
|
-
'4000-8' + u.substring(13, 16),
|
|
60
|
-
u.substring(16, 28),
|
|
61
|
-
].join('-');
|
|
62
|
-
return guid;
|
|
63
|
-
}, [id]);
|
|
51
|
+
// useId is stable between server and client renders
|
|
52
|
+
const generatedID = React.useId();
|
|
53
|
+
const inputID = typeof id === 'string' ? id : generatedID;
|
|
64
54
|
const inputChange = React.useCallback(event => {
|
|
65
55
|
if (onChange) {
|
|
66
56
|
onChange(event);
|
|
@@ -92,7 +82,9 @@ const TextArea = (props, ref) => {
|
|
|
92
82
|
return (React.createElement(overridesMap.Container, { ref: ref, fullWidth: fullWidth, className: className, style: style, focused: focused },
|
|
93
83
|
typeof label !== 'undefined' && label !== null && (React.createElement(overridesMap.Label, { htmlFor: inputID, error: error },
|
|
94
84
|
label,
|
|
95
|
-
typeof requiredAsterisk !== 'undefined' &&
|
|
85
|
+
typeof requiredAsterisk !== 'undefined' &&
|
|
86
|
+
requiredAsterisk !== null &&
|
|
87
|
+
requiredAsterisk !== false && (React.createElement(overridesMap.Asterisk, null, typeof requiredAsterisk === 'boolean' ? '*' : requiredAsterisk)))),
|
|
96
88
|
React.createElement(overridesMap.InputWrapper, { error: error, focused: focused, fullWidth: fullWidth },
|
|
97
89
|
hasStartIcon && (React.createElement(overridesMap.IconWrapper, { position: "start" }, startIcon)),
|
|
98
90
|
React.createElement(overridesMap.Input, Object.assign({}, nativeInputProps, { hasStartIcon: hasStartIcon, hasEndIcon: hasEndIcon, ref: inputRef, id: inputID, onChange: inputChange, onFocus: inputFocus, onBlur: inputBlur })),
|
package/TextField/index.js
CHANGED
|
@@ -67,26 +67,14 @@ const defailtOverrides = {
|
|
|
67
67
|
const TextField = React.forwardRef((props, ref) => {
|
|
68
68
|
const { error, fullWidth, inputRef, label, id, errorText, className, style, endIcon, startIcon, requiredAsterisk, overrides, onChange, onFocus, onBlur } = props, nativeInputProps = __rest(props, ["error", "fullWidth", "inputRef", "label", "id", "errorText", "className", "style", "endIcon", "startIcon", "requiredAsterisk", "overrides", "onChange", "onFocus", "onBlur"]);
|
|
69
69
|
const [focused, setFocused] = React.useState(false);
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
const u = Date.now().toString(16) + Math.random().toString(16) + '0'.repeat(16);
|
|
75
|
-
return [
|
|
76
|
-
u.substring(0, 8),
|
|
77
|
-
u.substring(8, 12),
|
|
78
|
-
'4000-8' + u.substring(13, 16),
|
|
79
|
-
u.substring(16, 28),
|
|
80
|
-
].join('-');
|
|
81
|
-
}, [id]);
|
|
82
|
-
const stableOnChange = React.useCallback(onChange ? onChange : () => undefined, [onChange]); // уже есть
|
|
83
|
-
// const stableOnFocus = React.useCallback(onFocus, [onFocus]); // уже есть
|
|
84
|
-
// const stableOnBlur = React.useCallback(onBlur, [onBlur]); // уже есть
|
|
70
|
+
// useId is stable between server and client renders
|
|
71
|
+
const generatedID = React.useId();
|
|
72
|
+
const inputID = typeof id === 'string' ? id : generatedID;
|
|
85
73
|
const inputChange = React.useCallback(event => {
|
|
86
|
-
if (
|
|
87
|
-
|
|
74
|
+
if (typeof onChange === 'function') {
|
|
75
|
+
onChange(event);
|
|
88
76
|
}
|
|
89
|
-
}, [
|
|
77
|
+
}, [onChange]);
|
|
90
78
|
const inputFocus = React.useCallback(event => {
|
|
91
79
|
setFocused(true);
|
|
92
80
|
if (typeof onFocus === 'function') {
|
package/package.json
CHANGED
package/utils/TabManager.d.ts
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* window.document.addEventListener('keydown', keydown);
|
|
26
26
|
*
|
|
27
27
|
* return () => {
|
|
28
|
-
* window.document.
|
|
28
|
+
* window.document.removeEventListener('keydown', keydown);
|
|
29
29
|
* }
|
|
30
30
|
* }, []);
|
|
31
31
|
*
|
|
@@ -51,6 +51,12 @@ declare class TabManager {
|
|
|
51
51
|
registerContainer(container: HTMLElement): void;
|
|
52
52
|
unregisterContainer(container: HTMLElement, revertBackFocus?: boolean): void;
|
|
53
53
|
getElementsList(): HTMLElement[];
|
|
54
|
+
/**
|
|
55
|
+
* Index of the currently focused element in the list.
|
|
56
|
+
* Uses the real focus, so a focus set by mouse is taken into account
|
|
57
|
+
*/
|
|
58
|
+
getCurrentIndex(list: readonly HTMLElement[]): number;
|
|
59
|
+
focusByIndex(list: readonly HTMLElement[], index: number): void;
|
|
54
60
|
focusNext(): void;
|
|
55
61
|
focusPrev(): void;
|
|
56
62
|
}
|
package/utils/TabManager.js
CHANGED
|
@@ -39,7 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
39
39
|
* window.document.addEventListener('keydown', keydown);
|
|
40
40
|
*
|
|
41
41
|
* return () => {
|
|
42
|
-
* window.document.
|
|
42
|
+
* window.document.removeEventListener('keydown', keydown);
|
|
43
43
|
* }
|
|
44
44
|
* }, []);
|
|
45
45
|
*
|
|
@@ -78,7 +78,6 @@ class TabManager {
|
|
|
78
78
|
__classPrivateFieldSet(this, _TabManager_stack, __classPrivateFieldGet(this, _TabManager_stack, "f").filter(stackElem => {
|
|
79
79
|
const founded = stackElem.container === container;
|
|
80
80
|
if (founded && revertBackFocus && stackElem.lastFocused instanceof HTMLElement) {
|
|
81
|
-
console.debug('revert back focus to', stackElem.lastFocused);
|
|
82
81
|
stackElem.lastFocused.focus();
|
|
83
82
|
}
|
|
84
83
|
return !founded;
|
|
@@ -86,60 +85,55 @@ class TabManager {
|
|
|
86
85
|
}
|
|
87
86
|
getElementsList() {
|
|
88
87
|
const { stackElement } = this.getTopOfTheStack();
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
list.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (a.tabIndex < b.tabIndex) {
|
|
101
|
-
return -1;
|
|
102
|
-
}
|
|
103
|
-
if (a.tabIndex > b.tabIndex) {
|
|
104
|
-
return 1;
|
|
105
|
-
}
|
|
106
|
-
return 0;
|
|
107
|
-
});
|
|
88
|
+
if (!stackElement) {
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
const universe = stackElement.container.querySelectorAll('input, button, select, textarea, a[href], [tabindex]');
|
|
92
|
+
const list = Array.prototype.filter.call(universe, (item) => item.tabIndex >= 0 &&
|
|
93
|
+
!item.disabled &&
|
|
94
|
+
item !== stackElement.container);
|
|
95
|
+
// Positive tabIndex goes first (ascending), then tabIndex=0 in the DOM order.
|
|
96
|
+
// Array.prototype.sort is stable, so equal keys keep the DOM order
|
|
97
|
+
const sortKey = (el) => el.tabIndex === 0 ? Number.MAX_SAFE_INTEGER : el.tabIndex;
|
|
98
|
+
list.sort((a, b) => sortKey(a) - sortKey(b));
|
|
108
99
|
return list;
|
|
109
100
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
101
|
+
/**
|
|
102
|
+
* Index of the currently focused element in the list.
|
|
103
|
+
* Uses the real focus, so a focus set by mouse is taken into account
|
|
104
|
+
*/
|
|
105
|
+
getCurrentIndex(list) {
|
|
106
|
+
const { stackElement } = this.getTopOfTheStack();
|
|
107
|
+
const activeIndex = list.findIndex(el => el === document.activeElement);
|
|
108
|
+
if (activeIndex !== -1) {
|
|
109
|
+
return activeIndex;
|
|
118
110
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
111
|
+
return stackElement ? stackElement.lastIndex : -1;
|
|
112
|
+
}
|
|
113
|
+
focusByIndex(list, index) {
|
|
114
|
+
const { stackElement } = this.getTopOfTheStack();
|
|
115
|
+
const el = list[index];
|
|
116
|
+
if (!stackElement || !el) {
|
|
124
117
|
return;
|
|
125
118
|
}
|
|
119
|
+
stackElement.lastIndex = index;
|
|
120
|
+
el.focus();
|
|
126
121
|
}
|
|
127
|
-
|
|
128
|
-
const { stackElement, stackIndex } = this.getTopOfTheStack();
|
|
122
|
+
focusNext() {
|
|
129
123
|
const list = this.getElementsList();
|
|
130
|
-
|
|
131
|
-
if (list[nextIndex]) {
|
|
132
|
-
list[nextIndex].focus();
|
|
133
|
-
__classPrivateFieldGet(this, _TabManager_stack, "f")[stackIndex].lastIndex = nextIndex;
|
|
124
|
+
if (list.length === 0) {
|
|
134
125
|
return;
|
|
135
126
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
127
|
+
const nextIndex = this.getCurrentIndex(list) + 1;
|
|
128
|
+
this.focusByIndex(list, nextIndex < list.length ? nextIndex : 0);
|
|
129
|
+
}
|
|
130
|
+
focusPrev() {
|
|
131
|
+
const list = this.getElementsList();
|
|
132
|
+
if (list.length === 0) {
|
|
141
133
|
return;
|
|
142
134
|
}
|
|
135
|
+
const prevIndex = this.getCurrentIndex(list) - 1;
|
|
136
|
+
this.focusByIndex(list, prevIndex >= 0 ? prevIndex : list.length - 1);
|
|
143
137
|
}
|
|
144
138
|
}
|
|
145
139
|
_TabManager_stack = new WeakMap();
|