Разные фиксы #8

Merged
gorbenko.pavel merged 8 commits from fix-webpart-properties into main 2026-08-18 08:45:42 +00:00
6 changed files with 43 additions and 10 deletions
Showing only changes of commit 4fee057f15 - Show all commits
@@ -35,6 +35,11 @@ export default class RequestPortalWebPart extends BaseClientSideWebPart<IRequest
ErrorOverlay,
undefined,
React.createElement<IRequestPortalProps>(RequestPortal, {
// key на подменённом e-mail: смена подмены в property pane должна
// пересобрать раздел заново — иначе счётчики в боковом меню и открытые
// таблицы остались бы от прежнего пользователя (их эффекты завязаны на
// монтирование/context, а не на identity подмены).
key: this.properties.debugUserEmail || '__self__',
isDarkTheme: this._isDarkTheme,
environmentMessage: this._environmentMessage,
hasTeamsContext: !!this.context.sdks.microsoftTeams,
@@ -50,15 +50,26 @@
// Раскладка: боковой рейл (side) на всю высоту + основная область (main).
.layout {
display: flex;
align-items: stretch; // рейл тянется на высоту контента
align-items: flex-start; // рейл больше НЕ тянется по контенту — см. .side
width: 100%;
min-height: 78vh; // «присутствие» рейла даже когда контента мало
}
// Рейл-колонка: сам Sidebar тянется по высоте (flex-стретч).
// Рейл-колонка: раньше тянулась по высоте .main (align-items: stretch), из-за
// чего высота рейла плавала вместе с содержимым соседней колонки — например, у
// вкладки «Инициированные мной» появляется доп. фильтр по статусу, .main
// становится чуть выше, и градиентный фон Sidebar (посчитанный от своей же
// высоты) заметно менял оттенок. Прибиваем рейл к высоте окна (sticky + 100vh):
// теперь он не зависит от того, что показано рядом. На мобильном брейкпоинте
// сам Sidebar уходит в position:fixed (см. @tnsu/ui-kit) — там эти правила уже
// не действуют.
.side {
flex: 0 0 auto;
display: flex;
position: sticky;
top: 0;
align-self: flex-start;
height: 100vh;
}
.main {
@@ -134,7 +134,7 @@ const RequestPortal: React.FC<IRequestPortalProps> = ({ hasTeamsContext, context
// загрузки в таблице не перезапустится сам. Шлём событие обновления —
// таблица на него подписана и перечитает данные фоном, не убирая с экрана
// текущие строки (см. RequestsTableView/SearchView).
if (nav === route.nav && route.mode === 'submit') emitRequestsChanged();
if (nav === route.nav && route.mode === 'submit') emitRequestsChanged(true);
// Всё восстанавливаемое состояние раздела передаём ЯВНО, из запомненного.
// parseRoute подставил бы его и сам, но тогда оно осталось бы только в
// состоянии и не попало бы в адрес: ссылка на раздел вела бы на первую
@@ -416,7 +416,14 @@ export const RequestsTableView: React.FC<IRequestsTableViewProps> = ({
};
load().catch(() => undefined); // ошибки уже обработаны внутри load через setError
const unsubscribe = onRequestsChanged(() => {
const unsubscribe = onRequestsChanged((force) => {
// Явный повторный клик по уже открытому пункту меню — пользователь ждёт
// видимого обновления, а не молчаливой подмены данных за спиной. Прежние
// строки прячем сразу; свежие подставит load().
if (force) {
setRows(undefined);
setLoadedKey('');
}
load().catch(() => undefined);
});
// Пока вкладка браузера скрыта, обновляться незачем — при возврате к ней
@@ -200,7 +200,8 @@ export const SearchView: React.FC<ISearchViewProps> = ({
};
load().catch(() => undefined);
const unsubscribe = onRequestsChanged(() => {
const unsubscribe = onRequestsChanged((force) => {
if (force) setRows(undefined);
load().catch(() => undefined);
});
const timer = window.setInterval(() => {
@@ -4,12 +4,21 @@
// достаточно для нечастых событий (сохранение формы, решение согласующего).
const EVENT_NAME = 'rp-requests-changed';
export function emitRequestsChanged(): void {
window.dispatchEvent(new Event(EVENT_NAME));
/**
* force=true — повторный клик по УЖЕ открытому пункту меню (см. goto в
* RequestPortal.tsx): пользователь явно просит обновить список, поэтому таблица
* прячет прежние строки и показывает «Загрузка…», пока не придёт свежий ответ.
* force=false/не задан — фоновые обновления (создание заявки, решение
* согласующего, десятиминутный автообновление): строки остаются на экране, чтобы
* не моргать, если данные не изменились.
*/
export function emitRequestsChanged(force?: boolean): void {
window.dispatchEvent(new CustomEvent<{ force?: boolean }>(EVENT_NAME, { detail: { force } }));
}
/** Подписка на изменения. Возвращает функцию отписки (вызвать в cleanup эффекта). */
export function onRequestsChanged(handler: () => void): () => void {
window.addEventListener(EVENT_NAME, handler);
return () => window.removeEventListener(EVENT_NAME, handler);
export function onRequestsChanged(handler: (force?: boolean) => void): () => void {
const listener = (e: Event): void => handler((e as CustomEvent<{ force?: boolean }>).detail?.force);
window.addEventListener(EVENT_NAME, listener);
return () => window.removeEventListener(EVENT_NAME, listener);
}