@faststore/components 3.0.135 → 3.0.144

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 (70) hide show
  1. package/dist/cjs/atoms/Button/index.d.ts +1 -1
  2. package/dist/cjs/molecules/Dropdown/Dropdown.d.ts +1 -1
  3. package/dist/cjs/molecules/Dropdown/Dropdown.js +20 -16
  4. package/dist/cjs/molecules/Dropdown/Dropdown.js.map +1 -1
  5. package/dist/cjs/molecules/Dropdown/DropdownButton.d.ts +23 -5
  6. package/dist/cjs/molecules/Dropdown/DropdownButton.js +7 -7
  7. package/dist/cjs/molecules/Dropdown/DropdownButton.js.map +1 -1
  8. package/dist/cjs/molecules/Dropdown/DropdownItem.d.ts +9 -0
  9. package/dist/cjs/molecules/Dropdown/DropdownItem.js +8 -23
  10. package/dist/cjs/molecules/Dropdown/DropdownItem.js.map +1 -1
  11. package/dist/cjs/molecules/Dropdown/DropdownMenu.d.ts +7 -3
  12. package/dist/cjs/molecules/Dropdown/DropdownMenu.js +39 -11
  13. package/dist/cjs/molecules/Dropdown/DropdownMenu.js.map +1 -1
  14. package/dist/cjs/molecules/Dropdown/contexts/DropdownContext.d.ts +8 -8
  15. package/dist/cjs/molecules/Dropdown/contexts/DropdownContext.js +1 -1
  16. package/dist/cjs/molecules/Dropdown/contexts/DropdownContext.js.map +1 -1
  17. package/dist/cjs/molecules/Dropdown/hooks/useDropdown.d.ts +1 -1
  18. package/dist/cjs/molecules/Dropdown/hooks/useDropdown.js.map +1 -1
  19. package/dist/cjs/molecules/Dropdown/hooks/useDropdownItem.d.ts +15 -0
  20. package/dist/cjs/molecules/Dropdown/hooks/useDropdownItem.js +37 -0
  21. package/dist/cjs/molecules/Dropdown/hooks/useDropdownItem.js.map +1 -0
  22. package/dist/cjs/molecules/Dropdown/hooks/useDropdownPosition.d.ts +4 -2
  23. package/dist/cjs/molecules/Dropdown/hooks/useDropdownPosition.js +55 -18
  24. package/dist/cjs/molecules/Dropdown/hooks/useDropdownPosition.js.map +1 -1
  25. package/dist/cjs/molecules/Dropdown/hooks/useDropdownTrigger.d.ts +13 -0
  26. package/dist/cjs/molecules/Dropdown/hooks/useDropdownTrigger.js +20 -0
  27. package/dist/cjs/molecules/Dropdown/hooks/useDropdownTrigger.js.map +1 -0
  28. package/dist/cjs/molecules/QuantitySelector/QuantitySelector.js +6 -2
  29. package/dist/cjs/molecules/QuantitySelector/QuantitySelector.js.map +1 -1
  30. package/dist/esm/atoms/Button/index.d.ts +1 -1
  31. package/dist/esm/molecules/Dropdown/Dropdown.d.ts +1 -1
  32. package/dist/esm/molecules/Dropdown/Dropdown.js +20 -16
  33. package/dist/esm/molecules/Dropdown/Dropdown.js.map +1 -1
  34. package/dist/esm/molecules/Dropdown/DropdownButton.d.ts +23 -5
  35. package/dist/esm/molecules/Dropdown/DropdownButton.js +8 -8
  36. package/dist/esm/molecules/Dropdown/DropdownButton.js.map +1 -1
  37. package/dist/esm/molecules/Dropdown/DropdownItem.d.ts +9 -0
  38. package/dist/esm/molecules/Dropdown/DropdownItem.js +9 -24
  39. package/dist/esm/molecules/Dropdown/DropdownItem.js.map +1 -1
  40. package/dist/esm/molecules/Dropdown/DropdownMenu.d.ts +7 -3
  41. package/dist/esm/molecules/Dropdown/DropdownMenu.js +39 -11
  42. package/dist/esm/molecules/Dropdown/DropdownMenu.js.map +1 -1
  43. package/dist/esm/molecules/Dropdown/contexts/DropdownContext.d.ts +8 -8
  44. package/dist/esm/molecules/Dropdown/contexts/DropdownContext.js +1 -1
  45. package/dist/esm/molecules/Dropdown/contexts/DropdownContext.js.map +1 -1
  46. package/dist/esm/molecules/Dropdown/hooks/useDropdown.d.ts +1 -1
  47. package/dist/esm/molecules/Dropdown/hooks/useDropdown.js.map +1 -1
  48. package/dist/esm/molecules/Dropdown/hooks/useDropdownItem.d.ts +15 -0
  49. package/dist/esm/molecules/Dropdown/hooks/useDropdownItem.js +33 -0
  50. package/dist/esm/molecules/Dropdown/hooks/useDropdownItem.js.map +1 -0
  51. package/dist/esm/molecules/Dropdown/hooks/useDropdownPosition.d.ts +4 -2
  52. package/dist/esm/molecules/Dropdown/hooks/useDropdownPosition.js +55 -18
  53. package/dist/esm/molecules/Dropdown/hooks/useDropdownPosition.js.map +1 -1
  54. package/dist/esm/molecules/Dropdown/hooks/useDropdownTrigger.d.ts +13 -0
  55. package/dist/esm/molecules/Dropdown/hooks/useDropdownTrigger.js +16 -0
  56. package/dist/esm/molecules/Dropdown/hooks/useDropdownTrigger.js.map +1 -0
  57. package/dist/esm/molecules/QuantitySelector/QuantitySelector.js +6 -2
  58. package/dist/esm/molecules/QuantitySelector/QuantitySelector.js.map +1 -1
  59. package/package.json +2 -2
  60. package/src/atoms/Button/index.ts +1 -1
  61. package/src/molecules/Dropdown/Dropdown.tsx +27 -18
  62. package/src/molecules/Dropdown/DropdownButton.tsx +45 -32
  63. package/src/molecules/Dropdown/DropdownItem.tsx +39 -47
  64. package/src/molecules/Dropdown/DropdownMenu.tsx +58 -18
  65. package/src/molecules/Dropdown/contexts/DropdownContext.ts +11 -9
  66. package/src/molecules/Dropdown/hooks/useDropdown.ts +3 -3
  67. package/src/molecules/Dropdown/hooks/useDropdownItem.ts +56 -0
  68. package/src/molecules/Dropdown/hooks/useDropdownPosition.ts +62 -19
  69. package/src/molecules/Dropdown/hooks/useDropdownTrigger.ts +26 -0
  70. package/src/molecules/QuantitySelector/QuantitySelector.tsx +6 -1
