@startlibs/components 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 (115) hide show
  1. package/lib/BasicBulletList.mjs +9 -0
  2. package/lib/BasicBulletList.mjs.map +1 -0
  3. package/lib/Button.mjs +95 -0
  4. package/lib/Button.mjs.map +1 -0
  5. package/lib/ContextMenu.mjs +452 -0
  6. package/lib/ContextMenu.mjs.map +1 -0
  7. package/lib/Dialog.mjs +232 -0
  8. package/lib/Dialog.mjs.map +1 -0
  9. package/lib/Draggable.mjs +880 -0
  10. package/lib/Draggable.mjs.map +1 -0
  11. package/lib/ErrorBoundary.mjs +108 -0
  12. package/lib/ErrorBoundary.mjs.map +1 -0
  13. package/lib/FillLastLineResizer.mjs +238 -0
  14. package/lib/FillLastLineResizer.mjs.map +1 -0
  15. package/lib/Flip.mjs +604 -0
  16. package/lib/Flip.mjs.map +1 -0
  17. package/lib/FlipList.mjs +186 -0
  18. package/lib/FlipList.mjs.map +1 -0
  19. package/lib/FormatDate.mjs +25 -0
  20. package/lib/FormatDate.mjs.map +1 -0
  21. package/lib/Icon.mjs +50 -0
  22. package/lib/Icon.mjs.map +1 -0
  23. package/lib/Image.mjs +41 -0
  24. package/lib/Image.mjs.map +1 -0
  25. package/lib/Link.mjs +116 -0
  26. package/lib/Link.mjs.map +1 -0
  27. package/lib/Loading.mjs +10 -0
  28. package/lib/Loading.mjs.map +1 -0
  29. package/lib/LoadingDiv.mjs +83 -0
  30. package/lib/LoadingDiv.mjs.map +1 -0
  31. package/lib/Notifications.mjs +63 -0
  32. package/lib/Notifications.mjs.map +1 -0
  33. package/lib/PageLoader.mjs +42 -0
  34. package/lib/PageLoader.mjs.map +1 -0
  35. package/lib/PersistentNotifications.mjs +130 -0
  36. package/lib/PersistentNotifications.mjs.map +1 -0
  37. package/lib/Popup.mjs +49 -0
  38. package/lib/Popup.mjs.map +1 -0
  39. package/lib/SplitColumnsContainer.mjs +43 -0
  40. package/lib/SplitColumnsContainer.mjs.map +1 -0
  41. package/lib/SwitchDiv.mjs +61 -0
  42. package/lib/SwitchDiv.mjs.map +1 -0
  43. package/lib/TextButton.mjs +41 -0
  44. package/lib/TextButton.mjs.map +1 -0
  45. package/lib/ToastNotifications.mjs +215 -0
  46. package/lib/ToastNotifications.mjs.map +1 -0
  47. package/lib/Tooltip.mjs +160 -0
  48. package/lib/Tooltip.mjs.map +1 -0
  49. package/lib/TransitionDiv.mjs +56 -0
  50. package/lib/TransitionDiv.mjs.map +1 -0
  51. package/lib/WarningDialog.mjs +84 -0
  52. package/lib/WarningDialog.mjs.map +1 -0
  53. package/lib/alt/buttons/OnClickLoadingButton.mjs +36 -0
  54. package/lib/alt/buttons/OnClickLoadingButton.mjs.map +1 -0
  55. package/lib/alt/dialogs/DialogWithImageHeader.mjs +14 -0
  56. package/lib/alt/dialogs/DialogWithImageHeader.mjs.map +1 -0
  57. package/lib/alt/dialogs/DialogWithSidebar.mjs +106 -0
  58. package/lib/alt/dialogs/DialogWithSidebar.mjs.map +1 -0
  59. package/lib/alt/dialogs/MobileBubbleDialog.mjs +49 -0
  60. package/lib/alt/dialogs/MobileBubbleDialog.mjs.map +1 -0
  61. package/lib/containers/DialogContainer.mjs +413 -0
  62. package/lib/containers/DialogContainer.mjs.map +1 -0
  63. package/lib/containers/Reposition.mjs +35 -0
  64. package/lib/containers/Reposition.mjs.map +1 -0
  65. package/lib/containers/usePositioning.mjs +294 -0
  66. package/lib/containers/usePositioning.mjs.map +1 -0
  67. package/lib/index.cjs +5000 -0
  68. package/lib/index.cjs.map +1 -0
  69. package/lib/index.mjs +37 -0
  70. package/lib/index.mjs.map +1 -0
  71. package/lib/useDialogWithToggle.mjs +18 -0
  72. package/lib/useDialogWithToggle.mjs.map +1 -0
  73. package/lib/willUseIsSticky.mjs +123 -0
  74. package/lib/willUseIsSticky.mjs.map +1 -0
  75. package/package.json +44 -29
  76. package/src/BasicBulletList.jsx +21 -0
  77. package/src/Button.jsx +288 -0
  78. package/src/ContextMenu.jsx +621 -0
  79. package/src/Dialog.jsx +413 -0
  80. package/src/Draggable.jsx +801 -0
  81. package/src/ErrorBoundary.jsx +75 -0
  82. package/src/FillLastLineResizer.jsx +226 -0
  83. package/src/Flip.jsx +560 -0
  84. package/src/FlipList.jsx +136 -0
  85. package/src/FormatDate.jsx +9 -0
  86. package/src/Icon.jsx +49 -0
  87. package/src/Image.jsx +23 -0
  88. package/src/Link.jsx +68 -0
  89. package/src/Loading.jsx +29 -0
  90. package/src/LoadingDiv.jsx +75 -0
  91. package/src/Notifications.jsx +89 -0
  92. package/src/PageLoader.jsx +48 -0
  93. package/src/PersistentNotifications.jsx +182 -0
  94. package/src/Popup.jsx +49 -0
  95. package/src/SplitColumnsContainer.jsx +63 -0
  96. package/src/SwitchDiv.jsx +30 -0
  97. package/src/TextButton.jsx +34 -0
  98. package/src/ToastNotifications.jsx +298 -0
  99. package/src/Tooltip.jsx +158 -0
  100. package/src/TransitionDiv.jsx +34 -0
  101. package/src/WarningDialog.jsx +70 -0
  102. package/src/alt/buttons/OnClickLoadingButton.jsx +16 -0
  103. package/src/alt/dialogs/DialogWithImageHeader.jsx +42 -0
  104. package/src/alt/dialogs/DialogWithSidebar.jsx +135 -0
  105. package/src/alt/dialogs/MobileBubbleDialog.jsx +56 -0
  106. package/src/containers/DialogContainer.jsx +423 -0
  107. package/src/containers/Reposition.jsx +24 -0
  108. package/src/containers/usePositioning.jsx +305 -0
  109. package/src/index.js +35 -0
  110. package/src/useDialogWithToggle.jsx +12 -0
  111. package/src/willUseIsSticky.jsx +113 -0
  112. package/.babelrc +0 -27
  113. package/lib/index.js +0 -5429
  114. package/rollup.config.js +0 -52
  115. package/yarn-error.log +0 -3724
