Skip to content

Commit dbdfa18

Browse files
matej21claude
andauthored
Fixes (#6)
* fix(bindx): support has-many relations in PlaceholderHandle PlaceholderHandle now accepts SchemaRegistry to detect has-many relations and returns an empty has-many-like handle (items/map/length/iterator) instead of a plain field ref. HasOneHandle passes schema to placeholder. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(bindx-repeater): collect field selections from block render/form functions BlockDefinition now supports optional render/form functions that declare block-specific field selections. Added defensive Array.isArray guards in useSortedItems and sortEntities for placeholder has-many handles. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(bindx): simplify handle type system from 16 to 8 types Ref/Accessor split: Ref = pointer (no .value/.items), Accessor = live data (extends Ref). Renames: - FieldRefBase → FieldRef, FieldRef → FieldAccessor - HasManyRefBase → HasManyRef, HasManyRef → HasManyAccessor - HasOneRefBase/HasOneAccessorBase → HasOneRef, HasOneAccessor stays - EntityRefBase/EntityAccessorBase → EntityRef, EntityAccessor stays - SelectedEntityFieldsBase → EntityFieldsRef - SelectedEntityFields → EntityFieldsAccessor New hooks (useField, useHasMany, useHasOne) bridge Ref → Accessor with store subscriptions. Consumer components updated to accept Ref props and use hooks internally for data access. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent aeea0eb commit dbdfa18

75 files changed

Lines changed: 776 additions & 779 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/bindx-dataview/src/columnLeaf.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*/
99

1010
import React from 'react'
11-
import type { FieldRefBase, FilterHandler, FilterArtifact, EntityAccessor } from '@contember/bindx'
11+
import type { FieldRef, FilterHandler, FilterArtifact, EntityAccessor } from '@contember/bindx'
1212

1313
// ============================================================================
1414
// ColumnLeaf Props — the new ColumnMeta
@@ -25,7 +25,7 @@ export interface ColumnLeafProps {
2525
// ── Core ──
2626
readonly name: string
2727
readonly fieldName: string | null
28-
readonly fieldRef: FieldRefBase<unknown> | null
28+
readonly fieldRef: FieldRef<unknown> | null
2929
readonly sortingField: string | null
3030
readonly filterName: string | null
3131
readonly filterHandler: FilterHandler<FilterArtifact> | undefined

packages/bindx-dataview/src/columns.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
*/
1111

1212
import React, { ReactNode } from 'react'
13-
import type { FieldRefBase, HasOneRef, HasManyRef, FilterHandler, FilterArtifact, EntityAccessor, EnumFilterArtifact, EnumListFilterArtifact, SelectionMeta } from '@contember/bindx'
13+
import type { FieldRef, HasOneRef, HasManyRef, FilterHandler, FilterArtifact, EntityAccessor, EnumFilterArtifact, EnumListFilterArtifact, SelectionMeta } from '@contember/bindx'
1414
import { SelectionScope } from '@contember/bindx'
1515
import { FIELD_REF_META, createCollectorProxy } from '@contember/bindx-react'
1616
import { createColumn, type ColumnRenderProps } from './createColumn.js'
@@ -114,7 +114,7 @@ function renderEnumListDefault({ value }: ColumnRenderProps<readonly string[] |
114114
// ============================================================================
115115

116116
interface DataGridScalarColumnPropsBase<T> {
117-
field: FieldRefBase<T> | FieldRefBase<T | undefined>
117+
field: FieldRef<T> | FieldRef<T | undefined>
118118
header?: React.ReactNode
119119
sortable?: boolean
120120
filter?: boolean
@@ -256,7 +256,7 @@ export const DataGridActionColumn = Object.assign(
256256
// ============================================================================
257257

258258
export interface DataGridColumnProps<T> {
259-
field?: FieldRefBase<T>
259+
field?: FieldRef<T>
260260
header?: React.ReactNode
261261
sortable?: boolean
262262
filter?: boolean
@@ -270,7 +270,7 @@ export const DataGridColumn = Object.assign(
270270
},
271271
{
272272
staticRender: (props: Record<string, unknown>): React.ReactNode => {
273-
const fieldRef = props['field'] as FieldRefBase<unknown> | undefined
273+
const fieldRef = props['field'] as FieldRef<unknown> | undefined
274274
const fieldName = fieldRef ? extractFieldName(fieldRef) : null
275275
const header = props['header'] as React.ReactNode | undefined
276276
const sortable = (props['sortable'] as boolean | undefined) ?? false

packages/bindx-dataview/src/contextHooks.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@ import type {
1818
FilterHandler,
1919
FilterArtifact,
2020
DataViewLayout,
21-
FieldRefBase,
21+
FieldRef,
2222
} from '@contember/bindx'
2323

2424
// ============================================================================
2525
// Sorting
2626
// ============================================================================
2727

2828
export interface DataViewSortingMethods {
29-
setOrderBy<T>(field: FieldRefBase<T>, action: SortingDirectionAction, append?: boolean): void
29+
setOrderBy<T>(field: FieldRef<T>, action: SortingDirectionAction, append?: boolean): void
3030
}
3131

3232
export function useDataViewSortingState(): SortingState {
@@ -38,7 +38,7 @@ export function useDataViewSortingMethods(): DataViewSortingMethods {
3838
return useMemo(() => ({ setOrderBy: sorting.setOrderBy }), [sorting.setOrderBy])
3939
}
4040

41-
export function useDataViewSortingDirection<T>(field: FieldRefBase<T>): OrderDirection | null {
41+
export function useDataViewSortingDirection<T>(field: FieldRef<T>): OrderDirection | null {
4242
return useDataViewContext().sorting.directionOf(field)
4343
}
4444

packages/bindx-dataview/src/createColumn.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
import React from 'react'
10-
import type { FieldRefBase, FilterArtifact, FilterHandler, EntityAccessor } from '@contember/bindx'
10+
import type { FieldRef, FilterArtifact, FilterHandler, EntityAccessor } from '@contember/bindx'
1111
import type { ColumnTypeDef } from './columnTypes.js'
1212
import { ColumnLeaf, type ColumnLeafProps } from './columnLeaf.js'
1313
import { extractFieldName, extractEnumName } from './columns.js'
@@ -19,7 +19,7 @@ import { extractFieldName, extractEnumName } from './columns.js'
1919
export interface ColumnRenderProps<TValue> {
2020
readonly value: TValue
2121
readonly accessor: EntityAccessor<object>
22-
readonly fieldRef: FieldRefBase<unknown> | null
22+
readonly fieldRef: FieldRef<unknown> | null
2323
readonly fieldName: string | null
2424
}
2525

@@ -42,7 +42,7 @@ export interface CreateColumnConfig<TValue, TFilterArtifact extends FilterArtifa
4242
// ============================================================================
4343

4444
export interface ColumnComponentProps<TValue = unknown> {
45-
field: FieldRefBase<TValue>
45+
field: FieldRef<TValue>
4646
header?: React.ReactNode
4747
sortable?: boolean
4848
filter?: boolean
@@ -67,7 +67,7 @@ export function createColumn<TValue, TFilterArtifact extends FilterArtifact, TEx
6767
}
6868

6969
Column.staticRender = (props: Record<string, unknown>): React.ReactNode => {
70-
const fieldRef = props['field'] as FieldRefBase<unknown> | undefined
70+
const fieldRef = props['field'] as FieldRef<unknown> | undefined
7171
const fieldName = fieldRef ? extractFieldName(fieldRef) : null
7272
const header = props['header'] as React.ReactNode | undefined
7373
const sortable = (props['sortable'] as boolean | undefined) ?? columnType.defaultSortable

packages/bindx-dataview/src/createRelationColumn.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
*/
1212

1313
import React from 'react'
14-
import type { FieldRefBase, FilterArtifact, FilterHandler, EntityAccessor, SelectionMeta } from '@contember/bindx'
14+
import type { FieldRef, FilterArtifact, FilterHandler, EntityAccessor, SelectionMeta } from '@contember/bindx'
1515
import { SelectionScope } from '@contember/bindx'
1616
import { createCollectorProxy } from '@contember/bindx-react'
1717
import type { ColumnTypeDef } from './columnTypes.js'
@@ -51,7 +51,7 @@ export interface RelationCellWrapperContext {
5151
readonly item: EntityAccessor<object>
5252
readonly fieldName: string
5353
readonly filterName: string
54-
readonly fieldRef: FieldRefBase<unknown>
54+
readonly fieldRef: FieldRef<unknown>
5555
}
5656

5757
// ============================================================================
@@ -72,7 +72,7 @@ export interface RelationColumnProps<TEntity, TSelected> {
7272

7373
interface RelationCellConfig {
7474
/** How to collect selection for the relation (differs between hasOne and hasMany) */
75-
collectSelection: (renderer: (ref: unknown) => React.ReactNode, fieldRef: FieldRefBase<unknown>) => void
75+
collectSelection: (renderer: (ref: unknown) => React.ReactNode, fieldRef: FieldRef<unknown>) => void
7676
/** How to render the cell content (differs between hasOne and hasMany) */
7777
renderCell: (accessor: EntityAccessor<object>, fieldName: string, renderer: (ref: unknown) => React.ReactNode) => React.ReactNode
7878
}
@@ -83,7 +83,7 @@ export function createRelationColumn<TFilterArtifact extends FilterArtifact>(
8383
uiConfig: RelationColumnConfig = {},
8484
) {
8585
function buildLeaf(props: Record<string, unknown>): ColumnLeafProps {
86-
const fieldRef = props['field'] as FieldRefBase<unknown> | undefined
86+
const fieldRef = props['field'] as FieldRef<unknown> | undefined
8787
const fieldName = fieldRef ? extractFieldName(fieldRef) : null
8888
const renderer = props['children'] as ((ref: unknown) => React.ReactNode) | undefined
8989
const header = props['header'] as React.ReactNode | undefined

packages/bindx-dataview/src/filterWrappers.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
*/
2323

2424
import React, { type ReactElement } from 'react'
25-
import type { FieldRefBase } from '@contember/bindx'
25+
import type { FieldRef } from '@contember/bindx'
2626
import { FIELD_REF_META } from '@contember/bindx'
2727
import { DataViewFilterNameProvider } from './filterContext.js'
2828

@@ -31,12 +31,12 @@ import { DataViewFilterNameProvider } from './filterContext.js'
3131
// ============================================================================
3232

3333
interface FilterWrapperProps<T> {
34-
field: FieldRefBase<T>
34+
field: FieldRef<T>
3535
name?: string
3636
children: React.ReactNode
3737
}
3838

39-
function resolveFilterName<T>(name: string | undefined, field: FieldRefBase<T>): string {
39+
function resolveFilterName<T>(name: string | undefined, field: FieldRef<T>): string {
4040
return name ?? field[FIELD_REF_META].fieldName
4141
}
4242

@@ -172,7 +172,7 @@ export function DataViewIsDefinedFilter<T>({ field, name, children }: DataViewIs
172172

173173
export interface DataViewUnionTextFilterProps<T> {
174174
/** Single field or array of fields to search across */
175-
fields: FieldRefBase<T> | FieldRefBase<T>[]
175+
fields: FieldRef<T> | FieldRef<T>[]
176176
/** Required unique filter name */
177177
name: string
178178
children: React.ReactNode

packages/bindx-dataview/src/select/MultiSelect.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import { type ReactNode, useCallback, useMemo } from 'react'
1818
import type { EntityAccessor, EntityDef, HasManyRef } from '@contember/bindx'
19+
import { useHasMany } from '@contember/bindx-react'
1920
import {
2021
SelectCurrentEntitiesContext,
2122
SelectHandleSelectContext,
@@ -41,7 +42,8 @@ export function MultiSelect({
4142
onSelect,
4243
onUnselect,
4344
}: MultiSelectProps): ReactNode {
44-
const items = relation.items
45+
const accessor = useHasMany(relation)
46+
const items = accessor.items
4547
const selectedIds = useMemo(
4648
() => new Set(items.map(it => it.id)),
4749
[items],

packages/bindx-dataview/src/select/Select.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import { type ReactNode, useCallback, useMemo } from 'react'
1818
import type { EntityAccessor, EntityDef, HasOneRef } from '@contember/bindx'
19+
import { useHasOne } from '@contember/bindx-react'
1920
import { isPlaceholderId } from '@contember/bindx'
2021
import {
2122
SelectCurrentEntitiesContext,
@@ -42,7 +43,8 @@ export function Select({
4243
onSelect,
4344
onUnselect,
4445
}: SelectProps): ReactNode {
45-
const entity = relation.$entity
46+
const accessor = useHasOne(relation)
47+
const entity = accessor.$entity
4648
const entityId = entity.id
4749
const isConnected = !isPlaceholderId(entityId)
4850
const currentId = isConnected ? entityId : null

packages/bindx-dataview/src/select/SelectDataView.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import type {
3333
SortingDirections,
3434
} from '@contember/bindx'
3535
import { SelectionScope, buildQueryFromSelection, createFullTextFilterHandler, FIELD_REF_META } from '@contember/bindx'
36-
import type { FieldRefBase } from '@contember/bindx'
36+
import type { FieldRef } from '@contember/bindx'
3737
import {
3838
useBindxContext,
3939
useEntityList,
@@ -51,7 +51,7 @@ export interface SelectDataViewProps {
5151
/** Selection definer — called with collector proxy to discover fields */
5252
selection?: (it: EntityAccessor<object>) => ReactNode
5353
/** Field(s) to search across */
54-
queryField?: FieldRefBase<unknown> | FieldRefBase<unknown>[] | string[]
54+
queryField?: FieldRef<unknown> | FieldRef<unknown>[] | string[]
5555
/** Initial sort order */
5656
initialSorting?: Partial<Record<string, OrderDirection>>
5757
/** Static filter for the options list */

packages/bindx-dataview/src/sortingComponents.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,15 @@ import { Slot } from '@radix-ui/react-slot'
2020
import { composeEventHandlers } from '@radix-ui/primitive'
2121
import { useDataViewContext } from './DataViewContext.js'
2222
import { dataAttribute } from './dataAttribute.js'
23-
import type { SortingDirectionAction, OrderDirection, FieldRefBase } from '@contember/bindx'
23+
import type { SortingDirectionAction, OrderDirection, FieldRef } from '@contember/bindx'
2424

2525
// ============================================================================
2626
// DataViewSortingTrigger
2727
// ============================================================================
2828

2929
export interface DataViewSortingTriggerProps<T> {
3030
/** Field to sort by */
31-
field: FieldRefBase<T>
31+
field: FieldRef<T>
3232
/** Sorting action. Default: 'next' (cycles asc → desc → none) */
3333
action?: SortingDirectionAction
3434
/** Button element to render */
@@ -80,7 +80,7 @@ export const DataViewSortingTrigger = forwardRef<HTMLButtonElement, DataViewSort
8080

8181
export interface DataViewSortingSwitchProps<T> {
8282
/** Field to check sorting direction for */
83-
field: FieldRefBase<T>
83+
field: FieldRef<T>
8484
/** Render when sorted ascending */
8585
asc?: React.ReactNode
8686
/** Render when sorted descending */

0 commit comments

Comments
 (0)