@codeleap/web 3.18.3 → 3.18.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeleap/web",
3
- "version": "3.18.3",
3
+ "version": "3.18.6",
4
4
  "main": "src/index.ts",
5
5
  "repository": {
6
6
  "url": "https://github.com/codeleap-uk/internal-libs-monorepo.git",
@@ -41,7 +41,8 @@
41
41
  "react-window": "1.8.5",
42
42
  "slick-carousel": "^1.8.1",
43
43
  "url-parse": "^1.5.10",
44
- "uuid": "^8.3.2"
44
+ "uuid": "^8.3.2",
45
+ "zustand": "^4.4.1"
45
46
  },
46
47
  "peerDependencies": {
47
48
  "@codeleap/common": "*",
@@ -36,7 +36,6 @@ export type DrawerProps = {
36
36
  style?: React.CSSProperties
37
37
  animationDuration?: string
38
38
  closeButtonProps?: Partial<ActionIconProps>
39
- scrollLocked?: boolean
40
39
  closeIcon?: IconPlaceholder
41
40
  children?: React.ReactNode
42
41
  } & ComponentVariants<typeof DrawerPresets> & ComponentCommonProps
@@ -68,7 +67,6 @@ const defaultProps: Partial<DrawerProps> = {
68
67
  darkenBackground: true,
69
68
  size: '75vw',
70
69
  title: null,
71
- scrollLocked: false,
72
70
  closeIcon: 'x' as IconPlaceholder,
73
71
  }
74
72
 
@@ -95,11 +93,10 @@ export const Drawer = (props: DrawerProps) => {
95
93
  style,
96
94
  animationDuration,
97
95
  debugName,
98
- scrollLocked,
99
96
  closeIcon,
100
97
  } = allProps as DrawerProps
101
98
 
102
- usePopState(open, toggle, scrollLocked)
99
+ usePopState(open, toggle)
103
100
 
104
101
  const [hiddenStyle, axis, positioning] = resolveHiddenDrawerPosition(position)
105
102
 
@@ -134,7 +131,6 @@ export const Drawer = (props: DrawerProps) => {
134
131
  visible={open}
135
132
  css={variantStyles.overlay}
136
133
  onPress={toggle}
137
- scrollLocked={scrollLocked}
138
134
  />
139
135
  )}
140
136
 
@@ -25,17 +25,18 @@ import { ActionIcon, ActionIconProps } from '../ActionIcon'
25
25
  import { Scroll } from '../Scroll'
26
26
  import { ComponentCommonProps } from '../../types'
27
27
  import { Touchable, TouchableProps } from '../Touchable'
28
+ import { modalScrollLock, ModalStore } from '../../lib/modal'
28
29
 
29
30
  export * from './styles'
30
31
 
31
32
  export type ModalProps =
32
33
  {
33
- visible: boolean
34
+ visible?: boolean
34
35
  children?: React.ReactNode
35
36
  title?: React.ReactNode | string
36
37
  description?: React.ReactNode | string
37
38
  renderModalBody?: (props: ModalBodyProps) => React.ReactElement
38
- toggle: AnyFunction
39
+ toggle?: AnyFunction
39
40
  styles?: StylesOf<ModalComposition>
40
41
  style?: React.CSSProperties
41
42
  accessible?: boolean
@@ -56,9 +57,11 @@ export type ModalProps =
56
57
  overlayProps?: Partial<OverlayProps>
57
58
  zIndex?: number
58
59
  withScrollContainer?: boolean
59
- scrollLocked?: boolean
60
+ scrollLock?: boolean
60
61
  backdropProps?: Partial<TouchableProps>
61
62
  alterHistory?: boolean
63
+ modalId?: string
64
+ autoIndex?: boolean
62
65
  } & ComponentVariants<typeof ModalPresets> & ComponentCommonProps
63
66
 
