XFA - Use native radio and checkbox buttons

- Remove current stuff which relies on some system fonts to avoid bad rendering.
This commit is contained in:
Calixte Denizet 2021-05-27 17:44:55 +02:00
parent f2ade671ec
commit f61f80a5a3
4 changed files with 70 additions and 102 deletions

View File

@ -890,71 +890,45 @@ class CheckButton extends XFAObject {
}
[$toHTML](availableSpace) {
// TODO: shape and mark == default.
const style = toStyle(this, "border", "margin");
// TODO: border, shape and mark.
const style = toStyle("margin");
const size = measureToString(this.size);
style.width = style.height = size;
let mark, radius;
if (this.shape === "square") {
mark = "▪";
radius = "10%";
} else {
mark = "●";
radius = "50%";
}
if (!style.borderRadius) {
style.borderRadius = radius;
}
if (this.mark !== "default") {
// TODO: To avoid some rendering issues we should use svg
// to draw marks.
switch (this.mark) {
case "check":
mark = "✓";
break;
case "circle":
mark = "●";
break;
case "cross":
mark = "✕";
break;
case "diamond":
mark = "♦";
break;
case "square":
mark = "▪";
break;
case "star":
mark = "★";
break;
}
}
if (size !== "10px") {
style.fontSize = size;
style.lineHeight = size;
style.width = size;
style.height = size;
}
let type;
let className;
let groupId;
let id;
const fieldId = this[$getParent]()[$getParent]()[$uid];
const container = this[$getParent]()[$getParent]()[$getParent]();
if (container instanceof ExclGroup) {
groupId = container[$uid];
type = "radio";
className = "xfaRadio";
id = `${fieldId}-radio`;
} else {
type = "checkbox";
className = "xfaCheckbox";
}
const input = {
name: "input",
attributes: {
class: "xfaCheckbox",
class: className,
style,
fieldId,
type: "radio",
id: `${fieldId}-radio`,
type,
},
};
const container = this[$getParent]()[$getParent]()[$getParent]();
if (container instanceof ExclGroup) {
input.attributes.name = container[$uid];
if (id) {
input.attributes.id = id;
}
if (groupId) {
input.attributes.name = groupId;
}
return HTMLResult.success({
@ -962,17 +936,7 @@ class CheckButton extends XFAObject {
attributes: {
class: "xfaLabel",
},
children: [
input,
{
name: "span",
attributes: {
class: "xfaCheckboxMark",
mark,
style,
},
},
],
children: [input],
});
}
}

View File

@ -16,20 +16,26 @@
import { PageViewport } from "./display_utils.js";
class XfaLayer {
static setupStorage(html, fieldId, element, storage) {
static setupStorage(html, fieldId, element, storage, intent) {
const storedData = storage.getValue(fieldId, { value: null });
switch (element.name) {
case "textarea":
html.textContent = storedData.value !== null ? storedData.value : "";
if (intent === "print") {
break;
}
html.addEventListener("input", event => {
storage.setValue(fieldId, { value: event.target.value });
});
break;
case "input":
if (storedData.value !== null) {
html.setAttribute("value", storedData.value);
}
if (element.attributes.type === "radio") {
if (storedData.value) {
html.setAttribute("checked", true);
}
if (intent === "print") {
break;
}
html.addEventListener("change", event => {
const { target } = event;
for (const radio of document.getElementsByName(target.name)) {
@ -40,7 +46,23 @@ class XfaLayer {
}
storage.setValue(fieldId, { value: target.checked });
});
} else if (element.attributes.type === "checkbox") {
if (storedData.value) {
html.setAttribute("checked", true);
}
if (intent === "print") {
break;
}
html.addEventListener("input", event => {
storage.setValue(fieldId, { value: event.target.checked });
});
} else {
if (storedData.value !== null) {
html.setAttribute("value", storedData.value);
}
if (intent === "print") {
break;
}
html.addEventListener("input", event => {
storage.setValue(fieldId, { value: event.target.value });
});
@ -66,8 +88,13 @@ class XfaLayer {
}
}
static setAttributes(html, element, storage) {
static setAttributes(html, element, storage, intent) {
const { attributes } = element;
if (attributes.type === "radio") {
// Avoid to have a radio group when printing with the same as one
// already displayed.
attributes.name = `${attributes.name}-${intent}`;
}
for (const [key, value] of Object.entries(attributes)) {
if (value === null || value === undefined || key === "fieldId") {
continue;
@ -94,6 +121,7 @@ class XfaLayer {
static render(parameters) {
const storage = parameters.annotationStorage;
const root = parameters.xfa;
const intent = parameters.intent;
const rootHtml = document.createElement(root.name);
if (root.attributes) {
this.setAttributes(rootHtml, root);
@ -134,7 +162,7 @@ class XfaLayer {
const childHtml = document.createElement(name);
html.appendChild(childHtml);
if (child.attributes) {
this.setAttributes(childHtml, child, storage);
this.setAttributes(childHtml, child, storage, intent);
}
if (child.children && child.children.length > 0) {

View File

@ -125,38 +125,6 @@
resize: none;
}
.xfaLabel > input[type="radio"] {
/* Use this trick to make the checkbox invisible but
but still focusable. */
position: absolute;
left: -99999px;
}
.xfaLabel > input[type="radio"]:focus + .xfaCheckboxMark {
box-shadow: 0 0 5px rgba(0, 0, 0, 0.7);
}
.xfaCheckboxMark {
cursor: pointer;
flex: 0 0 auto;
border-style: solid;
border-width: 2px;
border-color: #8f8f9d;
font-size: 10px;
line-height: 10px;
width: 10px;
height: 10px;
text-align: center;
vertical-align: middle;
display: flex;
flex-direction: row;
align-items: center;
}
.xfaCheckbox:checked + .xfaCheckboxMark::after {
content: attr(mark);
}
.xfaButton {
cursor: pointer;
width: 100%;
@ -169,6 +137,13 @@
background: Highlight;
}
.xfaCheckbox,
.xfaRadio {
width: 100%;
height: 100%;
border: none;
}
.xfaRich {
white-space: pre-wrap;
}

View File

@ -82,6 +82,7 @@ class XfaLayerBuilder {
xfa: this.xfaHtml,
page: null,
annotationStorage: this.annotationStorage,
intent,
};
// Create an xfa layer div and render the form