@startlibs/utils 1.0.4 → 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.
Files changed (64) hide show
  1. package/lib/ActionUtils.mjs +117 -0
  2. package/lib/ActionUtils.mjs.map +1 -0
  3. package/lib/FetchUtils.mjs +100 -0
  4. package/lib/FetchUtils.mjs.map +1 -0
  5. package/lib/createPressEvent.mjs +74 -0
  6. package/lib/createPressEvent.mjs.map +1 -0
  7. package/lib/fixMediaQuery.mjs +64 -0
  8. package/lib/fixMediaQuery.mjs.map +1 -0
  9. package/lib/formatDate.mjs +110 -0
  10. package/lib/formatDate.mjs.map +1 -0
  11. package/lib/formatUtils.mjs +64 -0
  12. package/lib/formatUtils.mjs.map +1 -0
  13. package/lib/getClosestOffsetScroll.mjs +15 -0
  14. package/lib/getClosestOffsetScroll.mjs.map +1 -0
  15. package/lib/hooks/useEventListener.mjs +27 -0
  16. package/lib/hooks/useEventListener.mjs.map +1 -0
  17. package/lib/index.cjs +1811 -0
  18. package/lib/index.cjs.map +1 -0
  19. package/lib/index.mjs +19 -0
  20. package/lib/index.mjs.map +1 -0
  21. package/lib/lodashUtils.mjs +131 -0
  22. package/lib/lodashUtils.mjs.map +1 -0
  23. package/lib/promiseUtils.mjs +92 -0
  24. package/lib/promiseUtils.mjs.map +1 -0
  25. package/lib/reactUtils.mjs +12 -0
  26. package/lib/reactUtils.mjs.map +1 -0
  27. package/lib/reduceUtils.mjs +14 -0
  28. package/lib/reduceUtils.mjs.map +1 -0
  29. package/lib/smoothScroll.mjs +59 -0
  30. package/lib/smoothScroll.mjs.map +1 -0
  31. package/lib/styled.mjs +234 -0
  32. package/lib/styled.mjs.map +1 -0
  33. package/lib/timeUtils.mjs +81 -0
  34. package/lib/timeUtils.mjs.map +1 -0
  35. package/lib/timerUtils.mjs +11 -0
  36. package/lib/timerUtils.mjs.map +1 -0
  37. package/lib/utils.mjs +386 -0
  38. package/lib/utils.mjs.map +1 -0
  39. package/lib/wrapLazy.mjs +132 -0
  40. package/lib/wrapLazy.mjs.map +1 -0
  41. package/package.json +44 -30
  42. package/src/ActionUtils.js +90 -0
  43. package/src/FetchUtils.js +87 -0
  44. package/src/createPressEvent.jsx +79 -0
  45. package/src/fixMediaQuery.js +71 -0
  46. package/src/formatDate.js +121 -0
  47. package/src/formatUtils.jsx +63 -0
  48. package/src/getClosestOffsetScroll.js +12 -0
  49. package/src/hooks/useEventListener.js +26 -0
  50. package/src/index.js +19 -0
  51. package/src/lodashUtils.jsx +110 -0
  52. package/src/promiseUtils.jsx +75 -0
  53. package/src/reactUtils.jsx +7 -0
  54. package/src/reduceUtils.jsx +6 -0
  55. package/src/smoothScroll.js +58 -0
  56. package/src/styled.js +177 -0
  57. package/src/timeUtils.jsx +57 -0
  58. package/src/timerUtils.jsx +10 -0
  59. package/src/utils.jsx +342 -0
  60. package/src/wrapLazy.jsx +122 -0
  61. package/.babelrc +0 -26
  62. package/lib/index.js +0 -2399
  63. package/rollup.config.js +0 -44
  64. package/yarn-error.log +0 -3643
