feat(events): a searchable source's chosen note is found when the page lacks it
A searchable source answers the editor a bounded first page, so the value an author picked (or a template filled in) may not be on it, and its note with it. useChosenNote asks the source for that value as a search term, once per value, and shows the exact match's note. Found building the Rust kit warning (PLAN_REDESIGNS §2.6), whose source - rust.options.grouppermissions - is searchable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -220,6 +220,46 @@ function SearchableOptions({ sourceId, label, disabled, onPick }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The chosen option's note (MODULE_API 1.13.0), found even when the list the
|
||||||
|
* form holds does not reach it.
|
||||||
|
*
|
||||||
|
* A searchable source answers the form a bounded first page, so the value an
|
||||||
|
* author picked — or a template filled in — may not be in it, and its note with
|
||||||
|
* it. Then the source is asked for that value as a search term, once per value,
|
||||||
|
* and the row whose value matches exactly is the one whose note is shown. A
|
||||||
|
* failure is no note: advice never breaks the field it advises.
|
||||||
|
*/
|
||||||
|
function useChosenNote(sourceId, value, entry) {
|
||||||
|
const [asked, setAsked] = useState({ key: null, note: null })
|
||||||
|
const text = value === undefined || value === null ? '' : String(value)
|
||||||
|
const known = noteFor(entry, value)
|
||||||
|
const needAsk = Boolean(sourceId && entry?.state === 'ok' && entry.searchable && text && !known &&
|
||||||
|
!entry.options.some((o) => o.value === text))
|
||||||
|
const key = needAsk ? `${sourceId}\u0000${text}` : null
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!key || asked.key === key) return undefined
|
||||||
|
let alive = true
|
||||||
|
const timer = setTimeout(async () => {
|
||||||
|
try {
|
||||||
|
const answer = await api.admin.eventOptions(sourceId, text)
|
||||||
|
const row = answer?.ok ? (answer.options || []).find((o) => o.value === text) : null
|
||||||
|
if (alive) setAsked({ key, note: row?.note || null })
|
||||||
|
} catch {
|
||||||
|
if (alive) setAsked({ key, note: null })
|
||||||
|
}
|
||||||
|
}, 300)
|
||||||
|
return () => {
|
||||||
|
alive = false
|
||||||
|
clearTimeout(timer)
|
||||||
|
}
|
||||||
|
}, [key, asked.key, sourceId, text])
|
||||||
|
|
||||||
|
if (known) return known
|
||||||
|
return key && asked.key === key ? asked.note : null
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A row of buttons for a choice of two or three (Rust PLAN_REDESIGNS §2.4).
|
* A row of buttons for a choice of two or three (Rust PLAN_REDESIGNS §2.4).
|
||||||
*
|
*
|
||||||
@@ -366,7 +406,7 @@ function ParamField({ param, value, sourceEntry, disabled, onChange, missing, la
|
|||||||
// editable rather than look unset.
|
// editable rather than look unset.
|
||||||
const segmented = param.source && param.type !== 'boolean' && !values && isSegmentable(sourceEntry)
|
const segmented = param.source && param.type !== 'boolean' && !values && isSegmentable(sourceEntry)
|
||||||
const offList = segmented && asText !== '' && !sourceEntry.options.some((o) => o.value === asText)
|
const offList = segmented && asText !== '' && !sourceEntry.options.some((o) => o.value === asText)
|
||||||
const note = param.source ? noteFor(sourceEntry, value) : null
|
const note = useChosenNote(param.source, value, sourceEntry)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ marginBottom: 12 }}>
|
<div style={{ marginBottom: 12 }}>
|
||||||
|
|||||||
Reference in New Issue
Block a user