@faststore/components 2.0.53-alpha.0 → 2.0.56-alpha.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.
Files changed (58) hide show
  1. package/dist/atoms/Slider/Slider.d.ts +71 -0
  2. package/dist/atoms/Slider/Slider.js +57 -0
  3. package/dist/atoms/Slider/Slider.js.map +1 -0
  4. package/dist/atoms/Slider/index.d.ts +2 -0
  5. package/dist/atoms/Slider/index.js +2 -0
  6. package/dist/atoms/Slider/index.js.map +1 -0
  7. package/dist/hooks/UIProvider.d.ts +33 -0
  8. package/dist/hooks/UIProvider.js +74 -0
  9. package/dist/hooks/UIProvider.js.map +1 -0
  10. package/dist/hooks/index.d.ts +3 -0
  11. package/dist/hooks/index.js +4 -0
  12. package/dist/hooks/index.js.map +1 -0
  13. package/dist/hooks/useFadeEffect.d.ts +5 -0
  14. package/dist/hooks/useFadeEffect.js +18 -0
  15. package/dist/hooks/useFadeEffect.js.map +1 -0
  16. package/dist/hooks/useTrapFocus.d.ts +8 -0
  17. package/dist/hooks/useTrapFocus.js +75 -0
  18. package/dist/hooks/useTrapFocus.js.map +1 -0
  19. package/dist/index.d.ts +7 -0
  20. package/dist/index.js +5 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/molecules/Modal/Modal.d.ts +34 -0
  23. package/dist/molecules/Modal/Modal.js +38 -0
  24. package/dist/molecules/Modal/Modal.js.map +1 -0
  25. package/dist/molecules/Modal/ModalBody.d.ts +6 -0
  26. package/dist/molecules/Modal/ModalBody.js +4 -0
  27. package/dist/molecules/Modal/ModalBody.js.map +1 -0
  28. package/dist/molecules/Modal/ModalContent.d.ts +10 -0
  29. package/dist/molecules/Modal/ModalContent.js +23 -0
  30. package/dist/molecules/Modal/ModalContent.js.map +1 -0
  31. package/dist/molecules/Modal/ModalHeader.d.ts +19 -0
  32. package/dist/molecules/Modal/ModalHeader.js +11 -0
  33. package/dist/molecules/Modal/ModalHeader.js.map +1 -0
  34. package/dist/molecules/Modal/index.d.ts +5 -0
  35. package/dist/molecules/Modal/index.js +4 -0
  36. package/dist/molecules/Modal/index.js.map +1 -0
  37. package/dist/molecules/Table/Table.d.ts +1 -1
  38. package/dist/organisms/PriceRange/PriceRange.d.ts +38 -0
  39. package/dist/organisms/PriceRange/PriceRange.js +75 -0
  40. package/dist/organisms/PriceRange/PriceRange.js.map +1 -0
  41. package/dist/organisms/PriceRange/index.d.ts +2 -0
  42. package/dist/organisms/PriceRange/index.js +2 -0
  43. package/dist/organisms/PriceRange/index.js.map +1 -0
  44. package/package.json +2 -2
  45. package/src/atoms/Slider/Slider.tsx +220 -0
  46. package/src/atoms/Slider/index.ts +2 -0
  47. package/src/hooks/UIProvider.tsx +152 -0
  48. package/src/hooks/index.ts +3 -0
  49. package/src/hooks/useFadeEffect.ts +21 -0
  50. package/src/hooks/useTrapFocus.ts +108 -0
  51. package/src/index.ts +10 -0
  52. package/src/molecules/Modal/Modal.tsx +106 -0
  53. package/src/molecules/Modal/ModalBody.tsx +13 -0
  54. package/src/molecules/Modal/ModalContent.tsx +90 -0
  55. package/src/molecules/Modal/ModalHeader.tsx +47 -0
  56. package/src/molecules/Modal/index.tsx +5 -0
  57. package/src/organisms/PriceRange/PriceRange.tsx +188 -0
  58. package/src/organisms/PriceRange/index.ts +2 -0
