@abstraks-dev/ui-library 1.0.1

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 (158) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +708 -0
  3. package/dist/__tests__/Anchor.test.js +145 -0
  4. package/dist/__tests__/ArrowRight.test.js +91 -0
  5. package/dist/__tests__/Avatar.test.js +123 -0
  6. package/dist/__tests__/Button.test.js +82 -0
  7. package/dist/__tests__/Card.test.js +198 -0
  8. package/dist/__tests__/CheckCircle.test.js +98 -0
  9. package/dist/__tests__/Checkbox.test.js +161 -0
  10. package/dist/__tests__/ChevronDown.test.js +73 -0
  11. package/dist/__tests__/Close.test.js +98 -0
  12. package/dist/__tests__/EditSquare.test.js +99 -0
  13. package/dist/__tests__/Error.test.js +74 -0
  14. package/dist/__tests__/Footer.test.js +66 -0
  15. package/dist/__tests__/Heading.test.js +227 -0
  16. package/dist/__tests__/Hero.test.js +74 -0
  17. package/dist/__tests__/Label.test.js +123 -0
  18. package/dist/__tests__/Loader.test.js +115 -0
  19. package/dist/__tests__/MenuHover.test.js +137 -0
  20. package/dist/__tests__/Paragraph.test.js +93 -0
  21. package/dist/__tests__/PlusCircle.test.js +99 -0
  22. package/dist/__tests__/Radio.test.js +153 -0
  23. package/dist/__tests__/Select.test.js +187 -0
  24. package/dist/__tests__/Tabs.test.js +162 -0
  25. package/dist/__tests__/TextArea.test.js +127 -0
  26. package/dist/__tests__/TextInput.test.js +181 -0
  27. package/dist/__tests__/Toggle.test.js +120 -0
  28. package/dist/__tests__/TrashX.test.js +99 -0
  29. package/dist/__tests__/useHeadingAccessibility.test.js +144 -0
  30. package/dist/components/Anchor.js +131 -0
  31. package/dist/components/Animation.js +129 -0
  32. package/dist/components/AnimationGroup.js +207 -0
  33. package/dist/components/AnimationToggle.js +216 -0
  34. package/dist/components/Avatar.js +153 -0
  35. package/dist/components/Button.js +218 -0
  36. package/dist/components/Card.js +222 -0
  37. package/dist/components/Checkbox.js +305 -0
  38. package/dist/components/Crud.js +564 -0
  39. package/dist/components/DragAndDrop.js +337 -0
  40. package/dist/components/Error.js +206 -0
  41. package/dist/components/Footer.js +99 -0
  42. package/dist/components/Form.js +412 -0
  43. package/dist/components/Header.js +372 -0
  44. package/dist/components/Heading.js +134 -0
  45. package/dist/components/Hero.js +181 -0
  46. package/dist/components/Label.js +256 -0
  47. package/dist/components/Loader.js +302 -0
  48. package/dist/components/MenuHover.js +114 -0
  49. package/dist/components/Paragraph.js +128 -0
  50. package/dist/components/Prompt.js +61 -0
  51. package/dist/components/Radio.js +254 -0
  52. package/dist/components/Select.js +422 -0
  53. package/dist/components/SideMenu.js +313 -0
  54. package/dist/components/Tabs.js +297 -0
  55. package/dist/components/TextArea.js +370 -0
  56. package/dist/components/TextInput.js +286 -0
  57. package/dist/components/Toggle.js +186 -0
  58. package/dist/components/crudFiles/CrudEditBase.js +150 -0
  59. package/dist/components/crudFiles/CrudViewBase.js +39 -0
  60. package/dist/components/crudFiles/crudDevelopment.js +118 -0
  61. package/dist/components/crudFiles/crudEditHandlers.js +50 -0
  62. package/dist/constants/animation.js +30 -0
  63. package/dist/icons/ArrowIcon.js +32 -0
  64. package/dist/icons/ArrowRight.js +33 -0
  65. package/dist/icons/CheckCircle.js +33 -0
  66. package/dist/icons/ChevronDown.js +28 -0
  67. package/dist/icons/Close.js +33 -0
  68. package/dist/icons/EditSquare.js +33 -0
  69. package/dist/icons/Ellipses.js +34 -0
  70. package/dist/icons/Hamburger.js +39 -0
  71. package/dist/icons/LoadingSpinner.js +42 -0
  72. package/dist/icons/PlusCircle.js +33 -0
  73. package/dist/icons/SaveIcon.js +32 -0
  74. package/dist/icons/TrashX.js +33 -0
  75. package/dist/icons/__tests__/CheckCircle.test.js +9 -0
  76. package/dist/icons/__tests__/ChevronDown.test.js +9 -0
  77. package/dist/icons/__tests__/Close.test.js +9 -0
  78. package/dist/icons/__tests__/EditSquare.test.js +9 -0
  79. package/dist/icons/__tests__/PlusCircle.test.js +9 -0
  80. package/dist/icons/__tests__/TrashX.test.js +9 -0
  81. package/dist/icons/index.js +89 -0
  82. package/dist/index.js +332 -0
  83. package/dist/setupTests.js +3 -0
  84. package/dist/styles/_variables.scss +286 -0
  85. package/dist/styles/anchor.scss +40 -0
  86. package/dist/styles/animation-accessibility.scss +96 -0
  87. package/dist/styles/animation-toggle.scss +233 -0
  88. package/dist/styles/animation.scss +3781 -0
  89. package/dist/styles/avatar.scss +285 -0
  90. package/dist/styles/button.scss +430 -0
  91. package/dist/styles/card.scss +210 -0
  92. package/dist/styles/checkbox.scss +160 -0
  93. package/dist/styles/crud.scss +474 -0
  94. package/dist/styles/dragAndDrop.scss +312 -0
  95. package/dist/styles/error.scss +232 -0
  96. package/dist/styles/footer.scss +58 -0
  97. package/dist/styles/form.scss +420 -0
  98. package/dist/styles/grid.scss +29 -0
  99. package/dist/styles/header.scss +276 -0
  100. package/dist/styles/heading.scss +118 -0
  101. package/dist/styles/hero.scss +185 -0
  102. package/dist/styles/htmlElements.scss +20 -0
  103. package/dist/styles/image.scss +9 -0
  104. package/dist/styles/label.scss +340 -0
  105. package/dist/styles/list-item.scss +5 -0
  106. package/dist/styles/loader.scss +354 -0
  107. package/dist/styles/logo.scss +19 -0
  108. package/dist/styles/main.css +9056 -0
  109. package/dist/styles/main.css.map +1 -0
  110. package/dist/styles/main.scss +0 -0
  111. package/dist/styles/menu-hover.scss +30 -0
  112. package/dist/styles/paragraph.scss +88 -0
  113. package/dist/styles/prompt.scss +51 -0
  114. package/dist/styles/radio.scss +202 -0
  115. package/dist/styles/select.scss +363 -0
  116. package/dist/styles/side-menu.scss +334 -0
  117. package/dist/styles/tabs.scss +540 -0
  118. package/dist/styles/text-area.scss +388 -0
  119. package/dist/styles/text-input.scss +171 -0
  120. package/dist/styles/toggle.scss +0 -0
  121. package/dist/styles/unordered-list.scss +8 -0
  122. package/dist/utils/ScrollHandler.js +30 -0
  123. package/dist/utils/accessibility.js +128 -0
  124. package/dist/utils/heroUtils.js +316 -0
  125. package/dist/utils/index.js +104 -0
  126. package/dist/utils/inputValidation.js +29 -0
  127. package/dist/utils/keyboardNavigation.js +536 -0
  128. package/dist/utils/labelUtils.js +708 -0
  129. package/dist/utils/loaderUtils.js +387 -0
  130. package/dist/utils/menuUtils.js +575 -0
  131. package/dist/utils/useHeadingAccessibility.js +298 -0
  132. package/dist/utils/useRadioGroup.js +260 -0
  133. package/dist/utils/useSelectAccessibility.js +426 -0
  134. package/dist/utils/useTabsAccessibility.js +278 -0
  135. package/dist/utils/useTextAreaAccessibility.js +255 -0
  136. package/dist/utils/useTextInputAccessibility.js +295 -0
  137. package/dist/utils/useTypographyAccessibility.js +168 -0
  138. package/dist/utils/useWindowSize.js +32 -0
  139. package/dist/utils/utils/ScrollHandler.js +26 -0
  140. package/dist/utils/utils/accessibility.js +133 -0
  141. package/dist/utils/utils/heroUtils.js +348 -0
  142. package/dist/utils/utils/index.js +9 -0
  143. package/dist/utils/utils/inputValidation.js +22 -0
  144. package/dist/utils/utils/keyboardNavigation.js +664 -0
  145. package/dist/utils/utils/labelUtils.js +772 -0
  146. package/dist/utils/utils/loaderUtils.js +436 -0
  147. package/dist/utils/utils/menuUtils.js +651 -0
  148. package/dist/utils/utils/useHeadingAccessibility.js +334 -0
  149. package/dist/utils/utils/useRadioGroup.js +311 -0
  150. package/dist/utils/utils/useSelectAccessibility.js +498 -0
  151. package/dist/utils/utils/useTabsAccessibility.js +316 -0
  152. package/dist/utils/utils/useTextAreaAccessibility.js +303 -0
  153. package/dist/utils/utils/useTextInputAccessibility.js +338 -0
  154. package/dist/utils/utils/useTypographyAccessibility.js +180 -0
  155. package/dist/utils/utils/useWindowSize.js +26 -0
  156. package/dist/utils/utils/validation.js +131 -0
  157. package/dist/utils/validation.js +139 -0
  158. package/package.json +90 -0
