@xnetjs/react 2.0.0 → 2.2.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.
Files changed (3) hide show
  1. package/dist/index.d.ts +75 -4
  2. package/dist/index.js +110 -40
  3. package/package.json +10 -10
package/dist/index.d.ts CHANGED
@@ -3,9 +3,9 @@ export { i as FlattenNodeOptions, M as MigrationWarning, c as QueryListResult, d
3
3
  export { ErrorBoundary, ErrorBoundaryProps, InfiniteQueryFilter, InfiniteQueryPage, InfiniteQueryResult, MutateCreate, MutateDelete, MutateOp, MutateRestore, MutateUpdate, OfflineIndicator, OfflineIndicatorProps, PresenceUser, SyncStatus, UseIdentityResult, UseMutateResult, UseNodeOptions, UseNodeResult, useIdentity, useInfiniteQuery, useIsOffline, useMutate, useNode } from './core.js';
4
4
  import { U as UseTaskProjectionSyncResult, T as TaskProjectionInput, a as TaskTreeItem } from './experimental-DyPoZcJT.js';
5
5
  export { A as AddCommentOptions, a0 as AddReactionOptions, bm as AuthErrorScreen, aP as AuthTraceSummary, bl as AuthenticatingScreen, r as CommentNode, C as CommentThread, F as CommentVisibility, ad as CreateMessageRequestOptions, b7 as DemoBanner, b8 as DemoBannerProps, bb as DemoDataExpiredScreen, be as DemoLimits, bd as DemoModeState, b9 as DemoQuotaIndicator, ba as DemoQuotaIndicatorProps, bf as DemoUsage, b2 as DiscoveredPeer, aU as FileRef, ae as FirstContactAdmission, af as FirstContactDecision, ag as FirstContactDecisionInput, G as FirstContactMode, ah as FirstContactVisibility, aK as GrantConsentSummary, aL as GrantInput, bp as HubConnectScreen, aX as HubSearchOptions, aY as HubSearchResult, aZ as HubSearchState, b3 as HubStatusIndicator, bo as ImportIdentityScreen, I as InteractionPermission, c8 as MergedEditorContributions, ai as MessageRequestNode, aj as MessageRequestProperties, ak as MessageRequestStatus, M as ModeratedCommentNode, H as ModeratedCommentThread, J as ModerationFilterOptions, K as ModerationLabelSummary, bA as OnboardingContextValue, bD as OnboardingEvent, bj as OnboardingFlow, bB as OnboardingFlowProps, bE as OnboardingMachineContext, bh as OnboardingProvider, bz as OnboardingProviderProps, bF as OnboardingReducerState, bC as OnboardingState, P as PageTaskInput, e as PageTaskReferenceInput, bI as PageTasksPanel, bJ as PageTasksPanelProps, bW as PluginRegistryContext, L as PublicInteractionMode, N as PublicInteractionPolicySnapshot, O as PublicInteractionSurface, Q as PublicModerationMode, bv as QUICK_START_TEMPLATES, bG as QuickStartTemplate, a1 as ReactionCounterSnapshot, a2 as ReactionNode, a3 as ReactionType, bq as ReadyScreen, a$ as RemoteSchemaDefinition, b0 as RemoteSchemaState, R as ReplyContext, bQ as SecurityContextActions, bP as SecurityContextState, bR as SecurityContextValue, bM as SecurityProvider, bS as SecurityProviderProps, b4 as Skeleton, b6 as SkeletonProps, br as SmartWelcome, bs as SyncProgressOverlay, bH as SyncProgressOverlayProps, bK as TaskCollectionEmbed, bL as TaskCollectionEmbedProps, j as TaskProjectionHost, i as TaskProjectionReferenceInput, bn as UnsupportedBrowserScreen, aw as UseAuditOptions, av as UseAuditResult, aQ as UseAuthTraceResult, aS as UseBackupReturn, aA as UseBlameResult, aH as UseCanEditResult, aF as UseCanResult, p as UseCommentsOptions, q as UseCommentsResult, ay as UseDiffResult, aV as UseFileUploadReturn, b as UseFindOptions, c as UseFindResult, aM as UseGrantsResult, aq as UseHistoryResult, al as UseMessageRequestsOptions, am as UseMessageRequestsResult, S as UseModeratedThreadOptions, f as UsePageTaskSyncOptions, g as UsePageTaskSyncResult, a4 as UsePolicyFilteredReactionCountersOptions, a5 as UsePolicyFilteredReactionCountersResult, bU as UseSecurityOptions, bV as UseSecurityResult, k as UseTaskProjectionSyncOptions, m as UseTasksOptions, n as UseTasksResult, at as UseUndoOptions, as as UseUndoResult, aC as UseVerificationResult, V as UseVisibleCommentsOptions, W as UseVisibleCommentsResult, bk as WelcomeScreen, by as copyToClipboard, a7 as createConversationKey, bu as createInitialState, a8 as createMessageRequestProperties, v as createModerationLabelIndex, Y as createReactionCounterSnapshot, Z as dedupeReactions, aI as describeGrantConsent, w as evaluateCommentModeration, a9 as evaluateFirstContactDecision, x as evaluateInteractionPermission, aa as findLatestMessageRequest, bw as getPlatformAuthName, ab as hasAcceptedContact, b5 as injectSkeletonStyles, _ as isReactionVisible, c7 as mergeEditorContributions, y as moderateThread, bt as onboardingReducer, z as selectActiveInteractionPolicy, B as selectPublicInteractionMode, aN as summarizeAuthTrace, ac as summarizeMessageRequest, D as summarizeModerationLabel, E as summarizePublicInteractionPolicy, $ as summarizeReactionNode, bx as truncateDid, au as useAudit, aO as useAuthTrace, aR as useBackup, az as useBlame, aE as useCan, aG as useCanEdit, ca as useCommand, c0 as useCommands, an as useCommentCount, ao as useCommentCounts, o as useComments, b_ as useContributions, bc as useDemoMode, ax as useDiff, c4 as useEditorExtensions, c5 as useEditorExtensionsSafe, aT as useFileUpload, u as useFind, aJ as useGrants, ap as useHistory, aW as useHubSearch, aD as useHubStatus, c3 as useImporters, c6 as useMergedEditorContributions, a6 as useMessageRequests, t as useModeratedThread, bi as useOnboarding, d as usePageTaskSync, b1 as usePeerDiscovery, bX as usePluginRegistry, bY as usePluginRegistryOptional, bZ as usePlugins, X as usePolicyFilteredReactionCounters, a_ as useRemoteSchema, bT as useSecurity, bN as useSecurityContext, bO as useSecurityContextOptional, c2 as useSidebarItems, c1 as useSlashCommands, bg as useSyncManager, h as useTaskProjectionSync, l as useTasks, ar as useUndo, aB as useVerification, c9 as useView, b$ as useViews, s as useVisibleComments } from './experimental-DyPoZcJT.js';
6
- import { SchemaIRI, Schema, SavedViewDescriptor, DefinedSchema, PropertyBuilder, QueryASTOrderBy, QueryASTPage, QueryASTValidationResult, QueryASTPlannerGate, QueryASTAggregateExecution, ExternalReferenceProvider, SavedViewFeedLayout, SavedViewFeedDensity, FieldType, FieldConfig, ViewType, FilterGroup, SortConfig, RowHeight, SummaryFunction, FormViewConfig, FormFieldRule, CellValue, FormSubmissionMeta, NodeId, NodeState } from '@xnetjs/data';
6
+ import { SchemaIRI, Schema, SavedViewDescriptor, DefinedSchema, PropertyBuilder, QueryASTOrderBy, QueryASTPage, QueryASTValidationResult, QueryASTPlannerGate, QueryASTAggregateExecution, ExternalReferenceProvider, SavedViewFeedLayout, SavedViewFeedDensity, FieldType, FieldConfig, ViewType, FilterGroup, SortConfig, RowHeight, SummaryFunction, FormViewConfig, FormFieldRule, ViewGroupMeta, MapViewport, CellValue, FormSubmissionMeta, NodeId, NodeState } from '@xnetjs/data';
7
7
  export { ColumnConfig, ColumnDefinition, ColumnType, SavedViewFeedDensity, SavedViewFeedLayout, ViewConfig, ViewType } from '@xnetjs/data';
8
- import { QueryExecutionMode, QuerySourcePreference, QuerySearchFilter, QueryPageInfo, QueryMetadata } from '@xnetjs/data-bridge';
8
+ import { QueryExecutionMode, QuerySourcePreference, QuerySearchFilter, QueryPageInfo, QueryMetadata, QuerySpatialFilter } from '@xnetjs/data-bridge';
9
9
  import { LucideIcon } from 'lucide-react';
10
10
  import { ReactNode, JSX } from 'react';
11
11
  export { DatabaseRow, DatabaseRowData, ReverseRelation, UseCellOptions, UseCellResult, UseDatabaseDocResult, UseDatabaseOptions, UseDatabaseResult, UseDatabaseRowResult, UseDatabaseSchemaResult, UseRelatedRowsResult, UseReverseRelationsResult, useCell, useDatabase, useDatabaseDoc, useDatabaseRow, useDatabaseSchema, useRelatedRows, useReverseRelations } from './database.js';
@@ -563,6 +563,35 @@ interface GridViewModel {
563
563
  formConfig: FormViewConfig | null;
564
564
  formRules: Record<string, FormFieldRule>;
565
565
  formAccepting: boolean;
566
+ coverField: string | null;
567
+ cardSize: string | null;
568
+ coverFit: string | null;
569
+ colorBy: string | null;
570
+ groupMeta: Record<string, ViewGroupMeta>;
571
+ dateField: string | null;
572
+ endDateField: string | null;
573
+ latField: string | null;
574
+ lngField: string | null;
575
+ mapViewport: MapViewport | null;
576
+ }
577
+ /**
578
+ * The per-view presentation config a view component may patch through
579
+ * `setViewConfig` (exploration 0337). One node write per patch — each
580
+ * property merges independently (LWW) with other clients' edits.
581
+ */
582
+ interface GridViewConfigPatch {
583
+ groupBy?: string | null;
584
+ collapsedGroups?: string[];
585
+ coverField?: string | null;
586
+ cardSize?: string | null;
587
+ coverFit?: string | null;
588
+ colorBy?: string | null;
589
+ groupMeta?: Record<string, ViewGroupMeta>;
590
+ dateField?: string | null;
591
+ endDateField?: string | null;
592
+ latField?: string | null;
593
+ lngField?: string | null;
594
+ mapViewport?: MapViewport | null;
566
595
  }
567
596
  interface GridRowModel {
568
597
  id: string;
@@ -581,8 +610,21 @@ interface UseGridDatabaseOptions {
581
610
  viewId?: string;
582
611
  /** Quick-find text (full-text search via SQLite FTS) */
583
612
  search?: string;
584
- /** Row window size (default 500) */
613
+ /** Rows fetched per window grow (default 500) */
585
614
  pageSize?: number;
615
+ /**
616
+ * Ceiling on the live row window (default 2000). The window grows by
617
+ * `pageSize` via `fetchMoreRows` until this bound; beyond it further
618
+ * fetches are no-ops so the overfetch buffer and live snapshot stay
619
+ * bounded (exploration 0340).
620
+ */
621
+ maxLoaded?: number;
622
+ /**
623
+ * Spatial window over two cell properties (map views, exploration
624
+ * 0337): only rows inside the rect are fetched. Bypasses the
625
+ * materialized-view cache (the rect varies per pan).
626
+ */
627
+ spatial?: QuerySpatialFilter;
586
628
  }
587
629
  interface UseGridDatabaseResult {
588
630
  /** Database node (flattened) */
@@ -600,8 +642,33 @@ interface UseGridDatabaseResult {
600
642
  activeView: GridViewModel | null;
601
643
  /** Rows: view filters + sorts applied to the fetched window */
602
644
  rows: GridRowModel[];
645
+ /**
646
+ * The fetch window (exploration 0337): `size` is the row cap, `total`
647
+ * the full match count when the bridge reports one. Views use this to
648
+ * label truncation honestly instead of silently clipping.
649
+ */
650
+ rowWindow: {
651
+ size: number;
652
+ total: number | null;
653
+ };
603
654
  loading: boolean;
655
+ /** Exact matching row count for the whole database (null while unknown). */
656
+ totalRowCount: number | null;
657
+ /** Whether the row window can grow further (more rows exist past it). */
658
+ hasMoreRows: boolean;
659
+ /** Whether a window grow is awaiting the larger read. */
660
+ isFetchingMoreRows: boolean;
661
+ /** Grow the row window by `pageSize` (bounded by `maxLoaded`). */
662
+ fetchMoreRows: () => Promise<void>;
604
663
  updateCell: (rowId: string, fieldId: string, value: CellValue) => Promise<void>;
664
+ /**
665
+ * Write several cells (and optionally the row's sortKey) as ONE node
666
+ * update — a kanban card move is exactly one write carrying the group
667
+ * cell + the fractional position (exploration 0337).
668
+ */
669
+ updateRowCells: (rowId: string, cells: Record<string, CellValue>, opts?: {
670
+ sortKey?: string;
671
+ }) => Promise<void>;
605
672
  clearCells: (cells: Array<{
606
673
  rowId: string;
607
674
  fieldId: string;
@@ -624,6 +691,10 @@ interface UseGridDatabaseResult {
624
691
  toggleSort: (fieldId: string) => Promise<void>;
625
692
  setFilters: (filters: FilterGroup | null) => Promise<void>;
626
693
  setGroupBy: (fieldId: string | null) => Promise<void>;
694
+ /** Patch the active view's presentation config (exploration 0337) */
695
+ setViewConfig: (patch: GridViewConfigPatch) => Promise<void>;
696
+ /** Persist a group's collapsed state on the active view */
697
+ setGroupCollapsed: (groupKey: string, collapsed: boolean) => Promise<void>;
627
698
  setRowHeight: (rowHeight: RowHeight) => Promise<void>;
628
699
  setColumnSummary: (fieldId: string, fn: SummaryFunction) => Promise<void>;
629
700
  setFormConfig: (config: FormViewConfig) => Promise<void>;
@@ -918,4 +989,4 @@ declare function flattenTaskTree(items: TaskTreeItem[], depth?: number): Rendera
918
989
  declare function formatTaskDueDate(timestamp: number | undefined): string | null;
919
990
  declare function isTaskOverdue(timestamp: number | undefined, completed: boolean): boolean;
920
991
 
921
- export { type AddRowOptions, type CanvasTaskInput, type CheckoutOptions, type DraftReview, type DraftReviewCard, type DraftReviewMember, FlatNode, type GridFieldModel, type GridOptionModel, type GridRowModel, type GridViewModel, type PresenceAwareness, type PresencePeer, QueryFilter, type RenderableTaskRow, type SavedViewCanvasProjectionNode, type SavedViewDateBrushSelection, type SavedViewDateBucketFieldSummary, type SavedViewDateBucketInterval, type SavedViewDateBucketSummary, type SavedViewFacetSelection, type SavedViewFacetSummary, type SavedViewFacetValueSummary, type SavedViewFeedEnrichmentAdapter, type SavedViewFeedEnrichmentEntry, type SavedViewInspectorItem, type SavedViewInspectorItemKind, type SavedViewLensDraft, type SavedViewPresentationMode, type SavedViewPrivacyChip, type SavedViewPrivacyChipTone, type SavedViewPrivacySummary, type SavedViewQueryOverride, type SavedViewQueryResult, SavedViewResultTable, type SavedViewResultTableProps, type SavedViewRowInspectorModel, SavedViewRunner, type SavedViewRunnerProps, type SavedViewSchemaRegistry, type SavedViewSortDirection, type SavedViewVisualCanvasProjectionRequest, SavedViewVisualFeed, type SavedViewVisualLayoutId, type SavedViewVisualLayoutOption, type SavedViewVisualPreviewCreator, type SavedViewVisualPreviewKind, type SavedViewVisualPreviewModel, type SavedViewVisualPreviewPrivacy, type SavedViewVisualPreviewRelationship, type SavedViewVisualTimelineBucket, type SavedViewVisualWorkspaceLayout, TaskProjectionInput, TaskTreeItem, type UseBillingResult, type UseCanCreateResult, type UseCanvasTaskSyncOptions, type UseCanvasTaskSyncResult, type UseDraftResult, type UseEffectiveSchemaResult, type UseGlobalUndoResult, type UseGridDatabaseOptions, type UseGridDatabaseResult, type UsePresenceOptions, type UsePresenceResult, type UseSavedViewOptions, type UseSavedViewResult, UseTaskProjectionSyncResult, type UseTimeMachineOptions, type UseTimeMachineResult, createSavedViewCanvasProjectionNodes, createSavedViewLensDraft, createSavedViewVisualCanvasProjectionRequest, createSavedViewVisualPreviewFingerprint, deriveCachedSavedViewVisualPreviews, deriveSavedViewColumns, deriveSavedViewDateBucketSummaries, deriveSavedViewFacetSummaries, deriveSavedViewPrivacyChips, deriveSavedViewRowInspector, deriveSavedViewTimelineBuckets, deriveSavedViewVisualPreview, deriveSavedViewVisualPreviews, filterSavedViewRowsByDateBrush, filterSavedViewRowsByFacets, flattenTaskTree, formatSavedViewCellValue, formatTaskDueDate, getSavedViewSensitiveResultWarning, hasSavedViewVisualPreviewSensitiveData, isSavedViewVisualPreviewEmbeddable, isTaskOverdue, mergeSavedViewFeedEnrichment, savedViewVisualPreviewIsSelfActor, useBilling, useCanCreate, useCanvasTaskSync, useDraft, useEffectiveSchema, useGlobalUndo, useGridDatabase, usePresence, useSavedView, useTimeMachine };
992
+ export { type AddRowOptions, type CanvasTaskInput, type CheckoutOptions, type DraftReview, type DraftReviewCard, type DraftReviewMember, FlatNode, type GridFieldModel, type GridOptionModel, type GridRowModel, type GridViewConfigPatch, type GridViewModel, type PresenceAwareness, type PresencePeer, QueryFilter, type RenderableTaskRow, type SavedViewCanvasProjectionNode, type SavedViewDateBrushSelection, type SavedViewDateBucketFieldSummary, type SavedViewDateBucketInterval, type SavedViewDateBucketSummary, type SavedViewFacetSelection, type SavedViewFacetSummary, type SavedViewFacetValueSummary, type SavedViewFeedEnrichmentAdapter, type SavedViewFeedEnrichmentEntry, type SavedViewInspectorItem, type SavedViewInspectorItemKind, type SavedViewLensDraft, type SavedViewPresentationMode, type SavedViewPrivacyChip, type SavedViewPrivacyChipTone, type SavedViewPrivacySummary, type SavedViewQueryOverride, type SavedViewQueryResult, SavedViewResultTable, type SavedViewResultTableProps, type SavedViewRowInspectorModel, SavedViewRunner, type SavedViewRunnerProps, type SavedViewSchemaRegistry, type SavedViewSortDirection, type SavedViewVisualCanvasProjectionRequest, SavedViewVisualFeed, type SavedViewVisualLayoutId, type SavedViewVisualLayoutOption, type SavedViewVisualPreviewCreator, type SavedViewVisualPreviewKind, type SavedViewVisualPreviewModel, type SavedViewVisualPreviewPrivacy, type SavedViewVisualPreviewRelationship, type SavedViewVisualTimelineBucket, type SavedViewVisualWorkspaceLayout, TaskProjectionInput, TaskTreeItem, type UseBillingResult, type UseCanCreateResult, type UseCanvasTaskSyncOptions, type UseCanvasTaskSyncResult, type UseDraftResult, type UseEffectiveSchemaResult, type UseGlobalUndoResult, type UseGridDatabaseOptions, type UseGridDatabaseResult, type UsePresenceOptions, type UsePresenceResult, type UseSavedViewOptions, type UseSavedViewResult, UseTaskProjectionSyncResult, type UseTimeMachineOptions, type UseTimeMachineResult, createSavedViewCanvasProjectionNodes, createSavedViewLensDraft, createSavedViewVisualCanvasProjectionRequest, createSavedViewVisualPreviewFingerprint, deriveCachedSavedViewVisualPreviews, deriveSavedViewColumns, deriveSavedViewDateBucketSummaries, deriveSavedViewFacetSummaries, deriveSavedViewPrivacyChips, deriveSavedViewRowInspector, deriveSavedViewTimelineBuckets, deriveSavedViewVisualPreview, deriveSavedViewVisualPreviews, filterSavedViewRowsByDateBrush, filterSavedViewRowsByFacets, flattenTaskTree, formatSavedViewCellValue, formatTaskDueDate, getSavedViewSensitiveResultWarning, hasSavedViewVisualPreviewSensitiveData, isSavedViewVisualPreviewEmbeddable, isTaskOverdue, mergeSavedViewFeedEnrichment, savedViewVisualPreviewIsSelfActor, useBilling, useCanCreate, useCanvasTaskSync, useDraft, useEffectiveSchema, useGlobalUndo, useGridDatabase, usePresence, useSavedView, useTimeMachine };
package/dist/index.js CHANGED
@@ -4355,7 +4355,17 @@ function toViewModel(node) {
4355
4355
  sortKey: node.sortKey ?? "",
4356
4356
  formConfig: node.formConfig ?? null,
4357
4357
  formRules: node.formRules ?? {},
4358
- formAccepting: node.formAccepting ?? true
4358
+ formAccepting: node.formAccepting ?? true,
4359
+ coverField: node.coverField ?? null,
4360
+ cardSize: node.cardSize ?? null,
4361
+ coverFit: node.coverFit ?? null,
4362
+ colorBy: node.colorBy ?? null,
4363
+ groupMeta: node.groupMeta ?? {},
4364
+ dateField: node.dateField ?? null,
4365
+ endDateField: node.endDateField ?? null,
4366
+ latField: node.latField ?? null,
4367
+ lngField: node.lngField ?? null,
4368
+ mapViewport: node.mapViewport ?? null
4359
4369
  };
4360
4370
  }
4361
4371
  function fieldsToColumnDefinitions(fields) {
@@ -4377,27 +4387,39 @@ function dropSortKey(ordered, movedId, targetIndex) {
4377
4387
  }
4378
4388
  var formulaService = new FormulaService();
4379
4389
  function useGridDatabase(databaseId, options = {}) {
4380
- const { viewId, search, pageSize = 500 } = options;
4390
+ const { viewId, search, pageSize = 500, maxLoaded = 2e3, spatial } = options;
4381
4391
  const mutate = useMutate();
4382
4392
  const { did } = useIdentity();
4383
4393
  const { data: database, status: dbStatus } = useQuery(DatabaseSchema, databaseId);
4384
4394
  const { data: fieldNodes, status: fieldStatus } = useQuery(DatabaseFieldSchema, {
4385
4395
  where: { database: databaseId },
4386
- orderBy: { sortKey: "asc" }
4396
+ orderBy: { createdAt: "asc" }
4387
4397
  });
4388
4398
  const { data: viewNodes, status: viewStatus } = useQuery(DatabaseViewSchema, {
4389
4399
  where: { database: databaseId },
4390
- orderBy: { sortKey: "asc" }
4400
+ orderBy: { createdAt: "asc" }
4391
4401
  });
4392
4402
  const { data: optionNodes } = useQuery(DatabaseSelectOptionSchema, {
4393
4403
  where: { database: databaseId },
4394
- orderBy: { sortKey: "asc" }
4404
+ orderBy: { createdAt: "asc" }
4395
4405
  });
4396
- const { data: rowNodes, status: rowStatus } = useQuery(DatabaseRowSchema, {
4406
+ const {
4407
+ data: rowNodes,
4408
+ // Masked while a window grow is in flight (previous rows stay visible):
4409
+ // consumers must not fall back to a skeleton mid-grow or scroll position
4410
+ // is lost — `isFetchingMoreRows` reports the in-flight grow instead.
4411
+ loading: rowsLoading,
4412
+ totalCount: totalRowCount,
4413
+ hasMore: hasMoreRows,
4414
+ isFetchingNextPage: isFetchingMoreRows,
4415
+ fetchNextPage: fetchMoreRows
4416
+ } = useInfiniteQuery(DatabaseRowSchema, {
4397
4417
  where: { database: databaseId },
4398
4418
  orderBy: { sortKey: "asc" },
4399
- limit: pageSize,
4400
- materializedView: `db:${databaseId}${viewId ? `:view:${viewId}` : ""}`,
4419
+ pageSize,
4420
+ maxLoaded,
4421
+ page: { count: "exact" },
4422
+ ...spatial ? { spatial } : { materializedView: `db:${databaseId}${viewId ? `:view:${viewId}` : ""}` },
4401
4423
  ...search ? { search } : {}
4402
4424
  });
4403
4425
  const optionsByField = useMemo5(() => {
@@ -4484,11 +4506,22 @@ function useGridDatabase(databaseId, options = {}) {
4484
4506
  cancelled = true;
4485
4507
  };
4486
4508
  }, [rowNodes, fields, store]);
4509
+ const rowModelCacheRef = useRef4(
4510
+ { deps: [], map: /* @__PURE__ */ new WeakMap() }
4511
+ );
4487
4512
  const rows = useMemo5(() => {
4513
+ const cacheDeps = [fields, rollupValues, databaseId];
4514
+ if (cacheDeps.length !== rowModelCacheRef.current.deps.length || cacheDeps.some((d, i) => d !== rowModelCacheRef.current.deps[i])) {
4515
+ rowModelCacheRef.current = { deps: cacheDeps, map: /* @__PURE__ */ new WeakMap() };
4516
+ }
4517
+ const cache = rowModelCacheRef.current.map;
4488
4518
  const autoFields = fields.filter(
4489
4519
  (f) => ["created", "createdBy", "updated", "updatedBy"].includes(f.type)
4490
4520
  );
4491
- const base = (rowNodes ?? []).map((node) => {
4521
+ const formulaFields = fields.filter((f) => f.type === "formula");
4522
+ const rollupFields = fields.filter((f) => f.type === "rollup");
4523
+ const columnDefs = formulaFields.length > 0 || activeView ? fieldsToColumnDefinitions(fields) : [];
4524
+ const buildRow = (node) => {
4492
4525
  const cells = fromCellProperties(node);
4493
4526
  for (const field of autoFields) {
4494
4527
  switch (field.type) {
@@ -4506,47 +4539,43 @@ function useGridDatabase(databaseId, options = {}) {
4506
4539
  break;
4507
4540
  }
4508
4541
  }
4509
- return {
4542
+ const row = {
4510
4543
  id: node.id,
4511
4544
  sortKey: node.sortKey ?? "",
4512
4545
  cells
4513
4546
  };
4514
- });
4515
- const formulaFields = fields.filter((f) => f.type === "formula");
4516
- if (formulaFields.length > 0) {
4517
- const columnDefs = fieldsToColumnDefinitions(fields);
4518
- for (const row of base) {
4519
- for (const field of formulaFields) {
4520
- const columnDef = columnDefs.find((c) => c.id === field.id);
4521
- if (!columnDef) continue;
4522
- try {
4523
- row.cells[field.id] = formulaService.compute(
4524
- { id: row.id, databaseId, cells: row.cells },
4525
- columnDef,
4526
- columnDefs
4527
- );
4528
- } catch {
4529
- row.cells[field.id] = null;
4530
- }
4547
+ for (const field of formulaFields) {
4548
+ const columnDef = columnDefs.find((c) => c.id === field.id);
4549
+ if (!columnDef) continue;
4550
+ try {
4551
+ row.cells[field.id] = formulaService.compute(
4552
+ { id: row.id, databaseId, cells: row.cells },
4553
+ columnDef,
4554
+ columnDefs
4555
+ );
4556
+ } catch {
4557
+ row.cells[field.id] = null;
4531
4558
  }
4532
4559
  }
4533
- }
4534
- if (rollupValues.size > 0) {
4535
- for (const row of base) {
4536
- for (const field of fields) {
4537
- if (field.type !== "rollup") continue;
4538
- const computed = rollupValues.get(`${row.id}:${field.id}`);
4539
- if (computed !== void 0) row.cells[field.id] = computed;
4540
- }
4560
+ for (const field of rollupFields) {
4561
+ const computed = rollupValues.get(`${row.id}:${field.id}`);
4562
+ if (computed !== void 0) row.cells[field.id] = computed;
4541
4563
  }
4542
- }
4564
+ return row;
4565
+ };
4566
+ const base = (rowNodes ?? []).map((node) => {
4567
+ const hit = cache.get(node);
4568
+ if (hit) return hit;
4569
+ const row = buildRow(node);
4570
+ cache.set(node, row);
4571
+ return row;
4572
+ });
4543
4573
  base.sort((a, b) => compareSortKeys(a.sortKey, b.sortKey));
4544
4574
  if (!activeView) return base;
4545
- const columns = fieldsToColumnDefinitions(fields);
4546
- const filtered = filterRows(base, columns, activeView.filters);
4547
- return sortRows(filtered, columns, activeView.sorts);
4575
+ const filtered = filterRows(base, columnDefs, activeView.filters);
4576
+ return sortRows(filtered, columnDefs, activeView.sorts);
4548
4577
  }, [rowNodes, activeView, fields, databaseId, rollupValues]);
4549
- const loading = dbStatus === "loading" || fieldStatus === "loading" || viewStatus === "loading" || rowStatus === "loading";
4578
+ const loading = dbStatus === "loading" || fieldStatus === "loading" || viewStatus === "loading" || rowsLoading;
4550
4579
  const rowTailRef = useRef4(void 0);
4551
4580
  const fieldTailRef = useRef4(void 0);
4552
4581
  const viewTailRef = useRef4(void 0);
@@ -4611,6 +4640,17 @@ function useGridDatabase(databaseId, options = {}) {
4611
4640
  },
4612
4641
  [updateRowProps]
4613
4642
  );
4643
+ const updateRowCells = useCallback3(
4644
+ async (rowId, cells, opts) => {
4645
+ const props = {};
4646
+ for (const [fieldId, value] of Object.entries(cells)) {
4647
+ props[cellKey(fieldId)] = value;
4648
+ }
4649
+ if (opts?.sortKey !== void 0) props.sortKey = opts.sortKey;
4650
+ await updateRowProps(rowId, props);
4651
+ },
4652
+ [updateRowProps]
4653
+ );
4614
4654
  const clearCells = useCallback3(
4615
4655
  async (cells) => {
4616
4656
  const byRow = /* @__PURE__ */ new Map();
@@ -4829,6 +4869,28 @@ function useGridDatabase(databaseId, options = {}) {
4829
4869
  },
4830
4870
  [mutate, activeView]
4831
4871
  );
4872
+ const setViewConfig = useCallback3(
4873
+ async (patch) => {
4874
+ if (!activeView) return;
4875
+ const props = {};
4876
+ for (const [key, value] of Object.entries(patch)) {
4877
+ if (value !== void 0) props[key] = value;
4878
+ }
4879
+ if (Object.keys(props).length === 0) return;
4880
+ await mutate.update(DatabaseViewSchema, activeView.id, props);
4881
+ },
4882
+ [mutate, activeView]
4883
+ );
4884
+ const setGroupCollapsed = useCallback3(
4885
+ async (groupKey, collapsed) => {
4886
+ if (!activeView) return;
4887
+ const set = new Set(activeView.collapsedGroups);
4888
+ if (collapsed) set.add(groupKey);
4889
+ else set.delete(groupKey);
4890
+ await mutate.update(DatabaseViewSchema, activeView.id, { collapsedGroups: [...set] });
4891
+ },
4892
+ [mutate, activeView]
4893
+ );
4832
4894
  const setRowHeight = useCallback3(
4833
4895
  async (rowHeight) => {
4834
4896
  if (!activeView) return;
@@ -4898,8 +4960,14 @@ function useGridDatabase(databaseId, options = {}) {
4898
4960
  views,
4899
4961
  activeView,
4900
4962
  rows,
4963
+ rowWindow: { size: (rowNodes ?? []).length, total: totalRowCount ?? null },
4901
4964
  loading,
4965
+ totalRowCount,
4966
+ hasMoreRows,
4967
+ isFetchingMoreRows,
4968
+ fetchMoreRows,
4902
4969
  updateCell,
4970
+ updateRowCells,
4903
4971
  clearCells,
4904
4972
  addRow,
4905
4973
  deleteRows,
@@ -4916,6 +4984,8 @@ function useGridDatabase(databaseId, options = {}) {
4916
4984
  toggleSort,
4917
4985
  setFilters,
4918
4986
  setGroupBy,
4987
+ setViewConfig,
4988
+ setGroupCollapsed,
4919
4989
  setRowHeight,
4920
4990
  setColumnSummary,
4921
4991
  setFormConfig,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xnetjs/react",
3
- "version": "2.0.0",
3
+ "version": "2.2.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -46,15 +46,15 @@
46
46
  "y-protocols": "^1.0.6",
47
47
  "yjs": "^13.6.24",
48
48
  "@xnetjs/billing": "0.0.2",
49
- "@xnetjs/core": "2.0.0",
50
- "@xnetjs/crypto": "2.0.0",
51
- "@xnetjs/data": "2.0.0",
52
- "@xnetjs/data-bridge": "2.0.0",
53
- "@xnetjs/history": "2.0.0",
54
- "@xnetjs/identity": "2.0.0",
55
- "@xnetjs/plugins": "2.0.0",
56
- "@xnetjs/runtime": "0.5.0",
57
- "@xnetjs/sync": "2.0.0"
49
+ "@xnetjs/core": "2.2.0",
50
+ "@xnetjs/crypto": "2.2.0",
51
+ "@xnetjs/data": "2.2.0",
52
+ "@xnetjs/data-bridge": "2.2.0",
53
+ "@xnetjs/history": "2.2.0",
54
+ "@xnetjs/identity": "2.2.0",
55
+ "@xnetjs/plugins": "2.2.0",
56
+ "@xnetjs/runtime": "0.5.2",
57
+ "@xnetjs/sync": "2.2.0"
58
58
  },
59
59
  "peerDependencies": {
60
60
  "react": "^18.0.0 || ^19.0.0"