@noya-app/noya-designsystem 0.0.2

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 (73) hide show
  1. package/.turbo/turbo-build.log +17 -0
  2. package/CHANGELOG.md +7 -0
  3. package/README.md +1 -0
  4. package/dist/index.d.mts +1178 -0
  5. package/dist/index.d.ts +1178 -0
  6. package/dist/index.js +15651 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/index.mjs +15684 -0
  9. package/dist/index.mjs.map +1 -0
  10. package/package.json +49 -0
  11. package/src/__tests__/__snapshots__/fuzzyScorer.test.ts.snap +41 -0
  12. package/src/__tests__/fuzzyScorer.test.ts +85 -0
  13. package/src/components/ActivityIndicator.tsx +44 -0
  14. package/src/components/Avatar.tsx +64 -0
  15. package/src/components/Button.tsx +209 -0
  16. package/src/components/Chip.tsx +214 -0
  17. package/src/components/ContextMenu.tsx +224 -0
  18. package/src/components/Dialog.tsx +143 -0
  19. package/src/components/Divider.tsx +40 -0
  20. package/src/components/DropdownMenu.tsx +208 -0
  21. package/src/components/FillInputField.tsx +43 -0
  22. package/src/components/FillPreviewBackground.tsx +132 -0
  23. package/src/components/GradientPicker.tsx +88 -0
  24. package/src/components/Grid.tsx +54 -0
  25. package/src/components/GridView.tsx +468 -0
  26. package/src/components/IconButton.tsx +46 -0
  27. package/src/components/InputField.tsx +568 -0
  28. package/src/components/InputFieldWithCompletions.tsx +477 -0
  29. package/src/components/Label.tsx +62 -0
  30. package/src/components/LabeledElementView.tsx +185 -0
  31. package/src/components/ListView.tsx +950 -0
  32. package/src/components/Popover.tsx +113 -0
  33. package/src/components/Progress.tsx +57 -0
  34. package/src/components/RadioGroup.tsx +163 -0
  35. package/src/components/ScrollArea.tsx +70 -0
  36. package/src/components/Select.tsx +152 -0
  37. package/src/components/Slider.tsx +82 -0
  38. package/src/components/Sortable.tsx +296 -0
  39. package/src/components/Spacer.tsx +29 -0
  40. package/src/components/Stack.tsx +142 -0
  41. package/src/components/Switch.tsx +67 -0
  42. package/src/components/Text.tsx +164 -0
  43. package/src/components/Toast.tsx +86 -0
  44. package/src/components/Tooltip.tsx +43 -0
  45. package/src/components/TreeView.tsx +85 -0
  46. package/src/components/internal/Menu.tsx +222 -0
  47. package/src/components/internal/TextInput.tsx +296 -0
  48. package/src/components/internal/__tests__/TextInput.test.tsx +144 -0
  49. package/src/contexts/DesignSystemConfiguration.tsx +57 -0
  50. package/src/contexts/DialogContext.tsx +190 -0
  51. package/src/contexts/GlobalInputBlurContext.tsx +61 -0
  52. package/src/contexts/ImageDataContext.tsx +44 -0
  53. package/src/hooks/__tests__/mergeEventHandlers.test.ts +47 -0
  54. package/src/hooks/mergeEventHandlers.ts +55 -0
  55. package/src/hooks/useHover.ts +173 -0
  56. package/src/hooks/useObjectURL.ts +22 -0
  57. package/src/hooks/usePlatform.ts +13 -0
  58. package/src/index.tsx +77 -0
  59. package/src/mediaQuery.ts +13 -0
  60. package/src/theme/dark.ts +37 -0
  61. package/src/theme/index.ts +17 -0
  62. package/src/theme/light.ts +178 -0
  63. package/src/utils/breakpoints.ts +45 -0
  64. package/src/utils/completions.ts +21 -0
  65. package/src/utils/createSectionedMenu.ts +38 -0
  66. package/src/utils/fuzzyScorer.ts +105 -0
  67. package/src/utils/getGradientBackground.tsx +33 -0
  68. package/src/utils/handleNudge.ts +30 -0
  69. package/src/utils/mouseEvent.ts +7 -0
  70. package/src/utils/sketchColor.ts +34 -0
  71. package/src/utils/sketchPattern.ts +29 -0
  72. package/src/utils/withSeparatorElements.ts +31 -0
  73. package/tsconfig.json +3 -0