@@ -0,0 +1,19 @@
1
+ import { HTMLAttributes } from 'react';
2
+ import { IconButtonProps } from '../IconButton';
3
+ export interface ModalHeaderProps extends HTMLAttributes<HTMLDivElement> {
4
+ /**
5
+ * Title for header modal.
6
+ */
7
+ title: string;
8
+ /**
9
+ * Description for header modal.
10
+ */
11
+ description?: string;
12
+ /**
13
+ * Props for the Close Button component.
14
+ */
15
+ closeButtonProps?: Partial<Omit<IconButtonProps, 'onClick'>>;
16
+ onClose?: () => void;
17
+ }
18
+ declare const ModalHeader: ({ onClose, title, closeButtonProps, description, }: ModalHeaderProps) => JSX.Element;
19
+ export default ModalHeader;
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import { X } from '../../assets';
3
+ import IconButton from '../IconButton';
4
+ const ModalHeader = ({ onClose, title, closeButtonProps = {}, description, }) => {
5
+ return (React.createElement("header", { "data-fs-modal-header": true },
6
+ onClose && (React.createElement(IconButton, { onClick: () => onClose?.(), "data-fs-modal-header-close-button": true, icon: React.createElement(X, null), "aria-label": "Close modal", ...closeButtonProps })),
7
+ React.createElement("p", { "data-fs-modal-header-title": true }, title),
8
+ description && React.createElement("p", { "data-fs-modal-header-description": true }, description)));
9
+ };
10
+ export default ModalHeader;
11
+ //# sourceMappingURL=ModalHeader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ModalHeader.js","sourceRoot":"","sources":["../../../src/molecules/Modal/ModalHeader.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyB,MAAM,OAAO,CAAA;AAC7C,OAAO,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAChC,OAAO,UAA+B,MAAM,eAAe,CAAA;AAqB3D,MAAM,WAAW,GAAG,CAAC,EACnB,OAAO,EACP,KAAK,EACL,gBAAgB,GAAG,EAAE,EACrB,WAAW,GACM,EAAE,EAAE;IACrB,OAAO,CACL;QACG,OAAO,IAAI,CACV,oBAAC,UAAU,IACT,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE,6CAE1B,IAAI,EAAE,oBAAC,CAAC,OAAG,gBACA,aAAa,KACpB,gBAAgB,GACpB,CACH;QACD,iEAA+B,KAAK,CAAK;QACxC,WAAW,IAAI,uEAAqC,WAAW,CAAK,CAC9D,CACV,CAAA;AACH,CAAC,CAAA;AAED,eAAe,WAAW,CAAA"}
@@ -0,0 +1,5 @@
1
+ export { default } from './Modal';
2
+ export { default as ModalHeader } from './ModalHeader';
3
+ export { default as ModalBody } from './ModalBody';
4
+ export type { ModalProps } from './Modal';
5
+ export type { ModalHeaderProps } from './ModalHeader';
@@ -0,0 +1,4 @@
1
+ export { default } from './Modal';
2
+ export { default as ModalHeader } from './ModalHeader';
3
+ export { default as ModalBody } from './ModalBody';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/molecules/Modal/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,eAAe,CAAA;AACtD,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,aAAa,CAAA"}
@@ -22,5 +22,5 @@ export interface TableProps extends DetailedHTMLProps<TableHTMLAttributes<HTMLTa
22
22
  */
23
23
  nonce?: string | undefined;
24
24
  }
25
- declare const Table: React.ForwardRefExoticComponent<Pick<PropsWithChildren<TableProps>, "slot" | "style" | "summary" | "title" | "testId" | "variant" | "aria-label" | "children" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "className" | "contentEditable" | "contextMenu" | "dir" | "draggable" | "hidden" | "id" | "lang" | "nonce" | "placeholder" | "spellCheck" | "tabIndex" | "translate" | "radioGroup" | "role" | "about" | "datatype" | "inlist" | "prefix" | "property" | "resource" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "color" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "is" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onResize" | "onResizeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClick" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDrag" | "onDragCapture" | "onDragEnd" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStart" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerEnterCapture" | "onPointerLeave" | "onPointerLeaveCapture" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStart" | "onAnimationStartCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "width" | "key" | "align" | "bgcolor" | "border" | "cellPadding" | "cellSpacing" | "frame" | "rules"> & React.RefAttributes<HTMLTableElement>>;
25
+ declare const Table: React.ForwardRefExoticComponent<Pick<PropsWithChildren<TableProps>, "slot" | "style" | "summary" | "title" | "children" | "testId" | "variant" | "aria-label" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "className" | "contentEditable" | "contextMenu" | "dir" | "draggable" | "hidden" | "id" | "lang" | "nonce" | "placeholder" | "spellCheck" | "tabIndex" | "translate" | "radioGroup" | "role" | "about" | "datatype" | "inlist" | "prefix" | "property" | "resource" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "color" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "is" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onResize" | "onResizeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClick" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDrag" | "onDragCapture" | "onDragEnd" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStart" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerEnterCapture" | "onPointerLeave" | "onPointerLeaveCapture" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStart" | "onAnimationStartCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "width" | "key" | "align" | "bgcolor" | "border" | "cellPadding" | "cellSpacing" | "frame" | "rules"> & React.RefAttributes<HTMLTableElement>>;
26
26
  export default Table;
