@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.js CHANGED
@@ -2572,7 +2572,20 @@ var sliderRangeVariants = classVarianceAuthority.cva(
2572
2572
  variant: {
2573
2573
  default: "bg-foreground",
2574
2574
  primary: "bg-primary",
2575
- secondary: "bg-secondary",
2575
+ // #389 (#372'nin birebir ardılı): range `bg-secondary` idi — track'in
2576
+ // (`bg-secondary/20`) AYNI yüzey token'ına basıyordu; %20 alfa track ile
2577
+ // tam dolgu iki temada da ~1.0 oranında kalıyor, dolu kısım görünmüyordu
2578
+ // (light rgb(235,239,245) → 1.12 · dark rgb(2,8,23) → 1.01).
2579
+ // `bg-secondary-foreground` BİLEREK seçilmedi: light'ta --primary ile aynı
2580
+ // hue/sat'ta yalnız ΔL=%2 (fill↔fill 1.07), dark'ta birebir --primary
2581
+ // değeri (210 40% 98%) — secondary varyantı primary klonuna dönerdi.
2582
+ // `--secondary-500` (tokens.css slate skalası, RGB üçlüsü) iki temada da
2583
+ // track'e ≥3:1 (WCAG 1.4.11) veren TEK kademe: light 4.63, dark 4.23
2584
+ // (gerçek kart #111827 üstünde 3.83; 400 light'ta 2.50 ✗, 600 dark'ta
2585
+ // 2.66 ✗). Skala :root'ta tanımlı olduğundan tema değişiminde sabittir;
2586
+ // tüketici --secondary-500'ü ezerek özelleştirebilir.
2587
+ // Regresyon: __tests__/slider-secondary-contrast.test.ts
2588
+ secondary: "bg-[rgb(var(--secondary-500))]",
2576
2589
  accent: "bg-accent",
2577
2590
  success: "bg-success",
2578
2591
  warning: "bg-warning",
@@ -2591,7 +2604,18 @@ var sliderThumbVariants = classVarianceAuthority.cva(
2591
2604
  variant: {
2592
2605
  default: "border-foreground bg-background",
2593
2606
  primary: "border-primary bg-background",
2594
- secondary: "border-secondary bg-background",
2607
+ // #407 (P2, #389'un range eksenine EK ayrı eksen): tutamacın kenarlığı
2608
+ // `border-secondary`, dolgusu `bg-background` idi. Bu bileşen↔sayfa-zemini
2609
+ // ekseni: `--secondary` light'ta neredeyse beyaz (rgb(235,239,245)), dark'ta
2610
+ // `--background` ile neredeyse aynı → kenarlık 1.15 (light) / 1.01 (dark)
2611
+ // oranında kalıyor, secondary slider'ın tutamacı zeminde kayboluyordu.
2612
+ // #389'da bilerek kapsam dışıydı (mevcut kusuru testle "doğru" ilan etmemek için).
2613
+ // Non-text UI bileşeni → WCAG 1.4.11 ≥3:1. Skala taraması bu eksende de TEK
2614
+ // kademe bırakıyor — `--secondary-500`: light 4.76, dark 4.23 (gerçek kart
2615
+ // #111827 üstünde 3.73); 400 light'ta 2.56 ✗, 600 dark'ta 2.66 ✗. Range ile
2616
+ // aynı kademe: tutamaç ve dolgu tek renkte okunur.
2617
+ // Regresyon: __tests__/slider-secondary-contrast.test.ts
2618
+ secondary: "border-[rgb(var(--secondary-500))] bg-background",
2595
2619
  accent: "border-accent bg-background",
2596
2620
  success: "border-success bg-background",
2597
2621
  warning: "border-warning bg-background",
@@ -4099,23 +4123,49 @@ function DataTableBasic({
4099
4123
  ] }) }),
4100
4124
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-md border", children: /* @__PURE__ */ jsxRuntime.jsxs(Table, { children: [
4101
4125
  /* @__PURE__ */ jsxRuntime.jsx(TableHeader, { children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsxRuntime.jsx(TableRow, { children: headerGroup.headers.map((header) => {
4102
- return /* @__PURE__ */ jsxRuntime.jsx(TableHead, { children: header.isPlaceholder ? null : /* @__PURE__ */ jsxRuntime.jsxs(
4103
- "div",
4126
+ const canSort = enableSorting && header.column.getCanSort();
4127
+ const sorted = header.column.getIsSorted();
4128
+ const headerDef = header.column.columnDef.header;
4129
+ return /* @__PURE__ */ jsxRuntime.jsx(
4130
+ TableHead,
4104
4131
  {
4105
- className: cn(
4106
- "flex items-center gap-2",
4107
- header.column.getCanSort() && "cursor-pointer select-none"
4108
- ),
4109
- onClick: header.column.getToggleSortingHandler(),
4110
- children: [
4111
- reactTable.flexRender(
4112
- header.column.columnDef.header,
4113
- header.getContext()
4114
- ),
4115
- enableSorting && header.column.getCanSort() && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-auto", children: header.column.getIsSorted() === "desc" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "h-4 w-4" }) : header.column.getIsSorted() === "asc" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "h-4 w-4" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUpDown, { className: "h-4 w-4 opacity-50" }) })
4116
- ]
4117
- }
4118
- ) }, header.id);
4132
+ "aria-sort": canSort ? sorted === "asc" ? "ascending" : sorted === "desc" ? "descending" : "none" : void 0,
4133
+ children: header.isPlaceholder ? null : /* @__PURE__ */ jsxRuntime.jsxs(
4134
+ "div",
4135
+ {
4136
+ className: cn(
4137
+ "flex items-center gap-2",
4138
+ canSort && "cursor-pointer select-none"
4139
+ ),
4140
+ onClick: canSort ? header.column.getToggleSortingHandler() : void 0,
4141
+ children: [
4142
+ reactTable.flexRender(
4143
+ header.column.columnDef.header,
4144
+ header.getContext()
4145
+ ),
4146
+ canSort && /* #366: sıralama kontrolü yalnız fareyle çalışıyordu
4147
+ (dosyada `tabIndex`/`onKeyDown` grep'i 0 idi).
4148
+ Gerçek <button> ile Tab + Enter/Space native
4149
+ gelir; görünür odak focus-visible ring ile. */
4150
+ /* @__PURE__ */ jsxRuntime.jsx(
4151
+ "button",
4152
+ {
4153
+ type: "button",
4154
+ onClick: (event) => {
4155
+ event.stopPropagation();
4156
+ header.column.getToggleSortingHandler()?.(event);
4157
+ },
4158
+ "aria-label": `Sort by ${typeof headerDef === "string" ? headerDef : header.column.id}`,
4159
+ className: "ml-auto inline-flex items-center rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-ring",
4160
+ children: sorted === "desc" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "h-4 w-4" }) : sorted === "asc" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "h-4 w-4" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUpDown, { className: "h-4 w-4 opacity-50" })
4161
+ }
4162
+ )
4163
+ ]
4164
+ }
4165
+ )
4166
+ },
4167
+ header.id
4168
+ );
4119
4169
  }) }, headerGroup.id)) }),
