@stackframe/dashboard-ui-components 2.8.86 → 2.8.89

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/components/analytics-chart/analytics-chart-pie.js +3 -3
  2. package/dist/components/analytics-chart/analytics-chart-pie.js.map +1 -1
  3. package/dist/components/data-grid/data-grid-sizing.d.ts +2 -1
  4. package/dist/components/data-grid/data-grid-sizing.d.ts.map +1 -1
  5. package/dist/components/data-grid/data-grid-sizing.js +33 -4
  6. package/dist/components/data-grid/data-grid-sizing.js.map +1 -1
  7. package/dist/components/data-grid/data-grid-toolbar.js +18 -15
  8. package/dist/components/data-grid/data-grid-toolbar.js.map +1 -1
  9. package/dist/components/data-grid/data-grid.d.ts +35 -1
  10. package/dist/components/data-grid/data-grid.d.ts.map +1 -1
  11. package/dist/components/data-grid/data-grid.js +329 -127
  12. package/dist/components/data-grid/data-grid.js.map +1 -1
  13. package/dist/components/data-grid/data-grid.test.d.ts +1 -0
  14. package/dist/components/data-grid/data-grid.test.js +215 -0
  15. package/dist/components/data-grid/data-grid.test.js.map +1 -0
  16. package/dist/components/data-grid/index.d.ts +3 -2
  17. package/dist/components/data-grid/index.js +13 -0
  18. package/dist/components/data-grid/state.d.ts.map +1 -1
  19. package/dist/components/data-grid/state.js +24 -7
  20. package/dist/components/data-grid/state.js.map +1 -1
  21. package/dist/components/data-grid/types.d.ts +34 -3
  22. package/dist/components/data-grid/types.d.ts.map +1 -1
  23. package/dist/components/data-grid/use-data-source.d.ts +6 -0
  24. package/dist/components/data-grid/use-data-source.d.ts.map +1 -1
  25. package/dist/components/data-grid/use-data-source.js +10 -2
  26. package/dist/components/data-grid/use-data-source.js.map +1 -1
  27. package/dist/components/tabs.d.ts +5 -1
  28. package/dist/components/tabs.d.ts.map +1 -1
  29. package/dist/components/tabs.js +40 -27
  30. package/dist/components/tabs.js.map +1 -1
  31. package/dist/dashboard-ui-components.global.js +672 -368
  32. package/dist/dashboard-ui-components.global.js.map +4 -4
  33. package/dist/esm/components/analytics-chart/analytics-chart-pie.js +3 -3
  34. package/dist/esm/components/analytics-chart/analytics-chart-pie.js.map +1 -1
  35. package/dist/esm/components/data-grid/data-grid-sizing.d.ts +2 -1
  36. package/dist/esm/components/data-grid/data-grid-sizing.d.ts.map +1 -1
  37. package/dist/esm/components/data-grid/data-grid-sizing.js +33 -5
  38. package/dist/esm/components/data-grid/data-grid-sizing.js.map +1 -1
  39. package/dist/esm/components/data-grid/data-grid-toolbar.js +18 -15
  40. package/dist/esm/components/data-grid/data-grid-toolbar.js.map +1 -1
  41. package/dist/esm/components/data-grid/data-grid.d.ts +35 -1
  42. package/dist/esm/components/data-grid/data-grid.d.ts.map +1 -1
  43. package/dist/esm/components/data-grid/data-grid.js +329 -128
  44. package/dist/esm/components/data-grid/data-grid.js.map +1 -1
  45. package/dist/esm/components/data-grid/data-grid.test.d.ts +1 -0
  46. package/dist/esm/components/data-grid/data-grid.test.js +215 -0
  47. package/dist/esm/components/data-grid/data-grid.test.js.map +1 -0
  48. package/dist/esm/components/data-grid/index.d.ts +3 -2
  49. package/dist/esm/components/data-grid/index.js +3 -2
  50. package/dist/esm/components/data-grid/state.d.ts.map +1 -1
  51. package/dist/esm/components/data-grid/state.js +24 -7
  52. package/dist/esm/components/data-grid/state.js.map +1 -1
  53. package/dist/esm/components/data-grid/types.d.ts +34 -3
  54. package/dist/esm/components/data-grid/types.d.ts.map +1 -1
  55. package/dist/esm/components/data-grid/use-data-source.d.ts +6 -0
  56. package/dist/esm/components/data-grid/use-data-source.d.ts.map +1 -1
  57. package/dist/esm/components/data-grid/use-data-source.js +10 -2
  58. package/dist/esm/components/data-grid/use-data-source.js.map +1 -1
  59. package/dist/esm/components/tabs.d.ts +5 -1
  60. package/dist/esm/components/tabs.d.ts.map +1 -1
  61. package/dist/esm/components/tabs.js +40 -27
  62. package/dist/esm/components/tabs.js.map +1 -1
  63. package/dist/index.d.ts +3 -2
  64. package/package.json +4 -4
@@ -337,12 +337,14 @@ var DashboardUI = (() => {
337
337
  formatGridDate: () => formatGridDate,
338
338
  formatValue: () => formatValue,
339
339
  getDesignChartColor: () => getDesignChartColor,
340
+ getEffectiveMinWidth: () => getEffectiveMinWidth,
340
341
  getPayloadConfigFromPayload: () => getPayloadConfigFromPayload,
341
342
  getSortDirection: () => getSortDirection,
342
343
  getSortIndex: () => getSortIndex,
343
344
  getTotalPages: () => getTotalPages,
344
345
  isAnalyticsChartDataLayer: () => isAnalyticsChartDataLayer,
345
346
  isColumnVisible: () => isColumnVisible,
347
+ isDataGridInteractiveRowClickTarget: () => isDataGridInteractiveRowClickTarget,
346
348
  isTimeseriesState: () => isTimeseriesState,
347
349
  paginateRows: () => paginateRows,
348
350
  patchLayerById: () => patchLayerById,
@@ -1627,7 +1629,7 @@ This is likely an error in Stack. Please make sure you are running the newest ve
1627
1629
  },
1628
1630
  enumerable: false
1629
1631
  });
1630
- if (process.env.NEXT_PUBLIC_STACK_DEBUGGER_ON_ASSERTION_ERROR === "true") debugger;
1632
+ if ((typeof process !== "undefined" ? process.env.NEXT_PUBLIC_STACK_DEBUGGER_ON_ASSERTION_ERROR : void 0) === "true") debugger;
1631
1633
  }
1632
1634
  };
1633
1635
  StackAssertionError.prototype.name = "StackAssertionError";
@@ -4308,6 +4310,12 @@ This is likely an error in Stack. Please make sure you are running the newest ve
4308
4310
  // ../stack-shared/dist/esm/utils/react.js
4309
4311
  var import_react3 = __toESM(require_react());
4310
4312
 
4313
+ // ../stack-shared/dist/esm/utils/env.js
4314
+ function getProcessEnv(name) {
4315
+ if (typeof process === "undefined" || typeof process.env === "undefined") return;
4316
+ return process.env[name];
4317
+ }
4318
+
4311
4319
  // ../stack-shared/dist/esm/utils/results.js
4312
4320
  var Result = {
4313
4321
  fromThrowing,
@@ -4430,6 +4438,49 @@ This is likely an error in Stack. Please make sure you are running the newest ve
4430
4438
  return Object.assign(Result.error(new RetryError(errors)), { attempts: totalAttempts });
4431
4439
  }
4432
4440
 
4441
+ // ../stack-shared/dist/esm/utils/bytes.js
4442
+ function decodeBase64(input) {
4443
+ return new Uint8Array(atob(input).split("").map((char) => char.charCodeAt(0)));
4444
+ }
4445
+ function isBase64(input) {
4446
+ return /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(input);
4447
+ }
4448
+
4449
+ // ../stack-shared/dist/esm/utils/urls.js
4450
+ function createUrlIfValid(...args) {
4451
+ try {
4452
+ return new URL(...args);
4453
+ } catch (e40) {
4454
+ return null;
4455
+ }
4456
+ }
4457
+ function isValidUrl(url) {
4458
+ return !!createUrlIfValid(url);
4459
+ }
4460
+ function isValidHostname(hostname) {
4461
+ if (!hostname || hostname.startsWith(".") || hostname.endsWith(".") || hostname.includes("..")) return false;
4462
+ const url = createUrlIfValid(`https://${hostname}`);
4463
+ if (!url) return false;
4464
+ return url.hostname === hostname;
4465
+ }
4466
+ function isValidHostnameWithWildcards(hostname) {
4467
+ if (!hostname) return false;
4468
+ if (!hostname.includes("*")) return isValidHostname(hostname);
4469
+ if (hostname.startsWith(".") || hostname.endsWith(".")) return false;
4470
+ if (hostname.includes("..")) return false;
4471
+ const testHostname = hostname.replace(/\*+/g, "wildcard");
4472
+ if (!/^[a-zA-Z0-9.-]+$/.test(testHostname)) return false;
4473
+ const segments = hostname.split(/\*+/);
4474
+ for (let i = 0; i < segments.length; i++) {
4475
+ const segment = segments[i];
4476
+ if (segment === "") continue;
4477
+ if (i === 0 && segment.startsWith(".")) return false;
4478
+ if (i === segments.length - 1 && segment.endsWith(".")) return false;
4479
+ if (segment.includes("..")) return false;
4480
+ }
4481
+ return true;
4482
+ }
4483
+
4433
4484
  // ../stack-shared/dist/esm/known-errors.js
4434
4485
  var KnownError = class extends StatusError {
4435
4486
  constructor(statusCode, humanReadableMessage, details) {
@@ -4775,6 +4826,7 @@ This is likely an error in Stack. Please make sure you are running the newest ve
4775
4826
  `Restricted users cannot accept team invitations. Reason: ${restrictedReason.type}. Please complete the onboarding process before accepting team invitations.`,
4776
4827
  { restricted_reason: restrictedReason }
4777
4828
  ], (json) => [json.restricted_reason ?? { type: "anonymous" }]);