@@ -0,0 +1,38 @@
1
+ import React from 'react';
2
+ import type { AriaAttributes } from 'react';
3
+ import type { PriceProps, SliderProps } from '../../index';
4
+ export type PriceRangeProps = Omit<SliderProps, 'absoluteValuesLabel'> & {
5
+ /**
6
+ * The current use case variant for prices.
7
+ */
8
+ variant?: PriceProps['variant'];
9
+ /**
10
+ * Formatter function that transforms the raw price value and render the result.
11
+ */
12
+ formatter: PriceProps['formatter'];
13
+ /**
14
+ * Defines a string value that labels the current element.
15
+ */
16
+ 'aria-label'?: AriaAttributes['aria-label'];
17
+ };
18
+ type PriceRangeRefType = {
19
+ setPriceRangeValues: (values: {
20
+ min: number;
21
+ max: number;
22
+ }) => void;
23
+ };
24
+ declare const PriceRange: React.ForwardRefExoticComponent<Omit<SliderProps, "absoluteValuesLabel"> & {
25
+ /**
26
+ * The current use case variant for prices.
27
+ */
28
+ variant?: PriceProps['variant'];
29
+ /**
30
+ * Formatter function that transforms the raw price value and render the result.
31
+ */
32
+ formatter: PriceProps['formatter'];
33
+ /**
34
+ * Defines a string value that labels the current element.
35
+ */
36
+ 'aria-label'?: AriaAttributes['aria-label'];
37
+ } & React.RefAttributes<PriceRangeRefType | undefined>>;
38
+ export default PriceRange;
@@ -0,0 +1,75 @@
1
+ import React, { useRef, useState, useImperativeHandle, forwardRef } from 'react';
2
+ import { Price, Slider, InputField } from '../../index';
3
+ const PriceRange = forwardRef(function PriceRange({ formatter, max, min, step = 1, onChange, onEnd, testId = 'fs-price-range', variant, 'aria-label': ariaLabel, ...otherProps }, ref) {
4
+ const sliderRef = useRef();
5
+ useImperativeHandle(ref, () => ({
6
+ setPriceRangeValues: (values) => {
7
+ onChange?.(values);
8
+ sliderRef.current?.setSliderValues(values);
9
+ },
10
+ }));
11
+ const inputMinRef = useRef(null);
12
+ const inputMaxRef = useRef(null);
13
+ const [inputMinError, setInputMinError] = useState();
14
+ const [inputMaxError, setInputMaxError] = useState();
15
+ const [priceRange, setPriceRange] = useState({
16
+ min: Math.floor(min.selected),
17
+ max: Math.round(max.selected),
18
+ });
19
+ function onChangePriceRange(value) {
20
+ setInputMinError(undefined);
21
+ setInputMaxError(undefined);
22
+ setPriceRange({ min: value.min, max: value.max });
23
+ if (inputMinRef.current?.value) {
24
+ inputMinRef.current.value = String(value.min);
25
+ }
26
+ if (inputMaxRef.current?.value) {
27
+ inputMaxRef.current.value = String(value.max);
28
+ }
29
+ }
30
+ function onChangeInputMin(value) {
31
+ setInputMinError(undefined);
32
+ if (Number(value) < Math.floor(min.absolute)) {
33
+ return;
34
+ }
35
+ if (Number(value) > Math.floor(priceRange.max)) {
36
+ setInputMinError(`Min price can't be greater than max`);
37
+ }
38
+ setPriceRange({ ...priceRange, min: Number(value) });
39
+ sliderRef.current?.setSliderValues({
40
+ ...priceRange,
41
+ min: Number(value),
42
+ });
43
+ }
44
+ function onChangeInputMax(value) {
45
+ setInputMaxError(undefined);
46
+ if (Number(value) > Math.round(max.absolute)) {
47
+ return;
48
+ }
49
+ if (Number(value) < Math.round(priceRange.min)) {
50
+ setInputMaxError(`Max price can't be smaller than min`);
51
+ }
52
+ setPriceRange({ ...priceRange, max: Number(value) });
53
+ sliderRef.current?.setSliderValues({
54
+ ...priceRange,
55
+ max: Number(value),
56
+ });
57
+ }
58
+ return (React.createElement("div", { "data-fs-price-range": true, "data-testid": testId, ...otherProps },
59
+ React.createElement(Slider, { ref: sliderRef, min: min, max: max, step: step, onEnd: (value) => {
60
+ onEnd?.(value);
61
+ onChangePriceRange(value);
62
+ }, "aria-label": ariaLabel, onChange: (value) => onChange?.(value), absoluteValuesLabel: {
63
+ min: (React.createElement(Price, { value: Math.floor(min.absolute), variant: variant, formatter: formatter })),
64
+ max: (React.createElement(Price, { value: Math.round(max.absolute), variant: variant, formatter: formatter })),
65
+ }, minValueLabelComponent: (minValue) => {
66
+ return (React.createElement(Price, { value: minValue, variant: variant, formatter: formatter }));
67
+ }, maxValueLabelComponent: (maxValue) => {
68
+ return (React.createElement(Price, { value: maxValue, variant: variant, formatter: formatter }));
69
+ } }),
70
+ React.createElement("div", { "data-fs-price-range-inputs": true },
71
+ React.createElement(InputField, { id: "price-range-min", step: step, label: "Min", type: "number", inputMode: "numeric", error: inputMinError, inputRef: inputMinRef, min: Math.floor(min.absolute), max: priceRange.max, value: priceRange.min, onChange: (e) => onChangeInputMin(e.target.value), onBlur: () => !inputMinError && onEnd?.(priceRange) }),
72
+ React.createElement(InputField, { id: "price-range-max", label: "Max", step: step, type: "number", inputMode: "numeric", error: inputMaxError, inputRef: inputMaxRef, max: Math.round(max.absolute), min: priceRange.min, value: priceRange.max, onChange: (e) => onChangeInputMax(e.target.value), onBlur: () => !inputMaxError && onEnd?.(priceRange) }))));
73
+ });
74
+ export default PriceRange;
75
+ //# sourceMappingURL=PriceRange.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PriceRange.js","sourceRoot":"","sources":["../../../src/organisms/PriceRange/PriceRange.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { default } from './PriceRange';
2
+ export type { PriceRangeProps } from './PriceRange';
@@ -0,0 +1,2 @@
1
+ export { default } from './PriceRange';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/organisms/PriceRange/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faststore/components",
3
- "version": "2.0.53-alpha.0",
3
+ "version": "2.0.56-alpha.0",
4
4
  "module": "dist/index.js",
