@codeleap/web 4.0.1 → 4.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeleap/web",
3
- "version": "4.0.1",
3
+ "version": "4.1.0",
4
4
  "main": "src/index.ts",
5
5
  "repository": {
6
6
  "url": "https://github.com/codeleap-uk/internal-libs-monorepo.git",
@@ -20,7 +20,7 @@
20
20
  "lint": "eslint -c .eslintrc.js --fix \"./src/**/*.{ts,tsx,js,jsx}\""
21
21
  },
22
22
  "dependencies": {
23
- "@radix-ui/react-progress": "^1.0.3",
23
+ "@radix-ui/react-progress": "1.1.0",
24
24
  "@radix-ui/react-slider": "1.1.1",
25
25
  "@radix-ui/react-tooltip": "^1.0.6",
26
26
  "@tiptap/react": "2.1.16",
@@ -33,7 +33,7 @@
33
33
  "react-dropzone": "^14.2.3",
34
34
  "react-image-crop": "^10.1.8",
35
35
  "react-input-mask": "^2.0.4",
36
- "react-number-format": "^5.2.1",
36
+ "react-number-format": "^5.4.2",
37
37
  "react-select": "^5.7.3",
38
38
  "react-slick": "^0.29.0",
39
39
  "slick-carousel": "^1.8.1",
@@ -26,10 +26,9 @@ export const Collapse = (props: CollapseProps) => {
26
26
  animate={{ height: open ? 'auto' : 0, opacity: open ? 1 : 0 }}
27
27
  transition={{ duration: 0.2 }}
28
28
  {...rest}
29
- style={{
30
- ...styles.wrapper,
31
- ...(open ? styles?.['wrapper:open'] : styles?.['wrapper:closed']),
32
- }}
29
+ style={open ? styles?.['wrapper:open'] : styles?.['wrapper:closed']}
30
+ // @ts-expect-error css type
31
+ css={[styles.wrapper, open ? styles?.['wrapper:open'] : styles?.['wrapper:closed']]}
33
32
  >
34
33
  {children}
35
34
  </motion.div>
@@ -27,6 +27,7 @@ export const EmptyPlaceholder = (props: EmptyPlaceholderProps) => {
27
27
  imageWrapperProps,
28
28
  indicatorProps,
29
29
  debugName,
30
+ ImageComponent,
30
31
  } = {
31
32
  ...EmptyPlaceholder.defaultProps,
32
33
  ...props,
@@ -39,11 +40,14 @@ export const EmptyPlaceholder = (props: EmptyPlaceholderProps) => {
39
40
  const activityIndicatorStyles = useNestedStylesByKey('loader', styles)
40
41
 
41
42
  const _Image = React.useMemo(() => {
42
- if (TypeGuards.isString(image)) {
43
- return <img
43
+
44
+ if (!TypeGuards.isNil(image)) {
45
+
46
+ return <ImageComponent
44
47
  {...imageProps}
45
- src={image as HTMLImageElement['src']}
46
- // @ts-expect-error
48
+ // @ts-ignore
49
+ source={image as HTMLImageElement['src']}
50
+
47
51
  css={styles.image}
48
52
  />
49
53
  }
@@ -102,7 +106,7 @@ export const EmptyPlaceholder = (props: EmptyPlaceholderProps) => {
102
106
  EmptyPlaceholder.styleRegistryName = 'EmptyPlaceholder'
103
107
  EmptyPlaceholder.elements = ['wrapper', 'loader', 'title', 'description', 'image', 'imageWrapper', 'icon']
104
108
  EmptyPlaceholder.rootElement = 'wrapper'
105
-
109
+ EmptyPlaceholder.ImageComponent = 'img'
106
110
  EmptyPlaceholder.withVariantTypes = <S extends AnyRecord>(styles: S) => {
107
111
  return EmptyPlaceholder as (props: StyledComponentProps<EmptyPlaceholderProps, typeof styles>) => IJSX
108
112
  }
@@ -29,4 +29,5 @@ export type EmptyPlaceholderProps =
29
29
  wrapperProps?: ViewProps
30
30
  imageWrapperProps?: ViewProps
31
31
  indicatorProps?: Partial<ActivityIndicatorProps>
32
+ ImageComponent?: React.ComponentType
32
33
  }
@@ -56,6 +56,7 @@ export const ListLayout = (props: ListLayoutProps) => {
56
56
  ListLoadingIndicatorComponent,
57
57
  scrollableRef,
58
58
  showFooter = true,
59
+ wrapperProps,
59
60
  } = props
60
61
 
61
62
  const getKeyStyle = (key: ListParts) => mergeStyles([
@@ -67,7 +68,7 @@ export const ListLayout = (props: ListLayoutProps) => {
67
68
  const showIndicator = (isFetching || isFetchingNextPage) && !TypeGuards.isNil(ListLoadingIndicatorComponent)
68
69
 
69
70
  return (
70
- <View style={getKeyStyle('wrapper')} ref={scrollableRef}>
71
+ <View style={getKeyStyle('wrapper')} ref={scrollableRef} {...wrapperProps}>
71
72
  {!!ListHeaderComponent ? <ListHeaderComponent /> : null}
72
73
 
73
74
  {isEmpty ? <ListEmptyComponent debugName={debugName} {...placeholder} /> : null}
@@ -33,6 +33,7 @@ export function List(props: ListProps) {
33
33
  reloadTimeout,
34
34
  showFooter,
35
35
  style,
36
+ layoutWrapperProps,
36
37
  } = allProps
37
38
 
38
39
  const styles = useStylesFor(List.styleRegistryName, style)
@@ -75,6 +76,7 @@ export function List(props: ListProps) {
75
76
  <ListLayout
76
77
  {...allProps}
77
78
  {...layoutProps}
79
+ wrapperProps={layoutWrapperProps}
78
80
  styles={styles}
79
81
  showFooter={reloadingLayout ? false : showFooter}
80
82
  >
@@ -12,6 +12,7 @@ export type ListLayoutProps = Omit<ListProps, 'renderItem'> & UseInfiniteScrollR
12
12
  styles: StylesOf<ListComposition>
13
13
  children?: React.ReactNode
14
14
  showFooter?: boolean
15
+ wrapperProps?: Partial<ViewProps>
15
16
  }
16
17
 
17
18
  export type ListRefreshControlComponent = Partial<ListLayoutProps> & {
@@ -64,4 +65,5 @@ export type ListProps<T extends ListItem = ListItem> =
64
65
  masonryProps?: Partial<ListMasonryProps<T>>
65
66
  reloadTimeout?: number
66
67
  showFooter?: boolean
68
+ layoutWrapperProps?: Partial<ViewProps>
67
69
  }
@@ -29,7 +29,9 @@ const DefaultOption = (props: TCustomOption & { component: (props: TCustomOption
29
29
  debugName,
30
30
  } = props
31
31
 
32
- const styles = optionsStyles({ isSelected, isFocused, baseStyles: (itemProps?.style ?? {}) })
32
+ const optionProps = props?.data?.itemProps
33
+
34
+ const styles = optionsStyles({ isSelected, isFocused, baseStyles: (optionProps?.style ?? itemProps?.style ?? {}) })
33
35
 
34
36
  let _Component = null
35
37
 
@@ -41,6 +43,7 @@ const DefaultOption = (props: TCustomOption & { component: (props: TCustomOption
41
43
  rightIcon={isSelected && selectedIcon}
42
44
  debugName={debugName}
43
45
  {...itemProps}
46
+ {...optionProps}
44
47
  style={styles}
45
48
  />
46
49
  )
@@ -162,7 +165,7 @@ const defaultFormatPlaceholderNoItems = (props: PlaceholderProps & { text: strin
162
165
  }
163
166
 
164
167
  export const Select = forwardRef<HTMLInputElement, SelectProps>(
165
- <T extends string | number = string, Multi extends boolean = false>
168
+ <T extends string | number = string, Multi extends boolean = false>
166
169
  (props: SelectProps<T, Multi>, inputRef: React.ForwardedRef<HTMLInputElement>) => {
167
170
 
168
171
  type Option = FormTypes.Option<T>
@@ -221,7 +224,7 @@ export const Select = forwardRef<HTMLInputElement, SelectProps>(
221
224
 
222
225
  const _innerInputRef = useRef<any>(null)
223
226
  const innerInputRef = selectRef || _innerInputRef
224
- const innerWrapperRef = useRef(null)
227
+ const innerWrapperRef = useRef<HTMLDivElement>(null)
225
228
 
226
229
  const hasSelectedOptionState = !TypeGuards.isNil(_selectedOption) && TypeGuards.isFunction(_setSelectedOption)
227
230
 
@@ -259,11 +262,12 @@ export const Select = forwardRef<HTMLInputElement, SelectProps>(
259
262
  loadingStyles,
260
263
  inputMultiValueStyles,
261
264
  menuWrapperStyles,
262
- // @ts-expect-error @verify
265
+ // @ts-expect-error
263
266
  } = useSelectStyles({ ...props, styleRegistryName: Select.styleRegistryName }, {
264
267
  error: !!hasError,
265
268
  focused: isFocused,
266
269
  disabled: isDisabled,
270
+ parentWidth: innerWrapperRef?.current?.clientWidth,
267
271
  })
268
272
 
269
273
  useImperativeHandle(inputRef, () => {
@@ -410,6 +414,7 @@ export const Select = forwardRef<HTMLInputElement, SelectProps>(
410
414
  tabSelectsValue={false}
411
415
  tabIndex={0}
412
416
  backspaceRemovesValue={true}
417
+ menuPortalTarget={typeof document === 'undefined' ? undefined : document.body}
413
418
  {...otherProps}
414
419
  {..._props}
415
420
  onKeyDown={isFocused ? handleKeyDown : null}
@@ -424,7 +429,6 @@ export const Select = forwardRef<HTMLInputElement, SelectProps>(
424
429
  defaultOptions={loadOptionsOnMount}
425
430
  ref={innerInputRef}
426
431
  closeMenuOnSelect={closeOnSelect}
427
- menuPortalTarget={innerWrapperRef.current}
428
432
  placeholder={(loadOptionsOnMount && !loadedOptions) ? loadingMessage : placeholder}
429
433
  isDisabled={isDisabled}
430
434
  isClearable={clearable}
@@ -467,7 +471,7 @@ export const Select = forwardRef<HTMLInputElement, SelectProps>(
467
471
  />
468
472
  </InputBase>
469
473
  )
470
- }) as StyledComponentWithProps<SelectProps>
474
+ }) as StyledComponentWithProps<SelectProps>
471
475
 
472
476
  Select.styleRegistryName = 'Select'
473
477
 
@@ -49,6 +49,7 @@ export type ComponentState = {
49
49
  error?: boolean
50
50
  focused?: boolean
51
51
  disabled?: boolean
52
+ parentWidth?: number
52
53
  }
53
54
 
54
55
  export type OptionState = {
@@ -60,7 +61,7 @@ export type OptionState = {
60
61
  export function useSelectStyles<T, Multi extends boolean>(props: UseSelectStylesProps, state: ComponentState) {
61
62
  const { style } = props
62
63
 
63
- const { error, focused, disabled } = state
64
+ const { error, focused, disabled, parentWidth } = state
64
65
 
65
66
  const styles = useStylesFor(props?.styleRegistryName, style)
66
67
 
@@ -121,10 +122,18 @@ export function useSelectStyles<T, Multi extends boolean>(props: UseSelectStyles
121
122
  wrapper: stylesKey('itemsWrapper'),
122
123
  }
123
124
 
125
+ const parseMenuPortalStyles = (baseStyles: CSSObjectWithLabel) => {
126
+ return {
127
+ ...baseStyles,
128
+ width: parentWidth,
129
+ left: `calc(${baseStyles.left}px - ((${parentWidth}px - ${baseStyles.width}px) / 2))`
130
+ }
131
+ }
132
+
124
133
  const reactSelectStyles: StylesConfig<FormTypes.Option<T>, Multi, GroupBase<FormTypes.Option<T>>> = {
125
134
  container: (baseStyles) => stylesKey('inputContainer', baseStyles),
126
135
  control: () => stylesKey('inputContainer'),
127
- menuPortal: (baseStyles) => stylesKey('listPortal', baseStyles),
136
+ menuPortal: (baseStyles) => stylesKey('listPortal', parseMenuPortalStyles(baseStyles)),
128
137
  menu: (baseStyles) => stylesKey('listWrapper', baseStyles),
129
138
  menuList: (baseStyles) => stylesKey('list', baseStyles),
130
139
  group: () => ({}),
@@ -27,7 +27,7 @@ type DynamicSelectProps<T, Multi extends boolean> =
27
27
  >)
28
28
 
29
29
  export type ReactSelectProps<T, Multi extends boolean = false> = Omit<InputBaseProps, 'style'> & {
30
- options: FormTypes.Options<T>
30
+ options: FormTypes.Options<T> & { itemProps?: ButtonProps }
31
31
  value: SelectValue<T, Multi>
32
32
  onValueChange?: (value: SelectValue<T, Multi>) => void
33
33
  multiple?: Multi
@@ -45,6 +45,7 @@ export type ComponentPartProps = {
45
45
  export type TCustomOption = OptionProps & ComponentPartProps & ComponentCommonProps & {
46
46
  optionsStyles: (state: OptionState) => OptionState['baseStyles']
47
47
  selectedIcon?: string
48
+ data: OptionProps['data'] & { itemProps?: ButtonProps}
48
49
  itemProps?: ButtonProps
49
50
  styles?: OptionState['baseStyles']
50
51
  }
@@ -16,6 +16,8 @@ export type UseCropPickerProps = Partial<ReactCropProps> & {
16
16
  ref: React.MutableRefObject<FileInputRef> | React.Ref<FileInputRef>
17
17
  }
18
18
 
19
+ type ImageType = 'png' | 'jpeg' | 'webp'
20
+
19
21
  export function readImage(file: File | Blob): Promise<ImageReading> {
20
22
  const reader = new FileReader()
21
23
  return new Promise<ImageReading>((resolve) => {
@@ -28,7 +30,7 @@ export function readImage(file: File | Blob): Promise<ImageReading> {
28
30
  })
29
31
  }
30
32
 
31
- export function cropImage(image: ImageReading, crop: Crop): Promise<[string, Blob]> {
33
+ export function cropImage(image: ImageReading, crop: Crop, type: ImageType): Promise<[string, Blob]> {
32
34
  const canvas = document.createElement('canvas')
33
35
  const ctx = canvas.getContext('2d', { alpha: true })
34
36
 
@@ -61,7 +63,7 @@ export function cropImage(image: ImageReading, crop: Crop): Promise<[string, Blo
61
63
  readImage(blob).then(cropped => {
62
64
  resolve([cropped.src, blob])
63
65
  }).catch(reject)
64
- }, 'image/png')
66
+ }, `image/${type}`)
65
67
  })
66
68
  }
67
69
 
@@ -93,9 +95,9 @@ export function useCropPicker({
93
95
  setTimeout(() => setImage(null), 500)
94
96
  }
95
97
 
96
- const onConfirmCrop = async () => {
98
+ const onConfirmCrop = async (imageType: ImageType = 'jpeg') => {
97
99
  setIsLoading(true)
98
- const [preview, croppedFile] = await cropImage(image, relativeCrop)
100
+ const [preview, croppedFile] = await cropImage(image, relativeCrop, imageType)
99
101
  onResolved([
100
102
  {
101
103
  file: new File([croppedFile], 'cropped.jpg'),
@@ -5,7 +5,7 @@ const IS_SSR = typeof window === 'undefined' || typeof history === 'undefined'
5
5
 
6
6
  const defaultConfig: Partial<Config> = {
7
7
  historyEnabled: false,
8
- getMetadata: () => {}
8
+ getMetadata: () => {},
9
9
  }
10
10
 
11
11
  export type {
@@ -13,7 +13,7 @@ export type {
13
13
  Routes,
14
14
  }
15
15
 
16
- export class Navigation<O extends object, R extends object = {}> {
16
+ export class Navigation<O extends object, R extends object = {}, C extends Record<string, any> = {}> {
17
17
  private _history: History = {}
18
18
 
19
19
  private config: Config = defaultConfig
@@ -22,6 +22,8 @@ export class Navigation<O extends object, R extends object = {}> {
22
22
  return this._history
23
23
  }
24
24
 
25
+ public context: C = {} as C
26
+
25
27
  private putHistory(path: RoutePath, info: any = {}) {
26
28
  const idx = Object.keys(this._history).length + 1
27
29
 
@@ -34,7 +36,7 @@ export class Navigation<O extends object, R extends object = {}> {
34
36
  path,
35
37
  metadata: this.config?.getMetadata?.(),
36
38
  info,
37
- }
39
+ },
38
40
  }
39
41
 
40
42
  this._history = this.merge(this._history, value)
@@ -53,7 +55,7 @@ export class Navigation<O extends object, R extends object = {}> {
53
55
 
54
56
  constructor(
55
57
  routes: R,
56
- navigator: Navigator<O>,
58
+ navigator: Navigator<O, C>,
57
59
  config: Config = {},
58
60
  ) {
59
61
  this.navigator = navigator
@@ -69,15 +71,15 @@ export class Navigation<O extends object, R extends object = {}> {
69
71
  * @returns Is on the route - boolean
70
72
  */
71
73
  public isCurrentRoute<T extends keyof Routes<R>>(
72
- route: T,
74
+ route: T,
73
75
  // @ts-expect-error
74
76
  routeParams: Record<Routes<R>[T], string|number> = {} as any,
75
- exact: boolean = false,
77
+ exact = false,
76
78
  ) {
77
79
  if (IS_SSR) return false
78
-
80
+
79
81
  let path = window?.location?.pathname
80
-
82
+
81
83
  // @ts-ignore
82
84
  const routePath = this.getPathWithParams(route, routeParams)
83
85
 
@@ -99,7 +101,7 @@ export class Navigation<O extends object, R extends object = {}> {
99
101
  if (path?.includes(routePath)) {
100
102
  return true
101
103
  }
102
-
104
+
103
105
  return false
104
106
  }
105
107
 
@@ -134,9 +136,9 @@ export class Navigation<O extends object, R extends object = {}> {
134
136
  * @returns Path - string
135
137
  */
136
138
  public getPathWithParams<T extends keyof Routes<R>>(
137
- route: T,
139
+ route: T,
138
140
  // @ts-expect-error
139
- routeParams: Record<Routes<R>[T], string|number> = {} as any
141
+ routeParams: Record<Routes<R>[T], string|number> = {} as any,
140
142
  ) {
141
143
  let path = this.getPath(route)
142
144
 
@@ -162,7 +164,7 @@ export class Navigation<O extends object, R extends object = {}> {
162
164
  }
163
165
 
164
166
  /**
165
- * Function to navigate to the previous page, if history is enabled, the penultimate record will be used,
167
+ * Function to navigate to the previous page, if history is enabled, the penultimate record will be used,
166
168
  * otherwise the browser's own api with "history.back()"
167
169
  */
168
170
  public goBack() {
@@ -182,7 +184,7 @@ export class Navigation<O extends object, R extends object = {}> {
182
184
  }
183
185
 
184
186
  const info = this.merge(historyData?.info, {
185
- 'action': 'goBack'
187
+ 'action': 'goBack',
186
188
  })
187
189
 
188
190
  this.to(historyData?.path, historyData?.info?.options ?? {}, info)
@@ -194,9 +196,9 @@ export class Navigation<O extends object, R extends object = {}> {
194
196
  * @param options Route parameters (marked by {{}}), which will be automatically shown if they exist, and navigator options and route queryParams can also be passed through the "params" property
195
197
  */
196
198
  public navigate<T extends keyof Routes<R>>(
197
- route: T,
199
+ route: T,
198
200
  // @ts-expect-error
199
- options: Record<Routes<R>[T], string|number> & O & { params?: RouteParams } = {} as any
201
+ options: Record<Routes<R>[T], string|number> & O & { params?: RouteParams } = {} as any,
200
202
  ) {
201
203
  // @ts-ignore
202
204
  let path = this.getPath(route)
@@ -229,15 +231,15 @@ export class Navigation<O extends object, R extends object = {}> {
229
231
 
230
232
  if (typeof params === 'object') {
231
233
  let searchParams = null
232
-
234
+
233
235
  for (const paramKey in (params ?? {})) {
234
236
  const value = params?.[paramKey]
235
237
  const param = `${paramKey}=${encodeURIComponent(value)}`
236
238
  const separator = searchParams == null ? '' : '&'
237
-
239
+
238
240
  searchParams = `${searchParams ?? ''}${separator}${param}`
239
241
  }
240
-
242
+
241
243
  if (typeof searchParams === 'string') {
242
244
  if (path?.endsWith('/')) {
243
245
  path = path.slice(0, -1)
@@ -268,7 +270,7 @@ export class Navigation<O extends object, R extends object = {}> {
268
270
  this.putHistory(path, this.merge(options, info))
269
271
  }
270
272
 
271
- this.navigator(path, options)
273
+ this.navigator(path, options, this.context)
272
274
  }
273
275
 
274
276
  /**
@@ -1,7 +1,6 @@
1
-
2
1
  export type ExtractParams<T extends string> =
3
- T extends `${infer _Start}{{${infer Param}}}${infer Rest}`
4
- ? Param | ExtractParams<Rest>
2
+ T extends `${infer _Start}{{${infer Param}}}${infer Rest}`
3
+ ? Param | ExtractParams<Rest>
5
4
  : never
6
5
 
7
6
  type Params<T> = {
@@ -10,8 +9,8 @@ type Params<T> = {
10
9
 
11
10
  // @ts-ignore
12
11
  type ExtractRoutes<T, PM, Prefix extends string = ''> = {
13
- [K in keyof T & string]:
14
- T[K] extends string
12
+ [K in keyof T & string]:
13
+ T[K] extends string
15
14
  ? {
16
15
  // @ts-ignore
17
16
  [P in `${Prefix}${Prefix extends '' ? '' : '.'}${K}`]: PM[K]
@@ -19,7 +18,7 @@ type ExtractRoutes<T, PM, Prefix extends string = ''> = {
19
18
  : ExtractRoutes<T[K], PM[K], `${Prefix}${Prefix extends '' ? '' : '.'}${K}`>
20
19
  }[keyof T]
21
20
 
22
- type UnionToIntersection<U> =
21
+ type UnionToIntersection<U> =
23
22
  (U extends any ? (x: U)=> void : never) extends ((x: infer I)=>void) ? I : never
24
23
 
25
24
  export type Routes<T> = UnionToIntersection<ExtractRoutes<T, Params<T>>>
@@ -32,7 +31,7 @@ export type RouteParams = {
32
31
  [x: string]: string
33
32
  }
34
33
 
35
- export type Navigator<O extends object = {}> = (path: RoutePath, options: O) => void
34
+ export type Navigator<O extends object = {}, C extends Record<string, any> = {}> = (path: RoutePath, options: O, context?: C) => void
36
35
 
37
36
  export type AnyValue = {
38
37
  [key: string]: any
@@ -40,8 +39,8 @@ export type AnyValue = {
40
39
 
41
40
  export type HistoryData = {
42
41
  origin: string
43
- date: Date,
44
- path: RoutePath,
42
+ date: Date
43
+ path: RoutePath
45
44
  metadata: any
46
45
  info: any
47
46
  }
@@ -22,6 +22,11 @@ export type ElementMap = {
22
22
  'section': WithChildren<JSX.IntrinsicElements['section']>
23
23
  'header': WithChildren<JSX.IntrinsicElements['header']>
24
24
  'footer': WithChildren<JSX.IntrinsicElements['footer']>
25
+ 'nav': WithChildren<JSX.IntrinsicElements['nav']>
26
+ 'article': WithChildren<JSX.IntrinsicElements['article']>
27
+ 'ul': WithChildren<JSX.IntrinsicElements['ul']>
28
+ 'ol': WithChildren<JSX.IntrinsicElements['ol']>
29
+ 'aside': WithChildren<JSX.IntrinsicElements['aside']>
25
30
  }
26
31
 
27
32
  export type NativeHTMLElement = keyof ElementMap