@@ -0,0 +1,388 @@
1
+ @use 'variables' as *;
2
+
3
+ /* ==========================================================================
4
+ TextArea Component Styles
5
+ ========================================================================== */
6
+
7
+ /**
8
+ * TextArea Wrapper
9
+ * Main container for the entire textarea component
10
+ */
11
+ .text-area-wrapper {
12
+ display: flex;
13
+ flex-direction: column;
14
+ font-family: $font-family-body;
15
+ width: 100%;
16
+ gap: $spacing-xs;
17
+
18
+ /* Variant styles */
19
+ &--outline {
20
+ .text-area {
21
+ border: 2px solid $gray-300;
22
+ background-color: transparent;
23
+ }
24
+ }
25
+
26
+ &--filled {
27
+ .text-area {
28
+ background-color: $gray-100;
29
+ border: 1px solid transparent;
30
+
31
+ &:focus {
32
+ background-color: $color-white;
33
+ border-color: $color-primary;
34
+ }
35
+ }
36
+ }
37
+
38
+ /* Size variants */
39
+ &--small {
40
+ font-size: $font-size-sm;
41
+
42
+ .text-area {
43
+ padding: $spacing-sm;
44
+ min-height: 4rem;
45
+ }
46
+ }
47
+
48
+ &--medium {
49
+ font-size: $font-size-base;
50
+
51
+ .text-area {
52
+ padding: $spacing-md;
53
+ min-height: 6rem;
54
+ }
55
+ }
56
+
57
+ &--large {
58
+ font-size: $font-size-lg;
59
+
60
+ .text-area {
61
+ padding: $spacing-lg;
62
+ min-height: 8rem;
63
+ }
64
+ }
65
+
66
+ /* State variants */
67
+ &--error {
68
+ .text-area {
69
+ border-color: $color-error;
70
+ box-shadow: inset 0 1px 2px rgba($color-error, 0.1);
71
+
72
+ &:focus {
73
+ border-color: $color-error;
74
+ box-shadow: 0 0 0 2px rgba($color-error, 0.2);
75
+ }
76
+ }
77
+ }
78
+
79
+ &--disabled {
80
+ opacity: 0.6;
81
+ pointer-events: none;
82
+
83
+ .text-area {
84
+ background-color: $gray-100;
85
+ cursor: not-allowed;
86
+ }
87
+ }
88
+
89
+ &--readonly {
90
+ .text-area {
91
+ background-color: $gray-100;
92
+ cursor: default;
93
+ }
94
+ }
95
+
96
+ /* Responsive adjustments */
97
+ @media #{$medium} {
98
+ &--small .text-area {
99
+ padding: $spacing-xs;
100
+ min-height: 3.5rem;
101
+ }
102
+
103
+ &--medium .text-area {
104
+ padding: $spacing-sm;
105
+ min-height: 5rem;
106
+ }
107
+
108
+ &--large .text-area {
109
+ padding: $spacing-md;
110
+ min-height: 7rem;
111
+ }
112
+ }
113
+
114
+ @media #{$large} {
115
+ gap: $spacing-sm;
116
+
117
+ &--small .text-area {
118
+ min-height: 4rem;
119
+ }
120
+
121
+ &--medium .text-area {
122
+ min-height: 6rem;
123
+ }
124
+
125
+ &--large .text-area {
126
+ min-height: 8rem;
127
+ }
128
+ }
129
+
130
+ @media #{$xlarge} {
131
+ &--large {
132
+ font-size: $font-size-xl;
133
+
134
+ .text-area {
135
+ padding: $spacing-xl;
136
+ min-height: 10rem;
137
+ }
138
+ }
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Label Styles
144
+ */
145
+ .text-area__label {
146
+ color: $gray-700;
147
+ font-weight: $font-weight-medium;
148
+ margin-bottom: $spacing-xs;
149
+ display: block;
150
+
151
+ /* Required indicator */
152
+ &--required::after {
153
+ content: ' *';
154
+ color: $color-error;
155
+ }
156
+
157
+ /* Size variants */
158
+ .text-area-wrapper--small & {
159
+ font-size: $font-size-sm;
160
+ }
161
+
162
+ .text-area-wrapper--large & {
163
+ font-size: $font-size-lg;
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Main TextArea Element
169
+ */
170
+ .text-area {
171
+ /* Reset and base styles */
172
+ appearance: none;
173
+ background-color: $color-white;
174
+ border: 1px solid $gray-300;
175
+ border-radius: $border-radius-sm;
176
+ color: $gray-700;
177
+ font-family: $font-family-body;
178
+ font-size: inherit;
179
+ font-weight: $font-weight-normal;
180
+ line-height: $line-height-relaxed;
181
+ padding: $spacing-md;
182
+ transition: border-color 0.2s ease, box-shadow 0.2s ease,
183
+ background-color 0.2s ease;
184
+ width: 100%;
185
+ min-height: 6rem;
186
+
187
+ /* Resize behavior */
188
+ &--resize-none {
189
+ resize: none;
190
+ }
191
+
192
+ &--resize-both {
193
+ resize: both;
194
+ }
195
+
196
+ &--resize-horizontal {
197
+ resize: horizontal;
198
+ }
199
+
200
+ &--resize-vertical {
201
+ resize: vertical;
202
+ }
203
+
204
+ /* States */
205
+ &:hover:not(:disabled):not(:read-only) {
206
+ border-color: $gray-500;
207
+ }
208
+
209
+ &:focus {
210
+ border-color: $color-primary;
211
+ box-shadow: 0 0 0 2px rgba($color-primary, 0.2);
212
+ outline: none;
213
+ }
214
+
215
+ &:focus-visible {
216
+ outline: 2px solid $focus-color;
217
+ outline-offset: 2px;
218
+ }
219
+
220
+ /* Placeholder styling */
221
+ &::placeholder {
222
+ color: $gray-500;
223
+ opacity: 1;
224
+ }
225
+
226
+ /* Disabled state */
227
+ &:disabled,
228
+ &--disabled {
229
+ background-color: $gray-100;
230
+ border-color: $gray-300;
231
+ color: $gray-600;
232
+ cursor: not-allowed;
233
+ opacity: 0.6;
234
+
235
+ &:hover {
236
+ border-color: $gray-300;
237
+ }
238
+
239
+ &::placeholder {
240
+ color: $gray-400;
241
+ }
242
+ }
243
+
244
+ /* Read-only state */
245
+ &:read-only,
246
+ &--readonly {
247
+ background-color: $gray-100;
248
+ cursor: default;
249
+
250
+ &:focus {
251
+ border-color: $gray-400;
252
+ box-shadow: 0 0 0 2px rgba($gray-400, 0.2);
253
+ }
254
+ }
255
+
256
+ /* Error state */
257
+ &--error {
258
+ border-color: $color-error;
259
+ box-shadow: inset 0 1px 2px rgba($color-error, 0.1);
260
+
261
+ &:focus {
262
+ border-color: $color-error;
263
+ box-shadow: 0 0 0 2px rgba($color-error, 0.2);
264
+ }
265
+ }
266
+
267
+ /* Responsive adjustments */
268
+ @media #{$medium} {
269
+ font-size: $font-size-sm;
270
+ padding: $spacing-sm;
271
+ min-height: 5rem;
272
+ }
273
+
274
+ @media #{$large} {
275
+ font-size: $font-size-base;
276
+ padding: $spacing-md;
277
+ min-height: 6rem;
278
+ }
279
+
280
+ @media #{$xlarge} {
281
+ font-size: $font-size-lg;
282
+ padding: $spacing-lg;
283
+ min-height: 7rem;
284
+ }
285
+ }
286
+
287
+ /**
288
+ * Character Count Indicator
289
+ */
290
+ .text-area__char-count {
291
+ display: flex;
292
+ justify-content: space-between;
293
+ align-items: center;
294
+ font-size: $font-size-sm;
295
+ color: $gray-600;
296
+ margin-top: $spacing-xs;
297
+
298
+ &--warning {
299
+ color: $color-warning;
300
+ }
301
+
302
+ &--error {
303
+ color: $color-error;
304
+ }
305
+
306
+ &-text {
307
+ flex: 1;
308
+ }
309
+
310
+ &-numbers {
311
+ font-weight: $font-weight-medium;
312
+ font-variant-numeric: tabular-nums;
313
+ }
314
+
315
+ /* Responsive adjustments */
316
+ @media #{$medium} {
317
+ font-size: $font-size-xs;
318
+ }
319
+
320
+ @media #{$large} {
321
+ font-size: $font-size-sm;
322
+ }
323
+
324
+ @media #{$xlarge} {
325
+ font-size: $font-size-base;
326
+ }
327
+ }
328
+
329
+ /**
330
+ * Error Message
331
+ */
332
+ .text-area__error {
333
+ color: $color-error;
334
+ font-size: $font-size-sm;
335
+ margin-top: $spacing-xs;
336
+ display: flex;
337
+ align-items: flex-start;
338
+ gap: $spacing-xs;
339
+
340
+ /* Error icon (if added) */
341
+ &::before {
342
+ content: '⚠';
343
+ flex-shrink: 0;
344
+ margin-top: 1px;
345
+ }
346
+
347
+ /* Responsive adjustments */
348
+ @media #{$medium} {
349
+ font-size: $font-size-xs;
350
+ }
351
+
352
+ @media #{$large} {
353
+ font-size: $font-size-sm;
354
+ }
355
+
356
+ @media #{$xlarge} {
357
+ font-size: $font-size-base;
358
+ }
359
+ }
360
+
361
+ /* ==========================================================================
362
+ High Contrast Mode Support
363
+ ========================================================================== */
364
+ @media (prefers-contrast: high) {
365
+ .text-area {
366
+ border-width: 2px;
367
+
368
+ &:focus {
369
+ outline: 3px solid currentColor;
370
+ outline-offset: 2px;
371
+ }
372
+ }
373
+
374
+ .text-area__char-count--warning,
375
+ .text-area__char-count--error,
376
+ .text-area__error {
377
+ font-weight: $font-weight-bold;
378
+ }
379
+ }
380
+
381
+ /* ==========================================================================
382
+ Reduced Motion Support
383
+ ========================================================================== */
384
+ @media (prefers-reduced-motion: reduce) {
385
+ .text-area {
386
+ transition: none;
387
+ }
388
+ }
@@ -0,0 +1,171 @@
1
+ @use 'variables' as *;
2
+
3
+ // Text Input Component Styles
4
+ .text-input-wrapper {
5
+ display: flex;
6
+ width: 100%;
7
+ flex-direction: column;
8
+ position: relative;
9
+
10
+ // Label styles
11
+ .label {
12
+ flex-direction: row;
13
+ margin-bottom: 0.5rem;
14
+ }
15
+
16
+ // Base input styles
17
+ .text-input {
18
+ line-height: normal;
19
+ background-color: $color-white;
20
+ width: 100%;
21
+ padding: 0.75rem;
22
+ border: 1px solid $gray-300;
23
+ border-radius: 0.25rem;
24
+ font-size: 1rem;
25
+ font-weight: normal;
26
+ color: $gray-700;
27
+ transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
28
+
29
+ // Focus states
30
+ &:focus {
31
+ outline: none;
32
+ border-color: $color-muted-blue;
33
+ box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
34
+ }
35
+ box-shadow: none;
36
+
37
+ // Hover states
38
+ &:hover:not(:disabled):not(:focus) {
39
+ border-color: $gray-500;
40
+ }
41
+ }
42
+
43
+ // Size variants
44
+ &--small .text-input {
45
+ padding: 0.5rem;
46
+ font-size: 0.875rem;
47
+ }
48
+
49
+ &--large .text-input {
50
+ padding: 1rem;
51
+ font-size: 1.125rem;
52
+ }
53
+
54
+ // Style variants
55
+ &--outline .text-input {
56
+ background-color: transparent;
57
+ border-width: 2px;
58
+ }
59
+
60
+ &--filled .text-input {
61
+ background-color: $gray-100;
62
+ border-color: transparent;
63
+
64
+ &:focus {
65
+ background-color: $color-white;
66
+ border-color: $color-muted-blue;
67
+ }
68
+ }
69
+
70
+ // Error states
71
+ &--error .text-input {
72
+ border-color: $color-error;
73
+ box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);
74
+
75
+ &:focus {
76
+ border-color: $color-error;
77
+ box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);
78
+ }
79
+ }
80
+
81
+ // Error message
82
+ &__error {
83
+ margin-top: 0.25rem;
84
+ font-size: 0.875rem;
85
+ color: $color-error;
86
+ }
87
+
88
+ // Type-specific styles
89
+ .text-input--type-email,
90
+ .text-input--type-url {
91
+ font-family: monospace;
92
+ }
93
+
94
+ .text-input--type-number {
95
+ text-align: right;
96
+ }
97
+
98
+ // Responsive design
99
+ @media #{$medium} {
100
+ .text-input {
101
+ font-size: 1rem;
102
+ padding: 0.75rem 1rem;
103
+ }
104
+
105
+ &--small .text-input {
106
+ padding: 0.5rem 0.75rem;
107
+ font-size: 0.875rem;
108
+ }
109
+
110
+ &--large .text-input {
111
+ padding: 1rem 1.25rem;
112
+ font-size: 1.125rem;
113
+ }
114
+ }
115
+
116
+ @media #{$large} {
117
+ .text-input {
118
+ font-size: 1.125rem;
119
+ padding: 0.875rem 1rem;
120
+ }
121
+
122
+ &--small .text-input {
123
+ padding: 0.625rem 0.75rem;
124
+ font-size: 1rem;
125
+ }
126
+
127
+ &--large .text-input {
128
+ padding: 1.125rem 1.5rem;
129
+ font-size: 1.25rem;
130
+ }
131
+ }
132
+
133
+ @media #{$xlarge} {
134
+ .text-input {
135
+ max-width: 500px;
136
+ }
137
+
138
+ &--small .text-input {
139
+ max-width: 300px;
140
+ }
141
+
142
+ &--large .text-input {
143
+ max-width: 700px;
144
+ }
145
+ }
146
+
147
+ // High contrast mode support
148
+ @media (prefers-contrast: high) {
149
+ .text-input {
150
+ border-width: 2px;
151
+ border-color: $gray-700;
152
+
153
+ &:focus {
154
+ border-color: $color-muted-blue;
155
+ box-shadow: 0 0 0 0.25rem rgba(0, 123, 255, 0.5);
156
+ }
157
+ }
158
+
159
+ &--error .text-input {
160
+ border-color: $color-error;
161
+ border-width: 3px;
162
+ }
163
+ }
164
+
165
+ // Reduced motion support
166
+ @media (prefers-reduced-motion: reduce) {
167
+ .text-input {
168
+ transition: none;
169
+ }
170
+ }
171
+ }
File without changes
@@ -0,0 +1,8 @@
1
+
2
+ .unordered-list {
3
+ padding-left: 0;
4
+ list-style: none;
5
+ margin-block-start: 0;
6
+ margin-block-end: 0;
7
+ padding-inline-start: 0;
8
+ }
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _react = require("react");
8
+ var _propTypes = _interopRequireDefault(require("prop-types"));
9
+ var _reactRouterDom = require("react-router-dom");
10
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
11
+ const ScrollHandler = ({
12
+ location
13
+ }) => {
14
+ (0, _react.useEffect)(() => {
15
+ const element = document.getElementById(location.hash);
16
+ setTimeout(() => {
17
+ window.scrollTo({
18
+ behavior: element ? 'smooth' : 'auto',
19
+ top: element ? element.offsetTop : 0
20
+ });
21
+ }, 100);
22
+ }, [location]);
23
+ return null;
24
+ };
25
+ ScrollHandler.propTypes = {
26
+ location: _propTypes.default.shape({
27
+ hash: _propTypes.default.string
28
+ }).isRequired
29
+ };
30
+ var _default = exports.default = (0, _reactRouterDom.withRouter)(ScrollHandler);
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.safeFocus = exports.createLiveRegion = exports.announceToScreenReader = void 0;
7
+ /**
8
+ * Accessibility utility functions for screen readers and ARIA support
9
+ */
10
+
11
+ /**
12
+ * Announces a message to screen readers using ARIA live regions
13
+ * This is a safer alternative to direct DOM manipulation
14
+ *
15
+ * @param {string} message - The message to announce
16
+ * @param {'polite'|'assertive'} priority - The announcement priority level
17
+ * @param {number} timeout - How long to keep the announcement (default: 1000ms)
18
+ * @returns {Function} Cleanup function to remove the announcement early
19
+ *
20
+ * @example
21
+ * // Announce an error assertively
22
+ * const cleanup = announceToScreenReader('Form validation failed', 'assertive');
23
+ *
24
+ * // Announce info politely
25
+ * announceToScreenReader('Data saved automatically', 'polite');
26
+ */
27
+ const announceToScreenReader = (message, priority = 'polite', timeout = 1000) => {
28
+ // Ensure we're in a browser environment
29
+ if (typeof document === 'undefined') {
30
+ return () => {}; // Return no-op cleanup function for SSR
31
+ }
32
+ const announcement = document.createElement('div');
33
+ announcement.setAttribute('aria-live', priority);
34
+ announcement.setAttribute('aria-atomic', 'true');
35
+ announcement.className = 'sr-only'; // Use CSS class instead of inline styles
36
+ announcement.textContent = message;
37
+
38
+ // Add to document body
39
+ document.body.appendChild(announcement);
40
+
41
+ // Set up cleanup
42
+ const cleanup = () => {
43
+ if (document.body.contains(announcement)) {
44
+ document.body.removeChild(announcement);
45
+ }
46
+ };
47
+
48
+ // Auto cleanup after timeout
49
+ setTimeout(cleanup, timeout);
50
+
51
+ // Return cleanup function for manual cleanup if needed
52
+ return cleanup;
53
+ };
54
+
55
+ /**
56
+ * Creates a live region element that can be reused for announcements
57
+ * This is more efficient for multiple announcements
58
+ *
59
+ * @param {'polite'|'assertive'} priority - The announcement priority level
60
+ * @returns {Object} Object with announce and destroy methods
61
+ *
62
+ * @example
63
+ * const liveRegion = createLiveRegion('assertive');
64
+ * liveRegion.announce('First message');
65
+ * liveRegion.announce('Second message');
66
+ * liveRegion.destroy(); // Clean up when done
67
+ */
68
+ exports.announceToScreenReader = announceToScreenReader;
69
+ const createLiveRegion = (priority = 'polite') => {
70
+ // Ensure we're in a browser environment
71
+ if (typeof document === 'undefined') {
72
+ return {
73
+ announce: () => {},
74
+ destroy: () => {}
75
+ };
76
+ }
77
+ const liveRegion = document.createElement('div');
78
+ liveRegion.setAttribute('aria-live', priority);
79
+ liveRegion.setAttribute('aria-atomic', 'true');
80
+ liveRegion.className = 'sr-only';
81
+ liveRegion.setAttribute('role', priority === 'assertive' ? 'alert' : 'status');
82
+ document.body.appendChild(liveRegion);
83
+ return {
84
+ /**
85
+ * Announce a message in this live region
86
+ * @param {string} message - The message to announce
87
+ */
88
+ announce: message => {
89
+ liveRegion.textContent = message;
90
+ // Clear after a short delay to allow for new announcements
91
+ setTimeout(() => {
92
+ if (liveRegion.textContent === message) {
93
+ liveRegion.textContent = '';
94
+ }
95
+ }, 1000);
96
+ },
97
+ /**
98
+ * Clean up the live region
99
+ */
100
+ destroy: () => {
101
+ if (document.body.contains(liveRegion)) {
102
+ document.body.removeChild(liveRegion);
103
+ }
104
+ }
105
+ };
106
+ };
107
+
108
+ /**
109
+ * Utility to focus an element safely with fallback
110
+ * @param {HTMLElement|string} element - Element or selector to focus
111
+ * @param {Object} options - Focus options
112
+ * @returns {boolean} Whether focus was successful
113
+ */
114
+ exports.createLiveRegion = createLiveRegion;
115
+ const safeFocus = (element, options = {}) => {
116
+ try {
117
+ const el = typeof element === 'string' ? document.querySelector(element) : element;
118
+ if (el && typeof el.focus === 'function') {
119
+ el.focus(options);
120
+ return document.activeElement === el;
121
+ }
122
+ return false;
123
+ } catch (error) {
124
+ console.warn('Failed to focus element:', error);
125
+ return false;
126
+ }
127
+ };
128
+ exports.safeFocus = safeFocus;