@qoretechnologies/reqore 0.40.4 → 0.40.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.
@@ -1,3 +1,4 @@
1
+ import { size } from 'lodash';
1
2
  import { rgba } from 'polished';
2
3
  import React, { forwardRef, memo, useCallback, useMemo, useState } from 'react';
3
4
  import styled, { css } from 'styled-components';
@@ -73,6 +74,7 @@ export interface IReqoreButtonProps
73
74
  customTheme?: IReqoreCustomTheme;
74
75
  wrap?: boolean;
75
76
  badge?: TReqoreBadge | TReqoreBadge[];
77
+
76
78
  description?: string | number;
77
79
  maxWidth?: string;
78
80
  textAlign?: 'left' | 'center' | 'right';
@@ -315,28 +317,53 @@ export const ButtonBadge = memo(({ wrapGroup, compact, ...props }: IReqoreButton
315
317
  [props]
316
318
  );
317
319
 
318
- // If the content is a list
319
- if (Array.isArray(props.content)) {
320
- return (
321
- <>
322
- <ReqoreSpacer
323
- width={props.wrap ? undefined : PADDING_FROM_SIZE[props.size] / (compact ? 2 : 1)}
324
- height={!props.wrap ? undefined : PADDING_FROM_SIZE[props.size] / 2}
325
- />
326
- <ReqoreTagGroup wrap={wrapGroup}>
327
- {props.content.map((badge, index) => renderTag({ ...props, content: badge, key: index }))}
328
- </ReqoreTagGroup>
329
- </>
330
- );
331
- }
320
+ const content = Array.isArray(props.content) ? props.content : [props.content];
321
+
322
+ const leftBadges = content.filter(
323
+ (badge) => typeof badge === 'string' || typeof badge === 'number' || !badge?.align
324
+ );
325
+ const rightBadges = content.filter(
326
+ (badge) => typeof badge !== 'string' && typeof badge !== 'number' && badge?.align === 'right'
327
+ );
328
+ const middleBadges = content.filter(
329
+ (badge) => typeof badge !== 'string' && typeof badge !== 'number' && badge?.align === 'center'
330
+ );
331
+
332
+ const buildContent = (badge: TReqoreBadge) => {
333
+ if (typeof badge === 'string' || typeof badge === 'number') {
334
+ return { label: badge, align: undefined };
335
+ }
336
+
337
+ return { ...badge, align: undefined };
338
+ };
332
339
 
333
340
  return (
334
341
  <>
335
342
  <ReqoreSpacer
336
- width={props.wrap ? undefined : PADDING_FROM_SIZE[props.size]}
343
+ width={props.wrap ? undefined : PADDING_FROM_SIZE[props.size] / (compact ? 2 : 1)}
337
344
  height={!props.wrap ? undefined : PADDING_FROM_SIZE[props.size] / 2}
338
345
  />
339
- {renderTag({ ...props, key: 0 })}
346
+ {size(leftBadges) ? (
347
+ <ReqoreTagGroup wrap={wrapGroup} align='left'>
348
+ {leftBadges.map((badge, index) =>
349
+ renderTag({ ...props, content: buildContent(badge), key: index })
350
+ )}
351
+ </ReqoreTagGroup>
352
+ ) : null}
353
+ {size(middleBadges) ? (
354
+ <ReqoreTagGroup wrap={wrapGroup} fluid align='center'>
355
+ {middleBadges.map((badge, index) =>
356
+ renderTag({ ...props, content: buildContent(badge), key: index })
357
+ )}
358
+ </ReqoreTagGroup>
359
+ ) : null}
360
+ {size(rightBadges) ? (
361
+ <ReqoreTagGroup wrap={wrapGroup} align='right'>
362
+ {rightBadges.map((badge, index) =>
363
+ renderTag({ ...props, content: buildContent(badge), key: index })
364
+ )}
365
+ </ReqoreTagGroup>
366
+ ) : null}
340
367
  </>
341
368
  );
342
369
  });
