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
|
||||
/** Show [История] button + handler (только edit mode). */
|
||||
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
|
||||
}
|
||||
|
||||
@@ -101,6 +113,7 @@ export function RecordDrawer({
|
||||
isPending = false,
|
||||
onDelete,
|
||||
onHistory,
|
||||
toolbar,
|
||||
children,
|
||||
}: RecordDrawerProps) {
|
||||
const { t } = useTranslation()
|
||||
@@ -177,10 +190,36 @@ export function RecordDrawer({
|
||||
</div>
|
||||
</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) ===
|
||||
* Phase 2 здесь будет: sticky toolbar (search + filled-only toggle +
|
||||
* counter) → section anchor chips strip → optional left ToC rail в
|
||||
* wide mode (CSS grid 180px / 1fr). Пока children — SchemaDrivenForm. */}
|
||||
* Phase B остатки (deferred — требует x-section schema metadata):
|
||||
* - section anchor chips strip над form sections
|
||||
* - wide-mode left ToC rail (180px) в CSS grid 180px/1fr layout */}
|
||||
<div className="flex-1 overflow-y-auto px-5 py-4">{children}</div>
|
||||
|
||||
{/* === FOOTER (sticky bottom) === */}
|
||||
|
||||
Reference in New Issue
Block a user