@sats-group/ui-lib 74.2.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.
Files changed (227) hide show
  1. package/.nvmrc +1 -0
  2. package/README.md +35 -0
  3. package/catalog-info.yaml +14 -0
  4. package/eslint.config.mjs +94 -0
  5. package/fonts/Inter-BoldItalic.woff +0 -0
  6. package/fonts/Inter-BoldItalic.woff2 +0 -0
  7. package/fonts/Inter-ExtraBold.woff +0 -0
  8. package/fonts/Inter-ExtraBold.woff2 +0 -0
  9. package/fonts/Inter-Italic.woff +0 -0
  10. package/fonts/Inter-Italic.woff2 +0 -0
  11. package/fonts/Inter-Regular.woff +0 -0
  12. package/fonts/Inter-Regular.woff2 +0 -0
  13. package/fonts/Inter-SemiBold.woff +0 -0
  14. package/fonts/Inter-SemiBold.woff2 +0 -0
  15. package/fonts/LICENSE.txt +92 -0
  16. package/fonts/SATSHeadline-Bold.woff +0 -0
  17. package/fonts/SATSHeadline-BoldItalic.woff +0 -0
  18. package/fonts/SATSHeadline-RegularItalic.woff +0 -0
  19. package/fonts/SATSHeadline-SemiBoldItalic.woff +0 -0
  20. package/logos/e-avatar.svg +3 -0
  21. package/logos/elixia-letter.svg +3 -0
  22. package/logos/elixia-small.svg +8 -0
  23. package/logos/elixia.svg +8 -0
  24. package/logos/s-avatar.svg +3 -0
  25. package/logos/sats-letter.svg +3 -0
  26. package/logos/sats-small.svg +3 -0
  27. package/logos/sats.svg +4 -0
  28. package/package.json +58 -0
  29. package/react/add-bem-modifiers.ts +51 -0
  30. package/react/badge/badge.scss +53 -0
  31. package/react/badge/badge.tsx +28 -0
  32. package/react/badge/badge.types.ts +34 -0
  33. package/react/badge/index.ts +2 -0
  34. package/react/banner/banner.scss +118 -0
  35. package/react/banner/banner.tsx +92 -0
  36. package/react/banner/banner.types.ts +10 -0
  37. package/react/banner/index.ts +2 -0
  38. package/react/bomb/bomb.scss +33 -0
  39. package/react/bomb/bomb.tsx +19 -0
  40. package/react/bomb/bomb.types.ts +1 -0
  41. package/react/bomb/index.ts +2 -0
  42. package/react/button/button.tsx +19 -0
  43. package/react/button/button.types.ts +3 -0
  44. package/react/button/index.ts +2 -0
  45. package/react/checkbox/checkbox.scss +218 -0
  46. package/react/checkbox/checkbox.tsx +176 -0
  47. package/react/checkbox/checkbox.types.ts +19 -0
  48. package/react/checkbox/index.ts +2 -0
  49. package/react/chip/chip.scss +46 -0
  50. package/react/chip/chip.tsx +37 -0
  51. package/react/chip/chip.types.ts +18 -0
  52. package/react/chip/index.ts +2 -0
  53. package/react/chip/remove.tsx +14 -0
  54. package/react/chip-selected/chip-selected.scss +47 -0
  55. package/react/chip-selected/chip-selected.tsx +102 -0
  56. package/react/chip-selected/chip-selected.types.ts +11 -0
  57. package/react/chip-selected/index.ts +2 -0
  58. package/react/confirmation/confirmation.scss +60 -0
  59. package/react/confirmation/confirmation.tsx +85 -0
  60. package/react/confirmation/confirmation.types.ts +24 -0
  61. package/react/confirmation/index.ts +2 -0
  62. package/react/context-menu/context-menu.scss +183 -0
  63. package/react/context-menu/context-menu.tsx +200 -0
  64. package/react/context-menu/context-menu.types.ts +71 -0
  65. package/react/context-menu/index.ts +2 -0
  66. package/react/cropped-image/cropped-image.scss +48 -0
  67. package/react/cropped-image/cropped-image.tsx +36 -0
  68. package/react/cropped-image/cropped-image.types.ts +26 -0
  69. package/react/cropped-image/index.ts +2 -0
  70. package/react/dropdown-list/dropdown-list.scss +170 -0
  71. package/react/dropdown-list/dropdown-list.tsx +116 -0
  72. package/react/dropdown-list/dropdown-list.types.ts +17 -0
  73. package/react/dropdown-list/index.ts +2 -0
  74. package/react/expander/expander.scss +115 -0
  75. package/react/expander/expander.tsx +167 -0
  76. package/react/expander/expander.types.ts +26 -0
  77. package/react/expander/index.ts +2 -0
  78. package/react/filter/filter.scss +94 -0
  79. package/react/filter/filter.tsx +99 -0
  80. package/react/filter/filter.types.ts +8 -0
  81. package/react/filter/index.ts +2 -0
  82. package/react/filter-wrapper/filter-wrapper.scss +46 -0
  83. package/react/filter-wrapper/filter-wrapper.tsx +24 -0
  84. package/react/filter-wrapper/filter-wrapper.types.ts +10 -0
  85. package/react/filter-wrapper/index.ts +2 -0
  86. package/react/flag/flag.scss +26 -0
  87. package/react/flag/flag.tsx +27 -0
  88. package/react/flag/flag.types.ts +17 -0
  89. package/react/flag/index.ts +2 -0
  90. package/react/form-content/checkbox-category.tsx +183 -0
  91. package/react/form-content/form-content.checkbox-list.tsx +126 -0
  92. package/react/form-content/form-content.checkbox-list.types.ts +36 -0
  93. package/react/form-content/form-content.radio-list.tsx +58 -0
  94. package/react/form-content/form-content.range.tsx +20 -0
  95. package/react/form-content/form-content.range.types.ts +14 -0
  96. package/react/form-content/form-content.scss +234 -0
  97. package/react/form-content/form-content.search.tsx +47 -0
  98. package/react/form-content/form-content.tsx +95 -0
  99. package/react/form-content/form-content.types.ts +55 -0
  100. package/react/form-content/index.ts +2 -0
  101. package/react/form-content/types/index.d.ts +1 -0
  102. package/react/hidden-input/hidden-input.tsx +9 -0
  103. package/react/hidden-input/hidden-input.types.ts +6 -0
  104. package/react/hidden-input/index.ts +2 -0
  105. package/react/hooks/focus-previous-element.ts +30 -0
  106. package/react/hooks/is-running-on-client.ts +1 -0
  107. package/react/hooks/use-click-outside.ts +23 -0
  108. package/react/hooks/use-escape.ts +18 -0
  109. package/react/hooks/use-event.ts +29 -0
  110. package/react/hooks/use-is-mounted.ts +11 -0
  111. package/react/hooks/use-toggle.ts +19 -0
  112. package/react/icons/16/close.tsx +12 -0
  113. package/react/icons/18/close.tsx +18 -0
  114. package/react/icons/24/arrow-down.tsx +14 -0
  115. package/react/icons/24/arrow-right.tsx +14 -0
  116. package/react/icons/24/arrow-up.tsx +14 -0
  117. package/react/icons/24/close.tsx +12 -0
  118. package/react/icons/24/remove.tsx +12 -0
  119. package/react/icons/24/search.tsx +10 -0
  120. package/react/icons/icons.md +3 -0
  121. package/react/indexed-access-type.ts +1 -0
  122. package/react/link/index.ts +2 -0
  123. package/react/link/link.scss +44 -0
  124. package/react/link/link.tsx +62 -0
  125. package/react/link/link.types.ts +37 -0
  126. package/react/link-button/index.ts +2 -0
  127. package/react/link-button/link-button.tsx +17 -0
  128. package/react/link-button/link-button.types.ts +5 -0
  129. package/react/link-card/index.ts +2 -0
  130. package/react/link-card/link-card.scss +37 -0
  131. package/react/link-card/link-card.tsx +24 -0
  132. package/react/link-card/link-card.types.ts +5 -0
  133. package/react/logos/e-avatar.tsx +12 -0
  134. package/react/logos/elixia-letter.tsx +12 -0
  135. package/react/logos/elixia-small.tsx +12 -0
  136. package/react/logos/elixia.tsx +12 -0
  137. package/react/logos/index.ts +8 -0
  138. package/react/logos/s-avatar.tsx +12 -0
  139. package/react/logos/sats-letter.tsx +12 -0
  140. package/react/logos/sats-small.tsx +12 -0
  141. package/react/logos/sats.tsx +12 -0
  142. package/react/message/hook/use-message.ts +22 -0
  143. package/react/message/index.ts +2 -0
  144. package/react/message/message.scss +92 -0
  145. package/react/message/message.tsx +60 -0
  146. package/react/message/message.types.ts +39 -0
  147. package/react/message/publish.ts +19 -0
  148. package/react/message-field/index.ts +2 -0
  149. package/react/message-field/message-field.scss +21 -0
  150. package/react/message-field/message-field.tsx +70 -0
  151. package/react/message-field/message-field.types.ts +24 -0
  152. package/react/modal/index.ts +2 -0
  153. package/react/modal/modal.scss +162 -0
  154. package/react/modal/modal.tsx +130 -0
  155. package/react/modal/modal.types.ts +36 -0
  156. package/react/modal/tab-trapper.tsx +68 -0
  157. package/react/progress-bar/index.ts +2 -0
  158. package/react/progress-bar/progress-bar.scss +71 -0
  159. package/react/progress-bar/progress-bar.tsx +81 -0
  160. package/react/progress-bar/progress-bar.types.ts +35 -0
  161. package/react/radio/index.ts +2 -0
  162. package/react/radio/radio.scss +142 -0
  163. package/react/radio/radio.tsx +87 -0
  164. package/react/radio/radio.types.ts +15 -0
  165. package/react/scale-bar/index.ts +2 -0
  166. package/react/scale-bar/scale-bar.scss +22 -0
  167. package/react/scale-bar/scale-bar.tsx +29 -0
  168. package/react/scale-bar/scale-bar.types.ts +4 -0
  169. package/react/search/index.ts +2 -0
  170. package/react/search/search.scss +207 -0
  171. package/react/search/search.tsx +255 -0
  172. package/react/search/search.types.ts +43 -0
  173. package/react/select/chevron-down.tsx +24 -0
  174. package/react/select/index.ts +2 -0
  175. package/react/select/select.scss +135 -0
  176. package/react/select/select.tsx +105 -0
  177. package/react/select/select.types.ts +19 -0
  178. package/react/select-option/README.md +3 -0
  179. package/react/select-option/index.ts +2 -0
  180. package/react/select-option/select-option.tsx +16 -0
  181. package/react/select-option/select-option.types.ts +8 -0
  182. package/react/tag/index.ts +2 -0
  183. package/react/tag/tag.scss +107 -0
  184. package/react/tag/tag.tsx +26 -0
  185. package/react/tag/tag.types.ts +30 -0
  186. package/react/text/index.ts +2 -0
  187. package/react/text/text.scss +109 -0
  188. package/react/text/text.tsx +40 -0
  189. package/react/text/text.types.ts +29 -0
  190. package/react/text-area/index.ts +2 -0
  191. package/react/text-area/text-area.scss +180 -0
  192. package/react/text-area/text-area.tsx +153 -0
  193. package/react/text-area/text-area.types.ts +24 -0
  194. package/react/text-input/index.ts +2 -0
  195. package/react/text-input/text-input.scss +233 -0
  196. package/react/text-input/text-input.tsx +106 -0
  197. package/react/text-input/text-input.types.ts +19 -0
  198. package/react/toggle/index.ts +2 -0
  199. package/react/toggle/toggle.scss +69 -0
  200. package/react/toggle/toggle.tsx +83 -0
  201. package/react/toggle/toggle.types.ts +11 -0
  202. package/react/toolbox/index.ts +2 -0
  203. package/react/toolbox/toolbox.scss +68 -0
  204. package/react/toolbox/toolbox.tsx +43 -0
  205. package/react/toolbox/toolbox.types.ts +39 -0
  206. package/react/ts/debounce.ts +12 -0
  207. package/react/types.ts +38 -0
  208. package/react/use-input-validation.ts +47 -0
  209. package/react/use-input-validation.types.ts +12 -0
  210. package/react/visually-button/index.ts +2 -0
  211. package/react/visually-button/visually-button.scss +470 -0
  212. package/react/visually-button/visually-button.tsx +130 -0
  213. package/react/visually-button/visually-button.types.ts +71 -0
  214. package/react/visually-hidden/index.ts +2 -0
  215. package/react/visually-hidden/visually-hidden.scss +6 -0
  216. package/react/visually-hidden/visually-hidden.tsx +10 -0
  217. package/tokens/corner-radius.scss +5 -0
  218. package/tokens/dark.scss +392 -0
  219. package/tokens/darkmode.scss +131 -0
  220. package/tokens/elevation.scss +57 -0
  221. package/tokens/font-faces.scss +62 -0
  222. package/tokens/font-names.scss +2 -0
  223. package/tokens/font-sizes.scss +95 -0
  224. package/tokens/light.scss +392 -0
  225. package/tokens/lightmode.scss +131 -0
  226. package/tokens/primitives.scss +137 -0
  227. package/tokens/spacing.scss +12 -0
