Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/6349-batch7-plugin-grid.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@object-ui/plugin-grid': minor
---

The per-group aggregation type that `useGroupedData` takes is declared and exported as `GroupAggregationConfig` instead of `AggregationConfig` (objectui#6349, batch 7). `@object-ui/types` publishes a different `AggregationConfig`: the query-AST aggregation `DriverQueryConfig.aggregations` carries (`function`, `alias`, `distinct`, `separator`). The two shared only `field`, so one exported name stood for two contracts across the two packages.

**Type change, breaking for some consumers.** `AggregationConfig` is no longer exported from `@object-ui/plugin-grid`. Replace `import type { AggregationConfig } from '@object-ui/plugin-grid'` with `GroupAggregationConfig`; the members (`field`, `type`) are unchanged. The compiler names the replacement (TS2724, "Did you mean 'GroupAggregationConfig'?"). `AggregationType` and `AggregationResult` keep their names. The README's export list now names `GroupAggregationConfig`.

No runtime behaviour changes.
7 changes: 7 additions & 0 deletions .changeset/6349-batch7-plugin-kanban.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@object-ui/plugin-kanban': patch
---

The board implementation types its `conditionalFormatting` rules as `@object-ui/types`' `KanbanConditionalFormattingRule` by that name, instead of through a module-local alias named `ConditionalFormattingRule` (objectui#6349, batch 7). `@object-ui/types` publishes `ConditionalFormattingRule` for the grid's and list view's rule, so the alias put a second meaning behind that name. The alias was internal: this package's entry never exported it, so no import changes, and the rule type is the same.

No runtime behaviour changes.
9 changes: 9 additions & 0 deletions .changeset/6349-batch7-plugin-list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@object-ui/plugin-list': minor
---

The view switcher's views are typed as `@object-ui/core`'s `ListViewVisualization`, and this package no longer re-exports that union as `ViewType` (objectui#6349, batch 7). `@object-ui/types` publishes a different `ViewType`: the whole view-type vocabulary, which adds the `list` and `detail` categories to the nine visualizations the switcher draws. One exported name stood for two unions across the two packages.

**Type change, breaking for some consumers.** `ViewType` is no longer exported from `@object-ui/plugin-list` (TS2305 on import). Import `ListViewVisualization` from `@object-ui/core` instead; it is the same nine-member union. `ViewSwitcherProps` keeps its members, and `ViewSwitcherProps['currentView']` names the same union.

No runtime behaviour changes.
2 changes: 1 addition & 1 deletion packages/plugin-grid/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ import type {
GroupEntry,
UseGroupedDataResult,
AggregationType,
AggregationConfig,
GroupAggregationConfig,
AggregationResult,
CellRange,
UseCellClipboardOptions,
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-grid/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export type { ObjectGridComponentProps as ObjectGridProps } from './ObjectGrid';
export type { VirtualGridProps, VirtualGridColumn } from './VirtualGrid';
export type { InlineEditingProps } from './InlineEditing';
export type { ImportWizardProps, ImportResult } from './ImportWizard';
export type { GroupEntry, UseGroupedDataResult, AggregationType, AggregationConfig, AggregationResult } from './useGroupedData';
export type { GroupEntry, UseGroupedDataResult, AggregationType, GroupAggregationConfig, AggregationResult } from './useGroupedData';
export type { GroupRowProps } from './GroupRow';
export type { RowActionMenuProps } from './components/RowActionMenu';
export type { BulkActionBarProps } from './components/BulkActionBar';
Expand Down
21 changes: 15 additions & 6 deletions packages/plugin-grid/src/useGroupedData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,17 @@ import { deriveColumnSummary, type ListViewGroupHeaderRow } from '@objectstack/s
/** Supported aggregation function types. */
export type AggregationType = 'sum' | 'count' | 'avg' | 'min' | 'max' | 'count_distinct';

/** Describes a single aggregation to compute per group. */
export interface AggregationConfig {
/**
* Describes a single aggregation to compute per group — one entry of
* `ObjectGridSchema.aggregations`, drawn in that group's header.
*
* Not named `AggregationConfig`: `@object-ui/types` publishes that name for
* the query-AST aggregation `DriverQueryConfig.aggregations` carries
* (`function`, `alias`, `distinct`, `separator`), which `@object-ui/core`'s
* `QueryASTBuilder` compiles. The two share only `field`, so this one carries
* the narrower name (objectui#6349, batch 7).
*/
export interface GroupAggregationConfig {
/** The field to aggregate. */
field: string;
/** The aggregation function. */
Expand Down Expand Up @@ -180,7 +189,7 @@ function buildSegmentLabel(
*/
function computeAggregations(
rows: any[],
configs: AggregationConfig[],
configs: GroupAggregationConfig[],
): AggregationResult[] {
return configs.map(({ field, type }) => {
if (type === 'count_distinct') {
Expand Down Expand Up @@ -405,7 +414,7 @@ export function serverSummaryOf(type: AggregationType): (typeof SERVER_SUMMARY)[
*/
function readServerAggregations(
row: ListViewGroupHeaderRow,
configs: AggregationConfig[] | undefined,
configs: GroupAggregationConfig[] | undefined,
): AggregationResult[] {
if (!configs || configs.length === 0) return [];
const out: AggregationResult[] = [];
Expand Down Expand Up @@ -463,7 +472,7 @@ function sameGroupKey(a: unknown, b: unknown): boolean {
export function useGroupedData(
config: GroupingConfig | undefined,
data: any[],
aggregations?: AggregationConfig[],
aggregations?: GroupAggregationConfig[],
formatValue?: GroupValueFormatter,
server?: ServerGroupSource,
): UseGroupedDataResult {
Expand All @@ -481,7 +490,7 @@ export function useGroupedData(
export function useGroupedDataInOptionOrder(
config: GroupingConfig | undefined,
data: any[],
aggregations: AggregationConfig[] | undefined,
aggregations: GroupAggregationConfig[] | undefined,
formatValue: GroupValueFormatter | undefined,
server: ServerGroupSource | undefined,
optionRanks: GroupOptionRanks | undefined,
Expand Down
6 changes: 3 additions & 3 deletions packages/plugin-grid/src/useServerGrouping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,14 @@ import {
type ListViewGroupQuerySource,
} from '@objectstack/spec/ui';
import { isExpandableFieldType } from '@object-ui/core';
import { serverSummaryOf, type AggregationConfig, type UsableGroupingField } from './useGroupedData';
import { serverSummaryOf, type GroupAggregationConfig, type UsableGroupingField } from './useGroupedData';

/**
* `aggregations` as the summary columns the header query compiles from. An
* entry naming no field or an unknown type contributes no node, so it renders
* no number rather than a fabricated one.
*/
function summaryColumnsOf(aggregations: AggregationConfig[] | undefined): NonNullable<ListViewGroupQuerySource['columns']> {
function summaryColumnsOf(aggregations: GroupAggregationConfig[] | undefined): NonNullable<ListViewGroupQuerySource['columns']> {
const out: Array<{ field: string; summary: NonNullable<ReturnType<typeof serverSummaryOf>> }> = [];
for (const agg of aggregations ?? []) {
if (!agg || typeof agg.field !== 'string' || agg.field === '') continue;
Expand Down Expand Up @@ -126,7 +126,7 @@ export interface ServerGroupHeadersInput {
search?: string;
searchFields?: readonly string[];
/** `object-grid.aggregations` — the per-group numbers besides the count. */
aggregations?: AggregationConfig[];
aggregations?: GroupAggregationConfig[];
/** The object's field catalogue, for reference-typed grouping keys. */
objectFields?: Record<string, { type?: unknown; reference?: unknown; displayField?: unknown } | undefined>;
/** Any change re-asks the server (refresh, data invalidation). */
Expand Down
15 changes: 9 additions & 6 deletions packages/plugin-kanban/src/KanbanImpl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,11 @@ const SWIMLANE_AXIS_X_PADDING = 'px-2 pl-36 sm:pl-44'
export type { KanbanCard, KanbanColumn } from './types'

// Card formatting is the spec `{ condition, style }` CEL rule — the one dialect
// `object-kanban` declares since objectui#11522 — see @object-ui/types.
export type ConditionalFormattingRule = KanbanConditionalFormattingRule
// `object-kanban` declares since objectui#11522 — spelled by its own name,
// `KanbanConditionalFormattingRule` from @object-ui/types. This module used to
// alias it as `ConditionalFormattingRule`, a second meaning behind the name
// `@object-ui/types` gives the grid's and list view's rule (objectui#6349,
// batch 7); no package entry reached the alias.

export interface KanbanBoardProps {
columns: KanbanColumn[]
Expand All @@ -140,7 +143,7 @@ export interface KanbanBoardProps {
quickAdd?: boolean
onQuickAdd?: (columnId: string, title: string) => void
coverImageField?: string
conditionalFormatting?: ConditionalFormattingRule[]
conditionalFormatting?: KanbanConditionalFormattingRule[]
/** Object field definitions — see `getCardStyles` (objectui#3501). */
objectFields?: unknown
/** Field name for swimlane rows (2D grouping) */
Expand Down Expand Up @@ -169,7 +172,7 @@ export interface KanbanBoardProps {
// `current_user.*` conditions resolve here exactly as they do on grid rows.
function getCardStyles(
card: KanbanCard,
rules?: ConditionalFormattingRule[],
rules?: KanbanConditionalFormattingRule[],
scope?: Record<string, unknown>,
objectFields?: unknown,
): React.CSSProperties {
Expand All @@ -183,7 +186,7 @@ function getCardStyles(
return resolveConditionalFormatting(card as Record<string, unknown>, rules as any, scope, objectFields as never) as React.CSSProperties
}

function SortableCard({ card, onCardClick, conditionalFormatting, objectFields }: { card: KanbanCard; onCardClick?: (card: KanbanCard, event?: React.MouseEvent) => void; conditionalFormatting?: ConditionalFormattingRule[]; objectFields?: unknown }) {
function SortableCard({ card, onCardClick, conditionalFormatting, objectFields }: { card: KanbanCard; onCardClick?: (card: KanbanCard, event?: React.MouseEvent) => void; conditionalFormatting?: KanbanConditionalFormattingRule[]; objectFields?: unknown }) {
const {
attributes,
listeners,
Expand Down Expand Up @@ -547,7 +550,7 @@ function KanbanColumnView({
onCardClick?: (card: KanbanCard, event?: React.MouseEvent) => void
quickAdd?: boolean
onQuickAdd?: (columnId: string, title: string) => void
conditionalFormatting?: ConditionalFormattingRule[]
conditionalFormatting?: KanbanConditionalFormattingRule[]
/** Object field definitions — see `getCardStyles` (objectui#3501). */
objectFields?: unknown
/** Container-aware width override from useResizeObserver in KanbanBoardInner. */
Expand Down
26 changes: 13 additions & 13 deletions packages/plugin-list/src/ListView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type { SortItem } from '@object-ui/components';
import { Search, SlidersHorizontal, ArrowUpDown, X, EyeOff, Pencil, Group, Paintbrush, Inbox, Download, Rows4, Rows3, Rows2, Share2, Printer, Plus, Trash2, CheckSquare, AlertTriangle, ShieldAlert, RotateCw, Loader2, type LucideIcon } from 'lucide-react';
import type { FilterGroup } from '@object-ui/components';
import { VALUELESS_FILTER_BUILDER_OPERATORS, isFilterValueComplete } from '@object-ui/components';
import { ViewSwitcherDropdown, ViewType } from './ViewSwitcher';
import { ViewSwitcherDropdown } from './ViewSwitcher';
import { ViewSettingsPopover } from './components/ViewSettingsPopover';
import { UserFilters } from './UserFilters';
import { SchemaRenderer, useNavigationOverlay, classifyLoadError, usePredicateScope, useDataInvalidation, useFilterScope, useResolvedFilter } from '@object-ui/react';
Expand All @@ -21,7 +21,7 @@ import { useDensityMode, resolveInlineAriaProps } from '@object-ui/react';
import type { ListViewSchema, ObjectMapConfig } from '@object-ui/types';
import { detectStatusField, isSystemManagedField } from '@object-ui/types';
import { usePullToRefresh } from '@object-ui/mobile';
import { resolveConditionalFormatting, buildExpandFields, buildExportFileName, resolveEffectiveCrudAffordances, isObjectInlineEditable, partitionRowsByPredicate, normalizeListViewSchema, isListViewVisualization, rowHeightToDensityMode, mergeFilterNodes, FilterOperatorError, columnIdentity, collectPredicateFieldRefs, collectGroupingFieldRefs, listViewPredicates, PLATFORM_RECORD_COLUMNS, EXPANDABLE_FIELD_TYPES, UNMATERIALIZED_FIELD_TYPES, readObjectSortability, isPlatformSortableField, filterPlatformSortableSort } from '@object-ui/core';
import { type ListViewVisualization, resolveConditionalFormatting, buildExpandFields, buildExportFileName, resolveEffectiveCrudAffordances, isObjectInlineEditable, partitionRowsByPredicate, normalizeListViewSchema, isListViewVisualization, rowHeightToDensityMode, mergeFilterNodes, FilterOperatorError, columnIdentity, collectPredicateFieldRefs, collectGroupingFieldRefs, listViewPredicates, PLATFORM_RECORD_COLUMNS, EXPANDABLE_FIELD_TYPES, UNMATERIALIZED_FIELD_TYPES, readObjectSortability, isPlatformSortableField, filterPlatformSortableSort } from '@object-ui/core';
import { useObjectLabel, useSafeFieldLabel, createSafeTranslation, useDisplayLocale, pickLocalized } from '@object-ui/i18n';
// Two resolvers, two vocabularies — the repo spells the distinction into the
// NAMES (objectui#4167). `resolveInlineI18nLabel` is the spec's own
Expand Down Expand Up @@ -308,7 +308,7 @@ export interface ListViewProps {
* is a separate change with its own consumer sweep.
*/
dataSource?: any;
onViewChange?: (view: ViewType) => void;
onViewChange?: (view: ListViewVisualization) => void;
/**
* Fires with the advanced-filter group the toolbar's `FilterBuilder` emitted.
*
Expand Down Expand Up @@ -1350,8 +1350,8 @@ export const ListView = React.forwardRef<ListViewHandle, ListViewProps>(({

// Declared ahead of `toolbarFlags`, which reads it: which view is ON SCREEN
// decides whether a filter control has anything to reach (objectui#10327).
const [currentView, setCurrentView] = React.useState<ViewType>(
(schema.viewType as ViewType)
const [currentView, setCurrentView] = React.useState<ListViewVisualization>(
(schema.viewType as ListViewVisualization)
);

/**
Expand Down Expand Up @@ -3104,7 +3104,7 @@ export const ListView = React.forwardRef<ListViewHandle, ListViewProps>(({
// Capability-resolvable types: a visualization is only offered when its
// required field bindings resolve (ADR-0047) — kanban needs a groupBy,
// calendar a start date, etc. `grid` always renders.
const resolvable: ViewType[] = ['grid'];
const resolvable: ListViewVisualization[] = ['grid'];

// Check for Kanban capabilities (spec config takes precedence)
//
Expand Down Expand Up @@ -3187,7 +3187,7 @@ export const ListView = React.forwardRef<ListViewHandle, ListViewProps>(({
// undrawable kind (`page`) must NOT be pushed here: offering a switch into
// a branch that falls through to `grid` is the silent downgrade wearing a
// button.
if (schema.viewType && !resolvable.includes(schema.viewType as ViewType) &&
if (schema.viewType && !resolvable.includes(schema.viewType as ListViewVisualization) &&
isListViewVisualization(schema.viewType)) {
resolvable.push(schema.viewType);
}
Expand All @@ -3198,8 +3198,8 @@ export const ListView = React.forwardRef<ListViewHandle, ListViewProps>(({
// field renders garbage, so it must not be offered.
const whitelist = schema.appearance?.allowedVisualizations;
if (Array.isArray(whitelist) && whitelist.length > 0) {
const filtered = whitelist.filter((v: any) => resolvable.includes(v)) as ViewType[];
return filtered.length > 0 ? filtered : (['grid'] as ViewType[]);
const filtered = whitelist.filter((v: any) => resolvable.includes(v)) as ListViewVisualization[];
return filtered.length > 0 ? filtered : (['grid'] as ListViewVisualization[]);
}

return resolvable;
Expand Down Expand Up @@ -3231,7 +3231,7 @@ export const ListView = React.forwardRef<ListViewHandle, ListViewProps>(({
// Sync view from props
React.useEffect(() => {
if (schema.viewType) {
setCurrentView(schema.viewType as ViewType);
setCurrentView(schema.viewType as ListViewVisualization);
}
}, [schema.viewType]);

Expand All @@ -3240,16 +3240,16 @@ export const ListView = React.forwardRef<ListViewHandle, ListViewProps>(({
React.useEffect(() => {
try {
const savedView = localStorage.getItem(storageKey);
if (savedView && ['grid', 'kanban', 'calendar', 'timeline', 'gantt', 'map', 'gallery'].includes(savedView) && availableViews.includes(savedView as ViewType)) {
setCurrentView(savedView as ViewType);
if (savedView && ['grid', 'kanban', 'calendar', 'timeline', 'gantt', 'map', 'gallery'].includes(savedView) && availableViews.includes(savedView as ListViewVisualization)) {
setCurrentView(savedView as ListViewVisualization);
}
} catch (error) {
console.warn('Failed to load view preference from localStorage:', error);
}
}, [storageKey, availableViews]);
*/

const handleViewChange = React.useCallback((view: ViewType) => {
const handleViewChange = React.useCallback((view: ListViewVisualization) => {
setCurrentView(view);
try {
localStorage.setItem(storageKey, view);
Expand Down
Loading
Loading