@burojs/ui-shadcn 0.4.0 → 0.5.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 (137) hide show
  1. package/README.md +22 -5
  2. package/dist/bulk-summary-CwSd6Yjb.cjs +108 -0
  3. package/dist/bulk-summary-CwSd6Yjb.cjs.map +1 -0
  4. package/dist/bulk-summary-GxMDP4bw.js +108 -0
  5. package/dist/bulk-summary-GxMDP4bw.js.map +1 -0
  6. package/dist/button-BxnnE28H.cjs +55 -0
  7. package/dist/button-BxnnE28H.cjs.map +1 -0
  8. package/dist/button-MWVeZaqj.js +44 -0
  9. package/dist/button-MWVeZaqj.js.map +1 -0
  10. package/dist/candidates-gate-CX2PBu6l.js +63 -0
  11. package/dist/candidates-gate-CX2PBu6l.js.map +1 -0
  12. package/dist/candidates-gate-DiFI52r7.cjs +70 -0
  13. package/dist/candidates-gate-DiFI52r7.cjs.map +1 -0
  14. package/dist/candidates-relation-picker-CBsVcnPR.js +82 -0
  15. package/dist/candidates-relation-picker-CBsVcnPR.js.map +1 -0
  16. package/dist/candidates-relation-picker-Dd_GVLlu.cjs +84 -0
  17. package/dist/candidates-relation-picker-Dd_GVLlu.cjs.map +1 -0
  18. package/dist/check-confirm-dialog-BRByTFpg.cjs +1534 -0
  19. package/dist/check-confirm-dialog-BRByTFpg.cjs.map +1 -0
  20. package/dist/check-confirm-dialog-CkaVTPT9.js +1232 -0
  21. package/dist/check-confirm-dialog-CkaVTPT9.js.map +1 -0
  22. package/dist/dialog-B_rt9S1c.js +63 -0
  23. package/dist/dialog-B_rt9S1c.js.map +1 -0
  24. package/dist/dialog-DRNUq9EA.cjs +112 -0
  25. package/dist/dialog-DRNUq9EA.cjs.map +1 -0
  26. package/dist/filter-builder-B3mZFL-K.cjs +1761 -0
  27. package/dist/filter-builder-B3mZFL-K.cjs.map +1 -0
  28. package/dist/filter-builder-DOnaXNfP.js +1605 -0
  29. package/dist/filter-builder-DOnaXNfP.js.map +1 -0
  30. package/dist/flow-runtime-B6zOHYqI.cjs +255 -0
  31. package/dist/flow-runtime-B6zOHYqI.cjs.map +1 -0
  32. package/dist/flow-runtime-D8h99FSg.js +252 -0
  33. package/dist/flow-runtime-D8h99FSg.js.map +1 -0
  34. package/dist/flow-screen-CX05b3G9.cjs +705 -0
  35. package/dist/flow-screen-CX05b3G9.cjs.map +1 -0
  36. package/dist/flow-screen-CmbARSqg.js +701 -0
  37. package/dist/flow-screen-CmbARSqg.js.map +1 -0
  38. package/dist/index.cjs +2825 -11912
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.d.cts +1612 -1862
  41. package/dist/index.d.ts +1700 -1950
  42. package/dist/index.js +2498 -11605
  43. package/dist/index.js.map +1 -1
  44. package/dist/inline-flow-host-B7U1OJUe.cjs +112 -0
  45. package/dist/inline-flow-host-B7U1OJUe.cjs.map +1 -0
  46. package/dist/inline-flow-host-C3H2UpSA.js +112 -0
  47. package/dist/inline-flow-host-C3H2UpSA.js.map +1 -0
  48. package/dist/input-BI0czGyI.cjs +23 -0
  49. package/dist/input-BI0czGyI.cjs.map +1 -0
  50. package/dist/input-CUPStzZO.js +17 -0
  51. package/dist/input-CUPStzZO.js.map +1 -0
  52. package/dist/label-66W1qbke.js +29 -0
  53. package/dist/label-66W1qbke.js.map +1 -0
  54. package/dist/label-w6JYf2cF.cjs +36 -0
  55. package/dist/label-w6JYf2cF.cjs.map +1 -0
  56. package/dist/lazy-relation-picker-dialog-2oxnA6IR.js +14 -0
  57. package/dist/lazy-relation-picker-dialog-2oxnA6IR.js.map +1 -0
  58. package/dist/lazy-relation-picker-dialog-BfAGKzUG.cjs +19 -0
  59. package/dist/lazy-relation-picker-dialog-BfAGKzUG.cjs.map +1 -0
  60. package/dist/markdown/index.cjs +1 -1
  61. package/dist/markdown/index.js +1 -1
  62. package/dist/page-flow-route-CQ7HsbBm.cjs +277 -0
  63. package/dist/page-flow-route-CQ7HsbBm.cjs.map +1 -0
  64. package/dist/page-flow-route-CyXSvXSE.js +277 -0
  65. package/dist/page-flow-route-CyXSvXSE.js.map +1 -0
  66. package/dist/peek-filter-CnqHwTMU.cjs +51 -0
  67. package/dist/peek-filter-CnqHwTMU.cjs.map +1 -0
  68. package/dist/peek-filter-ErLfO153.js +28 -0
  69. package/dist/peek-filter-ErLfO153.js.map +1 -0
  70. package/dist/place-node--GdsP688.js +7315 -0
  71. package/dist/place-node--GdsP688.js.map +1 -0
  72. package/dist/place-node-SIp4XYQH.cjs +7833 -0
  73. package/dist/place-node-SIp4XYQH.cjs.map +1 -0
  74. package/dist/related-list-cards-B6x9HQQp.js +42 -0
  75. package/dist/related-list-cards-B6x9HQQp.js.map +1 -0
  76. package/dist/related-list-cards-nxuODlIA.cjs +42 -0
  77. package/dist/related-list-cards-nxuODlIA.cjs.map +1 -0
  78. package/dist/related-list-timeline-BYJKEbmA.cjs +142 -0
  79. package/dist/related-list-timeline-BYJKEbmA.cjs.map +1 -0
  80. package/dist/related-list-timeline-DEdfd-rr.js +142 -0
  81. package/dist/related-list-timeline-DEdfd-rr.js.map +1 -0
  82. package/dist/related-rows-B4levHcR.js +186 -0
  83. package/dist/related-rows-B4levHcR.js.map +1 -0
  84. package/dist/related-rows-zh5f_4QR.cjs +209 -0
  85. package/dist/related-rows-zh5f_4QR.cjs.map +1 -0
  86. package/dist/relation-create-BOaUe12G.cjs +362 -0
  87. package/dist/relation-create-BOaUe12G.cjs.map +1 -0
  88. package/dist/relation-create-Bg-eEJLs.js +337 -0
  89. package/dist/relation-create-Bg-eEJLs.js.map +1 -0
  90. package/dist/relation-many-picker-B-NCEUgX.cjs +465 -0
  91. package/dist/relation-many-picker-B-NCEUgX.cjs.map +1 -0
  92. package/dist/relation-many-picker-gdAOD-qK.js +464 -0
  93. package/dist/relation-many-picker-gdAOD-qK.js.map +1 -0
  94. package/dist/relation-picker-BAY77ivO.js +462 -0
  95. package/dist/relation-picker-BAY77ivO.js.map +1 -0
  96. package/dist/relation-picker-Da_uhFvA.cjs +479 -0
  97. package/dist/relation-picker-Da_uhFvA.cjs.map +1 -0
  98. package/dist/relation-picker-dialog-M0pQaWlp.cjs +300 -0
  99. package/dist/relation-picker-dialog-M0pQaWlp.cjs.map +1 -0
  100. package/dist/relation-picker-dialog-hA9rYrZB.js +299 -0
  101. package/dist/relation-picker-dialog-hA9rYrZB.js.map +1 -0
  102. package/dist/resource-page-router-OSQyRA5b.cjs +1779 -0
  103. package/dist/resource-page-router-OSQyRA5b.cjs.map +1 -0
  104. package/dist/resource-page-router-T4vS5gbJ.js +1582 -0
  105. package/dist/resource-page-router-T4vS5gbJ.js.map +1 -0
  106. package/dist/{rich-text-IDFvyvRE.js → rich-text-DFerywgz.js} +7 -4
  107. package/dist/{rich-text-IDFvyvRE.js.map → rich-text-DFerywgz.js.map} +1 -1
  108. package/dist/{rich-text-lndbYqzR.cjs → rich-text-vTyVjDoa.cjs} +25 -22
  109. package/dist/{rich-text-lndbYqzR.cjs.map → rich-text-vTyVjDoa.cjs.map} +1 -1
  110. package/dist/search-access-B7G5oR8n.cjs +812 -0
  111. package/dist/search-access-B7G5oR8n.cjs.map +1 -0
  112. package/dist/search-access-CURsWn-U.js +669 -0
  113. package/dist/search-access-CURsWn-U.js.map +1 -0
  114. package/dist/shell-BPlk8hEa.js +205 -0
  115. package/dist/shell-BPlk8hEa.js.map +1 -0
  116. package/dist/shell-DxhlsPwm.cjs +271 -0
  117. package/dist/shell-DxhlsPwm.cjs.map +1 -0
  118. package/dist/skeleton-U2noefj5.js +13 -0
  119. package/dist/skeleton-U2noefj5.js.map +1 -0
  120. package/dist/skeleton-_1gP7wan.cjs +18 -0
  121. package/dist/skeleton-_1gP7wan.cjs.map +1 -0
  122. package/dist/styles.css +241 -9
  123. package/dist/toaster-C7CiYA4Z.cjs +248 -0
  124. package/dist/toaster-C7CiYA4Z.cjs.map +1 -0
  125. package/dist/toaster-CfCRLgR-.js +164 -0
  126. package/dist/toaster-CfCRLgR-.js.map +1 -0
  127. package/dist/use-dialog-filter-state-BBCC0OEK.cjs +92 -0
  128. package/dist/use-dialog-filter-state-BBCC0OEK.cjs.map +1 -0
  129. package/dist/use-dialog-filter-state-BDcgS5Oz.js +87 -0
  130. package/dist/use-dialog-filter-state-BDcgS5Oz.js.map +1 -0
  131. package/dist/{use-theme-BwPWinB2.cjs → use-theme-DFlpXils.cjs} +12 -1
  132. package/dist/use-theme-DFlpXils.cjs.map +1 -0
  133. package/dist/{use-theme-IHmtrhEt.js → use-theme-dnE3Od3I.js} +7 -2
  134. package/dist/use-theme-dnE3Od3I.js.map +1 -0
  135. package/package.json +16 -14
  136. package/dist/use-theme-BwPWinB2.cjs.map +0 -1
  137. package/dist/use-theme-IHmtrhEt.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relation-create-BOaUe12G.cjs","names":["composeListFilter","normalizeFixedFilters","useList","React","cn","buttonVariants","useEntryGate","useListAccess","useApp","useOptionalT","useTestIds","useQuick","useRouteNavigate","useLinkComponent","React","useList","linksToPage","resourceShowHref","IconEye","ClaimedQuickLink","QUICK_ANCHOR_ATTRIBUTE","isPlainLeftClick","useOptionalT","React","useListAccess","namesHiddenField","useEntryGate","useApp","useTestIds","useQuick"],"sources":["../src/fields/relation-hand-back.tsx","../src/fields/relation-view-button.tsx","../src/fields/relation-create.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport {\n composeListFilter,\n type ErasedResourceHandle,\n type FilterExpression,\n type FixedFilterInput,\n normalizeFixedFilters,\n type SearchDecl,\n} from '@burojs/core';\nimport { useList } from '@burojs/react';\n\ntype ErasedExpression = FilterExpression<ErasedResourceHandle>;\n\n/**\n * The filter the select re-reads a created record through (P6-33): the picker's own option query\n * for an empty search (`composeListFilter`, the same `fixed` filters, the same search\n * declaration), conjoined with `<idField> eq <id>`. The row must come back through the query the\n * select offers from, or it is not selected.\n */\nexport function createdOptionFilter(input: {\n readonly fixed: readonly FixedFilterInput[] | undefined;\n readonly idField: string;\n readonly id: string | number;\n readonly searchDecl?: SearchDecl | undefined;\n readonly searchContext?: { readonly resource: string; readonly def: unknown };\n}): ErasedExpression {\n const own = createdOptionScope(input);\n const byId = { field: input.idField, op: 'eq', value: input.id } as ErasedExpression;\n return own === undefined ? byId : ({ and: [own, byId] } as ErasedExpression);\n}\n\n/**\n * The select's option query for an empty search, without the id predicate: what the picker itself\n * sends, so the same hidden-field rule (`namesHiddenField`) judges both.\n */\nexport function createdOptionScope(input: {\n readonly fixed: readonly FixedFilterInput[] | undefined;\n readonly searchDecl?: SearchDecl | undefined;\n readonly searchContext?: { readonly resource: string; readonly def: unknown };\n}): ErasedExpression | undefined {\n return composeListFilter({\n fixed: normalizeFixedFilters(input.fixed),\n search: '',\n ...(input.searchDecl === undefined ? {} : { searchDecl: input.searchDecl }),\n ...(input.searchContext === undefined ? {} : { searchContext: input.searchContext as never }),\n });\n}\n\n/** What the re-read settled on: the value to select, \"does not fit\", or a failed read. */\nexport type CreatedOptionResult =\n | { readonly kind: 'fits'; readonly value: string | number }\n | { readonly kind: 'does-not-fit' }\n | { readonly kind: 'failed' };\n\nexport interface CreatedOptionReadProps {\n readonly to: string;\n readonly filter: ErasedExpression;\n /** The picker passes `ignoreScope` when it has explicit fixed filters (same rule as its list). */\n readonly ignoreScope: boolean;\n readonly by: string | undefined;\n readonly idField: string;\n /** Called once, with the settled answer. */\n readonly onSettled: (result: CreatedOptionResult) => void;\n}\n\n/**\n * Mounted only while a created record is being handed back. One list read, limit 1, through the\n * select's own option query (Known finding 13): an empty answer is \"does not fit\", a failed read\n * is a failure, never \"fits\". Mounting it is the request; unmounting it drops the answer.\n */\nexport function CreatedOptionRead({\n to,\n filter,\n ignoreScope,\n by,\n idField,\n onSettled,\n}: CreatedOptionReadProps): null {\n const query = useList(to, {\n filter: filter as never,\n pagination: { kind: 'offset', page: 1, pageSize: 1 },\n ...(ignoreScope ? { providerOptionsOverride: { ignoreScope: true } } : {}),\n retry: false,\n });\n const settled = React.useRef(false);\n const latest = React.useRef(onSettled);\n latest.current = onSettled;\n const rows = (query.data as { readonly data?: readonly Record<string, unknown>[] } | undefined)\n ?.data;\n const answered = rows !== undefined && query.isCurrentData && !query.isFetching;\n const failed = query.isError && !query.isFetching;\n React.useEffect(() => {\n if (settled.current) return;\n if (failed) {\n settled.current = true;\n latest.current({ kind: 'failed' });\n return;\n }\n if (!answered) return;\n settled.current = true;\n const value = rows[0]?.[by ?? idField];\n latest.current(\n typeof value === 'string' || typeof value === 'number'\n ? { kind: 'fits', value }\n : { kind: 'does-not-fit' },\n );\n }, [answered, failed, rows, by, idField]);\n return null;\n}\n","'use client';\n\nimport * as React from 'react';\nimport { IconEye } from '@tabler/icons-react';\nimport { type RelationOpen, resourceShowHref } from '@burojs/core';\nimport {\n QUICK_ANCHOR_ATTRIBUTE,\n useApp,\n useEntryGate,\n useList,\n useListAccess,\n useOptionalT,\n useQuick,\n useRouteNavigate,\n useTestIds,\n} from '@burojs/react';\nimport { buttonVariants } from '../primitives/button.js';\nimport { cn } from '../lib/cn.js';\nimport { useLinkComponent } from '../layout/link-component.js';\nimport { ClaimedQuickLink, isPlainLeftClick } from './claimed-quick-link.js';\nimport { linksToPage } from './relation-display.js';\n\n/** A key a read may send: a non-empty string or a finite number (Known finding 1). */\nexport function selectKey(value: unknown): string | null {\n if (typeof value === 'string') return value.length === 0 ? null : value;\n if (typeof value === 'number') return Number.isFinite(value) ? String(value) : null;\n return null;\n}\n\nconst VIEW_CLASS = cn(buttonVariants({ variant: 'outline', size: 'icon' }), 'shrink-0');\n\nexport interface RelationViewButtonProps {\n readonly to: string;\n readonly value: unknown;\n readonly by: string | undefined;\n /**\n * The select has explicit fixed filters, so the caller owns the scope: the `by` read skips the\n * ambient sidebar scope as the picker's option read does.\n */\n readonly ignoreScope?: boolean;\n /** The form field holding the key: the test id's second part. */\n readonly fieldName: string;\n /** Runs before the overlay opens: the picker moves focus to its trigger, where it returns. */\n readonly beforeOpen: () => void;\n /**\n * The field's `ui.open` (P7 3.6): `quick` (default) opens the record above the form, `page`\n * goes to its show page through the navigation guard (a dirty form asks first), `none` draws\n * no button.\n */\n readonly open?: RelationOpen;\n}\n\n/**\n * \"View\" beside a relation select's trigger (P6-28): the target's `quick.show` as a new overlay\n * above the form, never a push and never closing the host. Drawn only when the select holds a\n * key and the entry gate is open without a row (the row-level re-check runs inside the overlay,\n * P3-44); while the gate is undecided nothing is drawn and nothing is read (P3-15). A `by` key is\n * resolved by the limit-2 read on click; zero or two rows open nothing.\n */\nexport function RelationViewButton(props: RelationViewButtonProps): React.ReactElement | null {\n const gate = useEntryGate();\n const { to, value, by } = props;\n const access = useListAccess(to);\n const key = selectKey(value);\n if (key === null || props.open === 'none') return null;\n // A `by` field the user may not view is never sent as a predicate: no \"view\", no read.\n if (by !== undefined && !(access.status === 'ready' && access.fieldVisible(by))) return null;\n const answer = gate(to, 'show');\n if (answer.state !== 'open') return null;\n // Keyed by the value: a click asked for one key's read and must not outlive it.\n return <ViewButton key={key} {...props} keyString={key} />;\n}\n\nfunction ViewButton({\n to,\n by,\n ignoreScope,\n fieldName,\n keyString,\n beforeOpen,\n open: openMode = 'quick',\n}: RelationViewButtonProps & { readonly keyString: string }): React.ReactElement | null {\n const app = useApp();\n const t = useOptionalT();\n const tid = useTestIds();\n const quick = useQuick();\n const navigate = useRouteNavigate();\n const RouterLink = useLinkComponent();\n const [asked, setAsked] = React.useState(false);\n const requested = React.useRef(false);\n const targetDef = app.registry.get(to);\n const rawIdField = (targetDef?.data as { readonly idField?: unknown } | undefined)?.idField;\n const idField = typeof rawIdField === 'string' && rawIdField.length > 0 ? rawIdField : 'id';\n const read = useList(to, {\n ...(asked && by !== undefined\n ? {\n filter: { field: by, op: 'eq', value: keyString } as never,\n pagination: { kind: 'offset', page: 1, pageSize: 2 } as never,\n }\n : {}),\n ...(ignoreScope === true ? { providerOptionsOverride: { ignoreScope: true } } : {}),\n enabled: asked && by !== undefined,\n retry: false,\n });\n // Rows of this key only: a placeholder carried over from another key's read is not an answer.\n const current = read.isCurrentData && !read.isPlaceholderData && !read.isFetching;\n const rows = current\n ? (read.data as { readonly data?: readonly Record<string, unknown>[] } | undefined)?.data\n : undefined;\n const open = React.useCallback(\n (id: string) => {\n beforeOpen();\n if (openMode === 'page') {\n // The page of the record, through the navigation guard: unsaved input asks first.\n if (targetDef !== undefined && linksToPage(app, targetDef))\n navigate(resourceShowHref(app.registry, targetDef, to, id));\n return;\n }\n quick.show(to, id);\n },\n [beforeOpen, quick, to, openMode, navigate, app, targetDef],\n );\n // Only a click opens: the read answers the click that asked for it, once.\n React.useEffect(() => {\n if (by === undefined || !asked || rows === undefined || !requested.current) return;\n requested.current = false;\n if (rows.length !== 1) {\n if (rows.length > 1 && process.env.NODE_ENV !== 'production')\n console.error(\n `[burojs] ${to}: more than one row has ${by} ${keyString}; \"view\" opened nothing.`,\n );\n return;\n }\n const id = rows[0]?.[idField];\n const next = selectKey(id);\n if (next !== null) open(next);\n }, [by, asked, rows, idField, keyString, to, open]);\n\n const label = t('relationPicker.view', { defaultValue: 'View' });\n const icon = <IconEye className=\"h-4 w-4\" aria-hidden />;\n const testid = tid.relationView(to, fieldName);\n // `page` needs a page to go to (the publish check says so): without one, nothing is drawn.\n if (openMode === 'page' && (targetDef === undefined || !linksToPage(app, targetDef))) return null;\n if (openMode === 'page' && by === undefined && targetDef !== undefined)\n return (\n <RouterLink\n href={resourceShowHref(app.registry, targetDef, to, keyString)}\n data-testid={testid}\n className={VIEW_CLASS}\n onClick={() => beforeOpen()}\n >\n {icon}\n <span className=\"sr-only\">{label}</span>\n </RouterLink>\n );\n if (by === undefined && targetDef !== undefined && linksToPage(app, targetDef))\n return (\n <ClaimedQuickLink\n href={resourceShowHref(app.registry, targetDef, to, keyString)}\n testid={testid}\n className={VIEW_CLASS}\n onOpen={() => open(keyString)}\n >\n {icon}\n <span className=\"sr-only\">{label}</span>\n </ClaimedQuickLink>\n );\n return (\n <button\n type=\"button\"\n className={VIEW_CLASS}\n data-testid={testid}\n aria-label={label}\n {...{ [QUICK_ANCHOR_ATTRIBUTE]: '' }}\n onClick={(event) => {\n event.preventDefault();\n event.stopPropagation();\n // No page to follow (or, for a `by` key, no id to build it from): a modified click is\n // nothing, never an overlay the user asked to avoid.\n if (!isPlainLeftClick(event)) return;\n if (by === undefined) {\n open(keyString);\n return;\n }\n requested.current = true;\n setAsked(true);\n // A key already read opens at once; the effect answers a click made before the read.\n if (rows !== undefined && rows.length === 1) {\n const next = selectKey(rows[0]?.[idField]);\n requested.current = false;\n if (next !== null) open(next);\n }\n }}\n >\n {icon}\n </button>\n );\n}\n","'use client';\n\nimport * as React from 'react';\nimport type { FixedFilterInput, SearchDecl } from '@burojs/core';\nimport {\n type QuickSettledRow,\n useApp,\n useListAccess,\n useEntryGate,\n useOptionalT,\n useQuick,\n useTestIds,\n} from '@burojs/react';\nimport { toast } from '../notification/toaster.js';\nimport {\n CreatedOptionRead,\n type CreatedOptionResult,\n createdOptionFilter,\n createdOptionScope,\n} from './relation-hand-back.js';\nimport { namesHiddenField } from './search-access.js';\nimport { selectKey } from './relation-view-button.js';\n\n/** What the picker and its dialog share: one pending create per select (P6-34). */\nexport interface SelectCreate {\n readonly pending: boolean;\n /**\n * The hand-back read is forbidden: its filter names a field P5 hides from the user (the same\n * rule that stops the picker's own option read), or the access map is not ready. A blocked\n * select offers no \"+ Create\" and starts none, so no predicate on a hidden field is ever sent.\n */\n readonly blocked: boolean;\n /** Runs `open` unless a create of this select is already pending or the select is gone. */\n start(open: () => Promise<QuickSettledRow>): void;\n}\n\ninterface SelectCreateInput {\n readonly to: string;\n readonly by: string | undefined;\n /** The row field the select holds (`by`, else the target's id field). */\n readonly valueField: string;\n readonly idField: string;\n readonly fixedFilters: readonly FixedFilterInput[] | undefined;\n /**\n * The select's candidates gate is not ready (pending, empty, forbidden): the filters above\n * lack the candidate filters, so the hand-back must not read.\n */\n readonly held?: boolean;\n readonly searchDecl: SearchDecl | undefined;\n readonly searchContext: { readonly resource: string; readonly def: unknown };\n /** Called with the value to select, as a user's pick would. */\n readonly onValue: (value: string | number) => void;\n}\n\n/**\n * The select's one pending create: the latch (a ref, so a double click or a second render cannot\n * start two), the mounted guard (a result arriving after the select unmounted is ignored — the\n * record exists, nothing is selected), and the hand-back re-read through the select's own option\n * query. `reader` is mounted by the select while a created record is being handed back.\n */\nexport function useSelectCreate(input: SelectCreateInput): {\n readonly create: SelectCreate;\n readonly reader: React.ReactNode;\n} {\n const t = useOptionalT();\n const { to: target } = input;\n const [pending, setPending] = React.useState(false);\n const [created, setCreated] = React.useState<{ readonly id: string } | null>(null);\n const latch = React.useRef(false);\n const mounted = React.useRef(true);\n React.useEffect(() => {\n mounted.current = true;\n return () => {\n mounted.current = false;\n };\n }, []);\n const latest = React.useRef(input);\n latest.current = input;\n const access = useListAccess(target);\n // The hand-back's own filter, judged as the picker judges its option query.\n const blocked =\n input.held === true ||\n access.status !== 'ready' ||\n namesHiddenField(\n createdOptionScope({\n fixed: input.fixedFilters,\n searchDecl: input.searchDecl,\n searchContext: input.searchContext,\n }),\n (name) => access.fieldVisible(name),\n );\n const blockedRef = React.useRef(blocked);\n blockedRef.current = blocked;\n\n const release = React.useCallback(() => {\n latch.current = false;\n if (mounted.current) {\n setPending(false);\n setCreated(null);\n }\n }, []);\n\n const start = React.useCallback(\n (open: () => Promise<QuickSettledRow>) => {\n if (latch.current || !mounted.current || blockedRef.current) return;\n latch.current = true;\n setPending(true);\n let opened: Promise<QuickSettledRow>;\n try {\n opened = open();\n } catch (error) {\n // A synchronous throw (a `beforeOpen`, the host) must not leave the select locked.\n release();\n throw error;\n }\n void opened.then(\n (row) => {\n if (!mounted.current) {\n latch.current = false;\n return;\n }\n const id =\n row === null ? null : (selectKey(row[latest.current.idField]) ?? selectKey(row.id));\n if (id === null) release();\n else setCreated({ id });\n },\n () => release(),\n );\n },\n [release],\n );\n\n const settle = React.useCallback(\n (result: CreatedOptionResult) => {\n if (mounted.current) {\n if (result.kind === 'fits') latest.current.onValue(result.value);\n else if (result.kind === 'does-not-fit')\n toast(\n t('relationPicker.createdNotFitting', {\n defaultValue: 'The record was created but does not fit this field',\n }),\n );\n else\n toast.error(\n t('relationPicker.createdNotSelected', {\n defaultValue: 'The record was created but could not be selected',\n }),\n );\n }\n release();\n },\n [release, t],\n );\n\n const create = React.useMemo<SelectCreate>(\n () => ({ pending, blocked, start }),\n [pending, blocked, start],\n );\n const reader =\n created === null || blocked ? null : (\n <CreatedOptionRead\n key={created.id}\n to={target}\n filter={createdOptionFilter({\n fixed: input.fixedFilters,\n idField: input.idField,\n id: created.id,\n searchDecl: input.searchDecl,\n searchContext: input.searchContext,\n })}\n ignoreScope={input.fixedFilters !== undefined && input.fixedFilters.length > 0}\n by={input.by}\n idField={input.idField}\n onSettled={settle}\n />\n );\n return { create, reader };\n}\n\n/** A string field a typed search text may fill: declared plain, not system, not an enum. */\nfunction plainStringField(def: unknown): boolean {\n if (def === null || typeof def !== 'object') return false;\n const field = def as {\n readonly kind?: unknown;\n readonly system?: unknown;\n readonly opts?: {\n readonly system?: unknown;\n readonly enum?: unknown;\n readonly localized?: unknown;\n };\n };\n return (\n field.kind === 'string' &&\n field.system !== true &&\n field.opts?.system !== true &&\n field.opts?.enum === undefined &&\n field.opts?.localized !== true\n );\n}\n\nexport interface RelationCreateButtonProps {\n readonly to: string;\n readonly fieldName: string;\n readonly create: SelectCreate;\n /** The trusted `defaults` tier: the registry field's static `eq` literals (P6-31). */\n readonly seeds: Readonly<Record<string, unknown>> | undefined;\n /** The text typed into the select's search, if any: prefills the display field as user input. */\n readonly search?: string;\n /** Runs before the overlay opens: closes the popover, returns focus to the trigger. */\n readonly beforeOpen: () => void;\n readonly className?: string;\n readonly testId?: string;\n}\n\n/**\n * \"+ create\" (P6-29): the target's `quick.create` as a new overlay above the form. Drawn only when\n * the entry gate is open (P3 4.4: `operations.create`, `views.quick.create`, the host wired, the\n * `create` availability); undecided or denied draws nothing and reads nothing. Disabled while one\n * create of the select is pending.\n */\nexport function RelationCreateButton(props: RelationCreateButtonProps): React.ReactElement | null {\n const gate = useEntryGate();\n const { to } = props;\n if (props.create.blocked || gate(to, 'create').state !== 'open') return null;\n return <CreateButton {...props} />;\n}\n\nfunction CreateButton({\n to,\n fieldName,\n create,\n seeds,\n search,\n beforeOpen,\n className,\n testId,\n}: RelationCreateButtonProps): React.ReactElement {\n const app = useApp();\n const t = useOptionalT();\n const tid = useTestIds();\n const quick = useQuick();\n const label = t('relationPicker.create', { defaultValue: '+ Create' });\n return (\n <button\n type=\"button\"\n data-testid={testId ?? tid.relationCreate(to, fieldName)}\n disabled={create.pending}\n className={className}\n onClick={() => {\n const text = search?.trim() ?? '';\n const displayField = (\n app.registry.get(to) as { readonly displayField?: unknown } | undefined\n )?.displayField;\n const fields = (\n app.registry.get(to) as { readonly fields?: Record<string, unknown> } | undefined\n )?.fields;\n const prefill =\n text.length > 0 &&\n typeof displayField === 'string' &&\n fields !== undefined &&\n Object.hasOwn(fields, displayField) &&\n plainStringField(fields[displayField])\n ? { [displayField]: text }\n : undefined;\n create.start(() => {\n beforeOpen();\n return quick.create(to, {\n ...(seeds === undefined || Object.keys(seeds).length === 0 ? {} : { defaults: seeds }),\n ...(prefill === undefined ? {} : { input: prefill }),\n });\n });\n }}\n >\n {label}\n </button>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,337 @@
1
+ import { l as cn } from "./use-theme-dnE3Od3I.js";
2
+ import { d as useLinkComponent, n as toast } from "./toaster-CfCRLgR-.js";
3
+ import { n as buttonVariants } from "./button-MWVeZaqj.js";
4
+ import { h as isPlainLeftClick, m as ClaimedQuickLink, o as linksToPage, t as namesHiddenField } from "./search-access-CURsWn-U.js";
5
+ import { composeListFilter, normalizeFixedFilters, resourceShowHref } from "@burojs/core";
6
+ import { QUICK_ANCHOR_ATTRIBUTE, useApp, useEntryGate, useList, useListAccess, useOptionalT, useQuick, useRouteNavigate, useTestIds } from "@burojs/react";
7
+ import { IconEye } from "@tabler/icons-react";
8
+ import * as React from "react";
9
+ import { jsx, jsxs } from "react/jsx-runtime";
10
+ //#region src/fields/relation-hand-back.tsx
11
+ function createdOptionFilter(input) {
12
+ const own = createdOptionScope(input);
13
+ const byId = {
14
+ field: input.idField,
15
+ op: "eq",
16
+ value: input.id
17
+ };
18
+ return own === void 0 ? byId : { and: [own, byId] };
19
+ }
20
+ function createdOptionScope(input) {
21
+ return composeListFilter({
22
+ fixed: normalizeFixedFilters(input.fixed),
23
+ search: "",
24
+ ...input.searchDecl === void 0 ? {} : { searchDecl: input.searchDecl },
25
+ ...input.searchContext === void 0 ? {} : { searchContext: input.searchContext }
26
+ });
27
+ }
28
+ function CreatedOptionRead({ to, filter, ignoreScope, by, idField, onSettled }) {
29
+ const query = useList(to, {
30
+ filter,
31
+ pagination: {
32
+ kind: "offset",
33
+ page: 1,
34
+ pageSize: 1
35
+ },
36
+ ...ignoreScope ? { providerOptionsOverride: { ignoreScope: true } } : {},
37
+ retry: false
38
+ });
39
+ const settled = React.useRef(false);
40
+ const latest = React.useRef(onSettled);
41
+ latest.current = onSettled;
42
+ const rows = query.data?.data;
43
+ const answered = rows !== void 0 && query.isCurrentData && !query.isFetching;
44
+ const failed = query.isError && !query.isFetching;
45
+ React.useEffect(() => {
46
+ if (settled.current) return;
47
+ if (failed) {
48
+ settled.current = true;
49
+ latest.current({ kind: "failed" });
50
+ return;
51
+ }
52
+ if (!answered) return;
53
+ settled.current = true;
54
+ const value = rows[0]?.[by ?? idField];
55
+ latest.current(typeof value === "string" || typeof value === "number" ? {
56
+ kind: "fits",
57
+ value
58
+ } : { kind: "does-not-fit" });
59
+ }, [
60
+ answered,
61
+ failed,
62
+ rows,
63
+ by,
64
+ idField
65
+ ]);
66
+ return null;
67
+ }
68
+ //#endregion
69
+ //#region src/fields/relation-view-button.tsx
70
+ function selectKey(value) {
71
+ if (typeof value === "string") return value.length === 0 ? null : value;
72
+ if (typeof value === "number") return Number.isFinite(value) ? String(value) : null;
73
+ return null;
74
+ }
75
+ const VIEW_CLASS = cn(buttonVariants({
76
+ variant: "outline",
77
+ size: "icon"
78
+ }), "shrink-0");
79
+ function RelationViewButton(props) {
80
+ const gate = useEntryGate();
81
+ const { to, value, by } = props;
82
+ const access = useListAccess(to);
83
+ const key = selectKey(value);
84
+ if (key === null || props.open === "none") return null;
85
+ if (by !== void 0 && !(access.status === "ready" && access.fieldVisible(by))) return null;
86
+ if (gate(to, "show").state !== "open") return null;
87
+ return jsx(ViewButton, {
88
+ ...props,
89
+ keyString: key
90
+ }, key);
91
+ }
92
+ function ViewButton({ to, by, ignoreScope, fieldName, keyString, beforeOpen, open: openMode = "quick" }) {
93
+ const app = useApp();
94
+ const t = useOptionalT();
95
+ const tid = useTestIds();
96
+ const quick = useQuick();
97
+ const navigate = useRouteNavigate();
98
+ const RouterLink = useLinkComponent();
99
+ const [asked, setAsked] = React.useState(false);
100
+ const requested = React.useRef(false);
101
+ const targetDef = app.registry.get(to);
102
+ const rawIdField = (targetDef?.data)?.idField;
103
+ const idField = typeof rawIdField === "string" && rawIdField.length > 0 ? rawIdField : "id";
104
+ const read = useList(to, {
105
+ ...asked && by !== void 0 ? {
106
+ filter: {
107
+ field: by,
108
+ op: "eq",
109
+ value: keyString
110
+ },
111
+ pagination: {
112
+ kind: "offset",
113
+ page: 1,
114
+ pageSize: 2
115
+ }
116
+ } : {},
117
+ ...ignoreScope === true ? { providerOptionsOverride: { ignoreScope: true } } : {},
118
+ enabled: asked && by !== void 0,
119
+ retry: false
120
+ });
121
+ const rows = read.isCurrentData && !read.isPlaceholderData && !read.isFetching ? read.data?.data : void 0;
122
+ const open = React.useCallback((id) => {
123
+ beforeOpen();
124
+ if (openMode === "page") {
125
+ if (targetDef !== void 0 && linksToPage(app, targetDef)) navigate(resourceShowHref(app.registry, targetDef, to, id));
126
+ return;
127
+ }
128
+ quick.show(to, id);
129
+ }, [
130
+ beforeOpen,
131
+ quick,
132
+ to,
133
+ openMode,
134
+ navigate,
135
+ app,
136
+ targetDef
137
+ ]);
138
+ React.useEffect(() => {
139
+ if (by === void 0 || !asked || rows === void 0 || !requested.current) return;
140
+ requested.current = false;
141
+ if (rows.length !== 1) {
142
+ if (rows.length > 1 && process.env.NODE_ENV !== "production") console.error(`[burojs] ${to}: more than one row has ${by} ${keyString}; "view" opened nothing.`);
143
+ return;
144
+ }
145
+ const id = rows[0]?.[idField];
146
+ const next = selectKey(id);
147
+ if (next !== null) open(next);
148
+ }, [
149
+ by,
150
+ asked,
151
+ rows,
152
+ idField,
153
+ keyString,
154
+ to,
155
+ open
156
+ ]);
157
+ const label = t("relationPicker.view", { defaultValue: "View" });
158
+ const icon = jsx(IconEye, {
159
+ className: "h-4 w-4",
160
+ "aria-hidden": true
161
+ });
162
+ const testid = tid.relationView(to, fieldName);
163
+ if (openMode === "page" && (targetDef === void 0 || !linksToPage(app, targetDef))) return null;
164
+ if (openMode === "page" && by === void 0 && targetDef !== void 0) return jsxs(RouterLink, {
165
+ href: resourceShowHref(app.registry, targetDef, to, keyString),
166
+ "data-testid": testid,
167
+ className: VIEW_CLASS,
168
+ onClick: () => beforeOpen(),
169
+ children: [icon, jsx("span", {
170
+ className: "sr-only",
171
+ children: label
172
+ })]
173
+ });
174
+ if (by === void 0 && targetDef !== void 0 && linksToPage(app, targetDef)) return jsxs(ClaimedQuickLink, {
175
+ href: resourceShowHref(app.registry, targetDef, to, keyString),
176
+ testid,
177
+ className: VIEW_CLASS,
178
+ onOpen: () => open(keyString),
179
+ children: [icon, jsx("span", {
180
+ className: "sr-only",
181
+ children: label
182
+ })]
183
+ });
184
+ return jsx("button", {
185
+ type: "button",
186
+ className: VIEW_CLASS,
187
+ "data-testid": testid,
188
+ "aria-label": label,
189
+ [QUICK_ANCHOR_ATTRIBUTE]: "",
190
+ onClick: (event) => {
191
+ event.preventDefault();
192
+ event.stopPropagation();
193
+ if (!isPlainLeftClick(event)) return;
194
+ if (by === void 0) {
195
+ open(keyString);
196
+ return;
197
+ }
198
+ requested.current = true;
199
+ setAsked(true);
200
+ if (rows !== void 0 && rows.length === 1) {
201
+ const next = selectKey(rows[0]?.[idField]);
202
+ requested.current = false;
203
+ if (next !== null) open(next);
204
+ }
205
+ },
206
+ children: icon
207
+ });
208
+ }
209
+ //#endregion
210
+ //#region src/fields/relation-create.tsx
211
+ function useSelectCreate(input) {
212
+ const t = useOptionalT();
213
+ const { to: target } = input;
214
+ const [pending, setPending] = React.useState(false);
215
+ const [created, setCreated] = React.useState(null);
216
+ const latch = React.useRef(false);
217
+ const mounted = React.useRef(true);
218
+ React.useEffect(() => {
219
+ mounted.current = true;
220
+ return () => {
221
+ mounted.current = false;
222
+ };
223
+ }, []);
224
+ const latest = React.useRef(input);
225
+ latest.current = input;
226
+ const access = useListAccess(target);
227
+ const blocked = input.held === true || access.status !== "ready" || namesHiddenField(createdOptionScope({
228
+ fixed: input.fixedFilters,
229
+ searchDecl: input.searchDecl,
230
+ searchContext: input.searchContext
231
+ }), (name) => access.fieldVisible(name));
232
+ const blockedRef = React.useRef(blocked);
233
+ blockedRef.current = blocked;
234
+ const release = React.useCallback(() => {
235
+ latch.current = false;
236
+ if (mounted.current) {
237
+ setPending(false);
238
+ setCreated(null);
239
+ }
240
+ }, []);
241
+ const start = React.useCallback((open) => {
242
+ if (latch.current || !mounted.current || blockedRef.current) return;
243
+ latch.current = true;
244
+ setPending(true);
245
+ let opened;
246
+ try {
247
+ opened = open();
248
+ } catch (error) {
249
+ release();
250
+ throw error;
251
+ }
252
+ opened.then((row) => {
253
+ if (!mounted.current) {
254
+ latch.current = false;
255
+ return;
256
+ }
257
+ const id = row === null ? null : selectKey(row[latest.current.idField]) ?? selectKey(row.id);
258
+ if (id === null) release();
259
+ else setCreated({ id });
260
+ }, () => release());
261
+ }, [release]);
262
+ const settle = React.useCallback((result) => {
263
+ if (mounted.current) {
264
+ if (result.kind === "fits") latest.current.onValue(result.value);
265
+ else if (result.kind === "does-not-fit") toast(t("relationPicker.createdNotFitting", { defaultValue: "The record was created but does not fit this field" }));
266
+ else toast.error(t("relationPicker.createdNotSelected", { defaultValue: "The record was created but could not be selected" }));
267
+ }
268
+ release();
269
+ }, [release, t]);
270
+ return {
271
+ create: React.useMemo(() => ({
272
+ pending,
273
+ blocked,
274
+ start
275
+ }), [
276
+ pending,
277
+ blocked,
278
+ start
279
+ ]),
280
+ reader: created === null || blocked ? null : jsx(CreatedOptionRead, {
281
+ to: target,
282
+ filter: createdOptionFilter({
283
+ fixed: input.fixedFilters,
284
+ idField: input.idField,
285
+ id: created.id,
286
+ searchDecl: input.searchDecl,
287
+ searchContext: input.searchContext
288
+ }),
289
+ ignoreScope: input.fixedFilters !== void 0 && input.fixedFilters.length > 0,
290
+ by: input.by,
291
+ idField: input.idField,
292
+ onSettled: settle
293
+ }, created.id)
294
+ };
295
+ }
296
+ function plainStringField(def) {
297
+ if (def === null || typeof def !== "object") return false;
298
+ const field = def;
299
+ return field.kind === "string" && field.system !== true && field.opts?.system !== true && field.opts?.enum === void 0 && field.opts?.localized !== true;
300
+ }
301
+ function RelationCreateButton(props) {
302
+ const gate = useEntryGate();
303
+ const { to } = props;
304
+ if (props.create.blocked || gate(to, "create").state !== "open") return null;
305
+ return jsx(CreateButton, { ...props });
306
+ }
307
+ function CreateButton({ to, fieldName, create, seeds, search, beforeOpen, className, testId }) {
308
+ const app = useApp();
309
+ const t = useOptionalT();
310
+ const tid = useTestIds();
311
+ const quick = useQuick();
312
+ const label = t("relationPicker.create", { defaultValue: "+ Create" });
313
+ return jsx("button", {
314
+ type: "button",
315
+ "data-testid": testId ?? tid.relationCreate(to, fieldName),
316
+ disabled: create.pending,
317
+ className,
318
+ onClick: () => {
319
+ const text = search?.trim() ?? "";
320
+ const displayField = app.registry.get(to)?.displayField;
321
+ const fields = app.registry.get(to)?.fields;
322
+ const prefill = text.length > 0 && typeof displayField === "string" && fields !== void 0 && Object.hasOwn(fields, displayField) && plainStringField(fields[displayField]) ? { [displayField]: text } : void 0;
323
+ create.start(() => {
324
+ beforeOpen();
325
+ return quick.create(to, {
326
+ ...seeds === void 0 || Object.keys(seeds).length === 0 ? {} : { defaults: seeds },
327
+ ...prefill === void 0 ? {} : { input: prefill }
328
+ });
329
+ });
330
+ },
331
+ children: label
332
+ });
333
+ }
334
+ //#endregion
335
+ export { selectKey as i, useSelectCreate as n, RelationViewButton as r, RelationCreateButton as t };
336
+
337
+ //# sourceMappingURL=relation-create-Bg-eEJLs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relation-create-Bg-eEJLs.js","names":[],"sources":["../src/fields/relation-hand-back.tsx","../src/fields/relation-view-button.tsx","../src/fields/relation-create.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport {\n composeListFilter,\n type ErasedResourceHandle,\n type FilterExpression,\n type FixedFilterInput,\n normalizeFixedFilters,\n type SearchDecl,\n} from '@burojs/core';\nimport { useList } from '@burojs/react';\n\ntype ErasedExpression = FilterExpression<ErasedResourceHandle>;\n\n/**\n * The filter the select re-reads a created record through (P6-33): the picker's own option query\n * for an empty search (`composeListFilter`, the same `fixed` filters, the same search\n * declaration), conjoined with `<idField> eq <id>`. The row must come back through the query the\n * select offers from, or it is not selected.\n */\nexport function createdOptionFilter(input: {\n readonly fixed: readonly FixedFilterInput[] | undefined;\n readonly idField: string;\n readonly id: string | number;\n readonly searchDecl?: SearchDecl | undefined;\n readonly searchContext?: { readonly resource: string; readonly def: unknown };\n}): ErasedExpression {\n const own = createdOptionScope(input);\n const byId = { field: input.idField, op: 'eq', value: input.id } as ErasedExpression;\n return own === undefined ? byId : ({ and: [own, byId] } as ErasedExpression);\n}\n\n/**\n * The select's option query for an empty search, without the id predicate: what the picker itself\n * sends, so the same hidden-field rule (`namesHiddenField`) judges both.\n */\nexport function createdOptionScope(input: {\n readonly fixed: readonly FixedFilterInput[] | undefined;\n readonly searchDecl?: SearchDecl | undefined;\n readonly searchContext?: { readonly resource: string; readonly def: unknown };\n}): ErasedExpression | undefined {\n return composeListFilter({\n fixed: normalizeFixedFilters(input.fixed),\n search: '',\n ...(input.searchDecl === undefined ? {} : { searchDecl: input.searchDecl }),\n ...(input.searchContext === undefined ? {} : { searchContext: input.searchContext as never }),\n });\n}\n\n/** What the re-read settled on: the value to select, \"does not fit\", or a failed read. */\nexport type CreatedOptionResult =\n | { readonly kind: 'fits'; readonly value: string | number }\n | { readonly kind: 'does-not-fit' }\n | { readonly kind: 'failed' };\n\nexport interface CreatedOptionReadProps {\n readonly to: string;\n readonly filter: ErasedExpression;\n /** The picker passes `ignoreScope` when it has explicit fixed filters (same rule as its list). */\n readonly ignoreScope: boolean;\n readonly by: string | undefined;\n readonly idField: string;\n /** Called once, with the settled answer. */\n readonly onSettled: (result: CreatedOptionResult) => void;\n}\n\n/**\n * Mounted only while a created record is being handed back. One list read, limit 1, through the\n * select's own option query (Known finding 13): an empty answer is \"does not fit\", a failed read\n * is a failure, never \"fits\". Mounting it is the request; unmounting it drops the answer.\n */\nexport function CreatedOptionRead({\n to,\n filter,\n ignoreScope,\n by,\n idField,\n onSettled,\n}: CreatedOptionReadProps): null {\n const query = useList(to, {\n filter: filter as never,\n pagination: { kind: 'offset', page: 1, pageSize: 1 },\n ...(ignoreScope ? { providerOptionsOverride: { ignoreScope: true } } : {}),\n retry: false,\n });\n const settled = React.useRef(false);\n const latest = React.useRef(onSettled);\n latest.current = onSettled;\n const rows = (query.data as { readonly data?: readonly Record<string, unknown>[] } | undefined)\n ?.data;\n const answered = rows !== undefined && query.isCurrentData && !query.isFetching;\n const failed = query.isError && !query.isFetching;\n React.useEffect(() => {\n if (settled.current) return;\n if (failed) {\n settled.current = true;\n latest.current({ kind: 'failed' });\n return;\n }\n if (!answered) return;\n settled.current = true;\n const value = rows[0]?.[by ?? idField];\n latest.current(\n typeof value === 'string' || typeof value === 'number'\n ? { kind: 'fits', value }\n : { kind: 'does-not-fit' },\n );\n }, [answered, failed, rows, by, idField]);\n return null;\n}\n","'use client';\n\nimport * as React from 'react';\nimport { IconEye } from '@tabler/icons-react';\nimport { type RelationOpen, resourceShowHref } from '@burojs/core';\nimport {\n QUICK_ANCHOR_ATTRIBUTE,\n useApp,\n useEntryGate,\n useList,\n useListAccess,\n useOptionalT,\n useQuick,\n useRouteNavigate,\n useTestIds,\n} from '@burojs/react';\nimport { buttonVariants } from '../primitives/button.js';\nimport { cn } from '../lib/cn.js';\nimport { useLinkComponent } from '../layout/link-component.js';\nimport { ClaimedQuickLink, isPlainLeftClick } from './claimed-quick-link.js';\nimport { linksToPage } from './relation-display.js';\n\n/** A key a read may send: a non-empty string or a finite number (Known finding 1). */\nexport function selectKey(value: unknown): string | null {\n if (typeof value === 'string') return value.length === 0 ? null : value;\n if (typeof value === 'number') return Number.isFinite(value) ? String(value) : null;\n return null;\n}\n\nconst VIEW_CLASS = cn(buttonVariants({ variant: 'outline', size: 'icon' }), 'shrink-0');\n\nexport interface RelationViewButtonProps {\n readonly to: string;\n readonly value: unknown;\n readonly by: string | undefined;\n /**\n * The select has explicit fixed filters, so the caller owns the scope: the `by` read skips the\n * ambient sidebar scope as the picker's option read does.\n */\n readonly ignoreScope?: boolean;\n /** The form field holding the key: the test id's second part. */\n readonly fieldName: string;\n /** Runs before the overlay opens: the picker moves focus to its trigger, where it returns. */\n readonly beforeOpen: () => void;\n /**\n * The field's `ui.open` (P7 3.6): `quick` (default) opens the record above the form, `page`\n * goes to its show page through the navigation guard (a dirty form asks first), `none` draws\n * no button.\n */\n readonly open?: RelationOpen;\n}\n\n/**\n * \"View\" beside a relation select's trigger (P6-28): the target's `quick.show` as a new overlay\n * above the form, never a push and never closing the host. Drawn only when the select holds a\n * key and the entry gate is open without a row (the row-level re-check runs inside the overlay,\n * P3-44); while the gate is undecided nothing is drawn and nothing is read (P3-15). A `by` key is\n * resolved by the limit-2 read on click; zero or two rows open nothing.\n */\nexport function RelationViewButton(props: RelationViewButtonProps): React.ReactElement | null {\n const gate = useEntryGate();\n const { to, value, by } = props;\n const access = useListAccess(to);\n const key = selectKey(value);\n if (key === null || props.open === 'none') return null;\n // A `by` field the user may not view is never sent as a predicate: no \"view\", no read.\n if (by !== undefined && !(access.status === 'ready' && access.fieldVisible(by))) return null;\n const answer = gate(to, 'show');\n if (answer.state !== 'open') return null;\n // Keyed by the value: a click asked for one key's read and must not outlive it.\n return <ViewButton key={key} {...props} keyString={key} />;\n}\n\nfunction ViewButton({\n to,\n by,\n ignoreScope,\n fieldName,\n keyString,\n beforeOpen,\n open: openMode = 'quick',\n}: RelationViewButtonProps & { readonly keyString: string }): React.ReactElement | null {\n const app = useApp();\n const t = useOptionalT();\n const tid = useTestIds();\n const quick = useQuick();\n const navigate = useRouteNavigate();\n const RouterLink = useLinkComponent();\n const [asked, setAsked] = React.useState(false);\n const requested = React.useRef(false);\n const targetDef = app.registry.get(to);\n const rawIdField = (targetDef?.data as { readonly idField?: unknown } | undefined)?.idField;\n const idField = typeof rawIdField === 'string' && rawIdField.length > 0 ? rawIdField : 'id';\n const read = useList(to, {\n ...(asked && by !== undefined\n ? {\n filter: { field: by, op: 'eq', value: keyString } as never,\n pagination: { kind: 'offset', page: 1, pageSize: 2 } as never,\n }\n : {}),\n ...(ignoreScope === true ? { providerOptionsOverride: { ignoreScope: true } } : {}),\n enabled: asked && by !== undefined,\n retry: false,\n });\n // Rows of this key only: a placeholder carried over from another key's read is not an answer.\n const current = read.isCurrentData && !read.isPlaceholderData && !read.isFetching;\n const rows = current\n ? (read.data as { readonly data?: readonly Record<string, unknown>[] } | undefined)?.data\n : undefined;\n const open = React.useCallback(\n (id: string) => {\n beforeOpen();\n if (openMode === 'page') {\n // The page of the record, through the navigation guard: unsaved input asks first.\n if (targetDef !== undefined && linksToPage(app, targetDef))\n navigate(resourceShowHref(app.registry, targetDef, to, id));\n return;\n }\n quick.show(to, id);\n },\n [beforeOpen, quick, to, openMode, navigate, app, targetDef],\n );\n // Only a click opens: the read answers the click that asked for it, once.\n React.useEffect(() => {\n if (by === undefined || !asked || rows === undefined || !requested.current) return;\n requested.current = false;\n if (rows.length !== 1) {\n if (rows.length > 1 && process.env.NODE_ENV !== 'production')\n console.error(\n `[burojs] ${to}: more than one row has ${by} ${keyString}; \"view\" opened nothing.`,\n );\n return;\n }\n const id = rows[0]?.[idField];\n const next = selectKey(id);\n if (next !== null) open(next);\n }, [by, asked, rows, idField, keyString, to, open]);\n\n const label = t('relationPicker.view', { defaultValue: 'View' });\n const icon = <IconEye className=\"h-4 w-4\" aria-hidden />;\n const testid = tid.relationView(to, fieldName);\n // `page` needs a page to go to (the publish check says so): without one, nothing is drawn.\n if (openMode === 'page' && (targetDef === undefined || !linksToPage(app, targetDef))) return null;\n if (openMode === 'page' && by === undefined && targetDef !== undefined)\n return (\n <RouterLink\n href={resourceShowHref(app.registry, targetDef, to, keyString)}\n data-testid={testid}\n className={VIEW_CLASS}\n onClick={() => beforeOpen()}\n >\n {icon}\n <span className=\"sr-only\">{label}</span>\n </RouterLink>\n );\n if (by === undefined && targetDef !== undefined && linksToPage(app, targetDef))\n return (\n <ClaimedQuickLink\n href={resourceShowHref(app.registry, targetDef, to, keyString)}\n testid={testid}\n className={VIEW_CLASS}\n onOpen={() => open(keyString)}\n >\n {icon}\n <span className=\"sr-only\">{label}</span>\n </ClaimedQuickLink>\n );\n return (\n <button\n type=\"button\"\n className={VIEW_CLASS}\n data-testid={testid}\n aria-label={label}\n {...{ [QUICK_ANCHOR_ATTRIBUTE]: '' }}\n onClick={(event) => {\n event.preventDefault();\n event.stopPropagation();\n // No page to follow (or, for a `by` key, no id to build it from): a modified click is\n // nothing, never an overlay the user asked to avoid.\n if (!isPlainLeftClick(event)) return;\n if (by === undefined) {\n open(keyString);\n return;\n }\n requested.current = true;\n setAsked(true);\n // A key already read opens at once; the effect answers a click made before the read.\n if (rows !== undefined && rows.length === 1) {\n const next = selectKey(rows[0]?.[idField]);\n requested.current = false;\n if (next !== null) open(next);\n }\n }}\n >\n {icon}\n </button>\n );\n}\n","'use client';\n\nimport * as React from 'react';\nimport type { FixedFilterInput, SearchDecl } from '@burojs/core';\nimport {\n type QuickSettledRow,\n useApp,\n useListAccess,\n useEntryGate,\n useOptionalT,\n useQuick,\n useTestIds,\n} from '@burojs/react';\nimport { toast } from '../notification/toaster.js';\nimport {\n CreatedOptionRead,\n type CreatedOptionResult,\n createdOptionFilter,\n createdOptionScope,\n} from './relation-hand-back.js';\nimport { namesHiddenField } from './search-access.js';\nimport { selectKey } from './relation-view-button.js';\n\n/** What the picker and its dialog share: one pending create per select (P6-34). */\nexport interface SelectCreate {\n readonly pending: boolean;\n /**\n * The hand-back read is forbidden: its filter names a field P5 hides from the user (the same\n * rule that stops the picker's own option read), or the access map is not ready. A blocked\n * select offers no \"+ Create\" and starts none, so no predicate on a hidden field is ever sent.\n */\n readonly blocked: boolean;\n /** Runs `open` unless a create of this select is already pending or the select is gone. */\n start(open: () => Promise<QuickSettledRow>): void;\n}\n\ninterface SelectCreateInput {\n readonly to: string;\n readonly by: string | undefined;\n /** The row field the select holds (`by`, else the target's id field). */\n readonly valueField: string;\n readonly idField: string;\n readonly fixedFilters: readonly FixedFilterInput[] | undefined;\n /**\n * The select's candidates gate is not ready (pending, empty, forbidden): the filters above\n * lack the candidate filters, so the hand-back must not read.\n */\n readonly held?: boolean;\n readonly searchDecl: SearchDecl | undefined;\n readonly searchContext: { readonly resource: string; readonly def: unknown };\n /** Called with the value to select, as a user's pick would. */\n readonly onValue: (value: string | number) => void;\n}\n\n/**\n * The select's one pending create: the latch (a ref, so a double click or a second render cannot\n * start two), the mounted guard (a result arriving after the select unmounted is ignored — the\n * record exists, nothing is selected), and the hand-back re-read through the select's own option\n * query. `reader` is mounted by the select while a created record is being handed back.\n */\nexport function useSelectCreate(input: SelectCreateInput): {\n readonly create: SelectCreate;\n readonly reader: React.ReactNode;\n} {\n const t = useOptionalT();\n const { to: target } = input;\n const [pending, setPending] = React.useState(false);\n const [created, setCreated] = React.useState<{ readonly id: string } | null>(null);\n const latch = React.useRef(false);\n const mounted = React.useRef(true);\n React.useEffect(() => {\n mounted.current = true;\n return () => {\n mounted.current = false;\n };\n }, []);\n const latest = React.useRef(input);\n latest.current = input;\n const access = useListAccess(target);\n // The hand-back's own filter, judged as the picker judges its option query.\n const blocked =\n input.held === true ||\n access.status !== 'ready' ||\n namesHiddenField(\n createdOptionScope({\n fixed: input.fixedFilters,\n searchDecl: input.searchDecl,\n searchContext: input.searchContext,\n }),\n (name) => access.fieldVisible(name),\n );\n const blockedRef = React.useRef(blocked);\n blockedRef.current = blocked;\n\n const release = React.useCallback(() => {\n latch.current = false;\n if (mounted.current) {\n setPending(false);\n setCreated(null);\n }\n }, []);\n\n const start = React.useCallback(\n (open: () => Promise<QuickSettledRow>) => {\n if (latch.current || !mounted.current || blockedRef.current) return;\n latch.current = true;\n setPending(true);\n let opened: Promise<QuickSettledRow>;\n try {\n opened = open();\n } catch (error) {\n // A synchronous throw (a `beforeOpen`, the host) must not leave the select locked.\n release();\n throw error;\n }\n void opened.then(\n (row) => {\n if (!mounted.current) {\n latch.current = false;\n return;\n }\n const id =\n row === null ? null : (selectKey(row[latest.current.idField]) ?? selectKey(row.id));\n if (id === null) release();\n else setCreated({ id });\n },\n () => release(),\n );\n },\n [release],\n );\n\n const settle = React.useCallback(\n (result: CreatedOptionResult) => {\n if (mounted.current) {\n if (result.kind === 'fits') latest.current.onValue(result.value);\n else if (result.kind === 'does-not-fit')\n toast(\n t('relationPicker.createdNotFitting', {\n defaultValue: 'The record was created but does not fit this field',\n }),\n );\n else\n toast.error(\n t('relationPicker.createdNotSelected', {\n defaultValue: 'The record was created but could not be selected',\n }),\n );\n }\n release();\n },\n [release, t],\n );\n\n const create = React.useMemo<SelectCreate>(\n () => ({ pending, blocked, start }),\n [pending, blocked, start],\n );\n const reader =\n created === null || blocked ? null : (\n <CreatedOptionRead\n key={created.id}\n to={target}\n filter={createdOptionFilter({\n fixed: input.fixedFilters,\n idField: input.idField,\n id: created.id,\n searchDecl: input.searchDecl,\n searchContext: input.searchContext,\n })}\n ignoreScope={input.fixedFilters !== undefined && input.fixedFilters.length > 0}\n by={input.by}\n idField={input.idField}\n onSettled={settle}\n />\n );\n return { create, reader };\n}\n\n/** A string field a typed search text may fill: declared plain, not system, not an enum. */\nfunction plainStringField(def: unknown): boolean {\n if (def === null || typeof def !== 'object') return false;\n const field = def as {\n readonly kind?: unknown;\n readonly system?: unknown;\n readonly opts?: {\n readonly system?: unknown;\n readonly enum?: unknown;\n readonly localized?: unknown;\n };\n };\n return (\n field.kind === 'string' &&\n field.system !== true &&\n field.opts?.system !== true &&\n field.opts?.enum === undefined &&\n field.opts?.localized !== true\n );\n}\n\nexport interface RelationCreateButtonProps {\n readonly to: string;\n readonly fieldName: string;\n readonly create: SelectCreate;\n /** The trusted `defaults` tier: the registry field's static `eq` literals (P6-31). */\n readonly seeds: Readonly<Record<string, unknown>> | undefined;\n /** The text typed into the select's search, if any: prefills the display field as user input. */\n readonly search?: string;\n /** Runs before the overlay opens: closes the popover, returns focus to the trigger. */\n readonly beforeOpen: () => void;\n readonly className?: string;\n readonly testId?: string;\n}\n\n/**\n * \"+ create\" (P6-29): the target's `quick.create` as a new overlay above the form. Drawn only when\n * the entry gate is open (P3 4.4: `operations.create`, `views.quick.create`, the host wired, the\n * `create` availability); undecided or denied draws nothing and reads nothing. Disabled while one\n * create of the select is pending.\n */\nexport function RelationCreateButton(props: RelationCreateButtonProps): React.ReactElement | null {\n const gate = useEntryGate();\n const { to } = props;\n if (props.create.blocked || gate(to, 'create').state !== 'open') return null;\n return <CreateButton {...props} />;\n}\n\nfunction CreateButton({\n to,\n fieldName,\n create,\n seeds,\n search,\n beforeOpen,\n className,\n testId,\n}: RelationCreateButtonProps): React.ReactElement {\n const app = useApp();\n const t = useOptionalT();\n const tid = useTestIds();\n const quick = useQuick();\n const label = t('relationPicker.create', { defaultValue: '+ Create' });\n return (\n <button\n type=\"button\"\n data-testid={testId ?? tid.relationCreate(to, fieldName)}\n disabled={create.pending}\n className={className}\n onClick={() => {\n const text = search?.trim() ?? '';\n const displayField = (\n app.registry.get(to) as { readonly displayField?: unknown } | undefined\n )?.displayField;\n const fields = (\n app.registry.get(to) as { readonly fields?: Record<string, unknown> } | undefined\n )?.fields;\n const prefill =\n text.length > 0 &&\n typeof displayField === 'string' &&\n fields !== undefined &&\n Object.hasOwn(fields, displayField) &&\n plainStringField(fields[displayField])\n ? { [displayField]: text }\n : undefined;\n create.start(() => {\n beforeOpen();\n return quick.create(to, {\n ...(seeds === undefined || Object.keys(seeds).length === 0 ? {} : { defaults: seeds }),\n ...(prefill === undefined ? {} : { input: prefill }),\n });\n });\n }}\n >\n {label}\n </button>\n );\n}\n"],"mappings":"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"}