4829
+ var TeamInvitationEmailMismatch = createKnownErrorConstructor(KnownError, "TEAM_INVITATION_EMAIL_MISMATCH", () => [403, "This team invitation was sent to a different email address. Sign in with the invited email, or add and verify that email on your account, then try again."], () => []);
4778
4830
  var EmailTemplateAlreadyExists = createKnownErrorConstructor(KnownError, "EMAIL_TEMPLATE_ALREADY_EXISTS", () => [409, "Email template already exists."], () => []);
4779
4831
  var OAuthConnectionNotConnectedToUser = createKnownErrorConstructor(KnownError, "OAUTH_CONNECTION_NOT_CONNECTED_TO_USER", () => [400, "The OAuth connection is not connected to any user."], () => []);
4780
4832
  var OAuthConnectionAlreadyConnectedToAnotherUser = createKnownErrorConstructor(KnownError, "OAUTH_CONNECTION_ALREADY_CONNECTED_TO_ANOTHER_USER", () => [409, "The OAuth connection is already connected to another user."], () => []);
@@ -4855,7 +4907,7 @@ This is likely an error in Stack. Please make sure you are running the newest ve
4855
4907
  var InvalidAuthorizationCode = createKnownErrorConstructor(KnownError, "INVALID_AUTHORIZATION_CODE", () => [400, "The given authorization code is invalid."], () => []);
4856
4908
  var InvalidAppleCredentials = createKnownErrorConstructor(KnownError, "INVALID_APPLE_CREDENTIALS", () => [400, "The Apple Sign In credentials could not be verified. Please try signing in again."], () => []);
4857
4909
  var OAuthProviderAccessDenied = createKnownErrorConstructor(KnownError, "OAUTH_PROVIDER_ACCESS_DENIED", () => [400, "The OAuth provider denied access to the user."], () => []);
4858
- var OAuthProviderTemporarilyUnavailable = createKnownErrorConstructor(KnownError, "OAUTH_PROVIDER_TEMPORARILY_UNAVAILABLE", () => [503, "The OAuth provider is temporarily unavailable. Please try signing in again."], () => []);
4910
+ var OAuthProviderTemporarilyUnavailable = createKnownErrorConstructor(KnownError, "OAUTH_PROVIDER_TEMPORARILY_UNAVAILABLE", () => [503, "The OAuth provider is temporarily unavailable. Please try again later."], () => []);
4859
4911
  var ContactChannelAlreadyUsedForAuthBySomeoneElse = createKnownErrorConstructor(KnownError, "CONTACT_CHANNEL_ALREADY_USED_FOR_AUTH_BY_SOMEONE_ELSE", (type, contactChannelValue, wouldWorkIfEmailWasVerified = false) => [
4860
4912
  409,
4861
4913
  `This ${type} ${contactChannelValue ? `"(${contactChannelValue})"` : ""} is already used for authentication by another account${wouldWorkIfEmailWasVerified ? " but the email is not verified. Please login to your existing account with the method you used to sign up, and then verify your email to sign in with this login method." : "."}`,
@@ -5116,6 +5168,7 @@ This is likely an error in Stack. Please make sure you are running the newest ve
5116
5168
  TeamNotFound,
5117
5169
  TeamMembershipNotFound,
5118
5170
  TeamInvitationRestrictedUserNotAllowed,
5171
+ TeamInvitationEmailMismatch,
5119
5172
  EmailTemplateAlreadyExists,
5120
5173
  OAuthConnectionNotConnectedToUser,
5121
5174
  OAuthConnectionAlreadyConnectedToAnotherUser,
@@ -5181,49 +5234,6 @@ This is likely an error in Stack. Please make sure you are running the newest ve
5181
5234
  knownErrorCodes.add(KnownError2.errorCode);
5182
5235
  }
5183
5236
 
5184
- // ../stack-shared/dist/esm/utils/bytes.js
5185
- function decodeBase64(input) {
5186
- return new Uint8Array(atob(input).split("").map((char) => char.charCodeAt(0)));
5187
- }
5188
- function isBase64(input) {
5189
- return /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(input);
5190
- }
5191
-
5192
- // ../stack-shared/dist/esm/utils/urls.js
5193
- function createUrlIfValid(...args) {
5194
- try {
5195
- return new URL(...args);
5196
- } catch (e40) {
5197
- return null;
5198
- }
5199
- }
5200
- function isValidUrl(url) {
5201
- return !!createUrlIfValid(url);
5202
- }
5203
- function isValidHostname(hostname) {
5204
- if (!hostname || hostname.startsWith(".") || hostname.endsWith(".") || hostname.includes("..")) return false;
5205
- const url = createUrlIfValid(`https://${hostname}`);
5206
- if (!url) return false;
5207
- return url.hostname === hostname;
5208
- }
5209
- function isValidHostnameWithWildcards(hostname) {
5210
- if (!hostname) return false;
5211
- if (!hostname.includes("*")) return isValidHostname(hostname);
5212
- if (hostname.startsWith(".") || hostname.endsWith(".")) return false;
5213
- if (hostname.includes("..")) return false;
5214
- const testHostname = hostname.replace(/\*+/g, "wildcard");
5215
- if (!/^[a-zA-Z0-9.-]+$/.test(testHostname)) return false;
5216
- const segments = hostname.split(/\*+/);
5217
- for (let i = 0; i < segments.length; i++) {
5218
- const segment = segments[i];
5219
- if (segment === "") continue;
5220
- if (i === 0 && segment.startsWith(".")) return false;
5221
- if (i === segments.length - 1 && segment.endsWith(".")) return false;
5222
- if (segment.includes("..")) return false;
5223
- }
5224
- return true;
5225
- }
5226
-
5227
5237
  // ../../node_modules/.pnpm/yup@1.7.1/node_modules/yup/index.esm.js
5228
5238
  var import_property_expr = __toESM(require_property_expr());
5229
5239
  var import_tiny_case = __toESM(require_tiny_case());
@@ -8475,8 +8485,10 @@ attempted value: ${formattedValue}
8475
8485
  });
8476
8486
  var neonAuthorizationHeaderSchema = basicAuthorizationHeaderSchema.test("is-authorization-header", "Invalid client_id:client_secret values; did you use the correct values for the integration?", (value) => {
8477
8487
  if (!value) return true;
8478
- const [clientId, clientSecret] = decodeBasicAuthorizationHeader(value) ?? throwErr(`Authz header invalid? This should've been validated by basicAuthorizationHeaderSchema: ${value}`);
8479
- for (const neonClientConfig of JSON.parse(process.env.STACK_INTEGRATION_CLIENTS_CONFIG || "[]")) if (clientId === neonClientConfig.client_id && clientSecret === neonClientConfig.client_secret) return true;
8488
+ const decoded = decodeBasicAuthorizationHeader(value);
8489
+ if (decoded === null) return true;
8490
+ const [clientId, clientSecret] = decoded;
8491
+ for (const neonClientConfig of JSON.parse(getProcessEnv("STACK_INTEGRATION_CLIENTS_CONFIG") || "[]")) if (clientId === neonClientConfig.client_id && clientSecret === neonClientConfig.client_secret) return true;
8480
8492
  return false;
8481
8493
  });
