@gamecp/ui 0.1.35 → 0.1.37

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.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
- import React5, { forwardRef, useState, useRef, useEffect, useCallback, useMemo, useId } from 'react';
3
- import { RiArrowDownSLine, RiCloseLine, RiSaveLine, RiCheckLine, RiSearchLine, RiAddLine, RiCodeLine, RiSettings3Line, RiFileCopyLine, RiEyeOffLine, RiEyeLine, RiRefreshLine, RiLoader4Line, RiFilterLine, RiGridFill, RiListUnordered, RiFileCopyFill, RiPauseFill, RiPauseLine, RiRestartFill, RiRestartLine, RiStopFill, RiStopLine, RiPlayFill, RiPlayLine, RiBarChartFill, RiBarChartLine, RiEyeFill, RiPlayCircleFill, RiPlayCircleLine, RiStopCircleFill, RiStopCircleLine, RiDeleteBinFill, RiDeleteBinLine, RiEditFill, RiEditLine, RiErrorWarningLine, RiInformationLine, RiAlertLine, RiShieldLine, RiLeafLine, RiGamepadLine, RiServerLine, RiWifiLine, RiArrowLeftSLine, RiArrowRightSLine } from 'react-icons/ri';
1
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
+ import React6, { forwardRef, useState, useRef, useEffect, useCallback, useMemo, useId } from 'react';
3
+ import { RiArrowDownSLine, RiCloseLine, RiSaveLine, RiCheckLine, RiSearchLine, RiAddLine, RiCodeLine, RiSettings3Line, RiFileCopyLine, RiEyeOffLine, RiEyeLine, RiRefreshLine, RiLoader4Line, RiFilterLine, RiGridFill, RiListUnordered, RiErrorWarningLine, RiInformationLine, RiAlertLine, RiShieldLine, RiLeafLine, RiGamepadLine, RiServerLine, RiWifiLine, RiArrowLeftSLine, RiArrowRightSLine, RiFileCopyFill, RiPauseFill, RiPauseLine, RiRestartFill, RiRestartLine, RiStopFill, RiStopLine, RiPlayFill, RiPlayLine, RiBarChartFill, RiBarChartLine, RiEyeFill, RiPlayCircleFill, RiPlayCircleLine, RiStopCircleFill, RiStopCircleLine, RiDeleteBinFill, RiDeleteBinLine, RiEditFill, RiEditLine } from 'react-icons/ri';
4
4
  import { createPortal } from 'react-dom';
5
5
  import { AnimatePresence, motion } from 'framer-motion';
6
6
  import { Tooltip } from 'react-tooltip';
