@startlibs/core 1.0.7 → 1.1.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/lib/SlotFill.mjs +232 -0
- package/lib/SlotFill.mjs.map +1 -0
- package/lib/StartlibsProvider.mjs +62 -0
- package/lib/StartlibsProvider.mjs.map +1 -0
- package/lib/hocs/WithLazyResource.mjs +74 -0
- package/lib/hocs/WithLazyResource.mjs.map +1 -0
- package/lib/hocs/WithRef.mjs +20 -0
- package/lib/hocs/WithRef.mjs.map +1 -0
- package/lib/hocs/consumes.mjs +22 -0
- package/lib/hocs/consumes.mjs.map +1 -0
- package/lib/hocs/withDialogError.mjs +22 -0
- package/lib/hocs/withDialogError.mjs.map +1 -0
- package/lib/hocs/withGroupEvent.mjs +45 -0
- package/lib/hocs/withGroupEvent.mjs.map +1 -0
- package/lib/hooks/WithConstant.mjs +13 -0
- package/lib/hooks/WithConstant.mjs.map +1 -0
- package/lib/hooks/WithEffect.mjs +12 -0
- package/lib/hooks/WithEffect.mjs.map +1 -0
- package/lib/hooks/useAsyncEffect.mjs +14 -0
- package/lib/hooks/useAsyncEffect.mjs.map +1 -0
- package/lib/hooks/useCallState.mjs +21 -0
- package/lib/hooks/useCallState.mjs.map +1 -0
- package/lib/hooks/useCallbackStable.mjs +17 -0
- package/lib/hooks/useCallbackStable.mjs.map +1 -0
- package/lib/hooks/useConstant.mjs +21 -0
- package/lib/hooks/useConstant.mjs.map +1 -0
- package/lib/hooks/useConsumerCatalog.mjs +25 -0
- package/lib/hooks/useConsumerCatalog.mjs.map +1 -0
- package/lib/hooks/useEnsureRef.mjs +9 -0
- package/lib/hooks/useEnsureRef.mjs.map +1 -0
- package/lib/hooks/useForceUpdate.mjs +28 -0
- package/lib/hooks/useForceUpdate.mjs.map +1 -0
- package/lib/hooks/useIsUnmounted.mjs +15 -0
- package/lib/hooks/useIsUnmounted.mjs.map +1 -0
- package/lib/hooks/usePopupToggle.mjs +33 -0
- package/lib/hooks/usePopupToggle.mjs.map +1 -0
- package/lib/hooks/usePrevious.mjs +28 -0
- package/lib/hooks/usePrevious.mjs.map +1 -0
- package/lib/hooks/useRecycleFocus.mjs +36 -0
- package/lib/hooks/useRecycleFocus.mjs.map +1 -0
- package/lib/hooks/useRefMemo.mjs +13 -0
- package/lib/hooks/useRefMemo.mjs.map +1 -0
- package/lib/hooks/useRefState.mjs +29 -0
- package/lib/hooks/useRefState.mjs.map +1 -0
- package/lib/hooks/useSignal.mjs +271 -0
- package/lib/hooks/useSignal.mjs.map +1 -0
- package/lib/hooks/useTitle.mjs +17 -0
- package/lib/hooks/useTitle.mjs.map +1 -0
- package/lib/hooks/useToggle.mjs +152 -0
- package/lib/hooks/useToggle.mjs.map +1 -0
- package/lib/hooks/useUpdateEffect.mjs +15 -0
- package/lib/hooks/useUpdateEffect.mjs.map +1 -0
- package/lib/hooks/useUpdateOnResize.mjs +18 -0
- package/lib/hooks/useUpdateOnResize.mjs.map +1 -0
- package/lib/hooks/useWithTransition.mjs +18 -0
- package/lib/hooks/useWithTransition.mjs.map +1 -0
- package/lib/hooks/willUseLayers.mjs +55 -0
- package/lib/hooks/willUseLayers.mjs.map +1 -0
- package/lib/hooks/willUseSharedCallback.mjs +70 -0
- package/lib/hooks/willUseSharedCallback.mjs.map +1 -0
- package/lib/hooks/willUseSharedState.mjs +41 -0
- package/lib/hooks/willUseSharedState.mjs.map +1 -0
- package/lib/hooks/willUseSpreadState.mjs +119 -0
- package/lib/hooks/willUseSpreadState.mjs.map +1 -0
- package/lib/index.cjs +1474 -0
- package/lib/index.cjs.map +1 -0
- package/lib/index.mjs +33 -0
- package/lib/index.mjs.map +1 -0
- package/package.json +43 -29
- package/src/SlotFill.jsx +166 -0
- package/src/StartlibsProvider.jsx +62 -0
- package/src/hocs/WithLazyResource.jsx +32 -0
- package/src/hocs/WithRef.js +20 -0
- package/src/hocs/Wrapper.jsx +40 -0
- package/src/hocs/consumes.jsx +16 -0
- package/src/hocs/withDialogError.jsx +16 -0
- package/src/hocs/withGroupEvent.jsx +43 -0
- package/src/hooks/WithConstant.jsx +7 -0
- package/src/hooks/WithEffect.jsx +6 -0
- package/src/hooks/useAsyncEffect.jsx +11 -0
- package/src/hooks/useCallState.js +15 -0
- package/src/hooks/useCallbackStable.jsx +12 -0
- package/src/hooks/useConstant.js +20 -0
- package/src/hooks/useConsumerCatalog.jsx +21 -0
- package/src/hooks/useEnsureRef.js +6 -0
- package/src/hooks/useForceUpdate.jsx +16 -0
- package/src/hooks/useIsUnmounted.jsx +12 -0
- package/src/hooks/usePopupToggle.js +30 -0
- package/src/hooks/usePrevious.jsx +27 -0
- package/src/hooks/useRecycleFocus.jsx +26 -0
- package/src/hooks/useRefMemo.jsx +10 -0
- package/src/hooks/useRefState.js +26 -0
- package/src/hooks/useSignal.jsx +193 -0
- package/src/hooks/useTitle.jsx +12 -0
- package/src/hooks/useToggle.js +126 -0
- package/src/hooks/useUpdateEffect.js +14 -0
- package/src/hooks/useUpdateOnResize.jsx +14 -0
- package/src/hooks/useWithTransition.jsx +14 -0
- package/src/hooks/willUseLayers.jsx +42 -0
- package/src/hooks/willUseSharedCallback.jsx +60 -0
- package/src/hooks/willUseSharedState.jsx +34 -0
- package/src/hooks/willUseSpreadState.jsx +103 -0
- package/src/index.js +32 -0
- package/.babelrc +0 -27
- package/lib/index.js +0 -1921
- package/rollup.config.js +0 -53
- package/yarn-error.log +0 -3374
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React, {useReducer} from 'react'
|
|
2
|
+
import _ from 'lodash/fp'
|
|
3
|
+
import {ifUndefined} from '@startlibs/utils'
|
|
4
|
+
|
|
5
|
+
const addExtraProp = (element, extra, override = true) => React.cloneElement(
|
|
6
|
+
element,
|
|
7
|
+
override ? {...element.props,...extra} : {...extra,...element.props},
|
|
8
|
+
element.props.children
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
export const WithLazyPlaceholder = ({value, fallback, children, forwardProps = false,...props}) => {
|
|
12
|
+
return <React.Suspense
|
|
13
|
+
fallback={React.Children.map(children(fallback,{isLoading:true}), (child) => React.isValidElement(child) ? addExtraProp(child, {isLoading: true}, false) : child)}
|
|
14
|
+
>
|
|
15
|
+
<Load value={value} children={children} forwardProps={forwardProps && props}/>
|
|
16
|
+
</React.Suspense>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
export const WithLazyResource = ({value, wrap,fallback,children,forwardProps = false,...props}) => {
|
|
21
|
+
|
|
22
|
+
return (wrap || fallback !== undefined)
|
|
23
|
+
? <React.Suspense fallback={ifUndefined(fallback,null)}><Load value={value} forwardProps={forwardProps && props} children={children}/></React.Suspense>
|
|
24
|
+
: <Load value={value} children={children} forwardProps={forwardProps && props}/>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const Load = ({value,children,forwardProps}) => {
|
|
28
|
+
const loaded = _.isArray(value) ? (value.forEach(v => v.promise()) || value.map(v => v.read())) : value.read()
|
|
29
|
+
const [,update] = useReducer((s) => s+1, 0)
|
|
30
|
+
const refresh = () => Promise.all(_.isArray(value) ? value.map(v => v.refresh().promise()) : [value.refresh().promise()]).then(update)
|
|
31
|
+
return forwardProps ? addExtraProp(children(loaded,{refresh,update}),forwardProps) : children(loaded, {refresh,update})
|
|
32
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import React from 'react'
|
|
3
|
+
|
|
4
|
+
export class WithRef extends React.Component {
|
|
5
|
+
|
|
6
|
+
constructor(props) {
|
|
7
|
+
super(props)
|
|
8
|
+
const total = props.length || props.children.length
|
|
9
|
+
const refs = new Array(total)
|
|
10
|
+
for (var i = 0; i < total; i++) {
|
|
11
|
+
refs[i] = React.createRef()
|
|
12
|
+
}
|
|
13
|
+
this.createdRefs = refs
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
render() {
|
|
17
|
+
const {children} = this.props
|
|
18
|
+
return children(...this.createdRefs)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
const valueOrDefault = (v, d) => v === undefined ? d : v
|
|
4
|
+
const mergePropsAndChildren = (a, b) => ({
|
|
5
|
+
...a, ...b,
|
|
6
|
+
children: (a.children && b.children) ?
|
|
7
|
+
<React.Fragment>{a.children}{b.children}</React.Fragment> : (a.children || b.children)
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
const wrapIf = (WrapperWithRenderProps, testKeyOrFn, defaultWrapPropsOrFn) => (functionAsChild) => (props) => {
|
|
11
|
+
const test = typeof testKeyOrFn === 'string' ? props[testKeyOrFn] : testKeyOrFn(props)
|
|
12
|
+
if (test) {
|
|
13
|
+
const defaultWrapProps = typeof defaultWrapPropsOrFn === "function" ? defaultWrapPropsOrFn(props) : defaultWrapPropsOrFn
|
|
14
|
+
if (WrapperWithRenderProps.hasRenderProps) {
|
|
15
|
+
const Wrapper = WrapperWithRenderProps.Wrapper
|
|
16
|
+
return <Wrapper {...defaultWrapProps} {...props} ref={undefined}>{(extra) => functionAsChild(mergePropsAndChildren(props, extra))}</Wrapper>
|
|
17
|
+
} else {
|
|
18
|
+
return <WrapperWithRenderProps {...defaultWrapProps} {...props} ref={undefined}>{functionAsChild(props)}</WrapperWithRenderProps>
|
|
19
|
+
}
|
|
20
|
+
} else {
|
|
21
|
+
return functionAsChild(props)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
const wrapAsDefault = (Wrapper, key, defaultWrapProps) => wrapIf(Wrapper, (props) => valueOrDefault(props[key], true), defaultWrapProps)
|
|
27
|
+
|
|
28
|
+
const singleWrap = (Wrapper, defaultWrapProps) => wrapIf(Wrapper, () => true, defaultWrapProps)
|
|
29
|
+
|
|
30
|
+
const wraps = (...autoWrappers) => (Component) => autoWrappers.reduceRight(
|
|
31
|
+
(acc, autoWrapper) => autoWrapper(acc),
|
|
32
|
+
(props) => <Component {...props}/>
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
export const wrap = wraps
|
|
36
|
+
|
|
37
|
+
wrap.renderProps = (Wrapper) => ({hasRenderProps:true,Wrapper})
|
|
38
|
+
wrap.if = wrapIf
|
|
39
|
+
wrap.always = singleWrap
|
|
40
|
+
wrap.asDefault = wrapAsDefault
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
const propsOrConsume = (prop, Consumer,childrenAsFunction) => prop ? childrenAsFunction(prop) : <Consumer>{childrenAsFunction}</Consumer>
|
|
4
|
+
|
|
5
|
+
const addConsumers = (Component,obj) =>
|
|
6
|
+
Object.entries(obj)
|
|
7
|
+
.reduce(
|
|
8
|
+
(acc, [propKey, Consumer]) => (props) =>
|
|
9
|
+
propsOrConsume(props[propKey],Consumer,v => acc({[propKey]: v, ...props})),
|
|
10
|
+
(props) => <Component {...props} />
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
export const consumes = (obj) => (Component) => {
|
|
14
|
+
const withConsumers = addConsumers(Component,obj)
|
|
15
|
+
return (props) => withConsumers(props)
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
export const withDialogError = (Dialog) => {
|
|
4
|
+
const ErrorBoundedComponent = React.forwardRef((props,ref) => {
|
|
5
|
+
return <Dialog ref={ref} {...props}/>
|
|
6
|
+
|
|
7
|
+
// const ErrorBoundary = __internals('ErrorBoundary')
|
|
8
|
+
/*return (
|
|
9
|
+
<ErrorBoundary Error={props.Error || __internals('DialogError')} closeDialog={props.closeDialog}>
|
|
10
|
+
<Dialog {...props}/>
|
|
11
|
+
</ErrorBoundary>
|
|
12
|
+
)*/
|
|
13
|
+
})
|
|
14
|
+
// ErrorBoundedComponent.contextTypes = {startlibs:()=>null}
|
|
15
|
+
return ErrorBoundedComponent
|
|
16
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import mitt from 'mitt'
|
|
3
|
+
|
|
4
|
+
export const GroupEventFactory = () => {
|
|
5
|
+
let emitter = mitt()
|
|
6
|
+
|
|
7
|
+
class WithGroupEvent extends React.Component {
|
|
8
|
+
componentDidMount() {
|
|
9
|
+
emitter.on('*', this.processEvent)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
componentWillUnmount() {
|
|
13
|
+
emitter.off('*',this.processEvent)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
events = {}
|
|
17
|
+
|
|
18
|
+
processEvent = (type, event) => {
|
|
19
|
+
(this.events[type] || []).forEach(c => c(event))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
groupEventProps = {
|
|
23
|
+
on: (type,callback) => {
|
|
24
|
+
this.events[type] = (this.events[type] || []).concat(callback)
|
|
25
|
+
},
|
|
26
|
+
off: (type,callback) => {
|
|
27
|
+
this.events[type] = (this.events[type] || []).filter(e=>e!==callback)
|
|
28
|
+
},
|
|
29
|
+
emit: emitter.emit
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
render() {
|
|
33
|
+
return this.props.children(this.groupEventProps)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return WithGroupEvent
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const withGroupEvent = (Component) => {
|
|
40
|
+
const WithGroupEvent = GroupEventFactory()
|
|
41
|
+
return React.forwardRef((props,ref) => <WithGroupEvent>{(groupEvent) => <Component {...props} ref={ref} groupEvent={groupEvent}/>}</WithGroupEvent>)
|
|
42
|
+
}
|
|
43
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React, {useMemo, useState} from 'react'
|
|
2
|
+
import {_s} from '@startlibs/utils'
|
|
3
|
+
|
|
4
|
+
const isPath = (path) => typeof path === 'string' || typeof path === 'number' || Array.isArray(path)
|
|
5
|
+
|
|
6
|
+
const getter = (value) => path => isPath(path) ? _s.get(path, value) : value
|
|
7
|
+
|
|
8
|
+
export const useCallState = (defaultValue, setCallback) => {
|
|
9
|
+
const [val, setValue] = useState(defaultValue)
|
|
10
|
+
|
|
11
|
+
return useMemo(() => {
|
|
12
|
+
const get = () => val
|
|
13
|
+
return {get, val, set: setValue}
|
|
14
|
+
}, [val])
|
|
15
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React, {useInsertionEffect} from 'react'
|
|
2
|
+
|
|
3
|
+
export const useCallbackStable = (fn) => {
|
|
4
|
+
const ref = React.useRef([fn, (...args) => ref[0](...args)]).current;
|
|
5
|
+
// Per Dan Abramov: useInsertionEffect executes marginally closer to the
|
|
6
|
+
// correct timing for ref synchronization than useLayoutEffect on React 18.
|
|
7
|
+
// See: https://github.com/facebook/react/pull/25881#issuecomment-1356244360
|
|
8
|
+
useInsertionEffect(() => {
|
|
9
|
+
ref[0] = fn;
|
|
10
|
+
});
|
|
11
|
+
return ref[1];
|
|
12
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React, {useRef} from 'react'
|
|
2
|
+
import {callIfFunction} from '@startlibs/utils'
|
|
3
|
+
|
|
4
|
+
const not_defined = {}
|
|
5
|
+
|
|
6
|
+
export const useConstant = (value) => {
|
|
7
|
+
const ref = useRef(not_defined)
|
|
8
|
+
if (ref.current === not_defined) {
|
|
9
|
+
ref.current = value
|
|
10
|
+
}
|
|
11
|
+
return ref.current
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const useLazyConstant= (value) => {
|
|
15
|
+
const ref = useRef(not_defined)
|
|
16
|
+
if (ref.current === not_defined) {
|
|
17
|
+
ref.current = callIfFunction(value)
|
|
18
|
+
}
|
|
19
|
+
return ref.current
|
|
20
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React, {useEffect, useMemo} from 'react'
|
|
2
|
+
import {callMany} from '@startlibs/utils'
|
|
3
|
+
import {useRefState} from './useRefState'
|
|
4
|
+
|
|
5
|
+
export const useConsumerCatalog = (mountCallback,unmountCallback) => {
|
|
6
|
+
const elements = useRefState([])
|
|
7
|
+
|
|
8
|
+
const register = callMany((element) => elements.set((elements) => elements.concat([element])), mountCallback)
|
|
9
|
+
const unregister = callMany((element) => elements.set((elements) => elements.filter(_ => _ !== element)), unmountCallback)
|
|
10
|
+
|
|
11
|
+
const manager = useMemo(()=> ({register,unregister}), [])
|
|
12
|
+
|
|
13
|
+
return [elements.get,manager]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const useCatalogManager = ({register,unregister},ref) => {
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
register(ref)
|
|
19
|
+
return () => unregister(ref)
|
|
20
|
+
},[ref])
|
|
21
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {useState} from 'react'
|
|
2
|
+
import {willUseSharedState} from './willUseSharedState'
|
|
3
|
+
|
|
4
|
+
export const useForceUpdate = () => {
|
|
5
|
+
const [value, setValue] = useState(0); // integer state
|
|
6
|
+
return () => setValue(value => value + 1); // update the state to force render
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const willUseForceUpdate = () => {
|
|
10
|
+
const [useValue,,set] = willUseSharedState()
|
|
11
|
+
const useForceUpdate = () => {
|
|
12
|
+
const [v,setV] = useValue(0)
|
|
13
|
+
return () => setV(value => value + 1)
|
|
14
|
+
}
|
|
15
|
+
return [useForceUpdate,() => set(value => value + 1)]
|
|
16
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {useEffect, useRef} from 'react'
|
|
2
|
+
|
|
3
|
+
export const useIsUnmounted = () => {
|
|
4
|
+
const unmounted = useRef(false)
|
|
5
|
+
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
unmounted.current = false
|
|
8
|
+
return () => { unmounted.current = true }
|
|
9
|
+
}, [])
|
|
10
|
+
|
|
11
|
+
return () => unmounted.current
|
|
12
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import {useToggle} from './useToggle'
|
|
3
|
+
import {callIfFunction} from '@startlibs/utils'
|
|
4
|
+
|
|
5
|
+
let prevOpenToggleClose = () => {}
|
|
6
|
+
let sameTick = false
|
|
7
|
+
|
|
8
|
+
export const clearPopups = () => {
|
|
9
|
+
if (!sameTick) {
|
|
10
|
+
prevOpenToggleClose()
|
|
11
|
+
prevOpenToggleClose = () => {}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const usePopupToggle = (startWith,callback) => {
|
|
16
|
+
const toggle = useToggle(startWith,(v,prev) => {
|
|
17
|
+
const result = callIfFunction(callback,v,prev)
|
|
18
|
+
if (result === false) { return false}
|
|
19
|
+
if (v !== false && prevOpenToggleClose !== toggle.close) {
|
|
20
|
+
prevOpenToggleClose()
|
|
21
|
+
prevOpenToggleClose = toggle.close
|
|
22
|
+
sameTick = true
|
|
23
|
+
window.setTimeout(()=>sameTick = false, 1)
|
|
24
|
+
}
|
|
25
|
+
if (v === false) {
|
|
26
|
+
prevOpenToggleClose = () => {}
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
return toggle
|
|
30
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import {useEffect, useRef} from 'react'
|
|
2
|
+
|
|
3
|
+
export function usePrevious(value,initialValue) {
|
|
4
|
+
const ref = useRef(initialValue);
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
ref.current = value;
|
|
7
|
+
});
|
|
8
|
+
return ref.current;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function usePreviousUnique(value,initialValue) {
|
|
12
|
+
const ref = useRef(initialValue);
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (value !== ref.current) {
|
|
15
|
+
ref.current = value;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
return ref.current;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function usePreviousForEffect(value,initialValue) {
|
|
22
|
+
const ref = useRef(initialValue);
|
|
23
|
+
useEffect(() => () => {
|
|
24
|
+
ref.current = value;
|
|
25
|
+
},[value]);
|
|
26
|
+
return ref.current;
|
|
27
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import styled from 'styled-components'
|
|
3
|
+
|
|
4
|
+
styled
|
|
5
|
+
|
|
6
|
+
const TabFocus = styled.div`width:0;height:0;position:absolute;`
|
|
7
|
+
export const useRecycleFocus = (ref, enabled = true,lastIndex) => {
|
|
8
|
+
|
|
9
|
+
const recycle = i => (e) => {
|
|
10
|
+
if (!enabled || !ref.current) {
|
|
11
|
+
return
|
|
12
|
+
}
|
|
13
|
+
const nextTab = ref.current.querySelectorAll('[tabindex]')
|
|
14
|
+
if (nextTab) {
|
|
15
|
+
if (i<0) {
|
|
16
|
+
console.log(nextTab[1])
|
|
17
|
+
nextTab[1].focus()
|
|
18
|
+
} else {
|
|
19
|
+
console.log(nextTab[nextTab.length - 2])
|
|
20
|
+
nextTab[nextTab.length - 2].focus()
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return [<TabFocus tabIndex={1} onFocus={recycle(0)}></TabFocus>,<TabFocus tabIndex={lastIndex || 1} onFocus={recycle(-1)}></TabFocus>]
|
|
26
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React, {useMemo, useRef} from 'react'
|
|
2
|
+
import {_s, callIfFunction, callIfFunctions} from '@startlibs/utils'
|
|
3
|
+
|
|
4
|
+
const isPath = (path) => typeof path === 'string' || typeof path === 'number' || Array.isArray(path)
|
|
5
|
+
|
|
6
|
+
const getter = (value) => path => path !== "" && isPath(path) ? _s.get(path,value) : value
|
|
7
|
+
|
|
8
|
+
const uninitialized = {}
|
|
9
|
+
|
|
10
|
+
export const useRefState = (defaultValue, setCallback) => {
|
|
11
|
+
const ref = useRef(uninitialized)
|
|
12
|
+
if (ref.current === uninitialized) {
|
|
13
|
+
ref.current = callIfFunction(defaultValue)
|
|
14
|
+
}
|
|
15
|
+
return useMemo(() => {
|
|
16
|
+
const get = (path) => getter(ref.current)(path)
|
|
17
|
+
const set = (v, config) => {
|
|
18
|
+
const prev = ref.current
|
|
19
|
+
ref.current = callIfFunctions(v, () => get())
|
|
20
|
+
if (prev !== ref.current && (!config || !config.ignoreCallback)) {
|
|
21
|
+
callIfFunction(setCallback, prev, ref.current)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return {get, set}
|
|
25
|
+
}, [])
|
|
26
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import React, {startTransition, use, useMemo, useCallback, useContext, useEffect, useRef, useState} from 'react'
|
|
2
|
+
import {useConstant, useLazyConstant} from './useConstant'
|
|
3
|
+
import {useForceUpdate} from './useForceUpdate'
|
|
4
|
+
import {_s, callIfFunction} from '@startlibs/utils'
|
|
5
|
+
import _ from 'lodash/fp'
|
|
6
|
+
|
|
7
|
+
const IS_ARRAY_ITEMS_EQUALS = (a,b) => a.length === b.length && !a.some((x, i) => !Object.is(x, b[i]));
|
|
8
|
+
const IS_DEPS_EQUALS = (a = [],b = []) => Object.is(a,b) || IS_ARRAY_ITEMS_EQUALS(a,b)
|
|
9
|
+
|
|
10
|
+
const SHALLOW_IS_EQUAL = (a,b) => Object.is(a,b) || (_.isArray(a) && _.isArray(b) && IS_ARRAY_ITEMS_EQUALS(a,b))
|
|
11
|
+
|
|
12
|
+
const EMPTY_DEPS = []
|
|
13
|
+
const EMPTY_VALUE = {"EMPTY":true}
|
|
14
|
+
export const useUpdateSignal = (initialValue) => {
|
|
15
|
+
const value = useRef(initialValue)
|
|
16
|
+
value.current = initialValue
|
|
17
|
+
return useLazyConstant(() => {
|
|
18
|
+
let registered = []
|
|
19
|
+
const register = (selector = _.identity, isEqual = SHALLOW_IS_EQUAL, withTransition = false) => {
|
|
20
|
+
const selectorRef = useRef()
|
|
21
|
+
selectorRef.current = selector
|
|
22
|
+
const update = useForceUpdate()
|
|
23
|
+
const lastValueRef = useRef(_s.select(selectorRef.current, value.current))
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
const item = [update, selectorRef, isEqual, lastValueRef, withTransition]
|
|
26
|
+
registered = [...registered, item]
|
|
27
|
+
return () => {
|
|
28
|
+
registered = registered.filter(v => v !== item)
|
|
29
|
+
}
|
|
30
|
+
}, [])
|
|
31
|
+
if (value.current === EMPTY_VALUE) {
|
|
32
|
+
return EMPTY_VALUE
|
|
33
|
+
}
|
|
34
|
+
return lastValueRef.current
|
|
35
|
+
}
|
|
36
|
+
const call = (...args) =>
|
|
37
|
+
registered.forEach(([fn, selectorRef, isEqual, lastValueRef, withTransition]) => {
|
|
38
|
+
const nextValue = _s.select(selectorRef.current, ...args)
|
|
39
|
+
if (!callIfFunction(isEqual,nextValue,lastValueRef.current)) {
|
|
40
|
+
lastValueRef.current = nextValue
|
|
41
|
+
if (withTransition) {
|
|
42
|
+
(withTransition === true ? startTransition : withTransition)(() => fn(nextValue))
|
|
43
|
+
} else {
|
|
44
|
+
fn(nextValue)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
return [register, call]
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const useSelectorSignal = (value) => {
|
|
53
|
+
const prevValue = useRef(value)
|
|
54
|
+
const [_register, call] = useUpdateSignal(value)
|
|
55
|
+
const register = useCallback((selector, deps, isEqual, withTransition) => {
|
|
56
|
+
const prevDeps = useRef(EMPTY_DEPS)
|
|
57
|
+
if (prevDeps.current !== EMPTY_DEPS && !IS_DEPS_EQUALS(deps, prevDeps.current)) {
|
|
58
|
+
call(prevValue.current)
|
|
59
|
+
}
|
|
60
|
+
prevDeps.current = deps
|
|
61
|
+
return _register(selector, isEqual, withTransition)
|
|
62
|
+
}, [])
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
if (prevValue.current !== value) {
|
|
65
|
+
prevValue.current = value
|
|
66
|
+
call(value)
|
|
67
|
+
}
|
|
68
|
+
},[value])
|
|
69
|
+
return register
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const updateStateFn = (path,update) => state =>
|
|
73
|
+
path
|
|
74
|
+
? _.update(path,(v) => callIfFunction(update,v),state)
|
|
75
|
+
: callIfFunction(update,state)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
export const createStatefulContext = (v,defaultIsEqual) => {
|
|
79
|
+
const Ctx = React.createContext(v)
|
|
80
|
+
const Provider = ({value, setValue, utils,children}) => {
|
|
81
|
+
const register = useSelectorSignal(value)
|
|
82
|
+
const v = useLazyConstant(() => [register,setValue, utils])
|
|
83
|
+
return <Ctx.Provider value={v}>{children}</Ctx.Provider>
|
|
84
|
+
}
|
|
85
|
+
const useCtx = () => useContext(Ctx)
|
|
86
|
+
|
|
87
|
+
const useSelectValue = (selector,deps,defaultIsEqual, withTransition) => {
|
|
88
|
+
const [useRegister] = useCtx()
|
|
89
|
+
if (deps === true) {
|
|
90
|
+
return useRegister(selector,[],undefined,startTransition)
|
|
91
|
+
} else if (_.isPlainObject(deps)) {
|
|
92
|
+
return useRegister(selector,deps.deps,deps.isEqual, deps.withTransition)
|
|
93
|
+
} else {
|
|
94
|
+
return useRegister(selector,_.isArray(deps) ? deps : deps?.deps,defaultIsEqual, withTransition)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const useSetValue = (path) => {
|
|
98
|
+
const [reg,set ] = useCtx()
|
|
99
|
+
return useConstant((update) => set(updateStateFn(path,update)))
|
|
100
|
+
}
|
|
101
|
+
const useUtils = (path,deps) => {
|
|
102
|
+
const [reg,set, utils ] = useCtx()
|
|
103
|
+
return utils
|
|
104
|
+
}
|
|
105
|
+
return [Provider,[useSelectValue, useSetValue, useUtils, useCtx]]
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export const createSelfStatefulContext = (v,defaultIsEqual) => {
|
|
109
|
+
const [Provider,[useSelectValue, useSetValue, useUtils, useCtx]] = createStatefulContext(v,defaultIsEqual)
|
|
110
|
+
|
|
111
|
+
const StateFullProvider = ({init = {}, utils,children}) => {
|
|
112
|
+
const [state,setState] = useState(init)
|
|
113
|
+
return <Provider value={state} setValue={setState} utils={utils}>
|
|
114
|
+
{children}
|
|
115
|
+
</Provider>
|
|
116
|
+
}
|
|
117
|
+
return [StateFullProvider,[useSelectValue, useSetValue, useUtils, useCtx]]
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
export const createLazyStatefulContext = (toPromise,defaultIsEqual) => {
|
|
122
|
+
const [Provider,[useSelectValue, useSetValue, useUtils, useCtx]] = createStatefulContext(EMPTY_VALUE,defaultIsEqual)
|
|
123
|
+
const useSelectLazyValue = (selector,deps,isEqual = defaultIsEqual,withTransition) => {
|
|
124
|
+
const value = useSelectValue(selector,deps,isEqual,withTransition)
|
|
125
|
+
const utils = useUtils()
|
|
126
|
+
if (value === EMPTY_VALUE) {
|
|
127
|
+
const loadedValue = use(utils._lazyValue())
|
|
128
|
+
return _s.select(selector,loadedValue)
|
|
129
|
+
}
|
|
130
|
+
return value
|
|
131
|
+
}
|
|
132
|
+
const useSetLazyValue = (path) => {
|
|
133
|
+
const [,setValue,utils] = useCtx()
|
|
134
|
+
return useConstant((update) => {
|
|
135
|
+
return setValue(value =>
|
|
136
|
+
value === EMPTY_VALUE
|
|
137
|
+
? updateStateFn(path,update)(utils.loadedValue())
|
|
138
|
+
: updateStateFn(path,update)(value)
|
|
139
|
+
)
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const useValuePromise = (selector,deps,isEqual,withTransition) => {
|
|
144
|
+
const value = useSelectValue(selector,deps,isEqual,withTransition)
|
|
145
|
+
const utils = useUtils()
|
|
146
|
+
return useCallback(() => {
|
|
147
|
+
if (value === EMPTY_VALUE) {
|
|
148
|
+
return utils._lazyValue().then(v => console.log(v,_s.select(selector,v),selector) || _s.select(selector,v))
|
|
149
|
+
}
|
|
150
|
+
return new Promise(value)
|
|
151
|
+
},[value,deps])
|
|
152
|
+
}
|
|
153
|
+
const LazyStateFullProvider = ({value, utils,children, fallback = null}) => {
|
|
154
|
+
const [state,setState] = useState(EMPTY_VALUE)
|
|
155
|
+
const promise = useRef()
|
|
156
|
+
const loadedValue = useRef()
|
|
157
|
+
const lazyUtils = useMemo(() => ({
|
|
158
|
+
...utils,
|
|
159
|
+
_lazyValue: () => {
|
|
160
|
+
promise.current ??= callIfFunction(toPromise || _.identity,value)
|
|
161
|
+
promise.current.then((value) => {
|
|
162
|
+
loadedValue.current = value
|
|
163
|
+
})
|
|
164
|
+
return promise.current
|
|
165
|
+
},
|
|
166
|
+
loadedValue: () => loadedValue.current
|
|
167
|
+
}),[value,utils])
|
|
168
|
+
return <Provider value={state} setValue={setState} utils={lazyUtils}>
|
|
169
|
+
{
|
|
170
|
+
fallback===false
|
|
171
|
+
? children
|
|
172
|
+
: <React.Suspense fallback={fallback}>{children}</React.Suspense>
|
|
173
|
+
}
|
|
174
|
+
</Provider>
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return [LazyStateFullProvider,[useSelectLazyValue, useSetLazyValue, useValuePromise]]
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export const createSelectableContext = (v) => {
|
|
181
|
+
const [Provider,[useSelectValue]] = createStatefulContext(v)
|
|
182
|
+
return [Provider,useSelectValue]
|
|
183
|
+
}
|
|
184
|
+
export const createTriggerUpdateContext = (v) => {
|
|
185
|
+
const [Provider,[useSelectValue]] = createStatefulContext(v)
|
|
186
|
+
const TriggerProvider = React.forwardRef(({children},ref) => {
|
|
187
|
+
const [v,setV] = useState(0)
|
|
188
|
+
const trigger = React.useMemo(() => () => setV(v => v+1),[v])
|
|
189
|
+
React.useImperativeHandle(ref, () => ({trigger}) , [])
|
|
190
|
+
return <Provider value={trigger}>{children}</Provider>
|
|
191
|
+
})
|
|
192
|
+
return [TriggerProvider,useSelectValue]
|
|
193
|
+
}
|