@syuttechnologies/layout 1.0.2 → 1.0.21

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 (53) hide show
  1. package/README.md +536 -0
  2. package/dist/components/ChangePasswordModal.d.ts.map +1 -1
  3. package/dist/components/ChangePasswordModal.js +22 -11
  4. package/dist/components/EnterpriseLayout.d.ts.map +1 -1
  5. package/dist/components/EnterpriseLayout.js +21 -6
  6. package/dist/components/ui/ActionMenu/ActionMenu.d.ts +52 -0
  7. package/dist/components/ui/ActionMenu/ActionMenu.d.ts.map +1 -0
  8. package/dist/components/ui/ActionMenu/ActionMenu.js +116 -0
  9. package/dist/components/ui/ActionMenu/index.d.ts +3 -0
  10. package/dist/components/ui/ActionMenu/index.d.ts.map +1 -0
  11. package/dist/components/ui/ActionMenu/index.js +2 -0
  12. package/dist/components/ui/ModuleHeader/ModuleHeader.d.ts +90 -0
  13. package/dist/components/ui/ModuleHeader/ModuleHeader.d.ts.map +1 -0
  14. package/dist/components/ui/ModuleHeader/ModuleHeader.js +433 -0
  15. package/dist/components/ui/ModuleHeader/index.d.ts +3 -0
  16. package/dist/components/ui/ModuleHeader/index.d.ts.map +1 -0
  17. package/dist/components/ui/ModuleHeader/index.js +1 -0
  18. package/dist/components/ui/SyutGrid/SyutGrid.d.ts +74 -0
  19. package/dist/components/ui/SyutGrid/SyutGrid.d.ts.map +1 -0
  20. package/dist/components/ui/SyutGrid/SyutGrid.js +306 -0
  21. package/dist/components/ui/SyutGrid/index.d.ts +3 -0
  22. package/dist/components/ui/SyutGrid/index.d.ts.map +1 -0
  23. package/dist/components/ui/SyutGrid/index.js +2 -0
  24. package/dist/components/ui/SyutSelect/SyutSelectUnified.d.ts +128 -0
  25. package/dist/components/ui/SyutSelect/SyutSelectUnified.d.ts.map +1 -0
  26. package/dist/components/ui/SyutSelect/SyutSelectUnified.js +679 -0
  27. package/dist/components/ui/SyutSelect/index.d.ts +3 -0
  28. package/dist/components/ui/SyutSelect/index.d.ts.map +1 -0
  29. package/dist/components/ui/SyutSelect/index.js +2 -0
  30. package/dist/icon-collection/icon-systems.d.ts +89 -0
  31. package/dist/icon-collection/icon-systems.d.ts.map +1 -0
  32. package/dist/icon-collection/icon-systems.js +70 -0
  33. package/dist/icon-collection/index.d.ts +4 -0
  34. package/dist/icon-collection/index.d.ts.map +1 -0
  35. package/dist/icon-collection/index.js +8 -0
  36. package/dist/index.d.ts +12 -1
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +19 -1
  39. package/package.json +9 -4
  40. package/src/components/ChangePasswordModal.tsx +26 -14
  41. package/src/components/EnterpriseLayout.tsx +23 -8
  42. package/src/components/ui/ActionMenu/ActionMenu.tsx +222 -0
  43. package/src/components/ui/ActionMenu/index.ts +2 -0
  44. package/src/components/ui/ModuleHeader/ModuleHeader.tsx +722 -0
  45. package/src/components/ui/ModuleHeader/index.ts +9 -0
  46. package/src/components/ui/SyutGrid/SyutGrid.tsx +483 -0
  47. package/src/components/ui/SyutGrid/index.ts +2 -0
  48. package/src/components/ui/SyutSelect/SyutSelectUnified.tsx +1115 -0
  49. package/src/components/ui/SyutSelect/index.ts +3 -0
  50. package/src/icon-collection/icon-systems.tsx +464 -0
  51. package/src/icon-collection/index.ts +13 -0
  52. package/src/index.ts +47 -1
  53. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,222 @@
