@moontra/moonui 6.17.0 → 6.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -2534,7 +2534,20 @@ var sliderRangeVariants = cva(
2534
2534
  variant: {
2535
2535
  default: "bg-foreground",
2536
2536
  primary: "bg-primary",
2537
- secondary: "bg-secondary",
2537
+ // #389 (#372'nin birebir ardılı): range `bg-secondary` idi — track'in
2538
+ // (`bg-secondary/20`) AYNI yüzey token'ına basıyordu; %20 alfa track ile
2539
+ // tam dolgu iki temada da ~1.0 oranında kalıyor, dolu kısım görünmüyordu
2540
+ // (light rgb(235,239,245) → 1.12 · dark rgb(2,8,23) → 1.01).
2541
+ // `bg-secondary-foreground` BİLEREK seçilmedi: light'ta --primary ile aynı
2542
+ // hue/sat'ta yalnız ΔL=%2 (fill↔fill 1.07), dark'ta birebir --primary
2543
+ // değeri (210 40% 98%) — secondary varyantı primary klonuna dönerdi.
2544
+ // `--secondary-500` (tokens.css slate skalası, RGB üçlüsü) iki temada da
2545
+ // track'e ≥3:1 (WCAG 1.4.11) veren TEK kademe: light 4.63, dark 4.23
2546
+ // (gerçek kart #111827 üstünde 3.83; 400 light'ta 2.50 ✗, 600 dark'ta
2547
+ // 2.66 ✗). Skala :root'ta tanımlı olduğundan tema değişiminde sabittir;
2548
+ // tüketici --secondary-500'ü ezerek özelleştirebilir.
2549
+ // Regresyon: __tests__/slider-secondary-contrast.test.ts
2550
+ secondary: "bg-[rgb(var(--secondary-500))]",
2538
2551
  accent: "bg-accent",
2539
2552
  success: "bg-success",
2540
2553
  warning: "bg-warning",
@@ -2553,7 +2566,18 @@ var sliderThumbVariants = cva(
2553
2566
  variant: {
2554
2567
  default: "border-foreground bg-background",
2555
2568
  primary: "border-primary bg-background",
2556
- secondary: "border-secondary bg-background",
2569
+ // #407 (P2, #389'un range eksenine EK ayrı eksen): tutamacın kenarlığı
2570
+ // `border-secondary`, dolgusu `bg-background` idi. Bu bileşen↔sayfa-zemini
2571
+ // ekseni: `--secondary` light'ta neredeyse beyaz (rgb(235,239,245)), dark'ta
2572
+ // `--background` ile neredeyse aynı → kenarlık 1.15 (light) / 1.01 (dark)
2573
+ // oranında kalıyor, secondary slider'ın tutamacı zeminde kayboluyordu.
2574
+ // #389'da bilerek kapsam dışıydı (mevcut kusuru testle "doğru" ilan etmemek için).
2575
+ // Non-text UI bileşeni → WCAG 1.4.11 ≥3:1. Skala taraması bu eksende de TEK
2576
+ // kademe bırakıyor — `--secondary-500`: light 4.76, dark 4.23 (gerçek kart
2577
+ // #111827 üstünde 3.73); 400 light'ta 2.56 ✗, 600 dark'ta 2.66 ✗. Range ile
2578
+ // aynı kademe: tutamaç ve dolgu tek renkte okunur.
2579
+ // Regresyon: __tests__/slider-secondary-contrast.test.ts
2580
+ secondary: "border-[rgb(var(--secondary-500))] bg-background",
2557
2581
  accent: "border-accent bg-background",
2558
2582
  success: "border-success bg-background",
2559
2583
  warning: "border-warning bg-background",
@@ -4061,23 +4085,49 @@ function DataTableBasic({
4061
4085
  ] }) }),
4062
4086
  /* @__PURE__ */ jsx("div", { className: "rounded-md border", children: /* @__PURE__ */ jsxs(Table, { children: [
4063
4087
  /* @__PURE__ */ jsx(TableHeader, { children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx(TableRow, { children: headerGroup.headers.map((header) => {
4064
- return /* @__PURE__ */ jsx(TableHead, { children: header.isPlaceholder ? null : /* @__PURE__ */ jsxs(
4065
- "div",
4088
+ const canSort = enableSorting && header.column.getCanSort();
4089
+ const sorted = header.column.getIsSorted();
4090
+ const headerDef = header.column.columnDef.header;
4091
+ return /* @__PURE__ */ jsx(
4092
+ TableHead,
4066
4093
  {
4067
- className: cn(
4068
- "flex items-center gap-2",
4069
- header.column.getCanSort() && "cursor-pointer select-none"
4070
- ),
4071
- onClick: header.column.getToggleSortingHandler(),
4072
- children: [
4073
- flexRender(
4074
- header.column.columnDef.header,
4075
- header.getContext()
4076
- ),
4077
- enableSorting && header.column.getCanSort() && /* @__PURE__ */ jsx("div", { className: "ml-auto", children: header.column.getIsSorted() === "desc" ? /* @__PURE__ */ jsx(ArrowDown, { className: "h-4 w-4" }) : header.column.getIsSorted() === "asc" ? /* @__PURE__ */ jsx(ArrowUp, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(ArrowUpDown, { className: "h-4 w-4 opacity-50" }) })
4078
- ]
4079
- }
4080
- ) }, header.id);
4094
+ "aria-sort": canSort ? sorted === "asc" ? "ascending" : sorted === "desc" ? "descending" : "none" : void 0,
4095
+ children: header.isPlaceholder ? null : /* @__PURE__ */ jsxs(
4096
+ "div",
4097
+ {
4098
+ className: cn(
4099
+ "flex items-center gap-2",
4100
+ canSort && "cursor-pointer select-none"
4101
+ ),
4102
+ onClick: canSort ? header.column.getToggleSortingHandler() : void 0,
4103
+ children: [
4104
+ flexRender(
4105
+ header.column.columnDef.header,
4106
+ header.getContext()
4107
+ ),
4108
+ canSort && /* #366: sıralama kontrolü yalnız fareyle çalışıyordu
4109
+ (dosyada `tabIndex`/`onKeyDown` grep'i 0 idi).
4110
+ Gerçek <button> ile Tab + Enter/Space native
4111
+ gelir; görünür odak focus-visible ring ile. */
4112
+ /* @__PURE__ */ jsx(
4113
+ "button",
4114
+ {
4115
+ type: "button",
4116
+ onClick: (event) => {
4117
+ event.stopPropagation();
4118
+ header.column.getToggleSortingHandler()?.(event);
4119
+ },
4120
+ "aria-label": `Sort by ${typeof headerDef === "string" ? headerDef : header.column.id}`,
4121
+ className: "ml-auto inline-flex items-center rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-ring",
4122
+ children: sorted === "desc" ? /* @__PURE__ */ jsx(ArrowDown, { className: "h-4 w-4" }) : sorted === "asc" ? /* @__PURE__ */ jsx(ArrowUp, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(ArrowUpDown, { className: "h-4 w-4 opacity-50" })
4123
+ }
4124
+ )
4125
+ ]
4126
+ }
4127
+ )
4128
+ },
4129
+ header.id
4130
+ );
4081
4131
  }) }, headerGroup.id)) }),
4082
4132
  /* @__PURE__ */ jsx(TableBody, { children: table.getRowModel().rows?.length ? table.getRowModel().rows.map((row) => /* @__PURE__ */ jsx(
4083
4133
  TableRow,
@@ -4845,7 +4895,20 @@ var progressIndicatorVariants = cva(
4845
4895
  variant: {
4846
4896
  default: "bg-foreground",
4847
4897
  primary: "bg-primary",
4848
- secondary: "bg-secondary",
4898
+ // #372: dolgu `bg-secondary` idi — track (`bg-secondary/20`) ile AYNI
4899
+ // yüzey token'ına basıyordu; %20 alfa track ile tam dolgu iki temada da
4900
+ // ~1.0 oranında kalıyor, bar görünmüyordu (light rgb(235,239,245) /
4901
+ // dark rgb(2,8,23)). `bg-secondary-foreground` BİLEREK seçilmedi:
4902
+ // light'ta --primary ile aynı hue/sat'ta yalnız ΔL=%2 (49 vs 51,
4903
+ // fill↔fill 1.07), dark'ta birebir --primary değeri (210 40% 98%) —
4904
+ // secondary varyantı primary'nin klonuna dönerdi. `--secondary-500`
4905
+ // (tokens.css slate skalası, RGB üçlüsü) iki temada da track'e ≥3:1
4906
+ // (WCAG 1.4.11) veren TEK kademe: light 4.63, dark 4.23 (gerçek kart
4907
+ // #111827 üstünde 3.83; 400 light'ta 2.50 ✗, 600 dark'ta 2.66 ✗).
4908
+ // Skala :root'ta tanımlı olduğundan tema değişiminde sabittir;
4909
+ // tüketici --secondary-500'ü ezerek özelleştirebilir.
4910
+ // Regresyon: __tests__/progress-secondary-contrast.test.ts
4911
+ secondary: "bg-[rgb(var(--secondary-500))]",
4849
4912
  success: "bg-success",
4850
4913
  warning: "bg-warning",
4851
4914
  error: "bg-error"
@@ -4939,6 +5002,26 @@ var formatFileSize = (bytes) => {
4939
5002
  const i = Math.floor(Math.log(bytes) / Math.log(k));
4940
5003
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
4941
5004
  };
5005
+ var matchesAcceptToken = (file, token) => {
5006
+ const normalized = token.trim().toLowerCase();
5007
+ if (!normalized)
5008
+ return false;
5009
+ if (normalized === "*" || normalized === "*/*")
5010
+ return true;
5011
+ if (normalized.startsWith(".")) {
5012
+ return file.name.toLowerCase().endsWith(normalized);
5013
+ }
5014
+ if (normalized.endsWith("/*")) {
5015
+ return file.type.toLowerCase().startsWith(normalized.slice(0, -1));
5016
+ }
5017
+ return file.type.toLowerCase() === normalized;
5018
+ };
5019
+ var isFileAccepted = (file, accept) => {
5020
+ const trimmed = accept.trim();
5021
+ if (trimmed === "" || trimmed === "*")
5022
+ return true;
5023
+ return trimmed.split(",").some((token) => matchesAcceptToken(file, token));
5024
+ };
4942
5025
  var FileItem = ({
4943
5026
  uploadedFile,
4944
5027
  onRemove
@@ -5017,13 +5100,7 @@ var FileUpload = React12__default.forwardRef(
5017
5100
  if (file.size > maxSize) {
5018
5101
  return `File size exceeds ${formatFileSize(maxSize)}`;
5019
5102
  }
5020
- if (accept !== "*" && !accept.split(",").some((type) => {
5021
- const cleanType = type.trim();
5022
- if (cleanType.includes("*")) {
5023
- return file.type.startsWith(cleanType.replace("*", ""));
5024
- }
5025
- return file.type === cleanType;
5026
- })) {
5103
+ if (!isFileAccepted(file, accept)) {
5027
5104
  return `File type not supported`;
5028
5105
  }
5029
5106
  return null;
@@ -5057,6 +5134,7 @@ var FileUpload = React12__default.forwardRef(
5057
5134
  preview: file.type.startsWith("image/") ? URL.createObjectURL(file) : void 0
5058
5135
  }));
5059
5136
  setUploadedFiles((prev) => [...prev, ...newUploadedFiles]);
5137
+ const progressIntervals = /* @__PURE__ */ new Map();
5060
5138
  for (const uploadedFile of newUploadedFiles) {
5061
5139
  let progress = 0;
5062
5140
  const interval = setInterval(() => {
@@ -5064,25 +5142,33 @@ var FileUpload = React12__default.forwardRef(
5064
5142
  if (progress >= 100) {
5065
5143
  progress = 100;
5066
5144
  clearInterval(interval);
5145
+ progressIntervals.delete(uploadedFile.id);
5067
5146
  setUploadedFiles(
5068
5147
  (prev) => prev.map(
5069
- (f) => f.id === uploadedFile.id ? { ...f, progress: 100, status: "success" } : f
5148
+ (f) => (
5149
+ // 'error'a düşmüş dosya simülasyonca geri 'success' yapılamaz —
5150
+ // error nihai durumdur (#378)
5151
+ f.id === uploadedFile.id && f.status === "uploading" ? { ...f, progress: 100, status: "success" } : f
5152
+ )
5070
5153
  )
5071
5154
  );
5072
5155
  } else {
5073
5156
  setUploadedFiles(
5074
5157
  (prev) => prev.map(
5075
- (f) => f.id === uploadedFile.id ? { ...f, progress } : f
5158
+ (f) => f.id === uploadedFile.id && f.status === "uploading" ? { ...f, progress } : f
5076
5159
  )
5077
5160
  );
5078
5161
  }
5079
5162
  }, 200);
5163
+ progressIntervals.set(uploadedFile.id, interval);
5080
5164
  }
5081
5165
  if (onUpload) {
5082
5166
  try {
5083
5167
  await onUpload(newUploadedFiles);
5084
5168
  } catch (error2) {
5085
5169
  console.error("Upload failed:", error2);
5170
+ progressIntervals.forEach((handle) => clearInterval(handle));
5171
+ progressIntervals.clear();
5086
5172
  setUploadedFiles(
5087
5173
  (prev) => prev.map(
5088
5174
  (f) => newUploadedFiles.some((nf) => nf.id === f.id) ? { ...f, status: "error" } : f