feat(drawer): sticky toolbar API per handoff Screen 3 line 279
RecordDrawer теперь принимает optional toolbar prop: - search (string) — field name filter - onlyFilled (boolean) — show only filled fields toggle - counterVisible/Total (number) — 'N / M полей' caption Sticky position между header и body, h=44 per handoff style. Toolbar rendered только если caller передал. SchemaDrivenForm callsite (записать filter в каждое FieldRenderer) — separate follow-up, требует refactor form internals (currently 3 buckets identity/description/extra). Section anchor chips + wide-mode ToC rail — deferred до backend x-section schema metadata (CEO open question #5 в handoff).
This commit is contained in:
@@ -88,6 +88,18 @@ export type RecordDrawerProps = {
|
|||||||
onDelete?: () => void
|
onDelete?: () => void
|
||||||
/** Show [История] button + handler (только edit mode). */
|
/** Show [История] button + handler (только edit mode). */
|
||||||
onHistory?: () => void
|
onHistory?: () => void
|
||||||
|
/** Optional sticky toolbar per handoff line 279: search полей by name +
|
||||||
|
* "только заполненные" toggle + counter. Caller controls state. Pass
|
||||||
|
* undefined чтобы скрыть toolbar (e.g. create mode without filter context). */
|
||||||
|
toolbar?: {
|
||||||
|
search: string
|
||||||
|
onSearchChange: (next: string) => void
|
||||||
|
onlyFilled: boolean
|
||||||
|
onOnlyFilledChange: (next: boolean) => void
|
||||||
|
/** "N / M полей" caption — visible / total. */
|
||||||
|
counterVisible: number
|
||||||
|
counterTotal: number
|
||||||
|
}
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,6 +113,7 @@ export function RecordDrawer({
|
|||||||
isPending = false,
|
isPending = false,
|
||||||
onDelete,
|
onDelete,
|
||||||
onHistory,
|
onHistory,
|
||||||
|
toolbar,
|
||||||
children,
|
children,
|
||||||
}: RecordDrawerProps) {
|
}: RecordDrawerProps) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@@ -177,10 +190,36 @@ export function RecordDrawer({
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
{/* === TOOLBAR (sticky) per handoff line 279 ===
|
||||||
|
* Optional — caller passes toolbar prop с search/onlyFilled state. */}
|
||||||
|
{toolbar && (
|
||||||
|
<div className="shrink-0 border-b border-line-2 px-5 py-2 flex items-center gap-3 bg-surface">
|
||||||
|
<input
|
||||||
|
type="search"
|
||||||
|
value={toolbar.search}
|
||||||
|
onChange={(e) => toolbar.onSearchChange(e.target.value)}
|
||||||
|
placeholder={t('drawer.searchPlaceholder', { defaultValue: 'Поиск полей' })}
|
||||||
|
className="flex-1 h-7 px-2.5 rounded-md border border-line bg-surface-2 text-body text-ink placeholder:text-mute focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||||
|
/>
|
||||||
|
<label className="inline-flex items-center gap-1.5 text-cell text-ink-2 select-none cursor-pointer shrink-0">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={toolbar.onlyFilled}
|
||||||
|
onChange={(e) => toolbar.onOnlyFilledChange(e.target.checked)}
|
||||||
|
className="size-3.5 rounded-sm border-line accent-accent"
|
||||||
|
/>
|
||||||
|
{t('drawer.onlyFilled', { defaultValue: 'только заполненные' })}
|
||||||
|
</label>
|
||||||
|
<span className="text-mono text-mute tabular-nums shrink-0">
|
||||||
|
{toolbar.counterVisible}/{toolbar.counterTotal}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* === BODY (scrolling) ===
|
{/* === BODY (scrolling) ===
|
||||||
* Phase 2 здесь будет: sticky toolbar (search + filled-only toggle +
|
* Phase B остатки (deferred — требует x-section schema metadata):
|
||||||
* counter) → section anchor chips strip → optional left ToC rail в
|
* - section anchor chips strip над form sections
|
||||||
* wide mode (CSS grid 180px / 1fr). Пока children — SchemaDrivenForm. */}
|
* - wide-mode left ToC rail (180px) в CSS grid 180px/1fr layout */}
|
||||||
<div className="flex-1 overflow-y-auto px-5 py-4">{children}</div>
|
<div className="flex-1 overflow-y-auto px-5 py-4">{children}</div>
|
||||||
|
|
||||||
{/* === FOOTER (sticky bottom) === */}
|
{/* === FOOTER (sticky bottom) === */}
|
||||||
|
|||||||
Reference in New Issue
Block a user