8482
8494
  var branchConfigSourceSchema = yupUnion(yupObject({
@@ -9723,8 +9735,9 @@ attempted value: ${formattedValue}
9723
9735
  return runAsynchronously(args[0], {
9724
9736
  ...args[1],
9725
9737
  onError: (error) => {
9726
- if (KnownError.isKnownError(error) && typeof process !== "undefined" && "production"?.includes("production")) alert(error.message);
9727
- else alert(`An unhandled error occurred. Please ${false ? `check the browser console for the full error.` : "report this to the developer."}
9738
+ const nodeEnv = getProcessEnv("NODE_ENV");
9739
+ if (KnownError.isKnownError(error) && nodeEnv?.includes("production")) alert(error.message);
9740
+ else alert(`An unhandled error occurred. Please ${nodeEnv === "development" ? `check the browser console for the full error.` : "report this to the developer."}
9728
9741
 
9729
9742
  ${error}`);
9730
9743
  args[1]?.onError?.(error);
@@ -16205,6 +16218,7 @@ ${error}`);
16205
16218
  size: size5 = "sm",
16206
16219
  glassmorphic: glassmorphicProp,
16207
16220
  gradient = "blue",
16221
+ trailing,
16208
16222
  className,
16209
16223
  ...props
16210
16224
  }) {
@@ -16221,65 +16235,77 @@ ${error}`);
16221
16235
  );
16222
16236
  }
16223
16237
  };
16224
- return /* @__PURE__ */ jsx(
16238
+ return /* @__PURE__ */ jsxs(
16225
16239
  "div",
16226
16240
  {
16227
16241
  className: cn(
16228
- "flex items-center gap-1 overflow-x-auto flex-nowrap [&::-webkit-scrollbar]:hidden",
16242
+ "flex w-full min-w-0 items-center gap-2",
16229
16243
  glassmorphic ? "rounded-xl bg-black/[0.08] dark:bg-white/[0.04] p-1 backdrop-blur-sm" : "border-b border-gray-300 dark:border-gray-800",
16230
16244
  className
16231
16245
  ),
16232
16246
  ...props,
16233
- children: categories.map((category) => {
16234
- const isActive = selectedCategory === category.id;
16235
- const badgeValue = category.badgeCount ?? category.count;
16236
- const shouldShowBadge = showBadge && badgeValue !== void 0;
16237
- return /* @__PURE__ */ jsxs(
16238
- "button",
16247
+ children: [
16248
+ /* @__PURE__ */ jsx(
16249
+ "div",
16239
16250
  {
16240
- onClick: () => handleSelect(category.id),
16241
- disabled: loadingCategoryId !== null,
16242
16251
  className: cn(
16243
- "font-medium transition-all duration-150 hover:transition-none relative flex flex-shrink-0 items-center justify-center gap-2 whitespace-nowrap",
16244
- "hover:text-gray-900 dark:hover:text-gray-100",
16245
- sizeClass.button,
16246
- glassmorphic ? "rounded-lg" : "",
16247
- isActive ? cn(
16248
- gradientClass.activeText,
16249
- glassmorphic && "bg-background shadow-sm ring-1 ring-black/[0.12] dark:ring-white/[0.06]"
16250
- ) : "text-gray-700 dark:text-gray-400"
16252
+ "flex min-h-0 min-w-0 flex-1 items-center gap-1 overflow-x-auto flex-nowrap [&::-webkit-scrollbar]:hidden"
16251
16253
  ),
16252
- children: [
16253
- loadingCategoryId === category.id && /* @__PURE__ */ jsx(
16254
- Spinner,
16254
+ children: categories.map((category) => {
16255
+ const isActive = selectedCategory === category.id;
16256
+ const badgeValue = category.badgeCount ?? category.count;
16257
+ const shouldShowBadge = showBadge && badgeValue !== void 0;
16258
+ return /* @__PURE__ */ jsxs(
16259
+ "button",
16255
16260
  {
16256
- size: 12,
16257
- className: "absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 pointer-events-none"
16258
- }
16259
- ),
16260
- /* @__PURE__ */ jsxs("span", { className: cn(
16261
- "flex items-center gap-2",
16262
- loadingCategoryId === category.id && "invisible"
16263
- ), children: [
16264
- category.label,
16265
- shouldShowBadge && /* @__PURE__ */ jsx(
16266
- "span",
16267
- {
16268
- className: cn(
16269
- "rounded-full",
16270
- sizeClass.badge,
16271
- isActive ? gradientClass.activeBadge : "bg-gray-200 dark:bg-gray-800 text-gray-600 dark:text-gray-400"
16261
+ onClick: () => handleSelect(category.id),
16262
+ disabled: loadingCategoryId !== null,
16263
+ className: cn(
16264
+ "font-medium transition-all duration-150 hover:transition-none relative flex flex-shrink-0 items-center justify-center gap-2 whitespace-nowrap",
16265
+ "hover:text-gray-900 dark:hover:text-gray-100",
16266
+ sizeClass.button,
16267
+ glassmorphic ? "rounded-lg" : "",
16268
+ isActive ? cn(
16269
+ gradientClass.activeText,
16270
+ glassmorphic && "bg-background shadow-sm ring-1 ring-black/[0.12] dark:ring-white/[0.06]"
16271
+ ) : "text-gray-700 dark:text-gray-400"
16272
+ ),
16273
+ children: [
16274
+ loadingCategoryId === category.id && /* @__PURE__ */ jsx(
16275
+ Spinner,
16276
+ {
16277
+ size: 12,
16278
+ className: "absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 pointer-events-none"
16279
+ }
16272
16280
  ),
16273
- children: badgeValue
16274
- }
16275
- )
16276
- ] }),
16277
- !glassmorphic && isActive && /* @__PURE__ */ jsx("div", { className: cn("absolute bottom-0 left-0 right-0 h-0.5", gradientClass.underline) })
16278
- ]
16279
- },
16280
- category.id
16281
- );
16282
- })
16281
+ /* @__PURE__ */ jsxs("span", { className: cn(
16282
+ "flex items-center gap-2",
16283
+ loadingCategoryId === category.id && "invisible"
16284
+ ), children: [
16285
+ category.icon && /* @__PURE__ */ jsx(category.icon, { className: "h-4 w-4 shrink-0", "aria-hidden": true }),
16286
+ category.label,
16287
+ shouldShowBadge && /* @__PURE__ */ jsx(
16288
+ "span",
16289
+ {
16290
+ className: cn(
16291
+ "rounded-full",
16292
+ sizeClass.badge,
16293
+ isActive ? gradientClass.activeBadge : "bg-gray-200 dark:bg-gray-800 text-gray-600 dark:text-gray-400"
16294
+ ),
16295
+ children: badgeValue
16296
+ }
16297
+ )
16298
+ ] }),
16299
+ !glassmorphic && isActive && /* @__PURE__ */ jsx("div", { className: cn("absolute bottom-0 left-0 right-0 h-0.5", gradientClass.underline) })
16300
+ ]
16301
+ },
16302
+ category.id
16303
+ );
16304
+ })
16305
+ }
16306
+ ),
16307
+ trailing != null ? /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center", children: trailing }) : null
16308
+ ]
16283
16309
  }
16284
16310
  );
16285
16311
  }
@@ -17150,7 +17176,7 @@ ${colorConfig.map(([key, itemConfig]) => {
17150
17176
  "div",
17151
17177
  {
17152
17178
  ref: wrapperRef,
17153
- className: "relative w-full select-none",
17179
+ className: "relative flex h-full min-h-0 w-full min-w-0 flex-1 flex-col select-none",
17154
17180
  onClick: (e40) => e40.stopPropagation(),
17155
17181
  children: [
17156
17182
  zoomRange && /* @__PURE__ */ jsx("div", { className: "absolute right-2 top-2 z-20", children: /* @__PURE__ */ jsxs(
@@ -17165,7 +17191,7 @@ ${colorConfig.map(([key, itemConfig]) => {
17165
17191
  ]
17166
17192
  }
17167
17193
  ) }),
17168
- /* @__PURE__ */ jsxs("div", { className: "flex min-h-[260px] flex-col items-center gap-6 sm:min-h-[320px] sm:flex-row sm:items-center sm:justify-center sm:gap-10", children: [
17194
+ /* @__PURE__ */ jsxs("div", { className: "flex h-full min-h-0 w-full flex-1 flex-wrap content-center items-center justify-center gap-x-10 gap-y-6", children: [
17169
17195
  /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 flex-col items-center gap-3", children: [
17170
17196
  /* @__PURE__ */ jsxs("div", { className: "relative", children: [
17171
17197
  /* @__PURE__ */ jsx(DesignChartContainer, { config: chartConfig, className: containerClassName, children: /* @__PURE__ */ jsxs(
@@ -17254,7 +17280,7 @@ ${colorConfig.map(([key, itemConfig]) => {
17254
17280
  showCompare && /* @__PURE__ */ jsx(TrendPill, { delta: activeDelta, size: "sm", label: strings.pieVsPrev })
17255
17281
  ] })
17256
17282
  ] }),
17257
- /* @__PURE__ */ jsx("ul", { className: "flex w-full max-w-[380px] flex-col gap-1", children: legendRows.map((r7) => {
17283
+ /* @__PURE__ */ jsx("ul", { className: "flex min-w-[200px] max-w-[300px] flex-col gap-1", children: legendRows.map((r7) => {
17258
17284
  const isActive = hoverKey === r7.key;
17259
17285
  const dimmed = hoverKey != null && !isActive;
17260
17286
  const rowDelta = formatDelta(r7.value, r7.prevValue);
@@ -19461,6 +19487,81 @@ ${colorConfig.map(([key, itemConfig]) => {
19461
19487
  // src/components/data-grid/data-grid.tsx
19462
19488
  var import_react30 = __toESM(require_react());
19463
19489
 
19490
+ // src/components/data-grid/data-grid-sizing.ts
19491
+ function colVar(id) {
19492
+ if (false) {
19493
+ console.warn(
19494
+ `[DataGrid] column id ${JSON.stringify(id)} contains characters that are not safe in a CSS custom property name. Prefer ascii identifiers.`
19495
+ );
19496
+ }
19497
+ return `--col-${id}`;
19498
+ }
19499
+ var MIN_COL_WIDTH = 20;
19500
+ var MIN_CUSTOM_HEADER_WIDTH = 50;
19501
+ var DEFAULT_MAX_COL_WIDTH = 800;
19502
+ var HEADER_CHROME_PX = 12 + 12 + 6 + 12 + 2;
19503
+ var measureContext = null;
19504
+ var headerWidthCache = /* @__PURE__ */ new Map();
19505
+ function measureHeaderLabelWidth(label) {
19506
+ const cached = headerWidthCache.get(label);
19507
+ if (cached != null) {
19508
+ return cached;
19509
+ }
19510
+ if (typeof document === "undefined") {
19511
+ return 0;
19512
+ }
19513
+ if (measureContext == null) {
19514
+ measureContext = document.createElement("canvas").getContext("2d");
19515
+ }
19516
+ if (measureContext == null) {
19517
+ return 0;
19518
+ }
19519
+ measureContext.font = "600 12px system-ui, -apple-system, sans-serif";
19520
+ const text = label.toUpperCase();
19521
+ const letterSpacingPx = 0.05 * 12;
19522
+ const width = Math.ceil(
19523
+ measureContext.measureText(text).width + letterSpacingPx * text.length
19524
+ );
19525
+ headerWidthCache.set(label, width);
19526
+ return width;
19527
+ }
19528
+ function getEffectiveMinWidth(col) {
19529
+ if (col.minWidth != null) {
19530
+ return col.minWidth;
19531
+ }
19532
+ const label = typeof col.header === "string" ? col.header : null;
19533
+ if (label == null) {
19534
+ return typeof col.header === "function" ? MIN_CUSTOM_HEADER_WIDTH : MIN_COL_WIDTH;
19535
+ }
19536
+ return Math.max(MIN_COL_WIDTH, measureHeaderLabelWidth(label) + HEADER_CHROME_PX);
19537
+ }
19538
+ function getColumnSizingStyle(col) {
19539
+ const w = `var(${colVar(col.id)})`;
19540
+ const grow = col.flex ?? 0;
19541
+ return {
19542
+ flex: `${grow} 0 ${w}`,
19543
+ width: w,
19544
+ minWidth: getEffectiveMinWidth(col),
19545
+ maxWidth: grow > 0 ? void 0 : col.maxWidth ?? DEFAULT_MAX_COL_WIDTH
19546
+ };
19547
+ }
19548
+ function createGridSizingStyle(widths, totalWidth) {
19549
+ const style = { "--grid-total-w": `${totalWidth}px` };
19550
+ for (const [id, w] of widths) {
19551
+ style[colVar(id)] = `${w}px`;
19552
+ }
19553
+ return style;
19554
+ }
19555
+ function applyDraggedColumnWidth(el, columnId, width, totalWidth) {
19556
+ el.style.setProperty(colVar(columnId), `${width}px`);
19557
+ el.style.setProperty("--grid-total-w", `${totalWidth}px`);
19558
+ }
19559
+ function clampColumnWidth(col, width) {
19560
+ const minWidth = getEffectiveMinWidth(col);
19561
+ const maxWidth = col.maxWidth ?? DEFAULT_MAX_COL_WIDTH;
19562
+ return Math.max(minWidth, Math.min(maxWidth, width));
19563
+ }
19564
+
19464
19565
  // src/components/data-grid/data-grid-toolbar.tsx
19465
19566
  var import_react28 = __toESM(require_react());
19466
19567
  function usePopover() {
@@ -19683,68 +19784,48 @@ ${colorConfig.map(([key, itemConfig]) => {
19683
19784
  () => columns.some((c4) => c4.type === "date" || c4.type === "dateTime"),
19684
19785
  [columns]
19685
19786
  );
19686
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-2.5 py-2.5 border-b border-foreground/[0.06]", children: [
19687
- !hideQuickSearch && /* @__PURE__ */ jsx(
19688
- QuickSearch,
19689
- {
19690
- value: state.quickSearch,
19691
- onChange: updateQuickSearch,
19692
- placeholder: strings.searchPlaceholder
19693
- }
19694
- ),
19695
- extraLeading,
19696
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0" }),
19697
- extra,
19698
- /* @__PURE__ */ jsxs("div", { className: "relative shrink-0", ref: columnPopover.ref, children: [
19699
- /* @__PURE__ */ jsx(
19700
- ToolbarButton,
19787
+ return /* @__PURE__ */ jsxs("div", { className: "flex w-full min-w-0 flex-col gap-2 px-2.5 py-2.5 border-b border-foreground/[0.06] sm:flex-row sm:items-center sm:gap-2", children: [
19788
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-wrap items-center gap-2", children: [
19789
+ !hideQuickSearch && /* @__PURE__ */ jsx(
19790
+ QuickSearch,
19701
19791
  {
19702
- onClick: () => columnPopover.setOpen(!columnPopover.open),
19703
- active: columnPopover.open,
19704
- title: strings.columns,
19705
- children: /* @__PURE__ */ jsx(n3, { className: "h-3.5 w-3.5" })
19792
+ value: state.quickSearch,
19793
+ onChange: updateQuickSearch,
19794
+ placeholder: strings.searchPlaceholder
19706
19795
  }
19707
19796
  ),
19708
- columnPopover.open && /* @__PURE__ */ jsx(PopoverPanel, { popoverRef: columnPopover.ref, className: "right-0 left-auto", children: /* @__PURE__ */ jsx(
19709
- ColumnManager,
19710
- {
19711
- columns,
19712
- visibility: state.columnVisibility,
19713
- onChange: updateVisibility,
19714
- strings,
19715
- dateDisplay: state.dateDisplay,
19716
- onDateDisplayChange: updateDateDisplay,
19717
- hasDateColumns
19718
- }
19719
- ) })
19797
+ extraLeading,
19798
+ extra
19720
19799
  ] }),
19721
- /* @__PURE__ */ jsx(ToolbarButton, { onClick: exportCsv, title: strings.export, children: /* @__PURE__ */ jsx(l2, { className: "h-3.5 w-3.5" }) })
19800
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center justify-end gap-2", children: [
19801
+ /* @__PURE__ */ jsxs("div", { className: "relative shrink-0", ref: columnPopover.ref, children: [
19802
+ /* @__PURE__ */ jsx(
19803
+ ToolbarButton,
19804
+ {
19805
+ onClick: () => columnPopover.setOpen(!columnPopover.open),
19806
+ active: columnPopover.open,
19807
+ title: strings.columns,
19808
+ children: /* @__PURE__ */ jsx(n3, { className: "h-3.5 w-3.5" })
19809
+ }
19810
+ ),
19811
+ columnPopover.open && /* @__PURE__ */ jsx(PopoverPanel, { popoverRef: columnPopover.ref, className: "right-0 left-auto", children: /* @__PURE__ */ jsx(
19812
+ ColumnManager,
19813
+ {
19814
+ columns,
19815
+ visibility: state.columnVisibility,
19816
+ onChange: updateVisibility,
19817
+ strings,
19818
+ dateDisplay: state.dateDisplay,
19819
+ onDateDisplayChange: updateDateDisplay,
19820
+ hasDateColumns
19821
+ }
19822
+ ) })
19823
+ ] }),
19824
+ /* @__PURE__ */ jsx(ToolbarButton, { onClick: exportCsv, title: strings.export, children: /* @__PURE__ */ jsx(l2, { className: "h-3.5 w-3.5" }) })
19825
+ ] })
19722
19826
  ] });
19723
19827
  }
19724
19828
 
19725
- // src/components/data-grid/data-grid-sizing.ts
19726
- function colVar(id) {
19727
- return `--col-${id}`;
19728
- }
19729
- function getColumnSizingStyle(col) {
19730
- const w = `var(${colVar(col.id)})`;
19731
- return { flex: `0 0 ${w}`, width: w, minWidth: col.minWidth ?? 50, maxWidth: col.maxWidth ?? 800 };
19732
- }
19733
- function createGridSizingStyle(widths, totalWidth) {
19734
- const style = { "--grid-total-w": `${totalWidth}px` };
19735
- for (const [id, w] of widths) {
19736
- style[colVar(id)] = `${w}px`;
19737
- }
19738
- return style;
19739
- }
19740
- function applyDraggedColumnWidth(el, columnId, width, totalWidth) {
19741
- el.style.setProperty(colVar(columnId), `${width}px`);
19742
- el.style.setProperty("--grid-total-w", `${totalWidth}px`);
19743
- }
19744
- function clampColumnWidth(col, width) {
19745
- return Math.max(col.minWidth ?? 50, Math.min(col.maxWidth ?? 800, width));
19746
- }
19747
-
19748
19829
  // src/components/data-grid/state.ts
19749
19830
  var EMPTY_SORT_MODEL = [];
19750
19831
  var EMPTY_SELECTION = {
@@ -19759,7 +19840,8 @@ ${colorConfig.map(([key, itemConfig]) => {
19759
19840
  const columnWidths = {};
19760
19841
  const columnOrder = [];
19761
19842
  for (const col of columns) {
19762
- columnWidths[col.id] = col.width ?? 150;
19843
+ const raw = col.width ?? 150;
19844
+ columnWidths[col.id] = clampColumnWidth(col, raw);
19763
19845
  columnOrder.push(col.id);
19764
19846
  }
19765
19847
  return {
@@ -19780,7 +19862,8 @@ ${colorConfig.map(([key, itemConfig]) => {
19780
19862
  return row[key];
19781
19863
  }
19782
19864
  function resolveColumnWidth(col, storedWidth) {
19783
- return storedWidth ?? col.width ?? 150;
19865
+ const raw = storedWidth ?? col.width ?? 150;
19866
+ return clampColumnWidth(col, raw);
19784
19867
  }
19785
19868
  function isColumnVisible(columnId, visibility) {
19786
19869
  return visibility[columnId] !== false;
@@ -19914,8 +19997,18 @@ ${colorConfig.map(([key, itemConfig]) => {
19914
19997
  { amount: 12, unit: "month" },
19915
19998
  { amount: Number.POSITIVE_INFINITY, unit: "year" }
19916
19999
  ];
20000
+ var relativeTimeFormatterCache = /* @__PURE__ */ new Map();
20001
+ function getRelativeTimeFormatter(locale2) {
20002
+ const key = locale2 ?? "__default__";
20003
+ let cached = relativeTimeFormatterCache.get(key);
20004
+ if (cached == null) {
20005
+ cached = new Intl.RelativeTimeFormat(locale2, { numeric: "auto" });
20006
+ relativeTimeFormatterCache.set(key, cached);
20007
+ }
20008
+ return cached;
20009
+ }
19917
20010
  function defaultFormatRelative(date2) {
19918
- const rtf = new Intl.RelativeTimeFormat(void 0, { numeric: "auto" });
20011
+ const rtf = getRelativeTimeFormatter();
19919
20012
  let duration = (date2.getTime() - Date.now()) / 1e3;
19920
20013
  for (const div of DIVISIONS) {
19921
20014
  if (Math.abs(duration) < div.amount) {
@@ -19945,14 +20038,14 @@ ${colorConfig.map(([key, itemConfig]) => {
19945
20038
  const csvRows = rows.map(
19946
20039
  (row) => columns.map((col) => {
19947
20040
  const val = resolveColumnValue(col, row);
19948
- const formatted = col.formatValue ? col.formatValue(val, row) : String(val ?? "");
20041
+ const formatted = col.formatValue ? String(col.formatValue(val, row) ?? "") : String(val ?? "");
19949
20042
  if (formatted.includes(",") || formatted.includes('"') || formatted.includes("\n")) {
19950
20043
  return `"${formatted.replace(/"/g, '""')}"`;
19951
20044
  }
19952
20045
  return formatted;
19953
20046
  })