@@ -402,6 +429,17 @@ const ReqoreButton = memo(
402
429
  const hasLeftIcon = icon || leftIconProps?.image;
403
430
  const hasRightIcon = rightIcon || rightIconProps?.image;
404
431
 
432
+ const hasRightAlignedBadge = useMemo(() => {
433
+ if (Array.isArray(badge)) {
434
+ return badge.some(
435
+ (badge) =>
436
+ typeof badge !== 'string' && typeof badge !== 'number' && badge?.align === 'right'
437
+ );
438
+ }
439
+
440
+ return typeof badge !== 'string' && typeof badge !== 'number' && badge?.align === 'right';
441
+ }, [badge]);
442
+
405
443
  return (
406
444
  <StyledButton
407
445
  {...rest}
@@ -498,7 +536,11 @@ const ReqoreButton = memo(
498
536
  _children ? (
499
537
  <ReqoreHorizontalSpacer
500
538
  width={1}
501
- style={textAlign !== 'right' ? { marginLeft: 'auto' } : undefined}
539
+ style={
540
+ textAlign !== 'right' && !hasRightAlignedBadge
541
+ ? { marginLeft: 'auto' }
542
+ : undefined
543
+ }
502
544
  />
503
545
  ) : null
504
546
  ) : (
@@ -515,7 +557,10 @@ const ReqoreButton = memo(
515
557
  style={
516
558
  textAlign !== 'right' || iconsAlign === 'center'
517
559
  ? {
518
- marginLeft: iconsAlign !== 'center' || !_children ? 'auto' : undefined,
560
+ marginLeft:
561
+ !hasRightAlignedBadge && (iconsAlign !== 'center' || !_children)
562
+ ? 'auto'
563
+ : undefined,
519
564
  marginRight:
520
565
  iconsAlign === 'center' || (textAlign === 'center' && !_children)
521
566
  ? 'auto'
@@ -28,6 +28,9 @@ export type TReqoreEffectColorManipulationAlpha =
28
28
  | 0.9
29
29
  | 1;
30
30
  export type TReqoreHexColor = `#${string}`;
31
+ export type TReqoreRgbColor = `rgb(${number}, ${number}, ${number})`;
32
+ export type TReqoreRgbaColor = `rgba(${number}, ${number}, ${number}, ${string})`;
33
+ export type TReqoreMultiTypeColor = TReqoreHexColor | TReqoreRgbColor | TReqoreRgbaColor;
31
34
  export type TReqoreColor = TReqoreHexColor | 'transparent';
32
35
  export type TReqoreEffectColor =
33
36
  | (TReqoreColor | TReqoreHexColor | 'main' | TReqoreIntent)
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import styled from 'styled-components';
2
+ import styled, { css } from 'styled-components';
3
3
  import { GAP_FROM_SIZE, TSizes } from '../../constants/sizes';
4
4
  import { IWithReqoreMinimal, IWithReqoreSize } from '../../types/global';
5
5
 
@@ -11,13 +11,32 @@ export interface IReqoreTagGroup
11
11
  gapSize?: TSizes;
12
12
  columns?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;
13
13
  wrap?: boolean;
14
+ fluid?: boolean;
15
+ align?: 'left' | 'center' | 'right';
14
16
  }
15
17
 
16
18
  const StyledTagGroup = styled.div`
17
19
  flex-shrink: ${({ wrap }: IReqoreTagGroup) => (wrap ? 1 : 0)};
20
+ flex-grow: ${({ fluid }: IReqoreTagGroup) => (fluid ? 1 : undefined)};
18
21
  display: flex;
19
22
  flex-wrap: ${({ wrap }: IReqoreTagGroup) => (wrap ? 'wrap' : 'nowrap')};
20
23
  gap: ${({ gapSize }: IReqoreTagGroup) => GAP_FROM_SIZE[gapSize]}px;
24
+
25
+ ${({ align }) => {
26
+ if (align === 'right') {
27
+ return css`
28
+ margin-left: auto;
29
+ justify-content: flex-end;
30
+ `;
31
+ }
32
+
33
+ if (align === 'center') {
34
+ return css`
35
+ margin: 0 auto;
36
+ justify-content: center;
37
+ `;
38
+ }
39
+ }}
21
40
  `;
22
41
 
23
42
  const ReqoreTagGroup = ({
@@ -56,6 +56,7 @@ export interface IReqoreTagProps
56
56
  IWithReqoreEffect,
57
57
  IWithReqoreCustomTheme {
58
58
  fixed?: boolean | 'key' | 'label';
59
+ align?: 'left' | 'right' | 'center';
59
60
  size?: TSizes;
60
61
  label?: string | number;
61
62
  labelKey?: string | number;
@@ -105,6 +106,26 @@ export const StyledTag = styled(StyledEffect)<IReqoreTagStyle>`
105
106
  width: ${({ width }) => width || undefined};
106
107
  transition: all 0.2s ease-out;
107
108
 
109
+ ${({ align }) => {
110
+ if (align === 'left') {
111
+ return css`
112
+ margin-right: auto;
113
+ `;
114
+ }
115
+
116
+ if (align === 'right') {
117
+ return css`
118
+ margin-left: auto;
119
+ `;
120
+ }
121
+
122
+ if (align === 'center') {
123
+ return css`
124
+ margin: 0 auto;
125
+ `;
126
+ }
127
+ }}
128
+
108
129
  ${InactiveIconScale};
109
130
 
110
131
  ${({ wrap, hasWidth }) =>
@@ -6,6 +6,8 @@ import {
6
6
  TReqoreEffectGradientColors,
7
7
  TReqoreEffectGradientColorsObject,
8
8
  TReqoreHexColor,
9
+ TReqoreMultiTypeColor,
10
+ TReqoreRgbaColor,
9
11
  } from '../components/Effect';
10
12
  import { Colors } from '../constants/colors';
11
13
  import { DEFAULT_INTENTS, IReqoreTheme, TReqoreIntent } from '../constants/theme';
@@ -39,8 +41,9 @@ export const getReadableColorFrom = (
39
41
  ): TReqoreHexColor => {
40
42
  const returnIfLight = ifLight || lighten(dim ? 0.05 : 0, Colors.DARK);
41
43
  const returnIfDark = ifDark || darken(dim ? 0.05 : 0, Colors.LIGHT);
44
+ const fixedColor = hexAToRGBA(from);
42
45
 
43
- return readableColor(from, returnIfLight, returnIfDark, false) as TReqoreHexColor;
46
+ return readableColor(fixedColor, returnIfLight, returnIfDark, false) as TReqoreHexColor;
44
47
  };
45
48
 
46
49
  export const percentToHexAlpha = (p: number) => {
@@ -51,6 +54,22 @@ export const percentToHexAlpha = (p: number) => {
51
54
  return hexValue.padStart(2, '0').toUpperCase(); // format with leading 0 and upper case characters
52
55
  };
53
56
 
57
+ export const hexAToRGBA = (hex: TReqoreMultiTypeColor): TReqoreRgbaColor => {
58
+ // Check if this color is already in rgba format
59
+ if (hex.startsWith('rgb')) {
60
+ return hex as TReqoreRgbaColor;
61
+ }
62
+
63
+ const _hex = hex.length === 4 ? hex + hex.slice(1, 3) : hex;
64
+
65
+ const r = parseInt(_hex.slice(1, 3), 16);
66
+ const g = parseInt(_hex.slice(3, 5), 16);
67
+ const b = parseInt(_hex.slice(5, 7), 16);
68
+ const a = parseInt(_hex.slice(7, 9) || 'ff', 16) / 255;
69
+
70
+ return `rgba(${r}, ${g}, ${b}, ${a.toFixed(2)})`;
71
+ };
72
+
54
73
  export const RGBAToHexA = (rgba, forceRemoveAlpha = false): TReqoreHexColor => {
55
74
  if (isValidSixCharHex(rgba)) {
56
75
  return rgba as TReqoreHexColor;
@@ -68,8 +87,8 @@ export const RGBAToHexA = (rgba, forceRemoveAlpha = false): TReqoreHexColor => {
68
87
  .join('')) as TReqoreHexColor; // Puts the array to togehter to a string
69
88
  };
70
89
 
71
- export const shouldDarken = (mainColor: TReqoreHexColor): boolean => {
72
- return getLuminance(mainColor) > 0.2;
90
+ export const shouldDarken = (mainColor: TReqoreMultiTypeColor): boolean => {
91
+ return getLuminance(hexAToRGBA(mainColor)) > 0.2;
73
92
  };
74
93
 
75
94
  export const getMainColor: (theme: IReqoreTheme, component: string) => TReqoreHexColor = (
@@ -80,19 +99,24 @@ export const getMainColor: (theme: IReqoreTheme, component: string) => TReqoreHe
80
99
  //export const changeBasedOnContrast = (color: string, lightness?: number): string =>
81
100
 
82
101
  export const changeLightness = (color: TReqoreHexColor, lightness?: number): TReqoreHexColor => {
102
+ const fixedColor = hexAToRGBA(color);
103
+
83
104
  return lightness
84
- ? shouldDarken(color)
85
- ? (darken(lightness, color) as TReqoreHexColor)
86
- : (lighten(lightness, color) as TReqoreHexColor)
105
+ ? shouldDarken(fixedColor)
106
+ ? (darken(lightness, fixedColor) as TReqoreHexColor)
107
+ : (lighten(lightness, fixedColor) as TReqoreHexColor)
87
108
  : color;
88
109
  };
89
110
 
90
- export const changeDarkness = (color: TReqoreHexColor, lightness?: number): TReqoreHexColor =>
91
- lightness
92
- ? shouldDarken(color)
93
- ? (lighten(lightness, color) as TReqoreHexColor)
94
- : (darken(lightness, color) as TReqoreHexColor)
111
+ export const changeDarkness = (color: TReqoreHexColor, lightness?: number): TReqoreHexColor => {
112
+ const fixedColor = hexAToRGBA(color);
113
+
114
+ return lightness
115
+ ? shouldDarken(fixedColor)
116
+ ? (lighten(lightness, fixedColor) as TReqoreHexColor)
117
+ : (darken(lightness, fixedColor) as TReqoreHexColor)
95
118
  : color;
119
+ };
96
120
 
97
121
  export const getColorByBgColor = (bgColor) => {
98
122
  if (!bgColor) {
@@ -95,8 +95,13 @@ const Template: StoryFn<typeof ReqoreButton> = (buttonProps) => {
95
95
  <ReqoreButton {...buttonProps} verticalPadding='small'>
96
96
  Vertical padding small
97
97
  </ReqoreButton>
98
- <ReqoreButton {...buttonProps} verticalPadding='normal'>
99
- Vertical padding normal
98
+ <ReqoreButton
99
+ {...buttonProps}
100
+ verticalPadding='normal'
101
+ rightIcon={undefined}
102
+ badge={{ align: 'right', label: 'R' }}
103
+ >
104
+ Vertical padding
100
105
  </ReqoreButton>
101
106
  <ReqoreButton {...buttonProps} verticalPadding='big'>
102
107
  Vertical padding big
@@ -111,17 +116,34 @@ const Template: StoryFn<typeof ReqoreButton> = (buttonProps) => {
111
116
  </ReqoreButton>
112
117
  </ReqoreControlGroup>
113
118
  <ReqoreControlGroup fluid wrap>
114
- <ReqoreButton {...buttonProps} fluid>
115
- Fluid button
119
+ <ReqoreButton
120
+ {...buttonProps}
121
+ fluid
122
+ badge={[
123
+ 1,
124
+ { label: 'in da middle', align: 'center' },
125
+ { label: 'right align', align: 'right' },
126
+ 2,
127
+ ]}
128
+ >
129
+ Positioned badges
130
+ </ReqoreButton>
131
+ <ReqoreButton
132
+ {...buttonProps}
133
+ fluid
134
+ badge={[1, { label: 'right align', align: 'right' }, 2]}
135
+ >
136
+ Positioned badges
116
137
  </ReqoreButton>
117
138
  </ReqoreControlGroup>
139
+
118
140
  <ReqoreControlGroup fluid wrap>
119
141
  <ReqoreButton {...buttonProps} fluid textAlign='center'>
120
142
  Fluid button with centered text
121
143
  </ReqoreButton>
122
144
  </ReqoreControlGroup>
123
145
  <ReqoreControlGroup fluid wrap>
124
- <ReqoreButton {...buttonProps} fluid textAlign='center' iconsAlign='center'>
146
+ <ReqoreButton {...buttonProps} fluid textAlign='center' iconsAlign='center' badge='badge'>
125
147
  Fluid button with centered text & icons
126
148
  </ReqoreButton>
127
149
  <ReqoreButton
@@ -130,6 +152,7 @@ const Template: StoryFn<typeof ReqoreButton> = (buttonProps) => {
130
152
  fluid
131
153
  textAlign='center'
132
154
  iconsAlign='center'
155
+ badge={[{ label: 'test', align: 'right' }, 2]}
133
156
  >
134
157
  Fluid button with centered text & icons
135
158
  </ReqoreButton>
@@ -107,6 +107,15 @@ export const Pending: Story = {
107
107
  },
108
108
  };
109
109
 
110
+ export const Muted: Story = {
111
+ render: Template,
112
+
113
+ args: {
114
+ opaque: true,
115
+ intent: 'muted',
116
+ },
117
+ };
118
+
110
119
  export const CustomTheme: Story = {
111
120
  render: Template,
112
121