@nixxie-cms/core 1.0.3 → 2.0.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 (203) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/CHANGES-1.1.md +134 -0
  3. package/context/dist/nixxie-cms-core-context.cjs.js +4 -3
  4. package/context/dist/nixxie-cms-core-context.esm.js +3 -2
  5. package/dist/declarations/src/access.d.ts +2 -2
  6. package/dist/declarations/src/access.d.ts.map +1 -1
  7. package/dist/declarations/src/admin-ui/components/Navigation.d.ts +2 -2
  8. package/dist/declarations/src/admin-ui/components/Navigation.d.ts.map +1 -1
  9. package/dist/declarations/src/admin-ui/context.d.ts +6 -6
  10. package/dist/declarations/src/admin-ui/context.d.ts.map +1 -1
  11. package/dist/declarations/src/admin-ui/utils/Fields.d.ts +3 -3
  12. package/dist/declarations/src/admin-ui/utils/Fields.d.ts.map +1 -1
  13. package/dist/declarations/src/admin-ui/utils/filters.d.ts +5 -5
  14. package/dist/declarations/src/admin-ui/utils/filters.d.ts.map +1 -1
  15. package/dist/declarations/src/admin-ui/utils/useCreateItem.d.ts +3 -3
  16. package/dist/declarations/src/admin-ui/utils/useCreateItem.d.ts.map +1 -1
  17. package/dist/declarations/src/admin-ui/utils/utils.d.ts +2 -2
  18. package/dist/declarations/src/admin-ui/utils/utils.d.ts.map +1 -1
  19. package/dist/declarations/src/context.d.ts +1 -1
  20. package/dist/declarations/src/context.d.ts.map +1 -1
  21. package/dist/declarations/src/fields/types/bigInt/index.d.ts +3 -3
  22. package/dist/declarations/src/fields/types/bigInt/index.d.ts.map +1 -1
  23. package/dist/declarations/src/fields/types/bytes/index.d.ts +3 -3
  24. package/dist/declarations/src/fields/types/bytes/index.d.ts.map +1 -1
  25. package/dist/declarations/src/fields/types/calendarDay/index.d.ts +3 -3
  26. package/dist/declarations/src/fields/types/calendarDay/index.d.ts.map +1 -1
  27. package/dist/declarations/src/fields/types/checkbox/index.d.ts +3 -3
  28. package/dist/declarations/src/fields/types/checkbox/index.d.ts.map +1 -1
  29. package/dist/declarations/src/fields/types/decimal/index.d.ts +3 -3
  30. package/dist/declarations/src/fields/types/decimal/index.d.ts.map +1 -1
  31. package/dist/declarations/src/fields/types/file/index.d.ts +4 -4
  32. package/dist/declarations/src/fields/types/file/index.d.ts.map +1 -1
  33. package/dist/declarations/src/fields/types/float/index.d.ts +3 -3
  34. package/dist/declarations/src/fields/types/float/index.d.ts.map +1 -1
  35. package/dist/declarations/src/fields/types/image/index.d.ts +4 -4
  36. package/dist/declarations/src/fields/types/image/index.d.ts.map +1 -1
  37. package/dist/declarations/src/fields/types/integer/index.d.ts +3 -3
  38. package/dist/declarations/src/fields/types/integer/index.d.ts.map +1 -1
  39. package/dist/declarations/src/fields/types/json/index.d.ts +3 -3
  40. package/dist/declarations/src/fields/types/json/index.d.ts.map +1 -1
  41. package/dist/declarations/src/fields/types/multiselect/index.d.ts +3 -3
  42. package/dist/declarations/src/fields/types/multiselect/index.d.ts.map +1 -1
  43. package/dist/declarations/src/fields/types/multiselect/views/index.d.ts.map +1 -1
  44. package/dist/declarations/src/fields/types/password/index.d.ts +3 -3
  45. package/dist/declarations/src/fields/types/password/index.d.ts.map +1 -1
  46. package/dist/declarations/src/fields/types/relationship/index.d.ts +8 -8
  47. package/dist/declarations/src/fields/types/relationship/index.d.ts.map +1 -1
  48. package/dist/declarations/src/fields/types/relationship/views/ComboboxMany.d.ts +3 -3
  49. package/dist/declarations/src/fields/types/relationship/views/ComboboxMany.d.ts.map +1 -1
  50. package/dist/declarations/src/fields/types/relationship/views/ComboboxSingle.d.ts +3 -3
  51. package/dist/declarations/src/fields/types/relationship/views/ComboboxSingle.d.ts.map +1 -1
  52. package/dist/declarations/src/fields/types/relationship/views/index.d.ts +3 -3
  53. package/dist/declarations/src/fields/types/relationship/views/index.d.ts.map +1 -1
  54. package/dist/declarations/src/fields/types/relationship/views/types.d.ts +3 -3
  55. package/dist/declarations/src/fields/types/relationship/views/types.d.ts.map +1 -1
  56. package/dist/declarations/src/fields/types/select/index.d.ts +3 -3
  57. package/dist/declarations/src/fields/types/select/index.d.ts.map +1 -1
  58. package/dist/declarations/src/fields/types/text/index.d.ts +3 -3
  59. package/dist/declarations/src/fields/types/text/index.d.ts.map +1 -1
  60. package/dist/declarations/src/fields/types/timestamp/index.d.ts +3 -3
  61. package/dist/declarations/src/fields/types/timestamp/index.d.ts.map +1 -1
  62. package/dist/declarations/src/fields/types/virtual/index.d.ts +7 -7
  63. package/dist/declarations/src/fields/types/virtual/index.d.ts.map +1 -1
  64. package/dist/declarations/src/helpers.d.ts +249 -13
  65. package/dist/declarations/src/helpers.d.ts.map +1 -1
  66. package/dist/declarations/src/index.d.ts +9 -4
  67. package/dist/declarations/src/index.d.ts.map +1 -1
  68. package/dist/declarations/src/internal-unstable/admin-ui/pages/ListPage/index.d.ts.map +1 -1
  69. package/dist/declarations/src/lib/admin-meta.d.ts +11 -11
  70. package/dist/declarations/src/lib/admin-meta.d.ts.map +1 -1
  71. package/dist/declarations/src/lib/core/access-control.d.ts +18 -18
  72. package/dist/declarations/src/lib/core/access-control.d.ts.map +1 -1
  73. package/dist/declarations/src/lib/core/cascade.d.ts +47 -0
  74. package/dist/declarations/src/lib/core/cascade.d.ts.map +1 -0
  75. package/dist/declarations/src/lib/core/initialise-lists.d.ts +27 -24
  76. package/dist/declarations/src/lib/core/initialise-lists.d.ts.map +1 -1
  77. package/dist/declarations/src/lib/env.d.ts +9 -0
  78. package/dist/declarations/src/lib/env.d.ts.map +1 -0
  79. package/dist/declarations/src/lib/system.d.ts +1 -1
  80. package/dist/declarations/src/lib/system.d.ts.map +1 -1
  81. package/dist/declarations/src/list-features.d.ts +162 -0
  82. package/dist/declarations/src/list-features.d.ts.map +1 -0
  83. package/dist/declarations/src/schema.d.ts +24 -23
  84. package/dist/declarations/src/schema.d.ts.map +1 -1
  85. package/dist/declarations/src/session.d.ts +75 -0
  86. package/dist/declarations/src/session.d.ts.map +1 -1
  87. package/dist/declarations/src/types/admin-meta.d.ts +11 -11
  88. package/dist/declarations/src/types/admin-meta.d.ts.map +1 -1
  89. package/dist/declarations/src/types/config/access-control.d.ts +42 -42
  90. package/dist/declarations/src/types/config/access-control.d.ts.map +1 -1
  91. package/dist/declarations/src/types/config/fields.d.ts +19 -19
  92. package/dist/declarations/src/types/config/fields.d.ts.map +1 -1
  93. package/dist/declarations/src/types/config/hooks.d.ts +131 -131
  94. package/dist/declarations/src/types/config/hooks.d.ts.map +1 -1
  95. package/dist/declarations/src/types/config/index.d.ts +190 -8
  96. package/dist/declarations/src/types/config/index.d.ts.map +1 -1
  97. package/dist/declarations/src/types/config/lists.d.ts +146 -108
  98. package/dist/declarations/src/types/config/lists.d.ts.map +1 -1
  99. package/dist/declarations/src/types/context.d.ts +507 -47
  100. package/dist/declarations/src/types/context.d.ts.map +1 -1
  101. package/dist/declarations/src/types/next-fields.d.ts +28 -28
  102. package/dist/declarations/src/types/next-fields.d.ts.map +1 -1
  103. package/dist/declarations/src/types/type-info.d.ts +3 -3
  104. package/dist/declarations/src/types/type-info.d.ts.map +1 -1
  105. package/dist/{express-455ae20c.cjs.js → express-84d534c2.cjs.js} +6 -6
  106. package/dist/{express-7559ca2d.esm.js → express-d0a4ce99.esm.js} +6 -6
  107. package/dist/{index-15c8f81e.esm.js → index-5d8b0b4e.esm.js} +363 -183
  108. package/dist/index-6055753b.cjs.js +393 -0
  109. package/dist/{index-42045902.cjs.js → index-ac29f382.cjs.js} +363 -185
  110. package/dist/index-f1703b7b.esm.js +386 -0
  111. package/dist/nixxie-cms-core.cjs.js +1388 -30
  112. package/dist/nixxie-cms-core.esm.js +1362 -24
  113. package/dist/{non-null-graphql-add6bb3d.cjs.js → non-null-graphql-4a44c122.cjs.js} +1 -1
  114. package/dist/{non-null-graphql-a84ed64d.esm.js → non-null-graphql-8c5feaae.esm.js} +1 -1
  115. package/dist/{resolve-hooks-165a9ce2.cjs.js → resolve-hooks-10a5f84c.cjs.js} +240 -6
  116. package/dist/{resolve-hooks-6813a045.esm.js → resolve-hooks-9e676794.esm.js} +238 -7
  117. package/dist/{system-a321642d.cjs.js → system-6b37a5f8.cjs.js} +33 -7
  118. package/dist/{system-03e49e4f.esm.js → system-e591d821.esm.js} +33 -7
  119. package/fields/dist/nixxie-cms-core-fields.cjs.js +29 -576
  120. package/fields/dist/nixxie-cms-core-fields.esm.js +18 -565
  121. package/fields/types/bytes/dist/nixxie-cms-core-fields-types-bytes.cjs.js +4 -2
  122. package/fields/types/bytes/dist/nixxie-cms-core-fields-types-bytes.esm.js +4 -2
  123. package/fields/types/multiselect/views/dist/nixxie-cms-core-fields-types-multiselect-views.cjs.js +1 -6
  124. package/fields/types/multiselect/views/dist/nixxie-cms-core-fields-types-multiselect-views.esm.js +1 -6
  125. package/fields/types/password/dist/nixxie-cms-core-fields-types-password.cjs.js +4 -2
  126. package/fields/types/password/dist/nixxie-cms-core-fields-types-password.esm.js +4 -2
  127. package/internal-unstable/artifacts/dist/nixxie-cms-core-internal-unstable-artifacts.cjs.js +4 -3
  128. package/internal-unstable/artifacts/dist/nixxie-cms-core-internal-unstable-artifacts.esm.js +4 -3
  129. package/package.json +4 -4
  130. package/scripts/cli/dist/nixxie-cms-core-scripts-cli.cjs.js +4 -3
  131. package/scripts/cli/dist/nixxie-cms-core-scripts-cli.esm.js +4 -3
  132. package/scripts/dist/nixxie-cms-core-scripts.cjs.js +4 -3
  133. package/scripts/dist/nixxie-cms-core-scripts.esm.js +4 -3
  134. package/session/dist/nixxie-cms-core-session.cjs.js +286 -0
  135. package/session/dist/nixxie-cms-core-session.esm.js +279 -1
  136. package/src/access.ts +25 -25
  137. package/src/admin-ui/admin-meta-graphql.ts +5 -5
  138. package/src/admin-ui/components/CreateButtonLink.tsx +46 -46
  139. package/src/admin-ui/components/Navigation.tsx +3 -3
  140. package/src/admin-ui/context.tsx +6 -6
  141. package/src/admin-ui/utils/Fields.tsx +241 -241
  142. package/src/admin-ui/utils/actionData.ts +36 -36
  143. package/src/admin-ui/utils/filters.ts +148 -148
  144. package/src/admin-ui/utils/useCreateItem.ts +171 -171
  145. package/src/admin-ui/utils/utils.tsx +127 -127
  146. package/src/context.ts +1 -1
  147. package/src/fields/non-null-graphql.ts +115 -115
  148. package/src/fields/types/bigInt/index.ts +6 -6
  149. package/src/fields/types/bytes/index.ts +6 -6
  150. package/src/fields/types/calendarDay/index.ts +18 -19
  151. package/src/fields/types/checkbox/index.ts +6 -6
  152. package/src/fields/types/decimal/index.ts +6 -6
  153. package/src/fields/types/file/index.ts +8 -8
  154. package/src/fields/types/float/index.ts +6 -6
  155. package/src/fields/types/image/index.ts +8 -8
  156. package/src/fields/types/integer/index.ts +6 -6
  157. package/src/fields/types/json/index.ts +5 -5
  158. package/src/fields/types/multiselect/index.ts +7 -7
  159. package/src/fields/types/multiselect/views/index.tsx +149 -151
  160. package/src/fields/types/password/index.ts +6 -6
  161. package/src/fields/types/relationship/index.ts +13 -13
  162. package/src/fields/types/relationship/views/ComboboxMany.tsx +110 -110
  163. package/src/fields/types/relationship/views/ComboboxSingle.tsx +115 -115
  164. package/src/fields/types/relationship/views/ContextualActions.tsx +139 -139
  165. package/src/fields/types/relationship/views/index.tsx +492 -492
  166. package/src/fields/types/relationship/views/types.ts +46 -46
  167. package/src/fields/types/relationship/views/useApolloQuery.ts +185 -185
  168. package/src/fields/types/relationship/views/useFilter.tsx +109 -109
  169. package/src/fields/types/select/index.ts +6 -6
  170. package/src/fields/types/text/index.ts +6 -6
  171. package/src/fields/types/timestamp/index.ts +23 -21
  172. package/src/fields/types/virtual/index.ts +11 -11
  173. package/src/helpers.ts +773 -42
  174. package/src/index.ts +66 -24
  175. package/src/internal-unstable/admin-ui/pages/ItemPage/common.tsx +4 -4
  176. package/src/internal-unstable/admin-ui/pages/ItemPage/index.tsx +5 -5
  177. package/src/internal-unstable/admin-ui/pages/ListPage/index.tsx +8 -8
  178. package/src/lib/admin-meta.ts +369 -369
  179. package/src/lib/context/createContext.ts +6 -0
  180. package/src/lib/core/access-control.ts +434 -434
  181. package/src/lib/core/cascade.ts +236 -0
  182. package/src/lib/core/initialise-lists.ts +49 -33
  183. package/src/lib/core/mutations/index.ts +7 -0
  184. package/src/lib/core/mutations/nested-mutation-many-input-resolvers.ts +145 -145
  185. package/src/lib/core/mutations/nested-mutation-one-input-resolvers.ts +71 -71
  186. package/src/lib/core/queries/output-field.ts +178 -178
  187. package/src/lib/env.ts +50 -0
  188. package/src/lib/id-field.ts +2 -2
  189. package/src/lib/system.ts +221 -207
  190. package/src/lib/typescript-schema-printer.ts +227 -227
  191. package/src/list-features.ts +476 -0
  192. package/src/schema.ts +92 -22
  193. package/src/session.ts +225 -0
  194. package/src/types/admin-meta.ts +218 -218
  195. package/src/types/config/access-control.ts +186 -186
  196. package/src/types/config/fields.ts +96 -96
  197. package/src/types/config/hooks.ts +529 -529
  198. package/src/types/config/index.ts +206 -7
  199. package/src/types/config/lists.ts +606 -565
  200. package/src/types/context.ts +592 -55
  201. package/src/types/next-fields.ts +31 -31
  202. package/src/types/type-info.ts +38 -38
  203. 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
+ }