@rolster/react-components 19.8.22 → 19.9.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/dist/es/index.mjs CHANGED
@@ -484,6 +484,17 @@ const RlsAmount = memo(RlsAmountComponent);
484
484
 
485
485
  const AREA_TEXT_MAX_ROWS = 8;
486
486
  const AREA_TEXT_MIN_ROWS = 3;
487
+ function writeAreaValue(element, valueArea) {
488
+ const rawArea = element.value;
489
+ if (rawArea === valueArea) {
490
+ return;
491
+ }
492
+ const selection = element.selectionStart;
493
+ const offset = valueArea.length - rawArea.length;
494
+ const caret = Math.max(0, Math.min(selection + offset, valueArea.length));
495
+ element.value = valueArea;
496
+ element.setSelectionRange(caret, caret);
497
+ }
487
498
  function RlsAreaTextComponent({ autoComplete, disabled, formControl, identifier, maxLength, maxRows, minRows, onBlur, onEnter, onFocus, onKeyDown, onKeyUp, onValue, placeholder, readOnly, resize = 'vertical', value }) {
488
499
  const valueInitial = String(formControl?.value ?? value ?? '');
489
500
  const rowsMin = minRows ?? AREA_TEXT_MIN_ROWS;
@@ -492,7 +503,9 @@ function RlsAreaTextComponent({ autoComplete, disabled, formControl, identifier,
492
503
  const [focused, setFocused] = useState(false);
493
504
  const elementRef = useRef(null);
494
505
  const areaRef = formControl?.elementRef ?? elementRef;
495
- const changeIsInternal = useRef(false);
506
+ const composing = useRef(false);
507
+ const valueSent = useRef(formControl?.value);
508
+ const valueSource = useRef(formControl ? formControl.value : value);
496
509
  const heightIsManual = useRef(false);
497
510
  const heightApplied = useRef(0);
498
511
  const refreshHeight = useCallback(() => {
@@ -518,13 +531,16 @@ function RlsAreaTextComponent({ autoComplete, disabled, formControl, identifier,
518
531
  heightApplied.current = height;
519
532
  }, [areaRef, rowsMax, rowsMin]);
520
533
  useEffect(() => {
521
- if (!changeIsInternal.current) {
522
- const nextValue = String(formControl?.value ?? value ?? '');
534
+ const source = formControl ? formControl.value : value;
535
+ const isEcho = Object.is(source, valueSent.current);
536
+ const isSame = Object.is(source, valueSource.current);
537
+ valueSource.current = source;
538
+ if (!isEcho && !isSame) {
539
+ const nextValue = String(source ?? '');
523
540
  if (valueArea !== nextValue) {
524
541
  setValueArea(nextValue);
525
542
  }
526
543
  }
527
- changeIsInternal.current = false;
528
544
  }, [formControl?.value, value, valueArea]);
529
545
  useEffect(() => {
530
546
  refreshHeight();
@@ -548,13 +564,28 @@ function RlsAreaTextComponent({ autoComplete, disabled, formControl, identifier,
548
564
  observer.observe(element);
549
565
  return () => observer.disconnect();
550
566
  }, [areaRef, resize]);
551
- const onChangeArea = useCallback((event) => {
552
- const nextValue = event.target.value;
553
- changeIsInternal.current = true;
567
+ const applyArea = useCallback((element) => {
568
+ const rawArea = element.value;
569
+ const formatter = formControl?.formatter;
570
+ const nextValue = formatter && formControl?.formatOn !== 'blur' && !composing.current
571
+ ? (formatter(rawArea) ?? '')
572
+ : rawArea;
573
+ writeAreaValue(element, nextValue);
574
+ valueSent.current = nextValue;
554
575
  onValue?.(nextValue);
555
576
  setValueArea(nextValue);
556
577
  formControl?.setValue(nextValue);
557
578
  }, [formControl, onValue]);
579
+ const onChangeArea = useCallback((event) => {
580
+ applyArea(event.target);
581
+ }, [applyArea]);
582
+ const onCompositionStart = useCallback(() => {
583
+ composing.current = true;
584
+ }, []);
585
+ const onCompositionEnd = useCallback((event) => {
586
+ composing.current = false;
587
+ applyArea(event.currentTarget);
588
+ }, [applyArea]);
558
589
  const onKeyDownArea = useCallback((event) => {
559
590
  onKeyDown?.(event);
560
591
  if (event.key === 'Enter') {
@@ -581,7 +612,7 @@ function RlsAreaTextComponent({ autoComplete, disabled, formControl, identifier,
581
612
  readonly: readOnly,
582
613
  resizable: resize !== 'none' && !disabledArea && !readOnly
583
614
  });
584
- return (jsxRuntimeExports.jsxs("div", { id: identifier, className: className, children: [jsxRuntimeExports.jsx("textarea", { ref: areaRef, className: "rls-area-text__component", autoComplete: autoComplete ?? 'off', placeholder: placeholder, disabled: disabledArea, readOnly: readOnly, maxLength: maxLength, rows: rowsMin, style: { resize }, onFocus: onFocusArea, onBlur: onBlurArea, onChange: onChangeArea, onKeyDown: onKeyDownArea, onKeyUp: onKeyUpArea, value: valueArea }), jsxRuntimeExports.jsx("span", { className: "rls-area-text__value", "aria-hidden": "true", children: valueArea })] }));
615
+ return (jsxRuntimeExports.jsxs("div", { id: identifier, className: className, children: [jsxRuntimeExports.jsx("textarea", { ref: areaRef, className: "rls-area-text__component", autoComplete: autoComplete ?? 'off', placeholder: placeholder, disabled: disabledArea, readOnly: readOnly, maxLength: maxLength, rows: rowsMin, style: { resize }, onFocus: onFocusArea, onBlur: onBlurArea, onChange: onChangeArea, onCompositionStart: onCompositionStart, onCompositionEnd: onCompositionEnd, onKeyDown: onKeyDownArea, onKeyUp: onKeyUpArea, value: valueArea }), jsxRuntimeExports.jsx("span", { className: "rls-area-text__value", "aria-hidden": "true", children: valueArea })] }));
585
616
  }
586
617
  const RlsAreaText = memo(RlsAreaTextComponent);
587
618
 
@@ -710,30 +741,82 @@ function RlsImageComponent({ src, rlsTheme }) {
710
741
  }
711
742
  const RlsImage = memo(RlsImageComponent);
712
743
 
744
+ function valueToInput(value) {
745
+ return value ? String(value) : '';
746
+ }
747
+ function selectionStartOf(element) {
748
+ try {
749
+ // Lanza o retorna null en inputs sin selección (number, email, etc.)
750
+ return element.selectionStart;
751
+ }
752
+ catch {
753
+ return null;
754
+ }
755
+ }
756
+ /**
757
+ * Escribe el valor formateado directamente en el DOM y conserva la posición
758
+ * del cursor, corrigiéndola por la diferencia de longitud. Es necesario
759
+ * porque, cuando el formateador rechaza un carácter, el estado del control
760
+ * no cambia y React no vuelve a escribir el input.
761
+ */
762
+ function writeInputValue(element, valueInput) {
763
+ const rawInput = element.value;
764
+ if (rawInput === valueInput) {
765
+ return;
766
+ }
767
+ const selection = selectionStartOf(element);
768
+ element.value = valueInput;
769
+ if (selection !== null) {
770
+ const offset = valueInput.length - rawInput.length;
771
+ const caret = Math.max(0, Math.min(selection + offset, valueInput.length));
772
+ element.setSelectionRange(caret, caret);
773
+ }
774
+ }
713
775
  function RlsInputComponent({ children, decimals, disabled, formControl, identifier, onBlur, onEnter, onFocus, onKeyDown, onKeyUp, onValue, placeholder, readOnly, type, value }) {
714
- const valueInitial = (formControl?.value ?? value) ? String(value) : '';
715
- const [valueInput, setValueInput] = useState(valueInitial);
776
+ const [valueInput, setValueInput] = useState(() => valueToInput(formControl?.value ?? value));
716
777
  const [focused, setFocused] = useState(false);
717
- const changeIsInternal = useRef(false);
778
+ const composing = useRef(false);
779
+ const valueSent = useRef(formControl?.value);
718
780
  useEffect(() => {
719
- if (!changeIsInternal.current && formControl) {
720
- const valueControl = formControl.value ? String(formControl.value) : '';
781
+ if (formControl && !Object.is(formControl.value, valueSent.current)) {
782
+ const valueControl = valueToInput(formControl.value);
721
783
  if (valueInput !== valueControl) {
722
784
  setValueInput(valueControl);
723
785
  }
724
786
  }
725
- changeIsInternal.current = false;
726
787
  }, [formControl?.value]);
727
- const onChangeInput = useCallback((event) => {
728
- const valueInput = event.target.value;
729
- const value = type === 'number'
730
- ? parseFloat((+valueInput).toFixed(decimals))
731
- : valueInput;
732
- changeIsInternal.current = true;
788
+ const applyInput = useCallback((element) => {
789
+ const rawInput = element.value;
790
+ const parsed = type === 'number'
791
+ ? parseFloat((+rawInput).toFixed(decimals))
792
+ : rawInput;
793
+ const formatter = formControl?.formatter;
794
+ const value = formatter && formControl?.formatOn !== 'blur' && !composing.current
795
+ ? formatter(parsed)
796
+ : parsed;
797
+ const textInput = Object.is(value, parsed)
798
+ ? rawInput
799
+ : type === 'number'
800
+ ? Number.isNaN(value)
801
+ ? ''
802
+ : String(value)
803
+ : valueToInput(value);
804
+ writeInputValue(element, textInput);
805
+ valueSent.current = value;
733
806
  onValue?.(value);
734
- setValueInput(valueInput);
807
+ setValueInput(textInput);
735
808
  formControl?.setValue(value);
736
809
  }, [formControl, onValue, type, decimals]);
810
+ const onChangeInput = useCallback((event) => {
811
+ applyInput(event.target);
812
+ }, [applyInput]);
813
+ const onCompositionStart = useCallback(() => {
814
+ composing.current = true;
815
+ }, []);
816
+ const onCompositionEnd = useCallback((event) => {
817
+ composing.current = false;
818
+ applyInput(event.currentTarget);
819
+ }, [applyInput]);
737
820
  const onKeyDownInput = useCallback((event) => {
738
821
  onKeyDown?.(event);
739
822
  if (event.key === 'Enter') {
@@ -757,7 +840,7 @@ function RlsInputComponent({ children, decimals, disabled, formControl, identifi
757
840
  disabled: formControl?.disabled || disabled,
758
841
  focused: formControl?.focused ?? focused
759
842
  });
760
- return (jsxRuntimeExports.jsxs("div", { id: identifier, className: className, children: [jsxRuntimeExports.jsx("input", { ref: formControl?.elementRef, className: "rls-input__component", autoComplete: "off", type: type ?? 'text', placeholder: placeholder, disabled: formControl?.disabled || disabled, readOnly: readOnly, onFocus: onFocusInput, onBlur: onBlurInput, onChange: onChangeInput, onKeyDown: onKeyDownInput, onKeyUp: onKeyUpInput, value: valueInput }), jsxRuntimeExports.jsx("span", { className: "rls-input__value", children: children })] }));
843
+ return (jsxRuntimeExports.jsxs("div", { id: identifier, className: className, children: [jsxRuntimeExports.jsx("input", { ref: formControl?.elementRef, className: "rls-input__component", autoComplete: "off", type: type ?? 'text', placeholder: placeholder, disabled: formControl?.disabled || disabled, readOnly: readOnly, onFocus: onFocusInput, onBlur: onBlurInput, onChange: onChangeInput, onCompositionStart: onCompositionStart, onCompositionEnd: onCompositionEnd, onKeyDown: onKeyDownInput, onKeyUp: onKeyUpInput, value: valueInput }), jsxRuntimeExports.jsx("span", { className: "rls-input__value", children: children })] }));
761
844
  }
762
845
  const RlsInput = memo(RlsInputComponent);
763
846