@contember/bindx-react 0.1.24 → 0.1.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/hooks/ContemberBindxProvider.d.ts +1 -26
- package/dist/hooks/ContemberBindxProvider.d.ts.map +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/useAccessor.d.ts +16 -0
- package/dist/hooks/useAccessor.d.ts.map +1 -0
- package/dist/hooks/useField.d.ts +1 -4
- package/dist/hooks/useField.d.ts.map +1 -1
- package/dist/hooks/useHasMany.d.ts +2 -5
- package/dist/hooks/useHasMany.d.ts.map +1 -1
- package/dist/hooks/useHasOne.d.ts +2 -5
- package/dist/hooks/useHasOne.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/jsx/analyzer.d.ts.map +1 -1
- package/dist/jsx/componentBuilder.types.d.ts +16 -12
- package/dist/jsx/componentBuilder.types.d.ts.map +1 -1
- package/dist/jsx/componentFactory.d.ts.map +1 -1
- package/dist/jsx/components/Attribute.d.ts +38 -0
- package/dist/jsx/components/Attribute.d.ts.map +1 -0
- package/dist/jsx/components/Entity.d.ts +2 -2
- package/dist/jsx/components/Entity.d.ts.map +1 -1
- package/dist/jsx/components/EntityList.d.ts.map +1 -1
- package/dist/jsx/components/Field.d.ts.map +1 -1
- package/dist/jsx/components/HasMany.d.ts.map +1 -1
- package/dist/jsx/components/HasOne.d.ts.map +1 -1
- package/dist/jsx/components/index.d.ts +1 -0
- package/dist/jsx/components/index.d.ts.map +1 -1
- package/dist/jsx/devAnnotations.d.ts +12 -0
- package/dist/jsx/devAnnotations.d.ts.map +1 -0
- package/dist/jsx/index.d.ts +1 -0
- package/dist/jsx/index.d.ts.map +1 -1
- package/dist/jsx/types.d.ts +3 -3
- package/dist/jsx/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/hooks/ContemberBindxProvider.tsx +3 -24
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useAccessor.ts +64 -0
- package/src/hooks/useField.ts +4 -20
- package/src/hooks/useHasMany.ts +4 -17
- package/src/hooks/useHasOne.ts +4 -17
- package/src/index.ts +3 -0
- package/src/jsx/analyzer.ts +1 -84
- package/src/jsx/collectorProxy.ts +2 -0
- package/src/jsx/componentBuilder.types.ts +31 -16
- package/src/jsx/componentFactory.ts +47 -5
- package/src/jsx/components/Attribute.tsx +90 -0
- package/src/jsx/components/Entity.tsx +5 -3
- package/src/jsx/components/EntityList.tsx +3 -1
- package/src/jsx/components/Field.tsx +11 -3
- package/src/jsx/components/HasMany.tsx +10 -1
- package/src/jsx/components/HasOne.tsx +5 -1
- package/src/jsx/components/index.ts +3 -0
- package/src/jsx/devAnnotations.ts +33 -0
- package/src/jsx/index.ts +1 -0
- package/src/jsx/types.ts +3 -3
|
@@ -2,6 +2,7 @@ import { memo, type ReactElement } from 'react'
|
|
|
2
2
|
import type { FieldProps, SelectionFieldMeta, SelectionProvider } from '../types.js'
|
|
3
3
|
import { FIELD_REF_META, BINDX_COMPONENT } from '../types.js'
|
|
4
4
|
import { useField } from '../../hooks/useField.js'
|
|
5
|
+
import { annotateElement, isDevAnnotationsEnabled } from '../devAnnotations.js'
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Field component - renders a scalar field value
|
|
@@ -28,13 +29,18 @@ function FieldImpl<T>({ field, children, format }: FieldProps<T>): ReactElement
|
|
|
28
29
|
|
|
29
30
|
// useField() subscribes to store and returns FieldAccessor with .value access
|
|
30
31
|
const accessor = useField(field)
|
|
32
|
+
const fieldName = field[FIELD_REF_META]?.fieldName
|
|
31
33
|
|
|
32
34
|
if (children) {
|
|
33
|
-
|
|
35
|
+
const result = children(accessor)
|
|
36
|
+
return <>{annotateElement(result, { 'data-field': fieldName })}</>
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
if (format) {
|
|
37
|
-
|
|
40
|
+
const content = format(accessor.value)
|
|
41
|
+
return isDevAnnotationsEnabled() && fieldName
|
|
42
|
+
? <span data-field={fieldName}>{content}</span>
|
|
43
|
+
: <>{content}</>
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
// Default: render value as string
|
|
@@ -42,7 +48,9 @@ function FieldImpl<T>({ field, children, format }: FieldProps<T>): ReactElement
|
|
|
42
48
|
return null
|
|
43
49
|
}
|
|
44
50
|
|
|
45
|
-
return
|
|
51
|
+
return isDevAnnotationsEnabled() && fieldName
|
|
52
|
+
? <span data-field={fieldName}>{String(accessor.value)}</span>
|
|
53
|
+
: <>{String(accessor.value)}</>
|
|
46
54
|
}
|
|
47
55
|
|
|
48
56
|
export const Field = memo(FieldImpl) as typeof FieldImpl
|
|
@@ -4,6 +4,7 @@ import { FIELD_REF_META, BINDX_COMPONENT, SCOPE_REF } from '../types.js'
|
|
|
4
4
|
import { mergeSelections } from '../SelectionMeta.js'
|
|
5
5
|
import { SelectionScope, generateHasManyAlias, type HasManyAccessor } from '@contember/bindx'
|
|
6
6
|
import { useHasMany } from '../../hooks/useHasMany.js'
|
|
7
|
+
import { annotateElement } from '../devAnnotations.js'
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* HasMany component - renders a has-many relation
|
|
@@ -38,8 +39,16 @@ function HasManyImpl<
|
|
|
38
39
|
>({ field, children }: HasManyProps<TEntity, TSelected, TBrand, TEntityName, TSchema>): ReactElement {
|
|
39
40
|
// useHasMany() subscribes to store and returns HasManyAccessor with .map()/.items/.length
|
|
40
41
|
const accessor = useHasMany(field)
|
|
42
|
+
const targetType = field[FIELD_REF_META]?.targetType
|
|
43
|
+
|
|
41
44
|
const items = accessor.map((item, index) => {
|
|
42
|
-
|
|
45
|
+
const result = children(item, index)
|
|
46
|
+
return <React.Fragment key={item.id}>{
|
|
47
|
+
annotateElement(result, {
|
|
48
|
+
'data-entity': targetType ?? '',
|
|
49
|
+
'data-entity-id': item.id,
|
|
50
|
+
})
|
|
51
|
+
}</React.Fragment>
|
|
43
52
|
})
|
|
44
53
|
|
|
45
54
|
return <>{items}</>
|
|
@@ -4,6 +4,7 @@ import { FIELD_REF_META, BINDX_COMPONENT, SCOPE_REF } from '../types.js'
|
|
|
4
4
|
import { mergeSelections } from '../SelectionMeta.js'
|
|
5
5
|
import { SelectionScope, type HasOneAccessor } from '@contember/bindx'
|
|
6
6
|
import { useHasOne } from '../../hooks/useHasOne.js'
|
|
7
|
+
import { annotateElement } from '../devAnnotations.js'
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* HasOne component - renders a has-one relation
|
|
@@ -29,8 +30,11 @@ function HasOneImpl<
|
|
|
29
30
|
>({ field, children }: HasOneProps<TEntity, TSelected, TBrand, TEntityName, TSchema>): ReactElement {
|
|
30
31
|
// useHasOne() subscribes to store and returns HasOneAccessor with .$entity
|
|
31
32
|
const accessor = useHasOne(field)
|
|
33
|
+
const relationName = field[FIELD_REF_META]?.fieldName
|
|
34
|
+
|
|
32
35
|
// Get the related entity reference (always available, may be placeholder with id=null)
|
|
33
|
-
|
|
36
|
+
const result = children(accessor.$entity)
|
|
37
|
+
return <>{annotateElement(result, { 'data-relation': relationName ?? '' })}</>
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
export const HasOne = memo(HasOneImpl) as typeof HasOneImpl
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { cloneElement, isValidElement, type ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Whether dev-mode data attributes are enabled.
|
|
5
|
+
*
|
|
6
|
+
* Controlled via `BINDX_DEV_ANNOTATIONS` env variable.
|
|
7
|
+
* Bundlers (Vite, webpack) replace `import.meta.env` at build time,
|
|
8
|
+
* so this entire module tree-shakes away in production builds.
|
|
9
|
+
*/
|
|
10
|
+
// @ts-ignore -- import.meta.env is provided by bundler
|
|
11
|
+
const DEV_ANNOTATIONS: boolean = typeof import.meta !== 'undefined' && import.meta.env?.VITE_BINDX_DEV_ANNOTATIONS === 'true'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* In dev mode, injects data attributes onto an existing React element via cloneElement.
|
|
15
|
+
* If annotations are disabled or the node is not a valid element, returns as-is.
|
|
16
|
+
*/
|
|
17
|
+
export function annotateElement(
|
|
18
|
+
element: ReactNode,
|
|
19
|
+
attrs: Record<string, string>,
|
|
20
|
+
): ReactNode {
|
|
21
|
+
if (!DEV_ANNOTATIONS || !isValidElement(element)) {
|
|
22
|
+
return element
|
|
23
|
+
}
|
|
24
|
+
return cloneElement(element, attrs)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Returns true if dev annotations are enabled.
|
|
29
|
+
* Use for conditional wrapping (e.g., Field text render in span).
|
|
30
|
+
*/
|
|
31
|
+
export function isDevAnnotationsEnabled(): boolean {
|
|
32
|
+
return DEV_ANNOTATIONS
|
|
33
|
+
}
|
package/src/jsx/index.ts
CHANGED
|
@@ -52,6 +52,7 @@ export {
|
|
|
52
52
|
} from './analyzer.js'
|
|
53
53
|
|
|
54
54
|
// Components
|
|
55
|
+
export { Attribute, AttributeWithMeta, type AttributeProps } from './components/Attribute.js'
|
|
55
56
|
export { Field, FieldWithMeta } from './components/Field.js'
|
|
56
57
|
export { HasMany, HasManyWithMeta } from './components/HasMany.js'
|
|
57
58
|
export { HasOne, HasOneWithMeta } from './components/HasOne.js'
|
package/src/jsx/types.ts
CHANGED
|
@@ -97,7 +97,7 @@ export interface HasManyProps<
|
|
|
97
97
|
TSchema extends Record<string, object> = Record<string, object>,
|
|
98
98
|
> {
|
|
99
99
|
field: HasManyRef<TEntity, TSelected, TBrand, TEntityName, TSchema>
|
|
100
|
-
children: (item:
|
|
100
|
+
children: (item: EntityRef<TEntity, TSelected, TBrand, TEntityName, TSchema>, index: number) => ReactNode
|
|
101
101
|
filter?: unknown
|
|
102
102
|
orderBy?: unknown
|
|
103
103
|
limit?: number
|
|
@@ -123,7 +123,7 @@ export interface HasOneProps<
|
|
|
123
123
|
TSchema extends Record<string, object> = Record<string, object>,
|
|
124
124
|
> {
|
|
125
125
|
field: HasOneRef<TEntity, TSelected, TBrand, TEntityName, TSchema>
|
|
126
|
-
children: (entity:
|
|
126
|
+
children: (entity: EntityRef<TEntity, TSelected, TBrand, TEntityName, TSchema>) => ReactNode
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
/**
|
|
@@ -134,7 +134,7 @@ export interface HasOneProps<
|
|
|
134
134
|
export interface EntityComponentProps<TSchema extends Record<string, object>, K extends keyof TSchema & string> {
|
|
135
135
|
name: K
|
|
136
136
|
id: string
|
|
137
|
-
children: (entity:
|
|
137
|
+
children: (entity: EntityRef<TSchema[K], TSchema[K], import('@contember/bindx').AnyBrand, K, TSchema>) => ReactNode
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
/**
|