oneslash-design-system 1.2.15 → 1.2.17

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 (41) hide show
  1. package/dist/components/alert.jsx +1 -1
  2. package/dist/components/loadingScreen.jsx +2 -2
  3. package/dist/components/menuItem.jsx +1 -1
  4. package/dist/output.css +29 -5
  5. package/dist/tsconfig.tsbuildinfo +1 -0
  6. package/package.json +28 -4
  7. package/.claude/settings.local.json +0 -9
  8. package/.eslintrc.json +0 -3
  9. package/components/alert.tsx +0 -132
  10. package/components/button.tsx +0 -120
  11. package/components/checkBox.tsx +0 -60
  12. package/components/emptyBox.tsx +0 -33
  13. package/components/iconButton.tsx +0 -103
  14. package/components/loadingScreen.tsx +0 -30
  15. package/components/menu.tsx +0 -35
  16. package/components/menuItem.tsx +0 -117
  17. package/components/modal.tsx +0 -85
  18. package/components/navigation.tsx +0 -27
  19. package/components/popover.tsx +0 -69
  20. package/components/radioGroup.tsx +0 -50
  21. package/components/select.tsx +0 -253
  22. package/components/tab.tsx +0 -85
  23. package/components/tableCell.tsx +0 -24
  24. package/components/tableContainer.tsx +0 -15
  25. package/components/tableHeader.tsx +0 -15
  26. package/components/tableHeaderCell.tsx +0 -24
  27. package/components/tableRow.tsx +0 -15
  28. package/components/tabsContainer.tsx +0 -23
  29. package/components/tag.tsx +0 -81
  30. package/components/textField.tsx +0 -116
  31. package/components/textarea.tsx +0 -120
  32. package/components/timeStamp.tsx +0 -65
  33. package/components/tooltip.tsx +0 -66
  34. package/components/userImage.tsx +0 -64
  35. package/designTokens.js +0 -234
  36. package/index.css +0 -8
  37. package/index.ts +0 -21
  38. package/next.config.mjs +0 -4
  39. package/postcss.config.mjs +0 -8
  40. package/tailwind.config.ts +0 -232
  41. package/tsconfig.json +0 -37
