@spaced-out/ui-design-system 0.1.67 → 0.1.68

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/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [0.1.68](https://github.com/spaced-out/ui-design-system/compare/v0.1.67...v0.1.68) (2023-12-04)
6
+
7
+
8
+ ### Features
9
+
10
+ * bulk action buttons and table inline edit ([#155](https://github.com/spaced-out/ui-design-system/issues/155)) ([7ae82fc](https://github.com/spaced-out/ui-design-system/commit/7ae82fc6f3fba77f53d7a2269d7a4df675c6d69f))
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * circular loader extra space ([#154](https://github.com/spaced-out/ui-design-system/issues/154)) ([a6ce232](https://github.com/spaced-out/ui-design-system/commit/a6ce2326edc4ce7779471f171d70ba2f33203cfc))
16
+
5
17
  ### [0.1.67](https://github.com/spaced-out/ui-design-system/compare/v0.1.66...v0.1.67) (2023-11-10)
6
18
 
7
19
 
@@ -105,6 +105,20 @@
105
105
  "value": "{baseColor.indigo.940.70.value}"
106
106
  },
107
107
  "button-fill": {
108
+ "tableAction": {
109
+ "enabled": {
110
+ "value": "{baseColor.purple.600.value}"
111
+ },
112
+ "hovered": {
113
+ "value": "{baseColor.purple.700.value}"
114
+ },
115
+ "pressed": {
116
+ "value": "{baseColor.purple.700.value}"
117
+ },
118
+ "border": {
119
+ "value": "{baseColor.purple.400.value}"
120
+ }
121
+ },
108
122
  "primary": {
109
123
  "enabled": {
110
124
  "value": "{baseColor.purple.500.value}"
@@ -129,6 +129,9 @@
129
129
  "960": {
130
130
  "value": "960px"
131
131
  },
132
+ "1280": {
133
+ "value": "1280px"
134
+ },
132
135
  "fluid": {
133
136
  "value": "100%"
134
137
  },
@@ -15,27 +15,29 @@
15
15
  @value spinnerSize: size60;
16
16
  @value spinnerThickness: size5;
17
17
 
18
- @value innerCircleSize: calc(spinnerSize - 2 * spinnerThickness);
19
-
20
- @value innerMaskGradient: radial-gradient(farthest-side, colorFillNone calc(99% - spinnerThickness), colorFillPrimary calc(100% - spinnerThickness));
18
+ @value innerMaskGradient: radial-gradient(farthest-side, colorFillNone calc(99% - var(--spinner-thickness)), colorFillPrimary calc(100% - var(--spinner-thickness)));
21
19
 
22
20
  .container {
23
21
  display: flex;
24
22
  justify-content: flex-start;
25
23
  align-items: flex-start;
24
+ --spinner-size: spinnerSize;
25
+ --spinner-thickness: spinnerThickness;
26
26
  }
27
27
 
28
28
  .mediumContainer {
29
- transform: scale(0.5);
29
+ --spinner-size: calc(0.5 * spinnerSize);
30
+ --spinner-thickness: calc(0.5 * spinnerThickness);
30
31
  }
31
32
 
32
33
  .smallContainer {
33
- transform: scale(0.36);
34
+ --spinner-size: calc(0.36 * spinnerSize);
35
+ --spinner-thickness: calc(0.36 * spinnerThickness);
34
36
  }
35
37
 
36
38
  .spinLoader {
37
- height: spinnerSize;
38
- width: spinnerSize;
39
+ height: var(--spinner-size);
40
+ width: var(--spinner-size);
39
41
  display: block;
40
42
  position: relative;
41
43
  animation: spin motionDurationSlowest linear 0s infinite normal;
@@ -46,9 +48,9 @@
46
48
  --color: colorFillPrimary;
47
49
  display: block;
48
50
  position: absolute;
49
- border-radius: calc(spinnerSize/2);
50
- height: spinnerSize;
51
- width: spinnerSize;
51
+ border-radius: calc(var(--spinner-size) / 2);
52
+ height: var(--spinner-size);
53
+ width: var(--spinner-size);
52
54
  top: spaceNone;
53
55
  right: spaceNone;
54
56
  background: conic-gradient(
@@ -63,18 +65,20 @@
63
65
  .ellipse {
64
66
  --color: colorFillPrimary;
65
67
  position: absolute;
66
- left: calc(spinnerSize - spinnerThickness);
67
68
  right: spaceNone;
68
69
  top: spaceNone;
69
70
  bottom: spaceNone;
70
- border-radius: calc(spinnerThickness/2);
71
- height: spinnerThickness;
72
- width: spinnerThickness;
71
+ border-radius: calc(var(--spinner-thickness) / 2);
72
+ height: var(--spinner-thickness);
73
+ width: var(--spinner-thickness);
73
74
  margin: auto;
74
75
  background: var(--color);
75
76
  }
76
77
 
77
78
  @keyframes spin {
79
+ from {
80
+ transform: rotate(0deg);
81
+ }
78
82
  to {
79
83
  transform: rotate(360deg);
80
84
  }
@@ -23,6 +23,7 @@ const SemanticIcon = _ref => {
23
23
  return /*#__PURE__*/React.createElement(React.Fragment, null, !!iconProps.name && /*#__PURE__*/React.createElement("div", {
24
24
  className: (0, _classify.default)(_SemanticIconModule.default.iconContainer, _SemanticIconModule.default[semantic], classNames?.wrapper)
25
25
  }, /*#__PURE__*/React.createElement(_Icon.Icon, _extends({}, iconProps, {
26
+ className: (0, _classify.default)(iconProps.className, classNames?.icon),
26
27
  color: _typography.TEXT_COLORS[semantic],
27
28
  type: type
28
29
  }))));
@@ -35,7 +35,12 @@ export const SemanticIcon = ({
35
35
  classNames?.wrapper,
36
36
  )}
37
37
  >
38
- <Icon {...iconProps} color={TEXT_COLORS[semantic]} type={type} />
38
+ <Icon
39
+ {...iconProps}
40
+ className={classify(iconProps.className, classNames?.icon)}
41
+ color={TEXT_COLORS[semantic]}
42
+ type={type}
43
+ />
39
44
  </div>
40
45
  )}
41
46
  </>
@@ -0,0 +1,514 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.TABLE_DOCS = void 0;
7
+ const TABLE_DOCS = {
8
+ argTypes: {
9
+ headers: {
10
+ type: {
11
+ required: true
12
+ },
13
+ description: 'Provide an array of objects to render the header, each object should consist of label(*), key(*), sortable, sticky and render function',
14
+ control: {
15
+ type: 'object'
16
+ },
17
+ table: {
18
+ type: {
19
+ summary: '{label: React.Node, key: $Keys<T>, className?: string, filterIcon?: React.Node, filtered?: boolean, subtext?: string, sortable?: boolean, headerIconClassName?: string, sticky?: boolean, render?: React.ComponentType<{data: T, extras?: U, className?: string, selected?: boolean,}>, }'
20
+ }
21
+ }
22
+ },
23
+ entries: {
24
+ type: {
25
+ required: true
26
+ },
27
+ description: 'Provide an array of object. Each object belong on one row in the table. Each object should have id and all the keys with values that is being passed in headers array',
28
+ control: {
29
+ type: 'object'
30
+ },
31
+ table: {
32
+ type: {
33
+ summary: 'Array<T>'
34
+ }
35
+ }
36
+ },
37
+ classNames: {
38
+ description: 'Provide optional classNames to be applied to the wrapper, table, tableHeader, tableBody, tableRow, or checkbox',
39
+ control: {
40
+ type: 'object'
41
+ },
42
+ table: {
43
+ type: {
44
+ summary: '{wrapper?: string, table?: string, tableHeader?: string, tableBody?: string, tableRow?: string, checkbox?: string}'
45
+ }
46
+ }
47
+ },
48
+ Row: {
49
+ description: 'Custom Row component to be passed to table',
50
+ table: {
51
+ type: {
52
+ summary: 'TableRow<T, U>'
53
+ }
54
+ }
55
+ },
56
+ sortable: {
57
+ description: 'Sort Icon will appear in right side of column\'s header with column level true sortable value',
58
+ name: 'sortable',
59
+ options: [false, true],
60
+ control: 'boolean',
61
+ table: {
62
+ type: {
63
+ summary: 'boolean'
64
+ },
65
+ defaultValue: {
66
+ summary: true
67
+ }
68
+ }
69
+ },
70
+ showHeader: {
71
+ description: 'Show or hide header',
72
+ options: [false, true],
73
+ control: 'boolean',
74
+ table: {
75
+ type: {
76
+ summary: 'boolean'
77
+ },
78
+ defaultValue: {
79
+ summary: true
80
+ }
81
+ }
82
+ },
83
+ defaultSortKey: {
84
+ description: 'key name for which table should be sorted by default. headers array should have sortable true for that key object',
85
+ control: {
86
+ type: 'text'
87
+ },
88
+ table: {
89
+ type: {
90
+ summary: 'string'
91
+ }
92
+ }
93
+ },
94
+ // defaultSortDirection?: 'asc' | 'desc' | 'original',
95
+ defaultSortDirection: {
96
+ description: 'default sorting direction for the defaultSortKey column',
97
+ control: {
98
+ type: 'text'
99
+ },
100
+ table: {
101
+ type: {
102
+ summary: '\'asc\' | \'desc\' | \'original\''
103
+ }
104
+ }
105
+ },
106
+ selectedKeys: {
107
+ description: 'array of keys which are selected. This prop is also used to decide weather to show checkboxes or not. So, pass an empty array incase no row is selected.',
108
+ control: {
109
+ type: 'object'
110
+ },
111
+ table: {
112
+ type: {
113
+ summary: 'string[]'
114
+ }
115
+ }
116
+ },
117
+ //onSelect?: (keys: string[]) => mixed
118
+ onSelect: {
119
+ description: 'callback function when any row is selected',
120
+ action: 'selected',
121
+ type: {
122
+ summary: '(keys: string[]) => mixed'
123
+ }
124
+ },
125
+ idName: {
126
+ description: 'name of key in row objects that has to be used as id'
127
+ },
128
+ //onSort?: (key: $Keys<T>
129
+ onSort: {
130
+ description: 'callback function when any table is sorted',
131
+ action: 'sorted',
132
+ type: {
133
+ summary: '(key: $Keys<T>, direction: SortDirection) => void'
134
+ }
135
+ },
136
+ isLoading: {
137
+ description: 'to wait for data to populate in table and show loading state when entries array is empty.',
138
+ options: [false, true],
139
+ control: 'boolean',
140
+ table: {
141
+ type: {
142
+ summary: 'boolean'
143
+ },
144
+ defaultValue: {
145
+ summary: false
146
+ }
147
+ }
148
+ },
149
+ emptyText: {
150
+ description: 'Provide component to be shown in case of empty data',
151
+ table: {
152
+ type: {
153
+ summary: 'React.Component'
154
+ }
155
+ }
156
+ },
157
+ customLoader: {
158
+ description: 'Provide optional component to be shown in case of loading state. It will override the default loader.',
159
+ table: {
160
+ type: {
161
+ summary: 'React.Component'
162
+ }
163
+ }
164
+ },
165
+ disabled: {
166
+ description: 'disable all the checkboxes of each row in the table',
167
+ options: [false, true],
168
+ control: 'boolean',
169
+ table: {
170
+ type: {
171
+ summary: 'boolean'
172
+ },
173
+ defaultValue: {
174
+ summary: false
175
+ }
176
+ }
177
+ },
178
+ borderRadius: {
179
+ description: 'Border Radius of all four corners of the table. Default value is borderRadiusMedium i.e., 12px.',
180
+ control: 'text',
181
+ table: {
182
+ type: {
183
+ summary: 'string'
184
+ },
185
+ defaultValue: {
186
+ summary: '12px'
187
+ }
188
+ }
189
+ },
190
+ stickyHeader: {
191
+ description: 'Sticky header will stick the header to the top of the table when scrolling. This would only work if the table wrapper is scrollable.',
192
+ control: 'boolean',
193
+ table: {
194
+ type: {
195
+ summary: 'boolean'
196
+ },
197
+ defaultValue: {
198
+ summary: false
199
+ }
200
+ }
201
+ }
202
+ },
203
+ parameters: {
204
+ componentSubtitle: 'Generates a Table component.',
205
+ docs: {
206
+ description: {
207
+ component: `
208
+ \`\`\`js
209
+ import {
210
+ Table,
211
+ StaticTable,
212
+ DefaultRow,
213
+ BasicSingleCell,
214
+ DateCell,
215
+ DoubleCell,
216
+ SingleCell,
217
+ MonogramCell,
218
+ TableActionBar,
219
+ ButtonCta,
220
+ DropdownCta,
221
+ TableTopBar,
222
+ TableBottomBar
223
+ } from "@spaced-out/ui-design-system/lib/components/Table";
224
+ \`\`\`
225
+ Table component internally uses HTML Table, Thead, Tbody, Tr, Th and Td tags. It accepts entries and headers for showing table.
226
+ The Table component takes care of lots of things for you but it's not a magical component, it wraps a lot of behavior, so you can have,
227
+ for instance, a static table with no sorting options.
228
+
229
+ StaticTables work just like regular tables, except they don't have interactive headers, so you lose sorting.
230
+ But remember, a regular interactive table uses a static table under the hood,
231
+ so how does it work? You can think of the StaticTable as a kind of controlled table component.
232
+ except that traditionally we'd update the entries prop and rerender from that.
233
+ That would work just fine, but instead we can just pass in the keys we care about. By using StaticTable,
234
+ we can simplify filtering and sorting outside of the table while the table keeps a stable value reference to all the entities.
235
+
236
+ To make any of rows selectable, we can do that by also passing in another property, selectedKeys.
237
+
238
+ Let's say we want to also make any of the rows selectable, we can do that by also passing in another property, selectedKeys.
239
+
240
+ ### Custom Cells
241
+
242
+ It's possible you need to render out a custom cell, not just the default cell.
243
+ There are two ways to do that. If you only need to modify a single cell,
244
+ you can simply add a render: \`React.ComponentType<{data: T, extras?: U}>\`
245
+ key to any given header item.
246
+
247
+ This cell renderer receives the _whole row_ of data, not just the individual cell, it's up to you what to do with it.
248
+
249
+ ### With Table Top, Bottom Bar and Actions
250
+
251
+ You can attach a top, bottom and action bar to the table by adding \`TableTopBar\`, \`TableBottomBar\` and \`TableActionBar\` components.
252
+ There are just semantic components which just provide padding and border standardization. You can add any component inside these components.
253
+ These also accept **className** prop.
254
+
255
+ ### Best Practices
256
+ * A Data Table should always have a \`TableTopBar\`
257
+ * Use \`stickyHeader\` prop to make the header sticky
258
+ * Always assign a height to the table wrapper to make it scrollable
259
+ * Manage the Table's Wrapper height effectively when using \`TableActionBar\` component
260
+ * Always use local \`ButtonCta\` and \`DropdownCta\` for actions in Action bar. These components are
261
+ just CSS Wrappers for Button and SimpleButtonDropdown components
262
+ * Use ButtonCta component to add a button CTA in the \`TableActionBar\`
263
+ * Use DropdownCta component to add a dropdown CTA button in the \`TableActionBar\` for screen width less than size1280(1280px)
264
+
265
+ \`\`\`jsx
266
+ <Table
267
+ headers={TABLE_HEADERS}
268
+ entries={TABLE_DATA}
269
+ stickyHeader={true}
270
+ classNames={{
271
+ wrapper: classify(css.tableWrapper, {
272
+ [css.withActionBar]: getNumberOfEntriesSelected() > 0,
273
+ })
274
+ }}
275
+ />
276
+ \`\`\`
277
+
278
+ \`\`\`css
279
+ .tableWrapper {
280
+ height: calc(sizeFullViewportHeight - size160);
281
+ }
282
+
283
+ .tableWrapper.withActionBar {
284
+ height: calc((sizeFullViewportHeight - size160) - actionBarHeight);
285
+ }
286
+ \`\`\`
287
+
288
+ ### Usage
289
+
290
+ \`\`\`jsx
291
+ const TABLE_HEADERS = [
292
+ { label: "Name", key: "name", sortable: true, className: css.mediumColumn },
293
+ { label: "Tel", key: "phone", sortable: true, className: css.mediumColumn },
294
+ ];
295
+
296
+ const TABLE_DATA = [
297
+ {
298
+ id: "1",
299
+ name: "Alice",
300
+ phone: "123-456-7890",
301
+ },
302
+ {
303
+ id: "2",
304
+ name: "Bob",
305
+ phone: "987-654-3210",
306
+ },
307
+ ];
308
+
309
+ export const _TableExample = (): React.Node => {
310
+ return (
311
+ <div className={css.container}>
312
+ {/* Top Bar with Filters */}
313
+ <TableTopBar>
314
+ <div className={css.left}>
315
+ <SubTitleMedium className={css.title}>Basic Data Table</SubTitleMedium>
316
+ <SearchInput />
317
+ </div>
318
+ </TableTopBar>
319
+
320
+ {/* Table */}
321
+ <Table
322
+ headers={TABLE_HEADERS}
323
+ entries={TABLE_DATA}
324
+ isLoading={isLoading}
325
+ stickyHeader={true}
326
+ borderRadius={'0'}
327
+ selectedKeys={staticTableKeysAcrossPages[currPage] || []}
328
+ onSelect={(keys) => {
329
+ setSelectAllEntries(false);
330
+ setStaticTableKeysAcrossPages({
331
+ ...staticTableKeysAcrossPages,
332
+ [currPage]: keys,
333
+ });
334
+ }}
335
+ classNames={{
336
+ wrapper: classify(css.tableWrapper, {
337
+ [css.withActionBar]: getNumberOfEntriesSelected() > 0,
338
+ })
339
+ }}
340
+ />
341
+
342
+ {/* Table Actions */}
343
+ {getNumberOfEntriesSelected() > 0 && (
344
+ <TableActionBar className={css.actionBar}>
345
+ <div className={css.leftActionSlot}>
346
+ <ButtonCta
347
+ onClick={() => {
348
+ setSelectAllEntries(true);
349
+ setStaticTableKeysAcrossPages({
350
+ ...staticTableKeysAcrossPages,
351
+ [currPage]: tableEntries.map((entry) => entry.id),
352
+ });
353
+ }}
354
+ >
355
+ Select all items
356
+ </ButtonCta>
357
+ {getNumberOfEntriesSelected() > 0 && (
358
+ <BodySmall color="inversePrimary">
359
+ {getNumberOfEntriesSelected() entries selected}
360
+ </BodySmall>
361
+ )}
362
+ </div>
363
+ <div className={classify(css.middleActionSlot, css.fullTable)}>
364
+ <ButtonCta size="small" iconLeftName="folder-plus">
365
+ Add to group
366
+ </ButtonCta>
367
+ </div>
368
+ <div className={classify(css.middleActionSlot, css.smallTable)}>
369
+ <ButtonCta size="small" iconLeftName="folder-plus">
370
+ Add to group
371
+ </ButtonCta>
372
+ <DropdownCta
373
+ ariaLabel="Icon Button Dropdown"
374
+ iconLeftName="ellipsis"
375
+ options={[
376
+ {
377
+ iconLeft: 'layer-plus',
378
+ key: '1',
379
+ label: 'Create group',
380
+ },
381
+ ]}
382
+ selectedKeys={selectedKeys}
383
+ onOptionSelect={() => setSelectedKeys([])}
384
+ size="small"
385
+ />
386
+ </div>
387
+ <div className={css.rightActionSlot}>
388
+ <ButtonCta
389
+ iconLeftName="close"
390
+ onClick={() => {
391
+ setSelectAllEntries(false);
392
+ setStaticTableKeysAcrossPages({});
393
+ }}
394
+ >
395
+ Close
396
+ </ButtonCta>
397
+ </div>
398
+ </TableActionBar>
399
+ )}
400
+
401
+ {/* Bottom Bar with Pagination */}
402
+ <TableBottomBar>
403
+ <Pagination
404
+ currentPage={currPage}
405
+ totalPages={totalPages}
406
+ onChange={(page) => {
407
+ onMove(page || 1);
408
+ }}
409
+ >
410
+ <SubTitleExtraSmall color="secondary">
411
+ Showing page {currPage} of {totalPages}
412
+ </SubTitleExtraSmall>
413
+ </Pagination>
414
+ </TableBottomBar>
415
+ </div>
416
+ );
417
+ };
418
+ \`\`\`
419
+
420
+ \`\`\`css
421
+ @value actionBarHeight: size50;
422
+
423
+ .container {
424
+ width: sizeFluid;
425
+ }
426
+
427
+ .left {
428
+ display: flex;
429
+ align-items: center;
430
+ }
431
+
432
+ .title {
433
+ margin-right: spaceMedium;
434
+ }
435
+
436
+ .mediumColumn {
437
+ width: calc(sizeFluid / 5);
438
+ }
439
+
440
+ .tableWrapper {
441
+ height: calc(sizeFullViewportHeight - size160);
442
+ }
443
+
444
+ .actionBar {
445
+ display: flex;
446
+ align-items: center;
447
+ gap: spaceXSmall;
448
+ }
449
+
450
+ .tableWrapper.withActionBar {
451
+ height: calc((sizeFullViewportHeight - size160) - actionBarHeight);
452
+ }
453
+
454
+ .leftActionSlot,
455
+ .rightActionSlot,
456
+ .middleActionSlot {
457
+ display: flex;
458
+ gap: spaceSmall;
459
+ align-items: center;
460
+ }
461
+
462
+ .leftActionSlot,
463
+ .rightActionSlot {
464
+ width: calc(sizeFluid / 4);
465
+ }
466
+
467
+ .leftActionSlot {
468
+ justify-content: flex-start;
469
+ }
470
+
471
+ .middleActionSlot {
472
+ width: calc(sizeFluid / 2);
473
+ justify-content: center;
474
+ gap: spaceXXSmall;
475
+ }
476
+
477
+ .rightActionSlot {
478
+ justify-content: flex-end;
479
+ }
480
+
481
+ .smallTable {
482
+ display: none;
483
+ }
484
+
485
+ @media only screen and (max-width: size1280) {
486
+ .fullTable {
487
+ display: none;
488
+ }
489
+
490
+ .smallTable {
491
+ display: flex;
492
+ }
493
+ }
494
+
495
+ .dangerText {
496
+ color: colorTextDanger;
497
+ }
498
+
499
+ .dangerText:hover,
500
+ .dangerText:active,
501
+ .dangerText:focus {
502
+ color: colorTextDanger;
503
+ }
504
+
505
+ \`\`\`
506
+ `
507
+ }
508
+ },
509
+ storySource: {
510
+ componentPath: '/src/components/Table/Table.js'
511
+ }
512
+ }
513
+ };
514
+ exports.TABLE_DOCS = TABLE_DOCS;