@@ -0,0 +1,296 @@
1
+ import {
2
+ closestCenter,
3
+ DndContext,
4
+ DragEndEvent,
5
+ DragMoveEvent,
6
+ DragOverlay,
7
+ DragStartEvent,
8
+ PointerSensor,
9
+ Translate,
10
+ useSensor,
11
+ useSensors,
12
+ } from '@dnd-kit/core';
13
+ import {
14
+ SortableContext,
15
+ useSortable,
16
+ verticalListSortingStrategy,
17
+ } from '@dnd-kit/sortable';
18
+ import React, {
19
+ createContext,
20
+ memo,
21
+ ReactNode,
22
+ Ref,
23
+ useCallback,
24
+ useContext,
25
+ useMemo,
26
+ useRef,
27
+ useState,
28
+ } from 'react';
29
+ import { createPortal } from 'react-dom';
30
+
31
+ export type RelativeDropPosition = 'above' | 'below' | 'inside';
32
+
33
+ export type DropValidator = (
34
+ sourceIndex: number,
35
+ destinationIndex: number,
36
+ position: RelativeDropPosition,
37
+ ) => boolean;
38
+
39
+ export const normalizeListIndex = (
40
+ index: number,
41
+ position: 'above' | 'below',
42
+ ): number => {
43
+ return position === 'above' ? index : index + 1;
44
+ };
45
+
46
+ const defaultAcceptsDrop: DropValidator = (
47
+ sourceIndex,
48
+ destinationIndex,
49
+ position,
50
+ ) => {
51
+ if (position === 'inside') return false;
52
+
53
+ const normalized = normalizeListIndex(destinationIndex, position);
54
+
55
+ if (sourceIndex === normalized || sourceIndex + 1 === normalized) {
56
+ return false;
57
+ }
58
+
59
+ return true;
60
+ };
61
+
62
+ const SortableItemContext = createContext<{
63
+ keys: string[];
64
+ position: Translate;
65
+ acceptsDrop: DropValidator;
66
+ setActivatorEvent: (event: PointerEvent) => void;
67
+ }>({
68
+ keys: [],
69
+ position: { x: 0, y: 0 },
70
+ acceptsDrop: defaultAcceptsDrop,
71
+ setActivatorEvent: () => {},
72
+ });
73
+
74
+ function validateDropIndicator(
75
+ acceptsDrop: DropValidator,
76
+ keys: string[],
77
+ activeId: string,
78
+ overId: string,
79
+ offsetTop: number,
80
+ elementTop: number,
81
+ elementHeight: number,
82
+ ): RelativeDropPosition | undefined {
83
+ const activeIndex = keys.indexOf(activeId);
84
+ const overIndex = keys.indexOf(overId);
85
+
86
+ const acceptsDropInside = acceptsDrop(activeIndex, overIndex, 'inside');
87
+
88
+ // If we're in the center of the element, prefer dropping inside
89
+ if (
90
+ offsetTop >= elementTop + elementHeight / 3 &&
91
+ offsetTop <= elementTop + (elementHeight * 2) / 3 &&
92
+ acceptsDropInside
93
+ )
94
+ return 'inside';
95
+
96
+ // Are we over the top or bottom half of the element?
97
+ const indicator =
98
+ offsetTop < elementTop + elementHeight / 2 ? 'above' : 'below';
99
+
100
+ // Drop above or below if possible, falling back to inside
101
+ return acceptsDrop(activeIndex, overIndex, indicator)
102
+ ? indicator
103
+ : acceptsDropInside
104
+ ? 'inside'
105
+ : undefined;
106
+ }
107
+
108
+ /* ----------------------------------------------------------------------------
109
+ * Item
110
+ * ------------------------------------------------------------------------- */
111
+
112
+ type UseSortableReturnType = ReturnType<typeof useSortable>;
113
+
114
+ interface ItemProps<T> {
115
+ id: string;
116
+ disabled?: boolean;
117
+ children: (
118
+ props: {
119
+ ref: Ref<T>;
120
+ relativeDropPosition?: RelativeDropPosition;
121
+ [key: string]: any;
122
+ } & UseSortableReturnType['attributes'],
123
+ ) => JSX.Element;
124
+ }
125
+
126
+ function SortableItem<T extends HTMLElement>({
127
+ id,
128
+ disabled,
129
+ children,
130
+ }: ItemProps<T>) {
131
+ const { keys, position, acceptsDrop, setActivatorEvent } =
132
+ useContext(SortableItemContext);
133
+ const sortable = useSortable({ id, disabled });
134
+
135
+ const {
136
+ active,
137
+ activatorEvent,
138
+ attributes,
139
+ listeners,
140
+ setNodeRef,
141
+ isDragging,
142
+ index,
143
+ overIndex,
144
+ over,
145
+ } = sortable;
146
+
147
+ if (activatorEvent) {
148
+ setActivatorEvent(activatorEvent as PointerEvent);
149
+ }
150
+
151
+ const eventY = (activatorEvent as PointerEvent | null)?.clientY ?? 0;
152
+ const offsetTop = eventY + position.y;
153
+
154
+ const ref = useCallback((node: T) => setNodeRef(node), [setNodeRef]);
155
+
156
+ return children({
157
+ ref,
158
+ ...attributes,
159
+ ...listeners,
160
+ relativeDropPosition:
161
+ index >= 0 && index === overIndex && !isDragging && active && over
162
+ ? validateDropIndicator(
163
+ acceptsDrop,
164
+ keys,
165
+ active.id,
166
+ over.id,
167
+ offsetTop,
168
+ over.rect.offsetTop,
169
+ over.rect.height,
170
+ )
171
+ : undefined,
172
+ });
173
+ }
174
+
175
+ /* ----------------------------------------------------------------------------
176
+ * Root
177
+ * ------------------------------------------------------------------------- */
178
+
179
+ interface RootProps {
180
+ keys: string[];
181
+ children: ReactNode;
182
+ renderOverlay?: (index: number) => ReactNode;
183
+ onMoveItem?: (
184
+ sourceIndex: number,
185
+ destinationIndex: number,
186
+ position: RelativeDropPosition,
187
+ ) => void;
188
+ acceptsDrop?: DropValidator;
189
+ }
190
+
191
+ function SortableRoot({
192
+ keys,
193
+ children,
194
+ onMoveItem,
195
+ renderOverlay,
196
+ acceptsDrop = defaultAcceptsDrop,
197
+ }: RootProps) {
198
+ const sensors = useSensors(
199
+ useSensor(PointerSensor, {
200
+ activationConstraint: {
201
+ distance: 4,
202
+ },
203
+ }),
204
+ );
205
+
206
+ const [activeIndex, setActiveIndex] = useState<number | undefined>();
207
+ const activatorEvent = useRef<PointerEvent | null>(null);
208
+
209
+ const setActivatorEvent = useCallback((event: PointerEvent) => {
210
+ activatorEvent.current = event;
211
+ }, []);
212
+
213
+ const [position, setPosition] = useState<Translate>({ x: 0, y: 0 });
214
+
215
+ const handleDragStart = useCallback(
216
+ (event: DragStartEvent) => {
217
+ setActiveIndex(keys.indexOf(event.active.id));
218
+ },
219
+ [keys],
220
+ );
221
+
222
+ const handleDragMove = useCallback((event: DragMoveEvent) => {
223
+ setPosition({ ...event.delta });
224
+ }, []);
225
+
226
+ const handleDragEnd = useCallback(
227
+ (event: DragEndEvent) => {
228
+ const { active, over } = event;
229
+
230
+ setActiveIndex(undefined);
231
+
232
+ if (over && active.id !== over.id) {
233
+ const oldIndex = keys.indexOf(active.id);
234
+ const newIndex = keys.indexOf(over.id);
235
+
236
+ const eventY = activatorEvent.current?.clientY ?? 0;
237
+ const offsetTop = eventY + position.y;
238
+
239
+ const indicator = validateDropIndicator(
240
+ acceptsDrop,
241
+ keys,
242
+ active.id,
243
+ over.id,
244
+ offsetTop,
245
+ over.rect.offsetTop,
246
+ over.rect.height,
247
+ );
248
+
249
+ if (!indicator) return;
250
+
251
+ onMoveItem?.(oldIndex, newIndex, indicator);
252
+ }
253
+ },
254
+ [acceptsDrop, keys, onMoveItem, position.y],
255
+ );
256
+
257
+ return (
258
+ <SortableItemContext.Provider
259
+ value={useMemo(
260
+ () => ({
261
+ keys,
262
+ acceptsDrop,
263
+ position,
264
+ setActivatorEvent,
265
+ }),
266
+ [acceptsDrop, keys, position, setActivatorEvent],
267
+ )}
268
+ >
269
+ <DndContext
270
+ sensors={sensors}
271
+ collisionDetection={closestCenter}
272
+ onDragStart={handleDragStart}
273
+ onDragMove={handleDragMove}
274
+ onDragEnd={handleDragEnd}
275
+ >
276
+ <SortableContext items={keys} strategy={verticalListSortingStrategy}>
277
+ {children}
278
+ </SortableContext>
279
+ {renderOverlay &&
280
+ createPortal(
281
+ <DragOverlay dropAnimation={null}>
282
+ {activeIndex !== undefined &&
283
+ activeIndex >= 0 &&
284
+ renderOverlay(activeIndex)}
285
+ </DragOverlay>,
286
+ document.body,
287
+ )}
288
+ </DndContext>
289
+ </SortableItemContext.Provider>
290
+ );
291
+ }
292
+
293
+ export namespace Sortable {
294
+ export const Item = memo(SortableItem);
295
+ export const Root = memo(SortableRoot);
296
+ }
@@ -0,0 +1,29 @@
1
+ import styled from 'styled-components';
2
+
3
+ interface Props {
4
+ size?: number | string;
5
+ inline?: boolean;
6
+ }
7
+
8
+ /* ----------------------------------------------------------------------------
9
+ * Vertical
10
+ * ------------------------------------------------------------------------- */
11
+
12
+ const SpacerVertical = styled.span<Props>(({ size, inline }) => ({
13
+ display: inline ? 'inline-block' : 'block',
14
+ ...(size === undefined ? { flex: 1 } : { minHeight: size }),
15
+ }));
16
+
17
+ /* ----------------------------------------------------------------------------
18
+ * Horizontal
19
+ * ------------------------------------------------------------------------- */
20
+
21
+ const SpacerHorizontal = styled.span<Props>(({ size, inline }) => ({
22
+ display: inline ? 'inline-block' : 'block',
23
+ ...(size === undefined ? { flex: 1 } : { minWidth: size }),
24
+ }));
25
+
26
+ export namespace Spacer {
27
+ export const Vertical = SpacerVertical;
28
+ export const Horizontal = SpacerHorizontal;
29
+ }
@@ -0,0 +1,142 @@
1
+ import React, {
2
+ Children,
3
+ CSSProperties,
4
+ ForwardedRef,
5
+ forwardRef,
6
+ memo,
7
+ ReactHTML,
8
+ ReactNode,
9
+ } from 'react';
10
+ import styled from 'styled-components';
11
+ import { BreakpointCollection, mergeBreakpoints } from '../utils/breakpoints';
12
+ import withSeparatorElements from '../utils/withSeparatorElements';
13
+
14
+ interface StyleProps {
15
+ display?: 'flex' | 'inline-flex' | 'none' | 'block';
16
+ visibility?: CSSProperties['visibility'];
17
+ position?: CSSProperties['position'];
18
+ zIndex?: CSSProperties['zIndex'];
19
+ gap?: CSSProperties['gap'];
20
+ inset?: CSSProperties['inset'];
21
+ top?: CSSProperties['top'];
22
+ right?: CSSProperties['right'];
23
+ bottom?: CSSProperties['bottom'];
24
+ left?: CSSProperties['left'];
25
+ flexDirection?: CSSProperties['flexDirection'];
26
+ justifyContent?: CSSProperties['justifyContent'];
27
+ alignItems?: CSSProperties['alignItems'];
28
+ alignSelf?: CSSProperties['alignSelf'];
29
+ flex?: CSSProperties['flex'];
30
+ flexWrap?: CSSProperties['flexWrap'];
31
+ height?: CSSProperties['height'];
32
+ minHeight?: CSSProperties['minHeight'];
33
+ maxHeight?: CSSProperties['maxHeight'];
34
+ width?: CSSProperties['width'];
35
+ minWidth?: CSSProperties['minWidth'];
36
+ maxWidth?: CSSProperties['maxWidth'];
37
+ aspectRatio?: CSSProperties['aspectRatio'];
38
+ padding?: CSSProperties['padding'];
39
+ paddingVertical?: string | number;
40
+ paddingHorizontal?: string | number;
41
+ margin?: CSSProperties['margin'];
42
+ background?: CSSProperties['background'];
43
+ backgroundSize?: CSSProperties['backgroundSize'];
44
+ backgroundPosition?: CSSProperties['backgroundPosition'];
45
+ borderRadius?: CSSProperties['borderRadius'];
46
+ overflowX?: CSSProperties['overflowX'];
47
+ overflowY?: CSSProperties['overflowY'];
48
+ overflow?: CSSProperties['overflow'];
49
+ textOverflow?: CSSProperties['textOverflow'];
50
+ boxShadow?: CSSProperties['boxShadow'];
51
+ outline?: CSSProperties['outline'];
52
+ border?: CSSProperties['border'];
53
+ borderTop?: CSSProperties['borderTop'];
54
+ borderRight?: CSSProperties['borderRight'];
55
+ borderBottom?: CSSProperties['borderBottom'];
56
+ borderLeft?: CSSProperties['borderLeft'];
57
+ cursor?: CSSProperties['cursor'];
58
+ userSelect?: CSSProperties['userSelect'];
59
+ transition?: CSSProperties['transition'];
60
+ opacity?: CSSProperties['opacity'];
61
+ filter?: CSSProperties['filter'];
62
+ color?: CSSProperties['color'];
63
+ order?: CSSProperties['order'];
64
+ pointerEvents?: CSSProperties['pointerEvents'];
65
+ lineHeight?: CSSProperties['lineHeight'];
66
+ }
67
+
68
+ export type StackBreakpointList = BreakpointCollection<StyleProps>;
69
+
70
+ interface Props extends StyleProps {
71
+ id?: string;
72
+ as?: keyof ReactHTML;
73
+ className?: string;
74
+ children?: ReactNode;
75
+ separator?: Parameters<typeof withSeparatorElements>[1];
76
+ breakpoints?: StackBreakpointList | null | false;
77
+ href?: string; // Shouldn't be here, ideally
78
+ tabIndex?: number;
79
+ }
80
+
81
+ const Element = styled.div<{
82
+ styleProps: StyleProps;
83
+ breakpoints?: StackBreakpointList | null | false;
84
+ }>(({ styleProps, breakpoints }) => ({
85
+ ...styleProps,
86
+ ...mergeBreakpoints(breakpoints || []),
87
+ }));
88
+
89
+ const StackBase = forwardRef(function StackBase(
90
+ { id, as, children, separator, breakpoints, tabIndex, href, ...rest }: Props,
91
+ forwardedRef: ForwardedRef<HTMLElement>,
92
+ ) {
93
+ const elements = separator
94
+ ? withSeparatorElements(Children.toArray(children), separator)
95
+ : children;
96
+
97
+ const styleProps: StyleProps = {
98
+ display: 'flex',
99
+ position: 'relative',
100
+ alignItems: 'stretch',
101
+ ...rest,
102
+ };
103
+
104
+ return (
105
+ <Element
106
+ ref={forwardedRef}
107
+ id={id}
108
+ as={as}
109
+ styleProps={styleProps}
110
+ breakpoints={breakpoints}
111
+ tabIndex={tabIndex}
112
+ {...(href && { href })}
113
+ >
114
+ {elements}
115
+ </Element>
116
+ );
117
+ });
118
+
119
+ type StackProps = Omit<Props, 'flexDirection'>;
120
+
121
+ const VerticalStack = memo(
122
+ forwardRef(function VStack(
123
+ props: StackProps,
124
+ forwardedRef: ForwardedRef<HTMLElement>,
125
+ ) {
126
+ return <StackBase {...props} flexDirection="column" ref={forwardedRef} />;
127
+ }),
128
+ );
129
+
130
+ const HorizontalStack = memo(
131
+ forwardRef(function HStack(
132
+ props: StackProps,
133
+ forwardedRef: ForwardedRef<HTMLElement>,
134
+ ) {
135
+ return <StackBase {...props} flexDirection="row" ref={forwardedRef} />;
136
+ }),
137
+ );
138
+
139
+ export const Stack = {
140
+ V: VerticalStack,
141
+ H: HorizontalStack,
142
+ };
@@ -0,0 +1,67 @@
1
+ import * as SwitchPrimitive from '@radix-ui/react-switch';
2
+ import React from 'react';
3
+ import styled from 'styled-components';
4
+
5
+ type SwitchVariant = 'normal' | 'primary' | 'secondary';
6
+
7
+ const SwitchRoot = styled(SwitchPrimitive.Root)<{
8
+ variant: SwitchVariant;
9
+ }>(({ theme, variant }) => ({
10
+ all: 'unset',
11
+ width: 32,
12
+ height: 19,
13
+ backgroundColor: theme.colors.activeBackground,
14
+ borderRadius: '9999px',
15
+ position: 'relative',
16
+ WebkitTapHighlightColor: 'rgba(0, 0, 0, 0)',
17
+ cursor: 'pointer',
18
+ // '&:focus': {
19
+ // boxShadow: `0 0 0 1px ${
20
+ // variant === 'secondary' ? theme.colors.secondary : theme.colors.primary
21
+ // }`,
22
+ // },
23
+ '&[data-state="checked"]': {
24
+ backgroundColor:
25
+ variant === 'primary'
26
+ ? theme.colors.primary
27
+ : variant === 'secondary'
28
+ ? theme.colors.secondary
29
+ : undefined,
30
+ },
31
+ }));
32
+
33
+ const SwitchThumb = styled(SwitchPrimitive.Thumb)({
34
+ display: 'block',
35
+ width: 15,
36
+ height: 15,
37
+ backgroundColor: 'white',
38
+ borderRadius: '9999px',
39
+ transition: 'transform 100ms',
40
+ transform: 'translateX(2px)',
41
+ willChange: 'transform',
42
+ '&[data-state="checked"]': { transform: 'translateX(15px)' },
43
+ });
44
+
45
+ interface Props {
46
+ value: boolean;
47
+ onChange: (value: boolean) => void;
48
+ variant?: SwitchVariant;
49
+ }
50
+
51
+ export const Switch = function Switch({
52
+ value,
53
+ onChange,
54
+ variant = 'normal',
55
+ }: Props) {
56
+ return (
57
+ <SwitchRoot
58
+ variant={variant}
59
+ checked={value}
60
+ onCheckedChange={(newValue) => {
61
+ onChange(newValue);
62
+ }}
63
+ >
64
+ <SwitchThumb />
65
+ </SwitchRoot>
66
+ );
67
+ };
@@ -0,0 +1,164 @@
1
+ import React, { ForwardedRef, forwardRef, ReactHTML, ReactNode } from 'react';
2
+ import styled, { CSSProperties } from 'styled-components';
3
+ import { Theme, ThemeColorName } from '../theme';
4
+ import { BreakpointCollection, mergeBreakpoints } from '../utils/breakpoints';
5
+
6
+ const elements: Record<keyof Theme['textStyles'], keyof ReactHTML> = {
7
+ title: 'h1',
8
+ subtitle: 'h1',
9
+ heading1: 'h1',
10
+ heading2: 'h2',
11
+ heading3: 'h3',
12
+ heading4: 'h4',
13
+ heading5: 'h5',
14
+ body: 'p',
15
+ small: 'span',
16
+ code: 'code',
17
+ label: 'span',
18
+ };
19
+
20
+ type StyleProps = {
21
+ flex?: CSSProperties['flex'];
22
+ padding?: CSSProperties['padding'];
23
+ background?: CSSProperties['background'];
24
+ borderRadius?: CSSProperties['borderRadius'];
25
+ textAlign?: CSSProperties['textAlign'];
26
+ fontWeight?: CSSProperties['fontWeight'];
27
+ fontStyle?: CSSProperties['fontStyle'];
28
+ fontSize?: CSSProperties['fontSize'];
29
+ lineHeight?: CSSProperties['lineHeight'];
30
+ fontFamily?: CSSProperties['fontFamily'];
31
+ wordBreak?: CSSProperties['wordBreak'];
32
+ whiteSpace?: CSSProperties['whiteSpace'];
33
+ height?: CSSProperties['height'];
34
+ width?: CSSProperties['width'];
35
+ opacity?: CSSProperties['opacity'];
36
+ position?: CSSProperties['position'];
37
+ overflow?: CSSProperties['overflow'];
38
+ textOverflow?: CSSProperties['textOverflow'];
39
+ textDecoration?: CSSProperties['textDecoration'];
40
+ display?: CSSProperties['display'];
41
+ top?: CSSProperties['top'];
42
+ right?: CSSProperties['right'];
43
+ bottom?: CSSProperties['bottom'];
44
+ left?: CSSProperties['left'];
45
+ userSelect?: CSSProperties['userSelect'];
46
+ };
47
+
48
+ export type TextBreakpointList = BreakpointCollection<StyleProps>;
49
+
50
+ interface Props extends StyleProps {
51
+ as?: keyof ReactHTML;
52
+ href?: string;
53
+ className?: string;
54
+ variant: keyof Theme['textStyles'];
55
+ breakpoints?: BreakpointCollection<StyleProps> | null | false;
56
+ color?: ThemeColorName;
57
+ children: ReactNode;
58
+ onClick?: () => void;
59
+ }
60
+
61
+ const StyledElement = styled.span<
62
+ {
63
+ variant: keyof Theme['textStyles'];
64
+ styleProps: StyleProps;
65
+ } & Pick<Props, 'variant' | 'breakpoints' | 'color'>
66
+ >(({ theme, variant, styleProps, breakpoints, color }) => ({
67
+ ...theme.textStyles[variant],
68
+ color: color ? theme.colors[color] : undefined,
69
+ ...styleProps,
70
+ ...mergeBreakpoints(breakpoints || []),
71
+ }));
72
+
73
+ export const Text = forwardRef(function Text(
74
+ {
75
+ as,
76
+ variant,
77
+ breakpoints,
78
+ children,
79
+ className,
80
+ color,
81
+ href,
82
+ onClick,
83
+ ...rest
84
+ }: Props,
85
+ forwardedRef: ForwardedRef<HTMLElement>,
86
+ ) {
87
+ const element = as ?? elements[variant] ?? 'span';
88
+
89
+ return (
90
+ <StyledElement
91
+ ref={forwardedRef}
92
+ as={element}
93
+ className={className}
94
+ variant={variant}
95
+ breakpoints={breakpoints}
96
+ styleProps={rest}
97
+ color={color}
98
+ onClick={onClick}
99
+ {...(href && { href })}
100
+ >
101
+ {children}
102
+ </StyledElement>
103
+ );
104
+ });
105
+
106
+ type PresetProps = Omit<Props, 'variant'>;
107
+
108
+ export const Heading1 = forwardRef(
109
+ (props: PresetProps, ref: ForwardedRef<HTMLElement>) => (
110
+ <Text ref={ref} {...props} variant="heading1" />
111
+ ),
112
+ );
113
+
114
+ export const Heading2 = forwardRef(
115
+ (props: PresetProps, ref: ForwardedRef<HTMLElement>) => (
116
+ <Text ref={ref} {...props} variant="heading2" />
117
+ ),
118
+ );
119
+
120
+ export const Heading3 = forwardRef(
121
+ (props: PresetProps, ref: ForwardedRef<HTMLElement>) => (
122
+ <Text ref={ref} {...props} variant="heading3" />
123
+ ),
124
+ );
125
+
126
+ export const Heading4 = forwardRef(
127
+ (props: PresetProps, ref: ForwardedRef<HTMLElement>) => (
128
+ <Text ref={ref} {...props} variant="heading4" />
129
+ ),
130
+ );
131
+
132
+ export const Heading5 = forwardRef(
133
+ (props: PresetProps, ref: ForwardedRef<HTMLElement>) => (
134
+ <Text ref={ref} {...props} variant="heading5" />
135
+ ),
136
+ );
137
+
138
+ export const Body = forwardRef(
139
+ (props: PresetProps, ref: ForwardedRef<HTMLElement>) => (
140
+ <Text ref={ref} {...props} variant="body" />
141
+ ),
142
+ );
143
+
144
+ export const Small = forwardRef(
145
+ (props: PresetProps, ref: ForwardedRef<HTMLElement>) => (
146
+ <Text ref={ref} {...props} variant="small" />
147
+ ),
148
+ );
149
+
150
+ // export const Label = forwardRef(
151
+ // (props: PresetProps, ref: ForwardedRef<HTMLElement>) => (
152
+ // <Text ref={ref} {...props} variant="label" />
153
+ // ),
154
+ // );
155
+
156
+ export const Italic = ({ children }: { children: ReactNode }) => (
157
+ <span
158
+ style={{
159
+ fontStyle: 'italic',
160
+ }}
161
+ >
162
+ {children}
163
+ </span>
164
+ );