@startlibs/core 1.0.2 → 1.0.3

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 (37) hide show
  1. package/lib/index.js +192 -97
  2. package/package.json +4 -2
  3. package/rollup.config.js +2 -0
  4. package/src/SlotFill.jsx +165 -0
  5. package/src/StartlibsProvider.jsx +61 -0
  6. package/src/hocs/WithLazyResource.jsx +32 -0
  7. package/src/hocs/WithRef.js +20 -0
  8. package/src/hocs/Wrapper.jsx +40 -0
  9. package/src/hocs/consumes.jsx +17 -0
  10. package/src/hocs/withDialogError.jsx +16 -0
  11. package/src/hocs/withGroupEvent.jsx +43 -0
  12. package/src/hooks/WithConstant.jsx +7 -0
  13. package/src/hooks/WithEffect.jsx +6 -0
  14. package/src/hooks/useAsyncEffect.jsx +11 -0
  15. package/src/hooks/useCallState.js +15 -0
  16. package/src/hooks/useCallbackStable.jsx +12 -0
  17. package/src/hooks/useConstant.js +20 -0
  18. package/src/hooks/useConsumerCatalog.jsx +21 -0
  19. package/src/hooks/useEnsureRef.js +7 -0
  20. package/src/hooks/useForceUpdate.jsx +16 -0
  21. package/src/hooks/useIsUnmounted.jsx +12 -0
  22. package/src/hooks/usePopupToggle.js +30 -0
  23. package/src/hooks/usePrevious.jsx +27 -0
  24. package/src/hooks/useRecycleFocus.jsx +28 -0
  25. package/src/hooks/useRefMemo.jsx +10 -0
  26. package/src/hooks/useRefState.js +26 -0
  27. package/src/hooks/useSignal.jsx +193 -0
  28. package/src/hooks/useTitle.jsx +12 -0
  29. package/src/hooks/useToggle.js +124 -0
  30. package/src/hooks/useUpdateEffect.js +14 -0
  31. package/src/hooks/useUpdateOnResize.jsx +14 -0
  32. package/src/hooks/willUseLayers.jsx +42 -0
  33. package/src/hooks/willUseSharedCallback.jsx +61 -0
  34. package/src/hooks/willUseSharedState.jsx +34 -0
  35. package/src/hooks/willUseSpreadState.jsx +101 -0
  36. package/src/index.js +31 -0
  37. package/yarn-error.log +3374 -0