1
+ import React, { useState, useRef, ReactNode } from 'react';
2
+ import { createPortal } from 'react-dom';
3
+ import IconSystem from '../../../icon-collection/icon-systems';
4
+
5
+ // ============================================================================
6
+ // TYPES
7
+ // ============================================================================
8
+
9
+ export interface ActionMenuItem<T = any> {
10
+ id: string;
11
+ label: string;
12
+ icon?: ReactNode;
13
+ onClick: (() => void) | ((row: T) => void);
14
+ variant?: 'default' | 'danger';
15
+ disabled?: boolean;
16
+ // Legacy props for conditional visibility
17
+ isVisible?: (row: T) => boolean;
18
+ }
19
+
20
+ // Type for ActionMenu's items prop - onClick is always () => void after processing
21
+ export interface ProcessedActionMenuItem {
22
+ id: string;
23
+ label: string;
24
+ icon?: ReactNode;
25
+ onClick: () => void;
26
+ variant?: 'default' | 'danger';
27
+ disabled?: boolean;
28
+ }
29
+
30
+ export interface ActionMenuProps {
31
+ /** Unique identifier for the menu */
32
+ id?: string;
33
+ /** Array of menu items to display - accepts both processed and raw items */
34
+ items: ProcessedActionMenuItem[] | ActionMenuItem[];
35
+ /** Custom trigger button content */
36
+ triggerContent?: ReactNode;
37
+ /** Additional class name for the trigger button */
38
+ triggerClassName?: string;
39
+ /** Menu position preference */
40
+ position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
41
+ /** Callback when menu opens/closes */
42
+ onOpenChange?: (isOpen: boolean) => void;
43
+ /** Disabled state */
44
+ disabled?: boolean;
45
+ }
46
+
47
+ // ============================================================================
48
+ // COMPONENT
49
+ // ============================================================================
50
+
51
+ /**
52
+ * ActionMenu - A reusable dropdown menu component that uses React Portal
53
+ * to render outside of parent containers, avoiding overflow clipping issues.
54
+ *
55
+ * @example
56
+ * ```tsx
57
+ * <ActionMenu
58
+ * items={[
59
+ * { id: 'view', label: 'View', icon: <EyeIcon />, onClick: () => handleView() },
60
+ * { id: 'edit', label: 'Edit', icon: <EditIcon />, onClick: () => handleEdit() },
61
+ * { id: 'delete', label: 'Delete', icon: <TrashIcon />, onClick: () => handleDelete(), variant: 'danger' },
62
+ * ]}
63
+ * />
64
+ * ```
65
+ */
66
+ export const ActionMenu: React.FC<ActionMenuProps> = ({
67
+ id,
68
+ items,
69
+ triggerContent,
70
+ triggerClassName = '',
71
+ position = 'bottom-right',
72
+ onOpenChange,
73
+ disabled = false,
74
+ }) => {
75
+ const [isOpen, setIsOpen] = useState(false);
76
+ const [menuPosition, setMenuPosition] = useState({ top: 0, left: 0 });
77
+ const triggerRef = useRef<HTMLButtonElement>(null);
78
+
79
+ const calculatePosition = () => {
80
+ if (!triggerRef.current) return;
81
+
82
+ const rect = triggerRef.current.getBoundingClientRect();
83
+ const menuHeight = 200; // Approximate menu height
84
+ const menuWidth = 180; // Approximate menu width
85
+ const viewportHeight = window.innerHeight;
86
+ const viewportWidth = window.innerWidth;
87
+ const spaceBelow = viewportHeight - rect.bottom;
88
+ const spaceRight = viewportWidth - rect.right;
89
+
90
+ let top: number;
91
+ let left: number;
92
+
93
+ // Determine vertical position
94
+ const shouldOpenUpward = position.startsWith('top') || spaceBelow < menuHeight;
95
+ if (shouldOpenUpward) {
96
+ top = rect.top - menuHeight - 4;
97
+ } else {
98
+ top = rect.bottom + 4;
99
+ }
100
+
101
+ // Determine horizontal position
102
+ const shouldOpenLeft = position.endsWith('left') || spaceRight < menuWidth;
103
+ if (shouldOpenLeft) {
104
+ left = Math.max(8, rect.left);
105
+ } else {
106
+ left = Math.max(8, rect.right - menuWidth);
107
+ }
108
+
109
+ // Ensure menu stays within viewport
110
+ top = Math.max(8, Math.min(top, viewportHeight - menuHeight - 8));
111
+ left = Math.max(8, Math.min(left, viewportWidth - menuWidth - 8));
112
+
113
+ setMenuPosition({ top, left });
114
+ };
115
+
116
+ const openMenu = () => {
117
+ if (disabled) return;
118
+ calculatePosition();
119
+ setIsOpen(true);
120
+ onOpenChange?.(true);
121
+ };
122
+
123
+ const closeMenu = () => {
124
+ setIsOpen(false);
125
+ onOpenChange?.(false);
126
+ };
127
+
128
+ const handleItemClick = (item: ProcessedActionMenuItem | ActionMenuItem) => {
129
+ if (item.disabled) return;
130
+ // Both processed and raw items should have onClick as () => void when used directly
131
+ (item.onClick as () => void)();
132
+ closeMenu();
133
+ };
134
+
135
+ const handleTriggerClick = (e: React.MouseEvent) => {
136
+ e.preventDefault();
137
+ e.stopPropagation();
138
+ if (isOpen) {
139
+ closeMenu();
140
+ } else {
141
+ openMenu();
142
+ }
143
+ };
144
+
145
+ return (
146
+ <div className="action-menu-container" id={id}>
147
+ <button
148
+ ref={triggerRef}
149
+ className={`action-menu-trigger ${isOpen ? 'active' : ''} ${triggerClassName}`}
150
+ onClick={handleTriggerClick}
151
+ type="button"
152
+ disabled={disabled}
153
+ aria-haspopup="menu"
154
+ aria-expanded={isOpen}
155
+ >
156
+ {triggerContent || <IconSystem.moreVertical size={16} />}
157
+ </button>
158
+
159
+ {isOpen && createPortal(
160
+ <>
161
+ {/* Backdrop */}
162
+ <div
163
+ onClick={closeMenu}
164
+ style={{
165
+ position: 'fixed',
166
+ top: 0,
167
+ left: 0,
168
+ right: 0,
169
+ bottom: 0,
170
+ zIndex: 9998,
171
+ background: 'transparent',
172
+ }}
173
+ aria-hidden="true"
174
+ />
175
+ {/* Menu */}
176
+ <div
177
+ role="menu"
178
+ onClick={(e) => e.stopPropagation()}
179
+ style={{
180
+ position: 'fixed',
181
+ top: menuPosition.top,
182
+ left: menuPosition.left,
183
+ zIndex: 9999,
184
+ minWidth: '180px',
185
+ maxHeight: '280px',
186
+ overflowY: 'auto',
187
+ background: 'white',
188
+ border: '2px solid var(--primary)',
189
+ borderRadius: '8px',
190
+ boxShadow: '0 12px 32px rgba(37, 99, 235, 0.2)',
191
+ padding: '0.5rem',
192
+ }}
193
+ >
194
+ {items.map((item, index) => {
195
+ // Check if we should add a divider before this item
196
+ const prevItem = items[index - 1];
197
+ const showDivider = prevItem && prevItem.variant !== 'danger' && item.variant === 'danger';
198
+
199
+ return (
200
+ <React.Fragment key={item.id}>
201
+ {showDivider && <div className="action-menu-divider" />}
202
+ <button
203
+ role="menuitem"
204
+ className={`action-menu-item ${item.variant === 'danger' ? 'action-menu-item-danger' : ''}`}
205
+ onClick={() => handleItemClick(item)}
206
+ disabled={item.disabled}
207
+ >
208
+ {item.icon}
209
+ <span>{item.label}</span>
210
+ </button>
211
+ </React.Fragment>
212
+ );
213
+ })}
214
+ </div>
215
+ </>,
216
+ document.body
217
+ )}
218
+ </div>
219
+ );
220
+ };
221
+
222
+ export default ActionMenu;
@@ -0,0 +1,2 @@
1
+ export { ActionMenu, type ActionMenuProps, type ActionMenuItem, type ProcessedActionMenuItem } from './ActionMenu';
2
+ export { default } from './ActionMenu';