feat: added status next in que and changed forms state machine
This commit is contained in:
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user