feat: added status next in que and changed forms state machine

This commit is contained in:
Тимур Городецкий
2026-08-05 13:53:26 +05:00
parent 5bc80df11c
commit 571614c031
18 changed files with 373 additions and 68 deletions
@@ -0,0 +1,113 @@
import * as React from 'react';
import { Button } from '../Button/Button';
import { Icon } from '../Icon/Icon';
// Значение поля для печати. Дату (input type=date, ISO yyyy-mm-dd) — в дд.мм.гггг.
function formatValue(el: HTMLInputElement): string {
if (el.type === 'date' && /^\d{4}-\d{2}-\d{2}$/.test(el.value)) {
const [y, m, d] = el.value.split('-');
return `${d}.${m}.${y}`;
}
return el.value;
}
// Печатаем как ДАННЫЕ, а не как форму: каждое поле ввода в клоне заменяем на
// обычный текст его значения (значения берём из живого DOM — React держит их как
// свойства, в клонированном HTML их нет). Так textarea/поля не занимают лишнего
// места и лист выглядит как справка, а не как незаполненный бланк.
function renderValuesAsText(source: Element, clone: Element): void {
const put = (cloneEl: Element | undefined, text: string): void => {
if (!cloneEl) return;
const div = document.createElement('div');
div.className = 'rp-print-value';
div.textContent = text && text.length > 0 ? text : '—';
(cloneEl.closest('label') ?? cloneEl).replaceWith(div);
};
const cInputs = clone.querySelectorAll('input');
source.querySelectorAll('input').forEach((el, i) => {
if (el.type === 'checkbox' || el.type === 'radio') {
put(cInputs[i], el.checked ? 'Да' : 'Нет');
} else {
put(cInputs[i], formatValue(el));
}
});
const cAreas = clone.querySelectorAll('textarea');
source.querySelectorAll('textarea').forEach((el, i) => put(cAreas[i], el.value));
const cSelects = clone.querySelectorAll('select');
source.querySelectorAll('select').forEach((el, i) => {
const opt = el.options[el.selectedIndex];
put(cSelects[i], opt && opt.value ? opt.text : '');
});
}
// Стили печатного окна: чистый лист (без «коробки» карточки), без иконок,
// значения — обычным текстом с переносами.
const PRINT_CSS =
// height:auto перебивает глобальный CSS SharePoint (html/body 100vh) — иначе
// body раздувается выше листа A4 и в печать идёт лишняя пустая страница.
'html,body{margin:0;background:#fff;height:auto!important;min-height:0!important}' +
'body{padding:24px;font-size:13px}' +
'svg{display:none!important}' +
'.rp-print-root>*{border:0!important;box-shadow:none!important;padding:0!important}' +
'.rp-print-value{white-space:pre-wrap;color:#000;padding:1px 0 2px}';
/**
* Кнопка «Печать»: переносит документ (`.rp-print-root`) в отдельное чистое окно,
* заменяя поля ввода на текст значений, и печатает его. Не зависит от скролл-
* контейнеров/высоты SharePoint, корректно разбивается на страницы. Общая для форм.
*/
export const PrintButton: React.FC<{ label?: string }> = ({ label = 'Печать' }) => {
const onPrint = (): void => {
const source = document.querySelector('.rp-print-root');
if (!source) {
window.print();
return;
}
const win = window.open('', '_blank', 'width=980,height=1200');
if (!win) {
window.print(); // всплывающее окно заблокировано — печатаем как есть
return;
}
const clone = source.cloneNode(true) as Element;
// Убираем из печати всё помеченное rp-no-print (напр. блок «Вложения»).
clone.querySelectorAll('.rp-no-print').forEach((el) => el.remove());
renderValuesAsText(source, clone);
// Переносим стили страницы (CSS-модули инжектятся как <style>, шрифты — <link>).
const head = Array.from(document.querySelectorAll('style, link[rel="stylesheet"]'))
.map((n) => n.outerHTML)
.join('\n');
win.document.open();
win.document.write(
`<!doctype html><html><head><meta charset="utf-8">${head}` +
`<style>${PRINT_CSS}</style></head><body>${clone.outerHTML}</body></html>`
);
win.document.close();
win.focus();
// Даём стилям/шрифтам примениться, затем печатаем и закрываем окно.
const done = (): void => {
win.print();
win.close();
};
if (win.document.readyState === 'complete') {
window.setTimeout(done, 350);
} else {
win.onload = (): void => {
window.setTimeout(done, 350);
};
}
};
return (
<Button variant="secondary" onClick={onPrint}>
<Icon name="document" size="sm" />
{label}
</Button>
);
};
@@ -21,5 +21,6 @@ export { FormModeProvider, useFormReadOnly } from './FormMode';
export { Icon } from './Icon/Icon';
export { SubmitActions } from './SubmitActions/SubmitActions';
export { RequestNumber } from './RequestNumber/RequestNumber';
export { PrintButton } from './PrintButton/PrintButton';
export { useRequestSubmit } from './useRequestSubmit';
export type { IRequestSubmit, SubmitKind } from './useRequestSubmit';