@fragments-sdk/ui 0.1.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 (73) hide show
  1. package/package.json +44 -0
  2. package/src/brand.ts +15 -0
  3. package/src/components/Alert/Alert.fragment.tsx +163 -0
  4. package/src/components/Alert/Alert.module.scss +116 -0
  5. package/src/components/Alert/index.tsx +95 -0
  6. package/src/components/Avatar/Avatar.fragment.tsx +147 -0
  7. package/src/components/Avatar/Avatar.module.scss +136 -0
  8. package/src/components/Avatar/index.tsx +177 -0
  9. package/src/components/Badge/Badge.fragment.tsx +151 -0
  10. package/src/components/Badge/Badge.module.scss +87 -0
  11. package/src/components/Badge/index.tsx +55 -0
  12. package/src/components/Button/Button.fragment.tsx +159 -0
  13. package/src/components/Button/Button.module.scss +97 -0
  14. package/src/components/Button/index.tsx +51 -0
  15. package/src/components/Card/Card.fragment.tsx +156 -0
  16. package/src/components/Card/Card.module.scss +86 -0
  17. package/src/components/Card/index.tsx +79 -0
  18. package/src/components/Checkbox/Checkbox.fragment.tsx +166 -0
  19. package/src/components/Checkbox/Checkbox.module.scss +144 -0
  20. package/src/components/Checkbox/index.tsx +166 -0
  21. package/src/components/Dialog/Dialog.fragment.tsx +179 -0
  22. package/src/components/Dialog/Dialog.module.scss +158 -0
  23. package/src/components/Dialog/index.tsx +230 -0
  24. package/src/components/EmptyState/EmptyState.fragment.tsx +222 -0
  25. package/src/components/EmptyState/EmptyState.module.scss +120 -0
  26. package/src/components/EmptyState/index.tsx +80 -0
  27. package/src/components/Input/Input.fragment.tsx +174 -0
  28. package/src/components/Input/Input.module.scss +64 -0
  29. package/src/components/Input/index.tsx +76 -0
  30. package/src/components/Menu/Menu.fragment.tsx +168 -0
  31. package/src/components/Menu/Menu.module.scss +190 -0
  32. package/src/components/Menu/index.tsx +318 -0
  33. package/src/components/Popover/Popover.fragment.tsx +178 -0
  34. package/src/components/Popover/Popover.module.scss +165 -0
  35. package/src/components/Popover/index.tsx +229 -0
  36. package/src/components/Progress/Progress.fragment.tsx +142 -0
  37. package/src/components/Progress/Progress.module.scss +185 -0
  38. package/src/components/Progress/index.tsx +196 -0
  39. package/src/components/RadioGroup/RadioGroup.fragment.tsx +188 -0
  40. package/src/components/RadioGroup/RadioGroup.module.scss +155 -0
  41. package/src/components/RadioGroup/index.tsx +166 -0
  42. package/src/components/Select/Select.fragment.tsx +173 -0
  43. package/src/components/Select/Select.module.scss +187 -0
  44. package/src/components/Select/index.tsx +233 -0
  45. package/src/components/Separator/Separator.fragment.tsx +148 -0
  46. package/src/components/Separator/Separator.module.scss +92 -0
  47. package/src/components/Separator/index.tsx +89 -0
  48. package/src/components/Skeleton/Skeleton.fragment.tsx +147 -0
  49. package/src/components/Skeleton/Skeleton.module.scss +166 -0
  50. package/src/components/Skeleton/index.tsx +185 -0
  51. package/src/components/Table/Table.fragment.tsx +193 -0
  52. package/src/components/Table/Table.module.scss +152 -0
  53. package/src/components/Table/index.tsx +266 -0
  54. package/src/components/Tabs/Tabs.fragment.tsx +155 -0
  55. package/src/components/Tabs/Tabs.module.scss +142 -0
  56. package/src/components/Tabs/index.tsx +142 -0
  57. package/src/components/Textarea/Textarea.fragment.tsx +171 -0
  58. package/src/components/Textarea/Textarea.module.scss +89 -0
  59. package/src/components/Textarea/index.tsx +128 -0
  60. package/src/components/Toast/Toast.fragment.tsx +210 -0
  61. package/src/components/Toast/Toast.module.scss +227 -0
  62. package/src/components/Toast/index.tsx +315 -0
  63. package/src/components/Toggle/Toggle.fragment.tsx +174 -0
  64. package/src/components/Toggle/Toggle.module.scss +103 -0
  65. package/src/components/Toggle/index.tsx +80 -0
  66. package/src/components/Tooltip/Tooltip.fragment.tsx +158 -0
  67. package/src/components/Tooltip/Tooltip.module.scss +82 -0
  68. package/src/components/Tooltip/index.tsx +135 -0
  69. package/src/index.ts +151 -0
  70. package/src/scss.d.ts +4 -0
  71. package/src/styles/globals.scss +17 -0
  72. package/src/tokens/_mixins.scss +93 -0
  73. package/src/tokens/_variables.scss +276 -0
