@popsure/dirty-swan 0.26.8 → 0.26.11

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.
Files changed (233) hide show
  1. package/dist/index.css +1 -0
  2. package/dist/index.css.map +1 -1
  3. package/dist/lib/scss/private/components/_buttons.scss +2 -0
  4. package/package.json +2 -1
  5. package/src/App.tsx +50 -0
  6. package/src/bin/index.ts +71 -0
  7. package/src/bin/tsconfig.json +13 -0
  8. package/src/bin/util/index.test.ts +85 -0
  9. package/src/bin/util/index.ts +132 -0
  10. package/src/bin/util/test/data.json +13 -0
  11. package/src/colors.scss +1 -0
  12. package/src/font-weight.scss +1 -0
  13. package/src/grid.scss +1 -0
  14. package/src/index.tsx +37 -0
  15. package/src/intro.stories.mdx +41 -0
  16. package/src/lib/components/autocompleteAddress/demo.tsx +38 -0
  17. package/src/lib/components/autocompleteAddress/index.stories.mdx +44 -0
  18. package/src/lib/components/autocompleteAddress/index.tsx +316 -0
  19. package/src/lib/components/autocompleteAddress/mapStyle.ts +187 -0
  20. package/src/lib/components/autocompleteAddress/style.module.scss +82 -0
  21. package/src/lib/components/autocompleteAddress/util/index.test.ts +51 -0
  22. package/src/lib/components/autocompleteAddress/util/index.ts +55 -0
  23. package/src/lib/components/button/icons/index.ts +14 -0
  24. package/src/lib/components/button/icons/send-purple.svg +4 -0
  25. package/src/lib/components/button/icons/send-white.svg +4 -0
  26. package/src/lib/components/button/index.stories.mdx +121 -0
  27. package/src/lib/components/button/index.tsx +64 -0
  28. package/src/lib/components/button/styles.module.scss +5 -0
  29. package/src/lib/components/cards/a.stories.mdx +44 -0
  30. package/src/lib/components/cards/cardButton/index.stories.mdx +47 -0
  31. package/src/lib/components/cards/cardButton/index.tsx +61 -0
  32. package/src/lib/components/cards/cardButton/style.module.scss +33 -0
  33. package/src/lib/components/cards/cardWithLeftIcon/index.stories.mdx +103 -0
  34. package/src/lib/components/cards/cardWithLeftIcon/index.tsx +87 -0
  35. package/src/lib/components/cards/cardWithLeftIcon/style.module.scss +23 -0
  36. package/src/lib/components/cards/cardWithTopIcon/index.stories.mdx +105 -0
  37. package/src/lib/components/cards/cardWithTopIcon/index.tsx +60 -0
  38. package/src/lib/components/cards/cardWithTopIcon/style.module.scss +10 -0
  39. package/src/lib/components/cards/cardWithTopLeftIcon/index.stories.mdx +101 -0
  40. package/src/lib/components/cards/cardWithTopLeftIcon/index.tsx +72 -0
  41. package/src/lib/components/cards/cardWithTopLeftIcon/style.module.scss +21 -0
  42. package/src/lib/components/cards/icons/arrow-right.svg +4 -0
  43. package/src/lib/components/cards/icons/chevron-right.svg +3 -0
  44. package/src/lib/components/cards/icons/feather-logo.svg +10 -0
  45. package/src/lib/components/cards/icons/index.ts +36 -0
  46. package/src/lib/components/cards/icons/info.svg +12 -0
  47. package/src/lib/components/cards/index.test.ts +37 -0
  48. package/src/lib/components/cards/index.tsx +57 -0
  49. package/src/lib/components/cards/infoCard/index.stories.mdx +61 -0
  50. package/src/lib/components/cards/infoCard/index.tsx +47 -0
  51. package/src/lib/components/cards/infoCard/style.module.scss +53 -0
  52. package/src/lib/components/chip/icons/remove-button-highlighted.svg +4 -0
  53. package/src/lib/components/chip/icons/remove-button.svg +4 -0
  54. package/src/lib/components/chip/index.stories.mdx +101 -0
  55. package/src/lib/components/chip/index.tsx +38 -0
  56. package/src/lib/components/chip/style.module.scss +54 -0
  57. package/src/lib/components/comparisonTable/components/Chevron.tsx +19 -0
  58. package/src/lib/components/comparisonTable/components/Row/index.tsx +68 -0
  59. package/src/lib/components/comparisonTable/components/Row/style.module.scss +114 -0
  60. package/src/lib/components/comparisonTable/components/TableArrows/Arrow.tsx +19 -0
  61. package/src/lib/components/comparisonTable/components/TableArrows/index.tsx +52 -0
  62. package/src/lib/components/comparisonTable/components/TableArrows/style.module.scss +53 -0
  63. package/src/lib/components/comparisonTable/components/TableInfoButton/index.tsx +37 -0
  64. package/src/lib/components/comparisonTable/components/TableInfoButton/style.module.scss +30 -0
  65. package/src/lib/components/comparisonTable/components/TableRating/StarIcon.tsx +13 -0
  66. package/src/lib/components/comparisonTable/components/TableRating/ZapIcon.tsx +17 -0
  67. package/src/lib/components/comparisonTable/components/TableRating/index.tsx +45 -0
  68. package/src/lib/components/comparisonTable/components/TableRating/style.module.scss +13 -0
  69. package/src/lib/components/comparisonTable/components/TableRowHeader/index.tsx +35 -0
  70. package/src/lib/components/comparisonTable/components/TableRowHeader/style.module.scss +3 -0
  71. package/src/lib/components/comparisonTable/components/TableTrueFalse.tsx +40 -0
  72. package/src/lib/components/comparisonTable/hooks/useActiveTableArrows.ts +63 -0
  73. package/src/lib/components/comparisonTable/index.stories.mdx +254 -0
  74. package/src/lib/components/comparisonTable/index.tsx +211 -0
  75. package/src/lib/components/comparisonTable/style.module.scss +104 -0
  76. package/src/lib/components/dateSelector/datepicker.scss +406 -0
  77. package/src/lib/components/dateSelector/icons/calendar.svg +6 -0
  78. package/src/lib/components/dateSelector/icons/chevron-left.svg +3 -0
  79. package/src/lib/components/dateSelector/icons/chevron-right.svg +3 -0
  80. package/src/lib/components/dateSelector/index.stories.mdx +62 -0
  81. package/src/lib/components/dateSelector/index.test.ts +33 -0
  82. package/src/lib/components/dateSelector/index.tsx +247 -0
  83. package/src/lib/components/dateSelector/style.module.scss +77 -0
  84. package/src/lib/components/downloadButton/icons/check.svg +3 -0
  85. package/src/lib/components/downloadButton/icons/download.svg +5 -0
  86. package/src/lib/components/downloadButton/index.stories.mdx +59 -0
  87. package/src/lib/components/downloadButton/index.tsx +67 -0
  88. package/src/lib/components/downloadButton/style.module.scss +19 -0
  89. package/src/lib/components/downloadRing/icons/check-outside-circle.tsx +26 -0
  90. package/src/lib/components/downloadRing/icons/download-cloud.tsx +18 -0
  91. package/src/lib/components/downloadRing/icons/style.module.scss +7 -0
  92. package/src/lib/components/downloadRing/index.stories.mdx +35 -0
  93. package/src/lib/components/downloadRing/index.tsx +79 -0
  94. package/src/lib/components/downloadRing/style.module.scss +66 -0
  95. package/src/lib/components/dropzone/images/error.tsx +18 -0
  96. package/src/lib/components/dropzone/images/file.tsx +26 -0
  97. package/src/lib/components/dropzone/images/style.module.scss +7 -0
  98. package/src/lib/components/dropzone/images/upload-complete.tsx +24 -0
  99. package/src/lib/components/dropzone/images/upload.tsx +18 -0
  100. package/src/lib/components/dropzone/index.stories.mdx +44 -0
  101. package/src/lib/components/dropzone/index.tsx +152 -0
  102. package/src/lib/components/dropzone/style.module.scss +90 -0
  103. package/src/lib/components/input/a.stories.mdx +28 -0
  104. package/src/lib/components/input/autoSuggestInput/index.stories.mdx +137 -0
  105. package/src/lib/components/input/autoSuggestInput/index.tsx +81 -0
  106. package/src/lib/components/input/autoSuggestInput/style.module.scss +71 -0
  107. package/src/lib/components/input/autoSuggestMultiSelect/index.stories.mdx +115 -0
  108. package/src/lib/components/input/autoSuggestMultiSelect/index.tsx +75 -0
  109. package/src/lib/components/input/autoSuggestMultiSelect/style.module.scss +4 -0
  110. package/src/lib/components/input/currency/format/index.test.ts +49 -0
  111. package/src/lib/components/input/currency/format/index.ts +15 -0
  112. package/src/lib/components/input/currency/index.stories.mdx +25 -0
  113. package/src/lib/components/input/currency/index.test.tsx +56 -0
  114. package/src/lib/components/input/currency/index.tsx +53 -0
  115. package/src/lib/components/input/iban/formatIban/index.test.ts +11 -0
  116. package/src/lib/components/input/iban/formatIban/index.ts +22 -0
  117. package/src/lib/components/input/iban/index.stories.mdx +21 -0
  118. package/src/lib/components/input/iban/index.tsx +20 -0
  119. package/src/lib/components/input/index.stories.mdx +62 -0
  120. package/src/lib/components/input/index.tsx +51 -0
  121. package/src/lib/components/input/style.module.scss +94 -0
  122. package/src/lib/components/modal/bottomModal/img/close.svg +4 -0
  123. package/src/lib/components/modal/bottomModal/index.tsx +68 -0
  124. package/src/lib/components/modal/bottomModal/style.module.scss +104 -0
  125. package/src/lib/components/modal/bottomOrRegularModal/index.tsx +43 -0
  126. package/src/lib/components/modal/bottomOrRegularModal/style.module.scss +16 -0
  127. package/src/lib/components/modal/hooks/useOnClose.ts +51 -0
  128. package/src/lib/components/modal/index.stories.mdx +316 -0
  129. package/src/lib/components/modal/index.ts +14 -0
  130. package/src/lib/components/modal/regularModal/img/close.svg +4 -0
  131. package/src/lib/components/modal/regularModal/index.tsx +55 -0
  132. package/src/lib/components/modal/regularModal/style.module.scss +106 -0
  133. package/src/lib/components/multiDropzone/UploadFileCell/index.tsx +138 -0
  134. package/src/lib/components/multiDropzone/UploadFileCell/style.module.scss +101 -0
  135. package/src/lib/components/multiDropzone/icons/bmp-complete.svg +10 -0
  136. package/src/lib/components/multiDropzone/icons/bmp.svg +10 -0
  137. package/src/lib/components/multiDropzone/icons/doc-complete.svg +11 -0
  138. package/src/lib/components/multiDropzone/icons/doc.svg +11 -0
  139. package/src/lib/components/multiDropzone/icons/docx-complete.svg +12 -0
  140. package/src/lib/components/multiDropzone/icons/docx.svg +12 -0
  141. package/src/lib/components/multiDropzone/icons/eye.svg +4 -0
  142. package/src/lib/components/multiDropzone/icons/generic-complete.svg +4 -0
  143. package/src/lib/components/multiDropzone/icons/generic-error.svg +7 -0
  144. package/src/lib/components/multiDropzone/icons/generic.svg +4 -0
  145. package/src/lib/components/multiDropzone/icons/heic-complete.svg +11 -0
  146. package/src/lib/components/multiDropzone/icons/heic.svg +11 -0
  147. package/src/lib/components/multiDropzone/icons/index.ts +51 -0
  148. package/src/lib/components/multiDropzone/icons/jpeg-complete.svg +11 -0
  149. package/src/lib/components/multiDropzone/icons/jpeg.svg +11 -0
  150. package/src/lib/components/multiDropzone/icons/jpg-complete.svg +10 -0
  151. package/src/lib/components/multiDropzone/icons/jpg.svg +10 -0
  152. package/src/lib/components/multiDropzone/icons/pdf-complete.svg +8 -0
  153. package/src/lib/components/multiDropzone/icons/pdf.svg +8 -0
  154. package/src/lib/components/multiDropzone/icons/png-complete.svg +10 -0
  155. package/src/lib/components/multiDropzone/icons/png.svg +10 -0
  156. package/src/lib/components/multiDropzone/icons/trash.svg +6 -0
  157. package/src/lib/components/multiDropzone/icons/upload.svg +5 -0
  158. package/src/lib/components/multiDropzone/index.stories.mdx +91 -0
  159. package/src/lib/components/multiDropzone/index.tsx +99 -0
  160. package/src/lib/components/multiDropzone/style.module.scss +32 -0
  161. package/src/lib/components/segmentedControl/index.stories.mdx +47 -0
  162. package/src/lib/components/segmentedControl/index.tsx +105 -0
  163. package/src/lib/components/segmentedControl/style.module.scss +36 -0
  164. package/src/lib/components/signaturePad/img/reset.svg +4 -0
  165. package/src/lib/components/signaturePad/img/sign.svg +3 -0
  166. package/src/lib/components/signaturePad/index.stories.mdx +17 -0
  167. package/src/lib/components/signaturePad/index.tsx +96 -0
  168. package/src/lib/components/signaturePad/style.module.scss +90 -0
  169. package/src/lib/index.tsx +71 -0
  170. package/src/lib/models/autoSuggestInput/index.ts +4 -0
  171. package/src/lib/models/download.ts +1 -0
  172. package/src/lib/models/downloadRing/index.ts +6 -0
  173. package/src/lib/scss/index.scss +22 -0
  174. package/src/lib/scss/private/_reset.scss +149 -0
  175. package/src/lib/scss/private/base/_colors.scss +19 -0
  176. package/src/lib/scss/private/base/_cursors.scss +31 -0
  177. package/src/lib/scss/private/base/_display.scss +35 -0
  178. package/src/lib/scss/private/base/_grid.scss +52 -0
  179. package/src/lib/scss/private/base/_index.scss +9 -0
  180. package/src/lib/scss/private/base/_shadows.scss +2 -0
  181. package/src/lib/scss/private/base/_spacing.scss +89 -0
  182. package/src/lib/scss/private/base/_typography.scss +128 -0
  183. package/src/lib/scss/private/base/_width_and_height.scss +25 -0
  184. package/src/lib/scss/private/base/cursors.stories.mdx +18 -0
  185. package/src/lib/scss/private/base/demo.tsx +119 -0
  186. package/src/lib/scss/private/base/display.stories.mdx +19 -0
  187. package/src/lib/scss/private/base/flex/_flex.scss +63 -0
  188. package/src/lib/scss/private/base/flex/flex.stories.mdx +139 -0
  189. package/src/lib/scss/private/base/flex/style.module.scss +24 -0
  190. package/src/lib/scss/private/base/spacing.stories.mdx +173 -0
  191. package/src/lib/scss/private/base/style.module.scss +42 -0
  192. package/src/lib/scss/private/base/typography.stories.mdx +71 -0
  193. package/src/lib/scss/private/base/width_and_height.stories.mdx +172 -0
  194. package/src/lib/scss/private/components/_badge.scss +41 -0
  195. package/src/lib/scss/private/components/_buttons.scss +193 -0
  196. package/src/lib/scss/private/components/_cards.scss +32 -0
  197. package/src/lib/scss/private/components/_index.scss +6 -0
  198. package/src/lib/scss/private/components/_input.scss +241 -0
  199. package/src/lib/scss/private/components/_notices.scss +39 -0
  200. package/src/lib/scss/private/components/_spinner.scss +60 -0
  201. package/src/lib/scss/private/components/assets/checkmark.svg +3 -0
  202. package/src/lib/scss/private/components/assets/icon-form-dropdown.svg +3 -0
  203. package/src/lib/scss/private/components/badge.stories.mdx +37 -0
  204. package/src/lib/scss/private/components/button.stories.mdx +107 -0
  205. package/src/lib/scss/private/components/cards.stories.mdx +35 -0
  206. package/src/lib/scss/private/components/checkbox.stories.mdx +47 -0
  207. package/src/lib/scss/private/components/input.stories.mdx +33 -0
  208. package/src/lib/scss/private/components/notices.stories.mdx +37 -0
  209. package/src/lib/scss/private/components/radio.stories.mdx +47 -0
  210. package/src/lib/scss/private/components/select.stories.mdx +17 -0
  211. package/src/lib/scss/private/components/spinner.stories.mdx +25 -0
  212. package/src/lib/scss/public/colors/_index.scss +2 -0
  213. package/src/lib/scss/public/colors/default.scss +125 -0
  214. package/src/lib/scss/public/colors/overrides.scss +0 -0
  215. package/src/lib/scss/public/colors.stories.mdx +27 -0
  216. package/src/lib/scss/public/demo.tsx +285 -0
  217. package/src/lib/scss/public/font/_index.scss +2 -0
  218. package/src/lib/scss/public/font/default.scss +3 -0
  219. package/src/lib/scss/public/font/overrides.scss +0 -0
  220. package/src/lib/scss/public/font-weight.scss +9 -0
  221. package/src/lib/scss/public/font-weight.stories.mdx +32 -0
  222. package/src/lib/scss/public/grid.scss +21 -0
  223. package/src/lib/scss/public/grid.stories.mdx +41 -0
  224. package/src/lib/scss/third-party/_google_places.scss +62 -0
  225. package/src/lib/scss/third-party/_index.scss +1 -0
  226. package/src/lib/scss/utils/_index.scss +3 -0
  227. package/src/lib/util/calendarDate/index.test.ts +32 -0
  228. package/src/lib/util/calendarDate/index.ts +30 -0
  229. package/src/lib/util/zeroFill.test.ts +15 -0
  230. package/src/lib/util/zeroFill.ts +7 -0
  231. package/src/react-app-env.d.ts +1 -0
  232. package/src/setupTests.js +8 -0
  233. package/src/theme.stories.mdx +54 -0