@@ -0,0 +1,9 @@
1
+ import React from 'react'
2
+ import {StartlibsConsumer} from '@startlibs/core'
3
+ import {formatDate} from '@startlibs/utils'
4
+
5
+ export const FormatDate = ({date,format, noUTC,children}) => <StartlibsConsumer>{({dateMessages}) => {
6
+ if (!date) { return null }
7
+ const formatted = formatDate(new Date(date), format, dateMessages, noUTC)
8
+ return children ? children(formatted) : formatted
9
+ }}</StartlibsConsumer>
package/src/Icon.jsx ADDED
@@ -0,0 +1,49 @@
1
+ import React from 'react'
2
+ import styled from 'styled-components'
3
+ import { withTheme } from 'styled-components'
4
+
5
+
6
+ class IconComponent extends React.Component {
7
+
8
+ state = {}
9
+
10
+ componentDidMount() {
11
+ const icon = this.props.icon
12
+ if ((this.props.theme.svgIcons || {})[icon]) {
13
+ this.props.theme.svgLoader(icon).then((i) => this.setState({Svg: i.default}))
14
+ }
15
+ }
16
+
17
+ render() {
18
+ const {icon, children, theme, clickable, customIcon, colorSet, color, ...rest} = this.props
19
+ if ((theme.svgIcons || {})[icon]) {
20
+ if (this.state.Svg) {
21
+ return <this.state.Svg {...rest} />
22
+ } else {
23
+ return null
24
+ }
25
+ }
26
+ return <span {...rest}>{theme.icons[icon] || (customIcon && icon)}{children}</span>
27
+ }
28
+ }
29
+
30
+ export const Icon = styled(withTheme(IconComponent))`
31
+ font-family: '${props => props.theme?.iconFont}' !important;
32
+ speak: none;
33
+ font-style: normal;
34
+ font-weight: normal;
35
+ font-variant: normal;
36
+ text-transform: none;
37
+ line-height: 1;
38
+
39
+ /* Better Font Rendering =========== */
40
+ -webkit-font-smoothing: antialiased;
41
+ -moz-osx-font-smoothing: grayscale;
42
+ user-select: none;
43
+ ${props => props.clickable && `
44
+ cursor: pointer;
45
+ &:hover {
46
+ opacity: 0.85;
47
+ }
48
+ `}
49
+ `
package/src/Image.jsx ADDED
@@ -0,0 +1,23 @@
1
+ import React, {forwardRef, useEffect, useState} from 'react'
2
+
3
+ export const Image = forwardRef(({dom,src,style, ...rest}, ref) => {
4
+
5
+ const [image, setImage] = useState(typeof src === 'string' ? src : (src && URL.createObjectURL(src)))
6
+
7
+ useEffect(() => {
8
+ if (src) {
9
+ const imageStr = typeof src === 'string' ? src : URL.createObjectURL(src)
10
+ const img = document.createElement('img')
11
+ img.onload = () => {
12
+ setImage(imageStr)
13
+ }
14
+ img.src = imageStr
15
+ } else {
16
+ setImage("")
17
+ }
18
+ }, [src])
19
+
20
+ return dom
21
+ ? <img {...rest} src={image} ref={ref}/>
22
+ : <div {...rest} style={{...style,backgroundImage:image && `url("${image}")`}} ref={ref}/>
23
+ })
package/src/Link.jsx ADDED
@@ -0,0 +1,68 @@
1
+ import React, {useMemo} from 'react'
2
+ import * as Wouter from 'wouter'
3
+ import { useBrowserLocation } from "wouter/use-browser-location"
4
+ import { useCallbackStable } from "@startlibs/core"
5
+ import {callIfFunction, addQueryString} from "@startlibs/utils";
6
+ import _ from 'lodash/fp'
7
+
8
+ export const Link = ({href,...props}) => _.isString(props.to) ? <Wouter.Link {...props} /> : <a {...props} href={href?.replace("~","")} />
9
+
10
+ export const HistoryLink = ({to,onClick,...props}) => {
11
+ return <a onClick={(e) => {
12
+ if (onClick && !callIfFunction(onClick,e)) { return }
13
+ if (e.isDefaultPrevented()) { return }
14
+ if (!to) { return }
15
+ e.preventDefault()
16
+ const {state,pathname,search,hash} = _.isObject(to) ? to : {pathname:to}
17
+ const url = (pathname || "") + (search ? "?" + search.replace(/^\?/,"") : "") + (hash ? "#" + hash.replace(/^#/,"") : "")
18
+ window.history.pushState(state, '', url)
19
+ var popStateEvent = new PopStateEvent('popstate', { state: state });
20
+ window.dispatchEvent(popStateEvent);
21
+ }} {...props} />
22
+ }
23
+
24
+ export const Route = ({element,children,...props}) => <Wouter.Route {...props}>{element || children}</Wouter.Route>
25
+ export const Routes = Wouter.Switch
26
+ export const Navigate = Wouter.Redirect
27
+ export const useLocation = () => {
28
+ const [path] = Wouter.useLocation()
29
+ const [search] = Wouter.useSearchParams()
30
+ return {pathname:path,search:search ? "?" + search : "",state:window.history.state}
31
+ }
32
+ export const useAbsolutePath = () => {
33
+ const router = Wouter.useRouter();
34
+ const [path] = Wouter.useLocation()
35
+ return router.base + path
36
+ }
37
+ export const useAbsoluteLocation = () => {
38
+ const router = Wouter.useRouter();
39
+ const {pathname,search} = useLocation()
40
+ return router.base + pathname + search
41
+ }
42
+ export const useNavigate = () => Wouter.useLocation()[1]
43
+ export const useParams = Wouter.useParams
44
+ export const useSearchParams = (defaultParams) => {
45
+ const [location, navigate] = Wouter.useLocation()
46
+ const search = Wouter.useSearch()
47
+ const searchParams = useMemo(() => _.assign(defaultParams,Object.fromEntries(new URLSearchParams(search).entries())), [search])
48
+
49
+ let tempSearchParams = searchParams
50
+
51
+ const setSearchParams = useCallbackStable((nextParams, options) => {
52
+ tempSearchParams = _.isFunction(nextParams) ? nextParams(tempSearchParams) : _.assign(tempSearchParams, nextParams)
53
+ const cleanParams = defaultParams
54
+ ? _.flow(
55
+ _.toPairs,
56
+ _.filter(([k,v]) => defaultParams[k] !== v),
57
+ _.fromPairs
58
+ )(tempSearchParams)
59
+ : tempSearchParams
60
+ navigate(addQueryString(location, cleanParams), options)
61
+ })
62
+
63
+ return [searchParams, setSearchParams]
64
+ }
65
+
66
+ export const Router = Wouter.Router
67
+
68
+ export {useBrowserLocation}
@@ -0,0 +1,29 @@
1
+ import styled from 'styled-components'
2
+ import {getColor} from '@startlibs/utils'
3
+
4
+ export const Loading = styled.div`
5
+ z-index:1;
6
+ --loading-color: ${props => props.color || (props.white ? 'rgba(255,255,255,0.9)' : getColor("main")(props))};
7
+ --loading-bw: ${props => props.borderWidth || 4}px;
8
+ width: ${props => props.size || 26}px;
9
+ aspect-ratio: 1;
10
+ flex-shrink: 0;
11
+ border-radius: 50%;
12
+ background:
13
+ radial-gradient(farthest-side, var(--loading-color) 94%, #0000) top/var(--loading-bw) var(--loading-bw) no-repeat,
14
+ conic-gradient(#0000 15%, var(--loading-color));
15
+ -webkit-mask: radial-gradient(farthest-side, #0000 calc(100% - var(--loading-bw)), #000 0);
16
+ mask: radial-gradient(farthest-side, #0000 calc(100% - var(--loading-bw)), #000 0);
17
+ animation: loading-spin 0.6s infinite linear;
18
+ ${props => props.absolute && `
19
+ position: absolute;
20
+ top: 0;
21
+ bottom: 0;
22
+ right: 0;
23
+ left: 0;
24
+ margin: auto;
25
+ `}
26
+ @keyframes loading-spin {
27
+ 100% {transform: rotate(1turn)}
28
+ }
29
+ `
@@ -0,0 +1,75 @@
1
+ import React from 'react'
2
+ import _ from 'lodash/fp'
3
+ import styled from 'styled-components'
4
+ import {willUseFlipSwitch} from './Flip'
5
+ import {Loading} from './Loading'
6
+ import {useEnsureRef, useLazyConstant} from '@startlibs/core'
7
+ import {twoWayAnimation} from '@startlibs/utils'
8
+
9
+
10
+ const [loadingEnter, loadingLeave] = twoWayAnimation('opacity:0;', 'opacity:1;')
11
+ const [onEnter, onLeave] = twoWayAnimation('opacity:0; transform:translateY(50%)', 'opacity:1;transform:translateY(0)')
12
+
13
+ const BlockContainer = styled.div`
14
+ position:relative;
15
+ `
16
+
17
+ const FlipDiv = ({useAnimation, outerRef, onEnter = onEnter, onLeave = onLeave, ...props}) => {
18
+ const ensuredRef = useEnsureRef(outerRef)
19
+ useAnimation(ensuredRef, onEnter, onLeave)
20
+ return <BlockContainer {...props} ref={ensuredRef}/>
21
+ }
22
+
23
+ const PaddingDiv = styled.div`
24
+ padding: ${props => props.height} 0;
25
+ `
26
+
27
+ export const LoadingDiv = ({defaultToSuspense = true, fallback, isLoading, className, height = 12, size = 32, borderWidth = 5, children}) => {
28
+ const flipAnimation = useLazyConstant(willUseFlipSwitch)
29
+
30
+ const loadingBlock = <PaddingDiv height={_.isString(height) ? borderWidth : (height+'rem')}>
31
+ <Loading autoFocus absolute size={size} borderWidth={borderWidth}/>
32
+ </PaddingDiv>
33
+
34
+ return <React.Suspense
35
+ fallback={<FlipDiv
36
+ key="loading"
37
+ padded
38
+ onEnter={loadingEnter}
39
+ onLeave={loadingLeave}
40
+ useAnimation={flipAnimation}
41
+ className={className}
42
+ >
43
+ {fallback}
44
+ {loadingBlock}
45
+ </FlipDiv>}
46
+ >
47
+ {
48
+ (typeof isLoading === 'boolean' || defaultToSuspense)
49
+ ? (isLoading
50
+ ? <FlipDiv
51
+ key="loading"
52
+ padded
53
+ onEnter={loadingEnter}
54
+ onLeave={loadingLeave}
55
+ useAnimation={flipAnimation}
56
+ className={className}
57
+ >
58
+ {fallback}
59
+ {loadingBlock}
60
+ </FlipDiv>
61
+ : <FlipDiv
62
+ key="loaded"
63
+ loaded
64
+ onEnter={loadingEnter}
65
+ onLeave={loadingLeave}
66
+ useAnimation={flipAnimation}
67
+ className={className}
68
+ >
69
+ {children}
70
+ </FlipDiv>
71
+ )
72
+ : children
73
+ }
74
+ </React.Suspense>
75
+ }
@@ -0,0 +1,89 @@
1
+ import React, {useEffect, useRef} from 'react'
2
+ import _ from 'lodash/fp'
3
+ import styled from 'styled-components'
4
+ import {Fill, useToggle, willUseSharedState} from '@startlibs/core'
5
+ import {callIfFunction, getColor, oneWayAnimation} from '@startlibs/utils'
6
+ import {useFlipAnimation} from './Flip'
7
+ import {Icon} from './Icon'
8
+
9
+ const NotificationContainer = styled.div `
10
+ position: fixed;
11
+ top: 0;
12
+ height: 1px;
13
+ left: 0;
14
+ right: 0;
15
+ text-align: center;
16
+ `
17
+
18
+ const Notification = styled.div `
19
+ padding: 1rem 4.5rem 1rem 1.5rem;
20
+ font-size: 14px;
21
+ font-weight: 500;
22
+ border-radius: 10px;
23
+ color: white;
24
+ text-align: left;
25
+ background: ${props => getColor(props.type)(props)};
26
+ box-shadow: 0 2px 0px 0px rgba(0,0,0,0.1);
27
+ min-width: 12rem;
28
+ min-height: 48px;
29
+ display: inline-flex;
30
+ align-items: center;
31
+ position: relative;
32
+ top: 1rem;
33
+ ${Icon} {
34
+ color: white;
35
+ display: inline-block;
36
+ background-color: rgba(0,0,0,0.1);
37
+ font-size: 18px;
38
+ width: 24px;
39
+ height: 24px;
40
+ text-align: center;
41
+ line-height: 24px;
42
+ border-radius: 50%;
43
+ cursor: pointer;
44
+ position: absolute;
45
+ top: 50%;
46
+ transform: translateY(-50%);
47
+ right: 1rem;
48
+ &:hover {
49
+ background-color: rgba(0,0,0,0.2);
50
+ }
51
+ }
52
+ `
53
+
54
+ const [useNotification, useNotificationRef,setNotification] = willUseSharedState()
55
+ export {useNotification, useNotificationRef, setNotification}
56
+ export const willSetNotification = (str) => () => setNotification(str)
57
+
58
+ const animation = oneWayAnimation('opacity:0; transform:translateY(-20px)', 'opacity:1;transform:translate(0)')
59
+
60
+ export const Notifications = () => {
61
+ const toggle = useToggle()
62
+ const [rawNotification,setNotification] = useNotification()
63
+
64
+ const {msg,type,timeout = 5000} = _.isObject(rawNotification) ? rawNotification : {type:"success",msg:rawNotification}
65
+
66
+ useEffect(() => {
67
+ if (msg) {
68
+ toggle.open()
69
+ setTimeout(toggle.willCloseAfter(timeout), 100)
70
+ }
71
+ },[msg])
72
+ useEffect(() => {
73
+ if (!toggle.isOpen) {
74
+ setNotification(null)
75
+ }
76
+ },[toggle.isOpen])
77
+
78
+ return toggle.isOpen && (<Fill name="Notifications">
79
+ <FlipNotification type={type}>
80
+ {callIfFunction(msg,toggle.close)}
81
+ <Icon icon="x" onClick={toggle.close}/>
82
+ </FlipNotification>
83
+ </Fill>)
84
+ }
85
+ const FlipNotification = (props) => {
86
+ const ref = useRef()
87
+ useFlipAnimation(ref,animation)
88
+ return <NotificationContainer ref={ref}><Notification id="js-share-notification" {...props}/></NotificationContainer>
89
+ }
@@ -0,0 +1,48 @@
1
+ import React, {useEffect} from 'react'
2
+ import styled, {css} from 'styled-components'
3
+ import {oneWayAnimation, getColor} from '@startlibs/utils'
4
+ import {useToggle} from '@startlibs/core'
5
+ import {useFlipLeave} from './Flip'
6
+ import {animationTiming} from '@startlibs/utils'
7
+
8
+ export const LoadingBar = styled.div`
9
+ width: 100%;
10
+ border-radius: 0 1px 1px 0;
11
+ height: 4px;
12
+ position: fixed;
13
+ background: ${props => props.bottom ? css`${getColor('main')}`:`#84D359`};
14
+ transform: translateX(-100%);
15
+ ${props => props.bottom ? "bottom:69px;" : "top: 0;"}
16
+ left: 0;
17
+ z-index: 99999;
18
+ box-shadow: ${props => props.bottom ? `0 0 1px 1px rgba(0,132,214,0.2);`:`0 0 2px 0 rgba(0,0,0,0.5);`};
19
+ ${props => props.loading && `
20
+ transform: translateX(-30%);
21
+ transition: transform 1.5s ease;
22
+ `}
23
+ ${props => props.closing && `
24
+ transform: translateX(0);
25
+ transition: transform 0.3s ease-in;
26
+ `}
27
+ `
28
+
29
+
30
+ export const PageLoader = ({isLoading}) => {
31
+ const loader = useToggle()
32
+ useEffect(() => {
33
+ if (isLoading) {
34
+ loader.open()
35
+ } else {
36
+ loader.closeAfter(500)
37
+ }
38
+ },[isLoading])
39
+
40
+ return <LoadingBar loading={loader.isOpen} closing={loader.closing}/>
41
+ }
42
+
43
+ const animation = oneWayAnimation('transform:translateX(-30%);','transform:translateX(0);')
44
+ export const SuspenseLoadingBar = () => {
45
+ const ref = React.useRef()
46
+ useFlipLeave(ref,animation,{reverse: false,timing:animationTiming('0.3s ease-in')})
47
+ return <LoadingBar ref={ref} loading/>
48
+ }
@@ -0,0 +1,182 @@
1
+ import React, {useRef} from 'react'
2
+ import _ from 'lodash/fp'
3
+ import styled from 'styled-components'
4
+ import {Fill, willUseSharedState} from '@startlibs/core'
5
+ import {media,getColor, callIfFunction, oneWayAnimation, printerMedia} from '@startlibs/utils'
6
+ import {useFlipAnimation} from './Flip'
7
+ import {Icon} from './Icon'
8
+ import {darken, transparentize } from 'polished';
9
+ import {Button} from './Button'
10
+
11
+ export const PersistentNotification = styled.div`
12
+ color: ${getColor('warning')};
13
+ h4 {
14
+ font-size: 15px;
15
+ font-weight: 500;
16
+ margin-bottom: 0.5rem;
17
+ }
18
+ p {
19
+ font-size: 13px;
20
+ margin-bottom: 0;
21
+ }
22
+ `
23
+
24
+ export const PersistentNotificationContent = styled.div`
25
+ display: flex;
26
+ align-items: center;
27
+ padding: 1.5rem;
28
+ `
29
+ export const PersistentNotificationIcon = styled(Icon)`
30
+ font-size: 38px;
31
+ width: 4rem;
32
+ height: 4rem;
33
+ line-height: 4rem;
34
+ border-radius: 100px;
35
+ text-align: center;
36
+ background: ${props => transparentize(0.88, getColor("warning")(props))};
37
+ margin-right: 1rem;
38
+ flex-shrink: 0;
39
+ ${props => props.icon === 'warning' && `font-size:30px;`}
40
+ `
41
+ export const PersistentNotificationActions = styled.div`
42
+ display: flex;
43
+ margin-top: -0.25rem;
44
+ padding: 0 1.5rem 1.5rem;
45
+ ${Button} {
46
+ flex-grow: 1;
47
+ }
48
+ ${Button} ~ ${Button} {
49
+ margin-left: 0;
50
+ margin-right: 1rem;
51
+ }
52
+ ${media.max(390)`
53
+ display: block;
54
+ ${Button} {
55
+ width: 100%;
56
+ }
57
+ ${Button} ~ ${Button} {
58
+ margin-left: 0;
59
+ margin-top: 0.75rem;
60
+ }
61
+ `}
62
+ `
63
+ export const PersistentNotificationCloseIcon = styled(Icon).attrs({icon: 'x'})`
64
+ position: absolute;
65
+ width: 30px;
66
+ height: 30px;
67
+ font-size: 20px;
68
+ line-height: 30px;
69
+ cursor: pointer;
70
+ border-radius: 50%;
71
+ text-align: center;
72
+ z-index: 1000;
73
+ top: 0.5rem;
74
+ right: 0.5rem;
75
+ color: ${props => props.type && props.type!== 'main' ? getColor(props.type)(props) : darken(0.1, getColor("main")(props))};
76
+ background: ${props => transparentize(0.90, getColor(props.type || "main")(props))};
77
+ :hover {
78
+ background: ${props => transparentize(0.85, getColor(props.type || "main")(props))};
79
+ }
80
+ :active {
81
+ background: ${props => transparentize(0.82, getColor(props.type || "main")(props))};
82
+ }
83
+ `
84
+
85
+
86
+ const [useNotifications, ,setNotifications] = willUseSharedState([])
87
+
88
+ export const openPersistentNotification = (notification) => {
89
+ const newNotification = {priority:0,type:"success",...(_.isObject(notification) ? notification : {content:notification})}
90
+
91
+ setNotifications(list => {
92
+ const nextIndex = list.findIndex(n => n.priority < newNotification.priority)
93
+ return nextIndex < 0
94
+ ? [...list,newNotification]
95
+ : [...list.slice(0,nextIndex),newNotification,...list.slice(nextIndex)]
96
+ })
97
+ return () => setNotifications(_.without([newNotification]))
98
+ }
99
+
100
+
101
+ const animation = oneWayAnimation('opacity:0; transform:translateY(-20px)', 'opacity:1;transform:translate(0)')
102
+
103
+ const NotificationContainer = styled.div `
104
+ position: fixed;
105
+ top: 0;
106
+ height: 0;
107
+ right: 0;
108
+ z-index:20;
109
+ `
110
+
111
+ const Notification = styled.div `
112
+ background: ${props => transparentize(0.9, getColor(props.type)(props))};
113
+ position: relative;
114
+ border-radius: 19px;
115
+ `
116
+ const NotificationWrapper = styled.div `
117
+ background: white;
118
+ margin-top: ${props => props.marginTop || "3.5rem"};
119
+ margin-right: 0.75rem;
120
+ margin-left: 0.75rem;
121
+ padding: 5px;
122
+ border-radius: 22px;
123
+ box-shadow: 0 0 10px 0 rgba(0,0,0,0.1), 0 1px 2px 1px rgba(0,0,0,0.15);
124
+ max-width: 364px;
125
+ ${media.mobile`
126
+ margin-top: ${props => props.marginTop || "4rem"};
127
+ `}
128
+ ${printerMedia`
129
+ display: none;
130
+ `}
131
+ `
132
+ const [useIsHiddenPersistentNotifications,,hidePersistentNotifications] = willUseSharedState(false)
133
+ export {useIsHiddenPersistentNotifications, hidePersistentNotifications}
134
+ export const PersistentNotifications = () => {
135
+ const [notifications,setNotifications] = useNotifications()
136
+ const [isHidden] = useIsHiddenPersistentNotifications()
137
+
138
+ if (!notifications.length || isHidden) {
139
+ return null;
140
+ }
141
+ const closeMessage = (e) => {
142
+ setNotifications((list) => list.slice(1))
143
+ }
144
+
145
+ const {content,type,marginTop,...notification} = notifications[0]
146
+
147
+ return <Fill name="PersistentNotifications">
148
+ <FlipNotification type={type} marginTop={marginTop}>
149
+ {content
150
+ ? callIfFunction(content,closeMessage)
151
+ : <DefaultNotification type={type} _onClose={closeMessage} {...notification} />
152
+ }
153
+ </FlipNotification>
154
+ </Fill>
155
+ }
156
+
157
+ const DefaultNotification = ({type, title,body,actions, icon, skipClose,_onClose,onClose}) => {
158
+ return <PersistentNotification>
159
+ <PersistentNotificationContent>
160
+ {(_.isString(icon) || !icon) ? <PersistentNotificationIcon icon={icon || type}/> : icon}
161
+ <div>
162
+ <h4>{title}</h4>
163
+ {body}
164
+ </div>
165
+ </PersistentNotificationContent>
166
+ {
167
+ actions &&
168
+ <PersistentNotificationActions>
169
+ {actions}
170
+ </PersistentNotificationActions>
171
+ }
172
+ {!skipClose && <PersistentNotificationCloseIcon type="warning" onClick={_.over([_onClose,onClose])}/>}
173
+ </PersistentNotification>
174
+ }
175
+
176
+ const FlipNotification = (props) => {
177
+ const ref = useRef()
178
+ useFlipAnimation(ref,animation)
179
+ return <NotificationContainer className="JS-fix-dialog-scroll" ref={ref}>
180
+ <NotificationWrapper marginTop={props.marginTop}><Notification id="js-share-notification" {...props}/></NotificationWrapper>
181
+ </NotificationContainer>
182
+ }
package/src/Popup.jsx ADDED
@@ -0,0 +1,49 @@
1
+ import React, {useMemo} from 'react'
2
+ import styled, {css} from 'styled-components'
3
+ import {Fill, useEnsureRef} from '@startlibs/core'
4
+ import {useFlipAnimation} from './Flip'
5
+ import {getDialogLayersSize} from './containers/DialogContainer'
6
+ import {usePositioning} from './containers/usePositioning'
7
+ import {twoWayAnimation, customTheme} from '@startlibs/utils'
8
+
9
+ const [fadeIn, fadeOut] = twoWayAnimation(`opacity:0;`, `opacity:1;`)
10
+
11
+ const PopupDiv = styled.div`
12
+ position: absolute;
13
+ background-color: #FFF;
14
+ box-shadow: 0 2px 2px 0 rgba(0,0,0,0.14), 0 3px 10px -2px rgba(0,0,0,0.15), 0 0px 2px 0px rgba(0,0,0,0.2);
15
+ border-radius: 8px;
16
+ padding: 1rem;
17
+ text-align: initial;
18
+ line-height: 140%;
19
+ font-size: 14px;
20
+ z-index: 999;
21
+ ${props => props.atTop && css`
22
+ margin-top: -0.25rem;
23
+ `}
24
+ ${props => props.atBottom && css`
25
+ margin-top: 0.25rem;
26
+ `}
27
+ ${customTheme('Popup')}
28
+ `
29
+
30
+ export const Popup = React.forwardRef(({slot = 'Popup',skipPortal, animation = fadeIn, leaveAnimation = true, animationOptions, arrowRef, className, onClick, children, ...rest},ref) => {
31
+ const popupRef = useEnsureRef(ref)
32
+ const [positionInfo, placeholder, arrow] = usePositioning(popupRef, rest, arrowRef)
33
+
34
+ // leaveAnimation=false → sem animação de saída (nenhum clone no unmount): útil quando
35
+ // o popup é portalado num slot persistente (ex: NavbarPopup) e o dono desmonta na
36
+ // navegação — senão o clone da saída fica órfão no slot. Default true = comportamento atual.
37
+ useFlipAnimation(popupRef, animation, leaveAnimation, animationOptions)
38
+ const slotName = useMemo(() => getDialogLayersSize() === 0 ? slot : (slot + '-' + (getDialogLayersSize() - 1)), [])
39
+ const popupContainer = <PopupDiv {...positionInfo} onClick={onClick} className={className} ref={popupRef}>
40
+ {arrow}
41
+ {children}
42
+ </PopupDiv>
43
+ return skipPortal ? popupContainer : <>
44
+ {placeholder}
45
+ <Fill name={slotName}>
46
+ {popupContainer}
47
+ </Fill>
48
+ </>
49
+ })
@@ -0,0 +1,63 @@
1
+ import React from 'react'
2
+
3
+ import {getMargin, media} from '@startlibs/utils'
4
+ import styled, {css} from 'styled-components'
5
+
6
+ const getMarginFromFieldChild = (props) => {
7
+ if (!props.last) return 0;
8
+ const fields = React.Children.toArray(props.children).filter(c => c.type.displayName === "Field" || c.type.displayName === "EnhancedInput")
9
+ if (!fields.length) return 0
10
+ const lastField = fields.slice(-1)[0]
11
+ return '-' + getMargin(props.theme.Field.margins)(lastField.props)
12
+ }
13
+
14
+
15
+ const splitColumnMargins = {small:0.5,'default':1, big:2}
16
+
17
+ export const SplitColumnsContainer = styled.div`
18
+
19
+ margin-bottom: ${getMarginFromFieldChild}rem;
20
+
21
+ ${props => css`
22
+ ${media.min(props.viewportMinWidth || 0)`
23
+ display: flex;
24
+ flex-wrap: nowrap;
25
+ margin-left: -${props=>getMargin(splitColumnMargins)(props)/2}rem;
26
+ margin-right: -${props=>getMargin(splitColumnMargins)(props)/2}rem;
27
+ margin-top: -${props=>getMargin(splitColumnMargins)(props)}rem;
28
+ > * {
29
+ flex-basis: 0;
30
+ flex-grow: 1;
31
+ margin-left: ${props=>getMargin(splitColumnMargins)(props)/2}rem;
32
+ margin-right: ${props=>getMargin(splitColumnMargins)(props)/2}rem;
33
+ margin-top: ${props=>getMargin(splitColumnMargins)(props)}rem;
34
+ }
35
+ `}
36
+ `}
37
+ ${props => props.twoColumnsBelowVW && css`
38
+ ${media.max(props.twoColumnsBelowVW)`
39
+ flex-wrap: wrap;
40
+ > * {
41
+ flex-basis: 47%;
42
+ }
43
+ `}
44
+ `}
45
+ `
46
+
47
+ export const fixDoubledLastMarginBottom = (children) => {
48
+ let last = React.Children.count(children)-1
49
+
50
+ function processChild(child, i) {
51
+ if (i === last && child && child.type && child.type.displayName === SplitColumnsContainer.displayName) {
52
+ return React.cloneElement(child, {last: true})
53
+ }
54
+ return child
55
+ }
56
+ if (!Array.isArray(children) || children.length===0) {
57
+ return processChild(children,0)
58
+ }
59
+ while ((!children[last] || (children[last] && children[last].type && children[last].type.displayName === "Errors")) && last > 0) {
60
+ last--
61
+ }
62
+ return React.Children.map(children,processChild)
63
+ }