@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,47 @@
1
+ import FuzzySearch from 'fuzzy-search';
2
+ import * as React from 'react';
3
+ import Search from '../search';
4
+ import { Search as Props, SearchItem } from './form-content.types';
5
+
6
+ const FormContentSearch: React.FC<Props> = ({
7
+ searchInput,
8
+ searchKeys,
9
+ searchDataset,
10
+ searchTerm = () => {},
11
+ searchResult = () => {},
12
+ }) => {
13
+ const [term, setTerm] = React.useState('');
14
+ const searchOptions = { sort: true };
15
+
16
+ const search = React.useMemo(() => {
17
+ const searcher = new FuzzySearch<SearchItem>(
18
+ searchDataset,
19
+ searchKeys,
20
+ searchOptions,
21
+ );
22
+ return (term: string) => searcher.search(term);
23
+ }, [searchDataset]);
24
+
25
+ const searchResults = React.useMemo(
26
+ () => (term.length > 0 ? search(term).slice(0, 5) : []),
27
+ [search, term],
28
+ );
29
+
30
+ React.useEffect(() => {
31
+ searchResult(searchResults);
32
+ searchTerm(term);
33
+ });
34
+
35
+ return (
36
+ <div className="form-content__search">
37
+ <Search
38
+ {...searchInput}
39
+ inputSize={Search.inputSizes.small}
40
+ isLabelVisible={false}
41
+ onChangeFunc={string => setTerm(string)}
42
+ />
43
+ </div>
44
+ );
45
+ };
46
+
47
+ export default FormContentSearch;
@@ -0,0 +1,95 @@
1
+ import * as React from 'react';
2
+ import classNames from 'classnames';
3
+
4
+ import Button from '../button';
5
+ import Close from '../icons/24/close';
6
+ import Text from '../text';
7
+
8
+ import CheckboxList from './form-content.checkbox-list';
9
+ import RadioList from './form-content.radio-list';
10
+ import Range from './form-content.range';
11
+
12
+ import { FormContent as Props } from './form-content.types';
13
+
14
+ import { ContentKeys } from '../form-content/form-content.types';
15
+ import TabTrapper from '../modal/tab-trapper';
16
+
17
+ const Content = (content: Props['content']) => {
18
+ switch (content.key) {
19
+ case ContentKeys.Checkbox: {
20
+ return <CheckboxList {...content} />;
21
+ }
22
+ case ContentKeys.Range: {
23
+ return <Range {...content} />;
24
+ }
25
+ case ContentKeys.Radio: {
26
+ return <RadioList {...content} />;
27
+ }
28
+ }
29
+ };
30
+
31
+ const FormContent: React.FC<Props> = ({
32
+ content,
33
+ closeLabel,
34
+ isOpen = false,
35
+ close,
36
+ onSubmit,
37
+ submitLabel,
38
+ title,
39
+ }) => (
40
+ <div className="form-content">
41
+ {isOpen ? <div className="form-content__background-overlay"></div> : null}
42
+ <TabTrapper isActive={isOpen}>
43
+ <div
44
+ className={classNames('form-content__modal', {
45
+ 'form-content__modal--open': isOpen,
46
+ })}
47
+ data-test-modal
48
+ >
49
+ <div className="form-content__modal-header">
50
+ <Text
51
+ elementName="span"
52
+ theme={Text.themes.emphasis}
53
+ size={Text.sizes.headline3}
54
+ className="form-content__modal-title"
55
+ >
56
+ {title}
57
+ </Text>
58
+ <div className="form-content__modal-close-small">
59
+ <Button
60
+ variant={Button.variants.secondary}
61
+ onClick={close}
62
+ size={Button.sizes.small}
63
+ leadingIcon={<Close />}
64
+ ariaLabel={closeLabel}
65
+ />
66
+ </div>
67
+ </div>
68
+ <div className="form-content__modal-content">{Content(content)}</div>
69
+ <div className="form-content__modal-action-wrapper">
70
+ <div className="form-content__modal-actions">
71
+ <div className="form-content__modal-close-large">
72
+ <Button
73
+ data-testid="close-form-content-modal"
74
+ variant={Button.variants.secondary}
75
+ size={Button.sizes.basic}
76
+ text={closeLabel}
77
+ onClick={close}
78
+ />
79
+ </div>
80
+ <Button
81
+ className="form-content__submit"
82
+ variant={Button.variants.primary}
83
+ size={Button.sizes.basic}
84
+ type="submit"
85
+ text={submitLabel}
86
+ onClick={onSubmit}
87
+ />
88
+ </div>
89
+ </div>
90
+ </div>
91
+ </TabTrapper>
92
+ </div>
93
+ );
94
+
95
+ export default FormContent;
@@ -0,0 +1,55 @@
1
+ import type { Radio } from '../radio/radio.types';
2
+ import type { Range } from './form-content.range.types';
3
+ import type { Search as SearchProps } from '../search/search.types';
4
+ import type { CheckboxList } from './form-content.checkbox-list.types';
5
+
6
+ export enum ContentKeys {
7
+ Radio = 'radio',
8
+ Range = 'range',
9
+ Checkbox = 'checkbox',
10
+ }
11
+
12
+ export type SearchItem = { key: string; label: string };
13
+
14
+ export type Search = {
15
+ searchKeys: string[];
16
+ searchDataset: SearchItem[];
17
+ searchInput: SearchProps;
18
+ searchTerm?: (arg: string) => void;
19
+ searchResult?: (arg: SearchItem[]) => void;
20
+ };
21
+
22
+ type RadioListBase = {
23
+ key: ContentKeys.Radio;
24
+ categories: {
25
+ header?: string;
26
+ options: Radio[];
27
+ id: string;
28
+ }[];
29
+ setSelectedValue?: (id: string, value: string) => void;
30
+ };
31
+
32
+ type RadioListSearch = RadioListBase & {
33
+ searchProps: Search;
34
+ };
35
+
36
+ export type RadioList = RadioListBase | RadioListSearch;
37
+
38
+ type FormContentBase = {
39
+ closeLabel: string;
40
+ isOpen?: boolean;
41
+ close?: () => void;
42
+ onSubmit?: () => void;
43
+ submitLabel: string;
44
+ title: string;
45
+ };
46
+
47
+ export type FormContentDropdownList = FormContentBase & {
48
+ content: RadioList;
49
+ };
50
+
51
+ export type FormContentFilter = FormContentBase & {
52
+ content: Range | CheckboxList | RadioList;
53
+ };
54
+
55
+ export type FormContent = FormContentDropdownList | FormContentFilter;
@@ -0,0 +1,2 @@
1
+ import FormContent from './form-content';
2
+ export default FormContent;
@@ -0,0 +1 @@
1
+ declare module 'react-tiny-collapse';
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+
3
+ import { HiddenInput as Props } from './hidden-input.types';
4
+
5
+ const HiddenInput: React.FunctionComponent<Props> = props => (
6
+ <input type="hidden" {...props} />
7
+ );
8
+
9
+ export default HiddenInput;
@@ -0,0 +1,6 @@
1
+ import type { InputHtmlProps } from '../types';
2
+
3
+ export type HiddenInput = {
4
+ name: string;
5
+ value: string;
6
+ } & InputHtmlProps;
@@ -0,0 +1,2 @@
1
+ import HiddenInput from './hidden-input';
2
+ export default HiddenInput;
@@ -0,0 +1,30 @@
1
+ import { RefObject, useEffect, useRef } from 'react';
2
+
3
+ const focusRef = (ref: React.MutableRefObject<HTMLElement | null>): void => {
4
+ ref?.current?.focus();
5
+ };
6
+
7
+ const focusPreviousElement = (
8
+ modal: RefObject<HTMLElement>,
9
+ isActive?: boolean,
10
+ ) => {
11
+ const previouslyFocusedElement = useRef<HTMLElement | null>(null);
12
+
13
+ const focusContent = () => focusRef(modal);
14
+ useEffect(() => {
15
+ if (modal.current && isActive) {
16
+ previouslyFocusedElement.current = document.activeElement as HTMLElement;
17
+ focusContent();
18
+ return;
19
+ }
20
+
21
+ if (!isActive) {
22
+ focusRef(previouslyFocusedElement);
23
+ }
24
+ }, [isActive]);
25
+
26
+ // NOTE: This happens on unmount only
27
+ useEffect(() => () => focusRef(previouslyFocusedElement), []);
28
+ };
29
+
30
+ export default focusPreviousElement;
@@ -0,0 +1 @@
1
+ export default typeof window !== 'undefined';
@@ -0,0 +1,23 @@
1
+ import * as React from 'react';
2
+
3
+ import useEvent from './use-event';
4
+
5
+ /** Executes the provided callback when a click event occurs outside of the provided ref */
6
+ const useClickOutside = (
7
+ ref: React.RefObject<Element | null>,
8
+ callback: () => void = () => {},
9
+ ): void => {
10
+ useEvent<MouseEvent>('mousedown', e => {
11
+ if (
12
+ ref.current &&
13
+ ref?.current !== e.target &&
14
+ // NOTE: Because of react typings we need to cast this, more info: https://stackoverflow.com/a/61164277
15
+ !ref.current.contains(e.target as Node) &&
16
+ e.clientX < document.body.scrollWidth
17
+ ) {
18
+ callback();
19
+ }
20
+ });
21
+ };
22
+
23
+ export default useClickOutside;
@@ -0,0 +1,18 @@
1
+ import { useCallback } from 'react';
2
+
3
+ import useEvent from './use-event';
4
+
5
+ const useEscape = (callback: () => void) => {
6
+ const handleKeyDown = useCallback(
7
+ (event: KeyboardEvent) => {
8
+ if (event.code === 'Escape') {
9
+ callback();
10
+ }
11
+ },
12
+ [callback],
13
+ );
14
+
15
+ useEvent('keydown', handleKeyDown);
16
+ };
17
+
18
+ export default useEscape;
@@ -0,0 +1,29 @@
1
+ import { useEffect } from 'react';
2
+
3
+ import isRunningOnClient from './is-running-on-client';
4
+
5
+ const defaultTarget = isRunningOnClient ? window : null;
6
+
7
+ const useEvent = <T extends Event = Event>(
8
+ name: string,
9
+ handler: (event: T) => void,
10
+ target: EventTarget | null = defaultTarget,
11
+ ) => {
12
+ useEffect(() => {
13
+ if (!name) {
14
+ return;
15
+ }
16
+
17
+ if (!target) {
18
+ return;
19
+ }
20
+
21
+ target.addEventListener(name, handler);
22
+
23
+ return () => {
24
+ target.removeEventListener(name, handler);
25
+ };
26
+ }, [name, handler, target]);
27
+ };
28
+
29
+ export default useEvent;
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+
3
+ export const useIsMounted = () => {
4
+ const [isMounted, setIsMounted] = React.useState(false);
5
+
6
+ React.useEffect(() => {
7
+ setIsMounted(true);
8
+ }, []);
9
+
10
+ return isMounted;
11
+ };
@@ -0,0 +1,19 @@
1
+ import { useState } from 'react';
2
+
3
+ const useToggle = (
4
+ initialState: boolean,
5
+ ): [
6
+ state: boolean,
7
+ toggle: () => void,
8
+ turnOn: () => void,
9
+ turnOff: () => void,
10
+ ] => {
11
+ const [state, setState] = useState(!!initialState);
12
+ const toggle = () => setState(!state);
13
+ const turnOn = () => setState(true);
14
+ const turnOff = () => setState(false);
15
+
16
+ return [state, toggle, turnOn, turnOff];
17
+ };
18
+
19
+ export default useToggle;
@@ -0,0 +1,12 @@
1
+ import * as React from 'react';
2
+ const SvgClose = () => (
3
+ <svg width={16} height={16} fill="none" xmlns="http://www.w3.org/2000/svg">
4
+ <path
5
+ fillRule="evenodd"
6
+ clipRule="evenodd"
7
+ d="M3.646 3.947a.5.5 0 0 1 .708 0L8 7.594l3.646-3.647a.5.5 0 0 1 .708.708L8.707 8.3l3.647 3.646a.5.5 0 0 1-.708.708L8 9.008l-3.646 3.647a.5.5 0 0 1-.708-.707L7.293 8.3 3.646 4.655a.5.5 0 0 1 0-.708Z"
8
+ fill="currentColor"
9
+ />
10
+ </svg>
11
+ );
12
+ export default SvgClose;
@@ -0,0 +1,18 @@
1
+ import * as React from 'react';
2
+ const SvgClose = () => (
3
+ <svg width={18} height={18} fill="none" xmlns="http://www.w3.org/2000/svg">
4
+ <path
5
+ fillRule="evenodd"
6
+ clipRule="evenodd"
7
+ d="M14.424 3.877a.6.6 0 0 1 0 .848l-9 9a.6.6 0 1 1-.848-.848l9-9a.6.6 0 0 1 .848 0Z"
8
+ fill="currentColor"
9
+ />
10
+ <path
11
+ fillRule="evenodd"
12
+ clipRule="evenodd"
13
+ d="M4.576 3.877a.6.6 0 0 1 .848 0l9 9a.6.6 0 0 1-.848.848l-9-9a.6.6 0 0 1 0-.848Z"
14
+ fill="currentColor"
15
+ />
16
+ </svg>
17
+ );
18
+ export default SvgClose;
@@ -0,0 +1,14 @@
1
+ import * as React from 'react';
2
+ const SvgArrowDown = () => (
3
+ <svg width={24} height={24} fill="none" xmlns="http://www.w3.org/2000/svg">
4
+ <path
5
+ d="m17 9.5-5 5-5-5"
6
+ stroke="currentColor"
7
+ strokeWidth={1.5}
8
+ strokeMiterlimit={10}
9
+ strokeLinecap="round"
10
+ strokeLinejoin="round"
11
+ />
12
+ </svg>
13
+ );
14
+ export default SvgArrowDown;
@@ -0,0 +1,14 @@
1
+ import * as React from 'react';
2
+ const SvgArrowRight = () => (
3
+ <svg width={24} height={24} fill="none" xmlns="http://www.w3.org/2000/svg">
4
+ <path
5
+ d="m9.5 7 5 5-5 5"
6
+ stroke="currentColor"
7
+ strokeWidth={1.5}
8
+ strokeMiterlimit={10}
9
+ strokeLinecap="round"
10
+ strokeLinejoin="round"
11
+ />
12
+ </svg>
13
+ );
14
+ export default SvgArrowRight;
@@ -0,0 +1,14 @@
1
+ import * as React from 'react';
2
+ const SvgArrowUp = () => (
3
+ <svg width={24} height={24} fill="none" xmlns="http://www.w3.org/2000/svg">
4
+ <path
5
+ d="m7 14.5 5-5 5 5"
6
+ stroke="currentColor"
7
+ strokeWidth={1.5}
8
+ strokeMiterlimit={10}
9
+ strokeLinecap="round"
10
+ strokeLinejoin="round"
11
+ />
12
+ </svg>
13
+ );
14
+ export default SvgArrowUp;
@@ -0,0 +1,12 @@
1
+ import * as React from 'react';
2
+ const SvgClose = () => (
3
+ <svg width={24} height={24} fill="none" xmlns="http://www.w3.org/2000/svg">
4
+ <path
5
+ fillRule="evenodd"
6
+ clipRule="evenodd"
7
+ d="M5.47 5.47a.75.75 0 0 1 1.06 0L12 10.94l5.47-5.47a.75.75 0 1 1 1.06 1.06L13.06 12l5.47 5.47a.75.75 0 1 1-1.06 1.06L12 13.06l-5.47 5.47a.75.75 0 0 1-1.06-1.06L10.94 12 5.47 6.53a.75.75 0 0 1 0-1.06Z"
8
+ fill="currentColor"
9
+ />
10
+ </svg>
11
+ );
12
+ export default SvgClose;
@@ -0,0 +1,12 @@
1
+ import * as React from 'react';
2
+ const SvgRemove = () => (
3
+ <svg width={24} height={24} fill="none" xmlns="http://www.w3.org/2000/svg">
4
+ <path
5
+ fillRule="evenodd"
6
+ clipRule="evenodd"
7
+ d="M12 2.75a9.25 9.25 0 1 0 0 18.5 9.25 9.25 0 0 0 0-18.5ZM1.25 12C1.25 6.063 6.063 1.25 12 1.25S22.75 6.063 22.75 12 17.937 22.75 12 22.75 1.25 17.937 1.25 12Zm6.583-4.167a.75.75 0 0 1 1.061 0L12 10.94l3.106-3.106a.75.75 0 0 1 1.06 1.061L13.062 12l3.106 3.106a.75.75 0 0 1-1.061 1.06L12 13.062l-3.106 3.106a.75.75 0 1 1-1.06-1.061L10.938 12 7.833 8.894a.75.75 0 0 1 0-1.06Z"
8
+ fill="currentColor"
9
+ />
10
+ </svg>
11
+ );
12
+ export default SvgRemove;
@@ -0,0 +1,10 @@
1
+ import * as React from 'react';
2
+ const SvgSearch = () => (
3
+ <svg width={24} height={24} fill="none" xmlns="http://www.w3.org/2000/svg">
4
+ <path
5
+ d="m20.47 21.53.53.53L22.06 21l-.53-.53-1.06 1.06Zm-4.65-6.77a.75.75 0 1 0-1.06 1.06l1.06-1.06Zm5.71 5.71-5.71-5.71-1.06 1.06 5.71 5.71 1.06-1.06ZM16.65 10.2a6.45 6.45 0 0 1-6.45 6.45v1.5a7.95 7.95 0 0 0 7.95-7.95h-1.5Zm-6.45 6.45a6.45 6.45 0 0 1-6.45-6.45h-1.5a7.95 7.95 0 0 0 7.95 7.95v-1.5ZM3.75 10.2a6.45 6.45 0 0 1 6.45-6.45v-1.5a7.95 7.95 0 0 0-7.95 7.95h1.5Zm6.45-6.45a6.45 6.45 0 0 1 6.45 6.45h1.5a7.95 7.95 0 0 0-7.95-7.95v1.5Z"
6
+ fill="currentColor"
7
+ />
8
+ </svg>
9
+ );
10
+ export default SvgSearch;
@@ -0,0 +1,3 @@
1
+ These icons are local to ui-lib so it doesn't have to depend on @sats-group/icons.
2
+
3
+ It might be nicer to refactor the few components that have embedded icons to take icons as input instead.
@@ -0,0 +1 @@
1
+ export type ValueOf<T> = T[keyof T];
@@ -0,0 +1,2 @@
1
+ import Link from './link';
2
+ export default Link;
@@ -0,0 +1,44 @@
1
+ @use '../../tokens/spacing';
2
+ @use '../../tokens/light';
3
+
4
+ .link {
5
+ display: inline-flex;
6
+ align-items: center;
7
+ gap: spacing.$xs;
8
+
9
+ &--background-default {
10
+ color: light.$on-buttons-on-link-default;
11
+ @media (hover: hover) {
12
+ &:hover {
13
+ color: light.$on-buttons-on-link-hover;
14
+ }
15
+ }
16
+
17
+ &-orange {
18
+ color: light.$on-buttons-on-action-default;
19
+ @media (hover: hover) {
20
+ &:hover {
21
+ color: light.$on-buttons-on-action-pressed;
22
+ }
23
+ }
24
+ }
25
+ }
26
+
27
+ &--background-fixed {
28
+ color: light.$on-buttons-on-fixed-link-default;
29
+ @media (hover: hover) {
30
+ &:hover {
31
+ color: light.$on-buttons-on-fixed-link-hover;
32
+ }
33
+ }
34
+
35
+ &-orange {
36
+ color: light.$on-buttons-on-fixed-action-default;
37
+ @media (hover: hover) {
38
+ &:hover {
39
+ color: light.$on-buttons-on-fixed-action-pressed;
40
+ }
41
+ }
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,62 @@
1
+ import * as React from 'react';
2
+
3
+ import Text from '../text';
4
+
5
+ import {
6
+ Link as Props,
7
+ variants,
8
+ sizes,
9
+ iconPositions,
10
+ backgrounds,
11
+ } from './link.types';
12
+ import classNames from 'classnames';
13
+
14
+ const Link: React.FC<Props> & {
15
+ variants: typeof variants;
16
+ sizes: typeof sizes;
17
+ iconPositions: typeof iconPositions;
18
+ backgrounds: typeof backgrounds;
19
+ } = ({
20
+ href,
21
+ icon,
22
+ testId,
23
+ text,
24
+ variant,
25
+ size = sizes.basic,
26
+ background = backgrounds.defualt,
27
+ iconPosition = iconPositions.prefix,
28
+ ...rest
29
+ }) => (
30
+ <Text
31
+ elementName="a"
32
+ size={size}
33
+ className={classNames('link', variant, {
34
+ [`link--background-${background}-${variant}`]: variant,
35
+ [`link--background-${background}`]: background,
36
+ })}
37
+ href={href}
38
+ data-testid={testId}
39
+ {...rest}
40
+ >
41
+ {icon ? (
42
+ iconPosition === iconPositions.prefix ? (
43
+ <>
44
+ {icon} {text}
45
+ </>
46
+ ) : (
47
+ <>
48
+ {text} {icon}
49
+ </>
50
+ )
51
+ ) : (
52
+ text
53
+ )}
54
+ </Text>
55
+ );
56
+
57
+ Link.variants = variants;
58
+ Link.sizes = sizes;
59
+ Link.iconPositions = iconPositions;
60
+ Link.backgrounds = backgrounds;
61
+
62
+ export default Link;
@@ -0,0 +1,37 @@
1
+ import { ObjectValues } from '../types';
2
+
3
+ import { sizes as TextSizes } from '../text/text.types';
4
+
5
+ import type { AnchorHTMLAttributes } from 'react';
6
+
7
+ export const variants = {
8
+ orange: 'orange',
9
+ } as const;
10
+
11
+ export const backgrounds = {
12
+ defualt: 'default',
13
+ fixed: 'fixed',
14
+ } as const;
15
+
16
+ export const sizes = {
17
+ interface: TextSizes.interface,
18
+ small: TextSizes.small,
19
+ basic: TextSizes.basic,
20
+ large: TextSizes.large,
21
+ };
22
+
23
+ export const iconPositions = {
24
+ prefix: 'prefix',
25
+ suffix: 'suffix',
26
+ };
27
+
28
+ export interface Link extends AnchorHTMLAttributes<HTMLAnchorElement> {
29
+ href: string;
30
+ icon?: React.ReactNode;
31
+ testId?: string;
32
+ text: string;
33
+ variant?: ObjectValues<typeof variants>;
34
+ background?: ObjectValues<typeof backgrounds>;
35
+ size?: ObjectValues<typeof sizes>;
36
+ iconPosition?: ObjectValues<typeof iconPositions>;
37
+ }
@@ -0,0 +1,2 @@
1
+ import LinkButton from './link-button';
2
+ export default LinkButton;
@@ -0,0 +1,17 @@
1
+ import * as React from 'react';
2
+
3
+ import VisuallyButton from '../visually-button/visually-button';
4
+
5
+ import { LinkButton as Props } from './link-button.types';
6
+
7
+ const LinkButton: React.FunctionComponent<Props> & {
8
+ sizes: typeof VisuallyButton.sizes;
9
+ variants: typeof VisuallyButton.variants;
10
+ } = ({ href, ...rest }) => (
11
+ <VisuallyButton elementName="a" href={href} {...rest} />
12
+ );
13
+
14
+ LinkButton.sizes = VisuallyButton.sizes;
15
+ LinkButton.variants = VisuallyButton.variants;
16
+
17
+ export default LinkButton;
@@ -0,0 +1,5 @@
1
+ import { VisuallyButtonLink } from '../visually-button/visually-button.types';
2
+
3
+ export type LinkButton = VisuallyButtonLink & {
4
+ href: string;
5
+ };