@nixxie-cms/core 1.0.3 → 1.1.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 (202) hide show
  1. package/CHANGES-1.1.md +134 -0
  2. package/context/dist/nixxie-cms-core-context.cjs.js +4 -3
  3. package/context/dist/nixxie-cms-core-context.esm.js +3 -2
  4. package/dist/declarations/src/access.d.ts +2 -2
  5. package/dist/declarations/src/access.d.ts.map +1 -1
  6. package/dist/declarations/src/admin-ui/components/Navigation.d.ts +2 -2
  7. package/dist/declarations/src/admin-ui/components/Navigation.d.ts.map +1 -1
  8. package/dist/declarations/src/admin-ui/context.d.ts +6 -6
  9. package/dist/declarations/src/admin-ui/context.d.ts.map +1 -1
  10. package/dist/declarations/src/admin-ui/utils/Fields.d.ts +3 -3
  11. package/dist/declarations/src/admin-ui/utils/Fields.d.ts.map +1 -1
  12. package/dist/declarations/src/admin-ui/utils/filters.d.ts +5 -5
  13. package/dist/declarations/src/admin-ui/utils/filters.d.ts.map +1 -1
  14. package/dist/declarations/src/admin-ui/utils/useCreateItem.d.ts +3 -3
  15. package/dist/declarations/src/admin-ui/utils/useCreateItem.d.ts.map +1 -1
  16. package/dist/declarations/src/admin-ui/utils/utils.d.ts +2 -2
  17. package/dist/declarations/src/admin-ui/utils/utils.d.ts.map +1 -1
  18. package/dist/declarations/src/context.d.ts +1 -1
  19. package/dist/declarations/src/context.d.ts.map +1 -1
  20. package/dist/declarations/src/fields/types/bigInt/index.d.ts +3 -3
  21. package/dist/declarations/src/fields/types/bigInt/index.d.ts.map +1 -1
  22. package/dist/declarations/src/fields/types/bytes/index.d.ts +3 -3
  23. package/dist/declarations/src/fields/types/bytes/index.d.ts.map +1 -1
  24. package/dist/declarations/src/fields/types/calendarDay/index.d.ts +3 -3
  25. package/dist/declarations/src/fields/types/calendarDay/index.d.ts.map +1 -1
  26. package/dist/declarations/src/fields/types/checkbox/index.d.ts +3 -3
  27. package/dist/declarations/src/fields/types/checkbox/index.d.ts.map +1 -1
  28. package/dist/declarations/src/fields/types/decimal/index.d.ts +3 -3
  29. package/dist/declarations/src/fields/types/decimal/index.d.ts.map +1 -1
  30. package/dist/declarations/src/fields/types/file/index.d.ts +4 -4
  31. package/dist/declarations/src/fields/types/file/index.d.ts.map +1 -1
  32. package/dist/declarations/src/fields/types/float/index.d.ts +3 -3
  33. package/dist/declarations/src/fields/types/float/index.d.ts.map +1 -1
  34. package/dist/declarations/src/fields/types/image/index.d.ts +4 -4
  35. package/dist/declarations/src/fields/types/image/index.d.ts.map +1 -1
  36. package/dist/declarations/src/fields/types/integer/index.d.ts +3 -3
  37. package/dist/declarations/src/fields/types/integer/index.d.ts.map +1 -1
  38. package/dist/declarations/src/fields/types/json/index.d.ts +3 -3
  39. package/dist/declarations/src/fields/types/json/index.d.ts.map +1 -1
  40. package/dist/declarations/src/fields/types/multiselect/index.d.ts +3 -3
  41. package/dist/declarations/src/fields/types/multiselect/index.d.ts.map +1 -1
  42. package/dist/declarations/src/fields/types/multiselect/views/index.d.ts.map +1 -1
  43. package/dist/declarations/src/fields/types/password/index.d.ts +3 -3
  44. package/dist/declarations/src/fields/types/password/index.d.ts.map +1 -1
  45. package/dist/declarations/src/fields/types/relationship/index.d.ts +8 -8
  46. package/dist/declarations/src/fields/types/relationship/index.d.ts.map +1 -1
  47. package/dist/declarations/src/fields/types/relationship/views/ComboboxMany.d.ts +3 -3
  48. package/dist/declarations/src/fields/types/relationship/views/ComboboxMany.d.ts.map +1 -1
  49. package/dist/declarations/src/fields/types/relationship/views/ComboboxSingle.d.ts +3 -3
  50. package/dist/declarations/src/fields/types/relationship/views/ComboboxSingle.d.ts.map +1 -1
  51. package/dist/declarations/src/fields/types/relationship/views/index.d.ts +3 -3
  52. package/dist/declarations/src/fields/types/relationship/views/index.d.ts.map +1 -1
  53. package/dist/declarations/src/fields/types/relationship/views/types.d.ts +3 -3
  54. package/dist/declarations/src/fields/types/relationship/views/types.d.ts.map +1 -1
  55. package/dist/declarations/src/fields/types/select/index.d.ts +3 -3
  56. package/dist/declarations/src/fields/types/select/index.d.ts.map +1 -1
  57. package/dist/declarations/src/fields/types/text/index.d.ts +3 -3
  58. package/dist/declarations/src/fields/types/text/index.d.ts.map +1 -1
  59. package/dist/declarations/src/fields/types/timestamp/index.d.ts +3 -3
  60. package/dist/declarations/src/fields/types/timestamp/index.d.ts.map +1 -1
  61. package/dist/declarations/src/fields/types/virtual/index.d.ts +7 -7
  62. package/dist/declarations/src/fields/types/virtual/index.d.ts.map +1 -1
  63. package/dist/declarations/src/helpers.d.ts +249 -13
  64. package/dist/declarations/src/helpers.d.ts.map +1 -1
  65. package/dist/declarations/src/index.d.ts +9 -4
  66. package/dist/declarations/src/index.d.ts.map +1 -1
  67. package/dist/declarations/src/internal-unstable/admin-ui/pages/ListPage/index.d.ts.map +1 -1
  68. package/dist/declarations/src/lib/admin-meta.d.ts +11 -11
  69. package/dist/declarations/src/lib/admin-meta.d.ts.map +1 -1
  70. package/dist/declarations/src/lib/core/access-control.d.ts +18 -18
  71. package/dist/declarations/src/lib/core/access-control.d.ts.map +1 -1
  72. package/dist/declarations/src/lib/core/cascade.d.ts +47 -0
  73. package/dist/declarations/src/lib/core/cascade.d.ts.map +1 -0
  74. package/dist/declarations/src/lib/core/initialise-lists.d.ts +27 -24
  75. package/dist/declarations/src/lib/core/initialise-lists.d.ts.map +1 -1
  76. package/dist/declarations/src/lib/env.d.ts +9 -0
  77. package/dist/declarations/src/lib/env.d.ts.map +1 -0
  78. package/dist/declarations/src/lib/system.d.ts +1 -1
  79. package/dist/declarations/src/lib/system.d.ts.map +1 -1
  80. package/dist/declarations/src/list-features.d.ts +162 -0
  81. package/dist/declarations/src/list-features.d.ts.map +1 -0
  82. package/dist/declarations/src/schema.d.ts +24 -23
  83. package/dist/declarations/src/schema.d.ts.map +1 -1
  84. package/dist/declarations/src/session.d.ts +75 -0
  85. package/dist/declarations/src/session.d.ts.map +1 -1
  86. package/dist/declarations/src/types/admin-meta.d.ts +11 -11
  87. package/dist/declarations/src/types/admin-meta.d.ts.map +1 -1
  88. package/dist/declarations/src/types/config/access-control.d.ts +42 -42
  89. package/dist/declarations/src/types/config/access-control.d.ts.map +1 -1
  90. package/dist/declarations/src/types/config/fields.d.ts +19 -19
  91. package/dist/declarations/src/types/config/fields.d.ts.map +1 -1
  92. package/dist/declarations/src/types/config/hooks.d.ts +131 -131
  93. package/dist/declarations/src/types/config/hooks.d.ts.map +1 -1
  94. package/dist/declarations/src/types/config/index.d.ts +171 -8
  95. package/dist/declarations/src/types/config/index.d.ts.map +1 -1
  96. package/dist/declarations/src/types/config/lists.d.ts +146 -108
  97. package/dist/declarations/src/types/config/lists.d.ts.map +1 -1
  98. package/dist/declarations/src/types/context.d.ts +349 -47
  99. package/dist/declarations/src/types/context.d.ts.map +1 -1
  100. package/dist/declarations/src/types/next-fields.d.ts +28 -28
  101. package/dist/declarations/src/types/next-fields.d.ts.map +1 -1
  102. package/dist/declarations/src/types/type-info.d.ts +3 -3
  103. package/dist/declarations/src/types/type-info.d.ts.map +1 -1
  104. package/dist/{express-7559ca2d.esm.js → express-0abbce07.esm.js} +6 -6
  105. package/dist/{express-455ae20c.cjs.js → express-7ca6f76a.cjs.js} +6 -6
  106. package/dist/{index-15c8f81e.esm.js → index-5d8b0b4e.esm.js} +363 -183
  107. package/dist/index-6055753b.cjs.js +393 -0
  108. package/dist/{index-42045902.cjs.js → index-ac29f382.cjs.js} +363 -185
  109. package/dist/index-f1703b7b.esm.js +386 -0
  110. package/dist/nixxie-cms-core.cjs.js +1387 -30
  111. package/dist/nixxie-cms-core.esm.js +1361 -24
  112. package/dist/{non-null-graphql-add6bb3d.cjs.js → non-null-graphql-4a44c122.cjs.js} +1 -1
  113. package/dist/{non-null-graphql-a84ed64d.esm.js → non-null-graphql-8c5feaae.esm.js} +1 -1
  114. package/dist/{resolve-hooks-165a9ce2.cjs.js → resolve-hooks-10a5f84c.cjs.js} +240 -6
  115. package/dist/{resolve-hooks-6813a045.esm.js → resolve-hooks-9e676794.esm.js} +238 -7
  116. package/dist/{system-03e49e4f.esm.js → system-4d2a2648.esm.js} +32 -7
  117. package/dist/{system-a321642d.cjs.js → system-69e1a285.cjs.js} +32 -7
  118. package/fields/dist/nixxie-cms-core-fields.cjs.js +29 -576
  119. package/fields/dist/nixxie-cms-core-fields.esm.js +18 -565
  120. package/fields/types/bytes/dist/nixxie-cms-core-fields-types-bytes.cjs.js +4 -2
  121. package/fields/types/bytes/dist/nixxie-cms-core-fields-types-bytes.esm.js +4 -2
  122. package/fields/types/multiselect/views/dist/nixxie-cms-core-fields-types-multiselect-views.cjs.js +1 -6
  123. package/fields/types/multiselect/views/dist/nixxie-cms-core-fields-types-multiselect-views.esm.js +1 -6
  124. package/fields/types/password/dist/nixxie-cms-core-fields-types-password.cjs.js +4 -2
  125. package/fields/types/password/dist/nixxie-cms-core-fields-types-password.esm.js +4 -2
  126. package/internal-unstable/artifacts/dist/nixxie-cms-core-internal-unstable-artifacts.cjs.js +4 -3
  127. package/internal-unstable/artifacts/dist/nixxie-cms-core-internal-unstable-artifacts.esm.js +4 -3
  128. package/package.json +4 -4
  129. package/scripts/cli/dist/nixxie-cms-core-scripts-cli.cjs.js +4 -3
  130. package/scripts/cli/dist/nixxie-cms-core-scripts-cli.esm.js +4 -3
  131. package/scripts/dist/nixxie-cms-core-scripts.cjs.js +4 -3
  132. package/scripts/dist/nixxie-cms-core-scripts.esm.js +4 -3
  133. package/session/dist/nixxie-cms-core-session.cjs.js +286 -0
  134. package/session/dist/nixxie-cms-core-session.esm.js +279 -1
  135. package/src/access.ts +25 -25
  136. package/src/admin-ui/admin-meta-graphql.ts +5 -5
  137. package/src/admin-ui/components/CreateButtonLink.tsx +46 -46
  138. package/src/admin-ui/components/Navigation.tsx +3 -3
  139. package/src/admin-ui/context.tsx +6 -6
  140. package/src/admin-ui/utils/Fields.tsx +241 -241
  141. package/src/admin-ui/utils/actionData.ts +36 -36
  142. package/src/admin-ui/utils/filters.ts +148 -148
  143. package/src/admin-ui/utils/useCreateItem.ts +171 -171
  144. package/src/admin-ui/utils/utils.tsx +127 -127
  145. package/src/context.ts +1 -1
  146. package/src/fields/non-null-graphql.ts +115 -115
  147. package/src/fields/types/bigInt/index.ts +6 -6
  148. package/src/fields/types/bytes/index.ts +6 -6
  149. package/src/fields/types/calendarDay/index.ts +18 -19
  150. package/src/fields/types/checkbox/index.ts +6 -6
  151. package/src/fields/types/decimal/index.ts +6 -6
  152. package/src/fields/types/file/index.ts +8 -8
  153. package/src/fields/types/float/index.ts +6 -6
  154. package/src/fields/types/image/index.ts +8 -8
  155. package/src/fields/types/integer/index.ts +6 -6
  156. package/src/fields/types/json/index.ts +5 -5
  157. package/src/fields/types/multiselect/index.ts +7 -7
  158. package/src/fields/types/multiselect/views/index.tsx +149 -151
  159. package/src/fields/types/password/index.ts +6 -6
  160. package/src/fields/types/relationship/index.ts +13 -13
  161. package/src/fields/types/relationship/views/ComboboxMany.tsx +110 -110
  162. package/src/fields/types/relationship/views/ComboboxSingle.tsx +115 -115
  163. package/src/fields/types/relationship/views/ContextualActions.tsx +139 -139
  164. package/src/fields/types/relationship/views/index.tsx +492 -492
  165. package/src/fields/types/relationship/views/types.ts +46 -46
  166. package/src/fields/types/relationship/views/useApolloQuery.ts +185 -185
  167. package/src/fields/types/relationship/views/useFilter.tsx +109 -109
  168. package/src/fields/types/select/index.ts +6 -6
  169. package/src/fields/types/text/index.ts +6 -6
  170. package/src/fields/types/timestamp/index.ts +23 -21
  171. package/src/fields/types/virtual/index.ts +11 -11
  172. package/src/helpers.ts +773 -42
  173. package/src/index.ts +66 -24
  174. package/src/internal-unstable/admin-ui/pages/ItemPage/common.tsx +4 -4
  175. package/src/internal-unstable/admin-ui/pages/ItemPage/index.tsx +5 -5
  176. package/src/internal-unstable/admin-ui/pages/ListPage/index.tsx +8 -8
  177. package/src/lib/admin-meta.ts +369 -369
  178. package/src/lib/context/createContext.ts +5 -0
  179. package/src/lib/core/access-control.ts +434 -434
  180. package/src/lib/core/cascade.ts +236 -0
  181. package/src/lib/core/initialise-lists.ts +49 -33
  182. package/src/lib/core/mutations/index.ts +7 -0
  183. package/src/lib/core/mutations/nested-mutation-many-input-resolvers.ts +145 -145
  184. package/src/lib/core/mutations/nested-mutation-one-input-resolvers.ts +71 -71
  185. package/src/lib/core/queries/output-field.ts +178 -178
  186. package/src/lib/env.ts +50 -0
  187. package/src/lib/id-field.ts +2 -2
  188. package/src/lib/system.ts +221 -207
  189. package/src/lib/typescript-schema-printer.ts +227 -227
  190. package/src/list-features.ts +476 -0
  191. package/src/schema.ts +91 -22
  192. package/src/session.ts +225 -0
  193. package/src/types/admin-meta.ts +218 -218
  194. package/src/types/config/access-control.ts +186 -186
  195. package/src/types/config/fields.ts +96 -96
  196. package/src/types/config/hooks.ts +529 -529
  197. package/src/types/config/index.ts +185 -7
  198. package/src/types/config/lists.ts +606 -565
  199. package/src/types/context.ts +426 -55
  200. package/src/types/next-fields.ts +31 -31
  201. package/src/types/type-info.ts +38 -38
  202. package/src/types/type-tests.ts +21 -21
