@rolster/react-components 19.11.0 → 19.12.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
@@ -3821,11 +3821,20 @@ const RlsModal = memo(RlsModalComponent);
3821
3821
  function formatTime(value) {
3822
3822
  return String(value).padStart(2, '0');
3823
3823
  }
3824
+ function pointerIsInsideTick({ clientX, clientY }, tick) {
3825
+ const { left, top, width, height } = tick.getBoundingClientRect();
3826
+ const distanceX = clientX - (left + width / 2);
3827
+ const distanceY = clientY - (top + height / 2);
3828
+ const radius = Math.min(width, height) / 2;
3829
+ return distanceX ** 2 + distanceY ** 2 <= radius ** 2;
3830
+ }
3824
3831
  const CLOCK_VALUES = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
3832
+ const CLOCK_HOURS = 12;
3833
+ const CLOCK_MINUTES = 60;
3825
3834
  const RADIUS = 48;
3826
3835
  const DIAL_RADIUS = 60;
3827
3836
  const TICK_RADIUS = 7;
3828
- function RlsPickerClockTick({ onValue, selectionIsHours, value }) {
3837
+ function RlsPickerClockTick({ selectionIsHours, value }) {
3829
3838
  const styleTick = useMemo(() => {
3830
3839
  const radian = (value / 6) * Math.PI;
3831
3840
  const x = Math.sin(radian) * RADIUS;
@@ -3835,20 +3844,13 @@ function RlsPickerClockTick({ onValue, selectionIsHours, value }) {
3835
3844
  top: `${DIAL_RADIUS - y - TICK_RADIUS - 1}rem`
3836
3845
  };
3837
3846
  }, []);
3838
- const label = useMemo(() => {
3847
+ const tickValue = useMemo(() => {
3839
3848
  if (selectionIsHours) {
3840
- return formatTime(value);
3841
- }
3842
- if (value === 12) {
3843
- return formatTime(0);
3849
+ return value;
3844
3850
  }
3845
- return formatTime(value * 5);
3851
+ return value === CLOCK_HOURS ? 0 : value * 5;
3846
3852
  }, [selectionIsHours]);
3847
- const onClick = useCallback((event) => {
3848
- event.preventDefault();
3849
- onValue(value);
3850
- }, [onValue]);
3851
- return (jsxRuntimeExports.jsx("div", { className: "rls-picker-clock__tick", style: styleTick, onClick: onClick, children: label }));
3853
+ return (jsxRuntimeExports.jsx("div", { className: "rls-picker-clock__tick", style: styleTick, "data-value": tickValue, children: formatTime(tickValue) }));
3852
3854
  }
3853
3855
  function RlsPickerClockComponent({ formControl, onListener, rlsTheme, time }) {
3854
3856
  const timeInitial = useMemo(() => {
@@ -3921,22 +3923,6 @@ function RlsPickerClockComponent({ formControl, onListener, rlsTheme, time }) {
3921
3923
  }
3922
3924
  changeIsInternal.current = false;
3923
3925
  }, [formControl?.value]);
3924
- const refreshComponent = useCallback((clientX, clientY) => {
3925
- const rectangle = plateElement.current.getBoundingClientRect();
3926
- const centerX = rectangle.left + rectangle.width / 2;
3927
- const centerY = rectangle.top + rectangle.height / 2;
3928
- const positionX = clientX - centerX;
3929
- const positionY = clientY - centerY;
3930
- const angle = Math.atan2(-positionX, positionY) + Math.PI;
3931
- const value = Math.round(angle / radianUnit);
3932
- refreshClock(value);
3933
- if (selectionIsHours) {
3934
- setHour(value === 0 ? 12 : value);
3935
- }
3936
- else {
3937
- setMinute(value);
3938
- }
3939
- }, [selectionIsHours, radianUnit]);
3940
3926
  const refreshClock = useCallback((value) => {
3941
3927
  const angle = value * radianUnit;
3942
3928
  const x2 = Math.sin(angle) * (RADIUS - TICK_RADIUS);
@@ -3952,6 +3938,27 @@ function RlsPickerClockComponent({ formControl, onListener, rlsTheme, time }) {
3952
3938
  pointElement.current.style.visibility =
3953
3939
  selectionIsHours || value % 5 === 0 ? 'hidden' : 'visible';
3954
3940
  }, [selectionIsHours, radianUnit]);
3941
+ const selectValue = useCallback((value) => {
3942
+ refreshClock(value);
3943
+ if (selectionIsHours) {
3944
+ setHour(value === 0 ? CLOCK_HOURS : value);
3945
+ }
3946
+ else {
3947
+ setMinute(value);
3948
+ }
3949
+ }, [selectionIsHours, refreshClock]);
3950
+ const selectFromPosition = useCallback((clientX, clientY) => {
3951
+ const rectangle = plateElement.current.getBoundingClientRect();
3952
+ const centerX = rectangle.left + rectangle.width / 2;
3953
+ const centerY = rectangle.top + rectangle.height / 2;
3954
+ const positionX = clientX - centerX;
3955
+ const positionY = clientY - centerY;
3956
+ // Angle in [0, 2π) measured clockwise from 12 o'clock
3957
+ const angle = Math.atan2(-positionX, positionY) + Math.PI;
3958
+ const steps = selectionIsHours ? CLOCK_HOURS : CLOCK_MINUTES;
3959
+ // Modulo keeps the last half step before 12 o'clock as 0, not as 60/12
3960
+ selectValue(Math.round(angle / radianUnit) % steps);
3961
+ }, [selectionIsHours, radianUnit, selectValue]);
3955
3962
  const onClickHour = useCallback(() => {
3956
3963
  setSelectionIsHours(true);
3957
3964
  }, []);
@@ -3964,49 +3971,56 @@ function RlsPickerClockComponent({ formControl, onListener, rlsTheme, time }) {
3964
3971
  const onClickPM = useCallback(() => {
3965
3972
  setZoneIsPM(true);
3966
3973
  }, []);
3967
- const onActiveSelection = useCallback(() => {
3974
+ const onPointerDown = useCallback((event) => {
3975
+ event.preventDefault();
3976
+ event.currentTarget.setPointerCapture(event.pointerId);
3968
3977
  selectionIsActive.current = true;
3969
- }, []);
3970
- const onMouseSelection = useCallback((event) => {
3978
+ const tick = event.target.closest('.rls-picker-clock__tick');
3979
+ // Pressing inside a label circle snaps to its exact value,
3980
+ // anywhere else (and while dragging) the angle decides
3981
+ if (tick?.dataset.value && pointerIsInsideTick(event, tick)) {
3982
+ selectValue(Number(tick.dataset.value));
3983
+ }
3984
+ else {
3985
+ selectFromPosition(event.clientX, event.clientY);
3986
+ }
3987
+ }, [selectValue, selectFromPosition]);
3988
+ const onPointerMove = useCallback((event) => {
3971
3989
  if (selectionIsActive.current) {
3972
- event.preventDefault();
3973
- refreshComponent(event.clientX, event.clientY);
3990
+ selectFromPosition(event.clientX, event.clientY);
3974
3991
  }
3975
- }, [refreshComponent]);
3976
- const onTouchSelection = useCallback((event) => {
3977
- const touch = event.touches.item(0);
3978
- if (selectionIsActive.current && touch) {
3979
- refreshComponent(touch.clientX, touch.clientY);
3992
+ }, [selectFromPosition]);
3993
+ const onPointerUp = useCallback(() => {
3994
+ if (!selectionIsActive.current) {
3995
+ return;
3980
3996
  }
3981
- }, [refreshComponent]);
3982
- const onInactiveSelection = useCallback(() => {
3983
3997
  selectionIsActive.current = false;
3984
- }, []);
3985
- const onClickValue = useCallback((value) => {
3998
+ // Once the hour is chosen, continue with the minutes
3986
3999
  if (selectionIsHours) {
3987
- setHour(value);
3988
- refreshClock(value);
3989
- }
3990
- else {
3991
- const minute = value === 12 ? 0 : value * 5;
3992
- setMinute(minute);
3993
- refreshClock(minute);
4000
+ setSelectionIsHours(false);
3994
4001
  }
3995
4002
  }, [selectionIsHours]);
4003
+ const onPointerCancel = useCallback(() => {
4004
+ selectionIsActive.current = false;
4005
+ }, []);
3996
4006
  const onCancel = useCallback(() => {
3997
4007
  onListener?.({ event: PickerListenerEvent.Cancel });
3998
4008
  }, [onListener]);
3999
4009
  const onCurrentTime = useCallback(() => {
4000
4010
  const currentTime = Time.now();
4011
+ const zoneIsPM = currentTime.hour >= 12;
4012
+ const hour = zoneIsPM ? currentTime.hour - 12 : currentTime.hour;
4001
4013
  changeIsInternal.current = true;
4002
4014
  formControl?.setValue(currentTime);
4003
- setHour(currentTime.hour);
4015
+ setZoneIsPM(zoneIsPM);
4016
+ setHour(hour);
4004
4017
  setMinute(currentTime.minute);
4018
+ refreshClock(selectionIsHours ? hour : currentTime.minute);
4005
4019
  onListener?.({
4006
4020
  event: PickerListenerEvent.Select,
4007
4021
  value: currentTime
4008
4022
  });
4009
- }, [formControl, onListener]);
4023
+ }, [formControl, onListener, selectionIsHours, refreshClock]);
4010
4024
  const onSelect = useCallback(() => {
4011
4025
  const hourValue = zoneIsPM
4012
4026
  ? hour !== 12
@@ -4023,10 +4037,10 @@ function RlsPickerClockComponent({ formControl, onListener, rlsTheme, time }) {
4023
4037
  value: timeValue
4024
4038
  });
4025
4039
  }, [formControl, hour, minute, zoneIsPM, onListener]);
4026
- return (jsxRuntimeExports.jsxs("div", { className: "rls-picker-clock", "rls-theme": rlsTheme, children: [jsxRuntimeExports.jsxs("div", { className: "rls-picker-clock__header", children: [jsxRuntimeExports.jsxs("div", { className: "rls-picker-clock__title", children: [jsxRuntimeExports.jsx("span", { className: classNameHour, onClick: onClickHour, children: hourFormat }), jsxRuntimeExports.jsx("span", { className: "rls-picker-clock__title__separator", children: ":" }), jsxRuntimeExports.jsx("span", { className: classNameMinute, onClick: onClickMinute, children: minuteFormat })] }), jsxRuntimeExports.jsxs("div", { className: "rls-picker-clock__zone", children: [jsxRuntimeExports.jsx("span", { className: classNameAM, onClick: onClickAM, children: "AM" }), jsxRuntimeExports.jsx("span", { className: classNamePM, onClick: onClickPM, children: "PM" })] })] }), jsxRuntimeExports.jsx("div", { className: "rls-picker-clock__body", children: jsxRuntimeExports.jsxs("div", { className: "rls-picker-clock__plate", ref: plateElement, onMouseDown: onActiveSelection, onMouseMove: onMouseSelection, onMouseUp: onInactiveSelection, onMouseLeave: onInactiveSelection, onTouchStart: onActiveSelection, onTouchMove: onTouchSelection, onTouchEnd: onInactiveSelection, children: [jsxRuntimeExports.jsx("div", { className: "rls-picker-clock__canvas", children: jsxRuntimeExports.jsx("svg", { width: "120rem", height: "120rem", children: jsxRuntimeExports.jsxs("g", { style: {
4040
+ return (jsxRuntimeExports.jsxs("div", { className: "rls-picker-clock", "rls-theme": rlsTheme, children: [jsxRuntimeExports.jsxs("div", { className: "rls-picker-clock__header", children: [jsxRuntimeExports.jsxs("div", { className: "rls-picker-clock__title", children: [jsxRuntimeExports.jsx("span", { className: classNameHour, onClick: onClickHour, children: hourFormat }), jsxRuntimeExports.jsx("span", { className: "rls-picker-clock__title__separator", children: ":" }), jsxRuntimeExports.jsx("span", { className: classNameMinute, onClick: onClickMinute, children: minuteFormat })] }), jsxRuntimeExports.jsxs("div", { className: "rls-picker-clock__zone", children: [jsxRuntimeExports.jsx("span", { className: classNameAM, onClick: onClickAM, children: "AM" }), jsxRuntimeExports.jsx("span", { className: classNamePM, onClick: onClickPM, children: "PM" })] })] }), jsxRuntimeExports.jsx("div", { className: "rls-picker-clock__body", children: jsxRuntimeExports.jsxs("div", { className: "rls-picker-clock__plate", ref: plateElement, onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: onPointerUp, onPointerCancel: onPointerCancel, children: [jsxRuntimeExports.jsx("div", { className: "rls-picker-clock__canvas", children: jsxRuntimeExports.jsx("svg", { width: "120rem", height: "120rem", children: jsxRuntimeExports.jsxs("g", { style: {
4027
4041
  transform: `translate(${DIAL_RADIUS}rem, ${DIAL_RADIUS}rem)`
4028
4042
  }, children: [jsxRuntimeExports.jsx("line", { ref: lineElement, className: "rls-picker-clock__canvas__line", x1: "-1", y1: "-1" }), jsxRuntimeExports.jsx("circle", { ref: centerElement, className: "rls-picker-clock__canvas__center", r: "4", cx: "-1", cy: "-1" }), jsxRuntimeExports.jsx("circle", { ref: indicatorElement, className: "rls-picker-clock__canvas__indicator", r: `${TICK_RADIUS}rem` }), jsxRuntimeExports.jsx("circle", { ref: pointElement, className: "rls-picker-clock__canvas__point", r: "3.5" })] }) }) }), jsxRuntimeExports.jsx("div", { className: "rls-picker-clock__hours", children: CLOCK_VALUES.map((value) => {
4029
- return (jsxRuntimeExports.jsx(RlsPickerClockTick, { value: value, selectionIsHours: selectionIsHours, onValue: onClickValue }, value));
4043
+ return (jsxRuntimeExports.jsx(RlsPickerClockTick, { value: value, selectionIsHours: selectionIsHours }, value));
4030
4044
  }) })] }) }), jsxRuntimeExports.jsxs("div", { className: "rls-picker-clock__footer", children: [jsxRuntimeExports.jsx("div", { className: "rls-picker-clock__footer--cancel", children: jsxRuntimeExports.jsx(RlsButton, { type: "flat", onClick: onCancel, children: labels.timeActionCancel }) }), jsxRuntimeExports.jsx("div", { className: "rls-picker-clock__footer--today", children: jsxRuntimeExports.jsx(RlsButtonAction, { icon: "timer", onClick: onCurrentTime }) }), jsxRuntimeExports.jsx("div", { className: "rls-picker-clock__footer--ok", children: jsxRuntimeExports.jsx(RlsButton, { type: "raised", onClick: onSelect, children: labels.timeActionSelect }) })] })] }));
4031
4045
  }
4032
4046
  const RlsPickerClock = memo(RlsPickerClockComponent);
@@ -4840,10 +4854,6 @@ function calculateImgDimension(image, dimension) {
4840
4854
  }
4841
4855
  function RlsImageEditorComponent(props) {
4842
4856
  const [selection, setSelection] = useState(props.selection ?? 60);
4843
- const [labels, setLabels] = useState({
4844
- actionRestore: reactI18n('editorImageActionRestore'),
4845
- actionSelect: reactI18n('editorImageActionSelect')
4846
- });
4847
4857
  const refBody = useRef(null);
4848
4858
  const refImage = useRef(null);
4849
4859
  const refSelection = useRef(null);
@@ -4968,16 +4978,9 @@ function RlsImageEditorComponent(props) {
4968
4978
  refreshImageStyle();
4969
4979
  refreshSelectionStyle(selection);
4970
4980
  };
4971
- const unsubscription = i18nSubscribe(() => {
4972
- setLabels({
4973
- actionRestore: reactI18n('editorImageActionRestore'),
4974
- actionSelect: reactI18n('editorImageActionSelect')
4975
- });
4976
- });
4977
4981
  window.addEventListener('resize', refreshImageStyle);
4978
4982
  return () => {
4979
4983
  window.removeEventListener('resize', refreshImageStyle);
4980
- unsubscription();
4981
4984
  };
4982
4985
  }, []);
4983
4986
  useEffect(() => {
@@ -5040,7 +5043,7 @@ function RlsImageEditorComponent(props) {
5040
5043
  context?.putImageData(originalImage.current, 0, 0);
5041
5044
  }
5042
5045
  }, []);
5043
- return (jsxRuntimeExports.jsxs("div", { className: "rls-image-editor", children: [jsxRuntimeExports.jsx("div", { ref: refBody, className: "rls-image-editor__body", children: jsxRuntimeExports.jsxs("div", { ref: refImage, className: "rls-image-editor__body__image", children: [jsxRuntimeExports.jsx("div", { ref: refSelection, className: "rls-image-editor__body__selection" }), jsxRuntimeExports.jsx("div", { ref: refOverlayTop, className: "rls-image-editor__body__overlay--top" }), jsxRuntimeExports.jsx("div", { ref: refOverlayRight, className: "rls-image-editor__body__overlay--right" }), jsxRuntimeExports.jsx("div", { ref: refOverlayBottom, className: "rls-image-editor__body__overlay--bottom" }), jsxRuntimeExports.jsx("div", { ref: refOverlayLeft, className: "rls-image-editor__body__overlay--left" }), jsxRuntimeExports.jsx("canvas", { ref: refCanvas })] }) }), jsxRuntimeExports.jsxs("div", { className: "rls-image-editor__footer", children: [jsxRuntimeExports.jsx("div", { className: "rls-image-editor__sliders", children: jsxRuntimeExports.jsx(RlsSlider, { prefixIcon: "external-link", value: selection, minValue: MIN_SLIDER_VALUE, maxValue: MAX_SLIDER_VALUE, onValue: setSelection, disabled: props.disabled }) }), jsxRuntimeExports.jsxs("div", { className: "rls-image-editor__actions", children: [props.children, jsxRuntimeExports.jsx(RlsButton, { type: "flat", rlsTheme: "standard", prefixIcon: "refresh", onClick: onRestore, disabled: props.disabled, children: labels.actionRestore }), jsxRuntimeExports.jsx(RlsButton, { type: "raised", rlsTheme: "primary", prefixIcon: "crop", onClick: onCropImage, disabled: props.disabled, children: labels.actionSelect })] })] }), jsxRuntimeExports.jsx("canvas", { ref: refPicture })] }));
5046
+ return (jsxRuntimeExports.jsxs("div", { className: "rls-image-editor", children: [jsxRuntimeExports.jsx("div", { ref: refBody, className: "rls-image-editor__body", children: jsxRuntimeExports.jsxs("div", { ref: refImage, className: "rls-image-editor__body__image", children: [jsxRuntimeExports.jsx("div", { ref: refSelection, className: "rls-image-editor__body__selection" }), jsxRuntimeExports.jsx("div", { ref: refOverlayTop, className: "rls-image-editor__body__overlay--top" }), jsxRuntimeExports.jsx("div", { ref: refOverlayRight, className: "rls-image-editor__body__overlay--right" }), jsxRuntimeExports.jsx("div", { ref: refOverlayBottom, className: "rls-image-editor__body__overlay--bottom" }), jsxRuntimeExports.jsx("div", { ref: refOverlayLeft, className: "rls-image-editor__body__overlay--left" }), jsxRuntimeExports.jsx("canvas", { ref: refCanvas })] }) }), jsxRuntimeExports.jsxs("div", { className: "rls-image-editor__footer", children: [jsxRuntimeExports.jsx("div", { className: "rls-image-editor__sliders", children: jsxRuntimeExports.jsx(RlsSlider, { prefixIcon: "crop", value: selection, minValue: MIN_SLIDER_VALUE, maxValue: MAX_SLIDER_VALUE, onValue: setSelection, disabled: props.disabled }) }), jsxRuntimeExports.jsxs("div", { className: "rls-image-editor__actions", children: [props.children, jsxRuntimeExports.jsx("button", { className: "rls-image-editor__action", "rls-theme": "standard", onClick: onRestore, disabled: props.disabled, children: jsxRuntimeExports.jsx(RlsIcon, { value: "refresh" }) }), jsxRuntimeExports.jsx("button", { className: "rls-image-editor__action", "rls-theme": "success", onClick: onCropImage, disabled: props.disabled, children: jsxRuntimeExports.jsx(RlsIcon, { value: "checkmark-circle-out" }) })] })] }), jsxRuntimeExports.jsx("canvas", { ref: refPicture })] }));
5044
5047
  }
5045
5048
  const RlsImageEditor = memo(RlsImageEditorComponent);
5046
5049
 
@@ -5098,7 +5101,7 @@ function useImageEditorController(options) {
5098
5101
  setSrcEditor(undefined);
5099
5102
  }, []);
5100
5103
  const RlsImageEditorChooser = useMemo(() => {
5101
- return (srcEditor && (jsxRuntimeExports.jsx(RlsImageEditorModal, { visible: true, src: srcEditor, formControl: options.formControl, maxWidth: options.maxWidth, quality: options.quality, ratio: options.ratio, selection: options.selection, onValue: onEditorValue, children: jsxRuntimeExports.jsx(RlsButton, { type: "flat", rlsTheme: "danger", onClick: onCancel, disabled: options.disabled, children: labels.actionCancel }) })));
5104
+ return (srcEditor && (jsxRuntimeExports.jsx(RlsImageEditorModal, { visible: true, src: srcEditor, formControl: options.formControl, maxWidth: options.maxWidth, quality: options.quality, ratio: options.ratio, selection: options.selection, onValue: onEditorValue, children: jsxRuntimeExports.jsx("button", { className: "rls-image-editor__action", "rls-theme": "danger", onClick: onCancel, disabled: options.disabled, children: jsxRuntimeExports.jsx(RlsIcon, { value: "close" }) }) })));
5102
5105
  }, [
5103
5106
  srcEditor,
5104
5107
  labels,