@@ -64,7 +64,7 @@ function Badge({
64
64
  var variantClasses = {
65
65
  primary: "btn-primary",
66
66
  secondary: "btn-secondary",
67
- danger: "btn-danger",
67
+ danger: "action-btn-delete",
68
68
  success: "action-btn-start",
69
69
  warning: "action-btn-pause",
70
70
  ghost: "bg-transparent hover:bg-muted hover:text-foreground border-transparent",
@@ -103,38 +103,44 @@ var Button = forwardRef(
103
103
  ref,
104
104
  className: classes,
105
105
  disabled: disabled || isLoading,
106
+ "aria-busy": isLoading,
106
107
  ...props,
107
108
  children: [
108
- isLoading && /* @__PURE__ */ jsxs(
109
- "svg",
110
- {
111
- className: "animate-spin h-4 w-4",
112
- xmlns: "http://www.w3.org/2000/svg",
113
- fill: "none",
114
- viewBox: "0 0 24 24",
115
- children: [
116
- /* @__PURE__ */ jsx(
117
- "circle",
118
- {
119
- className: "opacity-25",
120
- cx: "12",
121
- cy: "12",
122
- r: "10",
123
- stroke: "currentColor",
124
- strokeWidth: "4"
125
- }
126
- ),
127
- /* @__PURE__ */ jsx(
128
- "path",
129
- {
130
- className: "opacity-75",
131
- fill: "currentColor",
132
- d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
133
- }
134
- )
135
- ]
136
- }
137
- ),
109
+ isLoading && /* @__PURE__ */ jsxs(Fragment, { children: [
110
+ /* @__PURE__ */ jsxs(
111
+ "svg",
112
+ {
113
+ className: "animate-spin h-4 w-4",
114
+ xmlns: "http://www.w3.org/2000/svg",
115
+ fill: "none",
116
+ viewBox: "0 0 24 24",
117
+ "aria-hidden": "true",
118
+ role: "status",
119
+ children: [
120
+ /* @__PURE__ */ jsx(
121
+ "circle",
122
+ {
123
+ className: "opacity-25",
124
+ cx: "12",
125
+ cy: "12",
126
+ r: "10",
127
+ stroke: "currentColor",
128
+ strokeWidth: "4"
129
+ }
130
+ ),
131
+ /* @__PURE__ */ jsx(
132
+ "path",
133
+ {
134
+ className: "opacity-75",
135
+ fill: "currentColor",
136
+ d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
137
+ }
138
+ )
139
+ ]
140
+ }
141
+ ),
142
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Loading..." })
143
+ ] }),
138
144
  !isLoading && leftIcon && /* @__PURE__ */ jsx("span", { className: "flex-shrink-0", children: leftIcon }),
139
145
  children,
140
146
  !isLoading && rightIcon && /* @__PURE__ */ jsx("span", { className: "flex-shrink-0", children: rightIcon })
@@ -301,6 +307,13 @@ function Card({
301
307
  if (disabled) return;
302
308
  onClick?.();
303
309
  };
310
+ const handleKeyDown = (e) => {
311
+ if (disabled) return;
312
+ if ((clickable || onClick) && (e.key === "Enter" || e.key === " ")) {
313
+ e.preventDefault();
314
+ handleClick();
315
+ }
316
+ };
304
317
  const handleToggle = () => {
305
318
  if (accordion) {
306
319
  const newExpanded = !isExpanded;
@@ -314,6 +327,10 @@ function Card({
314
327
  className: `${baseClasses} ${className}`,
315
328
  style,
316
329
  onClick: clickable || onClick ? handleClick : void 0,
330
+ onKeyDown: clickable || onClick ? handleKeyDown : void 0,
331
+ role: clickable || onClick ? "button" : void 0,
332
+ tabIndex: clickable || onClick ? 0 : void 0,
333
+ "aria-disabled": disabled,
317
334
  children: [
318
335
  activeHeaderBorder && /* @__PURE__ */ jsx("div", { className: "absolute top-0 left-0 right-0 flex justify-center z-10 pointer-events-none", children: /* @__PURE__ */ jsx(
319
336
  "div",
@@ -851,7 +868,7 @@ function Modal({
851
868
  header ? /* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: header }) : title ? /* @__PURE__ */ jsxs(
852
869
  "div",
853
870
  {
854
- className: `px-6 py-4 border-b border-border flex justify-between items-center flex-shrink-0 ${customStyles.header || ""}`,
871
+ className: `px-6 py-4 border-b border-border flex justify-between items-center flex-shrink-0 bg-muted ${customStyles.header || ""}`,
855
872
  children: [
856
873
  /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: /* @__PURE__ */ jsx(
857
874
  "h2",
@@ -895,7 +912,7 @@ function Modal({
895
912
  footer && /* @__PURE__ */ jsx(
896
913
  "div",
897
914
  {
898
- className: `flex-shrink-0 px-6 py-4 border-t border-border ${footerBg === "white" ? "bg-card" : "bg-muted"} ${customStyles.footer || ""}`,
915
+ className: `flex-shrink-0 px-6 py-4 border-t border-border ${footerBg === "gray" ? "bg-card" : "bg-card"} ${customStyles.footer || ""}`,
899
916
  children: footer
900
917
  }
901
918
  )
@@ -926,7 +943,7 @@ function ConfirmDialog({ isOpen, options, onConfirm, onCancel }) {
926
943
  /* @__PURE__ */ jsx("p", { className: "text-foreground", children: message }),
927
944
  /* @__PURE__ */ jsxs("div", { className: "flex gap-3 justify-end", children: [
928
945
  /* @__PURE__ */ jsx(Button_default, { variant: "secondary", onClick: onCancel, children: cancelText }),
929
- /* @__PURE__ */ jsx(Button_default, { variant: getConfirmVariant(), onClick: onConfirm, children: confirmText })
946
+ /* @__PURE__ */ jsx(Button_default, { variant: getConfirmVariant(), onClick: onConfirm, autoFocus: true, children: confirmText })
930
947
  ] })
931
948
  ] }) });
932
949
  }
@@ -1018,9 +1035,13 @@ function DropdownItem({
1018
1035
  item,
1019
1036
  selected = false,
1020
1037
  multiple = false,
1021
- onClick
1038
+ onClick,
1039
+ focused = false,
1040
+ itemId
1022
1041
  }) {
1023
1042
  const { label, description, icon, disabled, variant = "default", example } = item;
1043
+ const resolvedIcon = typeof icon === "function" ? icon(selected) : icon;
1044
+ const resolvedDescription = typeof description === "function" ? description(selected) : description;
1024
1045
  const variantClasses5 = {
1025
1046
  default: "text-foreground hover:bg-muted/50",
1026
1047
  danger: "text-danger hover:bg-danger/10",
@@ -1029,9 +1050,11 @@ function DropdownItem({
1029
1050
  };
1030
1051
  const baseClasses = `
1031
1052
  w-full px-3 py-2 text-left text-sm transition-colors
1032
- flex items-start gap-3
1053
+ flex items-start gap-3 border-b border-border/30 last:border-b-0
1033
1054
  ${disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"}
1034
1055
  ${selected && !multiple ? "bg-primary text-primary-foreground" : variantClasses5[variant]}
1056
+ ${focused && !selected ? "bg-muted/70 ring-2 ring-primary/50 ring-inset" : ""}
1057
+ ${focused && selected ? "brightness-110 ring-2 ring-primary-foreground/30" : ""}
1035
1058
  `;
1036
1059
  return /* @__PURE__ */ jsxs(
1037
1060
  "button",
@@ -1042,6 +1065,9 @@ function DropdownItem({
1042
1065
  className: baseClasses,
1043
1066
  role: "option",
1044
1067
  "aria-selected": selected,
1068
+ "aria-disabled": disabled,
1069
+ id: itemId,
1070
+ tabIndex: -1,
1045
1071
  children: [
1046
1072
  multiple && /* @__PURE__ */ jsx(
1047
1073
  "div",
@@ -1049,16 +1075,16 @@ function DropdownItem({
1049
1075
  className: `
1050
1076
  mt-0.5 w-4 h-4 rounded border flex-shrink-0
1051
1077
  flex items-center justify-center
1052
- ${selected ? "bg-primary border-primary" : "border-input"}
1078
+ ${selected ? "bg-primary border-primary" : "border-border"}
1053
1079
  `,
1054
1080
  children: selected && /* @__PURE__ */ jsx(RiCheckLine, { className: "w-3 h-3 text-primary-foreground" })
1055
1081
  }
1056
1082
  ),
1057
- icon && /* @__PURE__ */ jsx("div", { className: `mt-0.5 flex-shrink-0 ${selected && !multiple ? "text-primary-foreground" : "text-muted-foreground"}`, children: icon }),
1083
+ resolvedIcon && /* @__PURE__ */ jsx("div", { className: `mt-0.5 flex-shrink-0 ${selected ? "text-primary-foreground" : "text-muted-foreground"}`, children: resolvedIcon }),
1058
1084
  /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
1059
- /* @__PURE__ */ jsx("div", { className: `font-medium ${selected && !multiple ? "text-primary-foreground" : ""}`, children: label }),
1060
- description && /* @__PURE__ */ jsx("div", { className: `text-xs mt-0.5 ${selected && !multiple ? "text-primary-foreground/80" : "text-muted-foreground"}`, children: description }),
1061
- example && /* @__PURE__ */ jsx("div", { className: `text-xs mt-0.5 ${selected && !multiple ? "text-primary-foreground/60" : "text-secondary-foreground"}`, children: example })
1085
+ /* @__PURE__ */ jsx("div", { className: `font-medium ${selected ? "text-primary-foreground" : ""}`, children: label }),
1086
+ resolvedDescription && /* @__PURE__ */ jsx("div", { className: `text-xs mt-0.5 ${selected ? "text-primary-foreground" : "text-muted-foreground"}`, children: resolvedDescription }),
1087
+ example && /* @__PURE__ */ jsx("div", { className: `text-xs mt-0.5 ${selected ? "text-primary-foreground/60" : "text-secondary-foreground"}`, children: example })
1062
1088
  ] }),
1063
1089
  selected && !multiple && /* @__PURE__ */ jsx(RiCheckLine, { className: "w-4 h-4 flex-shrink-0 text-primary-foreground" })
1064
1090
  ]
@@ -1090,6 +1116,7 @@ function Dropdown({
1090
1116
  }) {
1091
1117
  const [isOpen, setIsOpen] = useState(false);
1092
1118
  const [calculatedPosition, setCalculatedPosition] = useState(null);
1119
+ const [focusedIndex, setFocusedIndex] = useState(-1);
1093
1120
  const triggerRef = useRef(null);
1094
1121
  const dropdownRef = useRef(null);
1095
1122
  const shouldCloseOnSelect = closeOnSelect ?? !multiple;
@@ -1178,13 +1205,63 @@ function Dropdown({
1178
1205
  useEffect(() => {
1179
1206
  if (!isOpen) return;
1180
1207
  const handleKeyDown = (event) => {
1181
- if (event.key === "Escape") {
1182
- setIsOpen(false);
1208
+ if (!items || items.length === 0) {
1209
+ if (event.key === "Escape") {
1210
+ setIsOpen(false);
1211
+ }
1212
+ return;
1213
+ }
1214
+ switch (event.key) {
1215
+ case "ArrowDown":
1216
+ event.preventDefault();
1217
+ setFocusedIndex((prev) => {
1218
+ const nextIndex = prev < items.length - 1 ? prev + 1 : 0;
1219
+ let checkIndex = nextIndex;
1220
+ while (items[checkIndex]?.disabled && checkIndex !== prev) {
1221
+ checkIndex = checkIndex < items.length - 1 ? checkIndex + 1 : 0;
1222
+ }
1223
+ return checkIndex;
1224
+ });
1225
+ break;
1226
+ case "ArrowUp":
1227
+ event.preventDefault();
1228
+ setFocusedIndex((prev) => {
1229
+ const nextIndex = prev > 0 ? prev - 1 : items.length - 1;
1230
+ let checkIndex = nextIndex;
1231
+ while (items[checkIndex]?.disabled && checkIndex !== prev) {
1232
+ checkIndex = checkIndex > 0 ? checkIndex - 1 : items.length - 1;
1233
+ }
1234
+ return checkIndex;
1235
+ });
1236
+ break;
1237
+ case "Home":
1238
+ event.preventDefault();
1239
+ setFocusedIndex(0);
1240
+ break;
1241
+ case "End":
1242
+ event.preventDefault();
1243
+ setFocusedIndex(items.length - 1);
1244
+ break;
1245
+ case "Enter":
1246
+ case " ":
1247
+ event.preventDefault();
1248
+ if (focusedIndex >= 0 && focusedIndex < items.length) {
1249
+ const item = items[focusedIndex];
1250
+ if (!item.disabled) {
1251
+ handleItemSelect(item);
1252
+ }
1253
+ }
1254
+ break;
1255
+ case "Escape":
1256
+ event.preventDefault();
1257
+ setIsOpen(false);
1258
+ setFocusedIndex(-1);
1259
+ break;
1183
1260
  }
1184
1261
  };
1185
1262
  document.addEventListener("keydown", handleKeyDown);
1186
1263
  return () => document.removeEventListener("keydown", handleKeyDown);
1187
- }, [isOpen]);
1264
+ }, [isOpen, items, focusedIndex]);
1188
1265
  useEffect(() => {
1189
1266
  if (!isOpen) return;
1190
1267
  const handleReposition = () => {
@@ -1226,13 +1303,15 @@ function Dropdown({
1226
1303
  return children;
1227
1304
  }
1228
1305
  if (items) {
1229
- return /* @__PURE__ */ jsx("div", { className: "py-1", children: items.map((item) => /* @__PURE__ */ jsx(
1306
+ return /* @__PURE__ */ jsx("div", { className: "py-1", children: items.map((item, index) => /* @__PURE__ */ jsx(
1230
1307
  DropdownItem,
1231
1308
  {
1232
1309
  item,
1233
1310
  selected: isItemSelected(item.value),
1234
1311
  multiple,
1235
- onClick: () => handleItemSelect(item)
1312
+ onClick: () => handleItemSelect(item),
1313
+ focused: focusedIndex === index,
1314
+ itemId: `${id}-option-${index}`
1236
1315
  },
1237
1316
  item.value
1238
1317
  )) });
@@ -1247,9 +1326,18 @@ function Dropdown({
1247
1326
  onClick: handleTriggerClick,
1248
1327
  className: `${disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"} ${triggerClassName}`,
1249
1328
  role: "button",
1329
+ tabIndex: disabled ? -1 : 0,
1250
1330
  "aria-expanded": isOpen,
1251
1331
  "aria-haspopup": "listbox",
1252
- "aria-controls": id,
1332
+ "aria-controls": `${id}-listbox`,
1333
+ "aria-activedescendant": isOpen && focusedIndex >= 0 && items ? `${id}-option-${focusedIndex}` : void 0,
1334
+ onKeyDown: (e) => {
1335
+ if (disabled) return;
1336
+ if (e.key === "Enter" || e.key === " " || e.key === "ArrowDown") {
1337
+ e.preventDefault();
1338
+ setIsOpen(true);
1339
+ }
1340
+ },
1253
1341
  children: trigger
1254
1342
  }
1255
1343
  ),
@@ -1258,8 +1346,10 @@ function Dropdown({
1258
1346
  "div",
1259
1347
  {
1260
1348
  ref: dropdownRef,
1261
- id,
1349
+ id: `${id}-listbox`,
1262
1350
  role: "listbox",
1351
+ "aria-label": "Options",
1352
+ "aria-multiselectable": multiple,
1263
1353
  className: `fixed z-[1000001] card overflow-hidden animate-dropdown-in ${className}`,
1264
1354
  style: {
1265
1355
  top: calculatedPosition.isAbove ? "auto" : calculatedPosition.top,
@@ -2181,6 +2271,9 @@ function Switch({
2181
2271
  onChange(!checked);
2182
2272
  }
2183
2273
  };
2274
+ const switchId = React6.useId();
2275
+ const labelId = label ? `${switchId}-label` : void 0;
2276
+ const descriptionId = description ? `${switchId}-description` : void 0;
2184
2277
  const sizes = {
2185
2278
  sm: { track: "w-8 h-4", thumb: "w-3 h-3", translate: "translate-x-4" },
2186
2279
  md: { track: "w-11 h-6", thumb: "w-5 h-5", translate: "translate-x-5" },
@@ -2228,12 +2321,13 @@ function Switch({
2228
2321
  label && /* @__PURE__ */ jsx(
2229
2322
  "label",
2230
2323
  {
2324
+ id: labelId,
2325
+ htmlFor: switchId,
2231
2326
  className: "text-sm font-medium text-foreground cursor-pointer",
2232
- onClick: toggle,
2233
2327
  children: label
2234
2328
  }
2235
2329
  ),
2236
- description && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground leading-snug", children: description })
2330
+ description && /* @__PURE__ */ jsx("p", { id: descriptionId, className: "text-xs text-muted-foreground leading-snug", children: description })
2237
2331
  ]
2238
2332
  }
2239
2333
  );
@@ -2246,9 +2340,12 @@ function Switch({
2246
2340
  /* @__PURE__ */ jsx(
2247
2341
  "button",
2248
2342
  {
2343
+ id: switchId,
2249
2344
  type: "button",
2250
2345
  role: "switch",
2251
2346
  "aria-checked": checked,
2347
+ "aria-labelledby": labelId,
2348
+ "aria-describedby": descriptionId,
2252
2349
  disabled,
2253
2350
  onClick: toggle,
2254
2351
  "data-tooltip-id": tooltipId,
@@ -3303,14 +3400,14 @@ function FormInput({
3303
3400
  };
3304
3401
  const getIconConfig = (iconProp) => {
3305
3402
  if (!iconProp) return { left: null, right: null };
3306
- if (React5.isValidElement(iconProp)) {
3403
+ if (React6.isValidElement(iconProp)) {
3307
3404
  return { left: iconProp, right: null };
3308
3405
  }
3309
3406
  if (typeof iconProp === "object" && iconProp !== null && ("left" in iconProp || "right" in iconProp)) {
3310
3407
  return { left: iconProp.left || null, right: iconProp.right || null };
3311
3408
  }
3312
3409
  return {
3313
- left: React5.isValidElement(iconProp) ? iconProp : null,
3410
+ left: React6.isValidElement(iconProp) ? iconProp : null,
3314
3411
  right: null
3315
3412
  };
3316
3413
  };
@@ -3916,7 +4013,7 @@ function Notice({
3916
4013
  return /* @__PURE__ */ jsxs(
3917
4014
  "div",
3918
4015
  {
3919
- className: `relative flex items-start ${gap} ${padding} rounded-lg overflow-hidden ${styles.bg} ${className}`,
4016
+ className: `relative flex items-start ${gap} ${padding} rounded-xl overflow-hidden ${styles.bg} ${className}`,
3920
4017
  children: [
3921
4018
  /* @__PURE__ */ jsx(
3922
4019
  "div",
@@ -4094,6 +4191,7 @@ var LoadingSpinner = ({
4094
4191
  ]
4095
4192
  }
4096
4193
  ),
4194
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: message }),
4097
4195
  showMessage && /* @__PURE__ */ jsx("div", { className: "space-y-2", children: /* @__PURE__ */ jsx(
4098
4196
  "p",
4099
4197
  {
@@ -4874,11 +4972,20 @@ function DataTableRow({
4874
4972
  const baseClasses = "hover:bg-muted/50 transition-colors duration-150";
4875
4973
  const clickableClasses = onClick ? "cursor-pointer" : "";
4876
4974
  const hoverClasses = hover ? baseClasses : "";
4975
+ const handleKeyDown = (e) => {
4976
+ if (onClick && (e.key === "Enter" || e.key === " ")) {
4977
+ e.preventDefault();
4978
+ onClick();
4979
+ }
4980
+ };
4877
4981
  return /* @__PURE__ */ jsx(
4878
4982
  "tr",
4879
4983
  {
4880
4984
  className: `${hoverClasses} ${clickableClasses} ${className}`,
4881
4985
  onClick,
4986
+ onKeyDown: handleKeyDown,
4987
+ role: onClick ? "button" : void 0,
4988
+ tabIndex: onClick ? 0 : void 0,
4882
4989
  children
4883
4990
  }
4884
4991
  );