@stack-spot/citric-react 0.26.0 → 0.27.1
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/citric.css +54 -3
- package/dist/components/CheckboxGroup.d.ts +15 -1
- package/dist/components/CheckboxGroup.d.ts.map +1 -1
- package/dist/components/CheckboxGroup.js +5 -3
- package/dist/components/CheckboxGroup.js.map +1 -1
- package/dist/components/CitricComponent.d.ts +1 -1
- package/dist/components/CitricComponent.d.ts.map +1 -1
- package/dist/components/RadioGroup.d.ts +13 -1
- package/dist/components/RadioGroup.d.ts.map +1 -1
- package/dist/components/RadioGroup.js +3 -3
- package/dist/components/RadioGroup.js.map +1 -1
- package/dist/components/Select/MultiSelect.d.ts +60 -0
- package/dist/components/Select/MultiSelect.d.ts.map +1 -0
- package/dist/components/Select/MultiSelect.js +84 -0
- package/dist/components/Select/MultiSelect.js.map +1 -0
- package/dist/components/Select/RichSelect.d.ts +1 -1
- package/dist/components/Select/RichSelect.d.ts.map +1 -1
- package/dist/components/Select/RichSelect.js +14 -112
- package/dist/components/Select/RichSelect.js.map +1 -1
- package/dist/components/Select/hooks.d.ts +23 -0
- package/dist/components/Select/hooks.d.ts.map +1 -0
- package/dist/components/Select/hooks.js +114 -0
- package/dist/components/Select/hooks.js.map +1 -0
- package/dist/components/Select/types.d.ts +16 -5
- package/dist/components/Select/types.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/checkbox.js +1 -1
- package/dist/utils/checkbox.js.map +1 -1
- package/package.json +1 -1
- package/src/components/CheckboxGroup.tsx +18 -1
- package/src/components/CitricComponent.ts +1 -1
- package/src/components/RadioGroup.tsx +16 -1
- package/src/components/Select/MultiSelect.tsx +205 -0
- package/src/components/Select/RichSelect.tsx +16 -109
- package/src/components/Select/hooks.ts +133 -0
- package/src/components/Select/types.ts +16 -5
- package/src/index.ts +1 -0
- package/src/utils/checkbox.ts +1 -1
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { listToClass } from '@stack-spot/portal-theme'
|
|
2
2
|
import { useTranslate } from '@stack-spot/portal-translate'
|
|
3
|
-
import { useCallback,
|
|
3
|
+
import { useCallback, useMemo, useRef, useState } from 'react'
|
|
4
4
|
import { applyCSSVariable } from '../../utils/css'
|
|
5
5
|
import { defaultRenderKey, defaultRenderLabel } from '../../utils/options'
|
|
6
6
|
import { withRef } from '../../utils/react'
|
|
7
7
|
import { CitricComponent } from '../CitricComponent'
|
|
8
8
|
import { Input } from '../Input'
|
|
9
9
|
import { ProgressCircular } from '../ProgressCircular'
|
|
10
|
+
import { useDisabledEffect, useFocusEffect, useOpenPanelEffect } from './hooks'
|
|
10
11
|
import { SimpleSelect } from './SimpleSelect'
|
|
11
12
|
import { SelectProps } from './types'
|
|
12
13
|
|
|
@@ -30,10 +31,11 @@ export const RichSelect = withRef(
|
|
|
30
31
|
onFocus,
|
|
31
32
|
onBlur,
|
|
32
33
|
showArrow,
|
|
34
|
+
placeholder,
|
|
33
35
|
...props
|
|
34
36
|
}: SelectProps<T> & { type?: 'rich' },
|
|
35
37
|
) {
|
|
36
|
-
const [search, setSearch] = useState('')
|
|
38
|
+
const [search, setSearch] = useState<string | undefined>('')
|
|
37
39
|
const _element = useRef<HTMLDivElement | null>(null)
|
|
38
40
|
const element = ref ?? _element
|
|
39
41
|
const [open, setOpen] = useState(false)
|
|
@@ -46,13 +48,15 @@ export const RichSelect = withRef(
|
|
|
46
48
|
}, [])
|
|
47
49
|
|
|
48
50
|
const renderedOptions = useMemo(() => {
|
|
49
|
-
const items = required
|
|
51
|
+
const items = required
|
|
52
|
+
? []
|
|
53
|
+
: [<li key="" className="empty option" onClick={change(undefined)}>{renderLabel(undefined) || t.empty}</li>]
|
|
50
54
|
options.forEach((o) => {
|
|
51
55
|
const key = renderKey(o)
|
|
52
56
|
const label = renderLabel(o)
|
|
53
|
-
if (!search
|
|
57
|
+
if (!search?.trim() || label.toLocaleLowerCase().includes(search?.trim().toLocaleLowerCase())) {
|
|
54
58
|
items.push(
|
|
55
|
-
<li key={key} onClick={change(o)}>
|
|
59
|
+
<li key={key} onClick={change(o)} className="option">
|
|
56
60
|
{renderOption?.(o) ?? label}
|
|
57
61
|
</li>,
|
|
58
62
|
)
|
|
@@ -61,108 +65,9 @@ export const RichSelect = withRef(
|
|
|
61
65
|
return items
|
|
62
66
|
}, [options, value, required, search])
|
|
63
67
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
setSearch('')
|
|
68
|
-
const selectionPanel = element.current?.querySelector('.selection-panel') as HTMLElement | undefined
|
|
69
|
-
selectionPanel?.querySelector('ul')?.scrollTo({ top: 0 })
|
|
70
|
-
const getCurrent = () => selectionPanel?.querySelector('li.focused') as HTMLElement | undefined
|
|
71
|
-
const scrollTo = (li: HTMLElement) => {
|
|
72
|
-
const ul = li.closest('ul')
|
|
73
|
-
if (!ul) return
|
|
74
|
-
const { top: ulTop, height: ulHeight } = ul.getBoundingClientRect()
|
|
75
|
-
const { height: liHeight, top: liTop } = li.getBoundingClientRect()
|
|
76
|
-
const offset = liTop + ul.scrollTop - ulTop
|
|
77
|
-
if ((ul.scrollTop + ulHeight < offset + liHeight) || ul.scrollTop > offset) {
|
|
78
|
-
ul.scrollTo({ top: offset })
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
/* keyboard and mouse controls */
|
|
82
|
-
const listenToMouse = (event: Event) => {
|
|
83
|
-
if (!selectionPanel?.contains(event.target as HTMLElement)) {
|
|
84
|
-
setOpen(false)
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
const listenToKeyboard = (event: KeyboardEvent) => {
|
|
88
|
-
const isCharacter = event.key.length === 1
|
|
89
|
-
if (['Escape', 'ArrowUp', 'ArrowDown', 'Enter'].includes(event.key) ||
|
|
90
|
-
(searchable && (isCharacter || event.key === 'Backspace'))) {
|
|
91
|
-
event.preventDefault()
|
|
92
|
-
event.stopPropagation()
|
|
93
|
-
}
|
|
94
|
-
if (event.key === 'Escape') setOpen(false)
|
|
95
|
-
if (searchable) {
|
|
96
|
-
if (isCharacter) setSearch(v => `${v}${event.key}`)
|
|
97
|
-
if (event.key === 'Backspace') setSearch(v => v.substring(0, v.length - 1))
|
|
98
|
-
}
|
|
99
|
-
if (event.key === 'ArrowDown') {
|
|
100
|
-
const current = getCurrent()
|
|
101
|
-
const next = (current?.nextElementSibling ?? selectionPanel?.querySelector('li')) as HTMLAreaElement | undefined
|
|
102
|
-
if (next) {
|
|
103
|
-
current?.classList.remove('focused')
|
|
104
|
-
next.classList.add('focused')
|
|
105
|
-
scrollTo(next)
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
if (event.key === 'ArrowUp') {
|
|
109
|
-
const current = getCurrent()
|
|
110
|
-
const prev = (current?.previousElementSibling ?? selectionPanel?.querySelector('li:last-child')) as HTMLAreaElement | undefined
|
|
111
|
-
if (prev) {
|
|
112
|
-
current?.classList.remove('focused')
|
|
113
|
-
prev.classList.add('focused')
|
|
114
|
-
scrollTo(prev)
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
if (event.key === 'Enter') {
|
|
118
|
-
setTimeout(() => getCurrent()?.click(), 0)
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
// below, we wait 20ms so the same click that opened the select doesn't close it. Removing it will cause problems with selects under
|
|
122
|
-
// labels.
|
|
123
|
-
setTimeout(() => document.addEventListener('click', listenToMouse), 20)
|
|
124
|
-
document.addEventListener('keydown', listenToKeyboard)
|
|
125
|
-
return () => {
|
|
126
|
-
document.removeEventListener('click', listenToMouse)
|
|
127
|
-
document.removeEventListener('keydown', listenToKeyboard)
|
|
128
|
-
getCurrent()?.classList.remove('focused')
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}, [open])
|
|
132
|
-
|
|
133
|
-
/* this runs whenever the select is focused */
|
|
134
|
-
useEffect(() => {
|
|
135
|
-
if (focused) {
|
|
136
|
-
const listenToMouse = (event: MouseEvent) => {
|
|
137
|
-
if (!element.current?.contains(event.target as HTMLElement)) {
|
|
138
|
-
setFocused(false)
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
const listenToKeyboard = (event: KeyboardEvent) => {
|
|
142
|
-
if (['Enter', 'ArrowDown', 'ArrowUp'].includes(event.key)) {
|
|
143
|
-
event.preventDefault()
|
|
144
|
-
if (!element.current?.classList.contains('open')) setOpen(true)
|
|
145
|
-
}
|
|
146
|
-
if (event.key === 'Tab') {
|
|
147
|
-
setFocused(false)
|
|
148
|
-
if (element.current?.classList.contains('open')) setOpen(false)
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
document.addEventListener('click', listenToMouse)
|
|
152
|
-
document.addEventListener('keydown', listenToKeyboard)
|
|
153
|
-
return () => {
|
|
154
|
-
document.removeEventListener('click', listenToMouse)
|
|
155
|
-
document.removeEventListener('keydown', listenToKeyboard)
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}, [focused])
|
|
159
|
-
|
|
160
|
-
useEffect(() => {
|
|
161
|
-
if (disabled) {
|
|
162
|
-
setOpen(false)
|
|
163
|
-
setFocused(false)
|
|
164
|
-
}
|
|
165
|
-
}, [disabled])
|
|
68
|
+
useOpenPanelEffect({ open, setOpen, setSearch, element, searchable })
|
|
69
|
+
useFocusEffect({ element, focused, setFocused, setOpen })
|
|
70
|
+
useDisabledEffect({ disabled, setOpen, setFocused })
|
|
166
71
|
|
|
167
72
|
return (
|
|
168
73
|
<CitricComponent
|
|
@@ -195,7 +100,9 @@ export const RichSelect = withRef(
|
|
|
195
100
|
}}
|
|
196
101
|
aria-hidden
|
|
197
102
|
>
|
|
198
|
-
{
|
|
103
|
+
{value === undefined
|
|
104
|
+
? <span className="placeholder">{placeholder}</span>
|
|
105
|
+
: ((renderHeader?.(value) ?? renderOption?.(value) ?? renderLabel(value)) || <span></span>)}
|
|
199
106
|
{loading && <ProgressCircular size="xs" className="loader" />}
|
|
200
107
|
</header>
|
|
201
108
|
<div className="selection-panel" aria-hidden>
|
|
@@ -205,7 +112,7 @@ export const RichSelect = withRef(
|
|
|
205
112
|
<Input type="search" value={search} onChange={setSearch} tabIndex={-1} />
|
|
206
113
|
</div>
|
|
207
114
|
</div>}
|
|
208
|
-
<ul>{renderedOptions}</ul>
|
|
115
|
+
<ul className="options">{renderedOptions}</ul>
|
|
209
116
|
</div>
|
|
210
117
|
</CitricComponent>
|
|
211
118
|
)
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { useEffect } from 'react'
|
|
2
|
+
|
|
3
|
+
interface OpenPanelEffectParams {
|
|
4
|
+
open: boolean,
|
|
5
|
+
setOpen: React.Dispatch<React.SetStateAction<boolean>>,
|
|
6
|
+
setSearch: React.Dispatch<React.SetStateAction<string | undefined>>,
|
|
7
|
+
element: React.MutableRefObject<HTMLDivElement | null>,
|
|
8
|
+
searchable?: boolean,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface FocusEffectParams {
|
|
12
|
+
element: React.MutableRefObject<HTMLDivElement | null>,
|
|
13
|
+
focused: boolean,
|
|
14
|
+
setFocused: React.Dispatch<React.SetStateAction<boolean>>,
|
|
15
|
+
setOpen: React.Dispatch<React.SetStateAction<boolean>>,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface DisabledEffectParams {
|
|
19
|
+
disabled?: boolean,
|
|
20
|
+
setFocused: React.Dispatch<React.SetStateAction<boolean>>,
|
|
21
|
+
setOpen: React.Dispatch<React.SetStateAction<boolean>>,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* this runs whenever the selection panel is opened */
|
|
25
|
+
export function useOpenPanelEffect({ open, setOpen, setSearch, element, searchable }: OpenPanelEffectParams) {
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (open) {
|
|
28
|
+
setSearch('')
|
|
29
|
+
const selectionPanel = element.current?.querySelector('.selection-panel') as HTMLElement | undefined
|
|
30
|
+
selectionPanel?.querySelector('.options')?.scrollTo({ top: 0 })
|
|
31
|
+
const getCurrent = () => selectionPanel?.querySelector('.option.focused') as HTMLElement | undefined
|
|
32
|
+
const scrollTo = (item: HTMLElement) => {
|
|
33
|
+
const list = item.closest('.options')
|
|
34
|
+
if (!list) return
|
|
35
|
+
const { top: listTop, height: listHeight } = list.getBoundingClientRect()
|
|
36
|
+
const { height: itemHeight, top: itemTop } = item.getBoundingClientRect()
|
|
37
|
+
const offset = itemTop + list.scrollTop - listTop
|
|
38
|
+
if ((list.scrollTop + listHeight < offset + itemHeight) || list.scrollTop > offset) {
|
|
39
|
+
list.scrollTo({ top: offset })
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/* keyboard and mouse controls */
|
|
43
|
+
const listenToMouse = (event: Event) => {
|
|
44
|
+
if (!selectionPanel?.contains(event.target as HTMLElement)) {
|
|
45
|
+
setOpen(false)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const listenToKeyboard = (event: KeyboardEvent) => {
|
|
49
|
+
const isCharacter = event.key.length === 1
|
|
50
|
+
if (['Escape', 'ArrowUp', 'ArrowDown', 'Enter'].includes(event.key) ||
|
|
51
|
+
(searchable && (isCharacter || event.key === 'Backspace'))) {
|
|
52
|
+
event.preventDefault()
|
|
53
|
+
event.stopPropagation()
|
|
54
|
+
}
|
|
55
|
+
if (event.key === 'Escape') setOpen(false)
|
|
56
|
+
if (searchable) {
|
|
57
|
+
if (isCharacter) setSearch(v => `${v}${event.key}`)
|
|
58
|
+
if (event.key === 'Backspace') setSearch(v => v?.substring(0, v.length - 1))
|
|
59
|
+
}
|
|
60
|
+
if (event.key === 'ArrowDown') {
|
|
61
|
+
const current = getCurrent()
|
|
62
|
+
const next = (current?.nextElementSibling ?? selectionPanel?.querySelector('.option')) as HTMLAreaElement | undefined
|
|
63
|
+
if (next) {
|
|
64
|
+
current?.classList.remove('focused')
|
|
65
|
+
next.classList.add('focused')
|
|
66
|
+
scrollTo(next)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (event.key === 'ArrowUp') {
|
|
70
|
+
const current = getCurrent()
|
|
71
|
+
const prev = (
|
|
72
|
+
current?.previousElementSibling ?? selectionPanel?.querySelector('.option:last-child')
|
|
73
|
+
) as HTMLAreaElement | undefined
|
|
74
|
+
if (prev) {
|
|
75
|
+
current?.classList.remove('focused')
|
|
76
|
+
prev.classList.add('focused')
|
|
77
|
+
scrollTo(prev)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (event.key === 'Enter') {
|
|
81
|
+
setTimeout(() => getCurrent()?.click(), 0)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// below, we wait 20ms so the same click that opened the select doesn't close it. Removing it will cause problems with selects under
|
|
85
|
+
// labels.
|
|
86
|
+
setTimeout(() => document.addEventListener('click', listenToMouse), 20)
|
|
87
|
+
document.addEventListener('keydown', listenToKeyboard)
|
|
88
|
+
return () => {
|
|
89
|
+
document.removeEventListener('click', listenToMouse)
|
|
90
|
+
document.removeEventListener('keydown', listenToKeyboard)
|
|
91
|
+
getCurrent()?.classList.remove('focused')
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}, [open])
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* this runs whenever the select is focused */
|
|
98
|
+
export function useFocusEffect({ element, focused, setFocused, setOpen }: FocusEffectParams) {
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
if (focused) {
|
|
101
|
+
const listenToMouse = (event: MouseEvent) => {
|
|
102
|
+
if (!element.current?.contains(event.target as HTMLElement)) {
|
|
103
|
+
setFocused(false)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const listenToKeyboard = (event: KeyboardEvent) => {
|
|
107
|
+
if (['Enter', 'ArrowDown', 'ArrowUp'].includes(event.key)) {
|
|
108
|
+
event.preventDefault()
|
|
109
|
+
if (!element.current?.classList.contains('open')) setOpen(true)
|
|
110
|
+
}
|
|
111
|
+
if (event.key === 'Tab') {
|
|
112
|
+
setFocused(false)
|
|
113
|
+
if (element.current?.classList.contains('open')) setOpen(false)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
document.addEventListener('click', listenToMouse)
|
|
117
|
+
document.addEventListener('keydown', listenToKeyboard)
|
|
118
|
+
return () => {
|
|
119
|
+
document.removeEventListener('click', listenToMouse)
|
|
120
|
+
document.removeEventListener('keydown', listenToKeyboard)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}, [focused])
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function useDisabledEffect({ disabled, setOpen, setFocused }: DisabledEffectParams) {
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
if (disabled) {
|
|
129
|
+
setOpen(false)
|
|
130
|
+
setFocused(false)
|
|
131
|
+
}
|
|
132
|
+
}, [disabled])
|
|
133
|
+
}
|
|
@@ -32,7 +32,7 @@ export interface CommonSelectProps<T> extends WithColorScheme {
|
|
|
32
32
|
* @param option the item to compute a key for.
|
|
33
33
|
* @returns a string key.
|
|
34
34
|
*/
|
|
35
|
-
renderKey?: (
|
|
35
|
+
renderKey?: (option: T) => string | number | undefined,
|
|
36
36
|
/**
|
|
37
37
|
* Whether or not this input is required. This is also used to figure out if an empty option should be rendered or not.
|
|
38
38
|
*
|
|
@@ -73,6 +73,10 @@ export interface CommonSelectProps<T> extends WithColorScheme {
|
|
|
73
73
|
* @default 'rich'
|
|
74
74
|
*/
|
|
75
75
|
type?: 'simple' | 'rich',
|
|
76
|
+
/**
|
|
77
|
+
* A text to show when no option is selected.
|
|
78
|
+
*/
|
|
79
|
+
placeholder?: string,
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
export interface SimpleSelectProps<T> extends CommonSelectProps<T> {
|
|
@@ -85,6 +89,9 @@ export interface RichSelectProps<T> extends CommonSelectProps<T> {
|
|
|
85
89
|
/**
|
|
86
90
|
* Whether or not to render a search field. The search is based on the value returned by `renderLabel`.
|
|
87
91
|
*
|
|
92
|
+
* Attention: when "searchable" is true, make sure to implement `renderLabel` if your options are not strings or number. The search will
|
|
93
|
+
* be performed on the result of this function.
|
|
94
|
+
*
|
|
88
95
|
* @default false
|
|
89
96
|
*/
|
|
90
97
|
searchable?: boolean,
|
|
@@ -92,18 +99,18 @@ export interface RichSelectProps<T> extends CommonSelectProps<T> {
|
|
|
92
99
|
* A function to render the option in the selectable list.
|
|
93
100
|
*
|
|
94
101
|
* The `renderLabel` function is used if this is not provided.
|
|
95
|
-
* @param
|
|
102
|
+
* @param option the option.
|
|
96
103
|
* @returns the React Node.
|
|
97
104
|
*/
|
|
98
|
-
renderOption?: (
|
|
105
|
+
renderOption?: (option: T | undefined) => React.ReactNode,
|
|
99
106
|
/**
|
|
100
107
|
* A function to render the selected option in the header.
|
|
101
108
|
*
|
|
102
109
|
* The `renderOption` function is used if this is not provided.
|
|
103
|
-
* @param
|
|
110
|
+
* @param option the option.
|
|
104
111
|
* @returns the React Node.
|
|
105
112
|
*/
|
|
106
|
-
renderHeader?: (
|
|
113
|
+
renderHeader?: (option: T | undefined) => React.ReactNode,
|
|
107
114
|
/**
|
|
108
115
|
* The maximum height of the options list in pixels.
|
|
109
116
|
*
|
|
@@ -116,6 +123,10 @@ export interface RichSelectProps<T> extends CommonSelectProps<T> {
|
|
|
116
123
|
*/
|
|
117
124
|
showArrow?: boolean,
|
|
118
125
|
ref?: React.MutableRefObject<HTMLDivElement>,
|
|
126
|
+
/**
|
|
127
|
+
* Text to show in the header when no option is selected.
|
|
128
|
+
*/
|
|
129
|
+
placeholder?: string,
|
|
119
130
|
}
|
|
120
131
|
|
|
121
132
|
export type BaseSelectProps<T> = SimpleSelectProps<T> | RichSelectProps<T>
|
package/src/index.ts
CHANGED
|
@@ -37,6 +37,7 @@ export * from './components/ProgressCircular'
|
|
|
37
37
|
export * from './components/RadioGroup'
|
|
38
38
|
export * from './components/Rating'
|
|
39
39
|
export * from './components/Select'
|
|
40
|
+
export * from './components/Select/MultiSelect'
|
|
40
41
|
export * from './components/SelectBox'
|
|
41
42
|
export * from './components/Skeleton'
|
|
42
43
|
export * from './components/Slider'
|
package/src/utils/checkbox.ts
CHANGED