@@ -0,0 +1,61 @@
1
+ import React from 'react'
2
+ import _ from 'lodash/fp'
3
+ import {fixMediaQuery,callIfFunction} from '@startlibs/utils'
4
+ import {SlotProvider, clearPopups} from '@startlibs/core'
5
+
6
+ export const StartlibsContext = React.createContext({})
7
+ export const PopupContext = React.createContext({})
8
+ export const StartlibsMetacontext = React.createContext(()=>{})
9
+
10
+ export const StartlibsConsumer = StartlibsContext.Consumer
11
+
12
+ const wrappeStyle = {height: '100%'}
13
+
14
+
15
+ export class StartlibsProvider extends React.Component {
16
+
17
+ componentDidMount() {
18
+ fixMediaQuery()
19
+ const addVHFix = () => {
20
+ let vh = window.innerHeight * 0.01;
21
+ document.documentElement?.style?.setProperty('--vh', `${vh}px`);
22
+ }
23
+ window.addEventListener('resize', _.debounce(200, addVHFix))
24
+ addVHFix()
25
+ }
26
+
27
+ openToggles = new Map()
28
+
29
+ popupToggleRender = ({isOpen,close},ref) => {
30
+ if (isOpen && !this.openToggles.has(ref)) {
31
+ this.closePopup()
32
+ this.openToggles.set(ref,close)
33
+ }
34
+ }
35
+ closePopup = () => {
36
+ clearPopups()
37
+ }
38
+
39
+ startlibsMeta = new Map()
40
+
41
+ getMeta = (key,initValue) => {
42
+ const exists = this.startlibsMeta.get(key)
43
+ if (exists) return exists
44
+ this.startlibsMeta.set(key,callIfFunction(initValue))
45
+ return initValue
46
+ }
47
+
48
+ render() {
49
+ return <StartlibsContext.Provider value={this.props.value}>
50
+ <StartlibsMetacontext.Provider value={this.getMeta}>
51
+ <PopupContext.Provider value={this.popupToggleRender}>
52
+ <div style={wrappeStyle} onClick={this.closePopup}>
53
+ <SlotProvider>
54
+ {this.props.children}
55
+ </SlotProvider>
56
+ </div>
57
+ </PopupContext.Provider>
58
+ </StartlibsMetacontext.Provider>
59
+ </StartlibsContext.Provider>
60
+ }
61
+ }
@@ -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,17 @@
1
+ import React from 'react'
2
+ import styled from 'styled-components'
3
+
4
+ const propsOrConsume = (prop, Consumer,childrenAsFunction) => prop ? childrenAsFunction(prop) : <Consumer>{childrenAsFunction}</Consumer>
5
+
6
+ const addConsumers = (Component,obj) =>
7
+ Object.entries(obj)
8
+ .reduce(
9
+ (acc, [propKey, Consumer]) => (props) =>
10
+ propsOrConsume(props[propKey],Consumer,v => acc({[propKey]: v, ...props})),
11
+ (props) => <Component {...props} />
12
+ )
13
+
14
+ export const consumes = (obj) => (Component) => {
15
+ const withConsumers = addConsumers(Component,obj)
16
+ return (props) => withConsumers(props)
17
+ }
@@ -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,7 @@
1
+ import React, {useEffect} from 'react'
2
+ import {useLazyConstant} from '@startlibs/core'
3
+
4
+ export const WithConstant = ({inputs=[],value,children}) => {
5
+ const constant = useLazyConstant(value,inputs)
6
+ return children(constant)
7
+ }
@@ -0,0 +1,6 @@
1
+ import React, {useEffect} from 'react'
2
+
3
+ export const WithEffect = ({inputs=[],children}) => {
4
+ useEffect(children,inputs)
5
+ return null
6
+ }
@@ -0,0 +1,11 @@
1
+ import React, {useEffect} from 'react'
2
+ import {callIfFunction} from '@startlibs/utils'
3
+
4
+ export const useAsyncEffect = (fn,args) => {
5
+ useEffect(() => {
6
+ const result = fn()
7
+ return () => {
8
+ result.then(end => callIfFunction(end))
9
+ }
10
+ },args)
11
+ }
@@ -0,0 +1,15 @@
1
+ import React, {useMemo, useState} 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 => 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, {useMemo, useRef} from 'react'
2
+ import {_s, callIfFunction, callIfFunctions} 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,mapGetOrSet} from '@startlibs/utils'
3
+ import {useRefState} from '@startlibs/core'
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,7 @@
1
+ import React, {useMemo, useRef} from 'react'
2
+ import {_s, callIfFunction, callIfFunctions} from '@startlibs/utils'
3
+
4
+ export const useEnsureRef = (forwardedRef) => {
5
+ const ref = useRef()
6
+ return forwardedRef || ref
7
+ }
@@ -0,0 +1,16 @@
1
+ import {useState} from 'react'
2
+ import {willUseSharedState} from '@startlibs/core'
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, {useRef, useContext} from 'react'
2
+ import {useToggle} from '@startlibs/core'
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,28 @@
1
+ import React from 'react'
2
+ import _ from 'lodash/fp'
3
+ import styled from 'styled-components'
4
+ import {useEventListener} from "@startlibs/utils";
5
+
6
+ styled
7
+
8
+ const TabFocus = styled.div`width:0;height:0;position:absolute;`
9
+ export const useRecycleFocus = (ref, enabled = true,lastIndex) => {
10
+
11
+ const recycle = i => (e) => {
12
+ if (!enabled || !ref.current) {
13
+ return
14
+ }
15
+ const nextTab = ref.current.querySelectorAll('[tabindex]')
16
+ if (nextTab) {
17
+ if (i<0) {
18
+ console.log(nextTab[1])
19
+ nextTab[1].focus()
20
+ } else {
21
+ console.log(nextTab[nextTab.length - 2])
22
+ nextTab[nextTab.length - 2].focus()
23
+ }
24
+ }
25
+ }
26
+
27
+ return [<TabFocus tabIndex={1} onFocus={recycle(0)}></TabFocus>,<TabFocus tabIndex={lastIndex || 1} onFocus={recycle(-1)}></TabFocus>]
28
+ }
@@ -0,0 +1,10 @@
1
+ import React, {useRef} from 'react'
2
+ import {callIfFunction} from '@startlibs/utils'
3
+
4
+ export const useRefMemo = (value) => {
5
+ const ref = useRef(value)
6
+ if (ref.current !== value) {
7
+ ref.current = callIfFunction(value)
8
+ }
9
+ return ref
10
+ }
@@ -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
+ }