@@ -1,117 +0,0 @@
1
- 'use client';
2
- import React, { useState, useEffect, useCallback, SVGProps, JSX } from 'react';
3
- import NextLink from 'next/link';
4
- import UserImage from './userImage';
5
- import Tag from './tag';
6
-
7
- type IconType = (props: SVGProps<SVGSVGElement>) => JSX.Element;
8
-
9
- interface MenuItemProps {
10
- href?: string;
11
- iconName?: string;
12
- userHandle?: string;
13
- userImgUrl?: string;
14
- label: string;
15
- isSelected?: boolean;
16
- onClick?: any;
17
- className?: string;
18
- size?: 'medium' | 'large';
19
- tag?: {
20
- label: React.ReactNode;
21
- iconName?: string;
22
- };
23
- iconRight?: string;
24
- }
25
-
26
- export default function MenuItem({
27
- href,
28
- iconName,
29
- userHandle,
30
- userImgUrl,
31
- label,
32
- isSelected,
33
- onClick,
34
- className = '',
35
- size = 'medium',
36
- tag,
37
- iconRight,
38
- }: MenuItemProps) {
39
- const [IconLeft, setIconLeft] = useState<IconType | null>(null);
40
- const [IconRight, setIconRight] = useState<IconType | null>(null);
41
-
42
- const loadIcon = useCallback(async (iconName?: string) => {
43
- if (!iconName) return null;
44
- try {
45
- const module = await import('@heroicons/react/24/outline');
46
- const IconComponent = module[iconName as keyof typeof module] as IconType;
47
- return IconComponent || null;
48
- } catch (error) {
49
- console.error(`Failed to load icon ${iconName}:`, error);
50
- return null;
51
- }
52
- }, []);
53
-
54
- useEffect(() => {
55
- const fetchIcon = async () => {
56
- if (iconName) {
57
- setIconLeft(await loadIcon(iconName));
58
- }
59
- if (iconRight) {
60
- setIconRight(await loadIcon(iconRight));
61
- }
62
- };
63
- fetchIcon();
64
- }, [iconName, iconRight, loadIcon]);
65
-
66
- // Size-based icon and text classes
67
- const iconSize = size === 'large' ? 'w-6 h-6' : 'w-5 h-5';
68
- const labelClass = size === 'large' ? 'text-body1' : 'text-body2';
69
- const tagSize = size === 'large' ? 'medium' : 'small';
70
-
71
- const content = (
72
- <div
73
- className={`
74
- flex items-center p-2 rounded-[8px] cursor-pointer justify-between transition-colors duration-200 ease-in-out
75
- ${isSelected
76
- ? 'bg-light-background-accent300 dark:bg-dark-background-accent300 hover:bg-light-background-accent200 dark:hover:bg-dark-background-accent200'
77
- : 'hover:bg-light-background-accent200 hover:dark:bg-dark-background-accent200'}
78
- ${className}
79
- `}
80
- style={{ width: '100%' }}
81
- onClick={onClick}
82
- >
83
- {/* Left group: icon/userImg + label + tag with 8px gap */}
84
- <div className="flex items-center gap-1">
85
- {userImgUrl ? (
86
- <UserImage userHandle={userHandle || ''} userImgUrl={userImgUrl} />
87
- ) : (
88
- IconLeft && (
89
- <IconLeft
90
- className={`${iconSize} text-light-text-secondary dark:text-dark-text-secondary`}
91
- />
92
- )
93
- )}
94
- <span className={`whitespace-nowrap ${labelClass} text-light-text-primary dark:text-dark-text-primary`}>
95
- {label}
96
- </span>
97
- {tag && (
98
- <Tag
99
- variant="contained"
100
- size={tagSize}
101
- label={tag.label}
102
- iconName={tag.iconName as any}
103
- />
104
- )}
105
- </div>
106
-
107
- {/* Right icon aligned to the right */}
108
- {IconRight && (
109
- <IconRight
110
- className={`${iconSize} text-light-text-secondary dark:text-dark-text-secondary flex-shrink-0`}
111
- />
112
- )}
113
- </div>
114
- );
115
-
116
- return href ? <NextLink href={href}>{content}</NextLink> : content;
117
- }
@@ -1,85 +0,0 @@
1
- 'use client';
2
- import React, { useEffect, useRef } from 'react';
3
-
4
- interface ModalProps {
5
- isOpen: boolean;
6
- title?: string;
7
- children: React.ReactNode;
8
- onClose: () => void;
9
- actions?: React.ReactNode;
10
- size?: 'medium' | 'large';
11
- }
12
-
13
- export default function Modal({
14
- isOpen,
15
- title,
16
- children,
17
- onClose,
18
- actions,
19
- size = 'medium', // Default size is medium
20
- }: ModalProps) {
21
-
22
- if (!isOpen) return null;
23
-
24
- // close modal by clicking elsewhere
25
- const handleOverlayClick = (e: React.MouseEvent<HTMLDivElement>) => {
26
- if (e.target === e.currentTarget) {
27
- onClose();
28
- }
29
- };
30
-
31
- // close modal by esc keypress
32
- useEffect(() => {
33
- const handleKeyDown = (e: KeyboardEvent) => {
34
- if (e.key === 'Escape') {
35
- onClose();
36
- }
37
- };
38
- window.addEventListener('keydown', handleKeyDown);
39
- return () => {
40
- window.removeEventListener('keydown', handleKeyDown);
41
- };
42
- }, [onClose]);
43
-
44
- // Determine width based on size prop
45
- const modalWidth = size === 'large' ? 'w-[1200px]' : 'w-[600px]';
46
- const maxWidth = size === 'large' ? '1200px' : '600px';
47
-
48
-
49
-
50
- return (
51
- <div
52
- className="fixed inset-[-32px] bg-black bg-opacity-50 flex items-center justify-center z-50"
53
- onClick={handleOverlayClick}
54
- role="dialog"
55
- aria-labelledby="modal-title"
56
- aria-modal="true"
57
- tabIndex={-1}
58
- >
59
- <div
60
- className={`bg-light-background-default dark:bg-dark-background-default p-6 rounded-[8px] space-y-4 ${modalWidth}`}
61
- style={{
62
- maxWidth,
63
- width: 'calc(100vw - 64px)',
64
- maxHeight: '800px',
65
- height: 'auto',
66
- overflowY: 'auto',
67
- }}
68
- >
69
- {title && (
70
- <h2 id="modal-title" className="text-h6">
71
- {title}
72
- </h2>
73
- )}
74
- <div className="text-body1 space-y-4">
75
- {children}
76
- </div>
77
- {actions && (
78
- <div className="flex justify-between">
79
- {actions}
80
- </div>
81
- )}
82
- </div>
83
- </div>
84
- );
85
- }
@@ -1,27 +0,0 @@
1
- 'use client';
2
- import React from 'react';
3
-
4
- interface NavigationProps {
5
- children: React.ReactNode;
6
- className?: string;
7
- }
8
-
9
- export default function Navigation({
10
- children,
11
- className = '',
12
- }: NavigationProps) {
13
- return (
14
- <nav
15
- className={`
16
- bg-light-background-default dark:bg-dark-background-default
17
- border-r border-light-misc-divider dark:border-dark-misc-divider
18
- p-[10px]
19
- ${className}
20
- `}
21
- >
22
- {children}
23
- </nav>
24
- );
25
- }
26
-
27
- export { Navigation };
@@ -1,69 +0,0 @@
1
- 'use client';
2
- import React, { useState, useEffect } from 'react';
3
- import { createPortal } from 'react-dom';
4
- import { usePopper } from 'react-popper';
5
-
6
- interface PopoverProps {
7
- id?: string;
8
- anchorEl?: HTMLElement | null;
9
- open: boolean;
10
- onClose: () => void;
11
- children: React.ReactNode;
12
- }
13
-
14
- export default function Popover({
15
- id,
16
- anchorEl,
17
- open,
18
- onClose,
19
- children,
20
- }: PopoverProps) {
21
- const [popoverElement, setPopoverElement] = useState<HTMLDivElement | null>(null);
22
- const [hasMounted, setHasMounted] = useState(false);
23
-
24
- // Initialize Popper.js
25
- const { styles, attributes } = usePopper(anchorEl, popoverElement, {
26
- placement: 'bottom-start', // Default placement, can be customized
27
- modifiers: [
28
- { name: 'offset', options: { offset: [0, 8] } }, // Offset for spacing between anchor and popover
29
- ],
30
- });
31
-
32
- // Handle outside clicks to close the popover
33
- useEffect(() => {
34
- const handleClickOutside = (event: MouseEvent) => {
35
- if (popoverElement && !popoverElement.contains(event.target as Node) && anchorEl) {
36
- onClose();
37
- }
38
- };
39
-
40
- if (open) {
41
- document.addEventListener('mousedown', handleClickOutside);
42
- }
43
- return () => {
44
- document.removeEventListener('mousedown', handleClickOutside);
45
- };
46
- }, [open, anchorEl, popoverElement, onClose]);
47
-
48
- // Ensure popover is only rendered after the component mounts
49
- useEffect(() => {
50
- setHasMounted(true);
51
- }, []);
52
-
53
- if (!open || !hasMounted || !anchorEl) return null;
54
-
55
- // Render popover in a portal to prevent layout shifts and positioning issues
56
- return createPortal(
57
- <div
58
- id={id}
59
- ref={setPopoverElement}
60
- style={{ ...styles.popper, display: open ? 'block' : 'none' }}
61
- {...attributes.popper}
62
- className="bg-light-background-accent300 dark:bg-dark-background-accent300 rounded-[8px] shadow-lg p-2"
63
- role="dialog"
64
- >
65
- {children}
66
- </div>,
67
- document.body // Mounting the popover in the document body for isolation
68
- );
69
- }
@@ -1,50 +0,0 @@
1
- 'use client';
2
- import React from 'react';
3
-
4
- interface RadioOption {
5
- label: string;
6
- value: string;
7
- }
8
-
9
- interface RadioGroupProps {
10
- options: RadioOption[];
11
- selectedValue: string;
12
- onChange: (value: string) => void;
13
- direction?: 'horizontal' | 'vertical';
14
- }
15
-
16
- export default function RadioGroup({
17
- options,
18
- selectedValue,
19
- onChange,
20
- direction = 'vertical',
21
- }: RadioGroupProps) {
22
- return (
23
- <div
24
- className={`flex ${ direction === 'horizontal' ? 'space-x-4' : 'flex-col space-y-2' }`}
25
- >
26
- {options.map((option) => (
27
- <label
28
- key={option.value}
29
- className="flex items-center cursor-pointer"
30
- onClick={() => onChange(option.value)}
31
- >
32
- {/* outer circle */}
33
- <div
34
- className={`relative flex justify-center items-center w-4 h-4 rounded-full border-2
35
- ${selectedValue === option.value
36
- ? 'border-light-text-primary dark:border-dark-text-primary'
37
- : 'border-light-text-secondary dark:border-dark-text-secondary'}
38
- `}
39
- >
40
- {/* Inner circle */}
41
- {selectedValue === option.value && (
42
- <div className='absolute w-2 h-2 rounded-full bg-light-text-primary dark:bg-dark-text-primary'/>
43
- )}
44
- </div>
45
- <span className="ml-2 text-body1 text-light-text-primary dark:text-dark-text-primary">{option.label}</span>
46
- </label>
47
- ))}
48
- </div>
49
- );
50
- }
@@ -1,253 +0,0 @@
1
- 'use client';
2
- import React, { useState, useEffect, useRef, useCallback, SVGProps, JSX } from 'react';
3
- import { ChevronDownIcon } from '@heroicons/react/24/outline';
4
- import Menu from './menu';
5
- import MenuItem from './menuItem';
6
-
7
- type IconType = (props: SVGProps<SVGSVGElement>) => JSX.Element;
8
-
9
- export interface SelectOption {
10
- value: string;
11
- label: string;
12
- iconName?: string;
13
- }
14
-
15
- interface SelectProps {
16
- value?: string | string[];
17
- options: SelectOption[];
18
- onChange?: (value: string | string[]) => void;
19
- disabled?: boolean;
20
- placeholder?: string;
21
- decoIconName?: string;
22
- width?: number | string;
23
- multiple?: boolean;
24
- className?: string;
25
- }
26
-
27
- export default function Select({
28
- value,
29
- options,
30
- onChange,
31
- disabled = false,
32
- placeholder = 'Label',
33
- decoIconName,
34
- width,
35
- multiple = false,
36
- className = '',
37
- }: SelectProps) {
38
- const [isOpen, setIsOpen] = useState(false);
39
- const [isFocused, setIsFocused] = useState(false);
40
- const [isHovered, setIsHovered] = useState(false);
41
- const [DecoIcon, setDecoIcon] = useState<IconType | null>(null);
42
- const [mounted, setMounted] = useState(false);
43
-
44
- const selectRef = useRef<HTMLDivElement>(null);
45
- const menuRef = useRef<HTMLDivElement>(null);
46
-
47
- // Handle SSR
48
- useEffect(() => {
49
- setMounted(true);
50
- }, []);
51
-
52
- // Load decoration icon
53
- const loadIcon = useCallback(async (iconName?: string) => {
54
- if (!iconName) return null;
55
- try {
56
- const module = await import('@heroicons/react/24/outline');
57
- const IconComponent = module[iconName as keyof typeof module] as IconType;
58
- return IconComponent || null;
59
- } catch (error) {
60
- console.error(`Failed to load icon ${iconName}:`, error);
61
- return null;
62
- }
63
- }, []);
64
-
65
- useEffect(() => {
66
- const fetchIcon = async () => {
67
- if (decoIconName) {
68
- setDecoIcon(await loadIcon(decoIconName));
69
- }
70
- };
71
- fetchIcon();
72
- }, [decoIconName, loadIcon]);
73
-
74
- // Close menu when clicking outside
75
- useEffect(() => {
76
- if (!mounted) return;
77
-
78
- const handleClickOutside = (event: MouseEvent) => {
79
- if (
80
- selectRef.current &&
81
- menuRef.current &&
82
- !selectRef.current.contains(event.target as Node) &&
83
- !menuRef.current.contains(event.target as Node)
84
- ) {
85
- setIsOpen(false);
86
- setIsFocused(false);
87
- }
88
- };
89
-
90
- if (isOpen) {
91
- document.addEventListener('mousedown', handleClickOutside);
92
- }
93
-
94
- return () => {
95
- document.removeEventListener('mousedown', handleClickOutside);
96
- };
97
- }, [isOpen, mounted]);
98
-
99
- const handleToggle = () => {
100
- if (disabled) return;
101
- setIsOpen(!isOpen);
102
- setIsFocused(!isOpen);
103
- };
104
-
105
- const handleSelect = (optionValue: string) => {
106
- if (multiple) {
107
- const currentValues = Array.isArray(value) ? value : [];
108
- const newValues = currentValues.includes(optionValue)
109
- ? currentValues.filter(v => v !== optionValue)
110
- : [...currentValues, optionValue];
111
- onChange?.(newValues);
112
- } else {
113
- onChange?.(optionValue);
114
- setIsOpen(false);
115
- setIsFocused(false);
116
- }
117
- };
118
-
119
- const getDisplayLabel = () => {
120
- if (!value) return placeholder;
121
-
122
- if (multiple && Array.isArray(value)) {
123
- if (value.length === 0) return placeholder;
124
- const labels = value
125
- .map(v => options.find(opt => opt.value === v)?.label)
126
- .filter(Boolean);
127
- return labels.join(', ');
128
- }
129
-
130
- return options.find(opt => opt.value === value)?.label || placeholder;
131
- };
132
-
133
- const isSelected = (optionValue: string) => {
134
- if (multiple && Array.isArray(value)) {
135
- return value.includes(optionValue);
136
- }
137
- return value === optionValue;
138
- };
139
-
140
- // Determine border styles based on state
141
- const getBorderStyles = () => {
142
- if (disabled) {
143
- return 'border border-light-actionOutlinedBorder-disabled dark:border-dark-actionOutlinedBorder-disabled';
144
- }
145
- if (isFocused || isOpen) {
146
- return 'border border-light-accent-main dark:border-dark-accent-main outline outline-1 outline-light-accent-main dark:outline-dark-accent-main outline-offset-0';
147
- }
148
- if (isHovered) {
149
- return 'border border-light-actionOutlinedBorder-enabled dark:border-dark-actionOutlinedBorder-enabled';
150
- }
151
- return 'border border-light-actionOutlinedBorder-enabled dark:border-dark-actionOutlinedBorder-enabled';
152
- };
153
-
154
- // Determine background color
155
- const getBackgroundColor = () => {
156
- if (isHovered && !isOpen && !disabled) {
157
- return 'bg-light-action-hover dark:bg-dark-action-hover';
158
- }
159
- return 'bg-light-background-default dark:bg-dark-background-default';
160
- };
161
-
162
- // Determine text color
163
- const getTextColor = () => {
164
- if (disabled) {
165
- return 'text-light-text-disabled dark:text-dark-text-disabled';
166
- }
167
- return 'text-light-text-primary dark:text-dark-text-primary';
168
- };
169
-
170
- // Determine icon color
171
- const getIconColor = () => {
172
- if (disabled) {
173
- return 'text-light-text-disabled dark:text-dark-text-disabled';
174
- }
175
- return 'text-light-text-secondary dark:text-dark-text-secondary';
176
- };
177
-
178
- return (
179
- <div className={`relative ${className}`} style={{ width: width || '100%' }}>
180
- {/* Select Button */}
181
- <div
182
- ref={selectRef}
183
- className={`
184
- rounded-[8px]
185
- ${getBorderStyles()}
186
- ${getBackgroundColor()}
187
- ${disabled ? 'cursor-not-allowed' : 'cursor-pointer'}
188
- transition-all duration-200 ease-in-out
189
- outline-none
190
- `}
191
- onClick={handleToggle}
192
- onMouseEnter={() => !disabled && setIsHovered(true)}
193
- onMouseLeave={() => setIsHovered(false)}
194
- tabIndex={disabled ? -1 : 0}
195
- onKeyDown={(e) => {
196
- if (e.key === 'Enter' || e.key === ' ') {
197
- e.preventDefault();
198
- handleToggle();
199
- }
200
- }}
201
- >
202
- {/* Inner Content Container with Padding */}
203
- <div className={`flex items-center justify-between p-2 ${getTextColor()}`}>
204
- {/* Content Container */}
205
- <div className="flex items-center space-x-1 flex-1 overflow-hidden">
206
- {/* Optional Decoration Icon */}
207
- {DecoIcon && (
208
- <DecoIcon className={`w-6 h-6 flex-shrink-0 ${getIconColor()}`} />
209
- )}
210
-
211
- {/* Display Text */}
212
- <span className="text-body1 truncate">
213
- {getDisplayLabel()}
214
- </span>
215
- </div>
216
-
217
- {/* Chevron Icon */}
218
- <ChevronDownIcon
219
- className={`
220
- w-6 h-6 flex-shrink-0 ml-1
221
- ${getIconColor()}
222
- transition-transform duration-200
223
- ${isOpen ? 'transform rotate-180' : ''}
224
- `}
225
- />
226
- </div>
227
- </div>
228
-
229
- {/* Dropdown Menu */}
230
- {mounted && isOpen && (
231
- <div
232
- className="absolute z-50 mt-1 left-0"
233
- style={{ width: width || '100%' }}
234
- >
235
- <Menu ref={menuRef} width={width || '100%'}>
236
- {options.map((option) => (
237
- <MenuItem
238
- key={option.value}
239
- label={option.label}
240
- iconName={option.iconName}
241
- isSelected={isSelected(option.value)}
242
- onClick={() => handleSelect(option.value)}
243
- className="w-full"
244
- />
245
- ))}
246
- </Menu>
247
- </div>
248
- )}
249
- </div>
250
- );
251
- }
252
-
253
- export { Select };
@@ -1,85 +0,0 @@
1
- 'use client';
2
- import React, { useState, useEffect, useCallback } from 'react';
3
- import { useRouter, usePathname } from 'next/navigation';
4
-
5
- type TabProps = {
6
- label: string;
7
- href?: string;
8
- isSelected: boolean;
9
- onClickTab: () => void;
10
- onClickActionIcon?: any;
11
- decoIcon?: string;
12
- actionIcon?: string;
13
- };
14
-
15
- type IconType = React.ComponentType<React.SVGProps<SVGSVGElement>>;
16
-
17
- export default function Tab({
18
- label,
19
- href,
20
- isSelected,
21
- onClickTab,
22
- onClickActionIcon,
23
- decoIcon,
24
- actionIcon
25
- }: TabProps) {
26
- const router = useRouter();
27
- const pathname = usePathname();
28
-
29
- const [IconLeft, setIconLeft] = useState<IconType | null>(null);
30
- const [IconRight, setIconRight] = useState<IconType | null>(null);
31
-
32
- // Load icon dynamically
33
- const loadIcon = useCallback(async (iconName?: string) => {
34
- if (!iconName) return null;
35
- try {
36
- const module = await import('@heroicons/react/24/outline');
37
- const Icon = module[iconName as keyof typeof module] as IconType;
38
- return Icon || null;
39
- } catch (error) {
40
- console.error(`Failed to load icon ${iconName}:`, error);
41
- return null;
42
- }
43
- }, []);
44
-
45
- useEffect(() => {
46
- const fetchIcons = async () => {
47
- if (decoIcon) {
48
- setIconLeft(await loadIcon(decoIcon));
49
- }
50
- if (actionIcon) {
51
- setIconRight(await loadIcon(actionIcon));
52
- }
53
- };
54
- fetchIcons();
55
- }, [decoIcon, actionIcon, loadIcon]);
56
-
57
- const handleClick = () => {
58
- onClickTab();
59
- if (href) {
60
- router.push(href);
61
- }
62
- };
63
-
64
- return (
65
- <div
66
- className={`
67
- flex items-center space-x-1 py-1 px-[6px] rounded-[8px] cursor-pointer justify-start transition-colors duration-200 ease-in-out
68
- ${isSelected
69
- ? 'bg-light-primary-dark dark:bg-dark-primary-dark text-light-text-contrast dark:text-dark-text-contrast'
70
- : 'hover:bg-light-background-accent200 dark:hover:bg-dark-background-accent200'}
71
- `}
72
- onClick={handleClick}
73
- >
74
- {IconLeft && <IconLeft className="w-6 h-6" />}
75
- <span className="whitespace-nowrap text-body1 px-[6px]">
76
- {label}
77
- </span>
78
- {IconRight && (
79
- <div onClick={onClickActionIcon} className="cursor-pointer">
80
- <IconRight className="w-6 h-6" />
81
- </div>
82
- )}
83
- </div>
84
- );
85
- }