@@ -0,0 +1,110 @@
1
+ import _ from 'lodash/fp'
2
+ import {callIfFunction} from './utils'
3
+
4
+ const moveElement = _.curry((from, to, list) => {
5
+ const el = list[from]
6
+ const without = _.pullAt(from, list)
7
+ without.splice(to, 0, el)
8
+ return without
9
+ })
10
+
11
+ const unsetOrRemoveAt = _.curry((path, value) => {
12
+ const pathArr = toPath(path)
13
+ const [lastKey] = pathArr.slice(-1)
14
+ return update(pathArr.slice(0, -1), (v) => Array.isArray(v) ? removeAt(lastKey, v) : unset(lastKey, v), value)
15
+ })
16
+
17
+ const pathToString = (path) => toPath(path).join(".")
18
+
19
+ const sanitizePath = (path) =>
20
+ Array.isArray(path)
21
+ ? path.map(sanitizePath)
22
+ : typeof path === 'number'
23
+ ? path.toString()
24
+ : path === undefined
25
+ ? []
26
+ : path
27
+
28
+ const toPath = (path) => _.toPath(sanitizePath(path))
29
+
30
+ const isEmptyPath = (path) => (path && !path.length)
31
+
32
+ const update = _.curry((path, updater, state) => {
33
+ const sanitizedPath = sanitizePath(path)
34
+ if (isEmptyPath(sanitizedPath)) {
35
+ return updater(state)
36
+ } else {
37
+ return _.update(sanitizedPath, updater, state)
38
+ }
39
+ })
40
+
41
+ const get = _.curry((path, state) => {
42
+ const sanitizedPath = sanitizePath(path)
43
+ if (isEmptyPath(sanitizedPath)) {
44
+ return state
45
+ } else {
46
+ return _.get(sanitizedPath, state)
47
+ }
48
+ })
49
+
50
+ const set = _.curry((path, value, state) => {
51
+ const sanitizedPath = sanitizePath(path)
52
+ if (isEmptyPath(sanitizedPath)) {
53
+ return value
54
+ } else {
55
+ return _.set(sanitizedPath, value, state)
56
+ }
57
+ })
58
+ const unset = _.curry((path, state) => {
59
+ const sanitizedPath = sanitizePath(path)
60
+ if (isEmptyPath(sanitizedPath)) {
61
+ return undefined
62
+ } else {
63
+ return _.unset(sanitizedPath, state)
64
+ }
65
+ })
66
+
67
+ const removeNAt = _.curry((pos, n, value) => [...value.slice(0, Number(pos)), ...value.slice(Number(pos)+n)])
68
+ const removeAt = removeNAt(_.__,1)
69
+
70
+ const when = _.curry((cond,fn,value) => callIfFunction(cond,value) ? fn(value) : value)
71
+ const assign = _.curry((a,b) => _.assign(b,a))
72
+ const concat = _.curry((a,b) => _.concat(b,a))
73
+
74
+ const each = _.each.convert({cap:false})
75
+ const find = _.find.convert({cap:false})
76
+ const filter = _.filter.convert({cap:false})
77
+ const map = _.map.convert({cap:false})
78
+ const mapValues = _.mapValues.convert({cap:false})
79
+ const flatMap = _.flatMap.convert({cap:false})
80
+
81
+ const insertAt = _.curry((pos, newElements, value) => [...value.slice(0, pos).concat(newElements), ...value.slice(pos)])
82
+
83
+ const select = _.curry((selector,value) => (_.isFunction(selector) ? selector : (selector ? _.get(selector) : _.identity))(value))
84
+ const itemToggle = _.curry((item,list ) => (list||[]).indexOf(item)>=0 ? _.without([item],list) : [...(list||[]),item])
85
+
86
+ export const _s = {
87
+ moveElement,
88
+ unsetOrRemoveAt,
89
+ pathToString,
90
+ toPath,
91
+ update,
92
+ get,
93
+ set,
94
+ select,
95
+ unset,
96
+ removeNAt,
97
+ removeAt,
98
+ itemToggle,
99
+ when,
100
+ assign,
101
+ concat,
102
+ each,
103
+ find,
104
+ filter,
105
+ map,
106
+ flatMap,
107
+ mapValues,
108
+ insertAt,
109
+ flowEach: (...args) => _.flow(...args.filter(Boolean))
110
+ }
@@ -0,0 +1,75 @@
1
+ import React from 'react'
2
+ import _ from "lodash/fp";
3
+
4
+ export const createPromiseResolverPair = () => {
5
+ let res, rej;
6
+ const promise = new Promise((res2,rej2) => {
7
+ res = res2;
8
+ rej = rej2;
9
+ })
10
+ return [promise,[res,rej]]
11
+ }
12
+
13
+
14
+ export const createSimplePromiseLock = () => {
15
+ let promiseQueue = Promise.resolve()
16
+ return () => {
17
+ let resolve = _.identity
18
+ const nextPromise = new Promise(res => {
19
+ resolve = res
20
+ })
21
+ const prevPromise = promiseQueue
22
+ promiseQueue = nextPromise
23
+ return prevPromise.then(() => resolve)
24
+ }
25
+ }
26
+ async function* createSinglePromiseQueue() {
27
+ let lastPromise = Promise.resolve()
28
+ /* eslint-disable no-await-in-loop */
29
+ while (true) {
30
+ await lastPromise
31
+ let resolve = _.identity
32
+ lastPromise = new Promise(res => {
33
+ resolve = res
34
+ })
35
+ yield resolve
36
+ }
37
+ }
38
+
39
+
40
+ export async function* createPromiseQueue(parallel = 1) {
41
+ const queues = _.times(createSinglePromiseQueue, parallel)
42
+ const promises = queues.map(q => q.next())
43
+ const locks = await Promise.all(promises)
44
+
45
+ const getNextAvailableLock = async () => {
46
+ const index = locks.findIndex(Boolean)
47
+ if (index >= 0) {
48
+ const lock = locks[index]
49
+ locks[index] = null
50
+ promises[index] = queues[index].next().then(result => {
51
+ locks[index] = result
52
+ })
53
+ return [lock.value, locks.filter(Boolean).length]
54
+ }
55
+ await Promise.race(promises)
56
+ return getNextAvailableLock()
57
+ }
58
+ while (true) {
59
+ const lock = await getNextAvailableLock()
60
+ yield lock
61
+ }
62
+ }
63
+
64
+ export const promiseMapReduce = (arr, callback) =>
65
+ arr.reduce((acc, item) => acc.then(seq => callback(item).then(res => [...seq, res])), Promise.resolve([]))
66
+
67
+ export const asyncDebounce = (fn, delay = 200) => {
68
+ let timeout
69
+ return (...args) => {
70
+ clearTimeout(timeout)
71
+ return new Promise(res => {
72
+ timeout = setTimeout(() => res(fn(...args)), delay)
73
+ })
74
+ }
75
+ }
@@ -0,0 +1,7 @@
1
+ import React from 'react'
2
+
3
+ export const addExtraProp = (element, extra, override = true) => React.cloneElement(
4
+ element,
5
+ override ? {...element.props,...extra} : {...extra,...element.props},
6
+ element.props.children
7
+ )
@@ -0,0 +1,6 @@
1
+ import _ from 'lodash/fp'
2
+
3
+ export const compareBest = (compare) => (best,next) => best ? (compare(best,next) ? next : best) : next
4
+
5
+ export const toLowest = (getValue = _.identity) => compareBest((best,next) => (getValue(next) < getValue(best)))
6
+ export const toHighest = (getValue = _.identity) => compareBest((best,next) => (getValue(next) > getValue(best)))
@@ -0,0 +1,58 @@
1
+ // @flow
2
+ // Get the top position of an element in the document
3
+ const getTop = function(element, start) {
4
+ // return value of html.getBoundingClientRect().top ... IE : 0, other browsers : -pageYOffset
5
+ if(!element || element.nodeName === 'HTML') return -start
6
+ return element.getBoundingClientRect().top + start
7
+ }
8
+ // ease in out function thanks to:
9
+ // http://blog.greweb.fr/2012/02/bezier-curve-based-easing-functions-from-concept-to-implementation/
10
+ const easeInOutCubic = function (t) { return t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1 }
11
+
12
+ // calculate the scroll position we should be in
13
+ // given the start and end point of the scroll
14
+ // the time elapsed from the beginning of the scroll
15
+ // and the total duration of the scroll (default 500ms)
16
+ const position = function(start, end, elapsed, duration) {
17
+ if (elapsed > duration) return end;
18
+ return start + (end - start) * easeInOutCubic(elapsed / duration); // <-- you can change the easing funtion there
19
+ // return start + (end - start) * (elapsed / duration); // <-- this would give a linear scroll
20
+ }
21
+
22
+ // we use requestAnimationFrame to be called by the browser before every repaint
23
+ // if the first argument is an element then scroll to the top of this element
24
+ // if the first argument is numeric then scroll to this location
25
+ // if the callback exist, it is called when the scrolling is finished
26
+ // if context is set then scroll that element, else scroll window
27
+ export const smoothScroll = (el, duration = 500, callback, ctx) => {
28
+ const context = ctx || window;
29
+ const start = context.scrollTop || window.pageYOffset;
30
+ const offset = el instanceof HTMLElement ? 0 : el.offset||0
31
+
32
+ const end = offset + (typeof el === 'number' ? Math.round(el) :
33
+ el instanceof HTMLElement ? getTop(el, start) : getTop(el.target,start))
34
+
35
+ const clock = Date.now();
36
+ const requestAnimationFrame = window.requestAnimationFrame ||
37
+ window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame ||
38
+ function(fn){window.setTimeout(fn, 15);};
39
+
40
+ const step = () => {
41
+ const elapsed = Date.now() - clock;
42
+ if (context !== window) {
43
+ context.scrollTop = position(start, end, elapsed, duration);
44
+ }
45
+ else {
46
+ window.scroll(window.pageXOffset, position(start, end, elapsed, duration));
47
+ }
48
+
49
+ if (elapsed > duration) {
50
+ if (typeof callback === 'function') {
51
+ callback(el);
52
+ }
53
+ } else {
54
+ requestAnimationFrame(step);
55
+ }
56
+ }
57
+ step();
58
+ }
package/src/styled.js ADDED
@@ -0,0 +1,177 @@
1
+ // @flow
2
+ import _ from 'lodash/fp'
3
+ import {darken} from 'polished'
4
+ import React from 'react'
5
+ import styled, {css} from 'styled-components'
6
+ import createEmotion from 'create-emotion'
7
+ import {scrollWidth} from './fixMediaQuery'
8
+ import {_s} from './lodashUtils'
9
+ import {callIfFunction} from './utils'
10
+
11
+ export const getColor = (propsOrColor, hover) => {
12
+ if (propsOrColor === undefined) {
13
+ return ({theme})=>theme.colors.gray150
14
+ }
15
+ if (typeof propsOrColor==='string') {
16
+ return ({theme})=>theme.colors[propsOrColor] || propsOrColor
17
+ }
18
+ const {theme,...props} = propsOrColor
19
+ if (props.dark) {
20
+ return darken(0.1, getColor({theme, ..._.unset( 'dark', props)}))
21
+ } else if (hover && props.hover) {
22
+ return getColor({theme, color: props.hover})
23
+ } else if (props.color) {
24
+ return theme.colors[props.color] || props.color
25
+ } else if (props.highlight) {
26
+ return theme.colors.main
27
+ } else if (props.alert) {
28
+ return theme.colors.alert
29
+ } else if (props.success) {
30
+ return theme.colors.success
31
+ } else {
32
+ return theme.colors.gray150
33
+ }
34
+ }
35
+
36
+ export const getMargin = (enumeratedMargins) => (props) => enumeratedMargins[props.margin] || props.margin || enumeratedMargins['default'] || 0
37
+
38
+ /**
39
+ * Returns the value of `props[path]` or `defaultValue`
40
+ * @example
41
+ * const Button = styled.button`
42
+ * color: ${prop('color', 'red')};
43
+ * `
44
+ */
45
+ export const prop = (path,defaultValue) =>
46
+ (props = {}) => _.get(path.trim(), props) || defaultValue
47
+
48
+ export const customTheme = (type) =>
49
+ (props = {}) => _.get(["theme","startlibs",type], props) || ''
50
+
51
+ /**
52
+ * Returns `pass` if prop is truthy. Otherwise returns `fail`
53
+ * @example
54
+ * // usage with styled-theme
55
+ * import styled from 'styled-components'
56
+ * import { ifProp } from 'styled-tools'
57
+ * import { palette } from 'styled-theme'
58
+ *
59
+ * const Button = styled.button`
60
+ * background-color: ${ifProp('transparent', 'transparent', palette(0))};
61
+ * color: ${ifProp(['transparent', 'accent'], palette('secondary', 0))};
62
+ * font-size: ${ifProp({ size: 'large' }, '20px', ifProp({ size: 'medium' }, '16px', '12px'))};
63
+ *`
64
+ */
65
+ export const ifProp = (path, pass, fail) => {
66
+ if (pass && !_.isFunction(pass)) { // old version
67
+ return (props = {}) => {
68
+ let result = false
69
+ if (Array.isArray(path)) {
70
+ result = path.every(p=> testSinglePath(props,p))
71
+ } else if (typeof path === 'object') {
72
+ result = !Object.entries(path).find(([k,v])=>props[k]!==v)
73
+ } else {
74
+ result = testSinglePath(props,path)
75
+ }
76
+ return result ? pass : fail
77
+ }
78
+ } else {
79
+ return (...argsTrue) => {
80
+ const check = pass || _.identity
81
+ const fn = props => check(_.get(path,props)) ? css(...argsTrue) : ''
82
+ fn.orElse = (...argsFalse) => (props) => check(_.get(path,props)) ? css(...argsTrue) : css(...argsFalse)
83
+ return fn
84
+ }
85
+ }
86
+ }
87
+ export const ifPropEquals = (path,str) => ifProp(path,_.isEqual(str))
88
+ export const ifNotProp = (path) => ifProp(path,v => !v)
89
+
90
+ const checkCustomScreenWidth = (sizeParis,customWidth) => {
91
+ const currentWidth = callIfFunction(customWidth)
92
+ return !sizeParis.find(([size,type]) => {
93
+ if (type === "min-width") {
94
+ return currentWidth < size
95
+ }
96
+ if (type === "max-width") {
97
+ return currentWidth > size
98
+ }
99
+ })
100
+ }
101
+
102
+ export const media = (...sizePairs) => (...args) => {
103
+ return css`
104
+ ${props =>
105
+ props.theme?.overrideMediaWidth
106
+ ? (checkCustomScreenWidth(sizePairs,props.theme?.overrideMediaWidth) ? css(...args) : "")
107
+ : css`@media ${sizePairs.map(([size,type]) => `(${type}: ${size+scrollWidth}px)`).join(' and ')} {
108
+ ${ css(...args) }
109
+ }`
110
+ }`
111
+ }
112
+
113
+ media.mobile = media([600,'max-width'])
114
+ media.between = (min,max) => media([min,'min-width'],[max,'max-width'])
115
+ media.max = (n) => media([n,'max-width'])
116
+ media.min = (n) => media([n,'min-width'])
117
+ media.desktop = media([601,'min-width'])
118
+
119
+ export const vheight = (strings,size) => {
120
+ const propType = strings[0]
121
+ const calc = (strings[1]||"").replace(";","").trim()
122
+ return `
123
+ ${propType} ${calc ? `calc(${size}vh ${calc})` : `${size}vh`};
124
+ ${propType} ${calc ? `calc(calc(var(--vh, 1vh) * ${size}) ${calc})` : `calc(var(--vh, 1vh) * ${size})`};
125
+ `
126
+ }
127
+
128
+ function testSinglePath(props,path) {
129
+ if (typeof path === 'string') {
130
+ return !!_s.get(path, props)
131
+ } else if (typeof path === 'number') {
132
+ return !!props[path]
133
+ } else {
134
+ return false
135
+ }
136
+ }
137
+
138
+ const getFrameOffset = (i,total) => Math.round(i*100/(total-1))
139
+
140
+
141
+ const emotion = createEmotion({
142
+ nonce: (typeof document !== 'undefined' &&
143
+ (document.querySelector('meta[property="csp-nonce"]') || {}).content) || 'animation_nonce'
144
+ })
145
+
146
+ export const oneWayAnimation = (...frames) => {
147
+ return emotion.keyframes`
148
+ ${frames.map((frame,i) =>
149
+ !frame ? '' : `${getFrameOffset(i,frames.length)}% {
150
+ ${frame}
151
+ }`
152
+ )}`
153
+ }
154
+
155
+ export const twoWayAnimation = (...frames) => [frames,_.reverse(frames)].map((frames)=>oneWayAnimation(...frames))
156
+
157
+ export const animationTiming = (props) => (...animations) => animations.filter(_=>_).map(_=>_+' '+props).join(',')
158
+
159
+ export const identityTranslateFrames = oneWayAnimation('transform: translateY(0);', 'transform: translateY(0);')
160
+ export const identityOpacityFrames = oneWayAnimation('opacity: 1;', 'opacity:1;')
161
+
162
+ export const styledExtend = ({target,componentStyle}) => (...template) => {
163
+ const wasStatic = !_.find(_.isFunction,componentStyle.rules)
164
+ const v = styled(target)(...template,...(wasStatic ? [] : [() => ""]))
165
+ v.componentStyle.rules = [...componentStyle.rules,...v.componentStyle.rules]
166
+ return v
167
+ }
168
+
169
+ export const printerMedia = (...args) => (props) =>
170
+ props.theme.printMode
171
+ ? css(...args)
172
+ : css`@media print { ${css(...args)} }`
173
+
174
+ export const notPrinterMedia = (...args) => (props) =>
175
+ props.theme.printMode
176
+ ? ''
177
+ : css`@media screen { ${css(...args)} }`
@@ -0,0 +1,57 @@
1
+ import React from 'react'
2
+
3
+ export const SECOND = 1000
4
+ export const MINUTE = SECOND * 60
5
+ export const HOUR = MINUTE * 60
6
+ export const DAY = HOUR * 24
7
+ export const WEEK = DAY * 7
8
+ export const MONTH = DAY * 31
9
+ export const YEAR = DAY * 366
10
+
11
+ export const getStartOfDay = (d,utc = false) => {
12
+ const date = new Date(d)
13
+ if (utc) {
14
+ date.setUTCHours(0,0,0,0)
15
+ } else {
16
+ date.setHours(0,0,0,0)
17
+ }
18
+ return date
19
+ }
20
+ export const isSameDay = (a,b,utc = false) => getStartOfDay(a,utc).getTime() === getStartOfDay(b,utc).getTime()
21
+
22
+
23
+
24
+ export const intervalsIntersect = ([a1, a2], [b1, b2]) =>
25
+ (b2 >= a1) && (b1 <= a2)
26
+
27
+ export const intervalsIntersectExclusive = ([a1, a2], [b1, b2]) =>
28
+ (b2 > a1) && (b1 < a2)
29
+
30
+ export const intervalsIntersection = (a, b) => {
31
+ if (!intervalsIntersect(a,b)) {
32
+ return []
33
+ }
34
+ return [[Math.max(a[0], b[0]), Math.min(a[1], b[1])]]
35
+ }
36
+
37
+ export const diffIntervals = ([a1, a2], [b1, b2]) => {
38
+ if (
39
+ a1 >= b2 || // a after b
40
+ b1 >= a2 // a before b
41
+ ) {
42
+ return [[a1, a2]]
43
+ } else if (a1 > b1 && a2 < b2) { // a inside b
44
+ return []
45
+ }
46
+ if (b1 > a1 && b2 < a2) { // b inside a
47
+ return [[a1, b1], [b2, a2]]
48
+ }
49
+ if (b1 <= a1) { // b intersecting left of a
50
+ return [[b2, a2]]
51
+ } else { // b intersecting right of a
52
+ return [[a1, b1]]
53
+ }
54
+ }
55
+
56
+ export const blockDiffIntervals = (a, b) => intervalsIntersect(a, b) ? [] : [a]
57
+ export const startBlockDiffIntervals = (a, b) => intervalsIntersectExclusive(a, b) && a[0] >= b[0] ? [] : [a]
@@ -0,0 +1,10 @@
1
+ import React from 'react'
2
+ import styled from 'styled-components/macro'
3
+
4
+ styled
5
+ export const timerPromise = (time) => new Promise((res) => setTimeout(res,time))
6
+
7
+ export const cancelableTimer = (res,time) => {
8
+ const v = setTimeout(res,time)
9
+ return () => clearTimeout(v)
10
+ }