4120
4170
  /* @__PURE__ */ jsxRuntime.jsx(TableBody, { children: table.getRowModel().rows?.length ? table.getRowModel().rows.map((row) => /* @__PURE__ */ jsxRuntime.jsx(
4121
4171
  TableRow,
@@ -4883,7 +4933,20 @@ var progressIndicatorVariants = classVarianceAuthority.cva(
4883
4933
  variant: {
4884
4934
  default: "bg-foreground",
4885
4935
  primary: "bg-primary",
4886
- secondary: "bg-secondary",
4936
+ // #372: dolgu `bg-secondary` idi — track (`bg-secondary/20`) ile AYNI
4937
+ // yüzey token'ına basıyordu; %20 alfa track ile tam dolgu iki temada da
4938
+ // ~1.0 oranında kalıyor, bar görünmüyordu (light rgb(235,239,245) /
4939
+ // dark rgb(2,8,23)). `bg-secondary-foreground` BİLEREK seçilmedi:
4940
+ // light'ta --primary ile aynı hue/sat'ta yalnız ΔL=%2 (49 vs 51,
4941
+ // fill↔fill 1.07), dark'ta birebir --primary değeri (210 40% 98%) —
4942
+ // secondary varyantı primary'nin klonuna dönerdi. `--secondary-500`
4943
+ // (tokens.css slate skalası, RGB üçlüsü) iki temada da track'e ≥3:1
4944
+ // (WCAG 1.4.11) veren TEK kademe: light 4.63, dark 4.23 (gerçek kart
4945
+ // #111827 üstünde 3.83; 400 light'ta 2.50 ✗, 600 dark'ta 2.66 ✗).
4946
+ // Skala :root'ta tanımlı olduğundan tema değişiminde sabittir;
4947
+ // tüketici --secondary-500'ü ezerek özelleştirebilir.
4948
+ // Regresyon: __tests__/progress-secondary-contrast.test.ts
4949
+ secondary: "bg-[rgb(var(--secondary-500))]",
4887
4950
  success: "bg-success",
4888
4951
  warning: "bg-warning",
4889
4952
  error: "bg-error"
@@ -4977,6 +5040,26 @@ var formatFileSize = (bytes) => {
4977
5040
  const i = Math.floor(Math.log(bytes) / Math.log(k));
4978
5041
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
4979
5042
  };
5043
+ var matchesAcceptToken = (file, token) => {
5044
+ const normalized = token.trim().toLowerCase();
5045
+ if (!normalized)
5046
+ return false;
5047
+ if (normalized === "*" || normalized === "*/*")
5048
+ return true;
5049
+ if (normalized.startsWith(".")) {
5050
+ return file.name.toLowerCase().endsWith(normalized);
5051
+ }
5052
+ if (normalized.endsWith("/*")) {
5053
+ return file.type.toLowerCase().startsWith(normalized.slice(0, -1));
5054
+ }
5055
+ return file.type.toLowerCase() === normalized;
5056
+ };
5057
+ var isFileAccepted = (file, accept) => {
5058
+ const trimmed = accept.trim();
5059
+ if (trimmed === "" || trimmed === "*")
5060
+ return true;
5061
+ return trimmed.split(",").some((token) => matchesAcceptToken(file, token));
5062
+ };
4980
5063
  var FileItem = ({
4981
5064
  uploadedFile,
4982
5065
  onRemove
@@ -5055,13 +5138,7 @@ var FileUpload = React12__namespace.default.forwardRef(
5055
5138
  if (file.size > maxSize) {
5056
5139
  return `File size exceeds ${formatFileSize(maxSize)}`;
5057
5140
  }
5058
- if (accept !== "*" && !accept.split(",").some((type) => {
5059
- const cleanType = type.trim();
5060
- if (cleanType.includes("*")) {
5061
- return file.type.startsWith(cleanType.replace("*", ""));
5062
- }
5063
- return file.type === cleanType;
5064
- })) {
5141
+ if (!isFileAccepted(file, accept)) {
5065
5142
  return `File type not supported`;
5066
5143
  }
5067
5144
  return null;
@@ -5095,6 +5172,7 @@ var FileUpload = React12__namespace.default.forwardRef(
5095
5172
  preview: file.type.startsWith("image/") ? URL.createObjectURL(file) : void 0
5096
5173
  }));
5097
5174
  setUploadedFiles((prev) => [...prev, ...newUploadedFiles]);
5175
+ const progressIntervals = /* @__PURE__ */ new Map();
5098
5176
  for (const uploadedFile of newUploadedFiles) {
5099
5177
  let progress = 0;
5100
5178
  const interval = setInterval(() => {
@@ -5102,25 +5180,33 @@ var FileUpload = React12__namespace.default.forwardRef(
5102
5180
  if (progress >= 100) {
5103
5181
  progress = 100;
5104
5182
  clearInterval(interval);
5183
+ progressIntervals.delete(uploadedFile.id);
5105
5184
  setUploadedFiles(
5106
5185
  (prev) => prev.map(
5107
- (f) => f.id === uploadedFile.id ? { ...f, progress: 100, status: "success" } : f
5186
+ (f) => (
5187
+ // 'error'a düşmüş dosya simülasyonca geri 'success' yapılamaz —
5188
+ // error nihai durumdur (#378)
5189
+ f.id === uploadedFile.id && f.status === "uploading" ? { ...f, progress: 100, status: "success" } : f
5190
+ )
5108
5191
  )
5109
5192
  );
5110
5193
  } else {
5111
5194
  setUploadedFiles(
5112
5195
  (prev) => prev.map(
5113
- (f) => f.id === uploadedFile.id ? { ...f, progress } : f
5196
+ (f) => f.id === uploadedFile.id && f.status === "uploading" ? { ...f, progress } : f
5114
5197
  )
5115
5198
  );
5116
5199
  }
5117
5200
  }, 200);
5201
+ progressIntervals.set(uploadedFile.id, interval);
5118
5202
  }
5119
5203
  if (onUpload) {
5120
5204
  try {
5121
5205
  await onUpload(newUploadedFiles);
5122
5206
  } catch (error2) {
5123
5207
  console.error("Upload failed:", error2);
5208
+ progressIntervals.forEach((handle) => clearInterval(handle));
5209
+ progressIntervals.clear();
5124
5210
  setUploadedFiles(
5125
5211
  (prev) => prev.map(
5126
5212
  (f) => newUploadedFiles.some((nf) => nf.id === f.id) ? { ...f, status: "error" } : f