@rolster/react-components 19.8.18 → 19.8.20

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
@@ -1,4 +1,4 @@
1
- import require$$0, { memo, useMemo, useState, useRef, useCallback, useEffect, useInsertionEffect, createContext, useContext } from 'react';
1
+ import require$$0, { memo, useMemo, useState, useRef, useCallback, useEffect, useInsertionEffect, useId, createContext, useContext } from 'react';
2
2
  import { currencyFormat, BigDecimal, valueIsDefined, SealedPartial } from '@rolster/commons';
3
3
  import { i18n, i18nSubscribe } from '@rolster/i18n';
4
4
  import ReactDOM, { createPortal } from 'react-dom';
@@ -1979,7 +1979,7 @@ function RlsNavbarMenuComponent({ options, onOption }) {
1979
1979
  }
1980
1980
  const RlsNavbarMenu = memo(RlsNavbarMenuComponent);
1981
1981
 
1982
- function PageButton({ page, onSelect }) {
1982
+ function RlsPaginationButton({ page, onSelect }) {
1983
1983
  const className = renderClassStatus('rls-pagination__page', {
1984
1984
  active: page.active
1985
1985
  });
@@ -2033,7 +2033,7 @@ function RlsPaginationComponent({ suggestions, count, filter, onPagination, posi
2033
2033
  const goLastPagination = useCallback(() => {
2034
2034
  refreshPagination(controller.current?.goLastPage());
2035
2035
  }, [refreshPagination]);
2036
- return (jsxRuntimeExports.jsxs("div", { className: "rls-pagination", children: [jsxRuntimeExports.jsxs("div", { className: "rls-pagination__actions", children: [jsxRuntimeExports.jsx("button", { className: "rls-pagination__action", onClick: goFirstPagination, disabled: template?.firstPage, children: jsxRuntimeExports.jsx(RlsIcon, { value: "arrowhead-left" }) }), jsxRuntimeExports.jsx("button", { className: "rls-pagination__action", onClick: goPreviousPagination, disabled: template?.firstPage, children: jsxRuntimeExports.jsx(RlsIcon, { value: "arrow-ios-left" }) })] }), jsxRuntimeExports.jsxs("div", { className: "rls-pagination__body", children: [jsxRuntimeExports.jsx("div", { className: "rls-pagination__pages", children: template?.pages.map((page) => (jsxRuntimeExports.jsx(PageButton, { page: page, onSelect: goToPagination }, page.value))) }), jsxRuntimeExports.jsx("div", { className: "rls-pagination__description", children: template?.description }), jsxRuntimeExports.jsx("span", { className: "rls-pagination__count", children: suggestions.length })] }), jsxRuntimeExports.jsxs("div", { className: "rls-pagination__actions", children: [jsxRuntimeExports.jsx("button", { className: "rls-pagination__action", onClick: goNextPagination, disabled: template?.lastPage, children: jsxRuntimeExports.jsx(RlsIcon, { value: "arrow-ios-right" }) }), jsxRuntimeExports.jsx("button", { className: "rls-pagination__action", onClick: goLastPagination, disabled: template?.lastPage, children: jsxRuntimeExports.jsx(RlsIcon, { value: "arrowhead-right" }) })] })] }));
2036
+ return (jsxRuntimeExports.jsxs("div", { className: "rls-pagination", children: [jsxRuntimeExports.jsxs("div", { className: "rls-pagination__actions", children: [jsxRuntimeExports.jsx("button", { className: "rls-pagination__action", onClick: goFirstPagination, disabled: template?.firstPage, children: jsxRuntimeExports.jsx(RlsIcon, { value: "arrowhead-left" }) }), jsxRuntimeExports.jsx("button", { className: "rls-pagination__action", onClick: goPreviousPagination, disabled: template?.firstPage, children: jsxRuntimeExports.jsx(RlsIcon, { value: "arrow-ios-left" }) })] }), jsxRuntimeExports.jsxs("div", { className: "rls-pagination__body", children: [jsxRuntimeExports.jsx("div", { className: "rls-pagination__pages", children: template?.pages.map((page) => (jsxRuntimeExports.jsx(RlsPaginationButton, { page: page, onSelect: goToPagination }, page.value))) }), jsxRuntimeExports.jsx("div", { className: "rls-pagination__description", children: template?.description }), jsxRuntimeExports.jsx("span", { className: "rls-pagination__count", children: suggestions.length })] }), jsxRuntimeExports.jsxs("div", { className: "rls-pagination__actions", children: [jsxRuntimeExports.jsx("button", { className: "rls-pagination__action", onClick: goNextPagination, disabled: template?.lastPage, children: jsxRuntimeExports.jsx(RlsIcon, { value: "arrow-ios-right" }) }), jsxRuntimeExports.jsx("button", { className: "rls-pagination__action", onClick: goLastPagination, disabled: template?.lastPage, children: jsxRuntimeExports.jsx(RlsIcon, { value: "arrowhead-right" }) })] })] }));
2037
2037
  }
2038
2038
  const RlsPagination = memo(RlsPaginationComponent);
2039
2039
 
@@ -2374,57 +2374,183 @@ function RlsPickerYearComponent({ date, disabled, formControl, maxDate, minDate,
2374
2374
  }
2375
2375
  const RlsPickerYear = memo(RlsPickerYearComponent);
2376
2376
 
2377
- function calculateInitialValue(value, minValue, maxValue) {
2378
- return minValue > value ? minValue : maxValue < value ? maxValue : value;
2377
+ const RATE_MIN = 0;
2378
+ const RATE_MAX = 100;
2379
+ const PRECISION = 12;
2380
+ function clampNumber(value, minValue, maxValue) {
2381
+ if (minValue > maxValue) {
2382
+ return minValue;
2383
+ }
2384
+ return Math.min(Math.max(value, minValue), maxValue);
2385
+ }
2386
+ function normalizeSliderValue(value, minValue, maxValue, step) {
2387
+ const valueClamped = clampNumber(value, minValue, maxValue);
2388
+ if (!step || step <= 0) {
2389
+ return valueClamped;
2390
+ }
2391
+ if (valueClamped <= minValue) {
2392
+ return minValue;
2393
+ }
2394
+ if (valueClamped >= maxValue) {
2395
+ return maxValue;
2396
+ }
2397
+ const steps = Math.round((valueClamped - minValue) / step);
2398
+ const valueStepped = parseFloat((minValue + steps * step).toPrecision(PRECISION));
2399
+ return clampNumber(valueStepped, minValue, maxValue);
2400
+ }
2401
+ function sliderValueToRate(value, minValue, maxValue) {
2402
+ const range = maxValue - minValue;
2403
+ if (range <= 0) {
2404
+ return RATE_MIN;
2405
+ }
2406
+ return clampNumber(((value - minValue) / range) * RATE_MAX, RATE_MIN, RATE_MAX);
2379
2407
  }
2380
- function calculateInitialRate(value, minValue, maxValue) {
2381
- const rateMax = maxValue - minValue;
2382
- const rateValue = value - minValue;
2383
- return Math.ceil((rateValue / rateMax) * 100);
2408
+ function sliderRateToValue(rate, minValue, maxValue, step) {
2409
+ const range = maxValue - minValue;
2410
+ const rateClamped = clampNumber(rate, RATE_MIN, RATE_MAX);
2411
+ return normalizeSliderValue(minValue + (range * rateClamped) / RATE_MAX, minValue, maxValue, step);
2384
2412
  }
2385
- function RlsSliderComponent({ children, className, disabled, formControl, identifier, maxValue, minValue, onValue, prefixIcon, rlsTheme, value }) {
2413
+
2414
+ const DEFAULT_STEP = 1;
2415
+ const PAGE_STEP_DIVISOR = 10;
2416
+ function RlsSliderComponent({ children, className, disabled, formControl, identifier, maxValue, minValue, onValue, prefixIcon, rlsTheme, step, value }) {
2386
2417
  const minValueSlider = useMemo(() => {
2387
2418
  return minValue ?? 0;
2388
2419
  }, [minValue]);
2389
2420
  const maxValueSlider = useMemo(() => {
2390
2421
  return maxValue ?? 100;
2391
2422
  }, [maxValue]);
2392
- const [valueSlider, setValue] = useState(calculateInitialValue(formControl?.value ?? value ?? 0, minValueSlider, maxValueSlider));
2393
- const [rate, setRate] = useState(calculateInitialRate(valueSlider, minValueSlider, maxValueSlider));
2394
- const refComponent = useRef(null);
2423
+ const stepSlider = useMemo(() => {
2424
+ return step && step > 0 ? step : DEFAULT_STEP;
2425
+ }, [step]);
2426
+ const [valueSlider, setValueSlider] = useState(() => {
2427
+ return normalizeSliderValue(formControl?.value ?? value ?? 0, minValueSlider, maxValueSlider, stepSlider);
2428
+ });
2429
+ const [dragging, setDragging] = useState(false);
2395
2430
  const refTrack = useRef(null);
2396
- const refTrackOn = useRef(null);
2397
2431
  const refThumb = useRef(null);
2432
+ const refValue = useRef(valueSlider);
2433
+ const refValueExternal = useRef(formControl?.value ?? value);
2434
+ const labelId = useId();
2435
+ const rate = useMemo(() => {
2436
+ return sliderValueToRate(valueSlider, minValueSlider, maxValueSlider);
2437
+ }, [valueSlider, minValueSlider, maxValueSlider]);
2398
2438
  const classNameSlider = renderClassStatus('rls-slider', {
2399
2439
  complet: valueSlider === maxValueSlider,
2400
2440
  disabled: disabled,
2441
+ dragging: dragging,
2401
2442
  empty: valueSlider === minValueSlider
2402
2443
  }, className);
2444
+ const commitValue = useCallback((valueNext) => {
2445
+ if (refValue.current === valueNext) {
2446
+ return;
2447
+ }
2448
+ refValue.current = valueNext;
2449
+ setValueSlider(valueNext);
2450
+ formControl?.setValue(valueNext);
2451
+ onValue?.(valueNext);
2452
+ }, [formControl, onValue]);
2453
+ const commitRate = useCallback((rateNext) => {
2454
+ commitValue(sliderRateToValue(rateNext, minValueSlider, maxValueSlider, stepSlider));
2455
+ }, [commitValue, minValueSlider, maxValueSlider, stepSlider]);
2456
+ const calculateRateFromClientX = useCallback((clientX) => {
2457
+ const { left, width } = refTrack.current.getBoundingClientRect();
2458
+ return width > 0 ? ((clientX - left) / width) * 100 : 0;
2459
+ }, []);
2460
+ const onPointerDown = useCallback((event) => {
2461
+ if (disabled) {
2462
+ return;
2463
+ }
2464
+ event.preventDefault();
2465
+ refThumb.current.focus();
2466
+ setDragging(true);
2467
+ commitRate(calculateRateFromClientX(event.clientX));
2468
+ }, [disabled, commitRate, calculateRateFromClientX]);
2469
+ const calculateValueFromKey = useCallback((key) => {
2470
+ const pageStep = Math.max(stepSlider, (maxValueSlider - minValueSlider) / PAGE_STEP_DIVISOR);
2471
+ switch (key) {
2472
+ case 'ArrowRight':
2473
+ case 'ArrowUp':
2474
+ return refValue.current + stepSlider;
2475
+ case 'ArrowLeft':
2476
+ case 'ArrowDown':
2477
+ return refValue.current - stepSlider;
2478
+ case 'PageUp':
2479
+ return refValue.current + pageStep;
2480
+ case 'PageDown':
2481
+ return refValue.current - pageStep;
2482
+ case 'Home':
2483
+ return minValueSlider;
2484
+ case 'End':
2485
+ return maxValueSlider;
2486
+ default:
2487
+ return undefined;
2488
+ }
2489
+ }, [stepSlider, minValueSlider, maxValueSlider]);
2490
+ const onKeyDown = useCallback((event) => {
2491
+ if (disabled) {
2492
+ return;
2493
+ }
2494
+ const valueNext = calculateValueFromKey(event.key);
2495
+ if (valueNext === undefined) {
2496
+ return;
2497
+ }
2498
+ event.preventDefault();
2499
+ commitValue(normalizeSliderValue(valueNext, minValueSlider, maxValueSlider, stepSlider));
2500
+ }, [
2501
+ disabled,
2502
+ calculateValueFromKey,
2503
+ commitValue,
2504
+ minValueSlider,
2505
+ maxValueSlider,
2506
+ stepSlider
2507
+ ]);
2508
+ useEffect(() => {
2509
+ if (!dragging) {
2510
+ return;
2511
+ }
2512
+ const onPointerMove = (event) => {
2513
+ commitRate(calculateRateFromClientX(event.clientX));
2514
+ };
2515
+ const onPointerRelease = () => {
2516
+ setDragging(false);
2517
+ };
2518
+ window.addEventListener('pointermove', onPointerMove);
2519
+ window.addEventListener('pointerup', onPointerRelease);
2520
+ window.addEventListener('pointercancel', onPointerRelease);
2521
+ return () => {
2522
+ window.removeEventListener('pointermove', onPointerMove);
2523
+ window.removeEventListener('pointerup', onPointerRelease);
2524
+ window.removeEventListener('pointercancel', onPointerRelease);
2525
+ };
2526
+ }, [dragging, commitRate, calculateRateFromClientX]);
2403
2527
  useEffect(() => {
2404
2528
  const valueInitial = formControl?.value ?? value ?? 0;
2405
- refThumb.current.style.left = `${rate}%`;
2406
- refTrackOn.current.style.width = `${rate}%`;
2407
- if (valueInitial !== valueSlider) {
2408
- formControl?.setValue(valueSlider);
2409
- onValue?.(valueSlider);
2529
+ if (valueInitial !== refValue.current) {
2530
+ formControl?.setValue(refValue.current);
2531
+ onValue?.(refValue.current);
2410
2532
  }
2411
2533
  }, []);
2412
- const calculateValueWithRate = useCallback((rate) => {
2413
- const value = Math.ceil(((maxValueSlider - minValueSlider) * rate) / 100);
2414
- refThumb.current.style.left = `${rate}%`;
2415
- refTrackOn.current.style.width = `${rate}%`;
2416
- const sliderValue = value + minValueSlider;
2417
- setRate(rate);
2418
- setValue(sliderValue);
2419
- formControl?.setValue(sliderValue);
2420
- onValue?.(sliderValue);
2421
- }, [minValueSlider, maxValueSlider, formControl, onValue]);
2422
- const onClickTrack = useCallback((event) => {
2423
- const { left, width } = refTrack.current.getBoundingClientRect();
2424
- const rate = Math.ceil(((event.clientX - left) / width) * 100);
2425
- calculateValueWithRate(rate);
2426
- }, [minValueSlider, maxValueSlider]);
2427
- return (jsxRuntimeExports.jsxs("div", { id: identifier, className: classNameSlider, "rls-theme": rlsTheme, children: [children && jsxRuntimeExports.jsx("span", { className: "rls-slider__label", children: children }), jsxRuntimeExports.jsxs("div", { className: "rls-slider__body", children: [prefixIcon && jsxRuntimeExports.jsx(RlsIcon, { value: prefixIcon }), jsxRuntimeExports.jsxs("div", { ref: refComponent, className: "rls-slider__component", children: [jsxRuntimeExports.jsx("div", { ref: refTrack, className: "rls-slider__track", onClick: onClickTrack, children: jsxRuntimeExports.jsx("div", { ref: refTrackOn, className: "rls-slider__track__on" }) }), jsxRuntimeExports.jsxs("div", { ref: refThumb, className: "rls-slider__thumb", children: [rate, "%"] })] })] })] }));
2534
+ useEffect(() => {
2535
+ const valueExternal = formControl?.value ?? value;
2536
+ if (valueExternal === undefined ||
2537
+ valueExternal === refValueExternal.current) {
2538
+ return;
2539
+ }
2540
+ refValueExternal.current = valueExternal;
2541
+ commitValue(normalizeSliderValue(valueExternal, minValueSlider, maxValueSlider, stepSlider));
2542
+ }, [
2543
+ value,
2544
+ formControl?.value,
2545
+ commitValue,
2546
+ minValueSlider,
2547
+ maxValueSlider,
2548
+ stepSlider
2549
+ ]);
2550
+ useEffect(() => {
2551
+ commitValue(normalizeSliderValue(refValue.current, minValueSlider, maxValueSlider, stepSlider));
2552
+ }, [commitValue, minValueSlider, maxValueSlider, stepSlider]);
2553
+ return (jsxRuntimeExports.jsxs("div", { id: identifier, className: classNameSlider, "rls-theme": rlsTheme, children: [children && (jsxRuntimeExports.jsx("span", { id: labelId, className: "rls-slider__label", children: children })), jsxRuntimeExports.jsxs("div", { className: "rls-slider__body", children: [prefixIcon && jsxRuntimeExports.jsx(RlsIcon, { value: prefixIcon }), jsxRuntimeExports.jsxs("div", { className: "rls-slider__component", onPointerDown: onPointerDown, children: [jsxRuntimeExports.jsx("div", { ref: refTrack, className: "rls-slider__track", children: jsxRuntimeExports.jsx("div", { className: "rls-slider__track__on", style: { width: `${rate}%` } }) }), jsxRuntimeExports.jsx("div", { ref: refThumb, className: "rls-slider__thumb", style: { left: `${rate}%` }, role: "slider", tabIndex: disabled ? -1 : 0, "aria-orientation": "horizontal", "aria-valuemin": minValueSlider, "aria-valuemax": maxValueSlider, "aria-valuenow": valueSlider, "aria-disabled": disabled || undefined, "aria-labelledby": children ? labelId : undefined, onKeyDown: onKeyDown, children: valueSlider })] })] })] }));
2428
2554
  }
2429
2555
  const RlsSlider = memo(RlsSliderComponent);
2430
2556
 
@@ -4641,11 +4767,17 @@ function RlsImageEditorComponent(props) {
4641
4767
  useResize({ refElement: refImage, onResize: onResizeElement });
4642
4768
  const onCropImage = useCallback(() => {
4643
4769
  const cropProps = getCropProperties();
4644
- const width = props.maxWidth || cropProps.width;
4645
- const height = width * getRatioFactor(ratio);
4770
+ const width = Math.round(props.maxWidth
4771
+ ? Math.min(props.maxWidth, cropProps.width)
4772
+ : cropProps.width);
4773
+ const height = Math.round(width * getRatioFactor(ratio));
4646
4774
  refPicture.current.width = width;
4647
4775
  refPicture.current.height = height;
4648
4776
  const context = refPicture.current.getContext('2d');
4777
+ if (context) {
4778
+ context.imageSmoothingEnabled = true;
4779
+ context.imageSmoothingQuality = 'high';
4780
+ }
4649
4781
  context?.drawImage(refCanvas.current, cropProps.left, cropProps.top, cropProps.width, cropProps.height, 0, 0, width, height);
4650
4782
  refPicture.current.toBlob((blob) => {
4651
4783
  if (blob) {