@@ -0,0 +1,45 @@
1
+ import classNames from 'classnames';
2
+
3
+ import StarIcon from './StarIcon';
4
+ import ZapIcon from './ZapIcon';
5
+
6
+ import styles from './style.module.scss';
7
+
8
+ type RatingTypes = 'star' | 'zap';
9
+
10
+ interface TableRatingProps {
11
+ type: RatingTypes;
12
+ rating: number;
13
+ }
14
+
15
+ const getRatingIcon = (type: RatingTypes) => {
16
+ const iconDictionary = {
17
+ zap: ZapIcon,
18
+ star: StarIcon,
19
+ };
20
+
21
+ return iconDictionary[type] || iconDictionary['star'];
22
+ };
23
+
24
+ const VALID_VALUES = [1, 2, 3];
25
+
26
+ const TableRating = (props: TableRatingProps) => {
27
+ const { rating, type } = props;
28
+ const SelectedIcon = getRatingIcon(type);
29
+
30
+ return (
31
+ <div>
32
+ {VALID_VALUES.map((value) => (
33
+ <SelectedIcon
34
+ key={value}
35
+ className={classNames(
36
+ styles.icon,
37
+ value <= rating ? styles.filled : styles.empty
38
+ )}
39
+ />
40
+ ))}
41
+ </div>
42
+ );
43
+ };
44
+
45
+ export default TableRating;
@@ -0,0 +1,13 @@
1
+ @use "../../../../scss/public/colors" as *;
2
+
3
+ .icon {
4
+ margin-right: 4px;
5
+ }
6
+
7
+ .filled {
8
+ fill: $ds-primary-500;
9
+ }
10
+
11
+ .empty {
12
+ fill: $ds-grey-200;
13
+ }
@@ -0,0 +1,35 @@
1
+ import React from 'react';
2
+ import classNames from 'classnames';
3
+
4
+ import TableInfoButton from '../TableInfoButton';
5
+
6
+ import styles from './style.module.scss';
7
+
8
+ const TableRowHeader = (props: {
9
+ label: string;
10
+ icon?: string;
11
+ subtitle?: string;
12
+ onClickInfo?: () => void;
13
+ }) => {
14
+ const { icon, label, subtitle, onClickInfo } = props;
15
+ return (
16
+ <div className="d-flex">
17
+ <span className={`mr8 ${styles.icon}`}>{icon}</span>
18
+ <div>
19
+ <p className="p-p d-inline">
20
+ <span
21
+ className={classNames({
22
+ mr8: onClickInfo,
23
+ })}
24
+ >
25
+ {label}
26
+ </span>
27
+ {onClickInfo && <TableInfoButton onClick={onClickInfo} />}
28
+ </p>
29
+ {subtitle && <p className="p-p--small tc-grey-500">{subtitle}</p>}
30
+ </div>
31
+ </div>
32
+ );
33
+ };
34
+
35
+ export default TableRowHeader;
@@ -0,0 +1,40 @@
1
+ const TableTrueFalse = ({
2
+ value,
3
+ className = '',
4
+ }: {
5
+ value: boolean;
6
+ className?: string;
7
+ }) => {
8
+ if (value) {
9
+ return (
10
+ <svg
11
+ width="18"
12
+ height="13"
13
+ fill="none"
14
+ xmlns="http://www.w3.org/2000/svg"
15
+ className={className}
16
+ >
17
+ <path
18
+ d="M15.667 1.833L6.5 11 2.333 6.833"
19
+ stroke="#8E8CEE"
20
+ strokeWidth="3"
21
+ strokeLinecap="round"
22
+ strokeLinejoin="round"
23
+ />
24
+ </svg>
25
+ );
26
+ }
27
+
28
+ return (
29
+ <svg width="12" height="12" fill="none" xmlns="http://www.w3.org/2000/svg">
30
+ <path
31
+ fillRule="evenodd"
32
+ clipRule="evenodd"
33
+ d="M11.707 1.707A1 1 0 0010.293.293L6 4.586 1.707.293A1 1 0 00.293 1.707L4.586 6 .293 10.293a1 1 0 101.414 1.414L6 7.414l4.293 4.293a1 1 0 001.414-1.414L7.414 6l4.293-4.293z"
34
+ fill="#D2D2D8"
35
+ />
36
+ </svg>
37
+ );
38
+ };
39
+
40
+ export default TableTrueFalse;
@@ -0,0 +1,63 @@
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import debounce from 'lodash.debounce';
3
+
4
+ export interface ActiveTableArrows {
5
+ left: boolean;
6
+ right: boolean;
7
+ }
8
+
9
+ export const useActiveTableArrows = (): {
10
+ activeArrows: ActiveTableArrows;
11
+ contentContainerRef: React.Ref<HTMLDivElement>;
12
+ contentWrapperRef: React.Ref<HTMLDivElement>;
13
+ } => {
14
+ const [activeArrows, setActiveArrows] = useState<ActiveTableArrows>({
15
+ left: false,
16
+ right: true,
17
+ });
18
+ const contentContainerRef = useRef<HTMLDivElement | null>(null);
19
+ const contentWrapperRef = useRef<HTMLDivElement | null>(null);
20
+
21
+ const handleTableScroll = (e: Event) => {
22
+ const width = window.innerWidth;
23
+
24
+ if (
25
+ e.target === contentWrapperRef.current &&
26
+ contentContainerRef.current &&
27
+ contentContainerRef.current
28
+ ) {
29
+ const left =
30
+ contentContainerRef.current.getBoundingClientRect().left * -1;
31
+
32
+ setActiveArrows({
33
+ left: left > 0,
34
+ right:
35
+ left + width <
36
+ contentContainerRef.current.getBoundingClientRect().width,
37
+ });
38
+ }
39
+ };
40
+
41
+ const debouncedTableScroll = debounce(handleTableScroll, 150);
42
+
43
+ useEffect(() => {
44
+ contentWrapperRef.current?.addEventListener(
45
+ 'scroll',
46
+ debouncedTableScroll,
47
+ {
48
+ passive: true,
49
+ }
50
+ );
51
+
52
+ return contentWrapperRef.current?.removeEventListener(
53
+ 'scroll',
54
+ handleTableScroll
55
+ );
56
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
57
+
58
+ return {
59
+ activeArrows,
60
+ contentContainerRef,
61
+ contentWrapperRef,
62
+ };
63
+ };
@@ -0,0 +1,254 @@
1
+ import { Meta, Preview } from '@storybook/addon-docs/blocks';
2
+
3
+ import { ComparisonTable, TableRating, TableTrueFalse } from '.';
4
+
5
+ <Meta title="JSX/Comparison Table" component={ComparisonTable} />
6
+
7
+ # Comparison Table
8
+
9
+ The Comparison Table component provides an easy way to compare vast amounts of information in a fast and easy way.
10
+
11
+ <Preview>
12
+ <iframe
13
+ width="100%"
14
+ height="450"
15
+ src="https://www.figma.com/embed?embed_host=share&url=https%3A%2F%2Fwww.figma.com%2Ffile%2FMKs4cbojdVOBKUxv7okb93%2FDirty-Swan-Design-System%3Fnode-id%3D2025%253A12194"
16
+ allowFullScreen
17
+ />
18
+ </Preview>
19
+
20
+ ## Arguments
21
+
22
+ | attribute | unit | description | default value | required |
23
+ | ----------- | ------------------- | ---------------------------------------------------------- | ------------- | -------- |
24
+ | headers | [Header[]](#header) | The structure of the table | n/a | true |
25
+ | data | array | The title text that needs to be displayed | n/a | true |
26
+ | hideDetails | boolean | Hide table groups that do not have the `default` attribute | false | false |
27
+
28
+ ## Types
29
+
30
+ ### Cell
31
+
32
+ ```typescript
33
+ export interface CellBaseProps<T> {
34
+ label?: React.ReactNode;
35
+ render?: (value: any, element: T) => React.ReactNode;
36
+ key: Extract<keyof T, string>;
37
+ addonId?: string | number;
38
+ }
39
+ ```
40
+
41
+ ### Header
42
+
43
+ ```typescript
44
+ export interface Header<T> {
45
+ id: number;
46
+ label?: React.ReactNode;
47
+ cells: Array<Cell<T>>;
48
+ default?: boolean;
49
+ }
50
+ ```
51
+
52
+ ## Default example
53
+
54
+ export const ComparisonTableWithData = () => {
55
+ const headers = [
56
+ {
57
+ id: 1,
58
+ cells: [
59
+ {
60
+ id: 1,
61
+ key: 'name',
62
+ label: 'Films',
63
+ },
64
+ {
65
+ id: 2,
66
+ key: 'country',
67
+ label: 'Country',
68
+ },
69
+ {
70
+ id: 4,
71
+ key: 'imdb',
72
+ label: 'IMDB rating',
73
+ },
74
+ {
75
+ id: 3,
76
+ key: 'rating',
77
+ label: 'Our rating',
78
+ render: (value) => <TableRating type="star" rating={value} />,
79
+ },
80
+ {
81
+ id: 5,
82
+ key: 'recommended',
83
+ label: 'Recommended',
84
+ render: (value) => <TableRating type="zap" rating={value} />,
85
+ },
86
+ {
87
+ id: 5,
88
+ key: 'familyFriendly',
89
+ label: 'Family friendly',
90
+ render: (value) => <TableTrueFalse value={value} />,
91
+ },
92
+ {
93
+ id: 6,
94
+ key: 'boxOffice',
95
+ label: 'Box office',
96
+ render: (value) =>
97
+ value.toLocaleString('de-DE', {
98
+ style: 'currency',
99
+ currency: 'EUR',
100
+ }),
101
+ },
102
+ ],
103
+ },
104
+ ];
105
+ const data = [
106
+ {
107
+ id: 1,
108
+ name: 'Pulp Fiction',
109
+ country: 'United States',
110
+ year: '1994',
111
+ rating: 3,
112
+ imdb: 8.9,
113
+ recommended: 3,
114
+ familyFriendly: false,
115
+ boxOffice: 213928762,
116
+ },
117
+ {
118
+ id: 2,
119
+ name: 'Parasite',
120
+ country: 'South Korea',
121
+ year: '2019',
122
+ rating: 2,
123
+ imdb: 8.6,
124
+ recommended: 3,
125
+ familyFriendly: false,
126
+ boxOffice: 355475245,
127
+ },
128
+ {
129
+ id: 3,
130
+ name: 'Spirited Away',
131
+ country: 'Japan',
132
+ year: '2001',
133
+ rating: 3,
134
+ imdb: 8.6,
135
+ recommended: 3,
136
+ familyFriendly: true,
137
+ boxOffice: 258908054,
138
+ },
139
+ ];
140
+ return <ComparisonTable data={data} headers={headers} />;
141
+ };
142
+
143
+ <ComparisonTableWithData />
144
+
145
+ ```typescript jsx
146
+ import ComparisonTable, {
147
+ TableRating,
148
+ TableTrueFalse,
149
+ TableHeader,
150
+ } from '@popsure/dirty-swan';
151
+
152
+ interface TableData {
153
+ id: number;
154
+ name: string;
155
+ country: string;
156
+ year: string;
157
+ rating: number;
158
+ imdb: number;
159
+ recommended: number;
160
+ familyFriendly: boolean;
161
+ boxOffice: number;
162
+ }
163
+
164
+ export const ComparisonTableWithData = () => {
165
+ const headers: Array<TableHeader<TableData>> = [
166
+ {
167
+ id: 1,
168
+ cells: [
169
+ {
170
+ id: 1,
171
+ key: 'name',
172
+ label: 'Films',
173
+ },
174
+ {
175
+ id: 2,
176
+ key: 'country',
177
+ label: 'Country',
178
+ },
179
+ {
180
+ id: 4,
181
+ key: 'imdb',
182
+ label: 'IMDB rating',
183
+ },
184
+ {
185
+ id: 3,
186
+ key: 'rating',
187
+ label: 'Our rating',
188
+ render: (value) => <TableRating type="star" rating={value} />,
189
+ },
190
+ {
191
+ id: 5,
192
+ key: 'recommended',
193
+ label: 'Recommended',
194
+ render: (value) => <TableRating type="zap" rating={value} />,
195
+ },
196
+ {
197
+ id: 5,
198
+ key: 'familyFriendly',
199
+ label: 'Family friendly',
200
+ render: (value) => <TableTrueFalse value={value} />,
201
+ },
202
+ {
203
+ id: 6,
204
+ key: 'boxOffice',
205
+ label: 'Box office',
206
+ render: (value) =>
207
+ value.toLocaleString('de-DE', {
208
+ style: 'currency',
209
+ currency: 'EUR',
210
+ }),
211
+ },
212
+ ],
213
+ },
214
+ ];
215
+ const data: Array<TableData> = [
216
+ {
217
+ id: 1,
218
+ name: 'Pulp Fiction',
219
+ country: 'United States',
220
+ year: '1994',
221
+ rating: 3,
222
+ imdb: 8.9,
223
+ recommended: 3,
224
+ familyFriendly: false,
225
+ boxOffice: 213928762,
226
+ },
227
+ {
228
+ id: 2,
229
+ name: 'Parasite',
230
+ country: 'South Korea',
231
+ year: '2019',
232
+ rating: 2,
233
+ imdb: 8.6,
234
+ recommended: 3,
235
+ familyFriendly: false,
236
+ boxOffice: 355475245,
237
+ },
238
+ {
239
+ id: 3,
240
+ name: 'Spirited Away',
241
+ country: 'Japan',
242
+ year: '2001',
243
+ rating: 3,
244
+ imdb: 8.6,
245
+ recommended: 3,
246
+ familyFriendly: true,
247
+ boxOffice: 258908054,
248
+ },
249
+ ];
250
+ return <ComparisonTable data={data} headers={headers} />;
251
+ };
252
+
253
+ <ComparisonTableWithData />;
254
+ ```
@@ -0,0 +1,211 @@
1
+ import React, { useRef, useState } from 'react';
2
+ import { ScrollSync, ScrollSyncPane } from 'react-scroll-sync';
3
+ import classNames from 'classnames';
4
+
5
+ import Row from './components/Row';
6
+ import TableArrows, { ArrowValues } from './components/TableArrows';
7
+ import TableRating from './components/TableRating';
8
+ import TableTrueFalse from './components/TableTrueFalse';
9
+ import TableRowHeader from './components/TableRowHeader';
10
+ import TableInfoButton from './components/TableInfoButton';
11
+ import Chevron from './components/Chevron';
12
+ import { useActiveTableArrows } from './hooks/useActiveTableArrows';
13
+
14
+ import baseStyles from './style.module.scss';
15
+
16
+ export interface CellBaseProps<T> {
17
+ /** Used to display the row's title */
18
+ label?: React.ReactNode;
19
+ /**
20
+ *
21
+ * @param value The current data value
22
+ * @param element The complete data object
23
+ */
24
+ render?: (value: any, element: T) => React.ReactNode; //TODO: add typing to value param
25
+ }
26
+
27
+ interface CellWithId<T> extends CellBaseProps<T> {
28
+ /** Used when adding component add-ons */
29
+ addonId: string | number;
30
+ }
31
+
32
+ export type Cell<T> =
33
+ | ({ key: Extract<keyof T, string> } & CellBaseProps<T>)
34
+ | ({ key?: undefined } & CellWithId<T>);
35
+
36
+ export interface TableHeader<T> {
37
+ /** Required unique id number for each table group */
38
+ id: number;
39
+ /** Used to display a table group subheader */
40
+ label?: React.ReactNode;
41
+ cells: Array<Cell<T>>;
42
+ default?: boolean;
43
+ }
44
+
45
+ export interface ComparisonTableProps<T> {
46
+ headers: Array<TableHeader<T>>;
47
+ data: Array<T>;
48
+ hideDetails?: boolean;
49
+ styles?: {
50
+ header?: string;
51
+ container?: string;
52
+ };
53
+ }
54
+
55
+ const ComparisonTable = <T extends { id: number }>(
56
+ props: ComparisonTableProps<T>
57
+ ) => {
58
+ const { headers, data, hideDetails, styles } = props;
59
+ const [showMore, setShowMore] = useState<boolean>(false);
60
+ const headerContainerRef = useRef<HTMLDivElement | null>(null);
61
+ const { activeArrows, contentContainerRef, contentWrapperRef } =
62
+ useActiveTableArrows();
63
+
64
+ /** narrow types */
65
+ const headerContainer = headerContainerRef
66
+ ? headerContainerRef.current
67
+ : null;
68
+ const contentWrapper =
69
+ typeof contentWrapperRef === 'object' && contentWrapperRef
70
+ ? contentWrapperRef.current
71
+ : null;
72
+
73
+ const handleArrowsClick = (value: ArrowValues) => {
74
+ if (headerContainerRef.current) {
75
+ headerContainerRef.current.scroll({
76
+ top: 0,
77
+ left:
78
+ value === 'next'
79
+ ? headerContainerRef.current.scrollLeft + window.innerWidth
80
+ : headerContainerRef.current.scrollLeft - window.innerWidth,
81
+ behavior: 'smooth',
82
+ });
83
+ }
84
+ };
85
+
86
+ const toggleMoreRows = async () => {
87
+ if (showMore && headerContainer && contentWrapper) {
88
+ window.scroll(
89
+ 0,
90
+ window.scrollY +
91
+ (contentWrapper.getBoundingClientRect().y -
92
+ headerContainer.getBoundingClientRect().bottom)
93
+ );
94
+ }
95
+ setShowMore(!showMore);
96
+ };
97
+
98
+ return (
99
+ <ScrollSync>
100
+ <div>
101
+ <div className={classNames(baseStyles.header, styles?.header)}>
102
+ <ScrollSyncPane>
103
+ <div className={baseStyles.container} ref={headerContainerRef}>
104
+ <div className={classNames(baseStyles['overflow-container'])}>
105
+ <div className={baseStyles['group-container']}>
106
+ <TableArrows
107
+ onClick={handleArrowsClick}
108
+ active={activeArrows}
109
+ />
110
+ <Row<T>
111
+ key="table-header-row"
112
+ rowId="table-header-row"
113
+ cell={headers[0].cells[0]}
114
+ data={data}
115
+ isRowHeader
116
+ />
117
+ </div>
118
+ </div>
119
+ </div>
120
+ </ScrollSyncPane>
121
+ </div>
122
+ <ScrollSyncPane>
123
+ <div
124
+ className={classNames(baseStyles.container, styles?.container)}
125
+ ref={contentWrapperRef}
126
+ >
127
+ <div
128
+ className={classNames(baseStyles['overflow-container'])}
129
+ ref={contentContainerRef}
130
+ >
131
+ <div className={baseStyles['group-container']}>
132
+ {Array.isArray(headers) &&
133
+ headers
134
+ .filter(
135
+ (headerGroup) =>
136
+ !hideDetails || showMore || headerGroup.default
137
+ )
138
+ .map((headerGroup, headerGroupIndex) => (
139
+ <div key={headerGroup.id}>
140
+ {
141
+ /**
142
+ * Print a table subheader if the `label` value is present
143
+ */
144
+ headerGroup.label && (
145
+ <div className={baseStyles['group-title']}>
146
+ <h4 className={`p-h4 ${baseStyles.sticky}`}>
147
+ {headerGroup.label}
148
+ </h4>
149
+ </div>
150
+ )
151
+ }
152
+
153
+ {headerGroup.cells?.map((cell, index) => {
154
+ const rowId = `${headerGroup.id}-${
155
+ cell.key ?? 'addon'
156
+ }-${index}`;
157
+
158
+ /** Do not render the first row */
159
+ if (index === 0 && headerGroupIndex === 0)
160
+ return null;
161
+
162
+ return (
163
+ <Row<T>
164
+ key={rowId}
165
+ rowId={rowId}
166
+ cell={cell}
167
+ data={data}
168
+ />
169
+ );
170
+ })}
171
+ </div>
172
+ ))}
173
+ {hideDetails && (
174
+ <div
175
+ className={classNames(
176
+ baseStyles['show-details-container'],
177
+ baseStyles.sticky,
178
+ 'mt48'
179
+ )}
180
+ >
181
+ <div>
182
+ <button
183
+ className={`w100 d-flex p-a p-h4 c-pointer ${baseStyles['show-details-button']}`}
184
+ onClick={toggleMoreRows}
185
+ >
186
+ {showMore ? 'Hide details' : 'Show details'}
187
+ <Chevron
188
+ className={
189
+ showMore ? '' : baseStyles['icon-inverted']
190
+ }
191
+ />
192
+ </button>
193
+ </div>
194
+ </div>
195
+ )}
196
+ </div>
197
+ </div>
198
+ </div>
199
+ </ScrollSyncPane>
200
+ </div>
201
+ </ScrollSync>
202
+ );
203
+ };
204
+
205
+ export {
206
+ ComparisonTable,
207
+ TableRating,
208
+ TableTrueFalse,
209
+ TableRowHeader,
210
+ TableInfoButton,
211
+ };