@oliasoft-open-source/react-ui-library 2.3.4 → 2.4.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": "@oliasoft-open-source/react-ui-library",
3
- "version": "2.3.4",
3
+ "version": "2.4.0",
4
4
  "description": "Reusable UI components for React projects",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -126,6 +126,7 @@
126
126
  "react-icons": "^4.2.0",
127
127
  "react-infinite-scroll-component": "^6.1.0",
128
128
  "react-laag": "^2.0.3",
129
+ "react-resizable": "^3.0.4",
129
130
  "react-router-dom": "^5.3.0",
130
131
  "react-svg": "^14.0.12",
131
132
  "react-toastify": "^8.0.2",
@@ -0,0 +1,73 @@
1
+ import React, { useState } from 'react';
2
+ import { Resizable } from 'react-resizable';
3
+ import PropTypes from 'prop-types';
4
+ import styles from './drawer.module.less';
5
+
6
+ const ResizeHandle = React.forwardRef((props, ref) => {
7
+ const { handleAxis, ...rest } = props;
8
+ return (
9
+ <div
10
+ ref={ref}
11
+ className={styles.resizeHandle}
12
+ {...rest} // eslint-disable-line react/jsx-props-no-spreading
13
+ />
14
+ );
15
+ });
16
+
17
+ export const DrawerResizeWrapper = ({
18
+ children,
19
+ width,
20
+ minWidth,
21
+ right,
22
+ onResize,
23
+ open,
24
+ }) => {
25
+ const [isResizing, setIsResizing] = useState(false);
26
+ const MINIMUM_OPEN_WIDTH = 100;
27
+ const MAXIMUM_OPEN_WIDTH = window.innerWidth / 2;
28
+
29
+ const handleResize = (event, { node, size, handle }) => {
30
+ if (
31
+ open &&
32
+ size.width > MINIMUM_OPEN_WIDTH &&
33
+ size.width < MAXIMUM_OPEN_WIDTH
34
+ ) {
35
+ onResize(size.width);
36
+ }
37
+ };
38
+
39
+ return onResize ? (
40
+ <Resizable
41
+ width={width}
42
+ height={100} // Arbitrary height value. It's required by react-resizable even if height not adjustable but doesn't actually do anything in this case
43
+ minConstraints={[minWidth, null]}
44
+ onResize={handleResize}
45
+ handle={open ? <ResizeHandle /> : null}
46
+ resizeHandles={right ? ['w'] : ['e']}
47
+ axis="x"
48
+ className={isResizing ? styles.isResizing : ''}
49
+ onResizeStart={() => setIsResizing(true)}
50
+ onResizeStop={() => setIsResizing(false)}
51
+ >
52
+ {children}
53
+ </Resizable>
54
+ ) : (
55
+ <>{children}</>
56
+ );
57
+ };
58
+
59
+ DrawerResizeWrapper.defaultProps = {
60
+ right: false,
61
+ width: 400,
62
+ minWidth: null,
63
+ children: null,
64
+ onResize: null,
65
+ };
66
+
67
+ DrawerResizeWrapper.propTypes = {
68
+ right: PropTypes.bool,
69
+ width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
70
+ minWidth: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
71
+ children: PropTypes.node,
72
+ onResize: PropTypes.func,
73
+ };
@@ -31,6 +31,7 @@ export const DrawerTabs = ({
31
31
  return (
32
32
  <div
33
33
  style={{ width }}
34
+ className={styles.tabsContent}
34
35
  key={index}
35
36
  data-testid={testId && `${testId}-content-${index}`}
36
37
  >
@@ -5,6 +5,7 @@ import { FaChevronLeft, FaChevronRight } from 'react-icons/fa';
5
5
  import { Button } from '../button/button';
6
6
  import { DrawerTabs } from './drawer-tabs';
7
7
  import styles from './drawer.module.less';
8
+ import { DrawerResizeWrapper } from './drawer-resize-wrapper';
8
9
 
9
10
  export const Drawer = ({
10
11
  background,
@@ -23,6 +24,7 @@ export const Drawer = ({
23
24
  tabs,
24
25
  defaultTabIndex,
25
26
  testId,
27
+ onResize,
26
28
  }) => {
27
29
  const isStandardButton = button === true;
28
30
  const isCustomButton = !isStandardButton && isValidElement(button);
@@ -30,7 +32,8 @@ export const Drawer = ({
30
32
  isStandardButton || tabs ? useState(openProp) : [openProp, null];
31
33
  const [activeTab, setActiveTab] = useState(open ? defaultTabIndex : null);
32
34
  const openWidth = Array.isArray(width) ? width[activeTab] : width;
33
- const currentWidth = open ? openWidth : closedWidth;
35
+ const TABS_WIDTH = 38;
36
+ const currentWidth = open ? openWidth : tabs ? TABS_WIDTH : closedWidth;
34
37
 
35
38
  const handleTabClick = (index) => {
36
39
  if (activeTab === index) {
@@ -43,58 +46,64 @@ export const Drawer = ({
43
46
  };
44
47
 
45
48
  return (
46
- <div
47
- className={cx(
48
- styles.drawer,
49
- shadow ? styles.shadow : '',
50
- fixed ? styles.fixed : styles.inline,
51
- right ? styles.right : styles.left,
52
- )}
53
- style={{
54
- top,
55
- }}
49
+ <DrawerResizeWrapper
50
+ width={currentWidth}
51
+ minWidth={tabs ? TABS_WIDTH : closedWidth}
52
+ onResize={onResize}
53
+ open={open}
54
+ right={right}
56
55
  >
57
56
  <div
58
- className={cx(styles.drawerContent, border && styles.border)}
59
- style={{ background, borderColor: border, width: currentWidth }}
60
- >
61
- {tabs ? (
62
- <DrawerTabs
63
- width={openWidth}
64
- testId={testId}
65
- open={open}
66
- tabs={tabs}
67
- activeTab={activeTab}
68
- background={background}
69
- handleTabClick={handleTabClick}
70
- />
71
- ) : (
72
- <div style={{ width: openWidth }}>{children}</div>
57
+ className={cx(
58
+ styles.drawer,
59
+ shadow ? styles.shadow : '',
60
+ fixed ? styles.fixed : styles.inline,
61
+ right ? styles.right : styles.left,
73
62
  )}
74
- </div>
75
-
76
- {button && (
77
- <span
78
- className={cx(
79
- styles.toggleButton,
80
- open && (isStandardButton || (isCustomButton && buttonAnimate))
81
- ? styles.toggleButtonOpen
82
- : '',
83
- buttonPosition === 'top' ? styles.top : styles.bottom,
84
- )}
63
+ style={{ top }}
64
+ >
65
+ <div
66
+ className={cx(styles.drawerContent, border && styles.border)}
67
+ style={{ background, borderColor: border, width: currentWidth }}
85
68
  >
86
- {isCustomButton ? (
87
- button
88
- ) : (
89
- <Button
90
- onClick={setOpen ? () => setOpen(!open) : null}
91
- round
92
- icon={right ? <FaChevronRight /> : <FaChevronLeft />}
69
+ {tabs ? (
70
+ <DrawerTabs
71
+ width={openWidth}
72
+ testId={testId}
73
+ open={open}
74
+ tabs={tabs}
75
+ activeTab={activeTab}
76
+ background={background}
77
+ handleTabClick={handleTabClick}
93
78
  />
79
+ ) : (
80
+ <div style={{ width: openWidth }}>{children}</div>
94
81
  )}
95
- </span>
96
- )}
97
- </div>
82
+ </div>
83
+
84
+ {button && (
85
+ <span
86
+ className={cx(
87
+ styles.toggleButton,
88
+ open && (isStandardButton || (isCustomButton && buttonAnimate))
89
+ ? styles.toggleButtonOpen
90
+ : '',
91
+ buttonPosition === 'top' ? styles.top : styles.bottom,
92
+ )}
93
+ >
94
+ {isCustomButton ? (
95
+ button
96
+ ) : (
97
+ <Button
98
+ onClick={setOpen ? () => setOpen(!open) : null}
99
+ round
100
+ icon={right ? <FaChevronRight /> : <FaChevronLeft />}
101
+ />
102
+ )}
103
+ </span>
104
+ )}
105
+ </div>
106
+ </DrawerResizeWrapper>
98
107
  );
99
108
  };
100
109
 
@@ -115,6 +124,7 @@ Drawer.defaultProps = {
115
124
  tabs: null,
116
125
  defaultTabIndex: 0,
117
126
  testId: undefined,
127
+ onResize: null,
118
128
  };
119
129
 
120
130
  Drawer.propTypes = {
@@ -147,4 +157,5 @@ Drawer.propTypes = {
147
157
  ),
148
158
  testId: PropTypes.string,
149
159
  defaultTabIndex: PropTypes.number,
160
+ onResize: PropTypes.func,
150
161
  };
@@ -1,12 +1,14 @@
1
1
  @import '../../style/variables.less';
2
2
 
3
3
  :root {
4
+ --color-background-drawer-tabs: var(--color-neutral-100);
4
5
  --color-background-drawer-tab: #fff9f4;
5
6
  --color-background-drawer-tab-hover: white;
6
7
  --color-border-drawer-tab: #e4cbb7;
7
8
  }
8
9
 
9
10
  html[data-theme='dark'] {
11
+ --color-background-drawer-tabs: var(--color-neutral-900);
10
12
  --color-background-drawer-tab: var(--color-primary-muted-800);
11
13
  --color-background-drawer-tab-hover: var(--color-primary-muted-700);
12
14
  --color-border-drawer-tab: var(--color-border);
@@ -17,10 +19,12 @@ html[data-theme='dark'] {
17
19
 
18
20
  &.left {
19
21
  order: -1;
22
+ margin-left: -1px;
20
23
  }
21
24
 
22
25
  &.right {
23
26
  order: 9999;
27
+ margin-right: -1px;
24
28
  }
25
29
  }
26
30
 
@@ -58,9 +62,12 @@ html[data-theme='dark'] {
58
62
  overflow-y: auto;
59
63
  max-height: 100%;
60
64
  min-height: 100%;
61
-
62
65
  transition: width 0.2s;
63
66
 
67
+ .isResizing & {
68
+ transition: none;
69
+ }
70
+
64
71
  @media print {
65
72
  background: transparent !important;
66
73
  }
@@ -81,7 +88,7 @@ html[data-theme='dark'] {
81
88
  display: inline-block;
82
89
  transform: rotate(180deg);
83
90
  position: absolute;
84
- z-index: 1;
91
+ z-index: @zindex_drawer + 3;
85
92
 
86
93
  .left & {
87
94
  right: -19px;
@@ -106,28 +113,30 @@ html[data-theme='dark'] {
106
113
 
107
114
  .tabs {
108
115
  position: absolute;
109
- top: 20px;
116
+ top: 0;
117
+ bottom: 0;
118
+ background-color: var(--color-background-drawer-tabs);
119
+ padding-top: 20px;
110
120
  z-index: @zindex_drawer + 1;
121
+ border-left: 1px solid var(--color-border);
122
+ border-right: 1px solid var(--color-border);
111
123
 
112
124
  .left & {
113
- left: 100%;
114
- margin-left: -1px;
125
+ right: 0;
115
126
  }
116
127
 
117
128
  .right & {
118
- right: 100%;
119
- margin-right: -1px;
129
+ left: 0;
120
130
  }
121
131
 
122
132
  .tab {
123
- height: 40px;
124
- width: 40px;
133
+ height: 38px;
134
+ width: 38px;
125
135
  display: flex;
126
136
  justify-content: center;
127
137
  align-items: center;
128
- margin-bottom: 1px;
129
- border-radius: 4px;
130
138
  cursor: pointer;
139
+ margin: -1px;
131
140
  transition: color 0.3s, background-color 0.3s;
132
141
  border: 1px solid var(--color-border-drawer-tab);
133
142
 
@@ -165,3 +174,37 @@ html[data-theme='dark'] {
165
174
  }
166
175
  }
167
176
  }
177
+
178
+ .tabsContent {
179
+ .left & {
180
+ padding-right: 36px;
181
+ }
182
+ .right & {
183
+ padding-left: 36px;
184
+ }
185
+ }
186
+
187
+ .resizeHandle {
188
+ position: absolute;
189
+ top: 0;
190
+ bottom: 0;
191
+ width: 4px;
192
+ z-index: @zindex_drawer + 2;
193
+ cursor: ew-resize;
194
+
195
+ &:hover {
196
+ background: rgba(@colorPrimary, 0.25);
197
+ }
198
+
199
+ &:active {
200
+ background: @colorPrimary;
201
+ }
202
+
203
+ .left & {
204
+ right: 0;
205
+ }
206
+
207
+ .right & {
208
+ left: 0;
209
+ }
210
+ }
@@ -5,6 +5,8 @@ import {
5
5
  FaCogs,
6
6
  FaPenAlt,
7
7
  FaQuestion,
8
+ FaTrash,
9
+ FaPlus,
8
10
  } from 'react-icons/fa';
9
11
  import { Drawer, Button, List, Heading, Flex } from '../../..';
10
12
  import { listSubheadingsBadges } from '../list/list.stories-data';
@@ -29,7 +31,7 @@ export default {
29
31
  },
30
32
  decorators: [
31
33
  (story) => (
32
- <Flex height="400px">
34
+ <Flex height="400px" wrap={false}>
33
35
  <div
34
36
  style={{
35
37
  flexGrow: 1,
@@ -154,3 +156,141 @@ Tabs.parameters = {
154
156
  },
155
157
  },
156
158
  };
159
+
160
+ export const MultipleTabbedDrawers = () => {
161
+ const tabs1 = [
162
+ {
163
+ icon: <FaQuestion />,
164
+ content: (
165
+ <div style={{ padding: 20 }}>
166
+ <Heading top>Help</Heading>
167
+ {'Example content goes here lorem ipsum. '.repeat(500)}
168
+ </div>
169
+ ),
170
+ },
171
+ {
172
+ icon: <FaCogs />,
173
+ content: (
174
+ <div style={{ padding: 20 }}>
175
+ <Heading top>Settings</Heading>
176
+ {'Example content goes here lorem ipsum. '.repeat(500)}
177
+ </div>
178
+ ),
179
+ },
180
+ ];
181
+ const tabs2 = [
182
+ {
183
+ icon: <FaPenAlt />,
184
+ content: (
185
+ <div style={{ padding: 20 }}>
186
+ <Heading top>Edit</Heading>
187
+ {'Example content goes here lorem ipsum. '.repeat(500)}
188
+ </div>
189
+ ),
190
+ },
191
+ ];
192
+ return (
193
+ <>
194
+ <Drawer
195
+ right
196
+ background="var(--color-background-raised)"
197
+ tabs={tabs1}
198
+ border
199
+ open
200
+ />
201
+ <Drawer
202
+ right
203
+ background="var(--color-background-raised)"
204
+ tabs={tabs2}
205
+ border
206
+ open
207
+ />
208
+ </>
209
+ );
210
+ };
211
+
212
+ export const Resizable = () => {
213
+ const [open, setOpen] = useState(true);
214
+ const [listDrawerWidth, setListDrawerWidth] = useState(300);
215
+ const [tabbedDrawerWidth, setTabbedDrawerWidth] = useState(300);
216
+ const tabs = [
217
+ {
218
+ icon: <FaQuestion />,
219
+ content: (
220
+ <div style={{ padding: 20 }}>
221
+ <Heading top>Help</Heading>
222
+ {'Example content goes here lorem ipsum. '.repeat(500)}
223
+ </div>
224
+ ),
225
+ },
226
+ ];
227
+ return (
228
+ <>
229
+ <Drawer
230
+ button={
231
+ <Button
232
+ onClick={setOpen ? () => setOpen(!open) : null}
233
+ round
234
+ icon={<FaChevronLeft />}
235
+ />
236
+ }
237
+ open={open}
238
+ background="var(--color-background-raised)"
239
+ border
240
+ width={listDrawerWidth}
241
+ closedWidth={50}
242
+ onResize={(newWidth) => setListDrawerWidth(newWidth)}
243
+ >
244
+ <List
245
+ drawer
246
+ list={{
247
+ name: 'Animals',
248
+ actions: [
249
+ {
250
+ label: 'Add',
251
+ icon: <FaPlus />,
252
+ onClick: () => {},
253
+ },
254
+ ],
255
+ items: [
256
+ {
257
+ id: 1,
258
+ name: 'Aardvark',
259
+ onClick: () => {},
260
+ actions: [
261
+ {
262
+ label: 'Delete',
263
+ icon: <FaTrash />,
264
+ onClick: () => {},
265
+ },
266
+ ],
267
+ },
268
+ {
269
+ id: 2,
270
+ name: 'Kangaroo',
271
+ active: true,
272
+ actions: [
273
+ {
274
+ label: 'Delete',
275
+ icon: <FaTrash />,
276
+ onClick: () => {},
277
+ },
278
+ ],
279
+ },
280
+ ],
281
+ }}
282
+ narrow={!open}
283
+ />
284
+ </Drawer>
285
+ <Drawer
286
+ right
287
+ background="var(--color-background-raised)"
288
+ tabs={tabs}
289
+ border
290
+ open
291
+ width={tabbedDrawerWidth}
292
+ onResize={(newWidth) => setTabbedDrawerWidth(newWidth)}
293
+ />
294
+ </>
295
+ );
296
+ };
@@ -22,7 +22,12 @@ export default {
22
22
  args: {
23
23
  alignItems: undefined,
24
24
  justifyContent: undefined,
25
- children: [<Button label="Button" />, <Text>Text</Text>],
25
+ children: (
26
+ <>
27
+ <Button label="Button" />
28
+ <Text>Text</Text>
29
+ </>
30
+ ),
26
31
  },
27
32
  };
28
33
 
@@ -21,14 +21,16 @@ export default {
21
21
  },
22
22
  args: {
23
23
  columns: '1fr 1fr 1fr',
24
- children: [
25
- <Card>Item 1</Card>,
26
- <Card>Item 2</Card>,
27
- <Card>Item 3</Card>,
28
- <Card>Item 4</Card>,
29
- <Card>Item 5</Card>,
30
- <Card>Item 6</Card>,
31
- ],
24
+ children: (
25
+ <>
26
+ <Card>Item 1</Card>
27
+ <Card>Item 2</Card>
28
+ <Card>Item 3</Card>
29
+ <Card>Item 4</Card>
30
+ <Card>Item 5</Card>
31
+ <Card>Item 6</Card>
32
+ </>
33
+ ),
32
34
  },
33
35
  };
34
36
 
@@ -73,7 +73,6 @@
73
73
  bottom: 100%;
74
74
  margin-bottom: 3px;
75
75
  font-size: 10px;
76
- color: var(--color-text-faint);
77
76
  }
78
77
 
79
78
  .left,