64
67
  function focusModal(event: FocusEvent, id: string) {
@@ -148,7 +151,9 @@ const defaultProps: Partial<ModalProps> = {
148
151
  zIndex: null,
149
152
  description: null,
150
153
  withScrollContainer: false,
151
- scrollLocked: true,
154
+ scrollLock: false,
155
+ autoIndex: false,
156
+ alterHistory: false,
152
157
  }
153
158
 
154
159
  export const ModalContent = (
@@ -178,12 +183,15 @@ export const ModalContent = (
178
183
  zIndex,
179
184
  withScrollContainer,
180
185
  debugName,
181
- scrollLocked,
182
186
  backdropProps = {},
183
- alterHistory = false,
187
+ alterHistory,
188
+ id: modalId,
189
+ autoIndex,
184
190
  ...props
185
191
  } = modalProps
186
192
 
193
+ const index = ModalStore(store => (store.indexes?.[modalId] ?? 0))
194
+
187
195
  const id = useId()
188
196
  const modalRef = useRef(null)
189
197
  const variantStyles = useDefaultComponentStyle<'u:Modal', typeof ModalPresets>('u:Modal', {
@@ -264,10 +272,11 @@ export const ModalContent = (
264
272
  aria-hidden={!visible}
265
273
  css={[
266
274
  variantStyles.wrapper,
267
- _zIndex,
268
275
  visible
269
276
  ? variantStyles['wrapper:visible']
270
277
  : variantStyles['wrapper:hidden'],
278
+ autoIndex ? { zIndex: index } : {},
279
+ _zIndex,
271
280
  ]}
272
281
  >
273
282
  <Overlay
@@ -279,7 +288,6 @@ export const ModalContent = (
279
288
  ? variantStyles['backdrop:visible']
280
289
  : variantStyles['backdrop:hidden'],
281
290
  ]}
282
- scrollLocked={scrollLocked}
283
291
  {...overlayProps}
284
292
  />
285
293
 
@@ -343,18 +351,24 @@ export const Modal = (props) => {
343
351
 
344
352
  const {
345
353
  accessible,
346
- keepMounted,
347
- visible,
354
+ visible: _visible,
355
+ scrollLock,
356
+ modalId: _modalId,
357
+ autoIndex,
358
+ toggle: _toggle,
348
359
  } = allProps
349
360
 
350
- const modalId = useRef(v4())
361
+ const modalId = useRef(_modalId ?? v4())
362
+ const setIndex = ModalStore(store => store.setIndex)
363
+
364
+ const visible = TypeGuards.isBoolean(_visible) ? _visible : ModalStore(store => (store.modals?.[_modalId] ?? false))
365
+ const toggle = TypeGuards.isFunction(_toggle) ? _toggle : ModalStore(store => () => store.toggle(_modalId))
351
366
 
352
367
  onMount(() => {
353
368
  if (accessible) {
354
- const currentId = modalId
355
369
  const appRoot = document.body
356
- appRoot.addEventListener('focusin', (e) => focusModal(e, currentId))
357
- return () => appRoot.removeEventListener('focusin', (e) => focusModal(e, currentId))
370
+ appRoot.addEventListener('focusin', (e) => focusModal(e, modalId.current))
371
+ return () => appRoot.removeEventListener('focusin', (e) => focusModal(e, modalId.current))
358
372
  }
359
373
  })
360
374
 
@@ -365,16 +379,16 @@ export const Modal = (props) => {
365
379
  appRoot.setAttribute('tabindex', `${-1}`)
366
380
  }
367
381
 
368
- if (visible) {
369
- document.body.style.overflowY = 'hidden'
370
- document.body.style.overflowX = 'hidden'
371
- } else {
372
- document.body.style.overflowY = 'visible'
373
- document.body.style.overflowX = 'hidden'
382
+ if (scrollLock) modalScrollLock(visible, modalId.current)
383
+
384
+ if (autoIndex) {
385
+ setTimeout(() => {
386
+ setIndex(visible, modalId.current)
387
+ }, visible ? 0 : 500)
374
388
  }
375
389
  }, [visible])
376
390
 
377
- const content = <ModalContent {...props} id={modalId.current} />
391
+ const content = <ModalContent {...props} visible={visible} toggle={toggle} id={modalId.current} />
378
392
 
379
393
  // if (renderStatus === 'unmounted') return null
380
394
 
@@ -388,4 +402,3 @@ export const Modal = (props) => {
388
402
  }
389
403
 
390
404
  Modal.defaultProps = defaultProps
391
-
@@ -14,7 +14,6 @@ export type OverlayProps<T extends NativeHTMLElement = 'div'> = {
14
14
  visible?: boolean
15
15
  styles?: StylesOf<OverlayComposition>
16
16
  onPress?: TouchableProps<'div'>['onClick']
17
- scrollLocked?: boolean
18
17
  } & ComponentVariants<typeof OverlayPresets> & Omit<ViewProps<T>, 'variants' | 'responsiveVariants'>
19
18
 
20
19
  export * from './styles'
@@ -25,7 +24,6 @@ export const Overlay = <T extends NativeHTMLElement>(overlayProps:OverlayProps<T
25
24
  responsiveVariants,
26
25
  variants,
27
26
  styles,
28
- scrollLocked = false,
29
27
  ...props
30
28
  } = overlayProps
31
29
 
@@ -35,7 +33,7 @@ export const Overlay = <T extends NativeHTMLElement>(overlayProps:OverlayProps<T
35
33
  styles,
36
34
  })
37
35
 
38
- usePopState(visible, props.onPress, scrollLocked)
36
+ usePopState(visible, props.onPress)
39
37
 
40
38
  const Component = props.onClick || props.onPress ? Touchable : View
41
39
 
package/src/index.ts CHANGED
@@ -10,3 +10,4 @@ export { CreateOSAlert, useGlobalAlertComponent, AlertOutlet } from './lib/OSAle
10
10
  export type { GlobalAlertComponentProps, GlobalAlertType } from './lib/OSAlert'
11
11
  export * from './lib/keyboard'
12
12
  export * from './lib/localStorage'
13
+ export * from './lib/modal'
@@ -0,0 +1,80 @@
1
+ import { TypeGuards } from '@codeleap/common'
2
+ import { create } from 'zustand'
3
+
4
+ type TModalStore = {
5
+ identifier: string | null
6
+ indexes: Record<string, number>
7
+ modals: Record<string, boolean>
8
+ setIndex: (visible: boolean, modalIdentifier: string) => void
9
+ toggle: (id: string, to?: boolean) => void
10
+ }
11
+
12
+ const INDEX = 99
13
+
14
+ export const ModalStore = create<TModalStore>((set) => ({
15
+ identifier: null,
16
+ indexes: {},
17
+ modals: {},
18
+ setIndex: (visible: boolean, modalIdentifier: string) => set(store => {
19
+ const indexes = store.indexes
20
+
21
+ if (!visible) {
22
+ indexes[modalIdentifier] = INDEX
23
+
24
+ return { indexes }
25
+ }
26
+
27
+ let currentIndex = 0
28
+
29
+ for (const key in indexes) {
30
+ if (indexes[key] > currentIndex) {
31
+ currentIndex = indexes[key]
32
+ }
33
+ }
34
+
35
+ indexes[modalIdentifier] = currentIndex + INDEX
36
+
37
+ return { indexes }
38
+ }),
39
+ toggle: (id: string, to = null) => set(store => {
40
+ const modals = store.modals
41
+
42
+ const hasModal = TypeGuards.isBoolean(modals?.[id])
43
+ const visible = TypeGuards.isBoolean(to) ? to : (hasModal ? !modals?.[id] : true)
44
+
45
+ modals[id] = visible
46
+
47
+ return { modals }
48
+ })
49
+ }))
50
+
51
+ export function toggleModal(id: string, to: boolean = null) {
52
+ ModalStore.getState().toggle(id, to)
53
+ }
54
+
55
+ export function modalScrollLock(to: boolean, modalIdentifier: string) {
56
+ let modalId = ModalStore.getState().identifier
57
+
58
+ const alreadyDifferentOpenedModal = !TypeGuards.isNil(modalId) && modalIdentifier !== modalId
59
+
60
+ if (alreadyDifferentOpenedModal) return
61
+
62
+ if (TypeGuards.isNil(modalId) && to === true) {
63
+ ModalStore.setState({ identifier: modalIdentifier })
64
+ modalId = modalIdentifier
65
+ } else if (!TypeGuards.isNil(modalId) && to === false) {
66
+ ModalStore.setState({ identifier: null })
67
+ }
68
+
69
+ const htmlStyle = document?.documentElement?.style
70
+
71
+ if (htmlStyle.overflowX !== 'hidden') {
72
+ htmlStyle.overflowX = 'hidden'
73
+ }
74
+
75
+ if (to) {
76
+ htmlStyle.overflowY = 'hidden'
77
+ } else if (modalIdentifier === modalId && to === false) {
78
+ htmlStyle.overflowY = 'auto'
79
+ }
80
+ }
@@ -1,6 +1,6 @@
1
1
  import { AnyFunction, useIsomorphicEffect, useUnmount } from '@codeleap/common'
2
2
 
3
- export const usePopState = (dependence: boolean, handler: AnyFunction, scrollLocked = true) => {
3
+ export const usePopState = (dependence: boolean, handler: AnyFunction) => {
4
4
  useIsomorphicEffect(() => {
5
5
  if (dependence) {
6
6
  window.history.pushState(null, null, window.location.pathname)