refactor(PageHeaderActions): restructure actions layout and enhance button visibility for improved user interaction
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m52s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m52s
This commit is contained in:
@@ -436,6 +436,7 @@
|
||||
height: 100%;
|
||||
}
|
||||
.header-actions .btn svg { width: 18px; height: 18px; }
|
||||
.header-actions .header-actions-right { flex-shrink: 0; }
|
||||
|
||||
/* Ensure dropdowns in header-actions overlay tables/cards correctly but stay below modals */
|
||||
.header-actions .dropdown-menu,
|
||||
|
||||
@@ -602,7 +602,7 @@ function ServerManager() {
|
||||
</span>
|
||||
}
|
||||
actions={
|
||||
<div className="d-flex flex-wrap align-items-center gap-2">
|
||||
<>
|
||||
<PageHeaderActions
|
||||
loading={loading}
|
||||
onRefresh={handleRefreshAll}
|
||||
@@ -612,33 +612,34 @@ function ServerManager() {
|
||||
onSave={handleSaveChanges}
|
||||
disableSave={loading}
|
||||
/>
|
||||
<span className="vr d-none d-md-inline align-self-center" style={{ minHeight: '1.5rem' }} />
|
||||
<div className="btn-group" role="group" aria-label="Переключение режимов">
|
||||
<div className="mt-3 d-flex flex-wrap align-items-center justify-content-end gap-2">
|
||||
<div className="btn-group" role="group" aria-label="Переключение режимов">
|
||||
<button
|
||||
className={`btn ${activeTab === 'servers' ? 'btn-primary' : 'btn-outline-primary'}`}
|
||||
onClick={() => setActiveTab('servers')}
|
||||
type="button"
|
||||
>
|
||||
<IconServer size={16} className="me-1" />
|
||||
Серверы
|
||||
</button>
|
||||
<button
|
||||
className={`btn ${activeTab === 'connections' ? 'btn-primary' : 'btn-outline-primary'}`}
|
||||
onClick={() => setActiveTab('connections')}
|
||||
type="button"
|
||||
>
|
||||
<IconGitBranch size={16} className="me-1" />
|
||||
Связи
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
className={`btn ${activeTab === 'servers' ? 'btn-primary' : 'btn-outline-primary'}`}
|
||||
onClick={() => setActiveTab('servers')}
|
||||
type="button"
|
||||
className="btn btn-primary"
|
||||
onClick={handleOpenAddServerModal}
|
||||
>
|
||||
<IconServer size={16} className="me-1" />
|
||||
Серверы
|
||||
</button>
|
||||
<button
|
||||
className={`btn ${activeTab === 'connections' ? 'btn-primary' : 'btn-outline-primary'}`}
|
||||
onClick={() => setActiveTab('connections')}
|
||||
type="button"
|
||||
>
|
||||
<IconGitBranch size={16} className="me-1" />
|
||||
Связи
|
||||
<IconPlus size={16} className="me-1" /> Добавить
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-primary"
|
||||
onClick={handleOpenAddServerModal}
|
||||
>
|
||||
<IconPlus size={16} className="me-1" /> Добавить
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
|
||||
|
||||
@@ -3,12 +3,12 @@ import React from 'react'
|
||||
/**
|
||||
* Единый заголовок страницы в стиле Tabler UI.
|
||||
* Используется на всех страницах для одинакового UX.
|
||||
* Кнопки (actions) отображаются под названием страницы.
|
||||
* Кнопки (actions) отображаются справа от названия страницы.
|
||||
* @param {string} title - Заголовок страницы
|
||||
* @param {React.ReactNode} [icon] - Иконка слева от заголовка (например, <IconServer />)
|
||||
* @param {string} [pretitle] - Небольшая надпись над заголовком (page-pretitle)
|
||||
* @param {React.ReactNode} [meta] - Подзаголовок или мета-информация под заголовком
|
||||
* @param {React.ReactNode} [actions] - Кнопки/действия под заголовком (btn-list, btn-group)
|
||||
* @param {React.ReactNode} [actions] - Кнопки/действия справа в шапке (btn-list, btn-group)
|
||||
*/
|
||||
function PageHeader({ title, icon, pretitle, actions, meta }) {
|
||||
return (
|
||||
@@ -26,14 +26,12 @@ function PageHeader({ title, icon, pretitle, actions, meta }) {
|
||||
<div className="text-muted small mt-1">{meta}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{actions && (
|
||||
<div className="row mt-2">
|
||||
<div className="col d-print-none">
|
||||
{actions && (
|
||||
<div className="col-auto ms-auto d-print-none">
|
||||
{actions}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -59,9 +59,13 @@ function PageHeaderActions({
|
||||
{(onImport || onExport) && placeholderBtn(160)}
|
||||
{onClear && placeholderBtn(130)}
|
||||
{onClearCommunities && placeholderBtn(180)}
|
||||
{onSave && placeholderBtn(140)}
|
||||
{onHistory && placeholderBtn(110)}
|
||||
</div>
|
||||
{(onSave || onHistory) && (
|
||||
<div className="header-actions-right ms-auto d-flex align-items-center gap-2">
|
||||
{onSave && placeholderBtn(140)}
|
||||
{onHistory && placeholderBtn(110)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -140,33 +144,37 @@ function PageHeaderActions({
|
||||
<IconEraser className="me-1" /> Очистить community
|
||||
</button>
|
||||
)}
|
||||
{onSave && (
|
||||
<button className="btn btn-primary" type="button" onClick={onSave} disabled={disableSave} title="Сохранить">
|
||||
{loading ? (
|
||||
<>
|
||||
<span className="spinner-border spinner-border-sm me-2" role="status"></span>
|
||||
Сохранение...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<IconDeviceFloppy className="me-1" /> Сохранить
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
{onHistory && (
|
||||
<div className="btn-group">
|
||||
<button className="btn btn-outline-secondary" type="button" onClick={onHistory} title="История версий">История</button>
|
||||
{lastModified && (
|
||||
<button className="btn btn-outline-secondary" type="button" onClick={onHistory} title={`Последние изменения: ${formatDateTimeWithRelative(lastModified)}`}>
|
||||
<IconClock className="me-1" />
|
||||
<span className="d-none d-sm-inline">{formatDateTimeWithRelative(lastModified)}</span>
|
||||
<span className="d-inline d-sm-none">{formatDateTimeWithRelative(lastModified)}</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{(onSave || onHistory) && (
|
||||
<div className="header-actions-right ms-auto d-flex align-items-center gap-2">
|
||||
{onSave && (
|
||||
<button className="btn btn-primary" type="button" onClick={onSave} disabled={disableSave} title="Сохранить">
|
||||
{loading ? (
|
||||
<>
|
||||
<span className="spinner-border spinner-border-sm me-2" role="status"></span>
|
||||
Сохранение...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<IconDeviceFloppy className="me-1" /> Сохранить
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
{onHistory && (
|
||||
<div className="btn-group d-none d-sm-inline-flex">
|
||||
<button className="btn btn-outline-secondary" type="button" onClick={onHistory} title="История версий">История</button>
|
||||
{lastModified && (
|
||||
<button className="btn btn-outline-secondary" type="button" onClick={onHistory} title={`Последние изменения: ${formatDateTimeWithRelative(lastModified)}`}>
|
||||
<IconClock className="me-1" />
|
||||
<span className="d-none d-sm-inline">{formatDateTimeWithRelative(lastModified)}</span>
|
||||
<span className="d-inline d-sm-none">{formatDateTimeWithRelative(lastModified)}</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{/* Компактное меню для маленьких экранов */}
|
||||
{secondaryButtons && (
|
||||
<div className="btn-group d-inline-flex d-sm-none">
|
||||
|
||||
Reference in New Issue
Block a user