@@ -1,139 +1,139 @@
1
- import { type Key, type PropsWithChildren, useMemo } from 'react'
2
-
3
- import { Icon } from '@keystar/ui/icon'
4
- import { arrowUpRightIcon } from '@keystar/ui/icon/icons/arrowUpRightIcon'
5
- import { plusIcon } from '@keystar/ui/icon/icons/plusIcon'
6
- import { Grid } from '@keystar/ui/layout'
7
- import { ActionMenu, Item } from '@keystar/ui/menu'
8
- import { Text } from '@keystar/ui/typography'
9
-
10
- import { useList } from '../../../../admin-ui/context'
11
- import type { FieldProps, ListMeta } from '../../../../types'
12
- import type { RelationshipController } from './types'
13
- import { ActionButton } from '@keystar/ui/button'
14
- import { Tooltip, TooltipTrigger } from '@keystar/ui/tooltip'
15
-
16
- type RelationshipProps = {
17
- onAdd: () => void
18
- } & FieldProps<() => RelationshipController>
19
-
20
- export function ContextualActions(props: PropsWithChildren<RelationshipProps>) {
21
- const { children, ...otherProps } = props
22
- return (
23
- <Grid gap="regular" alignItems="end" columns="minmax(0, 1fr) auto">
24
- {children}
25
- <ContextualActionsMenu {...otherProps} />
26
- </Grid>
27
- )
28
- }
29
-
30
- function ContextualActionsMenu(props: RelationshipProps) {
31
- const { field, onAdd, onChange } = props
32
-
33
- const foreignList = useList(field.refListKey)
34
- const relatedItemHref = useRelatedItemHref(props)
35
- const relatedItemLabel = useRelatedItemLabel(field)
36
- const allowAdd = !field.hideCreate && !!onChange
37
- const items = useMemo(() => {
38
- const result = []
39
- if (allowAdd) {
40
- result.push({
41
- icon: plusIcon,
42
- key: 'add',
43
- label: `Add ${foreignList.singular.toLocaleLowerCase()}`,
44
- })
45
- }
46
-
47
- result.push({
48
- key: 'view',
49
- icon: arrowUpRightIcon,
50
- href: relatedItemHref,
51
- label: relatedItemLabel,
52
- })
53
-
54
- return result
55
- }, [allowAdd, foreignList, relatedItemHref, relatedItemLabel])
56
-
57
- const onAction = (key: Key) => {
58
- switch (key) {
59
- case 'add': {
60
- onAdd()
61
- break
62
- }
63
- }
64
- }
65
-
66
- // we don't want to change the presence or lack thereof of a selected value
67
- // but since `allowAdd` is based on config, it's fairly static and showing
68
- // a menu when the menu will only have one item is quite silly
69
- if (!allowAdd) {
70
- return (
71
- <TooltipTrigger>
72
- <ActionButton {...(relatedItemHref ? { href: relatedItemHref } : { isDisabled: true })}>
73
- <Icon src={arrowUpRightIcon} />
74
- </ActionButton>
75
- <Tooltip>{relatedItemLabel}</Tooltip>
76
- </TooltipTrigger>
77
- )
78
- }
79
-
80
- return (
81
- <ActionMenu
82
- aria-label={`Actions for ${field.label}`}
83
- direction="bottom"
84
- align="end"
85
- isDisabled={items.length === 0}
86
- disabledKeys={relatedItemHref === null ? ['view'] : []}
87
- items={items}
88
- onAction={onAction}
89
- >
90
- {item => (
91
- <Item key={item.key} href={item.href ?? undefined} textValue={item.label}>
92
- <Icon src={item.icon} />
93
- <Text>{item.label}</Text>
94
- </Item>
95
- )}
96
- </ActionMenu>
97
- )
98
- }
99
-
100
- export function useRelatedItemLabel(field: RelationshipController) {
101
- const foreignList = useList(field.refListKey)
102
- if (field.many) {
103
- return `View related ${foreignList.plural.toLocaleLowerCase()}`
104
- }
105
- return `View ${foreignList.singular.toLocaleLowerCase()}`
106
- }
107
-
108
- export function useRelatedItemHref({
109
- field,
110
- value,
111
- }: Pick<FieldProps<() => RelationshipController>, 'field' | 'value'>) {
112
- const foreignList = useList(field.refListKey)
113
- if (value.kind === 'one') {
114
- if (!value.value) return null
115
- // the related item isn't actually created yet so we can't view it
116
- if (value.value.built) return null
117
-
118
- return `/${foreignList.path}/${value.value.id}`
119
- }
120
- let query: string | undefined
121
- if (field.refFieldKey && value.id !== null) {
122
- query = buildQueryForRelationshipFieldWithForeignField(foreignList, field.refFieldKey, value.id)
123
- } else if (value.kind === 'many' && value.value.length > 0) {
124
- query = `!id_in=${JSON.stringify(value.value.map(x => x.id))}`
125
- }
126
- if (query === undefined) return null
127
-
128
- return `/${foreignList.path}?${query}`
129
- }
130
-
131
- export function buildQueryForRelationshipFieldWithForeignField(
132
- foreignList: ListMeta,
133
- refFieldKey: string,
134
- localId: string
135
- ) {
136
- const foreignField = foreignList.fields[refFieldKey]
137
- const foreignMany: boolean = (foreignField.fieldMeta as any).many
138
- return `filter=${refFieldKey}_${foreignMany ? 'some' : 'is'}_${JSON.stringify(foreignMany ? [localId] : localId)}`
139
- }
1
+ import { type Key, type PropsWithChildren, useMemo } from 'react'
2
+
3
+ import { Icon } from '@keystar/ui/icon'
4
+ import { arrowUpRightIcon } from '@keystar/ui/icon/icons/arrowUpRightIcon'
5
+ import { plusIcon } from '@keystar/ui/icon/icons/plusIcon'
6
+ import { Grid } from '@keystar/ui/layout'
7
+ import { ActionMenu, Item } from '@keystar/ui/menu'
8
+ import { Text } from '@keystar/ui/typography'
9
+
10
+ import { useList } from '../../../../admin-ui/context'
11
+ import type { FieldProps, CollectionMeta } from '../../../../types'
12
+ import type { RelationshipController } from './types'
13
+ import { ActionButton } from '@keystar/ui/button'
14
+ import { Tooltip, TooltipTrigger } from '@keystar/ui/tooltip'
15
+
16
+ type RelationshipProps = {
17
+ onAdd: () => void
18
+ } & FieldProps<() => RelationshipController>
19
+
20
+ export function ContextualActions(props: PropsWithChildren<RelationshipProps>) {
21
+ const { children, ...otherProps } = props
22
+ return (
23
+ <Grid gap="regular" alignItems="end" columns="minmax(0, 1fr) auto">
24
+ {children}
25
+ <ContextualActionsMenu {...otherProps} />
26
+ </Grid>
27
+ )
28
+ }
29
+
30
+ function ContextualActionsMenu(props: RelationshipProps) {
31
+ const { field, onAdd, onChange } = props
32
+
33
+ const foreignList = useList(field.refListKey)
34
+ const relatedItemHref = useRelatedItemHref(props)
35
+ const relatedItemLabel = useRelatedItemLabel(field)
36
+ const allowAdd = !field.hideCreate && !!onChange
37
+ const items = useMemo(() => {
38
+ const result = []
39
+ if (allowAdd) {
40
+ result.push({
41
+ icon: plusIcon,
42
+ key: 'add',
43
+ label: `Add ${foreignList.singular.toLocaleLowerCase()}`,
44
+ })
45
+ }
46
+
47
+ result.push({
48
+ key: 'view',
49
+ icon: arrowUpRightIcon,
50
+ href: relatedItemHref,
51
+ label: relatedItemLabel,
52
+ })
53
+
54
+ return result
55
+ }, [allowAdd, foreignList, relatedItemHref, relatedItemLabel])
56
+
57
+ const onAction = (key: Key) => {
58
+ switch (key) {
59
+ case 'add': {
60
+ onAdd()
61
+ break
62
+ }
63
+ }
64
+ }
65
+
66
+ // we don't want to change the presence or lack thereof of a selected value
67
+ // but since `allowAdd` is based on config, it's fairly static and showing
68
+ // a menu when the menu will only have one item is quite silly
69
+ if (!allowAdd) {
70
+ return (
71
+ <TooltipTrigger>
72
+ <ActionButton {...(relatedItemHref ? { href: relatedItemHref } : { isDisabled: true })}>
73
+ <Icon src={arrowUpRightIcon} />
74
+ </ActionButton>
75
+ <Tooltip>{relatedItemLabel}</Tooltip>
76
+ </TooltipTrigger>
77
+ )
78
+ }
79
+
80
+ return (
81
+ <ActionMenu
82
+ aria-label={`Actions for ${field.label}`}
83
+ direction="bottom"
84
+ align="end"
85
+ isDisabled={items.length === 0}
86
+ disabledKeys={relatedItemHref === null ? ['view'] : []}
87
+ items={items}
88
+ onAction={onAction}
89
+ >
90
+ {item => (
91
+ <Item key={item.key} href={item.href ?? undefined} textValue={item.label}>
92
+ <Icon src={item.icon} />
93
+ <Text>{item.label}</Text>
94
+ </Item>
95
+ )}
96
+ </ActionMenu>
97
+ )
98
+ }
99
+
100
+ export function useRelatedItemLabel(field: RelationshipController) {
101
+ const foreignList = useList(field.refListKey)
102
+ if (field.many) {
103
+ return `View related ${foreignList.plural.toLocaleLowerCase()}`
104
+ }
105
+ return `View ${foreignList.singular.toLocaleLowerCase()}`
106
+ }
107
+
108
+ export function useRelatedItemHref({
109
+ field,
110
+ value,
111
+ }: Pick<FieldProps<() => RelationshipController>, 'field' | 'value'>) {
112
+ const foreignList = useList(field.refListKey)
113
+ if (value.kind === 'one') {
114
+ if (!value.value) return null
115
+ // the related item isn't actually created yet so we can't view it
116
+ if (value.value.built) return null
117
+
118
+ return `/${foreignList.path}/${value.value.id}`
119
+ }
120
+ let query: string | undefined
121
+ if (field.refFieldKey && value.id !== null) {
122
+ query = buildQueryForRelationshipFieldWithForeignField(foreignList, field.refFieldKey, value.id)
123
+ } else if (value.kind === 'many' && value.value.length > 0) {
124
+ query = `!id_in=${JSON.stringify(value.value.map(x => x.id))}`
125
+ }
126
+ if (query === undefined) return null
127
+
128
+ return `/${foreignList.path}?${query}`
129
+ }
130
+
131
+ export function buildQueryForRelationshipFieldWithForeignField(
132
+ foreignList: CollectionMeta,
133
+ refFieldKey: string,
134
+ localId: string
135
+ ) {
136
+ const foreignField = foreignList.fields[refFieldKey]
137
+ const foreignMany: boolean = (foreignField.fieldMeta as any).many
138
+ return `filter=${refFieldKey}_${foreignMany ? 'some' : 'is'}_${JSON.stringify(foreignMany ? [localId] : localId)}`
139
+ }