@@ -1,6 +1,9 @@
1
1
  import { createContext } from 'react'
2
2
 
3
- export type DropdownContextState = {
3
+ export type DropdownContextState<
4
+ T extends HTMLElement = HTMLElement,
5
+ E extends HTMLElement = HTMLElement,
6
+ > = {
4
7
  /**
5
8
  * Control de Dropdown state as Opened (true) or Closed (false).
6
9
  */
@@ -8,7 +11,7 @@ export type DropdownContextState = {
8
11
  /**
9
12
  * Reference to DropdownButton, used to calculate a position for the DropdownMenu.
10
13
  */
11
- dropdownButtonRef: React.RefObject<HTMLButtonElement> | null
14
+ dropdownTriggerRef: React.MutableRefObject<T | null> | null
12
15
  /**
13
16
  * Reference to a selected DropdownItem, used to manipulate focus.
14
17
  */
@@ -16,11 +19,7 @@ export type DropdownContextState = {
16
19
  /**
17
20
  * Array of References to dropdownItems in a DropdownMenu.
18
21
  */
19
- dropdownItemsRef: React.MutableRefObject<HTMLButtonElement[]> | null
20
- /**
21
- * Close DropdownMenu event inherited from Modal.
22
- */
23
- onDismiss?(): void
22
+ dropdownItemsRef: React.MutableRefObject<E[]> | null
24
23
  /**
25
24
  * Function responsible for close the DropdownMenu in this context.
26
25
  */
@@ -33,16 +32,19 @@ export type DropdownContextState = {
33
32
  * Function responsible for switch the the DropdownMenu state in this context.
34
33
  */
35
34
  toggle?(): void
36
-
37
35
  /**
38
36
  * Identifier to be used in aria-controls
39
37
  */
40
38
  id: string
39
+ /**
40
+ * Associates the dropdown trigger element's ref for managing its position and interaction events.
41
+ */
42
+ addDropdownTriggerRef?(ref: T | null): void
41
43
  }
42
44
 
43
45
  const defaultState: DropdownContextState = {
44
46
  isOpen: false,
45
- dropdownButtonRef: null,
47
+ dropdownTriggerRef: null,
46
48
  selectedDropdownItemIndexRef: null,
47
49
  dropdownItemsRef: null,
48
50
  id: 'fs-dropdown',
@@ -7,12 +7,12 @@ import DropdownContext from '../contexts/DropdownContext'
7
7
  * Hook to use the Dropdown context.
8
8
  * @returns Dropdown context.
9
9
  */
10
- export const useDropdown = () => {
11
- const context = useContext<DropdownContextState>(DropdownContext)
10
+ export const useDropdown = <T extends HTMLElement = HTMLElement, E extends HTMLElement = HTMLElement>() => {
11
+ const context = useContext<DropdownContextState<HTMLElement, HTMLElement>>(DropdownContext)
12
12
 
13
13
  if (context === undefined) {
14
14
  throw new Error('Do not use useDropdown hook outside the Dropdown context.')
15
15
  }
16
16
 
17
- return context
17
+ return context as DropdownContextState<T, E>
18
18
  }
@@ -0,0 +1,56 @@
1
+ import React, { useImperativeHandle, useRef, useState } from 'react'
2
+
3
+ import { useDropdown } from './useDropdown'
4
+
5
+ export type UseDropdownItemProps<E extends HTMLElement = HTMLElement> = {
6
+ ref: React.ForwardedRef<E>
7
+ onClick?: React.MouseEventHandler<E>
8
+ dismissOnClick?: boolean
9
+ }
10
+
11
+ export const useDropdownItem = <E extends HTMLElement = HTMLElement>({
12
+ ref,
13
+ onClick,
14
+ dismissOnClick = true,
15
+ }: UseDropdownItemProps<E>) => {
16
+ const { dropdownItemsRef, selectedDropdownItemIndexRef, close } = useDropdown<
17
+ never,
18
+ E
19
+ >()
20
+
21
+ const [dropdownItemIndex, setDropdownItemIndex] = useState(0)
22
+ const dropdownItemRef = useRef<E>()
23
+
24
+ const addToRefs = (el: E) => {
25
+ if (el && !dropdownItemsRef?.current.includes(el)) {
26
+ dropdownItemsRef?.current.push(el)
27
+ setDropdownItemIndex(
28
+ dropdownItemsRef?.current.findIndex((element) => element === el) ?? 0
29
+ )
30
+ }
31
+
32
+ dropdownItemRef.current = el
33
+ }
34
+
35
+ const onFocusItem = () => {
36
+ selectedDropdownItemIndexRef!.current = dropdownItemIndex
37
+ dropdownItemsRef?.current[selectedDropdownItemIndexRef!.current]?.focus()
38
+ }
39
+
40
+ const handleOnClickItem = (event: React.MouseEvent<E, MouseEvent>) => {
41
+ onClick?.(event)
42
+ dismissOnClick && close?.()
43
+ }
44
+
45
+ useImperativeHandle(ref, () => dropdownItemRef.current!, [])
46
+
47
+ return {
48
+ ref: addToRefs,
49
+ onFocus: onFocusItem,
50
+ onMouseEnter: onFocusItem,
51
+ onClick: handleOnClickItem,
52
+ role: 'menuitem',
53
+ tabIndex: -1,
54
+ 'data-index': dropdownItemIndex,
55
+ }
56
+ }
@@ -1,33 +1,76 @@
1
+ import { useEffect, useState } from 'react'
1
2
  import { useDropdown } from './useDropdown'
2
3
 
3
- type DropdownPosition = Pick<React.CSSProperties, 'position' | 'top' | 'left'>
4
+ type DropdownPosition = {
5
+ loading: boolean
6
+ } & Pick<React.CSSProperties, 'position' | 'top' | 'left' | 'right' | 'transform'>
4
7
 
5
8
  /**
6
9
  * Hook used to find the DropdownMenu position in relation to DropdownButton
7
10
  * @returns Style with positions.
8
11
  */
9
- export const useDropdownPosition = (): DropdownPosition => {
10
- const { dropdownButtonRef } = useDropdown()
12
+ export const useDropdownPosition = (align: 'left' | 'center' | 'right' = 'left'): DropdownPosition => {
13
+ const { dropdownTriggerRef, isOpen } = useDropdown()
11
14
 
12
- // Necessary to use this component in SSR
13
- const isBrowser = typeof window !== 'undefined'
15
+ const [positionProps, setPositionProps] = useState({
16
+ top: 0,
17
+ left: 0 as React.CSSProperties['left'],
18
+ right: 'auto',
19
+ transform: 'none',
20
+ loading: true
21
+ })
14
22
 
15
- const buttonRect = dropdownButtonRef?.current?.getBoundingClientRect()
16
- const topLevel = buttonRect?.top ?? 0
17
- const topOffset = buttonRect?.height ?? 0
18
- const leftLevel = buttonRect?.left ?? 0
23
+ useEffect(() => {
24
+ const updateMenuPosition = () => {
25
+ // Necessary to use this component in SSR
26
+ const isBrowser = typeof window !== 'undefined'
19
27
 
20
- // The scroll properties fix the position of DropdownMenu when the scroll is activated.
21
- const scrollTop = isBrowser ? document?.documentElement?.scrollTop : 0
22
- const scrollLeft = isBrowser ? document?.documentElement?.scrollLeft : 0
28
+ if (!dropdownTriggerRef?.current) return
23
29
 
24
- const topPosition = topLevel + topOffset + scrollTop
30
+ const buttonRect = dropdownTriggerRef.current.getBoundingClientRect()
31
+ const topLevel = buttonRect?.top ?? 0
32
+ const topOffset = buttonRect?.height ?? 0
33
+ const leftLevel = buttonRect?.left ?? 0
34
+ const buttonWidth = buttonRect?.width ?? 0
25
35
 
26
- const leftPosition = leftLevel + scrollLeft
36
+ // The scroll properties fix the position of DropdownMenu when the scroll is activated.
37
+ const scrollTop = isBrowser ? document?.documentElement?.scrollTop : 0
38
+ const scrollLeft = isBrowser ? document?.documentElement?.scrollLeft : 0
27
39
 
28
- return {
29
- position: 'absolute',
30
- top: topPosition,
31
- left: leftPosition,
32
- }
40
+ const topPosition = topLevel + topOffset + scrollTop
41
+
42
+ let leftPosition: React.CSSProperties['left'] = leftLevel + scrollLeft
43
+ let rightPosition = 'auto'
44
+ let transform = 'none'
45
+
46
+ if (align === 'right') {
47
+ rightPosition = `${document.documentElement.clientWidth - leftLevel - buttonWidth}px`
48
+ leftPosition = 'auto'
49
+ } else if (align === 'center') {
50
+ leftPosition = leftLevel + (buttonWidth / 2) + scrollLeft
51
+ transform = 'translateX(-50%)'
52
+ }
53
+
54
+ setPositionProps({
55
+ top: topPosition,
56
+ left: leftPosition,
57
+ right: rightPosition,
58
+ transform,
59
+ loading: false
60
+ })
61
+ }
62
+
63
+ if (isOpen) {
64
+ // Update the position of the menu
65
+ updateMenuPosition()
66
+ window.addEventListener('resize', updateMenuPosition)
67
+ }
68
+
69
+ // Cleanup listener on unmount or close
70
+ return () => {
71
+ window.removeEventListener('resize', updateMenuPosition)
72
+ }
73
+ }, [dropdownTriggerRef, isOpen, align])
74
+
75
+ return { ...positionProps, position: 'absolute' as const }
33
76
  }
@@ -0,0 +1,26 @@
1
+ import { useDropdown } from './useDropdown'
2
+ import React, { useImperativeHandle } from 'react'
3
+
4
+ type UseDropdownTriggerProps<T extends HTMLElement = HTMLElement> = {
5
+ triggerRef: React.ForwardedRef<T>
6
+ label?: string
7
+ }
8
+
9
+ export const useDropdownTrigger = <T extends HTMLElement = HTMLElement>({
10
+ triggerRef,
11
+ }: UseDropdownTriggerProps<T>) => {
12
+ const { toggle, dropdownTriggerRef, addDropdownTriggerRef, isOpen, id } =
13
+ useDropdown<T>()
14
+
15
+ useImperativeHandle(triggerRef, () => dropdownTriggerRef!.current!, [
16
+ dropdownTriggerRef,
17
+ ])
18
+
19
+ return {
20
+ onClick: toggle,
21
+ ref: addDropdownTriggerRef,
22
+ 'aria-expanded': isOpen,
23
+ 'aria-controls': id,
24
+ 'aria-haspopup': 'menu' as const,
25
+ }
26
+ }
@@ -113,7 +113,8 @@ const QuantitySelector = ({
113
113
  }, [initial])
114
114
 
115
115
  const changeInputValue = (e: React.ChangeEvent<HTMLInputElement>) => {
116
- setQuantity(Number(e.currentTarget.value))
116
+ const numericValue = e.target.value.replace(/\D/g, '')
117
+ setQuantity(Number(numericValue))
117
118
  }
118
119
 
119
120
  return (
@@ -139,6 +140,10 @@ const QuantitySelector = ({
139
140
  value={useUnitMultiplier ? multipliedUnit : quantity}
140
141
  onChange={changeInputValue}
141
142
  onBlur={validateBlur}
143
+ onInput={(event: React.FormEvent<HTMLInputElement>) => {
144
+ const input = event.currentTarget
145
+ input.value = input.value.replace(/\D/g, '')
146
+ }}
142
147
  disabled={disabled}
143
148
  />
144
149
  <IconButton