@@ -0,0 +1,136 @@
1
+ @use '../../tokens/variables' as *;
2
+
3
+ // ============================================
4
+ // Avatar Component Styles
5
+ // ============================================
6
+
7
+ .avatar {
8
+ position: relative;
9
+ display: inline-flex;
10
+ align-items: center;
11
+ justify-content: center;
12
+ flex-shrink: 0;
13
+ overflow: hidden;
14
+ border-radius: var(--fui-radius-full, $fui-radius-full);
15
+ background-color: var(--fui-bg-tertiary, $fui-bg-tertiary);
16
+ color: var(--fui-text-secondary, $fui-text-secondary);
17
+ font-family: var(--fui-font-sans, $fui-font-sans);
18
+ user-select: none;
19
+ }
20
+
21
+ // Shape variant
22
+ .square {
23
+ border-radius: var(--fui-radius-md, $fui-radius-md);
24
+ }
25
+
26
+ // ============================================
27
+ // Size Variants
28
+ // ============================================
29
+
30
+ .xs {
31
+ width: 1.5rem; // 24px
32
+ height: 1.5rem;
33
+ font-size: var(--fui-font-size-2xs, $fui-font-size-2xs);
34
+ }
35
+
36
+ .sm {
37
+ width: 2rem; // 32px
38
+ height: 2rem;
39
+ font-size: var(--fui-font-size-xs, $fui-font-size-xs);
40
+ }
41
+
42
+ .md {
43
+ width: 2.5rem; // 40px
44
+ height: 2.5rem;
45
+ font-size: var(--fui-font-size-sm, $fui-font-size-sm);
46
+ }
47
+
48
+ .lg {
49
+ width: 3rem; // 48px
50
+ height: 3rem;
51
+ font-size: var(--fui-font-size-base, $fui-font-size-base);
52
+ }
53
+
54
+ .xl {
55
+ width: 4rem; // 64px
56
+ height: 4rem;
57
+ font-size: var(--fui-font-size-lg, $fui-font-size-lg);
58
+ }
59
+
60
+ // ============================================
61
+ // Image
62
+ // ============================================
63
+
64
+ .image {
65
+ width: 100%;
66
+ height: 100%;
67
+ object-fit: cover;
68
+ }
69
+
70
+ // ============================================
71
+ // Initials / Fallback
72
+ // ============================================
73
+
74
+ .initials {
75
+ font-weight: var(--fui-font-weight-medium, $fui-font-weight-medium);
76
+ color: var(--fui-text-inverse, $fui-text-inverse);
77
+ text-transform: uppercase;
78
+ line-height: 1;
79
+ }
80
+
81
+ .fallbackIcon {
82
+ width: 60%;
83
+ height: 60%;
84
+ color: var(--fui-text-tertiary, $fui-text-tertiary);
85
+ }
86
+
87
+ // ============================================
88
+ // Avatar Group
89
+ // ============================================
90
+
91
+ .group {
92
+ display: inline-flex;
93
+ flex-direction: row-reverse;
94
+ justify-content: flex-end;
95
+ }
96
+
97
+ .groupItem {
98
+ margin-left: -0.5rem;
99
+ border: 2px solid var(--fui-bg-primary, $fui-bg-primary);
100
+
101
+ &:last-child {
102
+ margin-left: 0;
103
+ }
104
+ }
105
+
106
+ // Adjust overlap for different sizes
107
+ .group .xs.groupItem {
108
+ margin-left: -0.375rem;
109
+ }
110
+
111
+ .group .sm.groupItem {
112
+ margin-left: -0.5rem;
113
+ }
114
+
115
+ .group .md.groupItem {
116
+ margin-left: -0.625rem;
117
+ }
118
+
119
+ .group .lg.groupItem {
120
+ margin-left: -0.75rem;
121
+ }
122
+
123
+ .group .xl.groupItem {
124
+ margin-left: -1rem;
125
+ }
126
+
127
+ // Overflow count indicator
128
+ .overflow {
129
+ margin-left: -0.625rem;
130
+ background-color: var(--fui-bg-tertiary, $fui-bg-tertiary);
131
+ border: 2px solid var(--fui-bg-primary, $fui-bg-primary);
132
+
133
+ .initials {
134
+ color: var(--fui-text-secondary, $fui-text-secondary);
135
+ }
136
+ }
@@ -0,0 +1,177 @@
1
+ import * as React from 'react';
2
+ import styles from './Avatar.module.scss';
3
+ // Import globals to ensure CSS variables are defined
4
+ import '../../styles/globals.scss';
5
+
6
+ // ============================================
7
+ // Types
8
+ // ============================================
9
+
10
+ export type AvatarSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
11
+
12
+ export interface AvatarProps {
13
+ /** Image source URL */
14
+ src?: string;
15
+ /** Alt text for the image */
16
+ alt?: string;
17
+ /** Fallback initials (1-2 characters recommended) */
18
+ initials?: string;
19
+ /** Full name - used to generate initials if not provided */
20
+ name?: string;
21
+ /** Size variant */
22
+ size?: AvatarSize;
23
+ /** Shape variant */
24
+ shape?: 'circle' | 'square';
25
+ /** Custom background color for fallback */
26
+ color?: string;
27
+ /** Additional class name */
28
+ className?: string;
29
+ }
30
+
31
+ export interface AvatarGroupProps {
32
+ /** Maximum number of avatars to display */
33
+ max?: number;
34
+ /** Size for all avatars in the group */
35
+ size?: AvatarSize;
36
+ /** Children (Avatar components) */
37
+ children: React.ReactNode;
38
+ /** Additional class name */
39
+ className?: string;
40
+ }
41
+
42
+ // ============================================
43
+ // Helper Functions
44
+ // ============================================
45
+
46
+ function getInitials(name: string): string {
47
+ const parts = name.trim().split(/\s+/);
48
+ if (parts.length === 1) {
49
+ return parts[0].charAt(0).toUpperCase();
50
+ }
51
+ return (parts[0].charAt(0) + parts[parts.length - 1].charAt(0)).toUpperCase();
52
+ }
53
+
54
+ function stringToColor(str: string): string {
55
+ // Generate a consistent color from a string
56
+ let hash = 0;
57
+ for (let i = 0; i < str.length; i++) {
58
+ hash = str.charCodeAt(i) + ((hash << 5) - hash);
59
+ }
60
+ const hue = Math.abs(hash % 360);
61
+ return `hsl(${hue}, 65%, 50%)`;
62
+ }
63
+
64
+ // ============================================
65
+ // Avatar Component
66
+ // ============================================
67
+
68
+ const AvatarBase = React.forwardRef<HTMLDivElement, AvatarProps>(
69
+ function AvatarBase(
70
+ {
71
+ src,
72
+ alt = '',
73
+ initials,
74
+ name,
75
+ size = 'md',
76
+ shape = 'circle',
77
+ color,
78
+ className,
79
+ },
80
+ ref
81
+ ) {
82
+ const [imageError, setImageError] = React.useState(false);
83
+
84
+ // Reset error state when src changes
85
+ React.useEffect(() => {
86
+ setImageError(false);
87
+ }, [src]);
88
+
89
+ const showFallback = !src || imageError;
90
+ const displayInitials = initials || (name ? getInitials(name) : '');
91
+ const fallbackColor = color || (name ? stringToColor(name) : undefined);
92
+
93
+ const avatarClasses = [
94
+ styles.avatar,
95
+ styles[size],
96
+ shape === 'square' && styles.square,
97
+ className,
98
+ ].filter(Boolean).join(' ');
99
+
100
+ const style: React.CSSProperties = {};
101
+ if (showFallback && fallbackColor) {
102
+ style.backgroundColor = fallbackColor;
103
+ }
104
+
105
+ return (
106
+ <div ref={ref} className={avatarClasses} style={style}>
107
+ {!showFallback && (
108
+ <img
109
+ src={src}
110
+ alt={alt}
111
+ className={styles.image}
112
+ onError={() => setImageError(true)}
113
+ />
114
+ )}
115
+ {showFallback && displayInitials && (
116
+ <span className={styles.initials}>{displayInitials}</span>
117
+ )}
118
+ {showFallback && !displayInitials && (
119
+ <svg
120
+ className={styles.fallbackIcon}
121
+ xmlns="http://www.w3.org/2000/svg"
122
+ viewBox="0 0 24 24"
123
+ fill="currentColor"
124
+ aria-hidden="true"
125
+ >
126
+ <path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" />
127
+ </svg>
128
+ )}
129
+ </div>
130
+ );
131
+ }
132
+ );
133
+
134
+ // ============================================
135
+ // Avatar Group Component
136
+ // ============================================
137
+
138
+ function AvatarGroup({
139
+ max,
140
+ size = 'md',
141
+ children,
142
+ className,
143
+ }: AvatarGroupProps) {
144
+ const childArray = React.Children.toArray(children);
145
+ const displayCount = max && max < childArray.length ? max : childArray.length;
146
+ const overflowCount = max && childArray.length > max ? childArray.length - max : 0;
147
+
148
+ const groupClasses = [styles.group, className].filter(Boolean).join(' ');
149
+
150
+ return (
151
+ <div className={groupClasses}>
152
+ {childArray.slice(0, displayCount).map((child, index) => {
153
+ if (React.isValidElement<AvatarProps>(child)) {
154
+ return React.cloneElement(child, {
155
+ key: index,
156
+ size: child.props.size || size,
157
+ className: [styles.groupItem, child.props.className].filter(Boolean).join(' '),
158
+ });
159
+ }
160
+ return child;
161
+ })}
162
+ {overflowCount > 0 && (
163
+ <div className={[styles.avatar, styles[size], styles.overflow].join(' ')}>
164
+ <span className={styles.initials}>+{overflowCount}</span>
165
+ </div>
166
+ )}
167
+ </div>
168
+ );
169
+ }
170
+
171
+ // ============================================
172
+ // Compound Component Export
173
+ // ============================================
174
+
175
+ export const Avatar = Object.assign(AvatarBase, {
176
+ Group: AvatarGroup,
177
+ });
@@ -0,0 +1,151 @@
1
+ import React from 'react';
2
+ import { defineSegment } from '@fragments/core';
3
+ import { Badge } from './index.js';
4
+
5
+ export default defineSegment({
6
+ component: Badge,
7
+
8
+ meta: {
9
+ name: 'Badge',
10
+ description: 'Compact label for status, counts, or categorization. Draws attention to metadata without dominating the layout.',
11
+ category: 'feedback',
12
+ status: 'stable',
13
+ tags: ['status', 'label', 'tag', 'count', 'chip'],
14
+ since: '0.1.0',
15
+ },
16
+
17
+ usage: {
18
+ when: [
19
+ 'Showing item status (active, pending, archived)',
20
+ 'Displaying counts or quantities inline',
21
+ 'Categorizing or tagging content',
22
+ 'Highlighting new or updated items',
23
+ ],
24
+ whenNot: [
25
+ 'Conveying critical errors (use Alert instead)',
26
+ 'Long-form status messages (use Alert)',
27
+ 'Interactive filtering (use chip/toggle group)',
28
+ 'Navigation labels (use tabs or links)',
29
+ ],
30
+ guidelines: [
31
+ 'Keep badge text under 20 characters',
32
+ 'Use dot variant for live status indicators',
33
+ 'Pair success/error variants with meaningful labels, not just colors',
34
+ 'Use onRemove for user-created tags only, not system-generated badges',
35
+ ],
36
+ accessibility: [
37
+ 'Badge text must be meaningful without relying on color alone',
38
+ 'Removable badges must have accessible dismiss button labels',
39
+ 'Avoid using badges as the sole indicator of important state changes',
40
+ ],
41
+ },
42
+
43
+ props: {
44
+ children: {
45
+ type: 'node',
46
+ description: 'Badge label text',
47
+ required: true,
48
+ },
49
+ variant: {
50
+ type: 'enum',
51
+ description: 'Visual style indicating severity or category',
52
+ values: ['default', 'success', 'warning', 'error', 'info'],
53
+ default: 'default',
54
+ },
55
+ size: {
56
+ type: 'enum',
57
+ description: 'Badge size',
58
+ values: ['sm', 'md'],
59
+ default: 'md',
60
+ },
61
+ dot: {
62
+ type: 'boolean',
63
+ description: 'Show a colored dot indicator before the label',
64
+ default: 'false',
65
+ },
66
+ icon: {
67
+ type: 'node',
68
+ description: 'Optional icon element before the text',
69
+ },
70
+ onRemove: {
71
+ type: 'function',
72
+ description: 'Makes the badge removable. Called when X is clicked.',
73
+ },
74
+ },
75
+
76
+ relations: [
77
+ { component: 'Alert', relationship: 'alternative', note: 'Use Alert for prominent, longer messages with actions' },
78
+ { component: 'Tag', relationship: 'sibling', note: 'Tag is interactive (clickable/filterable); Badge is display-only' },
79
+ ],
80
+
81
+ contract: {
82
+ propsSummary: [
83
+ 'children: ReactNode - badge label (required)',
84
+ 'variant: default|success|warning|error|info - visual style',
85
+ 'size: sm|md - badge size',
86
+ 'dot: boolean - show status dot indicator',
87
+ 'onRemove: () => void - makes badge removable',
88
+ ],
89
+ scenarioTags: [
90
+ 'feedback.status',
91
+ 'display.label',
92
+ 'display.count',
93
+ 'content.tag',
94
+ ],
95
+ a11yRules: ['A11Y_BADGE_CONTRAST', 'A11Y_BADGE_DISMISS'],
96
+ },
97
+
98
+ variants: [
99
+ {
100
+ name: 'Default',
101
+ description: 'Neutral badge for general labels',
102
+ render: () => <Badge>Default</Badge>,
103
+ },
104
+ {
105
+ name: 'Status Variants',
106
+ description: 'All severity variants for different contexts',
107
+ render: () => (
108
+ <div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap' }}>
109
+ <Badge variant="default">Default</Badge>
110
+ <Badge variant="success">Active</Badge>
111
+ <Badge variant="warning">Pending</Badge>
112
+ <Badge variant="error">Failed</Badge>
113
+ <Badge variant="info">New</Badge>
114
+ </div>
115
+ ),
116
+ },
117
+ {
118
+ name: 'With Dot',
119
+ description: 'Live status indicators using dot prefix',
120
+ render: () => (
121
+ <div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap' }}>
122
+ <Badge variant="success" dot>Online</Badge>
123
+ <Badge variant="warning" dot>Away</Badge>
124
+ <Badge variant="error" dot>Offline</Badge>
125
+ </div>
126
+ ),
127
+ },
128
+ {
129
+ name: 'Small Size',
130
+ description: 'Compact badges for dense UIs',
131
+ render: () => (
132
+ <div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
133
+ <Badge size="sm" variant="info">v2.1</Badge>
134
+ <Badge size="sm" variant="success">Stable</Badge>
135
+ <Badge size="md" variant="info">Standard</Badge>
136
+ </div>
137
+ ),
138
+ },
139
+ {
140
+ name: 'Removable',
141
+ description: 'User-created tags that can be dismissed',
142
+ render: () => (
143
+ <div style={{ display: 'flex', gap: '8px' }}>
144
+ <Badge variant="info" onRemove={() => {}}>React</Badge>
145
+ <Badge variant="info" onRemove={() => {}}>TypeScript</Badge>
146
+ <Badge variant="info" onRemove={() => {}}>CSS</Badge>
147
+ </div>
148
+ ),
149
+ },
150
+ ],
151
+ });
@@ -0,0 +1,87 @@
1
+ @use '../../tokens/variables' as *;
2
+ @use '../../tokens/mixins' as *;
3
+
4
+ .badge {
5
+ display: inline-flex;
6
+ align-items: center;
7
+ gap: var(--fui-space-1, $fui-space-1);
8
+ border-radius: var(--fui-radius-full, $fui-radius-full);
9
+ font-family: var(--fui-font-sans, $fui-font-sans);
10
+ font-weight: var(--fui-font-weight-medium, $fui-font-weight-medium);
11
+ line-height: 1;
12
+ white-space: nowrap;
13
+ }
14
+
15
+ // Sizes
16
+ .sm {
17
+ padding: 2px var(--fui-space-2, $fui-space-2);
18
+ font-size: var(--fui-font-size-2xs, $fui-font-size-2xs);
19
+ }
20
+
21
+ .md {
22
+ padding: var(--fui-space-1, $fui-space-1) 10px;
23
+ font-size: var(--fui-font-size-xs, $fui-font-size-xs);
24
+ }
25
+
26
+ // Variants
27
+ .default {
28
+ background-color: var(--fui-bg-tertiary, $fui-bg-tertiary);
29
+ color: var(--fui-text-secondary, $fui-text-secondary);
30
+ }
31
+
32
+ .success {
33
+ background-color: var(--fui-color-success-bg, $fui-color-success-bg);
34
+ color: var(--fui-color-success, $fui-color-success);
35
+ }
36
+
37
+ .warning {
38
+ background-color: var(--fui-color-warning-bg, $fui-color-warning-bg);
39
+ color: var(--fui-color-warning, $fui-color-warning);
40
+ }
41
+
42
+ .error {
43
+ background-color: var(--fui-color-danger-bg, $fui-color-danger-bg);
44
+ color: var(--fui-color-danger, $fui-color-danger);
45
+ }
46
+
47
+ .info {
48
+ background-color: var(--fui-color-info-bg, $fui-color-info-bg);
49
+ color: var(--fui-color-info, $fui-color-info);
50
+ }
51
+
52
+ .dot {
53
+ width: 6px;
54
+ height: 6px;
55
+ border-radius: 50%;
56
+ background-color: currentColor;
57
+
58
+ .sm & {
59
+ width: 5px;
60
+ height: 5px;
61
+ }
62
+ }
63
+
64
+ .icon {
65
+ display: flex;
66
+ align-items: center;
67
+ }
68
+
69
+ .remove {
70
+ @include button-reset;
71
+ @include interactive-base;
72
+
73
+ padding: 0 2px;
74
+ font-size: 14px;
75
+ color: inherit;
76
+ opacity: 0.6;
77
+ line-height: 1;
78
+ border-radius: 2px;
79
+
80
+ .sm & {
81
+ font-size: 12px;
82
+ }
83
+
84
+ &:hover {
85
+ opacity: 1;
86
+ }
87
+ }
@@ -0,0 +1,55 @@
1
+ import * as React from 'react';
2
+ import { Button as BaseButton } from '@base-ui/react/button';
3
+ import styles from './Badge.module.scss';
4
+ // Import globals to ensure CSS variables are defined
5
+ import '../../styles/globals.scss';
6
+
7
+ export interface BadgeProps {
8
+ children: React.ReactNode;
9
+ variant?: 'default' | 'success' | 'warning' | 'error' | 'info';
10
+ size?: 'sm' | 'md';
11
+ dot?: boolean;
12
+ icon?: React.ReactNode;
13
+ onRemove?: () => void;
14
+ className?: string;
15
+ }
16
+
17
+ export const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(
18
+ function Badge(
19
+ {
20
+ children,
21
+ variant = 'default',
22
+ size = 'md',
23
+ dot = false,
24
+ icon,
25
+ onRemove,
26
+ className,
27
+ },
28
+ ref
29
+ ) {
30
+ const classes = [styles.badge, styles[size], styles[variant], className]
31
+ .filter(Boolean)
32
+ .join(' ');
33
+
34
+ return (
35
+ <span ref={ref} className={classes}>
36
+ {dot && <span className={styles.dot} aria-hidden="true" />}
37
+ {icon && (
38
+ <span className={styles.icon} aria-hidden="true">
39
+ {icon}
40
+ </span>
41
+ )}
42
+ {children}
43
+ {onRemove && (
44
+ <BaseButton
45
+ onClick={onRemove}
46
+ aria-label="Remove"
47
+ className={styles.remove}
48
+ >
49
+ &times;
50
+ </BaseButton>
51
+ )}
52
+ </span>
53
+ );
54
+ }
55
+ );