@abstraks-dev/ui-library 2.2.0 → 2.3.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.
@@ -0,0 +1,195 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = exports.Modal = void 0;
7
+ var _react = _interopRequireWildcard(require("react"));
8
+ var _reactDom = require("react-dom");
9
+ var _propTypes = require("prop-types");
10
+ var _icons = require("../icons");
11
+ var _ssrSafeId = require("../utils/ssrSafeId");
12
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
13
+ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
14
+ /**
15
+ * Modal - A stateless, accessible modal dialog component
16
+ *
17
+ * Features:
18
+ * - Portal rendering to document.body
19
+ * - Multiple sizes (sm, md, lg, xl, full)
20
+ * - Accessible with proper ARIA attributes and focus management
21
+ * - Keyboard support (Escape to close)
22
+ * - Backdrop click to close (optional)
23
+ * - Scroll lock on body
24
+ * - Header, body, and footer sections
25
+ * - Customizable close button
26
+ * - Animation support
27
+ *
28
+ * @component
29
+ * @example
30
+ * <Modal
31
+ * isOpen={true}
32
+ * onClose={() => setIsOpen(false)}
33
+ * title="Confirm Action"
34
+ * size="md"
35
+ * >
36
+ * <p>Are you sure you want to continue?</p>
37
+ * </Modal>
38
+ */
39
+ const Modal = exports.Modal = /*#__PURE__*/(0, _react.forwardRef)(({
40
+ // Core props
41
+ id,
42
+ className = '',
43
+ // Content props
44
+ title,
45
+ children,
46
+ footer,
47
+ headerContent,
48
+ // Behavior props
49
+ isOpen = false,
50
+ onClose,
51
+ closeOnBackdropClick = true,
52
+ closeOnEscape = true,
53
+ showCloseButton = true,
54
+ preventBodyScroll = true,
55
+ // Appearance props
56
+ size = 'md',
57
+ centered = true,
58
+ // Accessibility props
59
+ 'aria-label': ariaLabel,
60
+ 'aria-describedby': ariaDescribedBy,
61
+ 'aria-labelledby': ariaLabelledBy,
62
+ role = 'dialog',
63
+ // Additional props
64
+ componentName = 'modal',
65
+ ...rest
66
+ }, ref) => {
67
+ const generateId = (0, _ssrSafeId.useStableId)('modal');
68
+ const stableId = id || generateId();
69
+ const titleId = `${stableId}-title`;
70
+ const contentId = `${stableId}-content`;
71
+
72
+ // Handle Escape key press
73
+ const handleEscape = (0, _react.useCallback)(event => {
74
+ if (closeOnEscape && event.key === 'Escape' && isOpen) {
75
+ event.preventDefault();
76
+ onClose?.();
77
+ }
78
+ }, [closeOnEscape, isOpen, onClose]);
79
+
80
+ // Handle backdrop click
81
+ const handleBackdropClick = (0, _react.useCallback)(event => {
82
+ if (closeOnBackdropClick && event.target === event.currentTarget) {
83
+ onClose?.();
84
+ }
85
+ }, [closeOnBackdropClick, onClose]);
86
+
87
+ // Manage body scroll lock
88
+ (0, _react.useEffect)(() => {
89
+ if (isOpen && preventBodyScroll) {
90
+ const originalOverflow = document.body.style.overflow;
91
+ const originalPaddingRight = document.body.style.paddingRight;
92
+
93
+ // Get scrollbar width
94
+ const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
95
+
96
+ // Lock scroll
97
+ document.body.style.overflow = 'hidden';
98
+ if (scrollbarWidth > 0) {
99
+ document.body.style.paddingRight = `${scrollbarWidth}px`;
100
+ }
101
+ return () => {
102
+ document.body.style.overflow = originalOverflow;
103
+ document.body.style.paddingRight = originalPaddingRight;
104
+ };
105
+ }
106
+ }, [isOpen, preventBodyScroll]);
107
+
108
+ // Add/remove escape key listener
109
+ (0, _react.useEffect)(() => {
110
+ if (isOpen && closeOnEscape) {
111
+ document.addEventListener('keydown', handleEscape);
112
+ return () => {
113
+ document.removeEventListener('keydown', handleEscape);
114
+ };
115
+ }
116
+ }, [isOpen, closeOnEscape, handleEscape]);
117
+
118
+ // Focus management
119
+ (0, _react.useEffect)(() => {
120
+ if (isOpen) {
121
+ // Store the currently focused element
122
+ const previousActiveElement = document.activeElement;
123
+
124
+ // Return focus when modal closes
125
+ return () => {
126
+ if (previousActiveElement && previousActiveElement.focus) {
127
+ previousActiveElement.focus();
128
+ }
129
+ };
130
+ }
131
+ }, [isOpen]);
132
+ if (!isOpen) {
133
+ return null;
134
+ }
135
+ const modalClassName = [componentName, `${componentName}--${size}`, centered && `${componentName}--centered`, className].filter(Boolean).join(' ');
136
+ const modalContent = /*#__PURE__*/_react.default.createElement("div", {
137
+ className: `${componentName}-overlay`,
138
+ onClick: handleBackdropClick
139
+ }, /*#__PURE__*/_react.default.createElement("div", _extends({
140
+ ref: ref,
141
+ id: stableId,
142
+ className: modalClassName,
143
+ role: role,
144
+ "aria-modal": "true",
145
+ "aria-label": ariaLabel || title,
146
+ "aria-labelledby": title ? titleId : ariaLabelledBy,
147
+ "aria-describedby": ariaDescribedBy || contentId
148
+ }, rest), (title || headerContent || showCloseButton) && /*#__PURE__*/_react.default.createElement("div", {
149
+ className: `${componentName}__header`
150
+ }, headerContent || /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, title && /*#__PURE__*/_react.default.createElement("h2", {
151
+ id: titleId,
152
+ className: `${componentName}__title`
153
+ }, title)), showCloseButton && /*#__PURE__*/_react.default.createElement("button", {
154
+ className: `${componentName}__close`,
155
+ onClick: onClose,
156
+ "aria-label": "Close modal",
157
+ type: "button"
158
+ }, /*#__PURE__*/_react.default.createElement(_icons.Close, {
159
+ dimensions: 24
160
+ }))), /*#__PURE__*/_react.default.createElement("div", {
161
+ id: contentId,
162
+ className: `${componentName}__body`
163
+ }, children), footer && /*#__PURE__*/_react.default.createElement("div", {
164
+ className: `${componentName}__footer`
165
+ }, footer)));
166
+
167
+ // Render modal in a portal
168
+ if (typeof window !== 'undefined') {
169
+ return /*#__PURE__*/(0, _reactDom.createPortal)(modalContent, document.body);
170
+ }
171
+ return null;
172
+ });
173
+ Modal.propTypes = {
174
+ id: _propTypes.string,
175
+ className: _propTypes.string,
176
+ title: _propTypes.string,
177
+ children: _propTypes.node,
178
+ footer: _propTypes.node,
179
+ headerContent: _propTypes.node,
180
+ isOpen: _propTypes.bool,
181
+ onClose: _propTypes.func,
182
+ closeOnBackdropClick: _propTypes.bool,
183
+ closeOnEscape: _propTypes.bool,
184
+ showCloseButton: _propTypes.bool,
185
+ preventBodyScroll: _propTypes.bool,
186
+ size: (0, _propTypes.oneOf)(['sm', 'md', 'lg', 'xl', 'full']),
187
+ centered: _propTypes.bool,
188
+ 'aria-label': _propTypes.string,
189
+ 'aria-describedby': _propTypes.string,
190
+ 'aria-labelledby': _propTypes.string,
191
+ role: _propTypes.string,
192
+ componentName: _propTypes.string
193
+ };
194
+ Modal.displayName = 'Modal';
195
+ var _default = exports.default = Modal;
package/dist/index.js CHANGED
@@ -24,6 +24,7 @@ var _exportNames = {
24
24
  Hero: true,
25
25
  Label: true,
26
26
  Loader: true,
27
+ Modal: true,
27
28
  Paragraph: true,
28
29
  Radio: true,
29
30
  Search: true,
@@ -38,8 +39,14 @@ var _exportNames = {
38
39
  ArrowRight: true,
39
40
  BookOpen: true,
40
41
  CameraIcon: true,
42
+ Check: true,
41
43
  CheckCircle: true,
42
44
  ChevronDown: true,
45
+ even: true,
46
+ ChevronLeft: true,
47
+ ChevronRight: true,
48
+ ChevronRightCircle: true,
49
+ ChevronUpCircle: true,
43
50
  Close: true,
44
51
  Comment: true,
45
52
  EditSquare: true,
@@ -151,6 +158,12 @@ Object.defineProperty(exports, "CaretDown", {
151
158
  return _CaretDown.default;
152
159
  }
153
160
  });
161
+ Object.defineProperty(exports, "Check", {
162
+ enumerable: true,
163
+ get: function () {
164
+ return _Check.Check;
165
+ }
166
+ });
154
167
  Object.defineProperty(exports, "CheckCircle", {
155
168
  enumerable: true,
156
169
  get: function () {
@@ -169,6 +182,30 @@ Object.defineProperty(exports, "ChevronDown", {
169
182
  return _ChevronDown.ChevronDown;
170
183
  }
171
184
  });
185
+ Object.defineProperty(exports, "ChevronLeft", {
186
+ enumerable: true,
187
+ get: function () {
188
+ return _ChevronLeft.ChevronLeft;
189
+ }
190
+ });
191
+ Object.defineProperty(exports, "ChevronRight", {
192
+ enumerable: true,
193
+ get: function () {
194
+ return _ChevronRight.ChevronRight;
195
+ }
196
+ });
197
+ Object.defineProperty(exports, "ChevronRightCircle", {
198
+ enumerable: true,
199
+ get: function () {
200
+ return _ChevronRightCircle.ChevronRightCircle;
201
+ }
202
+ });
203
+ Object.defineProperty(exports, "ChevronUpCircle", {
204
+ enumerable: true,
205
+ get: function () {
206
+ return _ChevronUpCircle.ChevronUpCircle;
207
+ }
208
+ });
172
209
  Object.defineProperty(exports, "Close", {
173
210
  enumerable: true,
174
211
  get: function () {
@@ -319,6 +356,12 @@ Object.defineProperty(exports, "Magnify", {
319
356
  return _Magnify.Magnify;
320
357
  }
321
358
  });
359
+ Object.defineProperty(exports, "Modal", {
360
+ enumerable: true,
361
+ get: function () {
362
+ return _Modal.default;
363
+ }
364
+ });
322
365
  Object.defineProperty(exports, "News", {
323
366
  enumerable: true,
324
367
  get: function () {
@@ -397,6 +440,12 @@ Object.defineProperty(exports, "TrashX", {
397
440
  return _TrashX.TrashX;
398
441
  }
399
442
  });
443
+ Object.defineProperty(exports, "even", {
444
+ enumerable: true,
445
+ get: function () {
446
+ return _ChevronDownCircle.even;
447
+ }
448
+ });
400
449
  var _utils = require("./utils");
401
450
  Object.keys(_utils).forEach(function (key) {
402
451
  if (key === "default" || key === "__esModule") return;
@@ -441,6 +490,7 @@ var _Heading = _interopRequireDefault(require("./components/Heading"));
441
490
  var _Hero = _interopRequireDefault(require("./components/Hero"));
442
491
  var _Label = _interopRequireDefault(require("./components/Label"));
443
492
  var _Loader = _interopRequireDefault(require("./components/Loader"));
493
+ var _Modal = _interopRequireDefault(require("./components/Modal"));
444
494
  var _Paragraph = _interopRequireDefault(require("./components/Paragraph"));
445
495
  var _Radio = require("./components/Radio");
446
496
  var _Search = require("./components/Search");
@@ -455,8 +505,14 @@ var _ArrowIcon = require("./icons/ArrowIcon");
455
505
  var _ArrowRight = require("./icons/ArrowRight");
456
506
  var _BookOpen = require("./icons/BookOpen");
457
507
  var _Camera = require("./icons/Camera");
508
+ var _Check = require("./icons/Check");
458
509
  var _CheckCircle = require("./icons/CheckCircle");
459
510
  var _ChevronDown = require("./icons/ChevronDown");
511
+ var _ChevronDownCircle = require("./icons/ChevronDownCircle");
512
+ var _ChevronLeft = require("./icons/ChevronLeft");
513
+ var _ChevronRight = require("./icons/ChevronRight");
514
+ var _ChevronRightCircle = require("./icons/ChevronRightCircle");
515
+ var _ChevronUpCircle = require("./icons/ChevronUpCircle");
460
516
  var _Close = require("./icons/Close");
461
517
  var _Comment = require("./icons/Comment");
462
518
  var _EditSquare = require("./icons/EditSquare");
@@ -7545,6 +7545,224 @@ body.scroll-locked {
7545
7545
  border: 0 !important;
7546
7546
  }
7547
7547
 
7548
+ .modal-overlay {
7549
+ position: fixed;
7550
+ top: 0;
7551
+ left: 0;
7552
+ right: 0;
7553
+ bottom: 0;
7554
+ background-color: rgba(0, 0, 0, 0.6);
7555
+ backdrop-filter: blur(4px);
7556
+ z-index: 9999;
7557
+ display: flex;
7558
+ align-items: center;
7559
+ justify-content: center;
7560
+ padding: 1rem;
7561
+ animation: fadeIn 0.2s ease-out;
7562
+ overflow-y: auto;
7563
+ }
7564
+ @media (prefers-reduced-motion: reduce) {
7565
+ .modal-overlay {
7566
+ animation: none;
7567
+ }
7568
+ }
7569
+
7570
+ .modal {
7571
+ background: #fff;
7572
+ border-radius: 8px;
7573
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
7574
+ display: flex;
7575
+ flex-direction: column;
7576
+ max-height: calc(100vh - 2rem);
7577
+ width: 100%;
7578
+ animation: slideIn 0.3s ease-out;
7579
+ position: relative;
7580
+ }
7581
+ @media (prefers-reduced-motion: reduce) {
7582
+ .modal {
7583
+ animation: none;
7584
+ }
7585
+ }
7586
+ @media (prefers-color-scheme: dark) {
7587
+ .modal {
7588
+ background: #1e1e1e;
7589
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
7590
+ }
7591
+ }
7592
+ .modal--sm {
7593
+ max-width: 400px;
7594
+ }
7595
+ .modal--md {
7596
+ max-width: 600px;
7597
+ }
7598
+ .modal--lg {
7599
+ max-width: 800px;
7600
+ }
7601
+ .modal--xl {
7602
+ max-width: 1200px;
7603
+ }
7604
+ .modal--full {
7605
+ max-width: calc(100vw - 2rem);
7606
+ max-height: calc(100vh - 2rem);
7607
+ }
7608
+ .modal--centered {
7609
+ margin: auto;
7610
+ }
7611
+ .modal__header {
7612
+ display: flex;
7613
+ align-items: center;
7614
+ justify-content: space-between;
7615
+ padding: 1.5rem;
7616
+ border-bottom: 1px solid #e9ecef;
7617
+ flex-shrink: 0;
7618
+ }
7619
+ @media (prefers-color-scheme: dark) {
7620
+ .modal__header {
7621
+ border-bottom-color: #333;
7622
+ }
7623
+ }
7624
+ .modal__title {
7625
+ margin: 0;
7626
+ font-size: 1.25rem;
7627
+ font-weight: 600;
7628
+ color: #212529;
7629
+ flex: 1;
7630
+ }
7631
+ @media (prefers-color-scheme: dark) {
7632
+ .modal__title {
7633
+ color: #fff;
7634
+ }
7635
+ }
7636
+ .modal__close {
7637
+ display: flex;
7638
+ align-items: center;
7639
+ justify-content: center;
7640
+ background: transparent;
7641
+ border: none;
7642
+ padding: 0.5rem;
7643
+ margin: -0.5rem -0.5rem -0.5rem 1rem;
7644
+ cursor: pointer;
7645
+ color: #6c757d;
7646
+ border-radius: 4px;
7647
+ transition: all 0.15s ease;
7648
+ flex-shrink: 0;
7649
+ }
7650
+ .modal__close:hover {
7651
+ background: #f8f9fa;
7652
+ color: #212529;
7653
+ }
7654
+ @media (prefers-color-scheme: dark) {
7655
+ .modal__close:hover {
7656
+ background: #333;
7657
+ color: #fff;
7658
+ }
7659
+ }
7660
+ .modal__close:focus-visible {
7661
+ outline: 2px solid #009900;
7662
+ outline-offset: 2px;
7663
+ }
7664
+ .modal__close svg {
7665
+ display: block;
7666
+ }
7667
+ .modal__body {
7668
+ padding: 1.5rem;
7669
+ overflow-y: auto;
7670
+ flex: 1;
7671
+ color: #495057;
7672
+ }
7673
+ @media (prefers-color-scheme: dark) {
7674
+ .modal__body {
7675
+ color: #dee2e6;
7676
+ }
7677
+ }
7678
+ .modal__body::-webkit-scrollbar {
7679
+ width: 8px;
7680
+ }
7681
+ .modal__body::-webkit-scrollbar-track {
7682
+ background: #f8f9fa;
7683
+ border-radius: 4px;
7684
+ }
7685
+ @media (prefers-color-scheme: dark) {
7686
+ .modal__body::-webkit-scrollbar-track {
7687
+ background: #2a2a2a;
7688
+ }
7689
+ }
7690
+ .modal__body::-webkit-scrollbar-thumb {
7691
+ background: #ced4da;
7692
+ border-radius: 4px;
7693
+ }
7694
+ .modal__body::-webkit-scrollbar-thumb:hover {
7695
+ background: #adb5bd;
7696
+ }
7697
+ @media (prefers-color-scheme: dark) {
7698
+ .modal__body::-webkit-scrollbar-thumb {
7699
+ background: #555;
7700
+ }
7701
+ .modal__body::-webkit-scrollbar-thumb:hover {
7702
+ background: #666;
7703
+ }
7704
+ }
7705
+ .modal__footer {
7706
+ display: flex;
7707
+ align-items: center;
7708
+ justify-content: flex-end;
7709
+ gap: 0.75rem;
7710
+ padding: 1.5rem;
7711
+ border-top: 1px solid #e9ecef;
7712
+ flex-shrink: 0;
7713
+ }
7714
+ @media (prefers-color-scheme: dark) {
7715
+ .modal__footer {
7716
+ border-top-color: #333;
7717
+ }
7718
+ }
7719
+
7720
+ @keyframes fadeIn {
7721
+ from {
7722
+ opacity: 0;
7723
+ }
7724
+ to {
7725
+ opacity: 1;
7726
+ }
7727
+ }
7728
+ @keyframes slideIn {
7729
+ from {
7730
+ opacity: 0;
7731
+ transform: translateY(-20px) scale(0.95);
7732
+ }
7733
+ to {
7734
+ opacity: 1;
7735
+ transform: translateY(0) scale(1);
7736
+ }
7737
+ }
7738
+ @media (max-width: 640px) {
7739
+ .modal-overlay {
7740
+ padding: 0.5rem;
7741
+ }
7742
+ .modal {
7743
+ max-height: calc(100vh - 1rem);
7744
+ }
7745
+ .modal--sm, .modal--md, .modal--lg, .modal--xl {
7746
+ max-width: 100%;
7747
+ }
7748
+ .modal__header, .modal__body, .modal__footer {
7749
+ padding: 1rem;
7750
+ }
7751
+ .modal__title {
7752
+ font-size: 1.125rem;
7753
+ }
7754
+ }
7755
+ @media (prefers-contrast: high) {
7756
+ .modal {
7757
+ border: 2px solid currentColor;
7758
+ }
7759
+ .modal__header, .modal__footer {
7760
+ border-color: currentColor;
7761
+ }
7762
+ .modal__close:focus-visible {
7763
+ outline-width: 3px;
7764
+ }
7765
+ }
7548
7766
  .logo {
7549
7767
  width: 50%;
7550
7768
  display: inline-flex;