19954
20047
  );
19955
- const csvContent = [
20048
+ const csvContent = "\uFEFF" + [
19956
20049
  header.join(","),
19957
20050
  ...csvRows.map((row) => row.join(","))
19958
20051
  ].join("\n");
@@ -19961,8 +20054,13 @@ ${colorConfig.map(([key, itemConfig]) => {
19961
20054
  const link = document.createElement("a");
19962
20055
  link.href = url;
19963
20056
  link.download = `${filename}.csv`;
19964
- link.click();
19965
- URL.revokeObjectURL(url);
20057
+ document.body.appendChild(link);
20058
+ try {
20059
+ link.click();
20060
+ } finally {
20061
+ link.remove();
20062
+ URL.revokeObjectURL(url);
20063
+ }
19966
20064
  }
19967
20065
 
19968
20066
  // src/components/data-grid/strings.ts
@@ -20076,6 +20174,45 @@ ${colorConfig.map(([key, itemConfig]) => {
20076
20174
  }
20077
20175
  );
20078
20176
  }
20177
+ function getNearestVerticalScrollElement(element) {
20178
+ let current = element?.parentElement ?? null;
20179
+ while (current) {
20180
+ const style = window.getComputedStyle(current);
20181
+ const overflowY = style.overflowY === "visible" ? style.overflow : style.overflowY;
20182
+ const canScrollVertically = (overflowY === "auto" || overflowY === "scroll" || overflowY === "overlay") && current.scrollHeight > current.clientHeight + 1;
20183
+ if (canScrollVertically) {
20184
+ return current;
20185
+ }
20186
+ current = current.parentElement;
20187
+ }
20188
+ return window;
20189
+ }
20190
+ function getEventTargetElement(target) {
20191
+ if (target instanceof Element) {
20192
+ return target;
20193
+ }
20194
+ if (target instanceof Node) {
20195
+ return target.parentElement;
20196
+ }
20197
+ return null;
20198
+ }
20199
+ function isDataGridInteractiveRowClickTarget(target) {
20200
+ const targetElement = getEventTargetElement(target);
20201
+ return targetElement?.closest([
20202
+ "a",
20203
+ "button",
20204
+ "input",
20205
+ "select",
20206
+ "textarea",
20207
+ '[role="button"]',
20208
+ '[role="menuitem"]',
20209
+ '[contenteditable]:not([contenteditable="false"])',
20210
+ "[data-no-row-click]"
20211
+ ].join(",")) != null;
20212
+ }
20213
+ function shouldIgnoreRowClick(event) {
20214
+ return event.defaultPrevented || isDataGridInteractiveRowClickTarget(event.target);
20215
+ }
20079
20216
  function HeaderCell({
20080
20217
  col,
20081
20218
  isSorted,
@@ -20097,7 +20234,7 @@ ${colorConfig.map(([key, itemConfig]) => {
20097
20234
  "div",
20098
20235
  {
20099
20236
  className: cn(
20100
- "group/header relative flex items-center gap-1.5 px-3 select-none bg-transparent",
20237
+ "group/header relative flex items-center gap-1.5 px-3 select-none bg-transparent overflow-hidden",
20101
20238
  "border-r border-black/[0.04] dark:border-white/[0.04] last:border-r-0",
20102
20239
  sortable && "cursor-pointer"
20103
20240
  ),
@@ -20111,7 +20248,7 @@ ${colorConfig.map(([key, itemConfig]) => {
20111
20248
  "span",
20112
20249
  {
20113
20250
  className: cn(
20114
- "flex-1 truncate text-xs font-semibold uppercase tracking-wider text-muted-foreground",
20251
+ "flex-1 min-w-0 truncate text-xs font-semibold uppercase tracking-wider text-muted-foreground",
20115
20252
  col.align === "center" && "text-center",
20116
20253
  col.align === "right" && "text-right"
20117
20254
  ),
@@ -20165,13 +20302,15 @@ ${colorConfig.map(([key, itemConfig]) => {
20165
20302
  content = formatCellValue(value);
20166
20303
  }
20167
20304
  const hasCellClick = col.onCellClick || col.onCellDoubleClick;
20305
+ const isWrap = col.cellOverflow === "wrap";
20168
20306
  return /* @__PURE__ */ jsx(
20169
20307
  "div",
20170
20308
  {
20171
20309
  className: cn(
20172
- "flex items-center px-3 truncate bg-transparent",
20310
+ "flex px-3 bg-transparent overflow-hidden",
20173
20311
  "border-r border-black/[0.04] dark:border-white/[0.04] last:border-r-0",
20174
20312
  "text-sm text-foreground",
20313
+ isWrap ? "items-start py-2" : "items-center",
20175
20314
  col.align === "center" && "justify-center",
20176
20315
  col.align === "right" && "justify-end",
20177
20316
  hasCellClick && "cursor-pointer"
@@ -20187,7 +20326,7 @@ ${colorConfig.map(([key, itemConfig]) => {
20187
20326
  e40.stopPropagation();
20188
20327
  col.onCellDoubleClick(ctx, e40);
20189
20328
  } : void 0,
20190
- children: content
20329
+ children: /* @__PURE__ */ jsx("div", { className: cn("min-w-0", isWrap ? "flex-1" : "truncate"), children: content })
20191
20330
  }
20192
20331
  );
20193
20332
  }
@@ -20225,6 +20364,13 @@ ${colorConfig.map(([key, itemConfig]) => {
20225
20364
  }
20226
20365
  );
20227
20366
  }
20367
+ function hashStringToInt(value) {
20368
+ let hash2 = 0;
20369
+ for (let i = 0; i < value.length; i++) {
20370
+ hash2 = (hash2 << 5) - hash2 + value.charCodeAt(i) | 0;
20371
+ }
20372
+ return Math.abs(hash2);
20373
+ }
20228
20374
  function SkeletonRow({
20229
20375
  columns,
20230
20376
  height,
@@ -20248,7 +20394,7 @@ ${colorConfig.map(([key, itemConfig]) => {
20248
20394
  DesignSkeleton,
20249
20395
  {
20250
20396
  className: "h-3.5 rounded-md",
20251
- style: { width: `${40 + Math.random() * 40}%` }
20397
+ style: { width: `${40 + hashStringToInt(col.id) % 40}%` }
20252
20398
  }
20253
20399
  )
20254
20400
  },
@@ -20282,9 +20428,12 @@ ${colorConfig.map(([key, itemConfig]) => {
20282
20428
  }
20283
20429
  );
20284
20430
  }
20431
+ var NOOP = () => {
20432
+ };
20285
20433
  function InfiniteScrollSentinel({
20286
20434
  onIntersect,
20287
20435
  isLoading,
20436
+ rootRef,
20288
20437
  strings
20289
20438
  }) {
20290
20439
  const ref = (0, import_react30.useRef)(null);
@@ -20297,11 +20446,14 @@ ${colorConfig.map(([key, itemConfig]) => {
20297
20446
  onIntersect();
20298
20447
  }
20299
20448
  },
20300
- { rootMargin: "200px" }
20449
+ {
20450
+ root: rootRef?.current ?? null,
20451
+ rootMargin: "200px"
20452
+ }
20301
20453
  );
20302
20454
  observer.observe(el);
20303
20455
  return () => observer.disconnect();
20304
- }, [onIntersect]);
20456
+ }, [onIntersect, rootRef]);
20305
20457
  return /* @__PURE__ */ jsx("div", { ref, className: "flex items-center justify-center py-4", children: isLoading && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
20306
20458
  /* @__PURE__ */ jsx("div", { className: "h-3 w-3 rounded-full border-2 border-current border-t-transparent animate-spin" }),
20307
20459
  strings.loadingMore
@@ -20399,10 +20551,13 @@ ${colorConfig.map(([key, itemConfig]) => {
20399
20551
  paginationMode = "paginated",
20400
20552
  selectionMode = "none",
20401
20553
  resizable = true,
20402
- rowHeight = 44,
20554
+ rowHeight: rowHeightProp = 44,
20555
+ estimatedRowHeight: estimatedRowHeightProp,
20403
20556
  headerHeight = 44,
20404
20557
  overscan = 5,
20405
20558
  maxHeight,
20559
+ fillHeight = true,
20560
+ stickyTop,
20406
20561
  toolbar,
20407
20562
  toolbarExtra,
20408
20563
  emptyState,
@@ -20418,6 +20573,9 @@ ${colorConfig.map(([key, itemConfig]) => {
20418
20573
  onSelectionChange,
20419
20574
  onSortChange
20420
20575
  } = props;
20576
+ const isDynamicRowHeight = rowHeightProp === "auto";
20577
+ const fixedRowHeight = isDynamicRowHeight ? void 0 : rowHeightProp;
20578
+ const estimatedRowHeight = estimatedRowHeightProp ?? (fixedRowHeight ?? 44);
20421
20579
  const strings = (0, import_react30.useMemo)(
20422
20580
  () => resolveDataGridStrings(stringsOverride),
20423
20581
  [stringsOverride]
@@ -20445,13 +20603,11 @@ ${colorConfig.map(([key, itemConfig]) => {
20445
20603
  const gridRef = (0, import_react30.useRef)(null);
20446
20604
  const handleSort = (0, import_react30.useCallback)(
20447
20605
  (columnId, multi) => {
20448
- onChange((s8) => {
20449
- const next = toggleSort(s8.sorting, columnId, multi);
20450
- onSortChange?.(next);
20451
- return { ...s8, sorting: next };
20452
- });
20606
+ const next = toggleSort(state.sorting, columnId, multi);
20607
+ onChange((s8) => ({ ...s8, sorting: next }));
20608
+ onSortChange?.(next);
20453
20609
  },
20454
- [onChange, onSortChange]
20610
+ [onChange, onSortChange, state.sorting]
20455
20611
  );
20456
20612
  const handleResize = (0, import_react30.useCallback)(
20457
20613
  (columnId, delta) => {
@@ -20484,27 +20640,23 @@ ${colorConfig.map(([key, itemConfig]) => {
20484
20640
  const handleRowClick = (0, import_react30.useCallback)(
20485
20641
  (row, rowId, event) => {
20486
20642
  if (selectionMode !== "none") {
20487
- onChange((s8) => {
20488
- const next = toggleRowSelection(
20489
- s8.selection,
20490
- rowId,
20491
- selectionMode,
20492
- event.shiftKey,
20493
- event.metaKey || event.ctrlKey,
20494
- rowIds
20495
- );
20496
- if (onSelectionChange) {
20497
- const selectedRows = rows.filter(
20498
- (r7) => next.selectedIds.has(getRowId(r7))
20499
- );
20500
- setTimeout(() => onSelectionChange(next.selectedIds, selectedRows), 0);
20501
- }
20502
- return { ...s8, selection: next };
20503
- });
20643
+ const next = toggleRowSelection(
20644
+ state.selection,
20645
+ rowId,
20646
+ selectionMode,
20647
+ event.shiftKey,
20648
+ event.metaKey || event.ctrlKey,
20649
+ rowIds
20650
+ );
20651
+ onChange((s8) => ({ ...s8, selection: next }));
20652
+ if (onSelectionChange) {
20653
+ const selectedRows = rows.filter((r7) => next.selectedIds.has(getRowId(r7)));
20654
+ onSelectionChange(next.selectedIds, selectedRows);
20655
+ }
20504
20656
  }
20505
20657
  onRowClick?.(row, rowId, event);
20506
20658
  },
20507
- [selectionMode, onChange, onRowClick, onSelectionChange, rowIds, rows, getRowId]
20659
+ [selectionMode, onChange, onRowClick, onSelectionChange, rowIds, rows, getRowId, state.selection]
20508
20660
  );
20509
20661
  const handleRowSelectionCheckboxClick = (0, import_react30.useCallback)(
20510
20662
  (row, rowId, event) => {
@@ -20513,27 +20665,138 @@ ${colorConfig.map(([key, itemConfig]) => {
20513
20665
  [handleRowClick]
20514
20666
  );
20515
20667
  const handleSelectAll = (0, import_react30.useCallback)(() => {
20516
- onChange((s8) => {
20517
- const allSelected2 = rowIds.every((id) => s8.selection.selectedIds.has(id));
20518
- const next = allSelected2 ? clearSelection() : selectAll(rowIds);
20519
- if (onSelectionChange) {
20520
- const selectedRows = allSelected2 ? [] : rows;
20521
- setTimeout(() => onSelectionChange(next.selectedIds, [...selectedRows]), 0);
20522
- }
20523
- return { ...s8, selection: next };
20524
- });
20525
- }, [onChange, rowIds, rows, onSelectionChange]);
20668
+ const allSelectedNow = rowIds.every((id) => state.selection.selectedIds.has(id));
20669
+ const next = allSelectedNow ? clearSelection() : selectAll(rowIds);
20670
+ const selectedRows = allSelectedNow ? [] : rows;
20671
+ onChange((s8) => ({ ...s8, selection: next }));
20672
+ if (onSelectionChange) {
20673
+ onSelectionChange(next.selectedIds, [...selectedRows]);
20674
+ }
20675
+ }, [onChange, rowIds, rows, onSelectionChange, state.selection]);
20526
20676
  const handleExportCsv = (0, import_react30.useCallback)(() => {
20527
20677
  exportToCsv(rows, visibleColumns, exportFilename);
20528
20678
  }, [rows, visibleColumns, exportFilename]);
20529
20679
  const scrollContainerRef = (0, import_react30.useRef)(null);
20530
20680
  const headerScrollRef = (0, import_react30.useRef)(null);
20681
+ const stickyChromeRef = (0, import_react30.useRef)(null);
20682
+ const rowsClipRef = (0, import_react30.useRef)(null);
20683
+ const measureElementFn = (0, import_react30.useCallback)(
20684
+ (el) => el.getBoundingClientRect().height,
20685
+ []
20686
+ );
20531
20687
  const rowVirtualizer = useVirtualizer({
20532
20688
  count: rows.length,
20533
20689
  getScrollElement: () => scrollContainerRef.current,
20534
- estimateSize: () => rowHeight,
20535
- overscan
20690
+ estimateSize: () => estimatedRowHeight,
20691
+ overscan,
20692
+ getItemKey: (index3) => {
20693
+ const row = rows[index3];
20694
+ return row != null ? String(getRowId(row)) : index3;
20695
+ },
20696
+ ...isDynamicRowHeight ? { measureElement: measureElementFn } : {}
20536
20697
  });
20698
+ (0, import_react30.useLayoutEffect)(() => {
20699
+ const grid = gridRef.current;
20700
+ const stickyEl = stickyChromeRef.current;
20701
+ if (!grid || !stickyEl) return;
20702
+ const parseRgba = (raw) => {
20703
+ const rgbaMatch = raw.match(/rgba?\(\s*([\d.]+),\s*([\d.]+),\s*([\d.]+)(?:,\s*([\d.]+))?\s*\)/);
20704
+ if (!rgbaMatch) return null;
20705
+ const alphaRaw = rgbaMatch[4];
20706
+ return [
20707
+ Number(rgbaMatch[1]),
20708
+ Number(rgbaMatch[2]),
20709
+ Number(rgbaMatch[3]),
20710
+ alphaRaw === void 0 ? 1 : Number(alphaRaw)
20711
+ ];
20712
+ };
20713
+ const blendOver = (base, top) => {
20714
+ const [tr, tg, tb, ta] = top;
20715
+ const [br, bg, bb, ba] = base;
20716
+ const outA = ta + ba * (1 - ta);
20717
+ if (outA === 0) return [0, 0, 0, 0];
20718
+ return [
20719
+ (tr * ta + br * ba * (1 - ta)) / outA,
20720
+ (tg * ta + bg * ba * (1 - ta)) / outA,
20721
+ (tb * ta + bb * ba * (1 - ta)) / outA,
20722
+ outA
20723
+ ];
20724
+ };
20725
+ const detect = () => {
20726
+ const layers = [];
20727
+ let ancestor = grid.parentElement;
20728
+ while (ancestor) {
20729
+ const parsed = parseRgba(getComputedStyle(ancestor).backgroundColor);
20730
+ if (parsed && parsed[3] > 0) {
20731
+ layers.push(parsed);
20732
+ if (parsed[3] >= 1) break;
20733
+ }
20734
+ ancestor = ancestor.parentElement;
20735
+ }
20736
+ if (layers.length === 0) {
20737
+ stickyEl.style.backgroundColor = "";
20738
+ return;
20739
+ }
20740
+ let result = layers[layers.length - 1];
20741
+ for (let i = layers.length - 2; i >= 0; i--) {
20742
+ result = blendOver(result, layers[i]);
20743
+ }
20744
+ const [r7, g, b] = result;
20745
+ stickyEl.style.backgroundColor = `rgb(${Math.round(r7)}, ${Math.round(g)}, ${Math.round(b)})`;
20746
+ };
20747
+ detect();
20748
+ const observer = new MutationObserver(detect);
20749
+ observer.observe(document.documentElement, { attributes: true, attributeFilter: ["class"] });
20750
+ return () => observer.disconnect();
20751
+ }, []);
20752
+ (0, import_react30.useLayoutEffect)(() => {
20753
+ const gridEl = gridRef.current;
20754
+ const stickyEl = stickyChromeRef.current;
20755
+ const bodyEl = scrollContainerRef.current;
20756
+ const clipEl = rowsClipRef.current;
20757
+ if (!gridEl || !stickyEl || !bodyEl || !clipEl) return;
20758
+ const verticalScrollEl = fillHeight ? bodyEl : getNearestVerticalScrollElement(gridEl);
20759
+ let extraObservedScrollEl = null;
20760
+ if (verticalScrollEl instanceof HTMLElement && verticalScrollEl !== bodyEl) {
20761
+ extraObservedScrollEl = verticalScrollEl;
20762
+ }
20763
+ const updateClip = () => {
20764
+ const stickyRect = stickyEl.getBoundingClientRect();
20765
+ const clipRect = clipEl.getBoundingClientRect();
20766
+ const overlap = Math.max(0, stickyRect.bottom - clipRect.top);
20767
+ const clipValue = overlap > 0 ? `inset(${overlap}px 0 0 0)` : "";
20768
+ const maskValue = overlap > 0 ? `linear-gradient(to bottom, transparent 0px, transparent ${overlap}px, black ${overlap}px, black 100%)` : "";
20769
+ clipEl.style.clipPath = clipValue;
20770
+ clipEl.style.setProperty("-webkit-clip-path", clipValue);
20771
+ clipEl.style.maskImage = maskValue;
20772
+ clipEl.style.setProperty("-webkit-mask-image", maskValue);
20773
+ };
20774
+ updateClip();
20775
+ bodyEl.addEventListener("scroll", updateClip);
20776
+ if (verticalScrollEl === window) {
20777
+ window.addEventListener("scroll", updateClip, true);
20778
+ } else if (extraObservedScrollEl) {
20779
+ extraObservedScrollEl.addEventListener("scroll", updateClip);
20780
+ }
20781
+ window.addEventListener("resize", updateClip);
20782
+ const ro = new ResizeObserver(updateClip);
20783
+ ro.observe(gridEl);
20784
+ ro.observe(stickyEl);
20785
+ ro.observe(bodyEl);
20786
+ if (extraObservedScrollEl) {
20787
+ ro.observe(extraObservedScrollEl);
20788
+ }
20789
+ return () => {
20790
+ bodyEl.removeEventListener("scroll", updateClip);
20791
+ if (verticalScrollEl === window) {
20792
+ window.removeEventListener("scroll", updateClip, true);
20793
+ } else if (extraObservedScrollEl) {
20794
+ extraObservedScrollEl.removeEventListener("scroll", updateClip);
20795
+ }
20796
+ window.removeEventListener("resize", updateClip);
20797
+ ro.disconnect();
20798
+ };
20799
+ }, [fillHeight]);
20537
20800
  const handleBodyScroll = (0, import_react30.useCallback)(() => {
20538
20801
  const body = scrollContainerRef.current;
20539
20802
  const header = headerScrollRef.current;
@@ -20567,12 +20830,14 @@ ${colorConfig.map(([key, itemConfig]) => {
20567
20830
  );
20568
20831
  const allSelected = rowIds.length > 0 && rowIds.every((id) => state.selection.selectedIds.has(id));
20569
20832
  const someSelected = !allSelected && rowIds.some((id) => state.selection.selectedIds.has(id));
20833
+ const infiniteScrollRootRef = paginationMode === "infinite" && (fillHeight || maxHeight != null) ? scrollContainerRef : void 0;
20570
20834
  return /* @__PURE__ */ jsxs(
20571
20835
  "div",
20572
20836
  {
20573
20837
  ref: gridRef,
20574
20838
  className: cn(
20575
- "flex flex-col h-full min-h-0 bg-transparent",
20839
+ "flex w-full min-w-0 max-w-full flex-col bg-transparent rounded-[calc(var(--radius)*2)]",
20840
+ fillHeight ? "min-h-0 h-full" : "min-h-0 h-auto",
20576
20841
  className
20577
20842
  ),
20578
20843
  style: maxHeight != null ? { ...gridSizingStyle, maxHeight } : gridSizingStyle,
@@ -20580,175 +20845,199 @@ ${colorConfig.map(([key, itemConfig]) => {
20580
20845
  "aria-rowcount": totalRowCount ?? rows.length,
20581
20846
  "aria-colcount": visibleColumns.length,
20582
20847
  children: [
20583
- toolbar !== false && /* @__PURE__ */ jsx("div", { className: "relative shrink-0 bg-transparent", children: toolbar ? toolbar(toolbarCtx) : /* @__PURE__ */ jsx(
20584
- DataGridToolbar,
20848
+ /* @__PURE__ */ jsxs(
20849
+ "div",
20585
20850
  {
20586
- ctx: toolbarCtx,
20587
- extra: typeof toolbarExtra === "function" ? toolbarExtra(toolbarCtx) : toolbarExtra
20851
+ ref: stickyChromeRef,
20852
+ className: "sticky z-20 w-full min-w-0 shrink-0 rounded-t-[calc(var(--radius)*2)] bg-background",
20853
+ style: { top: stickyTop ?? "var(--data-grid-sticky-top, 0px)" },
20854
+ children: [
20855
+ toolbar !== false && /* @__PURE__ */ jsx("div", { className: "relative bg-transparent", children: toolbar ? toolbar(toolbarCtx) : /* @__PURE__ */ jsx(
20856
+ DataGridToolbar,
20857
+ {
20858
+ ctx: toolbarCtx,
20859
+ extra: typeof toolbarExtra === "function" ? toolbarExtra(toolbarCtx) : toolbarExtra
20860
+ }
20861
+ ) }),
20862
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
20863
+ isRefetching && /* @__PURE__ */ jsx("div", { className: "absolute top-0 left-0 right-0 h-0.5 z-30 bg-foreground/[0.04] overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "h-full w-1/3 bg-blue-500/60 rounded-full animate-pulse" }) }),
20864
+ /* @__PURE__ */ jsx(
20865
+ "div",
20866
+ {
20867
+ ref: headerScrollRef,
20868
+ className: "w-full min-w-0 shrink-0 overflow-hidden border-b border-foreground/[0.06]",
20869
+ children: /* @__PURE__ */ jsxs(
20870
+ "div",
20871
+ {
20872
+ className: "flex",
20873
+ style: { height: headerHeight, minWidth: visibleColumnMetrics.totalWidth },
20874
+ role: "row",
20875
+ children: [
20876
+ selectionMode !== "none" && /* @__PURE__ */ jsx(
20877
+ "div",
20878
+ {
20879
+ className: "flex items-center justify-center border-r border-foreground/[0.04]",
20880
+ style: { width: 44 },
20881
+ children: selectionMode === "multiple" && /* @__PURE__ */ jsx(
20882
+ SelectionCheckbox,
20883
+ {
20884
+ checked: allSelected,
20885
+ indeterminate: someSelected,
20886
+ onChange: handleSelectAll,
20887
+ ariaLabel: "Select all rows"
20888
+ }
20889
+ )
20890
+ }
20891
+ ),
20892
+ visibleColumns.map((col) => /* @__PURE__ */ jsx(
20893
+ HeaderCell,
20894
+ {
20895
+ col,
20896
+ isSorted: getSortDirection(state.sorting, col.id),
20897
+ sortIndex: getSortIndex(state.sorting, col.id),
20898
+ resizable,
20899
+ onSort: handleSort,
20900
+ onResize: handleResize,
20901
+ onResizeEnd: handleResizeEnd
20902
+ },
20903
+ col.id
20904
+ ))
20905
+ ]
20906
+ }
20907
+ )
20908
+ }
20909
+ )
20910
+ ] })
20911
+ ]
20588
20912
  }
20589
- ) }),
20590
- /* @__PURE__ */ jsxs("div", { className: "relative flex min-h-0 flex-1 flex-col", children: [
20591
- isRefetching && /* @__PURE__ */ jsx("div", { className: "absolute top-0 left-0 right-0 h-0.5 z-30 bg-foreground/[0.04] overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "h-full w-1/3 bg-blue-500/60 rounded-full animate-pulse" }) }),
20592
- /* @__PURE__ */ jsx(
20593
- "div",
20594
- {
20595
- ref: headerScrollRef,
20596
- className: "overflow-hidden shrink-0 border-b border-foreground/[0.06]",
20597
- children: /* @__PURE__ */ jsxs(
20913
+ ),
20914
+ /* @__PURE__ */ jsx(
20915
+ "div",
20916
+ {
20917
+ ref: scrollContainerRef,
20918
+ className: cn(
20919
+ "w-full min-w-0 overflow-auto bg-transparent",
20920
+ fillHeight ? "min-h-0 flex-1" : "flex-none",
20921
+ "[&::-webkit-scrollbar]:w-1.5 [&::-webkit-scrollbar]:h-1.5",
20922
+ "[&::-webkit-scrollbar-track]:bg-transparent",
20923
+ "[&::-webkit-scrollbar-thumb]:bg-foreground/[0.08] [&::-webkit-scrollbar-thumb]:rounded-full",
20924
+ "[&::-webkit-scrollbar-thumb]:hover:bg-foreground/[0.15]"
20925
+ ),
20926
+ onScroll: handleBodyScroll,
20927
+ children: /* @__PURE__ */ jsxs("div", { ref: rowsClipRef, children: [
20928
+ isLoading && /* @__PURE__ */ jsx("div", { style: { minWidth: visibleColumnMetrics.totalWidth }, children: loadingState ?? Array.from({ length: 8 }).map((_, i) => /* @__PURE__ */ jsx(
20929
+ SkeletonRow,
20930
+ {
20931
+ columns: visibleColumns,
20932
+ height: estimatedRowHeight,
20933
+ showCheckbox: selectionMode !== "none"
20934
+ },
20935
+ i
20936
+ )) }),
20937
+ !isLoading && rows.length === 0 && /* @__PURE__ */ jsx(
20598
20938
  "div",
20599
20939
  {
20600
- className: "flex",
20601
- style: { height: headerHeight, minWidth: visibleColumnMetrics.totalWidth },
20602
- role: "row",
20603
- children: [
20604
- selectionMode !== "none" && /* @__PURE__ */ jsx(
20940
+ className: "flex items-center justify-center py-16 text-sm text-muted-foreground",
20941
+ style: { minWidth: visibleColumnMetrics.totalWidth },
20942
+ children: emptyState ?? strings.noData
20943
+ }
20944
+ ),
20945
+ !isLoading && rows.length > 0 && /* @__PURE__ */ jsx(
20946
+ "div",
20947
+ {
20948
+ style: {
20949
+ height: rowVirtualizer.getTotalSize(),
20950
+ width: "100%",
20951
+ minWidth: visibleColumnMetrics.totalWidth,
20952
+ position: "relative"
20953
+ },
20954
+ children: rowVirtualizer.getVirtualItems().map((virtualRow) => {
20955
+ const row = rows[virtualRow.index] ?? throwErr(
20956
+ `DataGrid: virtualized row index ${virtualRow.index} out of range (rows.length=${rows.length})`
20957
+ );
20958
+ const rowId = getRowId(row);
20959
+ const isSelected = state.selection.selectedIds.has(rowId);
20960
+ const isOddRow = virtualRow.index % 2 === 1;
20961
+ return /* @__PURE__ */ jsxs(
20605
20962
  "div",
20606
20963
  {
20607
- className: "flex items-center justify-center border-r border-foreground/[0.04]",
20608
- style: { width: 44 },
20609
- children: selectionMode === "multiple" && /* @__PURE__ */ jsx(
20610
- SelectionCheckbox,
20611
- {
20612
- checked: allSelected,
20613
- indeterminate: someSelected,
20614
- onChange: handleSelectAll,
20615
- ariaLabel: "Select all rows"
20964
+ ref: isDynamicRowHeight ? rowVirtualizer.measureElement : void 0,
20965
+ "data-index": virtualRow.index,
20966
+ className: cn(
20967
+ "absolute left-0 w-full flex",
20968
+ "border-b border-black/[0.03] dark:border-white/[0.03]",
20969
+ "transition-colors duration-75",
20970
+ isSelected ? "bg-blue-500/[0.06] dark:bg-blue-400/[0.08] hover:bg-blue-500/[0.08] dark:hover:bg-blue-400/[0.1]" : isOddRow ? "bg-foreground/[0.02] dark:bg-foreground/[0.03] hover:bg-foreground/[0.04] dark:hover:bg-foreground/[0.06]" : "hover:bg-foreground/[0.025] dark:hover:bg-foreground/[0.04]",
20971
+ (selectionMode !== "none" || onRowClick) && "cursor-pointer"
20972
+ ),
20973
+ style: {
20974
+ ...isDynamicRowHeight ? { minHeight: estimatedRowHeight } : { height: fixedRowHeight },
20975
+ transform: `translateY(${virtualRow.start}px)`
20976
+ },
20977
+ onClick: (e40) => {
20978
+ if (shouldIgnoreRowClick(e40)) {
20979
+ return;
20616
20980
  }
20617
- )
20618
- }
20619
- ),
20620
- visibleColumns.map((col) => /* @__PURE__ */ jsx(
20621
- HeaderCell,
20622
- {
20623
- col,
20624
- isSorted: getSortDirection(state.sorting, col.id),
20625
- sortIndex: getSortIndex(state.sorting, col.id),
20626
- resizable,
20627
- onSort: handleSort,
20628
- onResize: handleResize,
20629
- onResizeEnd: handleResizeEnd
20981
+ handleRowClick(row, rowId, e40);
20982
+ },
20983
+ onDoubleClick: (e40) => {
20984
+ if (shouldIgnoreRowClick(e40)) {
20985
+ return;
20986
+ }
20987
+ onRowDoubleClick?.(row, rowId, e40);
20988
+ },
20989
+ role: "row",
20990
+ "aria-rowindex": virtualRow.index + 2,
20991
+ "aria-selected": isSelected,
20992
+ "data-row-id": rowId,
20993
+ "data-state": isSelected ? "selected" : void 0,
20994
+ children: [
20995
+ selectionMode !== "none" && /* @__PURE__ */ jsx(
20996
+ "div",
20997
+ {
20998
+ className: "flex items-center justify-center border-r border-black/[0.04] dark:border-white/[0.04]",
20999
+ style: { width: 44 },
21000
+ children: /* @__PURE__ */ jsx(
21001
+ SelectionCheckbox,
21002
+ {
21003
+ checked: isSelected,
21004
+ onChange: (event) => handleRowSelectionCheckboxClick(row, rowId, event),
21005
+ ariaLabel: `Select row ${rowId}`
21006
+ }
21007
+ )
21008
+ }
21009
+ ),
21010
+ visibleColumns.map((col) => /* @__PURE__ */ jsx(
21011
+ DataCell,
21012
+ {
21013
+ col,
21014
+ row,
21015
+ rowId,
21016
+ rowIndex: virtualRow.index,
21017
+ isSelected,
21018
+ dateDisplay: state.dateDisplay
21019
+ },
21020
+ col.id
21021
+ ))
21022
+ ]
20630
21023
  },
20631
- col.id
20632
- ))
20633
- ]
21024
+ rowId
21025
+ );
21026
+ })
20634
21027
  }
20635
- )
20636
- }
20637
- ),
20638
- /* @__PURE__ */ jsxs(
20639
- "div",
20640
- {
20641
- ref: scrollContainerRef,
20642
- className: cn(
20643
- "min-h-0 overflow-auto flex-1 bg-transparent",
20644
- // Custom scrollbar
20645
- "[&::-webkit-scrollbar]:w-1.5 [&::-webkit-scrollbar]:h-1.5",
20646
- "[&::-webkit-scrollbar-track]:bg-transparent",
20647
- "[&::-webkit-scrollbar-thumb]:bg-foreground/[0.08] [&::-webkit-scrollbar-thumb]:rounded-full",
20648
- "[&::-webkit-scrollbar-thumb]:hover:bg-foreground/[0.15]"
20649
21028
  ),
20650
- onScroll: handleBodyScroll,
20651
- children: [
20652
- isLoading && /* @__PURE__ */ jsx("div", { style: { minWidth: visibleColumnMetrics.totalWidth }, children: loadingState ?? Array.from({ length: 8 }).map((_, i) => /* @__PURE__ */ jsx(
20653
- SkeletonRow,
20654
- {
20655
- columns: visibleColumns,
20656
- height: rowHeight,
20657
- showCheckbox: selectionMode !== "none"
20658
- },
20659
- i
20660
- )) }),
20661
- !isLoading && rows.length === 0 && /* @__PURE__ */ jsx(
20662
- "div",
20663
- {
20664
- className: "flex items-center justify-center py-16 text-sm text-muted-foreground",
20665
- style: { minWidth: visibleColumnMetrics.totalWidth },
20666
- children: emptyState ?? strings.noData
20667
- }
20668
- ),
20669
- !isLoading && rows.length > 0 && /* @__PURE__ */ jsx(
20670
- "div",
20671
- {
20672
- style: {
20673
- height: rowVirtualizer.getTotalSize(),
20674
- width: "100%",
20675
- minWidth: visibleColumnMetrics.totalWidth,
20676
- position: "relative"
20677
- },
20678
- children: rowVirtualizer.getVirtualItems().map((virtualRow) => {
20679
- const row = rows[virtualRow.index];
20680
- const rowId = getRowId(row);
20681
- const isSelected = state.selection.selectedIds.has(rowId);
20682
- const isOddRow = virtualRow.index % 2 === 1;
20683
- return /* @__PURE__ */ jsxs(
20684
- "div",
20685
- {
20686
- className: cn(
20687
- "absolute left-0 w-full flex",
20688
- "border-b border-black/[0.03] dark:border-white/[0.03]",
20689
- "transition-colors duration-75",
20690
- isSelected ? "bg-blue-500/[0.06] dark:bg-blue-400/[0.08] hover:bg-blue-500/[0.08] dark:hover:bg-blue-400/[0.1]" : isOddRow ? "bg-foreground/[0.02] dark:bg-foreground/[0.03] hover:bg-foreground/[0.04] dark:hover:bg-foreground/[0.06]" : "hover:bg-foreground/[0.025] dark:hover:bg-foreground/[0.04]",
20691
- selectionMode !== "none" && "cursor-pointer"
20692
- ),
20693
- style: {
20694
- height: rowHeight,
20695
- transform: `translateY(${virtualRow.start}px)`
20696
- },
20697
- onClick: (e40) => handleRowClick(row, rowId, e40),
20698
- onDoubleClick: (e40) => onRowDoubleClick?.(row, rowId, e40),
20699
- role: "row",
20700
- "aria-rowindex": virtualRow.index + 2,
20701
- "aria-selected": isSelected,
20702
- "data-row-id": rowId,
20703
- "data-state": isSelected ? "selected" : void 0,
20704
- children: [
20705
- selectionMode !== "none" && /* @__PURE__ */ jsx(
20706
- "div",
20707
- {
20708
- className: "flex items-center justify-center border-r border-black/[0.04] dark:border-white/[0.04]",
20709
- style: { width: 44 },
20710
- children: /* @__PURE__ */ jsx(
20711
- SelectionCheckbox,
20712
- {
20713
- checked: isSelected,
20714
- onChange: (event) => handleRowSelectionCheckboxClick(row, rowId, event),
20715
- ariaLabel: `Select row ${rowId}`
20716
- }
20717
- )
20718
- }
20719
- ),
20720
- visibleColumns.map((col) => /* @__PURE__ */ jsx(
20721
- DataCell,
20722
- {
20723
- col,
20724
- row,
20725
- rowId,
20726
- rowIndex: virtualRow.index,
20727
- isSelected,
20728
- dateDisplay: state.dateDisplay
20729
- },
20730
- col.id
20731
- ))
20732
- ]
20733
- },
20734
- rowId
20735
- );
20736
- })
20737
- }
20738
- ),
20739
- paginationMode === "infinite" && hasMore && !isLoading && /* @__PURE__ */ jsx(
20740
- InfiniteScrollSentinel,
20741
- {
20742
- onIntersect: onLoadMore ?? (() => {
20743
- }),
20744
- isLoading: isLoadingMore,
20745
- strings
20746
- }
20747
- )
20748
- ]
20749
- }
20750
- )
20751
- ] }),
21029
+ paginationMode === "infinite" && hasMore && !isLoading && /* @__PURE__ */ jsx(
21030
+ InfiniteScrollSentinel,
21031
+ {
21032
+ onIntersect: onLoadMore ?? NOOP,
21033
+ isLoading: isLoadingMore,
21034
+ rootRef: infiniteScrollRootRef,
21035
+ strings
21036
+ }
21037
+ )
21038
+ ] })
21039
+ }
21040
+ ),
20752
21041
  footer !== false && /* @__PURE__ */ jsxs("div", { className: "relative z-10 shrink-0 bg-transparent", children: [
20753
21042
  footer ? footer(footerCtx) : /* @__PURE__ */ jsx(
20754
21043
  DefaultFooter,
@@ -20788,7 +21077,8 @@ ${colorConfig.map(([key, itemConfig]) => {
20788
21077
  },
20789
21078
  hasMore: false,
20790
21079
  reload: () => {
20791
- }
21080
+ },
21081
+ error: null
20792
21082
  }), [processed]);
20793
21083
  }
20794
21084
  function useAsyncDataSource(opts) {
@@ -20806,6 +21096,7 @@ ${colorConfig.map(([key, itemConfig]) => {
20806
21096
  const [isRefetching, setIsRefetching] = (0, import_react31.useState)(false);
20807
21097
  const [isLoadingMore, setIsLoadingMore] = (0, import_react31.useState)(false);
20808
21098
  const [hasMore, setHasMore] = (0, import_react31.useState)(true);
21099
+ const [error, setError] = (0, import_react31.useState)(null);
20809
21100
  const cursorRef = (0, import_react31.useRef)(void 0);
20810
21101
  const abortRef = (0, import_react31.useRef)(null);
20811
21102
  const pageIndexRef = (0, import_react31.useRef)(0);
@@ -20844,6 +21135,7 @@ ${colorConfig.map(([key, itemConfig]) => {
20844
21135
  cursorRef.current = void 0;
20845
21136
  pageIndexRef.current = 0;
20846
21137
  }
21138
+ setError(null);
20847
21139
  try {
20848
21140
  const params = {
20849
21141
  sorting: currentSorting,
@@ -20878,6 +21170,7 @@ ${colorConfig.map(([key, itemConfig]) => {
20878
21170
  } catch (err) {
20879
21171
  if (controller.signal.aborted) return;
20880
21172
  console.error("[DataGrid] Data source error:", err);
21173
+ setError(err instanceof Error ? err : new Error(String(err)));
20881
21174
  } finally {
20882
21175
  if (!controller.signal.aborted) {
20883
21176
  setIsLoading(false);
@@ -20892,7 +21185,7 @@ ${colorConfig.map(([key, itemConfig]) => {
20892
21185
  fetchPage(false).catch(() => {
20893
21186
  });
20894
21187
  return () => abortRef.current?.abort();
20895
- }, [fetchPage, sortingKey, quickSearchKey, pagination.pageSize]);
21188
+ }, [fetchPage, dataSource, sortingKey, quickSearchKey, pagination.pageSize]);
20896
21189
  (0, import_react31.useEffect)(() => {
20897
21190
  if (paginationMode !== "server") {
20898
21191
  hasMountedServerPaginationRef.current = false;
@@ -20923,7 +21216,8 @@ ${colorConfig.map(([key, itemConfig]) => {
20923
21216
  isLoadingMore,
20924
21217
  loadMore,
20925
21218
  hasMore,
20926
- reload
21219
+ reload,
21220
+ error
20927
21221
  };
20928
21222
  }
20929
21223
  var NOOP_DATA_SOURCE = async function* () {
@@ -20942,6 +21236,16 @@ ${colorConfig.map(([key, itemConfig]) => {
20942
21236
  paginationMode
20943
21237
  } = opts;
20944
21238
  const isClientMode = data != null && !dataSource;
21239
+ if (false) {
21240
+ console.warn(
21241
+ "[useDataSource] neither `data` nor `dataSource` was provided \u2014 the grid will render empty indefinitely. Pass one or the other."
21242
+ );
21243
+ }
21244
+ if (false) {
21245
+ console.warn(
21246
+ '[useDataSource] `paginationMode: "infinite"` with a `data` array skips pagination entirely. Prefer `"client"` for in-memory lists or `"server"` + a `dataSource` generator for real paging.'
21247
+ );
21248
+ }
20945
21249
  const clientResult = useClientDataSource({
20946
21250
  data: data ?? [],
20947
21251
  columns,