@a13y/react 0.1.2 → 0.1.3

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.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createContext, useRef, useEffect, useCallback, useId, useState, useContext } from 'react';
1
+ import React4, { createContext, useRef, useEffect, useCallback, useId, useState, cloneElement, useContext } from 'react';
2
2
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
3
  import { announce } from '@a13y/core/runtime/announce';
4
4
 
@@ -844,6 +844,2822 @@ var AccessibleTabs = (props) => {
844
844
  )
845
845
  ] });
846
846
  };
847
+ var idCounter = 0;
848
+ function useId2(prefix = "a13y") {
849
+ const idRef = useRef(void 0);
850
+ if (!idRef.current) {
851
+ idRef.current = `${prefix}-${++idCounter}`;
852
+ }
853
+ return idRef.current;
854
+ }
855
+ var AccessibleTooltip = ({
856
+ content,
857
+ placement = "top",
858
+ delay = 300,
859
+ trigger = "both",
860
+ children,
861
+ className = "",
862
+ style = {}
863
+ }) => {
864
+ const tooltipId = useId2("tooltip");
865
+ const [isVisible, setIsVisible] = useState(false);
866
+ const timeoutRef = useRef(void 0);
867
+ const triggerRef = useRef(null);
868
+ const showTooltip = () => {
869
+ if (timeoutRef.current) {
870
+ clearTimeout(timeoutRef.current);
871
+ }
872
+ timeoutRef.current = setTimeout(() => {
873
+ setIsVisible(true);
874
+ }, delay);
875
+ };
876
+ const hideTooltip = () => {
877
+ if (timeoutRef.current) {
878
+ clearTimeout(timeoutRef.current);
879
+ }
880
+ setIsVisible(false);
881
+ };
882
+ useEffect(() => {
883
+ return () => {
884
+ if (timeoutRef.current) {
885
+ clearTimeout(timeoutRef.current);
886
+ }
887
+ };
888
+ }, []);
889
+ useEffect(() => {
890
+ if (!isVisible) return;
891
+ const handleEscape = (event) => {
892
+ if (event.key === "Escape") {
893
+ hideTooltip();
894
+ }
895
+ };
896
+ document.addEventListener("keydown", handleEscape);
897
+ return () => document.removeEventListener("keydown", handleEscape);
898
+ }, [isVisible]);
899
+ const shouldTriggerOnHover = trigger === "hover" || trigger === "both";
900
+ const shouldTriggerOnFocus = trigger === "focus" || trigger === "both";
901
+ const childProps = {
902
+ ref: triggerRef,
903
+ "aria-describedby": isVisible ? tooltipId : void 0
904
+ };
905
+ if (shouldTriggerOnHover) {
906
+ childProps.onMouseEnter = () => showTooltip();
907
+ childProps.onMouseLeave = () => hideTooltip();
908
+ }
909
+ if (shouldTriggerOnFocus) {
910
+ childProps.onFocus = () => showTooltip();
911
+ childProps.onBlur = () => hideTooltip();
912
+ }
913
+ const getTooltipPosition = () => {
914
+ const baseStyles = {
915
+ position: "absolute",
916
+ zIndex: 1e3,
917
+ padding: "8px 12px",
918
+ backgroundColor: "#333",
919
+ color: "#fff",
920
+ borderRadius: "4px",
921
+ fontSize: "14px",
922
+ whiteSpace: "nowrap",
923
+ pointerEvents: "none"
924
+ };
925
+ switch (placement) {
926
+ case "top":
927
+ return { ...baseStyles, bottom: "100%", left: "50%", transform: "translateX(-50%)", marginBottom: "8px" };
928
+ case "bottom":
929
+ return { ...baseStyles, top: "100%", left: "50%", transform: "translateX(-50%)", marginTop: "8px" };
930
+ case "left":
931
+ return { ...baseStyles, right: "100%", top: "50%", transform: "translateY(-50%)", marginRight: "8px" };
932
+ case "right":
933
+ return { ...baseStyles, left: "100%", top: "50%", transform: "translateY(-50%)", marginLeft: "8px" };
934
+ default:
935
+ return baseStyles;
936
+ }
937
+ };
938
+ return /* @__PURE__ */ jsxs("span", { style: { position: "relative", display: "inline-block" }, children: [
939
+ cloneElement(children, childProps),
940
+ isVisible && /* @__PURE__ */ jsx(
941
+ "span",
942
+ {
943
+ role: "tooltip",
944
+ id: tooltipId,
945
+ className,
946
+ style: { ...getTooltipPosition(), ...style },
947
+ children: content
948
+ }
949
+ )
950
+ ] });
951
+ };
952
+ var AccessibleToggle = ({
953
+ checked,
954
+ onChange,
955
+ label,
956
+ description,
957
+ disabled = false,
958
+ className = "",
959
+ switchClassName = "",
960
+ style = {}
961
+ }) => {
962
+ const toggleId = useId2("toggle");
963
+ const descriptionId = useId2("toggle-description");
964
+ const handleKeyDown = (event) => {
965
+ if (event.key === " " || event.key === "Enter") {
966
+ event.preventDefault();
967
+ if (!disabled) {
968
+ onChange(!checked);
969
+ }
970
+ }
971
+ };
972
+ const handleClick = () => {
973
+ if (!disabled) {
974
+ onChange(!checked);
975
+ }
976
+ };
977
+ const containerStyles = {
978
+ display: "inline-flex",
979
+ alignItems: "center",
980
+ gap: "12px",
981
+ cursor: disabled ? "not-allowed" : "pointer",
982
+ opacity: disabled ? 0.6 : 1,
983
+ ...style
984
+ };
985
+ const switchStyles = {
986
+ position: "relative",
987
+ width: "44px",
988
+ height: "24px",
989
+ backgroundColor: checked ? "#007bff" : "#ccc",
990
+ borderRadius: "12px",
991
+ transition: "background-color 0.2s",
992
+ flexShrink: 0
993
+ };
994
+ const thumbStyles = {
995
+ position: "absolute",
996
+ top: "2px",
997
+ left: checked ? "22px" : "2px",
998
+ width: "20px",
999
+ height: "20px",
1000
+ backgroundColor: "#fff",
1001
+ borderRadius: "50%",
1002
+ transition: "left 0.2s"
1003
+ };
1004
+ const labelStyles = {
1005
+ display: "flex",
1006
+ flexDirection: "column",
1007
+ gap: "4px"
1008
+ };
1009
+ const descriptionStyles = {
1010
+ fontSize: "0.875rem",
1011
+ color: "#666"
1012
+ };
1013
+ return /* @__PURE__ */ jsxs(
1014
+ "div",
1015
+ {
1016
+ className,
1017
+ style: containerStyles,
1018
+ onClick: handleClick,
1019
+ children: [
1020
+ /* @__PURE__ */ jsx(
1021
+ "div",
1022
+ {
1023
+ role: "switch",
1024
+ "aria-checked": checked,
1025
+ "aria-labelledby": toggleId,
1026
+ "aria-describedby": description ? descriptionId : void 0,
1027
+ "aria-disabled": disabled,
1028
+ tabIndex: disabled ? -1 : 0,
1029
+ onKeyDown: handleKeyDown,
1030
+ className: switchClassName,
1031
+ style: switchStyles,
1032
+ children: /* @__PURE__ */ jsx("div", { style: thumbStyles })
1033
+ }
1034
+ ),
1035
+ /* @__PURE__ */ jsxs("div", { style: labelStyles, children: [
1036
+ /* @__PURE__ */ jsx(
1037
+ "label",
1038
+ {
1039
+ id: toggleId,
1040
+ htmlFor: toggleId,
1041
+ style: { fontWeight: 500, cursor: disabled ? "not-allowed" : "pointer" },
1042
+ children: label
1043
+ }
1044
+ ),
1045
+ description && /* @__PURE__ */ jsx("span", { id: descriptionId, style: descriptionStyles, children: description })
1046
+ ] })
1047
+ ]
1048
+ }
1049
+ );
1050
+ };
1051
+ var AccessibleToast = ({
1052
+ message,
1053
+ type = "info",
1054
+ duration = 5e3,
1055
+ position = "top-right",
1056
+ action,
1057
+ isOpen,
1058
+ onClose,
1059
+ className = "",
1060
+ style = {}
1061
+ }) => {
1062
+ const toastId = useId2("toast");
1063
+ const [isVisible, setIsVisible] = useState(isOpen);
1064
+ useEffect(() => {
1065
+ setIsVisible(isOpen);
1066
+ }, [isOpen]);
1067
+ useEffect(() => {
1068
+ if (!isVisible || duration === 0) return;
1069
+ const timer = setTimeout(() => {
1070
+ setIsVisible(false);
1071
+ onClose();
1072
+ }, duration);
1073
+ return () => clearTimeout(timer);
1074
+ }, [isVisible, duration, onClose]);
1075
+ const handleKeyDown = (event) => {
1076
+ if (event.key === "Escape") {
1077
+ setIsVisible(false);
1078
+ onClose();
1079
+ }
1080
+ };
1081
+ if (!isVisible) return null;
1082
+ const getAriaRole = () => {
1083
+ return type === "error" ? "alert" : "status";
1084
+ };
1085
+ const getAriaPoliteness = () => {
1086
+ return type === "error" ? "assertive" : "polite";
1087
+ };
1088
+ const getPositionStyles = () => {
1089
+ const base = {
1090
+ position: "fixed",
1091
+ zIndex: 9999
1092
+ };
1093
+ switch (position) {
1094
+ case "top-right":
1095
+ return { ...base, top: "16px", right: "16px" };
1096
+ case "top-left":
1097
+ return { ...base, top: "16px", left: "16px" };
1098
+ case "bottom-right":
1099
+ return { ...base, bottom: "16px", right: "16px" };
1100
+ case "bottom-left":
1101
+ return { ...base, bottom: "16px", left: "16px" };
1102
+ case "top-center":
1103
+ return { ...base, top: "16px", left: "50%", transform: "translateX(-50%)" };
1104
+ case "bottom-center":
1105
+ return { ...base, bottom: "16px", left: "50%", transform: "translateX(-50%)" };
1106
+ default:
1107
+ return base;
1108
+ }
1109
+ };
1110
+ const getTypeStyles = () => {
1111
+ const baseTypeStyles = {
1112
+ success: { backgroundColor: "#10b981", color: "#fff" },
1113
+ error: { backgroundColor: "#ef4444", color: "#fff" },
1114
+ warning: { backgroundColor: "#f59e0b", color: "#fff" },
1115
+ info: { backgroundColor: "#3b82f6", color: "#fff" }
1116
+ };
1117
+ return baseTypeStyles[type];
1118
+ };
1119
+ const toastStyles = {
1120
+ ...getPositionStyles(),
1121
+ ...getTypeStyles(),
1122
+ minWidth: "300px",
1123
+ maxWidth: "500px",
1124
+ padding: "16px",
1125
+ borderRadius: "8px",
1126
+ boxShadow: "0 4px 6px rgba(0, 0, 0, 0.1)",
1127
+ display: "flex",
1128
+ alignItems: "center",
1129
+ gap: "12px",
1130
+ animation: "slideIn 0.3s ease-out",
1131
+ ...style
1132
+ };
1133
+ const messageStyles = {
1134
+ flex: 1,
1135
+ fontSize: "14px",
1136
+ lineHeight: "1.5"
1137
+ };
1138
+ const buttonStyles = {
1139
+ background: "transparent",
1140
+ border: "1px solid currentColor",
1141
+ color: "inherit",
1142
+ padding: "4px 12px",
1143
+ borderRadius: "4px",
1144
+ fontSize: "14px",
1145
+ cursor: "pointer",
1146
+ transition: "opacity 0.2s"
1147
+ };
1148
+ const closeButtonStyles = {
1149
+ ...buttonStyles,
1150
+ border: "none",
1151
+ padding: "4px 8px",
1152
+ fontSize: "18px",
1153
+ lineHeight: "1"
1154
+ };
1155
+ return /* @__PURE__ */ jsxs(
1156
+ "div",
1157
+ {
1158
+ id: toastId,
1159
+ role: getAriaRole(),
1160
+ "aria-live": getAriaPoliteness(),
1161
+ "aria-atomic": "true",
1162
+ className,
1163
+ style: toastStyles,
1164
+ onKeyDown: handleKeyDown,
1165
+ tabIndex: -1,
1166
+ children: [
1167
+ /* @__PURE__ */ jsx("div", { style: messageStyles, children: message }),
1168
+ action && /* @__PURE__ */ jsx(
1169
+ "button",
1170
+ {
1171
+ type: "button",
1172
+ onClick: () => {
1173
+ action.onClick();
1174
+ setIsVisible(false);
1175
+ onClose();
1176
+ },
1177
+ style: buttonStyles,
1178
+ onMouseEnter: (e) => e.currentTarget.style.opacity = "0.8",
1179
+ onMouseLeave: (e) => e.currentTarget.style.opacity = "1",
1180
+ children: action.label
1181
+ }
1182
+ ),
1183
+ /* @__PURE__ */ jsx(
1184
+ "button",
1185
+ {
1186
+ type: "button",
1187
+ onClick: () => {
1188
+ setIsVisible(false);
1189
+ onClose();
1190
+ },
1191
+ "aria-label": "Close notification",
1192
+ style: closeButtonStyles,
1193
+ onMouseEnter: (e) => e.currentTarget.style.opacity = "0.8",
1194
+ onMouseLeave: (e) => e.currentTarget.style.opacity = "1",
1195
+ children: "\xD7"
1196
+ }
1197
+ )
1198
+ ]
1199
+ }
1200
+ );
1201
+ };
1202
+ var ToastContainer = ({
1203
+ toasts,
1204
+ onRemove,
1205
+ position = "top-right"
1206
+ }) => {
1207
+ return /* @__PURE__ */ jsx(Fragment, { children: toasts.map((toast) => /* @__PURE__ */ jsx(
1208
+ AccessibleToast,
1209
+ {
1210
+ message: toast.message,
1211
+ type: toast.type,
1212
+ duration: toast.duration,
1213
+ position,
1214
+ action: toast.action,
1215
+ isOpen: true,
1216
+ onClose: () => onRemove(toast.id)
1217
+ },
1218
+ toast.id
1219
+ )) });
1220
+ };
1221
+ var AccessibleAccordion = ({
1222
+ items,
1223
+ allowMultiple = false,
1224
+ defaultOpenItems = [],
1225
+ onToggle,
1226
+ className = "",
1227
+ itemClassName = "",
1228
+ headerClassName = "",
1229
+ contentClassName = "",
1230
+ style = {}
1231
+ }) => {
1232
+ const [openItems, setOpenItems] = useState(
1233
+ new Set(defaultOpenItems)
1234
+ );
1235
+ const toggleItem = (itemId) => {
1236
+ setOpenItems((prev) => {
1237
+ const newOpenItems = new Set(prev);
1238
+ if (newOpenItems.has(itemId)) {
1239
+ newOpenItems.delete(itemId);
1240
+ onToggle?.(itemId, false);
1241
+ } else {
1242
+ if (!allowMultiple) {
1243
+ newOpenItems.clear();
1244
+ }
1245
+ newOpenItems.add(itemId);
1246
+ onToggle?.(itemId, true);
1247
+ }
1248
+ return newOpenItems;
1249
+ });
1250
+ };
1251
+ const handleKeyDown = (event, itemId, index) => {
1252
+ const item = items.find((i) => i.id === itemId);
1253
+ if (item?.disabled) return;
1254
+ switch (event.key) {
1255
+ case "Enter":
1256
+ case " ":
1257
+ event.preventDefault();
1258
+ toggleItem(itemId);
1259
+ break;
1260
+ case "ArrowDown":
1261
+ event.preventDefault();
1262
+ focusNextItem(index);
1263
+ break;
1264
+ case "ArrowUp":
1265
+ event.preventDefault();
1266
+ focusPreviousItem(index);
1267
+ break;
1268
+ case "Home":
1269
+ event.preventDefault();
1270
+ focusFirstItem();
1271
+ break;
1272
+ case "End":
1273
+ event.preventDefault();
1274
+ focusLastItem();
1275
+ break;
1276
+ }
1277
+ };
1278
+ const focusNextItem = (currentIndex) => {
1279
+ const nextIndex = (currentIndex + 1) % items.length;
1280
+ focusItemByIndex(nextIndex);
1281
+ };
1282
+ const focusPreviousItem = (currentIndex) => {
1283
+ const prevIndex = currentIndex === 0 ? items.length - 1 : currentIndex - 1;
1284
+ focusItemByIndex(prevIndex);
1285
+ };
1286
+ const focusFirstItem = () => {
1287
+ focusItemByIndex(0);
1288
+ };
1289
+ const focusLastItem = () => {
1290
+ focusItemByIndex(items.length - 1);
1291
+ };
1292
+ const focusItemByIndex = (index) => {
1293
+ const button = document.querySelector(
1294
+ `[data-accordion-button][data-index="${index}"]`
1295
+ );
1296
+ button?.focus();
1297
+ };
1298
+ const containerStyles = {
1299
+ border: "1px solid #e5e7eb",
1300
+ borderRadius: "8px",
1301
+ overflow: "hidden",
1302
+ ...style
1303
+ };
1304
+ const itemStyles = {
1305
+ borderBottom: "1px solid #e5e7eb"
1306
+ };
1307
+ const headerStyles = {
1308
+ width: "100%",
1309
+ padding: "16px",
1310
+ backgroundColor: "#fff",
1311
+ border: "none",
1312
+ textAlign: "left",
1313
+ fontSize: "16px",
1314
+ fontWeight: 500,
1315
+ cursor: "pointer",
1316
+ display: "flex",
1317
+ justifyContent: "space-between",
1318
+ alignItems: "center",
1319
+ transition: "background-color 0.2s"
1320
+ };
1321
+ const contentStyles = {
1322
+ padding: "16px",
1323
+ backgroundColor: "#f9fafb"
1324
+ };
1325
+ const iconStyles = (isOpen) => ({
1326
+ transition: "transform 0.2s",
1327
+ transform: isOpen ? "rotate(180deg)" : "rotate(0deg)"
1328
+ });
1329
+ return /* @__PURE__ */ jsx("div", { className, style: containerStyles, children: items.map((item, index) => {
1330
+ const isOpen = openItems.has(item.id);
1331
+ const buttonId = useId2(`accordion-button-${item.id}`);
1332
+ const panelId = useId2(`accordion-panel-${item.id}`);
1333
+ return /* @__PURE__ */ jsxs(
1334
+ "div",
1335
+ {
1336
+ className: itemClassName,
1337
+ style: { ...itemStyles, borderBottom: index === items.length - 1 ? "none" : itemStyles.borderBottom },
1338
+ children: [
1339
+ /* @__PURE__ */ jsx("h3", { style: { margin: 0 }, children: /* @__PURE__ */ jsxs(
1340
+ "button",
1341
+ {
1342
+ id: buttonId,
1343
+ type: "button",
1344
+ "aria-expanded": isOpen,
1345
+ "aria-controls": panelId,
1346
+ "aria-disabled": item.disabled,
1347
+ disabled: item.disabled,
1348
+ onClick: () => !item.disabled && toggleItem(item.id),
1349
+ onKeyDown: (e) => handleKeyDown(e, item.id, index),
1350
+ className: headerClassName,
1351
+ style: {
1352
+ ...headerStyles,
1353
+ cursor: item.disabled ? "not-allowed" : "pointer",
1354
+ opacity: item.disabled ? 0.6 : 1
1355
+ },
1356
+ "data-accordion-button": true,
1357
+ "data-index": index,
1358
+ onMouseEnter: (e) => !item.disabled && (e.currentTarget.style.backgroundColor = "#f9fafb"),
1359
+ onMouseLeave: (e) => e.currentTarget.style.backgroundColor = "#fff",
1360
+ children: [
1361
+ /* @__PURE__ */ jsx("span", { children: item.title }),
1362
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", style: iconStyles(isOpen), children: "\u25BC" })
1363
+ ]
1364
+ }
1365
+ ) }),
1366
+ isOpen && /* @__PURE__ */ jsx(
1367
+ "div",
1368
+ {
1369
+ id: panelId,
1370
+ role: "region",
1371
+ "aria-labelledby": buttonId,
1372
+ className: contentClassName,
1373
+ style: contentStyles,
1374
+ children: item.content
1375
+ }
1376
+ )
1377
+ ]
1378
+ },
1379
+ item.id
1380
+ );
1381
+ }) });
1382
+ };
1383
+ var AccessibleBreadcrumb = ({
1384
+ items,
1385
+ separator = "/",
1386
+ ariaLabel = "Breadcrumb",
1387
+ className = "",
1388
+ itemClassName = "",
1389
+ style = {}
1390
+ }) => {
1391
+ if (items.length === 0) return null;
1392
+ const navStyles = {
1393
+ ...style
1394
+ };
1395
+ const listStyles = {
1396
+ display: "flex",
1397
+ alignItems: "center",
1398
+ gap: "8px",
1399
+ listStyle: "none",
1400
+ margin: 0,
1401
+ padding: 0,
1402
+ flexWrap: "wrap"
1403
+ };
1404
+ const itemStyles = {
1405
+ display: "flex",
1406
+ alignItems: "center",
1407
+ gap: "8px"
1408
+ };
1409
+ const linkStyles = {
1410
+ color: "#3b82f6",
1411
+ textDecoration: "none",
1412
+ transition: "color 0.2s"
1413
+ };
1414
+ const currentStyles = {
1415
+ color: "#6b7280",
1416
+ fontWeight: 500
1417
+ };
1418
+ const separatorStyles = {
1419
+ color: "#9ca3af",
1420
+ userSelect: "none"
1421
+ };
1422
+ return /* @__PURE__ */ jsx("nav", { "aria-label": ariaLabel, className, style: navStyles, children: /* @__PURE__ */ jsx("ol", { style: listStyles, children: items.map((item, index) => {
1423
+ const isLast = index === items.length - 1;
1424
+ const isCurrent = isLast;
1425
+ return /* @__PURE__ */ jsxs("li", { className: itemClassName, style: itemStyles, children: [
1426
+ isCurrent ? /* @__PURE__ */ jsx("span", { "aria-current": "page", style: currentStyles, children: item.label }) : item.href ? /* @__PURE__ */ jsx(
1427
+ "a",
1428
+ {
1429
+ href: item.href,
1430
+ style: linkStyles,
1431
+ onMouseEnter: (e) => e.currentTarget.style.color = "#2563eb",
1432
+ onMouseLeave: (e) => e.currentTarget.style.color = "#3b82f6",
1433
+ children: item.label
1434
+ }
1435
+ ) : item.onClick ? /* @__PURE__ */ jsx(
1436
+ "button",
1437
+ {
1438
+ type: "button",
1439
+ onClick: item.onClick,
1440
+ style: {
1441
+ ...linkStyles,
1442
+ background: "transparent",
1443
+ border: "none",
1444
+ padding: 0,
1445
+ cursor: "pointer",
1446
+ font: "inherit"
1447
+ },
1448
+ onMouseEnter: (e) => e.currentTarget.style.color = "#2563eb",
1449
+ onMouseLeave: (e) => e.currentTarget.style.color = "#3b82f6",
1450
+ children: item.label
1451
+ }
1452
+ ) : /* @__PURE__ */ jsx("span", { style: currentStyles, children: item.label }),
1453
+ !isLast && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", style: separatorStyles, children: separator })
1454
+ ] }, index);
1455
+ }) }) });
1456
+ };
1457
+ var AccessibleCheckboxGroup = ({
1458
+ options,
1459
+ value,
1460
+ onChange,
1461
+ label,
1462
+ description,
1463
+ required = false,
1464
+ disabled = false,
1465
+ error,
1466
+ className = "",
1467
+ style = {}
1468
+ }) => {
1469
+ const descriptionId = useId2("checkbox-description");
1470
+ const errorId = useId2("checkbox-error");
1471
+ const handleChange = (optionValue, checked) => {
1472
+ if (disabled) return;
1473
+ const newValue = checked ? [...value, optionValue] : value.filter((v) => v !== optionValue);
1474
+ onChange(newValue);
1475
+ };
1476
+ const fieldsetStyles = {
1477
+ border: "1px solid #e5e7eb",
1478
+ borderRadius: "8px",
1479
+ padding: "16px",
1480
+ margin: 0,
1481
+ ...style
1482
+ };
1483
+ const legendStyles = {
1484
+ fontWeight: 600,
1485
+ fontSize: "16px",
1486
+ marginBottom: "8px",
1487
+ padding: "0 4px"
1488
+ };
1489
+ const descriptionStyles = {
1490
+ fontSize: "14px",
1491
+ color: "#6b7280",
1492
+ marginBottom: "12px"
1493
+ };
1494
+ const optionContainerStyles = {
1495
+ display: "flex",
1496
+ flexDirection: "column",
1497
+ gap: "12px"
1498
+ };
1499
+ const optionStyles = {
1500
+ display: "flex",
1501
+ alignItems: "flex-start",
1502
+ gap: "8px"
1503
+ };
1504
+ const checkboxStyles = {
1505
+ width: "18px",
1506
+ height: "18px",
1507
+ marginTop: "2px",
1508
+ cursor: disabled ? "not-allowed" : "pointer"
1509
+ };
1510
+ const labelStyles = {
1511
+ display: "flex",
1512
+ flexDirection: "column",
1513
+ gap: "2px",
1514
+ cursor: disabled ? "not-allowed" : "pointer",
1515
+ flex: 1
1516
+ };
1517
+ const optionDescriptionStyles = {
1518
+ fontSize: "13px",
1519
+ color: "#9ca3af"
1520
+ };
1521
+ const errorStyles = {
1522
+ color: "#ef4444",
1523
+ fontSize: "14px",
1524
+ marginTop: "8px"
1525
+ };
1526
+ return /* @__PURE__ */ jsxs(
1527
+ "fieldset",
1528
+ {
1529
+ className,
1530
+ style: fieldsetStyles,
1531
+ "aria-describedby": [description && descriptionId, error && errorId].filter(Boolean).join(" ") || void 0,
1532
+ disabled,
1533
+ children: [
1534
+ /* @__PURE__ */ jsxs("legend", { style: legendStyles, children: [
1535
+ label,
1536
+ required && /* @__PURE__ */ jsxs("span", { "aria-label": "required", style: { color: "#ef4444" }, children: [
1537
+ " ",
1538
+ "*"
1539
+ ] })
1540
+ ] }),
1541
+ description && /* @__PURE__ */ jsx("div", { id: descriptionId, style: descriptionStyles, children: description }),
1542
+ /* @__PURE__ */ jsx("div", { style: optionContainerStyles, role: "group", children: options.map((option) => {
1543
+ const checkboxId = useId2(`checkbox-${option.value}`);
1544
+ const optionDescId = option.description ? useId2(`checkbox-desc-${option.value}`) : void 0;
1545
+ const isChecked = value.includes(option.value);
1546
+ const isDisabled = disabled || option.disabled;
1547
+ return /* @__PURE__ */ jsxs("div", { style: optionStyles, children: [
1548
+ /* @__PURE__ */ jsx(
1549
+ "input",
1550
+ {
1551
+ type: "checkbox",
1552
+ id: checkboxId,
1553
+ checked: isChecked,
1554
+ onChange: (e) => handleChange(option.value, e.target.checked),
1555
+ disabled: isDisabled,
1556
+ "aria-describedby": optionDescId,
1557
+ style: checkboxStyles
1558
+ }
1559
+ ),
1560
+ /* @__PURE__ */ jsxs("label", { htmlFor: checkboxId, style: { ...labelStyles, opacity: isDisabled ? 0.6 : 1 }, children: [
1561
+ /* @__PURE__ */ jsx("span", { style: { fontWeight: 500 }, children: option.label }),
1562
+ option.description && /* @__PURE__ */ jsx("span", { id: optionDescId, style: optionDescriptionStyles, children: option.description })
1563
+ ] })
1564
+ ] }, option.value);
1565
+ }) }),
1566
+ error && /* @__PURE__ */ jsx("div", { id: errorId, role: "alert", style: errorStyles, children: error })
1567
+ ]
1568
+ }
1569
+ );
1570
+ };
1571
+ var AccessibleRadioGroup = ({
1572
+ options,
1573
+ value,
1574
+ onChange,
1575
+ name,
1576
+ label,
1577
+ description,
1578
+ required = false,
1579
+ disabled = false,
1580
+ error,
1581
+ className = "",
1582
+ style = {}
1583
+ }) => {
1584
+ const groupId = useId2("radio-group");
1585
+ const descriptionId = useId2("radio-description");
1586
+ const errorId = useId2("radio-error");
1587
+ const handleKeyDown = (event, currentIndex) => {
1588
+ let nextIndex = currentIndex;
1589
+ switch (event.key) {
1590
+ case "ArrowDown":
1591
+ case "ArrowRight":
1592
+ event.preventDefault();
1593
+ nextIndex = (currentIndex + 1) % options.length;
1594
+ break;
1595
+ case "ArrowUp":
1596
+ case "ArrowLeft":
1597
+ event.preventDefault();
1598
+ nextIndex = currentIndex === 0 ? options.length - 1 : currentIndex - 1;
1599
+ break;
1600
+ default:
1601
+ return;
1602
+ }
1603
+ while (options[nextIndex]?.disabled && nextIndex !== currentIndex) {
1604
+ if (event.key === "ArrowDown" || event.key === "ArrowRight") {
1605
+ nextIndex = (nextIndex + 1) % options.length;
1606
+ } else {
1607
+ nextIndex = nextIndex === 0 ? options.length - 1 : nextIndex - 1;
1608
+ }
1609
+ }
1610
+ if (!options[nextIndex]?.disabled) {
1611
+ onChange(options[nextIndex].value);
1612
+ const nextRadio = document.getElementById(`${groupId}-${options[nextIndex].value}`);
1613
+ nextRadio?.focus();
1614
+ }
1615
+ };
1616
+ const fieldsetStyles = {
1617
+ border: "1px solid #e5e7eb",
1618
+ borderRadius: "8px",
1619
+ padding: "16px",
1620
+ margin: 0,
1621
+ ...style
1622
+ };
1623
+ const legendStyles = {
1624
+ fontWeight: 600,
1625
+ fontSize: "16px",
1626
+ marginBottom: "8px",
1627
+ padding: "0 4px"
1628
+ };
1629
+ const descriptionStyles = {
1630
+ fontSize: "14px",
1631
+ color: "#6b7280",
1632
+ marginBottom: "12px"
1633
+ };
1634
+ const optionContainerStyles = {
1635
+ display: "flex",
1636
+ flexDirection: "column",
1637
+ gap: "12px"
1638
+ };
1639
+ const optionStyles = {
1640
+ display: "flex",
1641
+ alignItems: "flex-start",
1642
+ gap: "8px"
1643
+ };
1644
+ const radioStyles = {
1645
+ width: "18px",
1646
+ height: "18px",
1647
+ marginTop: "2px",
1648
+ cursor: disabled ? "not-allowed" : "pointer"
1649
+ };
1650
+ const labelStyles = {
1651
+ display: "flex",
1652
+ flexDirection: "column",
1653
+ gap: "2px",
1654
+ cursor: disabled ? "not-allowed" : "pointer",
1655
+ flex: 1
1656
+ };
1657
+ const optionDescriptionStyles = {
1658
+ fontSize: "13px",
1659
+ color: "#9ca3af"
1660
+ };
1661
+ const errorStyles = {
1662
+ color: "#ef4444",
1663
+ fontSize: "14px",
1664
+ marginTop: "8px"
1665
+ };
1666
+ return /* @__PURE__ */ jsxs(
1667
+ "fieldset",
1668
+ {
1669
+ className,
1670
+ style: fieldsetStyles,
1671
+ "aria-describedby": [description && descriptionId, error && errorId].filter(Boolean).join(" ") || void 0,
1672
+ disabled,
1673
+ children: [
1674
+ /* @__PURE__ */ jsxs("legend", { style: legendStyles, children: [
1675
+ label,
1676
+ required && /* @__PURE__ */ jsxs("span", { "aria-label": "required", style: { color: "#ef4444" }, children: [
1677
+ " ",
1678
+ "*"
1679
+ ] })
1680
+ ] }),
1681
+ description && /* @__PURE__ */ jsx("div", { id: descriptionId, style: descriptionStyles, children: description }),
1682
+ /* @__PURE__ */ jsx("div", { style: optionContainerStyles, role: "radiogroup", "aria-labelledby": groupId, children: options.map((option, index) => {
1683
+ const radioId = `${groupId}-${option.value}`;
1684
+ const optionDescId = option.description ? useId2(`radio-desc-${option.value}`) : void 0;
1685
+ const isChecked = value === option.value;
1686
+ const isDisabled = disabled || option.disabled;
1687
+ return /* @__PURE__ */ jsxs("div", { style: optionStyles, children: [
1688
+ /* @__PURE__ */ jsx(
1689
+ "input",
1690
+ {
1691
+ type: "radio",
1692
+ id: radioId,
1693
+ name,
1694
+ value: option.value,
1695
+ checked: isChecked,
1696
+ onChange: (e) => onChange(e.target.value),
1697
+ onKeyDown: (e) => handleKeyDown(e, index),
1698
+ disabled: isDisabled,
1699
+ "aria-describedby": optionDescId,
1700
+ style: radioStyles,
1701
+ tabIndex: isChecked ? 0 : -1
1702
+ }
1703
+ ),
1704
+ /* @__PURE__ */ jsxs("label", { htmlFor: radioId, style: { ...labelStyles, opacity: isDisabled ? 0.6 : 1 }, children: [
1705
+ /* @__PURE__ */ jsx("span", { style: { fontWeight: 500 }, children: option.label }),
1706
+ option.description && /* @__PURE__ */ jsx("span", { id: optionDescId, style: optionDescriptionStyles, children: option.description })
1707
+ ] })
1708
+ ] }, option.value);
1709
+ }) }),
1710
+ error && /* @__PURE__ */ jsx("div", { id: errorId, role: "alert", style: errorStyles, children: error })
1711
+ ]
1712
+ }
1713
+ );
1714
+ };
1715
+ function useAriaLive(politeness = "polite") {
1716
+ const [message, setMessage] = useState("");
1717
+ const timeoutRef = useRef(void 0);
1718
+ const clearMessage = () => {
1719
+ setMessage("");
1720
+ if (timeoutRef.current) {
1721
+ clearTimeout(timeoutRef.current);
1722
+ }
1723
+ };
1724
+ const setMessageWithClear = (newMessage) => {
1725
+ setMessage(newMessage);
1726
+ if (timeoutRef.current) {
1727
+ clearTimeout(timeoutRef.current);
1728
+ }
1729
+ timeoutRef.current = setTimeout(() => {
1730
+ setMessage("");
1731
+ }, 5e3);
1732
+ };
1733
+ useEffect(() => {
1734
+ return () => {
1735
+ if (timeoutRef.current) {
1736
+ clearTimeout(timeoutRef.current);
1737
+ }
1738
+ };
1739
+ }, []);
1740
+ return {
1741
+ setMessage: setMessageWithClear,
1742
+ clearMessage,
1743
+ message,
1744
+ liveRegionProps: {
1745
+ role: politeness === "assertive" ? "alert" : "status",
1746
+ "aria-live": politeness,
1747
+ "aria-atomic": "true",
1748
+ "aria-relevant": "additions text"
1749
+ }
1750
+ };
1751
+ }
1752
+ var AccessibleProgress = ({
1753
+ value,
1754
+ max = 100,
1755
+ min = 0,
1756
+ label,
1757
+ variant = "linear",
1758
+ showValue = false,
1759
+ announceChanges = false,
1760
+ className = "",
1761
+ style = {}
1762
+ }) => {
1763
+ const progressId = useId2("progress");
1764
+ const labelId = useId2("progress-label");
1765
+ const { setMessage, liveRegionProps, message } = useAriaLive("polite");
1766
+ const isIndeterminate = value === void 0;
1767
+ const percentage = isIndeterminate ? 0 : Math.round((value - min) / (max - min) * 100);
1768
+ React4.useEffect(() => {
1769
+ if (announceChanges && !isIndeterminate && value !== void 0) {
1770
+ const milestones = [25, 50, 75, 100];
1771
+ if (milestones.includes(percentage)) {
1772
+ setMessage(`${label}: ${percentage}% complete`);
1773
+ }
1774
+ }
1775
+ }, [percentage, announceChanges, isIndeterminate, value, label, setMessage]);
1776
+ const containerStyles = {
1777
+ display: "inline-flex",
1778
+ flexDirection: variant === "linear" ? "column" : "row",
1779
+ alignItems: variant === "linear" ? "stretch" : "center",
1780
+ gap: variant === "linear" ? "8px" : "12px",
1781
+ width: variant === "linear" ? "100%" : "auto",
1782
+ ...style
1783
+ };
1784
+ const labelContainerStyles = {
1785
+ display: "flex",
1786
+ justifyContent: "space-between",
1787
+ alignItems: "center",
1788
+ fontSize: "14px",
1789
+ fontWeight: 500
1790
+ };
1791
+ const renderLinearProgress = () => {
1792
+ const trackStyles = {
1793
+ width: "100%",
1794
+ height: "8px",
1795
+ backgroundColor: "#e5e7eb",
1796
+ borderRadius: "4px",
1797
+ overflow: "hidden",
1798
+ position: "relative"
1799
+ };
1800
+ const barStyles = {
1801
+ height: "100%",
1802
+ backgroundColor: "#3b82f6",
1803
+ transition: isIndeterminate ? "none" : "width 0.3s ease",
1804
+ width: isIndeterminate ? "30%" : `${percentage}%`,
1805
+ animation: isIndeterminate ? "indeterminateLinear 1.5s infinite ease-in-out" : "none"
1806
+ };
1807
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1808
+ /* @__PURE__ */ jsx("style", { children: `
1809
+ @keyframes indeterminateLinear {
1810
+ 0% { transform: translateX(-100%); }
1811
+ 100% { transform: translateX(400%); }
1812
+ }
1813
+ ` }),
1814
+ /* @__PURE__ */ jsxs("div", { style: labelContainerStyles, children: [
1815
+ /* @__PURE__ */ jsx("span", { id: labelId, children: label }),
1816
+ showValue && !isIndeterminate && /* @__PURE__ */ jsxs("span", { children: [
1817
+ percentage,
1818
+ "%"
1819
+ ] })
1820
+ ] }),
1821
+ /* @__PURE__ */ jsx("div", { style: trackStyles, children: /* @__PURE__ */ jsx("div", { style: barStyles }) })
1822
+ ] });
1823
+ };
1824
+ const renderCircularProgress = () => {
1825
+ const size = 48;
1826
+ const strokeWidth = 4;
1827
+ const radius = (size - strokeWidth) / 2;
1828
+ const circumference = 2 * Math.PI * radius;
1829
+ const offset = isIndeterminate ? circumference * 0.25 : circumference - percentage / 100 * circumference;
1830
+ const svgStyles = {
1831
+ transform: "rotate(-90deg)",
1832
+ animation: isIndeterminate ? "spin 1s linear infinite" : "none"
1833
+ };
1834
+ const circleStyles = {
1835
+ transition: isIndeterminate ? "none" : "stroke-dashoffset 0.3s ease"
1836
+ };
1837
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1838
+ /* @__PURE__ */ jsx("style", { children: `
1839
+ @keyframes spin {
1840
+ from { transform: rotate(-90deg); }
1841
+ to { transform: rotate(270deg); }
1842
+ }
1843
+ ` }),
1844
+ /* @__PURE__ */ jsxs("svg", { width: size, height: size, style: svgStyles, children: [
1845
+ /* @__PURE__ */ jsx(
1846
+ "circle",
1847
+ {
1848
+ cx: size / 2,
1849
+ cy: size / 2,
1850
+ r: radius,
1851
+ fill: "none",
1852
+ stroke: "#e5e7eb",
1853
+ strokeWidth
1854
+ }
1855
+ ),
1856
+ /* @__PURE__ */ jsx(
1857
+ "circle",
1858
+ {
1859
+ cx: size / 2,
1860
+ cy: size / 2,
1861
+ r: radius,
1862
+ fill: "none",
1863
+ stroke: "#3b82f6",
1864
+ strokeWidth,
1865
+ strokeDasharray: circumference,
1866
+ strokeDashoffset: offset,
1867
+ strokeLinecap: "round",
1868
+ style: circleStyles
1869
+ }
1870
+ )
1871
+ ] }),
1872
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "4px" }, children: [
1873
+ /* @__PURE__ */ jsx("span", { id: labelId, style: { fontSize: "14px", fontWeight: 500 }, children: label }),
1874
+ showValue && !isIndeterminate && /* @__PURE__ */ jsxs("span", { style: { fontSize: "12px", color: "#6b7280" }, children: [
1875
+ percentage,
1876
+ "%"
1877
+ ] })
1878
+ ] })
1879
+ ] });
1880
+ };
1881
+ return /* @__PURE__ */ jsxs("div", { className, style: containerStyles, children: [
1882
+ /* @__PURE__ */ jsx(
1883
+ "div",
1884
+ {
1885
+ id: progressId,
1886
+ role: "progressbar",
1887
+ "aria-labelledby": labelId,
1888
+ "aria-valuenow": isIndeterminate ? void 0 : value,
1889
+ "aria-valuemin": min,
1890
+ "aria-valuemax": max,
1891
+ "aria-valuetext": isIndeterminate ? "Loading..." : `${percentage}%`,
1892
+ children: variant === "linear" ? renderLinearProgress() : renderCircularProgress()
1893
+ }
1894
+ ),
1895
+ announceChanges && /* @__PURE__ */ jsx("div", { ...liveRegionProps, style: { position: "absolute", left: "-10000px", width: "1px", height: "1px", overflow: "hidden" }, children: message })
1896
+ ] });
1897
+ };
1898
+ var SkipLinks = ({
1899
+ links,
1900
+ className = "",
1901
+ style = {},
1902
+ linkStyle = {}
1903
+ }) => {
1904
+ if (links.length === 0) return null;
1905
+ const containerStyles = {
1906
+ position: "fixed",
1907
+ top: 0,
1908
+ left: 0,
1909
+ zIndex: 1e4,
1910
+ ...style
1911
+ };
1912
+ const listStyles = {
1913
+ listStyle: "none",
1914
+ margin: 0,
1915
+ padding: 0,
1916
+ display: "flex",
1917
+ flexDirection: "column",
1918
+ gap: "4px"
1919
+ };
1920
+ const defaultLinkStyles = {
1921
+ position: "absolute",
1922
+ left: "-10000px",
1923
+ top: "auto",
1924
+ width: "1px",
1925
+ height: "1px",
1926
+ overflow: "hidden",
1927
+ display: "block",
1928
+ padding: "12px 24px",
1929
+ backgroundColor: "#000",
1930
+ color: "#fff",
1931
+ textDecoration: "none",
1932
+ fontWeight: 600,
1933
+ fontSize: "14px",
1934
+ borderRadius: "0 0 4px 0",
1935
+ transition: "none",
1936
+ ...linkStyle
1937
+ };
1938
+ const focusStyles = {
1939
+ position: "static",
1940
+ width: "auto",
1941
+ height: "auto",
1942
+ overflow: "visible",
1943
+ left: "auto"
1944
+ };
1945
+ const handleClick = (e, href) => {
1946
+ e.preventDefault();
1947
+ const target = document.getElementById(href);
1948
+ if (target) {
1949
+ target.scrollIntoView({ behavior: "smooth", block: "start" });
1950
+ const originalTabIndex = target.getAttribute("tabindex");
1951
+ if (!target.hasAttribute("tabindex")) {
1952
+ target.setAttribute("tabindex", "-1");
1953
+ }
1954
+ target.focus();
1955
+ if (originalTabIndex === null) {
1956
+ setTimeout(() => {
1957
+ target.removeAttribute("tabindex");
1958
+ }, 100);
1959
+ }
1960
+ }
1961
+ };
1962
+ return /* @__PURE__ */ jsx(
1963
+ "nav",
1964
+ {
1965
+ "aria-label": "Skip navigation",
1966
+ className,
1967
+ style: containerStyles,
1968
+ children: /* @__PURE__ */ jsx("ul", { style: listStyles, children: links.map((link, index) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
1969
+ "a",
1970
+ {
1971
+ href: `#${link.href}`,
1972
+ onClick: (e) => handleClick(e, link.href),
1973
+ style: defaultLinkStyles,
1974
+ onFocus: (e) => {
1975
+ Object.assign(e.currentTarget.style, focusStyles);
1976
+ },
1977
+ onBlur: (e) => {
1978
+ Object.assign(e.currentTarget.style, defaultLinkStyles);
1979
+ },
1980
+ children: link.label
1981
+ }
1982
+ ) }, index)) })
1983
+ }
1984
+ );
1985
+ };
1986
+ var AccessiblePagination = ({
1987
+ currentPage,
1988
+ totalPages,
1989
+ onPageChange,
1990
+ siblingCount = 1,
1991
+ showFirstLast = false,
1992
+ ariaLabel = "Pagination",
1993
+ className = "",
1994
+ style = {}
1995
+ }) => {
1996
+ if (totalPages <= 1) return null;
1997
+ const generatePageNumbers = () => {
1998
+ const pages2 = [];
1999
+ pages2.push(1);
2000
+ const leftSiblingIndex = Math.max(currentPage - siblingCount, 2);
2001
+ const rightSiblingIndex = Math.min(currentPage + siblingCount, totalPages - 1);
2002
+ if (leftSiblingIndex > 2) {
2003
+ pages2.push("...");
2004
+ }
2005
+ for (let i = leftSiblingIndex; i <= rightSiblingIndex; i++) {
2006
+ pages2.push(i);
2007
+ }
2008
+ if (rightSiblingIndex < totalPages - 1) {
2009
+ pages2.push("...");
2010
+ }
2011
+ if (totalPages > 1) {
2012
+ pages2.push(totalPages);
2013
+ }
2014
+ return pages2;
2015
+ };
2016
+ const pages = generatePageNumbers();
2017
+ const navStyles = {
2018
+ ...style
2019
+ };
2020
+ const listStyles = {
2021
+ display: "flex",
2022
+ alignItems: "center",
2023
+ gap: "4px",
2024
+ listStyle: "none",
2025
+ margin: 0,
2026
+ padding: 0
2027
+ };
2028
+ const buttonBaseStyles = {
2029
+ minWidth: "40px",
2030
+ height: "40px",
2031
+ padding: "8px 12px",
2032
+ border: "1px solid #e5e7eb",
2033
+ backgroundColor: "#fff",
2034
+ borderRadius: "6px",
2035
+ fontSize: "14px",
2036
+ fontWeight: 500,
2037
+ cursor: "pointer",
2038
+ transition: "all 0.2s",
2039
+ display: "flex",
2040
+ alignItems: "center",
2041
+ justifyContent: "center"
2042
+ };
2043
+ const activeButtonStyles = {
2044
+ ...buttonBaseStyles,
2045
+ backgroundColor: "#3b82f6",
2046
+ color: "#fff",
2047
+ borderColor: "#3b82f6"
2048
+ };
2049
+ const disabledButtonStyles = {
2050
+ ...buttonBaseStyles,
2051
+ opacity: 0.5,
2052
+ cursor: "not-allowed"
2053
+ };
2054
+ const ellipsisStyles = {
2055
+ minWidth: "40px",
2056
+ height: "40px",
2057
+ display: "flex",
2058
+ alignItems: "center",
2059
+ justifyContent: "center",
2060
+ color: "#9ca3af"
2061
+ };
2062
+ const handlePageChange = (page) => {
2063
+ if (page >= 1 && page <= totalPages && page !== currentPage) {
2064
+ onPageChange(page);
2065
+ }
2066
+ };
2067
+ return /* @__PURE__ */ jsx("nav", { "aria-label": ariaLabel, className, style: navStyles, children: /* @__PURE__ */ jsxs("ul", { style: listStyles, children: [
2068
+ showFirstLast && /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
2069
+ "button",
2070
+ {
2071
+ type: "button",
2072
+ onClick: () => handlePageChange(1),
2073
+ disabled: currentPage === 1,
2074
+ "aria-label": "Go to first page",
2075
+ style: currentPage === 1 ? disabledButtonStyles : buttonBaseStyles,
2076
+ onMouseEnter: (e) => {
2077
+ if (currentPage !== 1) {
2078
+ e.currentTarget.style.backgroundColor = "#f3f4f6";
2079
+ }
2080
+ },
2081
+ onMouseLeave: (e) => {
2082
+ if (currentPage !== 1) {
2083
+ e.currentTarget.style.backgroundColor = "#fff";
2084
+ }
2085
+ },
2086
+ children: "\xAB\xAB"
2087
+ }
2088
+ ) }),
2089
+ /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
2090
+ "button",
2091
+ {
2092
+ type: "button",
2093
+ onClick: () => handlePageChange(currentPage - 1),
2094
+ disabled: currentPage === 1,
2095
+ "aria-label": "Go to previous page",
2096
+ style: currentPage === 1 ? disabledButtonStyles : buttonBaseStyles,
2097
+ onMouseEnter: (e) => {
2098
+ if (currentPage !== 1) {
2099
+ e.currentTarget.style.backgroundColor = "#f3f4f6";
2100
+ }
2101
+ },
2102
+ onMouseLeave: (e) => {
2103
+ if (currentPage !== 1) {
2104
+ e.currentTarget.style.backgroundColor = "#fff";
2105
+ }
2106
+ },
2107
+ children: "\xAB"
2108
+ }
2109
+ ) }),
2110
+ pages.map((page, index) => {
2111
+ if (page === "...") {
2112
+ return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("span", { style: ellipsisStyles, "aria-hidden": "true", children: "\u2026" }) }, `ellipsis-${index}`);
2113
+ }
2114
+ const pageNumber = page;
2115
+ const isActive = pageNumber === currentPage;
2116
+ return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
2117
+ "button",
2118
+ {
2119
+ type: "button",
2120
+ onClick: () => handlePageChange(pageNumber),
2121
+ "aria-label": `Go to page ${pageNumber}`,
2122
+ "aria-current": isActive ? "page" : void 0,
2123
+ style: isActive ? activeButtonStyles : buttonBaseStyles,
2124
+ onMouseEnter: (e) => {
2125
+ if (!isActive) {
2126
+ e.currentTarget.style.backgroundColor = "#f3f4f6";
2127
+ }
2128
+ },
2129
+ onMouseLeave: (e) => {
2130
+ if (!isActive) {
2131
+ e.currentTarget.style.backgroundColor = "#fff";
2132
+ }
2133
+ },
2134
+ children: pageNumber
2135
+ }
2136
+ ) }, pageNumber);
2137
+ }),
2138
+ /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
2139
+ "button",
2140
+ {
2141
+ type: "button",
2142
+ onClick: () => handlePageChange(currentPage + 1),
2143
+ disabled: currentPage === totalPages,
2144
+ "aria-label": "Go to next page",
2145
+ style: currentPage === totalPages ? disabledButtonStyles : buttonBaseStyles,
2146
+ onMouseEnter: (e) => {
2147
+ if (currentPage !== totalPages) {
2148
+ e.currentTarget.style.backgroundColor = "#f3f4f6";
2149
+ }
2150
+ },
2151
+ onMouseLeave: (e) => {
2152
+ if (currentPage !== totalPages) {
2153
+ e.currentTarget.style.backgroundColor = "#fff";
2154
+ }
2155
+ },
2156
+ children: "\xBB"
2157
+ }
2158
+ ) }),
2159
+ showFirstLast && /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
2160
+ "button",
2161
+ {
2162
+ type: "button",
2163
+ onClick: () => handlePageChange(totalPages),
2164
+ disabled: currentPage === totalPages,
2165
+ "aria-label": "Go to last page",
2166
+ style: currentPage === totalPages ? disabledButtonStyles : buttonBaseStyles,
2167
+ onMouseEnter: (e) => {
2168
+ if (currentPage !== totalPages) {
2169
+ e.currentTarget.style.backgroundColor = "#f3f4f6";
2170
+ }
2171
+ },
2172
+ onMouseLeave: (e) => {
2173
+ if (currentPage !== totalPages) {
2174
+ e.currentTarget.style.backgroundColor = "#fff";
2175
+ }
2176
+ },
2177
+ children: "\xBB\xBB"
2178
+ }
2179
+ ) })
2180
+ ] }) });
2181
+ };
2182
+ function useClickOutside(ref, handler, enabled = true) {
2183
+ useEffect(() => {
2184
+ if (!enabled) return;
2185
+ const handleClickOutside = (event) => {
2186
+ if (!ref.current || ref.current.contains(event.target)) {
2187
+ return;
2188
+ }
2189
+ handler(event);
2190
+ };
2191
+ document.addEventListener("mousedown", handleClickOutside);
2192
+ document.addEventListener("touchstart", handleClickOutside);
2193
+ return () => {
2194
+ document.removeEventListener("mousedown", handleClickOutside);
2195
+ document.removeEventListener("touchstart", handleClickOutside);
2196
+ };
2197
+ }, [ref, handler, enabled]);
2198
+ }
2199
+ function AccessibleCombobox({
2200
+ options,
2201
+ value,
2202
+ onChange,
2203
+ label,
2204
+ placeholder = "Select an option",
2205
+ searchable = false,
2206
+ required = false,
2207
+ disabled = false,
2208
+ error,
2209
+ className = "",
2210
+ style = {}
2211
+ }) {
2212
+ const [isOpen, setIsOpen] = useState(false);
2213
+ const [searchQuery, setSearchQuery] = useState("");
2214
+ const [highlightedIndex, setHighlightedIndex] = useState(0);
2215
+ const comboboxId = useId2("combobox");
2216
+ const labelId = useId2("combobox-label");
2217
+ const listboxId = useId2("combobox-listbox");
2218
+ const errorId = useId2("combobox-error");
2219
+ const containerRef = useRef(null);
2220
+ const inputRef = useRef(null);
2221
+ const listboxRef = useRef(null);
2222
+ useClickOutside(containerRef, () => setIsOpen(false), isOpen);
2223
+ const filteredOptions = searchable ? options.filter(
2224
+ (option) => option.label.toLowerCase().includes(searchQuery.toLowerCase())
2225
+ ) : options;
2226
+ const selectedOption = options.find((opt) => opt.value === value);
2227
+ useEffect(() => {
2228
+ if (isOpen && listboxRef.current) {
2229
+ const highlightedElement = listboxRef.current.querySelector(
2230
+ `[data-index="${highlightedIndex}"]`
2231
+ );
2232
+ highlightedElement?.scrollIntoView({ block: "nearest" });
2233
+ }
2234
+ }, [highlightedIndex, isOpen]);
2235
+ const handleToggle = () => {
2236
+ if (disabled) return;
2237
+ setIsOpen(!isOpen);
2238
+ if (!isOpen) {
2239
+ setSearchQuery("");
2240
+ setHighlightedIndex(0);
2241
+ setTimeout(() => inputRef.current?.focus(), 0);
2242
+ }
2243
+ };
2244
+ const handleSelect = (option) => {
2245
+ if (option.disabled) return;
2246
+ onChange(option.value);
2247
+ setIsOpen(false);
2248
+ setSearchQuery("");
2249
+ inputRef.current?.blur();
2250
+ };
2251
+ const handleKeyDown = (event) => {
2252
+ if (disabled) return;
2253
+ switch (event.key) {
2254
+ case "Enter":
2255
+ event.preventDefault();
2256
+ if (isOpen && filteredOptions[highlightedIndex]) {
2257
+ handleSelect(filteredOptions[highlightedIndex]);
2258
+ } else {
2259
+ setIsOpen(!isOpen);
2260
+ }
2261
+ break;
2262
+ case " ":
2263
+ if (!searchable || !isOpen) {
2264
+ event.preventDefault();
2265
+ setIsOpen(!isOpen);
2266
+ }
2267
+ break;
2268
+ case "Escape":
2269
+ event.preventDefault();
2270
+ setIsOpen(false);
2271
+ setSearchQuery("");
2272
+ break;
2273
+ case "ArrowDown":
2274
+ event.preventDefault();
2275
+ if (!isOpen) {
2276
+ setIsOpen(true);
2277
+ } else {
2278
+ setHighlightedIndex(
2279
+ (prev) => prev < filteredOptions.length - 1 ? prev + 1 : prev
2280
+ );
2281
+ }
2282
+ break;
2283
+ case "ArrowUp":
2284
+ event.preventDefault();
2285
+ if (isOpen) {
2286
+ setHighlightedIndex((prev) => prev > 0 ? prev - 1 : prev);
2287
+ }
2288
+ break;
2289
+ case "Home":
2290
+ if (isOpen) {
2291
+ event.preventDefault();
2292
+ setHighlightedIndex(0);
2293
+ }
2294
+ break;
2295
+ case "End":
2296
+ if (isOpen) {
2297
+ event.preventDefault();
2298
+ setHighlightedIndex(filteredOptions.length - 1);
2299
+ }
2300
+ break;
2301
+ }
2302
+ };
2303
+ const containerStyles = {
2304
+ position: "relative",
2305
+ width: "100%",
2306
+ ...style
2307
+ };
2308
+ const labelStyles = {
2309
+ display: "block",
2310
+ fontWeight: 600,
2311
+ fontSize: "14px",
2312
+ marginBottom: "6px"
2313
+ };
2314
+ const inputContainerStyles = {
2315
+ position: "relative",
2316
+ width: "100%"
2317
+ };
2318
+ const inputStyles = {
2319
+ width: "100%",
2320
+ padding: "10px 36px 10px 12px",
2321
+ border: `1px solid ${error ? "#ef4444" : "#e5e7eb"}`,
2322
+ borderRadius: "6px",
2323
+ fontSize: "14px",
2324
+ backgroundColor: disabled ? "#f9fafb" : "#fff",
2325
+ cursor: disabled ? "not-allowed" : "pointer",
2326
+ outline: "none"
2327
+ };
2328
+ const iconStyles = {
2329
+ position: "absolute",
2330
+ right: "12px",
2331
+ top: "50%",
2332
+ transform: "translateY(-50%)",
2333
+ pointerEvents: "none",
2334
+ transition: "transform 0.2s"
2335
+ };
2336
+ const listboxStyles = {
2337
+ position: "absolute",
2338
+ top: "100%",
2339
+ left: 0,
2340
+ right: 0,
2341
+ marginTop: "4px",
2342
+ maxHeight: "240px",
2343
+ overflowY: "auto",
2344
+ backgroundColor: "#fff",
2345
+ border: "1px solid #e5e7eb",
2346
+ borderRadius: "6px",
2347
+ boxShadow: "0 4px 6px rgba(0, 0, 0, 0.1)",
2348
+ zIndex: 1e3,
2349
+ listStyle: "none",
2350
+ margin: 0,
2351
+ padding: "4px"
2352
+ };
2353
+ const optionStyles = (isHighlighted, isSelected, isDisabled) => ({
2354
+ padding: "10px 12px",
2355
+ cursor: isDisabled ? "not-allowed" : "pointer",
2356
+ backgroundColor: isHighlighted ? "#f3f4f6" : isSelected ? "#e5e7eb" : "transparent",
2357
+ borderRadius: "4px",
2358
+ fontSize: "14px",
2359
+ opacity: isDisabled ? 0.5 : 1
2360
+ });
2361
+ const errorStyles = {
2362
+ marginTop: "6px",
2363
+ fontSize: "13px",
2364
+ color: "#ef4444"
2365
+ };
2366
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className, style: containerStyles, children: [
2367
+ /* @__PURE__ */ jsxs("label", { id: labelId, htmlFor: comboboxId, style: labelStyles, children: [
2368
+ label,
2369
+ required && /* @__PURE__ */ jsxs("span", { "aria-label": "required", style: { color: "#ef4444" }, children: [
2370
+ " ",
2371
+ "*"
2372
+ ] })
2373
+ ] }),
2374
+ /* @__PURE__ */ jsxs("div", { style: inputContainerStyles, children: [
2375
+ /* @__PURE__ */ jsx(
2376
+ "input",
2377
+ {
2378
+ ref: inputRef,
2379
+ id: comboboxId,
2380
+ type: "text",
2381
+ role: "combobox",
2382
+ "aria-labelledby": labelId,
2383
+ "aria-controls": listboxId,
2384
+ "aria-expanded": isOpen,
2385
+ "aria-autocomplete": searchable ? "list" : "none",
2386
+ "aria-activedescendant": isOpen && filteredOptions[highlightedIndex] ? `${listboxId}-option-${highlightedIndex}` : void 0,
2387
+ "aria-describedby": error ? errorId : void 0,
2388
+ "aria-required": required,
2389
+ "aria-invalid": !!error,
2390
+ disabled,
2391
+ value: searchable && isOpen ? searchQuery : selectedOption?.label || "",
2392
+ onChange: (e) => {
2393
+ if (searchable) {
2394
+ setSearchQuery(e.target.value);
2395
+ setHighlightedIndex(0);
2396
+ if (!isOpen) setIsOpen(true);
2397
+ }
2398
+ },
2399
+ onKeyDown: handleKeyDown,
2400
+ onClick: handleToggle,
2401
+ placeholder,
2402
+ style: inputStyles,
2403
+ readOnly: !searchable
2404
+ }
2405
+ ),
2406
+ /* @__PURE__ */ jsx(
2407
+ "span",
2408
+ {
2409
+ "aria-hidden": "true",
2410
+ style: {
2411
+ ...iconStyles,
2412
+ transform: `translateY(-50%) rotate(${isOpen ? "180deg" : "0deg"})`
2413
+ },
2414
+ children: "\u25BC"
2415
+ }
2416
+ )
2417
+ ] }),
2418
+ isOpen && /* @__PURE__ */ jsx(
2419
+ "ul",
2420
+ {
2421
+ ref: listboxRef,
2422
+ id: listboxId,
2423
+ role: "listbox",
2424
+ "aria-labelledby": labelId,
2425
+ style: listboxStyles,
2426
+ children: filteredOptions.length === 0 ? /* @__PURE__ */ jsx("li", { role: "option", "aria-disabled": "true", style: { padding: "10px 12px", color: "#9ca3af" }, children: "No options found" }) : filteredOptions.map((option, index) => {
2427
+ const isHighlighted = index === highlightedIndex;
2428
+ const isSelected = option.value === value;
2429
+ return /* @__PURE__ */ jsx(
2430
+ "li",
2431
+ {
2432
+ id: `${listboxId}-option-${index}`,
2433
+ role: "option",
2434
+ "aria-selected": isSelected,
2435
+ "aria-disabled": option.disabled,
2436
+ "data-index": index,
2437
+ onClick: () => handleSelect(option),
2438
+ onMouseEnter: () => setHighlightedIndex(index),
2439
+ style: optionStyles(isHighlighted, isSelected, !!option.disabled),
2440
+ children: option.label
2441
+ },
2442
+ String(option.value)
2443
+ );
2444
+ })
2445
+ }
2446
+ ),
2447
+ error && /* @__PURE__ */ jsx("div", { id: errorId, role: "alert", style: errorStyles, children: error })
2448
+ ] });
2449
+ }
2450
+ var AccessibleDatePicker = ({
2451
+ value,
2452
+ onChange,
2453
+ label,
2454
+ minDate,
2455
+ maxDate,
2456
+ disabledDates = [],
2457
+ required = false,
2458
+ disabled = false,
2459
+ error,
2460
+ dateFormat = "MM/DD/YYYY",
2461
+ className = "",
2462
+ style = {}
2463
+ }) => {
2464
+ const [isOpen, setIsOpen] = useState(false);
2465
+ const [viewDate, setViewDate] = useState(value || /* @__PURE__ */ new Date());
2466
+ const [focusedDate, setFocusedDate] = useState(null);
2467
+ const pickerId = useId2("datepicker");
2468
+ const labelId = useId2("datepicker-label");
2469
+ const calendarId = useId2("datepicker-calendar");
2470
+ const errorId = useId2("datepicker-error");
2471
+ const containerRef = useRef(null);
2472
+ const buttonRef = useRef(null);
2473
+ useClickOutside(containerRef, () => setIsOpen(false), isOpen);
2474
+ const formatDate = (date) => {
2475
+ if (!date) return "";
2476
+ const day = String(date.getDate()).padStart(2, "0");
2477
+ const month = String(date.getMonth() + 1).padStart(2, "0");
2478
+ const year = date.getFullYear();
2479
+ switch (dateFormat) {
2480
+ case "DD/MM/YYYY":
2481
+ return `${day}/${month}/${year}`;
2482
+ case "YYYY-MM-DD":
2483
+ return `${year}-${month}-${day}`;
2484
+ case "MM/DD/YYYY":
2485
+ default:
2486
+ return `${month}/${day}/${year}`;
2487
+ }
2488
+ };
2489
+ const isSameDay = (date1, date2) => {
2490
+ return date1.getDate() === date2.getDate() && date1.getMonth() === date2.getMonth() && date1.getFullYear() === date2.getFullYear();
2491
+ };
2492
+ const isDateDisabled = (date) => {
2493
+ if (minDate && date < minDate) return true;
2494
+ if (maxDate && date > maxDate) return true;
2495
+ return disabledDates.some((disabledDate) => isSameDay(date, disabledDate));
2496
+ };
2497
+ const getDaysInMonth = (date) => {
2498
+ const year = date.getFullYear();
2499
+ const month = date.getMonth();
2500
+ const firstDay = new Date(year, month, 1);
2501
+ const lastDay = new Date(year, month + 1, 0);
2502
+ const days2 = [];
2503
+ const startDayOfWeek = firstDay.getDay();
2504
+ for (let i = 0; i < startDayOfWeek; i++) {
2505
+ days2.push(new Date(year, month, -startDayOfWeek + i + 1));
2506
+ }
2507
+ for (let day = 1; day <= lastDay.getDate(); day++) {
2508
+ days2.push(new Date(year, month, day));
2509
+ }
2510
+ return days2;
2511
+ };
2512
+ const handlePreviousMonth = () => {
2513
+ setViewDate(new Date(viewDate.getFullYear(), viewDate.getMonth() - 1, 1));
2514
+ };
2515
+ const handleNextMonth = () => {
2516
+ setViewDate(new Date(viewDate.getFullYear(), viewDate.getMonth() + 1, 1));
2517
+ };
2518
+ const handleDateSelect = (date) => {
2519
+ if (isDateDisabled(date)) return;
2520
+ onChange(date);
2521
+ setIsOpen(false);
2522
+ buttonRef.current?.focus();
2523
+ };
2524
+ const handleKeyDown = (event, date) => {
2525
+ let nextDate = null;
2526
+ switch (event.key) {
2527
+ case "Enter":
2528
+ case " ":
2529
+ event.preventDefault();
2530
+ handleDateSelect(date);
2531
+ return;
2532
+ case "Escape":
2533
+ event.preventDefault();
2534
+ setIsOpen(false);
2535
+ buttonRef.current?.focus();
2536
+ return;
2537
+ case "ArrowLeft":
2538
+ event.preventDefault();
2539
+ nextDate = new Date(date);
2540
+ nextDate.setDate(date.getDate() - 1);
2541
+ break;
2542
+ case "ArrowRight":
2543
+ event.preventDefault();
2544
+ nextDate = new Date(date);
2545
+ nextDate.setDate(date.getDate() + 1);
2546
+ break;
2547
+ case "ArrowUp":
2548
+ event.preventDefault();
2549
+ nextDate = new Date(date);
2550
+ nextDate.setDate(date.getDate() - 7);
2551
+ break;
2552
+ case "ArrowDown":
2553
+ event.preventDefault();
2554
+ nextDate = new Date(date);
2555
+ nextDate.setDate(date.getDate() + 7);
2556
+ break;
2557
+ case "Home":
2558
+ event.preventDefault();
2559
+ nextDate = new Date(date.getFullYear(), date.getMonth(), 1);
2560
+ break;
2561
+ case "End":
2562
+ event.preventDefault();
2563
+ nextDate = new Date(date.getFullYear(), date.getMonth() + 1, 0);
2564
+ break;
2565
+ case "PageUp":
2566
+ event.preventDefault();
2567
+ nextDate = new Date(date.getFullYear(), date.getMonth() - 1, date.getDate());
2568
+ break;
2569
+ case "PageDown":
2570
+ event.preventDefault();
2571
+ nextDate = new Date(date.getFullYear(), date.getMonth() + 1, date.getDate());
2572
+ break;
2573
+ }
2574
+ if (nextDate) {
2575
+ setFocusedDate(nextDate);
2576
+ if (nextDate.getMonth() !== viewDate.getMonth()) {
2577
+ setViewDate(new Date(nextDate.getFullYear(), nextDate.getMonth(), 1));
2578
+ }
2579
+ }
2580
+ };
2581
+ const days = getDaysInMonth(viewDate);
2582
+ const monthNames = [
2583
+ "January",
2584
+ "February",
2585
+ "March",
2586
+ "April",
2587
+ "May",
2588
+ "June",
2589
+ "July",
2590
+ "August",
2591
+ "September",
2592
+ "October",
2593
+ "November",
2594
+ "December"
2595
+ ];
2596
+ const dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
2597
+ const containerStyles = {
2598
+ position: "relative",
2599
+ width: "100%",
2600
+ ...style
2601
+ };
2602
+ const labelStyles = {
2603
+ display: "block",
2604
+ fontWeight: 600,
2605
+ fontSize: "14px",
2606
+ marginBottom: "6px"
2607
+ };
2608
+ const buttonStyles = {
2609
+ width: "100%",
2610
+ padding: "10px 36px 10px 12px",
2611
+ border: `1px solid ${error ? "#ef4444" : "#e5e7eb"}`,
2612
+ borderRadius: "6px",
2613
+ fontSize: "14px",
2614
+ backgroundColor: disabled ? "#f9fafb" : "#fff",
2615
+ cursor: disabled ? "not-allowed" : "pointer",
2616
+ textAlign: "left",
2617
+ position: "relative"
2618
+ };
2619
+ const calendarStyles = {
2620
+ position: "absolute",
2621
+ top: "100%",
2622
+ left: 0,
2623
+ marginTop: "4px",
2624
+ backgroundColor: "#fff",
2625
+ border: "1px solid #e5e7eb",
2626
+ borderRadius: "8px",
2627
+ boxShadow: "0 4px 6px rgba(0, 0, 0, 0.1)",
2628
+ padding: "16px",
2629
+ zIndex: 1e3,
2630
+ minWidth: "280px"
2631
+ };
2632
+ const headerStyles = {
2633
+ display: "flex",
2634
+ justifyContent: "space-between",
2635
+ alignItems: "center",
2636
+ marginBottom: "12px"
2637
+ };
2638
+ const navButtonStyles = {
2639
+ padding: "6px 12px",
2640
+ border: "1px solid #e5e7eb",
2641
+ borderRadius: "4px",
2642
+ backgroundColor: "#fff",
2643
+ cursor: "pointer",
2644
+ fontSize: "14px"
2645
+ };
2646
+ const gridStyles = {
2647
+ display: "grid",
2648
+ gridTemplateColumns: "repeat(7, 1fr)",
2649
+ gap: "4px"
2650
+ };
2651
+ const dayHeaderStyles = {
2652
+ textAlign: "center",
2653
+ fontWeight: 600,
2654
+ fontSize: "12px",
2655
+ padding: "8px 0",
2656
+ color: "#6b7280"
2657
+ };
2658
+ const dayCellStyles = (_date, isCurrentMonth, isSelected, isToday, isDisabled) => ({
2659
+ padding: "8px",
2660
+ border: "none",
2661
+ borderRadius: "4px",
2662
+ fontSize: "14px",
2663
+ cursor: isDisabled ? "not-allowed" : "pointer",
2664
+ backgroundColor: isSelected ? "#3b82f6" : isToday ? "#e5e7eb" : "transparent",
2665
+ color: isSelected ? "#fff" : !isCurrentMonth ? "#9ca3af" : isDisabled ? "#d1d5db" : "#1f2937",
2666
+ fontWeight: isSelected || isToday ? 600 : 400,
2667
+ opacity: isDisabled ? 0.5 : 1
2668
+ });
2669
+ const errorStyles = {
2670
+ marginTop: "6px",
2671
+ fontSize: "13px",
2672
+ color: "#ef4444"
2673
+ };
2674
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className, style: containerStyles, children: [
2675
+ /* @__PURE__ */ jsxs("label", { id: labelId, htmlFor: pickerId, style: labelStyles, children: [
2676
+ label,
2677
+ required && /* @__PURE__ */ jsxs("span", { "aria-label": "required", style: { color: "#ef4444" }, children: [
2678
+ " ",
2679
+ "*"
2680
+ ] })
2681
+ ] }),
2682
+ /* @__PURE__ */ jsxs(
2683
+ "button",
2684
+ {
2685
+ ref: buttonRef,
2686
+ id: pickerId,
2687
+ type: "button",
2688
+ "aria-labelledby": labelId,
2689
+ "aria-expanded": isOpen,
2690
+ "aria-controls": calendarId,
2691
+ "aria-describedby": error ? errorId : void 0,
2692
+ "aria-required": required,
2693
+ "aria-invalid": !!error,
2694
+ disabled,
2695
+ onClick: () => !disabled && setIsOpen(!isOpen),
2696
+ style: buttonStyles,
2697
+ children: [
2698
+ value ? formatDate(value) : "Select date",
2699
+ /* @__PURE__ */ jsx(
2700
+ "span",
2701
+ {
2702
+ "aria-hidden": "true",
2703
+ style: {
2704
+ position: "absolute",
2705
+ right: "12px",
2706
+ top: "50%",
2707
+ transform: "translateY(-50%)"
2708
+ },
2709
+ children: "\u{1F4C5}"
2710
+ }
2711
+ )
2712
+ ]
2713
+ }
2714
+ ),
2715
+ isOpen && /* @__PURE__ */ jsxs("div", { id: calendarId, role: "dialog", "aria-modal": "false", "aria-label": "Calendar", style: calendarStyles, children: [
2716
+ /* @__PURE__ */ jsxs("div", { style: headerStyles, children: [
2717
+ /* @__PURE__ */ jsx(
2718
+ "button",
2719
+ {
2720
+ type: "button",
2721
+ onClick: handlePreviousMonth,
2722
+ "aria-label": "Previous month",
2723
+ style: navButtonStyles,
2724
+ children: "\u2039"
2725
+ }
2726
+ ),
2727
+ /* @__PURE__ */ jsxs("span", { style: { fontWeight: 600 }, children: [
2728
+ monthNames[viewDate.getMonth()],
2729
+ " ",
2730
+ viewDate.getFullYear()
2731
+ ] }),
2732
+ /* @__PURE__ */ jsx(
2733
+ "button",
2734
+ {
2735
+ type: "button",
2736
+ onClick: handleNextMonth,
2737
+ "aria-label": "Next month",
2738
+ style: navButtonStyles,
2739
+ children: "\u203A"
2740
+ }
2741
+ )
2742
+ ] }),
2743
+ /* @__PURE__ */ jsxs("div", { role: "grid", "aria-labelledby": labelId, style: gridStyles, children: [
2744
+ dayNames.map((day) => /* @__PURE__ */ jsx("div", { role: "columnheader", style: dayHeaderStyles, children: day }, day)),
2745
+ days.map((cellDate, index) => {
2746
+ const isCurrentMonth = cellDate.getMonth() === viewDate.getMonth();
2747
+ const isSelected = value ? isSameDay(cellDate, value) : false;
2748
+ const isToday = isSameDay(cellDate, /* @__PURE__ */ new Date());
2749
+ const isDisabled = isDateDisabled(cellDate);
2750
+ const isFocused = focusedDate ? isSameDay(cellDate, focusedDate) : false;
2751
+ return /* @__PURE__ */ jsx(
2752
+ "button",
2753
+ {
2754
+ type: "button",
2755
+ role: "gridcell",
2756
+ "aria-label": formatDate(cellDate),
2757
+ "aria-selected": isSelected,
2758
+ "aria-disabled": isDisabled,
2759
+ disabled: isDisabled,
2760
+ tabIndex: isFocused || isSelected && !focusedDate ? 0 : -1,
2761
+ onClick: () => handleDateSelect(cellDate),
2762
+ onKeyDown: (e) => handleKeyDown(e, cellDate),
2763
+ style: dayCellStyles(cellDate, isCurrentMonth, isSelected, isToday, isDisabled),
2764
+ onMouseEnter: (e) => {
2765
+ if (!isDisabled && !isSelected) {
2766
+ e.currentTarget.style.backgroundColor = "#f3f4f6";
2767
+ }
2768
+ },
2769
+ onMouseLeave: (e) => {
2770
+ if (!isDisabled && !isSelected) {
2771
+ e.currentTarget.style.backgroundColor = "transparent";
2772
+ }
2773
+ },
2774
+ children: cellDate.getDate()
2775
+ },
2776
+ index
2777
+ );
2778
+ })
2779
+ ] })
2780
+ ] }),
2781
+ error && /* @__PURE__ */ jsx("div", { id: errorId, role: "alert", style: errorStyles, children: error })
2782
+ ] });
2783
+ };
2784
+ function AccessibleTreeView({
2785
+ data,
2786
+ onSelect,
2787
+ expandedNodes: controlledExpanded,
2788
+ onToggle,
2789
+ multiSelect = false,
2790
+ selectedNodes: controlledSelected = [],
2791
+ ariaLabel = "Tree navigation",
2792
+ className = "",
2793
+ style = {}
2794
+ }) {
2795
+ const [internalExpanded, setInternalExpanded] = useState(/* @__PURE__ */ new Set());
2796
+ const [internalSelected, setInternalSelected] = useState(/* @__PURE__ */ new Set());
2797
+ const treeId = useId2("tree");
2798
+ const expandedSet = controlledExpanded ? new Set(controlledExpanded) : internalExpanded;
2799
+ const selectedSet = new Set(controlledSelected.length > 0 ? controlledSelected : Array.from(internalSelected));
2800
+ const toggleNode = (nodeId) => {
2801
+ const isExpanded = expandedSet.has(nodeId);
2802
+ if (controlledExpanded) {
2803
+ onToggle?.(nodeId, !isExpanded);
2804
+ } else {
2805
+ setInternalExpanded((prev) => {
2806
+ const next = new Set(prev);
2807
+ if (isExpanded) {
2808
+ next.delete(nodeId);
2809
+ } else {
2810
+ next.add(nodeId);
2811
+ }
2812
+ return next;
2813
+ });
2814
+ }
2815
+ };
2816
+ const selectNode = (node) => {
2817
+ if (node.disabled) return;
2818
+ if (multiSelect) {
2819
+ setInternalSelected((prev) => {
2820
+ const next = new Set(prev);
2821
+ if (next.has(node.id)) {
2822
+ next.delete(node.id);
2823
+ } else {
2824
+ next.add(node.id);
2825
+ }
2826
+ return next;
2827
+ });
2828
+ } else {
2829
+ setInternalSelected(/* @__PURE__ */ new Set([node.id]));
2830
+ }
2831
+ onSelect?.(node);
2832
+ };
2833
+ const getAllNodes = (nodes) => {
2834
+ const result = [];
2835
+ const traverse = (nodeList) => {
2836
+ for (const node of nodeList) {
2837
+ result.push(node);
2838
+ if (node.children && expandedSet.has(node.id)) {
2839
+ traverse(node.children);
2840
+ }
2841
+ }
2842
+ };
2843
+ traverse(nodes);
2844
+ return result;
2845
+ };
2846
+ const handleKeyDown = (event, node, allNodes2) => {
2847
+ const currentIndex = allNodes2.findIndex((n) => n.id === node.id);
2848
+ switch (event.key) {
2849
+ case "Enter":
2850
+ case " ":
2851
+ event.preventDefault();
2852
+ selectNode(node);
2853
+ break;
2854
+ case "ArrowDown":
2855
+ event.preventDefault();
2856
+ if (currentIndex < allNodes2.length - 1) {
2857
+ const nextNode = allNodes2[currentIndex + 1];
2858
+ document.getElementById(`${treeId}-${nextNode.id}`)?.focus();
2859
+ }
2860
+ break;
2861
+ case "ArrowUp":
2862
+ event.preventDefault();
2863
+ if (currentIndex > 0) {
2864
+ const prevNode = allNodes2[currentIndex - 1];
2865
+ document.getElementById(`${treeId}-${prevNode.id}`)?.focus();
2866
+ }
2867
+ break;
2868
+ case "ArrowRight":
2869
+ event.preventDefault();
2870
+ if (node.children && node.children.length > 0) {
2871
+ if (!expandedSet.has(node.id)) {
2872
+ toggleNode(node.id);
2873
+ } else if (node.children[0]) {
2874
+ document.getElementById(`${treeId}-${node.children[0].id}`)?.focus();
2875
+ }
2876
+ }
2877
+ break;
2878
+ case "ArrowLeft":
2879
+ event.preventDefault();
2880
+ if (node.children && expandedSet.has(node.id)) {
2881
+ toggleNode(node.id);
2882
+ }
2883
+ break;
2884
+ case "Home":
2885
+ event.preventDefault();
2886
+ if (allNodes2[0]) {
2887
+ document.getElementById(`${treeId}-${allNodes2[0].id}`)?.focus();
2888
+ }
2889
+ break;
2890
+ case "End":
2891
+ event.preventDefault();
2892
+ if (allNodes2[allNodes2.length - 1]) {
2893
+ document.getElementById(`${treeId}-${allNodes2[allNodes2.length - 1].id}`)?.focus();
2894
+ }
2895
+ break;
2896
+ }
2897
+ };
2898
+ const renderNode = (node, level, allNodes2) => {
2899
+ const hasChildren = node.children && node.children.length > 0;
2900
+ const isExpanded = expandedSet.has(node.id);
2901
+ const isSelected = selectedSet.has(node.id);
2902
+ const nodeStyles = {
2903
+ display: "flex",
2904
+ alignItems: "center",
2905
+ gap: "8px",
2906
+ padding: "8px 12px",
2907
+ paddingLeft: `${12 + level * 24}px`,
2908
+ cursor: node.disabled ? "not-allowed" : "pointer",
2909
+ backgroundColor: isSelected ? "#e5e7eb" : "transparent",
2910
+ borderRadius: "4px",
2911
+ opacity: node.disabled ? 0.5 : 1
2912
+ };
2913
+ const iconStyles = {
2914
+ width: "16px",
2915
+ textAlign: "center",
2916
+ transition: "transform 0.2s",
2917
+ transform: isExpanded ? "rotate(90deg)" : "rotate(0deg)"
2918
+ };
2919
+ return /* @__PURE__ */ jsxs(React4.Fragment, { children: [
2920
+ /* @__PURE__ */ jsxs(
2921
+ "div",
2922
+ {
2923
+ id: `${treeId}-${node.id}`,
2924
+ role: "treeitem",
2925
+ "aria-expanded": hasChildren ? isExpanded : void 0,
2926
+ "aria-selected": isSelected,
2927
+ "aria-level": level + 1,
2928
+ "aria-disabled": node.disabled,
2929
+ tabIndex: isSelected ? 0 : -1,
2930
+ onClick: () => {
2931
+ if (!node.disabled) {
2932
+ if (hasChildren) {
2933
+ toggleNode(node.id);
2934
+ }
2935
+ selectNode(node);
2936
+ }
2937
+ },
2938
+ onKeyDown: (e) => handleKeyDown(e, node, allNodes2),
2939
+ style: nodeStyles,
2940
+ onMouseEnter: (e) => {
2941
+ if (!node.disabled && !isSelected) {
2942
+ e.currentTarget.style.backgroundColor = "#f3f4f6";
2943
+ }
2944
+ },
2945
+ onMouseLeave: (e) => {
2946
+ if (!node.disabled && !isSelected) {
2947
+ e.currentTarget.style.backgroundColor = "transparent";
2948
+ }
2949
+ },
2950
+ children: [
2951
+ hasChildren ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", style: iconStyles, children: "\u25B6" }) : /* @__PURE__ */ jsx("span", { style: { width: "16px" } }),
2952
+ /* @__PURE__ */ jsx("span", { children: node.label })
2953
+ ]
2954
+ }
2955
+ ),
2956
+ hasChildren && isExpanded && /* @__PURE__ */ jsx("div", { role: "group", children: node.children.map((child) => renderNode(child, level + 1, allNodes2)) })
2957
+ ] }, node.id);
2958
+ };
2959
+ const allNodes = getAllNodes(data);
2960
+ const containerStyles = {
2961
+ ...style
2962
+ };
2963
+ return /* @__PURE__ */ jsx(
2964
+ "div",
2965
+ {
2966
+ id: treeId,
2967
+ role: "tree",
2968
+ "aria-label": ariaLabel,
2969
+ "aria-multiselectable": multiSelect,
2970
+ className,
2971
+ style: containerStyles,
2972
+ children: data.map((node) => renderNode(node, 0, allNodes))
2973
+ }
2974
+ );
2975
+ }
2976
+ function AccessibleTable({
2977
+ data,
2978
+ columns,
2979
+ caption,
2980
+ selectable = false,
2981
+ selectedRows: controlledSelected = [],
2982
+ onSelectionChange,
2983
+ getRowKey = (_, index) => String(index),
2984
+ sortable = false,
2985
+ sortColumn: controlledSortColumn,
2986
+ sortDirection: controlledSortDirection,
2987
+ onSort,
2988
+ className = "",
2989
+ style = {}
2990
+ }) {
2991
+ const [internalSelected, setInternalSelected] = useState(/* @__PURE__ */ new Set());
2992
+ const [internalSortColumn, setInternalSortColumn] = useState(null);
2993
+ const [internalSortDirection, setInternalSortDirection] = useState(null);
2994
+ const tableId = useId2("table");
2995
+ const selectedSet = new Set(controlledSelected.length > 0 ? controlledSelected : Array.from(internalSelected));
2996
+ const sortColumn = controlledSortColumn !== void 0 ? controlledSortColumn : internalSortColumn;
2997
+ const sortDirection = controlledSortDirection !== void 0 ? controlledSortDirection : internalSortDirection;
2998
+ const handleSelectAll = (checked) => {
2999
+ if (checked) {
3000
+ const allIndices = data.map((_, index) => index);
3001
+ setInternalSelected(new Set(allIndices));
3002
+ onSelectionChange?.(allIndices);
3003
+ } else {
3004
+ setInternalSelected(/* @__PURE__ */ new Set());
3005
+ onSelectionChange?.([]);
3006
+ }
3007
+ };
3008
+ const handleSelectRow = (index, checked) => {
3009
+ const newSelected = new Set(selectedSet);
3010
+ if (checked) {
3011
+ newSelected.add(index);
3012
+ } else {
3013
+ newSelected.delete(index);
3014
+ }
3015
+ setInternalSelected(newSelected);
3016
+ onSelectionChange?.(Array.from(newSelected));
3017
+ };
3018
+ const handleSort = (column) => {
3019
+ const col = columns.find((c) => c.key === column);
3020
+ if (!col?.sortable && !sortable) return;
3021
+ let newDirection = "asc";
3022
+ if (sortColumn === column) {
3023
+ if (sortDirection === "asc") {
3024
+ newDirection = "desc";
3025
+ } else if (sortDirection === "desc") {
3026
+ newDirection = null;
3027
+ }
3028
+ }
3029
+ if (onSort) {
3030
+ onSort(column, newDirection);
3031
+ } else {
3032
+ setInternalSortColumn(newDirection ? column : null);
3033
+ setInternalSortDirection(newDirection);
3034
+ }
3035
+ };
3036
+ const isAllSelected = data.length > 0 && selectedSet.size === data.length;
3037
+ const isSomeSelected = selectedSet.size > 0 && selectedSet.size < data.length;
3038
+ const tableStyles = {
3039
+ width: "100%",
3040
+ borderCollapse: "collapse",
3041
+ fontSize: "14px",
3042
+ ...style
3043
+ };
3044
+ const thStyles = {
3045
+ padding: "12px",
3046
+ textAlign: "left",
3047
+ borderBottom: "2px solid #e5e7eb",
3048
+ fontWeight: 600,
3049
+ backgroundColor: "#f9fafb"
3050
+ };
3051
+ const sortButtonStyles = {
3052
+ background: "none",
3053
+ border: "none",
3054
+ cursor: "pointer",
3055
+ display: "flex",
3056
+ alignItems: "center",
3057
+ gap: "8px",
3058
+ font: "inherit",
3059
+ fontWeight: 600,
3060
+ padding: 0,
3061
+ width: "100%"
3062
+ };
3063
+ const tdStyles = {
3064
+ padding: "12px",
3065
+ borderBottom: "1px solid #e5e7eb"
3066
+ };
3067
+ const rowStyles = (isSelected) => ({
3068
+ backgroundColor: isSelected ? "#eff6ff" : "transparent"
3069
+ });
3070
+ const getSortIcon = (column) => {
3071
+ if (sortColumn !== column) return "\u21C5";
3072
+ if (sortDirection === "asc") return "\u2191";
3073
+ if (sortDirection === "desc") return "\u2193";
3074
+ return "\u21C5";
3075
+ };
3076
+ return /* @__PURE__ */ jsxs(
3077
+ "table",
3078
+ {
3079
+ id: tableId,
3080
+ className,
3081
+ style: tableStyles,
3082
+ role: "table",
3083
+ "aria-label": caption,
3084
+ children: [
3085
+ /* @__PURE__ */ jsx("caption", { style: { padding: "12px", textAlign: "left", fontWeight: 600, fontSize: "16px" }, children: caption }),
3086
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
3087
+ selectable && /* @__PURE__ */ jsx("th", { scope: "col", style: { ...thStyles, width: "50px" }, children: /* @__PURE__ */ jsx(
3088
+ "input",
3089
+ {
3090
+ type: "checkbox",
3091
+ checked: isAllSelected,
3092
+ ref: (el) => {
3093
+ if (el) {
3094
+ el.indeterminate = isSomeSelected;
3095
+ }
3096
+ },
3097
+ onChange: (e) => handleSelectAll(e.target.checked),
3098
+ "aria-label": "Select all rows"
3099
+ }
3100
+ ) }),
3101
+ columns.map((column) => {
3102
+ const isSortable = column.sortable || sortable;
3103
+ return /* @__PURE__ */ jsx(
3104
+ "th",
3105
+ {
3106
+ scope: "col",
3107
+ style: { ...thStyles, width: column.width },
3108
+ "aria-sort": sortColumn === column.key ? sortDirection === "asc" ? "ascending" : sortDirection === "desc" ? "descending" : "none" : void 0,
3109
+ children: isSortable ? /* @__PURE__ */ jsxs(
3110
+ "button",
3111
+ {
3112
+ type: "button",
3113
+ onClick: () => handleSort(column.key),
3114
+ style: sortButtonStyles,
3115
+ "aria-label": `Sort by ${column.label}`,
3116
+ children: [
3117
+ /* @__PURE__ */ jsx("span", { children: column.label }),
3118
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: getSortIcon(column.key) })
3119
+ ]
3120
+ }
3121
+ ) : column.label
3122
+ },
3123
+ column.key
3124
+ );
3125
+ })
3126
+ ] }) }),
3127
+ /* @__PURE__ */ jsx("tbody", { children: data.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
3128
+ "td",
3129
+ {
3130
+ colSpan: columns.length + (selectable ? 1 : 0),
3131
+ style: { ...tdStyles, textAlign: "center", color: "#9ca3af" },
3132
+ children: "No data available"
3133
+ }
3134
+ ) }) : data.map((item, index) => {
3135
+ const isSelected = selectedSet.has(index);
3136
+ const rowKey = getRowKey(item, index);
3137
+ return /* @__PURE__ */ jsxs("tr", { style: rowStyles(isSelected), children: [
3138
+ selectable && /* @__PURE__ */ jsx("td", { style: tdStyles, children: /* @__PURE__ */ jsx(
3139
+ "input",
3140
+ {
3141
+ type: "checkbox",
3142
+ checked: isSelected,
3143
+ onChange: (e) => handleSelectRow(index, e.target.checked),
3144
+ "aria-label": `Select row ${index + 1}`
3145
+ }
3146
+ ) }),
3147
+ columns.map((column) => /* @__PURE__ */ jsx("td", { style: tdStyles, children: column.render ? column.render(item, index) : String(item[column.key] ?? "") }, column.key))
3148
+ ] }, rowKey);
3149
+ }) })
3150
+ ]
3151
+ }
3152
+ );
3153
+ }
3154
+ function useReducedMotion() {
3155
+ const [prefersReducedMotion, setPrefersReducedMotion] = useState(() => {
3156
+ if (typeof window === "undefined") return false;
3157
+ const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
3158
+ return mediaQuery.matches;
3159
+ });
3160
+ useEffect(() => {
3161
+ if (typeof window === "undefined") return;
3162
+ const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
3163
+ const handleChange = (event) => {
3164
+ setPrefersReducedMotion(event.matches);
3165
+ };
3166
+ if (mediaQuery.addEventListener) {
3167
+ mediaQuery.addEventListener("change", handleChange);
3168
+ return () => mediaQuery.removeEventListener("change", handleChange);
3169
+ }
3170
+ mediaQuery.addListener(handleChange);
3171
+ return () => mediaQuery.removeListener(handleChange);
3172
+ }, []);
3173
+ return prefersReducedMotion;
3174
+ }
3175
+ function useAnnounce() {
3176
+ const regionRef = useRef(null);
3177
+ useEffect(() => {
3178
+ if (!regionRef.current) {
3179
+ const region = document.createElement("div");
3180
+ region.setAttribute("role", "status");
3181
+ region.setAttribute("aria-live", "polite");
3182
+ region.setAttribute("aria-atomic", "true");
3183
+ region.style.position = "absolute";
3184
+ region.style.left = "-10000px";
3185
+ region.style.width = "1px";
3186
+ region.style.height = "1px";
3187
+ region.style.overflow = "hidden";
3188
+ document.body.appendChild(region);
3189
+ regionRef.current = region;
3190
+ }
3191
+ return () => {
3192
+ if (regionRef.current) {
3193
+ document.body.removeChild(regionRef.current);
3194
+ regionRef.current = null;
3195
+ }
3196
+ };
3197
+ }, []);
3198
+ const announce6 = useCallback(
3199
+ (message, politeness = "polite") => {
3200
+ if (!regionRef.current) return;
3201
+ regionRef.current.setAttribute("aria-live", politeness);
3202
+ regionRef.current.textContent = "";
3203
+ setTimeout(() => {
3204
+ if (regionRef.current) {
3205
+ regionRef.current.textContent = message;
3206
+ }
3207
+ }, 100);
3208
+ setTimeout(() => {
3209
+ if (regionRef.current) {
3210
+ regionRef.current.textContent = "";
3211
+ }
3212
+ }, 5100);
3213
+ },
3214
+ []
3215
+ );
3216
+ return announce6;
3217
+ }
3218
+ var AccessibleCarousel = ({
3219
+ items,
3220
+ autoPlay = false,
3221
+ interval = 5e3,
3222
+ controls = true,
3223
+ indicators = true,
3224
+ loop = true,
3225
+ ariaLabel = "Carousel",
3226
+ className = "",
3227
+ style = {}
3228
+ }) => {
3229
+ const [currentIndex, setCurrentIndex] = useState(0);
3230
+ const [isPlaying, setIsPlaying] = useState(autoPlay);
3231
+ const [isPaused, setIsPaused] = useState(false);
3232
+ const carouselId = useId2("carousel");
3233
+ const regionRef = useRef(null);
3234
+ const intervalRef = useRef(void 0);
3235
+ const prefersReducedMotion = useReducedMotion();
3236
+ const announce6 = useAnnounce();
3237
+ const totalSlides = items.length;
3238
+ useEffect(() => {
3239
+ if (isPlaying && !isPaused && !prefersReducedMotion) {
3240
+ intervalRef.current = setInterval(() => {
3241
+ handleNext();
3242
+ }, interval);
3243
+ }
3244
+ return () => {
3245
+ if (intervalRef.current) {
3246
+ clearInterval(intervalRef.current);
3247
+ }
3248
+ };
3249
+ }, [isPlaying, isPaused, currentIndex, interval, prefersReducedMotion]);
3250
+ const handlePrevious = () => {
3251
+ const newIndex = currentIndex === 0 ? loop ? totalSlides - 1 : 0 : currentIndex - 1;
3252
+ setCurrentIndex(newIndex);
3253
+ announce6(`Slide ${newIndex + 1} of ${totalSlides}`, "polite");
3254
+ };
3255
+ const handleNext = () => {
3256
+ const newIndex = currentIndex === totalSlides - 1 ? loop ? 0 : totalSlides - 1 : currentIndex + 1;
3257
+ setCurrentIndex(newIndex);
3258
+ announce6(`Slide ${newIndex + 1} of ${totalSlides}`, "polite");
3259
+ };
3260
+ const handleGoToSlide = (index) => {
3261
+ setCurrentIndex(index);
3262
+ announce6(`Slide ${index + 1} of ${totalSlides}`, "polite");
3263
+ };
3264
+ const handlePlayPause = () => {
3265
+ setIsPlaying(!isPlaying);
3266
+ announce6(isPlaying ? "Carousel paused" : "Carousel playing", "polite");
3267
+ };
3268
+ const handleKeyDown = (event) => {
3269
+ switch (event.key) {
3270
+ case "ArrowLeft":
3271
+ event.preventDefault();
3272
+ handlePrevious();
3273
+ break;
3274
+ case "ArrowRight":
3275
+ event.preventDefault();
3276
+ handleNext();
3277
+ break;
3278
+ case "Home":
3279
+ event.preventDefault();
3280
+ handleGoToSlide(0);
3281
+ break;
3282
+ case "End":
3283
+ event.preventDefault();
3284
+ handleGoToSlide(totalSlides - 1);
3285
+ break;
3286
+ }
3287
+ };
3288
+ const containerStyles = {
3289
+ position: "relative",
3290
+ overflow: "hidden",
3291
+ width: "100%",
3292
+ ...style
3293
+ };
3294
+ const slidesContainerStyles = {
3295
+ display: "flex",
3296
+ transition: prefersReducedMotion ? "none" : "transform 0.5s ease-in-out",
3297
+ transform: `translateX(-${currentIndex * 100}%)`
3298
+ };
3299
+ const slideStyles = {
3300
+ minWidth: "100%",
3301
+ flex: "0 0 auto"
3302
+ };
3303
+ const controlsContainerStyles = {
3304
+ position: "absolute",
3305
+ top: "50%",
3306
+ left: 0,
3307
+ right: 0,
3308
+ transform: "translateY(-50%)",
3309
+ display: "flex",
3310
+ justifyContent: "space-between",
3311
+ padding: "0 16px",
3312
+ pointerEvents: "none"
3313
+ };
3314
+ const controlButtonStyles = {
3315
+ pointerEvents: "auto",
3316
+ width: "48px",
3317
+ height: "48px",
3318
+ borderRadius: "50%",
3319
+ border: "none",
3320
+ backgroundColor: "rgba(0, 0, 0, 0.5)",
3321
+ color: "#fff",
3322
+ fontSize: "20px",
3323
+ cursor: "pointer",
3324
+ display: "flex",
3325
+ alignItems: "center",
3326
+ justifyContent: "center",
3327
+ transition: "background-color 0.2s"
3328
+ };
3329
+ const indicatorsStyles = {
3330
+ display: "flex",
3331
+ justifyContent: "center",
3332
+ gap: "8px",
3333
+ marginTop: "16px"
3334
+ };
3335
+ const indicatorButtonStyles = (isActive) => ({
3336
+ width: "12px",
3337
+ height: "12px",
3338
+ borderRadius: "50%",
3339
+ border: "none",
3340
+ backgroundColor: isActive ? "#3b82f6" : "#d1d5db",
3341
+ cursor: "pointer",
3342
+ padding: 0,
3343
+ transition: "background-color 0.2s"
3344
+ });
3345
+ const playPauseButtonStyles = {
3346
+ position: "absolute",
3347
+ bottom: "16px",
3348
+ right: "16px",
3349
+ padding: "8px 16px",
3350
+ borderRadius: "4px",
3351
+ border: "none",
3352
+ backgroundColor: "rgba(0, 0, 0, 0.7)",
3353
+ color: "#fff",
3354
+ fontSize: "14px",
3355
+ cursor: "pointer",
3356
+ display: "flex",
3357
+ alignItems: "center",
3358
+ gap: "6px"
3359
+ };
3360
+ return /* @__PURE__ */ jsxs(
3361
+ "section",
3362
+ {
3363
+ ref: regionRef,
3364
+ "aria-roledescription": "carousel",
3365
+ "aria-label": ariaLabel,
3366
+ className,
3367
+ onMouseEnter: () => setIsPaused(true),
3368
+ onMouseLeave: () => setIsPaused(false),
3369
+ onFocus: () => setIsPaused(true),
3370
+ onBlur: () => setIsPaused(false),
3371
+ onKeyDown: handleKeyDown,
3372
+ tabIndex: 0,
3373
+ children: [
3374
+ /* @__PURE__ */ jsxs("div", { style: containerStyles, children: [
3375
+ /* @__PURE__ */ jsx(
3376
+ "div",
3377
+ {
3378
+ id: `${carouselId}-slides`,
3379
+ style: slidesContainerStyles,
3380
+ "aria-live": "polite",
3381
+ "aria-atomic": "false",
3382
+ children: items.map((item, index) => /* @__PURE__ */ jsx(
3383
+ "div",
3384
+ {
3385
+ role: "group",
3386
+ "aria-roledescription": "slide",
3387
+ "aria-label": `Slide ${index + 1} of ${totalSlides}`,
3388
+ "aria-hidden": index !== currentIndex,
3389
+ style: slideStyles,
3390
+ children: item
3391
+ },
3392
+ index
3393
+ ))
3394
+ }
3395
+ ),
3396
+ controls && /* @__PURE__ */ jsxs("div", { style: controlsContainerStyles, children: [
3397
+ /* @__PURE__ */ jsx(
3398
+ "button",
3399
+ {
3400
+ type: "button",
3401
+ onClick: handlePrevious,
3402
+ "aria-label": "Previous slide",
3403
+ "aria-controls": `${carouselId}-slides`,
3404
+ disabled: !loop && currentIndex === 0,
3405
+ style: {
3406
+ ...controlButtonStyles,
3407
+ opacity: !loop && currentIndex === 0 ? 0.5 : 1,
3408
+ cursor: !loop && currentIndex === 0 ? "not-allowed" : "pointer"
3409
+ },
3410
+ onMouseEnter: (e) => {
3411
+ if (loop || currentIndex !== 0) {
3412
+ e.currentTarget.style.backgroundColor = "rgba(0, 0, 0, 0.7)";
3413
+ }
3414
+ },
3415
+ onMouseLeave: (e) => {
3416
+ e.currentTarget.style.backgroundColor = "rgba(0, 0, 0, 0.5)";
3417
+ },
3418
+ children: "\u2039"
3419
+ }
3420
+ ),
3421
+ /* @__PURE__ */ jsx(
3422
+ "button",
3423
+ {
3424
+ type: "button",
3425
+ onClick: handleNext,
3426
+ "aria-label": "Next slide",
3427
+ "aria-controls": `${carouselId}-slides`,
3428
+ disabled: !loop && currentIndex === totalSlides - 1,
3429
+ style: {
3430
+ ...controlButtonStyles,
3431
+ opacity: !loop && currentIndex === totalSlides - 1 ? 0.5 : 1,
3432
+ cursor: !loop && currentIndex === totalSlides - 1 ? "not-allowed" : "pointer"
3433
+ },
3434
+ onMouseEnter: (e) => {
3435
+ if (loop || currentIndex !== totalSlides - 1) {
3436
+ e.currentTarget.style.backgroundColor = "rgba(0, 0, 0, 0.7)";
3437
+ }
3438
+ },
3439
+ onMouseLeave: (e) => {
3440
+ e.currentTarget.style.backgroundColor = "rgba(0, 0, 0, 0.5)";
3441
+ },
3442
+ children: "\u203A"
3443
+ }
3444
+ )
3445
+ ] }),
3446
+ autoPlay && /* @__PURE__ */ jsxs(
3447
+ "button",
3448
+ {
3449
+ type: "button",
3450
+ onClick: handlePlayPause,
3451
+ "aria-label": isPlaying ? "Pause carousel" : "Play carousel",
3452
+ style: playPauseButtonStyles,
3453
+ onMouseEnter: (e) => {
3454
+ e.currentTarget.style.backgroundColor = "rgba(0, 0, 0, 0.9)";
3455
+ },
3456
+ onMouseLeave: (e) => {
3457
+ e.currentTarget.style.backgroundColor = "rgba(0, 0, 0, 0.7)";
3458
+ },
3459
+ children: [
3460
+ isPlaying ? "\u23F8" : "\u25B6",
3461
+ " ",
3462
+ isPlaying ? "Pause" : "Play"
3463
+ ]
3464
+ }
3465
+ )
3466
+ ] }),
3467
+ indicators && /* @__PURE__ */ jsx("div", { role: "group", "aria-label": "Slide indicators", style: indicatorsStyles, children: items.map((_, index) => /* @__PURE__ */ jsx(
3468
+ "button",
3469
+ {
3470
+ type: "button",
3471
+ onClick: () => handleGoToSlide(index),
3472
+ "aria-label": `Go to slide ${index + 1}`,
3473
+ "aria-current": index === currentIndex ? "true" : "false",
3474
+ style: indicatorButtonStyles(index === currentIndex),
3475
+ onMouseEnter: (e) => {
3476
+ if (index !== currentIndex) {
3477
+ e.currentTarget.style.backgroundColor = "#9ca3af";
3478
+ }
3479
+ },
3480
+ onMouseLeave: (e) => {
3481
+ if (index !== currentIndex) {
3482
+ e.currentTarget.style.backgroundColor = "#d1d5db";
3483
+ }
3484
+ }
3485
+ },
3486
+ index
3487
+ )) })
3488
+ ]
3489
+ }
3490
+ );
3491
+ };
3492
+ var AccessibleDrawer = ({
3493
+ isOpen,
3494
+ onClose,
3495
+ side = "right",
3496
+ modal = true,
3497
+ title,
3498
+ children,
3499
+ className = "",
3500
+ backdropClassName = "",
3501
+ style = {}
3502
+ }) => {
3503
+ const drawerId = useId2("drawer");
3504
+ const titleId = useId2("drawer-title");
3505
+ const { trapRef } = useFocusTrap({
3506
+ isActive: isOpen && modal,
3507
+ onEscape: onClose
3508
+ });
3509
+ const prefersReducedMotion = useReducedMotion();
3510
+ useEffect(() => {
3511
+ if (isOpen && modal) {
3512
+ const originalOverflow = document.body.style.overflow;
3513
+ document.body.style.overflow = "hidden";
3514
+ return () => {
3515
+ document.body.style.overflow = originalOverflow;
3516
+ };
3517
+ }
3518
+ }, [isOpen, modal]);
3519
+ if (!isOpen) return null;
3520
+ const getDrawerPosition = () => {
3521
+ const baseStyles = {
3522
+ position: "fixed",
3523
+ zIndex: 1e3,
3524
+ backgroundColor: "#fff",
3525
+ boxShadow: "0 4px 6px rgba(0, 0, 0, 0.1)",
3526
+ transition: prefersReducedMotion ? "none" : "transform 0.3s ease-in-out"
3527
+ };
3528
+ switch (side) {
3529
+ case "left":
3530
+ return {
3531
+ ...baseStyles,
3532
+ top: 0,
3533
+ left: 0,
3534
+ bottom: 0,
3535
+ width: "320px",
3536
+ maxWidth: "80vw"
3537
+ };
3538
+ case "right":
3539
+ return {
3540
+ ...baseStyles,
3541
+ top: 0,
3542
+ right: 0,
3543
+ bottom: 0,
3544
+ width: "320px",
3545
+ maxWidth: "80vw"
3546
+ };
3547
+ case "top":
3548
+ return {
3549
+ ...baseStyles,
3550
+ top: 0,
3551
+ left: 0,
3552
+ right: 0,
3553
+ height: "320px",
3554
+ maxHeight: "80vh"
3555
+ };
3556
+ case "bottom":
3557
+ return {
3558
+ ...baseStyles,
3559
+ bottom: 0,
3560
+ left: 0,
3561
+ right: 0,
3562
+ height: "320px",
3563
+ maxHeight: "80vh"
3564
+ };
3565
+ default:
3566
+ return baseStyles;
3567
+ }
3568
+ };
3569
+ const backdropStyles = {
3570
+ position: "fixed",
3571
+ top: 0,
3572
+ left: 0,
3573
+ right: 0,
3574
+ bottom: 0,
3575
+ backgroundColor: "rgba(0, 0, 0, 0.5)",
3576
+ zIndex: 999,
3577
+ opacity: isOpen ? 1 : 0,
3578
+ transition: prefersReducedMotion ? "none" : "opacity 0.3s ease-in-out"
3579
+ };
3580
+ const drawerStyles = {
3581
+ ...getDrawerPosition(),
3582
+ display: "flex",
3583
+ flexDirection: "column",
3584
+ overflowY: "auto",
3585
+ ...style
3586
+ };
3587
+ const headerStyles = {
3588
+ display: "flex",
3589
+ justifyContent: "space-between",
3590
+ alignItems: "center",
3591
+ padding: "16px 20px",
3592
+ borderBottom: "1px solid #e5e7eb"
3593
+ };
3594
+ const titleStyles = {
3595
+ fontSize: "18px",
3596
+ fontWeight: 600,
3597
+ margin: 0
3598
+ };
3599
+ const closeButtonStyles = {
3600
+ width: "32px",
3601
+ height: "32px",
3602
+ borderRadius: "4px",
3603
+ border: "none",
3604
+ backgroundColor: "transparent",
3605
+ cursor: "pointer",
3606
+ display: "flex",
3607
+ alignItems: "center",
3608
+ justifyContent: "center",
3609
+ fontSize: "20px",
3610
+ transition: "background-color 0.2s"
3611
+ };
3612
+ const contentStyles = {
3613
+ flex: 1,
3614
+ padding: "20px",
3615
+ overflowY: "auto"
3616
+ };
3617
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3618
+ modal && /* @__PURE__ */ jsx(
3619
+ "div",
3620
+ {
3621
+ className: backdropClassName,
3622
+ style: backdropStyles,
3623
+ onClick: onClose,
3624
+ "aria-hidden": "true"
3625
+ }
3626
+ ),
3627
+ /* @__PURE__ */ jsxs(
3628
+ "div",
3629
+ {
3630
+ ref: modal ? trapRef : null,
3631
+ id: drawerId,
3632
+ role: "dialog",
3633
+ "aria-modal": modal,
3634
+ "aria-labelledby": title ? titleId : void 0,
3635
+ className,
3636
+ style: drawerStyles,
3637
+ children: [
3638
+ /* @__PURE__ */ jsxs("div", { style: headerStyles, children: [
3639
+ title && /* @__PURE__ */ jsx("h2", { id: titleId, style: titleStyles, children: title }),
3640
+ /* @__PURE__ */ jsx(
3641
+ "button",
3642
+ {
3643
+ type: "button",
3644
+ onClick: onClose,
3645
+ "aria-label": "Close drawer",
3646
+ style: closeButtonStyles,
3647
+ onMouseEnter: (e) => {
3648
+ e.currentTarget.style.backgroundColor = "#f3f4f6";
3649
+ },
3650
+ onMouseLeave: (e) => {
3651
+ e.currentTarget.style.backgroundColor = "transparent";
3652
+ },
3653
+ children: "\xD7"
3654
+ }
3655
+ )
3656
+ ] }),
3657
+ /* @__PURE__ */ jsx("div", { style: contentStyles, children })
3658
+ ]
3659
+ }
3660
+ )
3661
+ ] });
3662
+ };
847
3663
  var useAccessibleForm = (config) => {
848
3664
  const {
849
3665
  fields,
@@ -1154,6 +3970,28 @@ var useFormField = (props) => {
1154
3970
  fieldRef
1155
3971
  };
1156
3972
  };
3973
+ function useMediaQuery(query) {
3974
+ const [matches, setMatches] = useState(() => {
3975
+ if (typeof window === "undefined") return false;
3976
+ const mediaQuery = window.matchMedia(query);
3977
+ return mediaQuery.matches;
3978
+ });
3979
+ useEffect(() => {
3980
+ if (typeof window === "undefined") return;
3981
+ const mediaQuery = window.matchMedia(query);
3982
+ setMatches(mediaQuery.matches);
3983
+ const handleChange = (event) => {
3984
+ setMatches(event.matches);
3985
+ };
3986
+ if (mediaQuery.addEventListener) {
3987
+ mediaQuery.addEventListener("change", handleChange);
3988
+ return () => mediaQuery.removeEventListener("change", handleChange);
3989
+ }
3990
+ mediaQuery.addListener(handleChange);
3991
+ return () => mediaQuery.removeListener(handleChange);
3992
+ }, [query]);
3993
+ return matches;
3994
+ }
1157
3995
  var DialogStackContext = createContext(null);
1158
3996
  var useDialogStack = () => {
1159
3997
  const context = useContext(DialogStackContext);
@@ -1870,6 +4708,6 @@ var Wizard = (props) => {
1870
4708
  // src/index.ts
1871
4709
  var VERSION = "0.0.0";
1872
4710
 
1873
- export { AccessibleButton, AccessibleDialog, AccessibleMenu, AccessibleModal, AccessibleTabs, DialogStackProvider, InfiniteList, NestedMenu, VERSION, VirtualizedList, Wizard, useAccessibleButton, useAccessibleDialog, useAccessibleForm, useDialogStack, useFocusTrap, useFormField, useKeyboardNavigation, useWizard };
4711
+ export { AccessibleAccordion, AccessibleBreadcrumb, AccessibleButton, AccessibleCarousel, AccessibleCheckboxGroup, AccessibleCombobox, AccessibleDatePicker, AccessibleDialog, AccessibleDrawer, AccessibleMenu, AccessibleModal, AccessiblePagination, AccessibleProgress, AccessibleRadioGroup, AccessibleTable, AccessibleTabs, AccessibleToast, AccessibleToggle, AccessibleTooltip, AccessibleTreeView, DialogStackProvider, InfiniteList, NestedMenu, SkipLinks, ToastContainer, VERSION, VirtualizedList, Wizard, useAccessibleButton, useAccessibleDialog, useAccessibleForm, useAnnounce, useAriaLive, useClickOutside, useDialogStack, useFocusTrap, useFormField, useId2 as useId, useKeyboardNavigation, useMediaQuery, useReducedMotion, useWizard };
1874
4712
  //# sourceMappingURL=index.js.map
1875
4713
  //# sourceMappingURL=index.js.map