formlift 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -3
- package/README.md +206 -2
- package/dist/{chunk-5VHPTLLV.cjs → chunk-33XWO4KB.cjs} +156 -34
- package/dist/chunk-33XWO4KB.cjs.map +1 -0
- package/dist/chunk-7NZPYWL3.cjs.map +1 -1
- package/dist/{chunk-BQAVPA7Q.js → chunk-NUKLGHKS.js} +157 -35
- package/dist/chunk-NUKLGHKS.js.map +1 -0
- package/dist/filters-Yzd4tG8r.d.ts +201 -0
- package/dist/filters-q6gqilzw.d.cts +201 -0
- package/dist/index.cjs +24 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -56
- package/dist/index.d.ts +5 -56
- package/dist/index.js +27 -5
- package/dist/react/index.cjs +1052 -836
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +528 -45
- package/dist/react/index.d.ts +528 -45
- package/dist/react/index.js +1278 -1062
- package/dist/react/index.js.map +1 -1
- package/dist/server/index.cjs.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-5VHPTLLV.cjs.map +0 -1
- package/dist/chunk-BQAVPA7Q.js.map +0 -1
- package/dist/layout-ExhBeO6b.d.cts +0 -27
- package/dist/layout-ExhBeO6b.d.ts +0 -27
|
@@ -997,7 +997,7 @@ function headerRow(lines, first) {
|
|
|
997
997
|
});
|
|
998
998
|
return names.every(Boolean) ? names : null;
|
|
999
999
|
}
|
|
1000
|
-
function toQuestions(fields, textOf = () => void 0) {
|
|
1000
|
+
function toQuestions(fields, textOf = () => void 0, { categoryCode = CODE } = {}) {
|
|
1001
1001
|
const sorted = [...fields].sort(
|
|
1002
1002
|
(a, b) => a.position.page - b.position.page || a.position.y - b.position.y || a.position.x - b.position.x
|
|
1003
1003
|
);
|
|
@@ -1165,7 +1165,7 @@ function toQuestions(fields, textOf = () => void 0) {
|
|
|
1165
1165
|
});
|
|
1166
1166
|
const at = (q) => top(q.parts.map((p) => p.field).filter((f) => f.position.page === q.page));
|
|
1167
1167
|
out.sort((a, b) => a.page - b.page || at(a) - at(b));
|
|
1168
|
-
setCategories(out, sorted, textOf);
|
|
1168
|
+
if (categoryCode) setCategories(out, sorted, textOf, categoryCode);
|
|
1169
1169
|
setSections(out, sorted, textOf);
|
|
1170
1170
|
setScales(out, textOf);
|
|
1171
1171
|
return mergeBlocks(out);
|
|
@@ -1270,10 +1270,10 @@ function mergeBlocks(out) {
|
|
|
1270
1270
|
return res;
|
|
1271
1271
|
}
|
|
1272
1272
|
var CODE = /^[A-Z]{2,4}$/;
|
|
1273
|
-
function setCategories(out, fields, textOf) {
|
|
1273
|
+
function setCategories(out, fields, textOf, code) {
|
|
1274
1274
|
const last = Math.max(0, ...fields.map((f) => f.position.page));
|
|
1275
1275
|
const found = [];
|
|
1276
|
-
const isCode = (t) =>
|
|
1276
|
+
const isCode = (t) => code.test(t.trim()) && !COLUMN.test(t.trim());
|
|
1277
1277
|
for (let page = 1; page <= last; page++) {
|
|
1278
1278
|
const lines = textOf(page) ?? [];
|
|
1279
1279
|
for (const l of lines) {
|
|
@@ -1431,25 +1431,7 @@ function tagsOf(text) {
|
|
|
1431
1431
|
if (pairs.some((m) => !m || m[1].trim().split(/\s+/).length > 3)) return null;
|
|
1432
1432
|
return Object.fromEntries(pairs.map((m) => [m[1].trim(), m[2].trim()]));
|
|
1433
1433
|
}
|
|
1434
|
-
var NO_FILTER = {
|
|
1435
|
-
search: "",
|
|
1436
|
-
answered: "all",
|
|
1437
|
-
remarks: "all",
|
|
1438
|
-
comments: "all",
|
|
1439
|
-
type: "all",
|
|
1440
|
-
category: "all",
|
|
1441
|
-
tags: {},
|
|
1442
|
-
choices: "all",
|
|
1443
|
-
picked: "all"
|
|
1444
|
-
};
|
|
1445
1434
|
var isFilled = (v) => v != null && v !== "" && v !== false;
|
|
1446
|
-
var partsLike = (q, re) => q.parts.filter((p) => re.test(p.name) || re.test(p.field.label));
|
|
1447
|
-
function check(parts, answers, want) {
|
|
1448
|
-
if (want === "all") return true;
|
|
1449
|
-
if (!parts.length) return false;
|
|
1450
|
-
const any = parts.some((p) => isFilled(answers[p.field.key]));
|
|
1451
|
-
return want === "filled" ? any : !any;
|
|
1452
|
-
}
|
|
1453
1435
|
function choiceParts(q) {
|
|
1454
1436
|
if (q.columns) return [];
|
|
1455
1437
|
const size = q.rows?.reduce((a, b) => a + b, 0) ?? 0;
|
|
@@ -1470,16 +1452,145 @@ function pickedOf(q, answers) {
|
|
|
1470
1452
|
const v = radio && answers[radio.key];
|
|
1471
1453
|
return typeof v === "string" && v ? [v] : [];
|
|
1472
1454
|
}
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1455
|
+
|
|
1456
|
+
// src/core/filters.ts
|
|
1457
|
+
var counted = (questions, values, label = (v) => v) => {
|
|
1458
|
+
const m = /* @__PURE__ */ new Map();
|
|
1459
|
+
for (const q of questions) for (const v of new Set(values(q))) m.set(v, (m.get(v) ?? 0) + 1);
|
|
1460
|
+
return [...m].map(([value, count]) => ({ value, label: label(value), count }));
|
|
1461
|
+
};
|
|
1462
|
+
var filledOptions = (yes, no) => [
|
|
1463
|
+
{ value: "filled", label: yes },
|
|
1464
|
+
{ value: "empty", label: no }
|
|
1465
|
+
];
|
|
1466
|
+
function filledTest(parts, value, answers) {
|
|
1467
|
+
if (!parts.length) return false;
|
|
1468
|
+
const any = parts.some((p) => isFilled(answers[p.field.key]));
|
|
1469
|
+
return value === "filled" ? any : !any;
|
|
1470
|
+
}
|
|
1471
|
+
var answeredFilter = ({
|
|
1472
|
+
id = "answered",
|
|
1473
|
+
title = "Answered",
|
|
1474
|
+
yes = "Answered",
|
|
1475
|
+
no = "Not answered",
|
|
1476
|
+
section = "Progress"
|
|
1477
|
+
} = {}) => ({
|
|
1478
|
+
id,
|
|
1479
|
+
title,
|
|
1480
|
+
section,
|
|
1481
|
+
options: (qs) => qs.length ? filledOptions(yes, no) : [],
|
|
1482
|
+
test: (q, v, a) => filledTest(q.parts, v, a)
|
|
1483
|
+
});
|
|
1484
|
+
function partFilter({
|
|
1485
|
+
id,
|
|
1486
|
+
title,
|
|
1487
|
+
match,
|
|
1488
|
+
yes = "Written",
|
|
1489
|
+
no = "Empty",
|
|
1490
|
+
section = "Progress"
|
|
1491
|
+
}) {
|
|
1492
|
+
const parts = (q) => q.parts.filter((p) => match.test(p.name) || match.test(p.field.label));
|
|
1493
|
+
return {
|
|
1494
|
+
id,
|
|
1495
|
+
title,
|
|
1496
|
+
section,
|
|
1497
|
+
options: (qs) => qs.some((q) => parts(q).length) ? filledOptions(yes, no) : [],
|
|
1498
|
+
test: (q, v, a) => filledTest(parts(q), v, a)
|
|
1499
|
+
};
|
|
1500
|
+
}
|
|
1501
|
+
var choiceFilter = ({
|
|
1502
|
+
id = "choices",
|
|
1503
|
+
title = "Has option",
|
|
1504
|
+
section = "Answers",
|
|
1505
|
+
min = 2,
|
|
1506
|
+
max = 40
|
|
1507
|
+
} = {}) => ({
|
|
1508
|
+
id,
|
|
1509
|
+
title,
|
|
1510
|
+
section,
|
|
1511
|
+
options: (qs) => counted(qs, choicesOf).filter((o) => o.count >= min).slice(0, max),
|
|
1512
|
+
test: (q, v) => choicesOf(q).includes(v)
|
|
1513
|
+
});
|
|
1514
|
+
var pickedFilter = ({
|
|
1515
|
+
id = "picked",
|
|
1516
|
+
title = "Answered with",
|
|
1517
|
+
section = "Answers",
|
|
1518
|
+
min = 2,
|
|
1519
|
+
max = 40
|
|
1520
|
+
} = {}) => ({
|
|
1521
|
+
id,
|
|
1522
|
+
title,
|
|
1523
|
+
section,
|
|
1524
|
+
options: (qs, answers) => {
|
|
1525
|
+
const offered = new Set(
|
|
1526
|
+
choiceFilter({ min, max }).options(qs, answers).map((o) => o.value)
|
|
1527
|
+
);
|
|
1528
|
+
const picked = new Map(counted(qs, (q) => pickedOf(q, answers)).map((o) => [o.value, o.count]));
|
|
1529
|
+
return [...offered].map((c) => ({ value: c, label: c, count: picked.get(c) ?? 0 }));
|
|
1530
|
+
},
|
|
1531
|
+
test: (q, v, a) => pickedOf(q, a).includes(v)
|
|
1532
|
+
});
|
|
1533
|
+
var categoryFilter = ({ id = "category", title = "Category", section = "bar" } = {}) => ({
|
|
1534
|
+
id,
|
|
1535
|
+
title,
|
|
1536
|
+
section,
|
|
1537
|
+
options: (qs) => counted(qs, (q) => q.category ? [q.category] : []),
|
|
1538
|
+
test: (q, v) => q.category === v
|
|
1539
|
+
});
|
|
1540
|
+
var typeFilter = ({
|
|
1541
|
+
id = "type",
|
|
1542
|
+
title = "Answer type",
|
|
1543
|
+
section = "Answer type",
|
|
1544
|
+
label = (t) => t
|
|
1545
|
+
} = {}) => ({
|
|
1546
|
+
id,
|
|
1547
|
+
title,
|
|
1548
|
+
section,
|
|
1549
|
+
options: (qs) => {
|
|
1550
|
+
const opts = counted(
|
|
1551
|
+
qs,
|
|
1552
|
+
(q) => q.parts.map((p) => p.field.type),
|
|
1553
|
+
(t) => label(t)
|
|
1554
|
+
);
|
|
1555
|
+
return opts.length > 1 ? opts : [];
|
|
1556
|
+
},
|
|
1557
|
+
test: (q, v) => q.parts.some((p) => p.field.type === v)
|
|
1558
|
+
});
|
|
1559
|
+
var tagFilter = (key, { title = key, section = "Question details" } = {}) => ({
|
|
1560
|
+
id: `tag:${key}`,
|
|
1561
|
+
title,
|
|
1562
|
+
section,
|
|
1563
|
+
options: (qs) => counted(qs, (q) => q.tags?.[key] ? [q.tags[key]] : []).sort(
|
|
1564
|
+
(a, b) => a.value.localeCompare(b.value, void 0, { numeric: true })
|
|
1565
|
+
),
|
|
1566
|
+
test: (q, v) => q.tags?.[key] === v
|
|
1567
|
+
});
|
|
1568
|
+
var tagFilters = ({ section = "Question details" } = {}) => (qs) => [...new Set(qs.flatMap((q) => Object.keys(q.tags ?? {})))].map((k) => tagFilter(k, { section }));
|
|
1569
|
+
var customFilter = (id, title, valuesOf, section) => ({
|
|
1570
|
+
id,
|
|
1571
|
+
title,
|
|
1572
|
+
section,
|
|
1573
|
+
options: (qs, answers) => counted(qs, (q) => valuesOf(q, answers)),
|
|
1574
|
+
test: (q, v, a) => valuesOf(q, a).includes(v)
|
|
1575
|
+
});
|
|
1576
|
+
var builtinFilters = (labels = {}) => [
|
|
1577
|
+
answeredFilter(),
|
|
1578
|
+
partFilter({ id: "remarks", title: "Remarks", match: /remark/i }),
|
|
1579
|
+
partFilter({ id: "comments", title: "Comments", match: /comment/i }),
|
|
1580
|
+
choiceFilter(),
|
|
1581
|
+
pickedFilter(),
|
|
1582
|
+
tagFilters(),
|
|
1583
|
+
typeFilter({ label: labels.type }),
|
|
1584
|
+
categoryFilter()
|
|
1585
|
+
];
|
|
1586
|
+
var resolveFilters = (sources, questions) => sources.flatMap((s) => typeof s === "function" ? s(questions) : [s]);
|
|
1587
|
+
var searchText = (q) => `${q.section ?? ""} ${q.intro ?? ""} ${q.title} ${q.parts.map((p) => p.field.label).join(" ")}`;
|
|
1588
|
+
function filterQuestions(questions, answers, filters, values, search = "") {
|
|
1589
|
+
const text = search.trim().toLowerCase();
|
|
1590
|
+
const active = filters.filter((f) => values[f.id] != null);
|
|
1591
|
+
return questions.filter(
|
|
1592
|
+
(q) => (!text || searchText(q).toLowerCase().includes(text)) && active.every((f) => f.test(q, values[f.id], answers))
|
|
1593
|
+
);
|
|
1483
1594
|
}
|
|
1484
1595
|
|
|
1485
1596
|
export {
|
|
@@ -1508,10 +1619,21 @@ export {
|
|
|
1508
1619
|
detectFields,
|
|
1509
1620
|
detectDocument,
|
|
1510
1621
|
toQuestions,
|
|
1511
|
-
NO_FILTER,
|
|
1512
1622
|
isFilled,
|
|
1513
1623
|
choicesOf,
|
|
1514
1624
|
pickedOf,
|
|
1515
|
-
|
|
1625
|
+
answeredFilter,
|
|
1626
|
+
partFilter,
|
|
1627
|
+
choiceFilter,
|
|
1628
|
+
pickedFilter,
|
|
1629
|
+
categoryFilter,
|
|
1630
|
+
typeFilter,
|
|
1631
|
+
tagFilter,
|
|
1632
|
+
tagFilters,
|
|
1633
|
+
customFilter,
|
|
1634
|
+
builtinFilters,
|
|
1635
|
+
resolveFilters,
|
|
1636
|
+
searchText,
|
|
1637
|
+
filterQuestions
|
|
1516
1638
|
};
|
|
1517
|
-
//# sourceMappingURL=chunk-
|
|
1639
|
+
//# sourceMappingURL=chunk-NUKLGHKS.js.map
|