react-animated-select 0.5.2 → 0.6.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/demo/src/shake.js DELETED
@@ -1,11 +0,0 @@
1
- export const shake = (el) => {
2
- if (!el) return
3
-
4
- el.classList.remove('--animated-error')
5
- void el.offsetWidth
6
- el.classList.add('--animated-error')
7
- }
8
-
9
- export const clearShake = (el) => {
10
- el?.classList.remove('--animated-error')
11
- }
@@ -1,35 +0,0 @@
1
- import {CSSTransition} from 'react-transition-group'
2
- import {useRef} from 'react'
3
-
4
- function SlideDown({visibility, children, duration = 300}) {
5
- const nodeRef = useRef(null)
6
-
7
- return(
8
- <CSSTransition
9
- in={visibility}
10
- timeout={300}
11
- classNames='slideDown'
12
- unmountOnExit
13
- nodeRef={nodeRef}
14
- onEnter={() => (nodeRef.current.style.height = '0px')}
15
- onEntering={() => (nodeRef.current.style.height = nodeRef.current.scrollHeight + 'px')}
16
- onEntered={() => (nodeRef.current.style.height = 'auto')}
17
- onExit={() => (nodeRef.current.style.height = nodeRef.current.scrollHeight + 'px')}
18
- onExiting={() => (nodeRef.current.style.height = '0px')}
19
- >
20
- <div
21
- ref={nodeRef}
22
- style={{
23
- overflow: 'hidden',
24
- transition: `height ${duration}ms ease`
25
- }}
26
- className='slideDown-enter-done'
27
- tabIndex={-1}
28
- >
29
- {children}
30
- </div>
31
- </CSSTransition>
32
- )
33
- }
34
-
35
- export default SlideDown
@@ -1,7 +0,0 @@
1
- import {defineConfig} from 'vite'
2
- import react from '@vitejs/plugin-react'
3
-
4
- export default defineConfig({
5
- plugins: [react()],
6
- base: '/react-animated-select/',
7
- })
package/src/animated.jsx DELETED
@@ -1,80 +0,0 @@
1
- import {useRef} from 'react'
2
- import {CSSTransition} from 'react-transition-group'
3
-
4
- const Animated = ({children, duration, widthMode = false, ...props}) => {
5
- const nodeRef = useRef(null)
6
- return (
7
- <CSSTransition
8
- nodeRef={nodeRef}
9
- timeout={duration}
10
- classNames='rac-slide-left'
11
- {...props}
12
- onEnter={() => {
13
- const el = nodeRef.current
14
- if (widthMode) {
15
- el.style.width = '0px'
16
- } else {
17
- el.style.height = '0px'
18
- el.style.transform = 'translateY(-10px)'
19
- }
20
- el.style.opacity = '0'
21
- }}
22
- onEntering={() => {
23
- const el = nodeRef.current
24
- el.offsetHeight
25
- if (widthMode) {
26
- el.style.width = el.scrollWidth + 'px'
27
- } else {
28
- el.style.height = el.scrollHeight + 'px'
29
- el.style.transform = 'translateY(0)'
30
- }
31
- el.style.opacity = '1'
32
- }}
33
- onEntered={() => {
34
- const el = nodeRef.current
35
- el.style.width = widthMode ? 'auto' : ''
36
- el.style.height = widthMode ? '' : 'auto'
37
- el.style.opacity = '1'
38
- el.style.transform = ''
39
- }}
40
- onExit={() => {
41
- const el = nodeRef.current
42
- if (widthMode) {
43
- el.style.width = el.offsetWidth + 'px'
44
- } else {
45
- el.style.height = el.offsetHeight + 'px'
46
- el.style.position = 'absolute'
47
- }
48
- el.style.opacity = '1'
49
- }}
50
- onExiting={() => {
51
- const el = nodeRef.current
52
- if (widthMode) {
53
- el.style.width = '0px'
54
- } else {
55
- el.style.height = '0px'
56
- el.style.transform = 'translateY(10px)'
57
- }
58
- el.style.opacity = '0'
59
- }}
60
- >
61
- <div
62
- ref={nodeRef}
63
- style={{
64
- display: 'flex',
65
- alignItems: 'center',
66
- height: '100%',
67
- overflow: 'hidden',
68
- whiteSpace: 'nowrap',
69
- transition: `all ${duration}ms ease`,
70
- top: 0,
71
- left: 0
72
- }}
73
- >
74
- {children}
75
- </div>
76
- </CSSTransition>
77
- )
78
- }
79
-
80
- export default Animated
package/src/getText.jsx DELETED
@@ -1,11 +0,0 @@
1
- import React from 'react'
2
-
3
- const getText = (children) => {
4
- if (!children) return ''
5
- if (typeof children === 'string' || typeof children === 'number') return String(children)
6
- if (Array.isArray(children)) return children.map(getText).join(' ').replace(/\s+/g, ' ').trim()
7
- if (React.isValidElement(children)) return getText(children.props.children)
8
- return ''
9
- }
10
-
11
- export default getText
package/src/icons.jsx DELETED
@@ -1,43 +0,0 @@
1
- export const XMarkIcon = ({className = '', ...props}) => (
2
- <svg
3
- className={className}
4
- role='button'
5
- aria-label='Clear selection'
6
- xmlns="http://www.w3.org/2000/svg"
7
- viewBox="0 0 320 512"
8
- width="1em"
9
- height="1em"
10
- fill="currentColor"
11
- {...props}
12
- >
13
- <path d="M310.6 361.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L160 301.3 54.6 406.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L114.7 256 9.4 150.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L160 210.7 265.4 105.4c12.5-12.5 32.8-12.5 45.3 0s12.5 32.8 0 45.3L205.3 256l105.3 105.4z"/>
14
- </svg>
15
- )
16
-
17
- export const ArrowUpIcon = ({className = '', ...props}) => (
18
- <svg
19
- className={className}
20
- xmlns="http://www.w3.org/2000/svg"
21
- viewBox="0 0 448 512"
22
- width="1em"
23
- height="1em"
24
- fill="currentColor"
25
- {...props}
26
- >
27
- <path d="M34.9 289.5l175.9-175.8c9.4-9.4 24.6-9.4 33.9 0L420.1 289.5c15.1 15.1 4.4 41-17 41H51.9c-21.4 0-32.1-25.9-17-41z"/>
28
- </svg>
29
- )
30
-
31
- export const CheckmarkIcon = ({className = '', ...props}) => (
32
- <svg
33
- className={className}
34
- xmlns="http://www.w3.org/2000/svg"
35
- viewBox="0 0 24 24"
36
- width="1em"
37
- height="1em"
38
- fill="currentColor"
39
- {...props}
40
- >
41
- <path d="M20.285 6.708a1 1 0 0 0-1.414-1.416l-9.192 9.192-4.243-4.244a1 1 0 1 0-1.414 1.416l5 5a1 1 0 0 0 1.414 0l9.849-9.948z"/>
42
- </svg>
43
- )
package/src/index.js DELETED
@@ -1,5 +0,0 @@
1
- export {default as Select} from './select'
2
- export {default as Option} from './option'
3
- export {default as OptGroup} from './optgroup'
4
-
5
- import './select.css'
package/src/makeId.jsx DELETED
@@ -1,21 +0,0 @@
1
- // if user does not provide a unique identifier, a unique ID is generated based on value, filtering out unwanted characters
2
- export const makeId = (str, fallback = 'invalid-option', seed = '') => {
3
- const safeSeed = seed ? seed.replace(/:/g, '') : ''
4
-
5
- if (typeof str !== 'string' || !str.trim()) {
6
- return safeSeed ? `${fallback}-${safeSeed}` : `${fallback}-${Math.random().toString(36).slice(2, 8)}`
7
- }
8
-
9
- const cleaned = str
10
- .normalize('NFKD')
11
- .replace(/[\u0300-\u036f]/g, '')
12
- .replace(/\s+/g, '-')
13
- .replace(/[^\p{L}\p{N}-]+/gu, '')
14
- .toLowerCase()
15
-
16
- if (!cleaned) {
17
- return safeSeed ? `${fallback}-${safeSeed}` : `${fallback}-${Math.random().toString(36).slice(2, 8)}`
18
- }
19
-
20
- return cleaned || `${fallback}-${Math.random().toString(36).slice(2, 8)}`
21
- }
package/src/optgroup.jsx DELETED
@@ -1,36 +0,0 @@
1
- import {useContext, useEffect, useMemo, createContext} from 'react'
2
- import {SelectContext} from './selectContext'
3
- import {makeId} from './makeId'
4
-
5
- export const GroupContext = createContext(null)
6
-
7
- export default function OptGroup({children, name, label, value, id, emptyGroupText = 'Empty group'}) {
8
- const ctx = useContext(SelectContext)
9
-
10
- const groupName = useMemo(() => {
11
- const val = name ?? label ?? value ?? id
12
- return (val !== undefined && val !== null && val !== '') ? String(val) : emptyGroupText
13
- }, [name, label, value, id, emptyGroupText])
14
-
15
- const groupId = useMemo(() => `group-marker-${makeId(groupName)}`, [groupName])
16
-
17
- useEffect(() => {
18
- if (!ctx) return
19
-
20
- const groupMarker = {
21
- id: groupId,
22
- group: groupName,
23
- isGroupMarker: true,
24
- disabled: true
25
- }
26
-
27
- ctx.registerOption(groupMarker)
28
- return () => ctx.unregisterOption(groupId)
29
- }, [ctx.registerOption, ctx.unregisterOption, groupId, groupName])
30
-
31
- return (
32
- <GroupContext.Provider value={groupName}>
33
- {children}
34
- </GroupContext.Provider>
35
- )
36
- }
package/src/option.jsx DELETED
@@ -1,50 +0,0 @@
1
- import {useEffect, useContext, useMemo, useId} from 'react'
2
- import {SelectContext} from './selectContext'
3
- import {GroupContext} from './optgroup'
4
- import getText from './getText'
5
-
6
- export default function Option({value, id, className, children, disabled, group: manualGroup}) {
7
- const ctx = useContext(SelectContext)
8
- const contextGroup = useContext(GroupContext)
9
-
10
- const registerOption = ctx?.registerOption;
11
- const unregisterOption = ctx?.unregisterOption;
12
-
13
- const uniqueId = useId()
14
- const stableId = useMemo(() => {
15
- return id ? String(id) : uniqueId.replace(/:/g, '')
16
- }, [id, uniqueId])
17
-
18
- useEffect(() => {
19
- if (!registerOption) return
20
-
21
- const textFallback = getText(children)
22
- const hasJsx = children !== undefined && children !== null
23
- let finalLabel = ''
24
-
25
- if (textFallback) {
26
- finalLabel = textFallback
27
- } else if (id !== undefined && id !== null && id !== '') {
28
- finalLabel = String(id)
29
- } else if (value !== undefined && value !== null && value !== '') {
30
- finalLabel = String(value)
31
- }
32
-
33
- const option = {
34
- id: stableId,
35
- value: value !== undefined ? value : textFallback,
36
- label: finalLabel,
37
- jsx: children,
38
- hasJsx,
39
- className,
40
- disabled: !!disabled,
41
- group: manualGroup || contextGroup || null
42
- }
43
-
44
- registerOption(option)
45
- return () => unregisterOption(stableId)
46
-
47
- }, [stableId, value, children, className, disabled, manualGroup, contextGroup, registerOption, unregisterOption])
48
-
49
- return null
50
- }
package/src/options.jsx DELETED
@@ -1,197 +0,0 @@
1
- import {CSSTransition} from 'react-transition-group'
2
- import {useRef, useState, useEffect, useCallback, memo} from 'react'
3
- import {createPortal} from 'react-dom'
4
-
5
- function Options({
6
- visibility,
7
- children,
8
- selectRef,
9
- onAnimationDone,
10
- unmount = true,
11
- duration,
12
- easing,
13
- offset,
14
- animateOpacity,
15
- style,
16
- className
17
- }) {
18
-
19
- const nodeRef = useRef(null)
20
- const [selectHeight, setSelectHeight] = useState(0)
21
-
22
- const [coords, setCoords] = useState({top: 0, left: 0, width: 0})
23
-
24
- const updateCoords = useCallback(() => {
25
- if (selectRef?.current) {
26
- const rect = selectRef.current.getBoundingClientRect()
27
- const windowHeight = window.innerHeight
28
-
29
- const dropdownHeight = nodeRef.current?.scrollHeight || 250
30
-
31
- const spaceBelow = windowHeight - rect.bottom
32
- const showUpward = spaceBelow < dropdownHeight && rect.top > spaceBelow
33
-
34
- setCoords({
35
- top: rect.top,
36
- bottom: rect.bottom,
37
- left: rect.left,
38
- width: rect.width,
39
- isUpward: showUpward
40
- })
41
- }
42
- }, [selectRef])
43
-
44
- useEffect(() => {
45
- if (visibility) {
46
- updateCoords()
47
-
48
- window.addEventListener('scroll', updateCoords, true)
49
- window.addEventListener('resize', updateCoords)
50
-
51
- return () => {
52
- window.removeEventListener('scroll', updateCoords, true)
53
- window.removeEventListener('resize', updateCoords)
54
- }
55
- }
56
- }, [visibility, updateCoords])
57
-
58
- const transitionString = `height var(--rac-duration) ${easing}${animateOpacity ? `, opacity var(--rac-duration) ${easing}` : ''}`;
59
-
60
- useEffect(() => {
61
- if (!selectRef?.current) return
62
- const updateHeight = () => setSelectHeight(selectRef.current.offsetHeight)
63
- updateHeight()
64
- const resizeObserver = new ResizeObserver((entries) => {
65
- for (let entry of entries) setSelectHeight(entry.target.offsetHeight)
66
- })
67
- resizeObserver.observe(selectRef.current)
68
- return () => resizeObserver.disconnect()
69
- }, [selectRef])
70
-
71
- const baseStyles = {
72
- position: 'fixed',
73
- '--rac-duration': `${duration}ms`,
74
- '--rac-easing': easing,
75
- left: `${coords.left}px`,
76
- width: `${coords.width}px`,
77
- overflow: 'hidden',
78
- zIndex: '1',
79
- height: visibility ? 'auto' : '0px',
80
- opacity: animateOpacity ? (visibility ? 1 : 0) : 1,
81
- pointerEvents: visibility ? 'all' : 'none',
82
- visibility: selectHeight ? 'visible' : 'hidden',
83
- boxSizing: 'border-box',
84
- transformOrigin: coords.isUpward ? 'bottom' : 'top',
85
-
86
- ...(coords.isUpward ? {
87
- bottom: `${window.innerHeight - coords.top + offset}px`,
88
- top: 'auto'
89
- } : {
90
- top: `${coords.bottom + offset}px`,
91
- bottom: 'auto'
92
- }),
93
- ...Object.fromEntries(
94
- Object.entries(style || {}).map(([key, value]) => [
95
- key.startsWith('--') ? key : `--rac-${key.replace(/([A-Z])/g, '-$1').toLowerCase()}`,
96
- value
97
- ])
98
- )
99
- }
100
-
101
- const handleEnter = useCallback(() => {
102
- const el = nodeRef.current
103
- if (!el) return
104
-
105
- el.style.height = '0px'
106
- if (animateOpacity) el.style.opacity = '0'
107
- el.style.transition = ''
108
- }, [animateOpacity])
109
-
110
- const handleEntering = useCallback(() => {
111
- const el = nodeRef.current
112
- if (!el) return
113
-
114
- el.style.transition = transitionString
115
- el.style.height = `${el.scrollHeight}px`
116
- if (animateOpacity) el.style.opacity = '1'
117
- }, [transitionString, animateOpacity])
118
-
119
- const handleEntered = useCallback(() => {
120
- const el = nodeRef.current
121
- if (!el) return
122
-
123
- el.style.height = 'auto'
124
- el.style.transition = ''
125
- if (onAnimationDone) onAnimationDone()
126
- }, [onAnimationDone])
127
-
128
- const handleExit = useCallback(() => {
129
- const el = nodeRef.current
130
- if (!el) return
131
-
132
- el.style.height = `${el.scrollHeight}px`
133
- if (animateOpacity) el.style.opacity = '1'
134
-
135
- el.offsetHeight
136
- el.style.transition = transitionString
137
- }, [transitionString, animateOpacity])
138
-
139
- const handleExiting = useCallback(() => {
140
- const el = nodeRef.current
141
- if (!el) return
142
-
143
- el.style.height = '0px'
144
- if (animateOpacity) el.style.opacity = '0'
145
- }, [animateOpacity])
146
-
147
- const handleExited = useCallback(() => {
148
- const el = nodeRef.current
149
- if (!el) return
150
- el.style.transition = ''
151
- }, [])
152
-
153
- return createPortal(
154
- <CSSTransition
155
- in={visibility}
156
- timeout={duration}
157
- classNames='rac-options'
158
- unmountOnExit={unmount}
159
- nodeRef={nodeRef}
160
- onEnter={handleEnter}
161
- onEntering={handleEntering}
162
- onEntered={handleEntered}
163
- onExit={handleExit}
164
- onExiting={handleExiting}
165
- onExited={handleExited}
166
- >
167
- <div
168
- ref={nodeRef}
169
- className={`rac-options ${className || ''}`}
170
- style={{
171
- ...baseStyles,
172
- '--rac-duration': `${duration}ms`,
173
- '--rac-duration-fast': 'calc(var(--rac-duration) * 0.5)',
174
- '--rac-duration-base': 'var(--rac-duration)',
175
- '--rac-duration-slow': 'calc(var(--rac-duration) * 1.3)',
176
-
177
- }}
178
- onMouseDown={(e) => {
179
- e.preventDefault()
180
- }}
181
- >
182
- {children}
183
- </div>
184
- </CSSTransition>, document.body
185
- )
186
- }
187
-
188
- export default memo(Options, (prev, next) => {
189
- return prev.visibility === next.visibility &&
190
- prev.duration === next.duration &&
191
- prev.easing === next.easing &&
192
- prev.offset === next.offset &&
193
- prev.animateOpacity === next.animateOpacity &&
194
- prev.selectRef === next.selectRef &&
195
- prev.children === next.children &&
196
- JSON.stringify(prev.style) === JSON.stringify(next.style)
197
- })