@@ -0,0 +1,19 @@
1
+ import { ObjectValues, InputHtmlProps } from '../types';
2
+
3
+ export const themes = {
4
+ light: 'light',
5
+ blue: 'blue',
6
+ } as const;
7
+
8
+ export type Checkbox = Omit<InputHtmlProps, 'value'> & {
9
+ intermediate?: boolean;
10
+ description?: string;
11
+ extra?: React.ReactNode;
12
+ isLabelVisible?: boolean;
13
+ label?: string;
14
+ name: string;
15
+ value: string;
16
+ showCustomError?: boolean;
17
+ theme?: ObjectValues<typeof themes>;
18
+ isUnderlined?: boolean;
19
+ };
@@ -0,0 +1,2 @@
1
+ import Checkbox from './checkbox';
2
+ export default Checkbox;
@@ -0,0 +1,46 @@
1
+ @use '../../tokens/light';
2
+ @use '../../tokens/spacing';
3
+
4
+ .chip {
5
+ box-sizing: border-box;
6
+ cursor: pointer;
7
+ outline: 0;
8
+ padding: 0;
9
+ text-decoration: none;
10
+ display: inline-block;
11
+ border-radius: 40px; // NOTE: Custom border radius by design
12
+ background-color: transparent;
13
+ border: 1px solid light.$ge-chips-unselected-default;
14
+
15
+ @media (hover: hover) {
16
+ &:not([disabled]):hover {
17
+ background-color: light.$ge-chips-unselected-hover;
18
+ color: light.$on-ge-on-chips-unselected-default;
19
+ }
20
+ }
21
+
22
+ &--selected {
23
+ background-color: light.$ge-chips-selected-default;
24
+ color: light.$on-ge-on-chips-selected-default;
25
+
26
+ @media (hover: hover) {
27
+ &:not([disabled]):hover {
28
+ background-color: light.$ge-chips-selected-hover;
29
+ color: light.$on-ge-on-chips-selected-default;
30
+ }
31
+ }
32
+ }
33
+
34
+ &__wrapper {
35
+ display: flex;
36
+ gap: spacing.$xs;
37
+ align-items: center;
38
+ padding: spacing.$xs spacing.$m;
39
+ }
40
+
41
+ &[disabled] {
42
+ background-color: light.$ge-chips-selected-disabled;
43
+ color: light.$on-ge-on-chips-selected-disabled;
44
+ cursor: not-allowed;
45
+ }
46
+ }
@@ -0,0 +1,37 @@
1
+ import * as React from 'react';
2
+ import cn from 'classnames';
3
+
4
+ import Close from '../icons/16/close';
5
+ import Text from '../text';
6
+
7
+ import { Chip as Props } from './chip.types';
8
+
9
+ const Chip: React.FC<Props> = ({
10
+ elementName = 'button',
11
+ className,
12
+ icon = true,
13
+ isSelected = false,
14
+ text,
15
+ ...rest
16
+ }) =>
17
+ React.createElement(
18
+ elementName,
19
+ {
20
+ className: cn('chip', className, { 'chip--selected': isSelected }),
21
+ ...rest,
22
+ },
23
+ <React.Fragment>
24
+ <div className="chip__wrapper">
25
+ {icon ? <Close /> : null}
26
+ <Text
27
+ className="chip__text"
28
+ size={Text.sizes.small}
29
+ theme={Text.themes.emphasis}
30
+ >
31
+ {text}
32
+ </Text>
33
+ </div>
34
+ </React.Fragment>,
35
+ );
36
+
37
+ export default Chip;
@@ -0,0 +1,18 @@
1
+ import { ButtonHtmlProps, LinkHtmlProps } from '../types';
2
+
3
+ type BasicChipProps = {
4
+ elementName: string;
5
+ className?: string;
6
+ icon?: boolean;
7
+ isSelected?: boolean;
8
+ text: string;
9
+ };
10
+
11
+ export type ChipButton = BasicChipProps & ButtonHtmlProps;
12
+
13
+ export type ChipLink = BasicChipProps & LinkHtmlProps;
14
+
15
+ export type Chip =
16
+ | ({ elementName: 'button' } & ChipButton)
17
+ | ({ elementName: 'a' } & ChipLink)
18
+ | ({ elementName: string } & (ChipLink | ChipButton));
@@ -0,0 +1,2 @@
1
+ import Chip from './chip';
2
+ export default Chip;
@@ -0,0 +1,14 @@
1
+ import * as React from 'react';
2
+
3
+ const SvgRemove = () => (
4
+ <svg width={18} height={19} fill="none" xmlns="http://www.w3.org/2000/svg">
5
+ <path
6
+ fillRule="evenodd"
7
+ clipRule="evenodd"
8
+ d="M9 18.998a9 9 0 1 0 0-18 9 9 0 0 0 0 18Zm3.429-12.429a.808.808 0 0 1 0 1.143l-2.286 2.286 2.286 2.286a.808.808 0 0 1-1.143 1.143L9 11.14l-2.286 2.287a.808.808 0 0 1-1.143-1.143l2.286-2.286-2.286-2.286a.808.808 0 0 1 1.143-1.143L9 8.855l2.286-2.286a.808.808 0 0 1 1.143 0Z"
9
+ fill="currentColor"
10
+ />
11
+ </svg>
12
+ );
13
+
14
+ export default SvgRemove;
@@ -0,0 +1,47 @@
1
+ @use '../../tokens/spacing';
2
+ @use '../../tokens/light';
3
+
4
+ .chip-selected {
5
+ &__selected-options-wrapper {
6
+ > * + * {
7
+ margin-top: spacing.$m;
8
+ }
9
+ }
10
+
11
+ &__collapse {
12
+ margin-top: spacing.$s;
13
+ min-height: 41px !important; // NOTE: This is a hack to display the first line of selected filters above the collapse
14
+ visibility: visible !important;
15
+ }
16
+
17
+ &__selected-options {
18
+ display: flex;
19
+ flex-wrap: wrap;
20
+ gap: spacing.$s;
21
+ }
22
+
23
+ &__clear-all-button {
24
+ display: flex;
25
+ flex-wrap: wrap;
26
+ gap: spacing.$xs;
27
+ }
28
+
29
+ &__expand {
30
+ display: flex;
31
+ align-items: center;
32
+ border: none;
33
+ color: inherit;
34
+ background-color: transparent;
35
+ margin-top: spacing.$m;
36
+ text-decoration: underline;
37
+ color: light.$on-buttons-on-link-default;
38
+
39
+ :hover {
40
+ color: light.$on-buttons-on-link-hover;
41
+ }
42
+ }
43
+
44
+ &__expand-text {
45
+ margin-right: spacing.$xs;
46
+ }
47
+ }
@@ -0,0 +1,102 @@
1
+ import * as React from 'react';
2
+ import Collapse from 'react-tiny-collapse';
3
+
4
+ import ChevronDown from '../icons/24/arrow-down';
5
+ import ChevronUp from '../icons/24/arrow-up';
6
+ import Chip from '../chip/chip';
7
+ import Text from '../text';
8
+
9
+ import useEvent from '../hooks/use-event';
10
+
11
+ import { ChipSelected as Props } from './chip-selected.types';
12
+ import Link from '../link';
13
+
14
+ const ChipSelected: React.FC<Props> = ({
15
+ resetSelected,
16
+ description,
17
+ hideSelected,
18
+ maxChipsToShow = 41,
19
+ showSelected,
20
+ chips,
21
+ }) => {
22
+ const [expandedSelectedValues, setExpandedSelectedValues] =
23
+ React.useState(false);
24
+
25
+ const [heightOfCollapse, setHeightOfCollapse] = React.useState<
26
+ number | undefined
27
+ >(0);
28
+ const divRef = React.useRef<HTMLDivElement>(null);
29
+
30
+ const updateSize = () => {
31
+ if (divRef && divRef.current) {
32
+ setHeightOfCollapse(divRef.current.clientHeight);
33
+ }
34
+ };
35
+
36
+ useEvent('resize', updateSize);
37
+
38
+ React.useEffect(() => {
39
+ if (divRef && divRef.current) {
40
+ setHeightOfCollapse(divRef.current.clientHeight);
41
+ }
42
+ }, [divRef]);
43
+
44
+ const toggleShowMore = () => {
45
+ setExpandedSelectedValues(prevState => !prevState);
46
+ };
47
+
48
+ return chips ? (
49
+ <div className="chip-selected__selected-options-wrapper">
50
+ <div className="chip-selected__clear-all-button">
51
+ <Text>{description}</Text>
52
+ <Link {...resetSelected} />
53
+ </div>
54
+ <div>
55
+ <Collapse
56
+ className="chip-selected__collapse"
57
+ isOpen={expandedSelectedValues}
58
+ >
59
+ <div className="chip-selected__selected-options" ref={divRef}>
60
+ {chips.map(chip => (
61
+ <Chip {...chip} key={chip.text} />
62
+ ))}
63
+ </div>
64
+ </Collapse>
65
+
66
+ {heightOfCollapse && heightOfCollapse > maxChipsToShow ? (
67
+ <button
68
+ type="button"
69
+ className="chip-selected__expand"
70
+ onClick={toggleShowMore}
71
+ >
72
+ {expandedSelectedValues ? (
73
+ <>
74
+ <Text
75
+ elementName="span"
76
+ size={Text.sizes.small}
77
+ className="chip-selected__expand-text"
78
+ >
79
+ {hideSelected}
80
+ </Text>
81
+ <ChevronUp />
82
+ </>
83
+ ) : (
84
+ <>
85
+ <Text
86
+ elementName="span"
87
+ size={Text.sizes.small}
88
+ className="chip-selected__expand-text"
89
+ >
90
+ {showSelected}
91
+ </Text>
92
+ <ChevronDown />
93
+ </>
94
+ )}
95
+ </button>
96
+ ) : null}
97
+ </div>
98
+ </div>
99
+ ) : null;
100
+ };
101
+
102
+ export default ChipSelected;
@@ -0,0 +1,11 @@
1
+ import type { Chip } from '../chip/chip.types';
2
+ import { Link } from '../link/link.types';
3
+
4
+ export type ChipSelected = {
5
+ hideSelected: string;
6
+ maxChipsToShow?: number;
7
+ resetSelected: Link;
8
+ description: string;
9
+ showSelected: string;
10
+ chips?: Chip[];
11
+ };
@@ -0,0 +1,2 @@
1
+ import ChipSelected from './chip-selected';
2
+ export default ChipSelected;
@@ -0,0 +1,60 @@
1
+ @use 'sass:color';
2
+ @use '../../tokens/corner-radius';
3
+ @use '../../tokens/elevation';
4
+ @use '../../tokens/light';
5
+ @use '../../tokens/spacing';
6
+
7
+ .confirmation {
8
+ $padding: spacing.$l;
9
+ background: color.adjust(light.$background-secondary-default, $alpha: -0.7);
10
+ box-sizing: border-box;
11
+ display: grid;
12
+ height: 100%;
13
+ align-items: center;
14
+ justify-items: center;
15
+ left: 0;
16
+ overflow: auto;
17
+ overscroll-behavior: contain;
18
+ padding: $padding;
19
+ position: fixed;
20
+ top: 0;
21
+ width: 100%;
22
+ z-index: 900;
23
+
24
+ &__content {
25
+ max-height: 100%;
26
+ max-width: 375px + (2 * $padding);
27
+ min-width: spacing.$minimum-viewport-width - (2 * $padding);
28
+ }
29
+
30
+ &__title {
31
+ padding: 0;
32
+ margin: 0;
33
+ margin-bottom: $padding;
34
+ }
35
+
36
+ &__text,
37
+ &__children {
38
+ margin-bottom: $padding;
39
+ }
40
+
41
+ &__buttons {
42
+ display: flex;
43
+ justify-content: center;
44
+ flex-wrap: wrap;
45
+ gap: spacing.$m;
46
+ }
47
+
48
+ &__button {
49
+ flex-grow: 1;
50
+ }
51
+
52
+ &__wrapper {
53
+ @include elevation.level(24);
54
+ background: light.$surface-primary-default;
55
+ border-radius: corner-radius.$s;
56
+ color: light.$on-surface-primary-default;
57
+ overflow: auto;
58
+ padding: spacing.$l;
59
+ }
60
+ }
@@ -0,0 +1,85 @@
1
+ import * as React from 'react';
2
+ import * as ReactDOM from 'react-dom';
3
+
4
+ import Button from '../button';
5
+ import LinkButton from '../link-button';
6
+ import TabTrapper from '../modal/tab-trapper';
7
+ import Text from '../text';
8
+ import useEscape from '../hooks/use-escape';
9
+ import { useIsMounted } from '../hooks/use-is-mounted';
10
+
11
+ import { Confirmation as Props } from './confirmation.types';
12
+
13
+ const Confirmation: React.FC<React.PropsWithChildren<Props>> = ({
14
+ children,
15
+ close,
16
+ confirmationButton,
17
+ confirmationLink,
18
+ onClose = () => {},
19
+ onConfirm,
20
+ text,
21
+ title,
22
+ ariaLabel,
23
+ }) => {
24
+ useEscape(onClose);
25
+
26
+ const isMounted = useIsMounted();
27
+
28
+ if (!isMounted) {
29
+ return null;
30
+ }
31
+
32
+ return ReactDOM.createPortal(
33
+ <div className="confirmation" role="dialog" aria-label={ariaLabel}>
34
+ <div className="confirmation__content">
35
+ <TabTrapper isActive={true}>
36
+ <div className="confirmation__wrapper">
37
+ {title ? (
38
+ <Text
39
+ className="confirmation__title"
40
+ elementName="h2"
41
+ theme={Text.themes.emphasis}
42
+ size={Text.sizes.headline3}
43
+ >
44
+ {title}
45
+ </Text>
46
+ ) : undefined}
47
+ {text ? (
48
+ <Text className="confirmation__text">{text}</Text>
49
+ ) : (
50
+ <div className="confirmation__children">{children}</div>
51
+ )}
52
+ <div className="confirmation__buttons">
53
+ {confirmationButton ? (
54
+ <Button
55
+ className="confirmation__button"
56
+ variant={Button.variants.primary}
57
+ {...confirmationButton}
58
+ onClick={onConfirm}
59
+ />
60
+ ) : undefined}
61
+ {confirmationLink ? (
62
+ <LinkButton
63
+ className="confirmation__button"
64
+ variant={LinkButton.variants.primary}
65
+ {...confirmationLink}
66
+ />
67
+ ) : undefined}
68
+ {close ? (
69
+ <Button
70
+ className="confirmation__button"
71
+ variant={Button.variants.secondary}
72
+ {...close}
73
+ onClick={onClose}
74
+ />
75
+ ) : undefined}
76
+ </div>
77
+ </div>
78
+ </TabTrapper>
79
+ </div>
80
+ </div>,
81
+ document.body,
82
+ );
83
+ };
84
+
85
+ export default Confirmation;
@@ -0,0 +1,24 @@
1
+ import { Button } from '../button/button.types';
2
+ import { LinkButton } from '../link-button/link-button.types';
3
+
4
+ type ConfirmationBase = {
5
+ close?: Button;
6
+ onClose?: () => void;
7
+ title?: string;
8
+ text?: string;
9
+ ariaLabel: string;
10
+ };
11
+
12
+ type ConfirmationRedirect = ConfirmationBase & {
13
+ confirmationLink: LinkButton;
14
+ confirmationButton?: never;
15
+ onConfirm?: never;
16
+ };
17
+
18
+ type ConfirmationAction = ConfirmationBase & {
19
+ confirmationLink?: never;
20
+ confirmationButton: Button;
21
+ onConfirm?: () => void;
22
+ };
23
+
24
+ export type Confirmation = ConfirmationRedirect | ConfirmationAction;
@@ -0,0 +1,2 @@
1
+ import Confirmation from './confirmation';
2
+ export default Confirmation;
@@ -0,0 +1,183 @@
1
+ @use '../../tokens/spacing';
2
+ @use '../../tokens/corner-radius';
3
+ @use '../../tokens/elevation';
4
+ @use '../../tokens/light';
5
+
6
+ .context-menu {
7
+ $breakpoint: 600px;
8
+ @include elevation.level(6);
9
+
10
+ background: light.$surface-primary-default;
11
+ position: fixed;
12
+ bottom: 0;
13
+ left: 0;
14
+ border-radius: corner-radius.$s corner-radius.$s 0 0;
15
+ min-width: 300px;
16
+ width: 100%;
17
+ z-index: 100;
18
+ padding: spacing.$s spacing.$m;
19
+ animation: slide 0.3s ease-in;
20
+
21
+ @media (min-width: $breakpoint) {
22
+ position: absolute;
23
+ left: initial;
24
+ transform: none;
25
+ border-radius: corner-radius.$s;
26
+ animation: none;
27
+ bottom: auto;
28
+ width: calc(100% - #{spacing.$m * 2});
29
+
30
+ &--top {
31
+ bottom: calc(100% + #{spacing.$xxs});
32
+ left: 50%;
33
+ transform: translateX(-50%);
34
+ }
35
+
36
+ &--top-right {
37
+ bottom: calc(100% + #{spacing.$xxs});
38
+ right: 0;
39
+ }
40
+
41
+ &--right {
42
+ left: calc(100% + #{spacing.$xxs});
43
+ top: 50%;
44
+ transform: translateY(-50%);
45
+ }
46
+
47
+ &--bottom-right {
48
+ right: 0;
49
+ top: calc(100% + #{spacing.$xxs});
50
+ }
51
+
52
+ &--bottom {
53
+ left: 50%;
54
+ top: calc(100% + #{spacing.$xxs});
55
+ transform: translateX(-50%);
56
+ }
57
+
58
+ &--bottom-left {
59
+ left: 0;
60
+ top: calc(100% + #{spacing.$xxs});
61
+ }
62
+
63
+ &--left {
64
+ right: calc(100% + #{spacing.$xxs});
65
+ top: 50%;
66
+ transform: translateY(-50%);
67
+ }
68
+
69
+ &--top-left {
70
+ left: 0;
71
+ bottom: calc(100% + #{spacing.$xxs});
72
+ }
73
+ }
74
+
75
+ &__background-overlay {
76
+ position: fixed;
77
+ width: 100%;
78
+ height: 100%;
79
+ top: 0;
80
+ left: 0;
81
+ background: light.$fixed-background-secondary-default;
82
+ opacity: 0.2;
83
+ z-index: 99;
84
+
85
+ @media (min-width: $breakpoint) {
86
+ display: none;
87
+ }
88
+ }
89
+
90
+ &__texts-wrapper {
91
+ display: flex;
92
+ justify-content: space-between;
93
+ margin-bottom: spacing.$xs;
94
+
95
+ &--center {
96
+ align-items: center;
97
+ }
98
+ }
99
+
100
+ &__texts {
101
+ color: light.$on-surface-primary-default;
102
+ }
103
+
104
+ &__texts-description {
105
+ color: light.$on-surface-primary-alternate;
106
+ }
107
+
108
+ &__list-items {
109
+ border-top: 1px solid light.$ge-divider-default;
110
+ }
111
+
112
+ &__list-item-link {
113
+ text-decoration: none;
114
+ }
115
+
116
+ &__list-item-form {
117
+ display: flex;
118
+ }
119
+
120
+ &__list-item-button, &__list-item-form-button {
121
+ outline: none;
122
+ border: none;
123
+ background: transparent;
124
+ padding: 0;
125
+ width: 100%;
126
+ }
127
+
128
+ &__list-icon {
129
+ margin-right: spacing.$s;
130
+ display: flex;
131
+ align-items: center;
132
+ color: light.$on-surface-primary-disabled;
133
+
134
+ &--theme-destructive {
135
+ color: light.$on-buttons-on-destructive-outlined-default;
136
+
137
+ &:hover {
138
+ color: light.$on-buttons-on-destructive-outlined-hover;
139
+ }
140
+ }
141
+ }
142
+
143
+ &__list-item-link,
144
+ &__list-item-button,
145
+ &__list-item-form {
146
+ padding: spacing.$s spacing.$xs;
147
+ }
148
+
149
+ &__list-item-link,
150
+ &__list-item-button,
151
+ &__list-item-form-button,
152
+ &__list-item-form {
153
+ display: flex;
154
+ align-items: center;
155
+ cursor: pointer;
156
+ color: light.$on-surface-primary-default;
157
+ border-radius: corner-radius.$s;
158
+ text-align: left;
159
+
160
+ &:hover {
161
+ background-color: light.$surface-primary-hover;
162
+ }
163
+
164
+ &--theme-destructive {
165
+ color: light.$on-buttons-on-destructive-outlined-default;
166
+
167
+ &:hover {
168
+ background-color: light.$buttons-destructive-outlined-hover;
169
+ color: light.$on-buttons-on-destructive-outlined-hover;
170
+ }
171
+ }
172
+ }
173
+ }
174
+
175
+ @keyframes slide {
176
+ from {
177
+ bottom: -100%;
178
+ }
179
+
180
+ to {
181
+ bottom: 0;
182
+ }
183
+ }