5
5
  "typings": "dist/index.d.ts",
6
6
  "author": "Emerson Laurentino @emersonlaurentino",
@@ -30,5 +30,5 @@
30
30
  "node": "16.18.0",
31
31
  "yarn": "1.19.1"
32
32
  },
33
- "gitHead": "a9a211450d2173e08fd8b8dcf4a6da4d78590a71"
33
+ "gitHead": "6afa204ec28032832e3867f805fbc5bcbbed4aab"
34
34
  }
@@ -0,0 +1,220 @@
1
+ /**
2
+ * This code is inspired by the work of [sandra-lewis](https://codesandbox.io/u/sandra-lewis)
3
+ */
4
+ import React, {
5
+ useState,
6
+ useMemo,
7
+ useImperativeHandle,
8
+ forwardRef,
9
+ } from 'react'
10
+ import type { ReactNode } from 'react'
11
+
12
+ interface Range {
13
+ absolute: number
14
+ selected: number
15
+ }
16
+
17
+ interface RangeLabel {
18
+ min: string | ReactNode
19
+ max: string | ReactNode
20
+ }
21
+
22
+ export type SliderProps = {
23
+ /**
24
+ * ID to find this component in testing tools (e.g.: cypress, testing library, and jest).
25
+ *
26
+ * @default 'fs-slider'
27
+ */
28
+ testId?: string
29
+ /**
30
+ * The minimum value of the slider.
31
+ */
32
+ min: Range
33
+ /**
34
+ * The maximum value of the slider.
35
+ */
36
+ max: Range
37
+ /**
38
+ * Specifies the number interval to be used in the inputs.
39
+ */
40
+ step?: number
41
+ /**
42
+ * Specifies the absolute values labels.
43
+ */
44
+ absoluteValuesLabel: RangeLabel
45
+ /**
46
+ * Callback that fires when the slider value changes.
47
+ */
48
+ onChange?: (value: { min: number; max: number }) => void
49
+ /**
50
+ * Callback that fires when the slider value ends changing.
51
+ */
52
+ onEnd?: (value: { min: number; max: number }) => void
53
+ /**
54
+ * A function used to set a human-readable value text based on the slider's current value.
55
+ */
56
+ getAriaValueText?(value: number, thumb?: 'min' | 'max'): string
57
+ /**
58
+ * Component that renders min value label above the left thumb.
59
+ */
60
+ minValueLabelComponent?: (minValue: number) => ReactNode
61
+ /**
62
+ * Component that renders max value label above the right thumb.
63
+ */
64
+ maxValueLabelComponent?: (maxValue: number) => ReactNode
65
+ }
66
+
67
+ type SliderRefType = {
68
+ setSliderValues: (values: { min: number; max: number }) => void
69
+ }
70
+
71
+ const percent = (value: number, min: number, max: number) =>
72
+ Math.round(((value - min) / (max - min)) * 100)
73
+
74
+ const Slider = forwardRef<SliderRefType | undefined, SliderProps>(
75
+ function Slider(
76
+ {
77
+ min,
78
+ max,
79
+ absoluteValuesLabel,
80
+ onChange,
81
+ onEnd,
82
+ testId = 'fs-slider',
83
+ getAriaValueText,
84
+ step,
85
+ minValueLabelComponent,
86
+ maxValueLabelComponent,
87
+ ...otherProps
88
+ },
89
+ ref
90
+ ) {
91
+ const widthPercent = useMemo(
92
+ () => (max.absolute - min.absolute) / 100,
93
+ [max.absolute, min.absolute]
94
+ )
95
+ const [minPercent, setMinPercent] = useState(() =>
96
+ percent(min.selected, min.absolute, max.absolute)
97
+ )
98
+
99
+ const [maxPercent, setMaxPercent] = useState(() =>
100
+ percent(max.selected, min.absolute, max.absolute)
101
+ )
102
+
103
+ const [minVal, setMinVal] = useState(() =>
104
+ Math.floor(min.absolute + minPercent * widthPercent)
105
+ )
106
+ const [maxVal, setMaxVal] = useState(() =>
107
+ Math.round(min.absolute + maxPercent * widthPercent)
108
+ )
109
+
110
+ const percentage = (value: number) => (value / max.absolute) * 100
111
+
112
+ useImperativeHandle(ref, () => ({
113
+ setSliderValues: (values: { min: number; max: number }) => {
114
+ const sliderMinValue = Math.min(Number(values.min), maxVal)
115
+ setMinVal(sliderMinValue)
116
+ setMinPercent(percent(sliderMinValue, min.absolute, max.absolute))
117
+
118
+ if (values.max > max.absolute) {
119
+ setMaxVal(max.absolute)
120
+ setMaxPercent(percent(max.absolute, min.absolute, max.absolute))
121
+ return
122
+ }
123
+
124
+ const sliderMaxValue = Math.max(Number(values.max), minVal)
125
+ setMaxVal(sliderMaxValue)
126
+ setMaxPercent(percent(sliderMaxValue, min.absolute, max.absolute))
127
+ },
128
+ }))
129
+
130
+ return (
131
+ <div data-fs-slider data-testid={testId}>
132
+ <div data-fs-slider-absolute-values>
133
+ <span>{absoluteValuesLabel.min}</span>
134
+ <span>{absoluteValuesLabel.max}</span>
135
+ </div>
136
+ <div data-fs-slider-wrapper>
137
+ <div
138
+ data-fs-slider-range
139
+ style={{
140
+ left: `${minPercent}%`,
141
+ width: `${maxPercent - minPercent}%`,
142
+ }}
143
+ />
144
+ <input
145
+ type="range"
146
+ min={Math.floor(min.absolute)}
147
+ max={Math.round(max.absolute)}
148
+ value={minVal}
149
+ step={step}
150
+ onMouseUp={() => onEnd?.({ min: minVal, max: maxVal })}
151
+ onTouchEnd={() => onEnd?.({ min: minVal, max: maxVal })}
152
+ onChange={(event) => {
153
+ const minValue = Math.min(Number(event.target.value), maxVal)
154
+
155
+ setMinVal(minValue)
156
+ setMinPercent(percent(minValue, min.absolute, max.absolute))
157
+ onChange?.({ min: minValue, max: maxVal })
158
+ }}
159
+ data-fs-slider-thumb="left"
160
+ aria-valuemin={min.absolute}
161
+ aria-valuemax={max.absolute}
162
+ aria-valuenow={minVal}
163
+ aria-label={String(minVal)}
164
+ aria-labelledby={getAriaValueText?.(minVal, 'min')}
165
+ {...otherProps}
166
+ />
167
+ {minValueLabelComponent && (
168
+ <span
169
+ data-fs-slider-value-label="min"
170
+ style={{
171
+ left: `calc(${percentage(minVal)}% + (${
172
+ 8 - percentage(minVal) * 0.2
173
+ }px))`,
174
+ }}
175
+ >
176
+ {minValueLabelComponent(minVal)}
177
+ </span>
178
+ )}
179
+
180
+ <input
181
+ type="range"
182
+ min={Math.floor(min.absolute)}
183
+ max={Math.round(max.absolute)}
184
+ value={maxVal}
185
+ step={step}
186
+ onMouseUp={() => onEnd?.({ min: minVal, max: maxVal })}
187
+ onTouchEnd={() => onEnd?.({ min: minVal, max: maxVal })}
188
+ onChange={(event) => {
189
+ const maxValue = Math.max(Number(event.target.value), minVal)
190
+
191
+ setMaxVal(maxValue)
192
+ setMaxPercent(percent(maxValue, min.absolute, max.absolute))
193
+ onChange?.({ min: minVal, max: maxValue })
194
+ }}
195
+ data-fs-slider-thumb="right"
196
+ aria-valuemin={min.absolute}
197
+ aria-valuemax={max.absolute}
198
+ aria-valuenow={maxVal}
199
+ aria-label={String(maxVal)}
200
+ aria-labelledby={getAriaValueText?.(maxVal, 'max')}
201
+ />
202
+ {maxValueLabelComponent && (
203
+ <span
204
+ data-fs-slider-value-label="max"
205
+ style={{
206
+ left: `calc(${percentage(maxVal)}% + (${
207
+ 8 - percentage(maxVal) * 0.2
208
+ }px))`,
209
+ }}
210
+ >
211
+ {maxValueLabelComponent(maxVal)}
212
+ </span>
213
+ )}
214
+ </div>
215
+ </div>
216
+ )
217
+ }
218
+ )
219
+
220
+ export default Slider
@@ -0,0 +1,2 @@
1
+ export { default } from './Slider'
2
+ export type { SliderProps } from './Slider'
@@ -0,0 +1,152 @@
1
+ import React from 'react'
2
+ import { createContext, useContext, useMemo, useReducer } from 'react'
3
+ import type { PropsWithChildren } from 'react'
4
+
5
+ interface Toast {
6
+ message: string
7
+ status: 'ERROR' | 'WARNING' | 'INFO'
8
+ title?: string
9
+ icon?: string
10
+ }
11
+
12
+ interface State {
13
+ /** Cart sidebar */
14
+ cart: boolean
15
+ /** Region modal */
16
+ modal: boolean
17
+ /** Menu slider */
18
+ navbar: boolean
19
+ /** Search page filter slider */
20
+ filter: boolean
21
+ toasts: Toast[]
22
+ }
23
+
24
+ type UIElement = 'navbar' | 'cart' | 'modal' | 'filter'
25
+
26
+ type Action =
27
+ | {
28
+ type: 'open'
29
+ payload: UIElement
30
+ }
31
+ | {
32
+ type: 'close'
33
+ payload: UIElement
34
+ }
35
+ | {
36
+ type: 'pushToast'
37
+ payload: Toast
38
+ }
39
+ | {
40
+ type: 'popToast'
41
+ }
42
+
43
+ const reducer = (state: State, action: Action): State => {
44
+ const { type } = action
45
+
46
+ switch (type) {
47
+ case 'open': {
48
+ const { payload } = action
49
+
50
+ document.body.classList.add('no-scroll')
51
+
52
+ return {
53
+ ...state,
54
+ [payload]: true,
55
+ }
56
+ }
57
+
58
+ case 'close': {
59
+ const { payload } = action
60
+
61
+ document.body.classList.remove('no-scroll')
62
+
63
+ return {
64
+ ...state,
65
+ [payload]: false,
66
+ }
67
+ }
68
+
69
+ case 'pushToast': {
70
+ return {
71
+ ...state,
72
+ toasts: [...state.toasts, action.payload],
73
+ }
74
+ }
75
+
76
+ case 'popToast': {
77
+ return {
78
+ ...state,
79
+ toasts: state.toasts.slice(1),
80
+ }
81
+ }
82
+
83
+ default:
84
+ throw new Error(`Action ${type} not implemented`)
85
+ }
86
+ }
87
+
88
+ const initializer = (): State => ({
89
+ cart: false,
90
+ modal: false,
91
+ navbar: false,
92
+ filter: false,
93
+ toasts: [],
94
+ })
95
+
96
+ interface Context extends State {
97
+ closeNavbar: () => void
98
+ openNavbar: () => void
99
+ closeFilter: () => void
100
+ openFilter: () => void
101
+ openCart: () => void
102
+ closeCart: () => void
103
+ openModal: () => void
104
+ closeModal: () => void
105
+ pushToast: (data: Toast) => void
106
+ popToast: () => void
107
+ }
108
+
109
+ const UIContext = createContext<Context | undefined>(undefined)
110
+
111
+ function UIProvider({ children }: PropsWithChildren) {
112
+ const [ui, dispatch] = useReducer(reducer, undefined, initializer)
113
+
114
+ const callbacks = useMemo(
115
+ () => ({
116
+ openFilter: () => dispatch({ type: 'open', payload: 'filter' }),
117
+ closeFilter: () => dispatch({ type: 'close', payload: 'filter' }),
118
+ openNavbar: () => dispatch({ type: 'open', payload: 'navbar' }),
119
+ closeNavbar: () => dispatch({ type: 'close', payload: 'navbar' }),
120
+ openCart: () => dispatch({ type: 'open', payload: 'cart' }),
121
+ closeCart: () => dispatch({ type: 'close', payload: 'cart' }),
122
+ openModal: () => dispatch({ type: 'open', payload: 'modal' }),
123
+ closeModal: () => dispatch({ type: 'close', payload: 'modal' }),
124
+ pushToast: (toast: Toast) =>
125
+ dispatch({ type: 'pushToast', payload: toast }),
126
+ popToast: () => dispatch({ type: 'popToast' }),
127
+ }),
128
+ []
129
+ )
130
+
131
+ const value = useMemo(
132
+ () => ({
133
+ ...ui,
134
+ ...callbacks,
135
+ }),
136
+ [callbacks, ui]
137
+ )
138
+
139
+ return <UIContext.Provider value={value}>{children}</UIContext.Provider>
140
+ }
141
+
142
+ export function useUI() {
143
+ const context = useContext(UIContext)
144
+
145
+ if (context === undefined) {
146
+ throw new Error('Missing UI context on React tree')
147
+ }
148
+
149
+ return context
150
+ }
151
+
152
+ export default UIProvider
@@ -0,0 +1,3 @@
1
+ export { default as UIProvider, useUI } from './UIProvider'
2
+ export { useFadeEffect } from './useFadeEffect'
3
+ export { useTrapFocus } from './useTrapFocus'
@@ -0,0 +1,21 @@
1
+ import { useState, useCallback, useEffect } from 'react'
2
+
3
+ export const useFadeEffect = () => {
4
+ const [fade, setFade] = useState<'in' | 'out'>('out')
5
+ const fadeOut = useCallback(() => setFade('out'), [])
6
+ const fadeIn = useCallback(() => setFade('in'), [])
7
+
8
+ useEffect(() => {
9
+ fadeIn()
10
+
11
+ return () => {
12
+ fadeOut()
13
+ }
14
+ }, [fadeIn, fadeOut])
15
+
16
+ return {
17
+ fade,
18
+ fadeIn,
19
+ fadeOut,
20
+ }
21
+ }