@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,11 +1,11 @@
1
1
  import { g } from '../../..'
2
2
  import { type ListMetaSource, getAdminMetaForRelationshipField } from '../../../lib/admin-meta'
3
3
  import {
4
- type BaseListTypeInfo,
4
+ type BaseCollectionTypeInfo,
5
5
  type CommonFieldConfig,
6
6
  type FieldTypeFunc,
7
7
  type JSONValue,
8
- type ListSortDescriptor,
8
+ type CollectionSortDescriptor,
9
9
  fieldType,
10
10
  } from '../../../types'
11
11
  import type { controller } from './views'
@@ -13,7 +13,7 @@ import type { controller } from './views'
13
13
  type ListKeyFromRef<Ref extends string> = Ref extends `${infer ListKey}.${string}` ? ListKey : Ref
14
14
 
15
15
  // This is the default display mode for Relationships
16
- type SelectDisplayConfig<ListTypeInfo extends BaseListTypeInfo, Ref extends string> = {
16
+ type SelectDisplayConfig<CollectionTypeInfo extends BaseCollectionTypeInfo, Ref extends string> = {
17
17
  ui?: {
18
18
  // Sets the relationship to display as a Select field
19
19
  displayMode?: 'select'
@@ -31,12 +31,12 @@ type SelectDisplayConfig<ListTypeInfo extends BaseListTypeInfo, Ref extends stri
31
31
  * The filter to apply when shown in the select.
32
32
  * Defaults to the initialSort (if set) on the related list.
33
33
  */
34
- filter?: ListTypeInfo['all']['lists'][ListKeyFromRef<Ref>]['inputs']['where']
34
+ filter?: CollectionTypeInfo['all']['lists'][ListKeyFromRef<Ref>]['inputs']['where']
35
35
  /**
36
36
  * The sort to apply when shown in the select.
37
37
  * Defaults to the initialSort (if set) on the related list.
38
38
  */
39
- sort?: ListSortDescriptor<string>
39
+ sort?: CollectionSortDescriptor<string>
40
40
  }
41
41
  }
42
42
 
@@ -56,7 +56,7 @@ type TableDisplayConfig = {
56
56
  many: true
57
57
  ui?: {
58
58
  displayMode: 'table'
59
- initialSort?: ListSortDescriptor<string>
59
+ initialSort?: CollectionSortDescriptor<string>
60
60
  columns?: string[]
61
61
 
62
62
  itemView: {
@@ -146,21 +146,21 @@ type FieldTypeInfo = {
146
146
  }
147
147
 
148
148
  export type RelationshipFieldConfig<
149
- ListTypeInfo extends BaseListTypeInfo,
150
- Ref extends `${keyof ListTypeInfo['all']['lists'] & string}${'' | `.${string}`}`,
151
- > = CommonFieldConfig<ListTypeInfo, FieldTypeInfo> & {
149
+ CollectionTypeInfo extends BaseCollectionTypeInfo,
150
+ Ref extends `${keyof CollectionTypeInfo['all']['lists'] & string}${'' | `.${string}`}`,
151
+ > = CommonFieldConfig<CollectionTypeInfo, FieldTypeInfo> & {
152
152
  many?: boolean
153
153
  ref: Ref
154
154
  ui?: {
155
155
  hideCreate?: boolean
156
156
  }
157
157
  } & (OneDbConfig | ManyDbConfig) &
158
- (SelectDisplayConfig<ListTypeInfo, Ref> | CountDisplayConfig | TableDisplayConfig)
158
+ (SelectDisplayConfig<CollectionTypeInfo, Ref> | CountDisplayConfig | TableDisplayConfig)
159
159
 
160
160
  export function relationship<
161
- ListTypeInfo extends BaseListTypeInfo,
162
- Ref extends `${keyof ListTypeInfo['all']['lists'] & string}${'' | `.${string}`}`,
163
- >({ ref, ...config }: RelationshipFieldConfig<ListTypeInfo, Ref>): FieldTypeFunc<ListTypeInfo> {
161
+ CollectionTypeInfo extends BaseCollectionTypeInfo,
162
+ Ref extends `${keyof CollectionTypeInfo['all']['lists'] & string}${'' | `.${string}`}`,
163
+ >({ ref, ...config }: RelationshipFieldConfig<CollectionTypeInfo, Ref>): FieldTypeFunc<CollectionTypeInfo> {
164
164
  const { many = false } = config
165
165
  const [foreignListKey, foreignFieldKey] = ref.split('.') as [string, string | undefined]
166
166
 
@@ -1,110 +1,110 @@
1
- import { ComboboxMulti, Item } from '@keystar/ui/combobox'
2
- import { css } from '@keystar/ui/style'
3
-
4
- import type { ListMeta, ListSortDescriptor } from '../../../../types'
5
- import type { RelationshipValue } from './types'
6
- import { useApolloQuery } from './useApolloQuery'
7
- import { useState } from 'react'
8
-
9
- export function ComboboxMany({
10
- extraSelection = '',
11
- forceValidation,
12
- isDisabled,
13
- isLoading,
14
- isReadOnly,
15
- isRequired,
16
- labelField,
17
- list,
18
- searchFields,
19
- state,
20
- filter,
21
- sort,
22
- ...props
23
- }: {
24
- autoFocus?: boolean
25
- description?: string
26
- forceValidation?: boolean
27
- isDisabled?: boolean
28
- isLoading?: boolean
29
- isReadOnly?: boolean
30
- isRequired?: boolean
31
- label?: string
32
- 'aria-label'?: string
33
- labelField: string
34
- searchFields: string[]
35
- list: ListMeta
36
- placeholder?: string
37
- filter?: Record<string, any> | null
38
- sort?: ListSortDescriptor<string> | null
39
- state: {
40
- kind: 'many'
41
- value: RelationshipValue[]
42
- onChange(value: RelationshipValue[]): void
43
- }
44
- extraSelection?: string
45
- }) {
46
- const { data, loadingState, error, onLoadMore, search, setSearch } = useApolloQuery({
47
- labelField,
48
- list,
49
- searchFields,
50
- state,
51
- filter,
52
- sort,
53
- })
54
- const [shouldShowErrors, setShouldShowErrors] = useState(false)
55
- const validationMessages =
56
- isRequired && state.value.length === 0 ? [`At least one ${list.singular} is required`] : []
57
-
58
- // TODO: better error UI
59
- // TODO: Handle permission errors
60
- // (ie; user has permission to read this relationship field, but
61
- // not the related list, or some items on the list)
62
- if (error) return <span>Error</span>
63
-
64
- const items: RelationshipValue[] = data?.items?.map(x => ({ ...x, built: false })) ?? []
65
- const fetchedIds = new Set(items.map(item => item.id))
66
-
67
- for (const item of state.value) {
68
- if (!fetchedIds.has(item.id)) {
69
- items.push(item)
70
- }
71
- }
72
-
73
- return (
74
- <ComboboxMulti
75
- {...props}
76
- isDisabled={isDisabled || isReadOnly}
77
- isRequired={isRequired}
78
- items={items}
79
- loadingState={loadingState}
80
- errorMessage={
81
- !!validationMessages.length && (shouldShowErrors || forceValidation)
82
- ? validationMessages.join('. ')
83
- : undefined
84
- }
85
- onBlur={() => {
86
- setShouldShowErrors(true)
87
- }}
88
- onInputChange={setSearch}
89
- inputValue={search}
90
- onLoadMore={onLoadMore}
91
- selectedKeys={state.value?.map(item => item.id.toString())}
92
- onSelectionChange={selection => {
93
- // TODO
94
- if (selection === 'all') return
95
-
96
- const selectedItems = items.filter(item => selection.has(item.id.toString()))
97
- state.onChange(selectedItems)
98
- }}
99
- minWidth="alias.singleLineWidth"
100
- width="auto"
101
- UNSAFE_className={css({
102
- // This should probably be addressed in @keystar/ui/combobox
103
- // - the mobile variant should respect the `width` prop
104
- '[role="button"]': { width: 'auto' },
105
- })}
106
- >
107
- {item => <Item>{item.label || item.id}</Item>}
108
- </ComboboxMulti>
109
- )
110
- }
1
+ import { ComboboxMulti, Item } from '@keystar/ui/combobox'
2
+ import { css } from '@keystar/ui/style'
3
+
4
+ import type { CollectionMeta, CollectionSortDescriptor } from '../../../../types'
5
+ import type { RelationshipValue } from './types'
6
+ import { useApolloQuery } from './useApolloQuery'
7
+ import { useState } from 'react'
8
+
9
+ export function ComboboxMany({
10
+ extraSelection = '',
11
+ forceValidation,
12
+ isDisabled,
13
+ isLoading,
14
+ isReadOnly,
15
+ isRequired,
16
+ labelField,
17
+ list,
18
+ searchFields,
19
+ state,
20
+ filter,
21
+ sort,
22
+ ...props
23
+ }: {
24
+ autoFocus?: boolean
25
+ description?: string
26
+ forceValidation?: boolean
27
+ isDisabled?: boolean
28
+ isLoading?: boolean
29
+ isReadOnly?: boolean
30
+ isRequired?: boolean
31
+ label?: string
32
+ 'aria-label'?: string
33
+ labelField: string
34
+ searchFields: string[]
35
+ list: CollectionMeta
36
+ placeholder?: string
37
+ filter?: Record<string, any> | null
38
+ sort?: CollectionSortDescriptor<string> | null
39
+ state: {
40
+ kind: 'many'
41
+ value: RelationshipValue[]
42
+ onChange(value: RelationshipValue[]): void
43
+ }
44
+ extraSelection?: string
45
+ }) {
46
+ const { data, loadingState, error, onLoadMore, search, setSearch } = useApolloQuery({
47
+ labelField,
48
+ list,
49
+ searchFields,
50
+ state,
51
+ filter,
52
+ sort,
53
+ })
54
+ const [shouldShowErrors, setShouldShowErrors] = useState(false)
55
+ const validationMessages =
56
+ isRequired && state.value.length === 0 ? [`At least one ${list.singular} is required`] : []
57
+
58
+ // TODO: better error UI
59
+ // TODO: Handle permission errors
60
+ // (ie; user has permission to read this relationship field, but
61
+ // not the related list, or some items on the list)
62
+ if (error) return <span>Error</span>
63
+
64
+ const items: RelationshipValue[] = data?.items?.map(x => ({ ...x, built: false })) ?? []
65
+ const fetchedIds = new Set(items.map(item => item.id))
66
+
67
+ for (const item of state.value) {
68
+ if (!fetchedIds.has(item.id)) {
69
+ items.push(item)
70
+ }
71
+ }
72
+
73
+ return (
74
+ <ComboboxMulti
75
+ {...props}
76
+ isDisabled={isDisabled || isReadOnly}
77
+ isRequired={isRequired}
78
+ items={items}
79
+ loadingState={loadingState}
80
+ errorMessage={
81
+ !!validationMessages.length && (shouldShowErrors || forceValidation)
82
+ ? validationMessages.join('. ')
83
+ : undefined
84
+ }
85
+ onBlur={() => {
86
+ setShouldShowErrors(true)
87
+ }}
88
+ onInputChange={setSearch}
89
+ inputValue={search}
90
+ onLoadMore={onLoadMore}
91
+ selectedKeys={state.value?.map(item => item.id.toString())}
92
+ onSelectionChange={selection => {
93
+ // TODO
94
+ if (selection === 'all') return
95
+
96
+ const selectedItems = items.filter(item => selection.has(item.id.toString()))
97
+ state.onChange(selectedItems)
98
+ }}
99
+ minWidth="alias.singleLineWidth"
100
+ width="auto"
101
+ UNSAFE_className={css({
102
+ // This should probably be addressed in @keystar/ui/combobox
103
+ // - the mobile variant should respect the `width` prop
104
+ '[role="button"]': { width: 'auto' },
105
+ })}
106
+ >
107
+ {item => <Item>{item.label || item.id}</Item>}
108
+ </ComboboxMulti>
109
+ )
110
+ }
@@ -1,115 +1,115 @@
1
- import { useState } from 'react'
2
-
3
- import { Combobox, Item } from '@keystar/ui/combobox'
4
- import { css } from '@keystar/ui/style'
5
-
6
- import type { ListMeta, ListSortDescriptor } from '../../../../types'
7
- import type { RelationshipValue } from './types'
8
- import { useApolloQuery } from './useApolloQuery'
9
-
10
- export function ComboboxSingle({
11
- forceValidation,
12
- isLoading,
13
- isRequired,
14
- labelField,
15
- list,
16
- searchFields,
17
- state,
18
- filter,
19
- sort,
20
- ...props
21
- }: {
22
- autoFocus?: boolean
23
- description?: string
24
- forceValidation?: boolean
25
- isDisabled?: boolean
26
- isLoading?: boolean
27
- isReadOnly?: boolean
28
- isRequired?: boolean
29
- label?: string
30
- labelField: string
31
- searchFields: string[]
32
- list: ListMeta
33
- placeholder?: string
34
- filter?: Record<string, any> | null
35
- sort?: ListSortDescriptor<string> | null
36
- state: {
37
- kind: 'one'
38
- value: RelationshipValue | null
39
- onChange(value: RelationshipValue | null): void
40
- }
41
- }) {
42
- const { data, loading, error, onLoadMore, search, setSearch } = useApolloQuery({
43
- labelField,
44
- list,
45
- searchFields,
46
- state,
47
- filter,
48
- sort,
49
- })
50
-
51
- const [shouldShowErrors, setShouldShowErrors] = useState(false)
52
- const validationMessages =
53
- isRequired && state.value === null ? [`A ${list.singular} is required`] : []
54
- const [lastSeenStateValue, setLastSeenStateValue] = useState(state.value)
55
-
56
- if (state.value !== lastSeenStateValue) {
57
- setLastSeenStateValue(state.value)
58
- setSearch(state.value?.label ?? '')
59
- }
60
-
61
- // TODO: better error UI
62
- // TODO: Handle permission errors
63
- // (ie; user has permission to read this relationship field, but
64
- // not the related list, or some items on the list)
65
- if (error) return <span>Error</span>
66
-
67
- const items: RelationshipValue[] = data?.items?.map(x => ({ ...x, built: false })) ?? []
68
-
69
- if (
70
- state.value !== null &&
71
- (state.value.built || !items.some(item => item.id === state.value?.id))
72
- ) {
73
- items.push(state.value)
74
- }
75
-
76
- return (
77
- <Combobox
78
- {...props}
79
- isRequired={isRequired}
80
- items={items}
81
- loadingState={loading || isLoading ? 'loading' : 'idle'}
82
- errorMessage={
83
- !!validationMessages.length && (shouldShowErrors || forceValidation)
84
- ? validationMessages.join('. ')
85
- : undefined
86
- }
87
- onBlur={() => {
88
- setShouldShowErrors(true)
89
- }}
90
- onInputChange={input => {
91
- setSearch(input)
92
-
93
- // unset the selected value when the user clears the input
94
- if (input === '') state.onChange(null)
95
- }}
96
- inputValue={search}
97
- onLoadMore={onLoadMore}
98
- selectedKey={state.value ? state.value.id.toString() : null}
99
- onSelectionChange={key => {
100
- const selectedItem = items.find(item => item.id.toString() === key) ?? null
101
- state.onChange(selectedItem)
102
- setSearch(selectedItem?.label ?? '')
103
- }}
104
- minWidth="alias.singleLineWidth"
105
- width="auto"
106
- UNSAFE_className={css({
107
- // This should probably be addressed in @keystar/ui/combobox
108
- // - the mobile variant should respect the `width` prop
109
- '[role="button"]': { width: 'auto' },
110
- })}
111
- >
112
- {item => <Item>{item.label || item.id}</Item>}
113
- </Combobox>
114
- )
115
- }
1
+ import { useState } from 'react'
2
+
3
+ import { Combobox, Item } from '@keystar/ui/combobox'
4
+ import { css } from '@keystar/ui/style'
5
+
6
+ import type { CollectionMeta, CollectionSortDescriptor } from '../../../../types'
7
+ import type { RelationshipValue } from './types'
8
+ import { useApolloQuery } from './useApolloQuery'
9
+
10
+ export function ComboboxSingle({
11
+ forceValidation,
12
+ isLoading,
13
+ isRequired,
14
+ labelField,
15
+ list,
16
+ searchFields,
17
+ state,
18
+ filter,
19
+ sort,
20
+ ...props
21
+ }: {
22
+ autoFocus?: boolean
23
+ description?: string
24
+ forceValidation?: boolean
25
+ isDisabled?: boolean
26
+ isLoading?: boolean
27
+ isReadOnly?: boolean
28
+ isRequired?: boolean
29
+ label?: string
30
+ labelField: string
31
+ searchFields: string[]
32
+ list: CollectionMeta
33
+ placeholder?: string
34
+ filter?: Record<string, any> | null
35
+ sort?: CollectionSortDescriptor<string> | null
36
+ state: {
37
+ kind: 'one'
38
+ value: RelationshipValue | null
39
+ onChange(value: RelationshipValue | null): void
40
+ }
41
+ }) {
42
+ const { data, loading, error, onLoadMore, search, setSearch } = useApolloQuery({
43
+ labelField,
44
+ list,
45
+ searchFields,
46
+ state,
47
+ filter,
48
+ sort,
49
+ })
50
+
51
+ const [shouldShowErrors, setShouldShowErrors] = useState(false)
52
+ const validationMessages =
53
+ isRequired && state.value === null ? [`A ${list.singular} is required`] : []
54
+ const [lastSeenStateValue, setLastSeenStateValue] = useState(state.value)
55
+
56
+ if (state.value !== lastSeenStateValue) {
57
+ setLastSeenStateValue(state.value)
58
+ setSearch(state.value?.label ?? '')
59
+ }
60
+
61
+ // TODO: better error UI
62
+ // TODO: Handle permission errors
63
+ // (ie; user has permission to read this relationship field, but
64
+ // not the related list, or some items on the list)
65
+ if (error) return <span>Error</span>
66
+
67
+ const items: RelationshipValue[] = data?.items?.map(x => ({ ...x, built: false })) ?? []
68
+
69
+ if (
70
+ state.value !== null &&
71
+ (state.value.built || !items.some(item => item.id === state.value?.id))
72
+ ) {
73
+ items.push(state.value)
74
+ }
75
+
76
+ return (
77
+ <Combobox
78
+ {...props}
79
+ isRequired={isRequired}
80
+ items={items}
81
+ loadingState={loading || isLoading ? 'loading' : 'idle'}
82
+ errorMessage={
83
+ !!validationMessages.length && (shouldShowErrors || forceValidation)
84
+ ? validationMessages.join('. ')
85
+ : undefined
86
+ }
87
+ onBlur={() => {
88
+ setShouldShowErrors(true)
89
+ }}
90
+ onInputChange={input => {
91
+ setSearch(input)
92
+
93
+ // unset the selected value when the user clears the input
94
+ if (input === '') state.onChange(null)
95
+ }}
96
+ inputValue={search}
97
+ onLoadMore={onLoadMore}
98
+ selectedKey={state.value ? state.value.id.toString() : null}
99
+ onSelectionChange={key => {
100
+ const selectedItem = items.find(item => item.id.toString() === key) ?? null
101
+ state.onChange(selectedItem)
102
+ setSearch(selectedItem?.label ?? '')
103
+ }}
104
+ minWidth="alias.singleLineWidth"
105
+ width="auto"
106
+ UNSAFE_className={css({
107
+ // This should probably be addressed in @keystar/ui/combobox
108
+ // - the mobile variant should respect the `width` prop
109
+ '[role="button"]': { width: 'auto' },
110
+ })}
111
+ >
112
+ {item => <Item>{item.label || item.id}</Item>}
113
+ </Combobox>
114
+ )
115
+ }