@12-apps/ui 6.24.7 → 6.25.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/components-catalog.md +1 -1
- package/dist/_internal/components/data-display/CardKit/CardKebab.js +4 -4
- package/dist/_internal/components/data-display/CardKit/use-remove-confirm.js +4 -4
- package/dist/_internal/components/data-display/CardKit/use-row-confirm.js +4 -4
- package/dist/_internal/components/data-display/DataViews/DataViewsGrid.js +34 -34
- package/dist/_internal/components/data-display/DataViews/DataViewsTableBase.js +40 -40
- package/dist/_internal/components/data-display/DataViews/DeleteViewDialog.js +4 -4
- package/dist/_internal/components/data-display/DataViews/ManageViewsDialog.js +5 -5
- package/dist/_internal/components/data-display/DataViews/SaveViewModal.js +4 -4
- package/dist/_internal/components/data-display/DataViews/data-views-category-pill.js +15 -15
- package/dist/_internal/components/data-display/DataViews/data-views-columns.js +5 -5
- package/dist/_internal/components/data-display/DataViews/data-views-display-panel.js +5 -5
- package/dist/_internal/components/data-display/DataViews/data-views-empty.js +4 -4
- package/dist/_internal/components/data-display/DataViews/data-views-export.js +4 -4
- package/dist/_internal/components/data-display/DataViews/data-views-filter-panel.js +16 -16
- package/dist/_internal/components/data-display/DataViews/data-views-grid-bodies.js +6 -6
- package/dist/_internal/components/data-display/DataViews/data-views-grid-helpers.js +4 -4
- package/dist/_internal/components/data-display/DataViews/data-views-grid-parts.js +31 -31
- package/dist/_internal/components/data-display/DataViews/data-views-inline-bar.js +20 -20
- package/dist/_internal/components/data-display/DataViews/data-views-more-filters.js +4 -4
- package/dist/_internal/components/data-display/DataViews/data-views-select-all-strip.js +4 -4
- package/dist/_internal/components/data-display/DataViews/data-views-shell-toolbar.js +9 -9
- package/dist/_internal/components/data-display/DataViews/data-views-table-parts.js +7 -7
- package/dist/_internal/components/data-display/DataViews/data-views-toolbar.js +8 -8
- package/dist/_internal/components/data-display/DataViews/data-views-view-nav.js +4 -4
- package/dist/_internal/components/data-display/DataViews/use-data-views-state.js +6 -6
- package/dist/_internal/components/feedback/ConfirmAction/ConfirmButton.js +4 -4
- package/dist/_internal/components/feedback/index.js +4 -4
- package/dist/_internal/components/form/Button/Button.js +3 -3
- package/dist/_internal/components/form/Button/Button.metrics.js +3 -1
- package/dist/_internal/components/form/Button/Button.styles.js +2 -2
- package/dist/_internal/components/form/CategorySelect/CategoryPanel.js +8 -8
- package/dist/_internal/components/form/CategorySelect/CategoryPanelFoot.js +2 -2
- package/dist/_internal/components/form/CategorySelect/CategoryPanelHead.js +2 -2
- package/dist/_internal/components/form/CategorySelect/CategoryPanelList.js +4 -4
- package/dist/_internal/components/form/CategorySelect/CategoryQuickActions.js +2 -2
- package/dist/_internal/components/form/CategorySelect/CategoryRows.js +7 -9
- package/dist/_internal/components/form/CategorySelect/CategorySelect.js +14 -14
- package/dist/_internal/components/form/CategorySelect/CategorySelect.styles.js +3 -1
- package/dist/_internal/components/form/CategorySelect/CategoryTrigger.js +2 -2
- package/dist/_internal/components/form/CategorySelect/category-keyboard.js +2 -2
- package/dist/_internal/components/form/CategorySelect/category-tree.js +11 -3
- package/dist/_internal/components/form/CategorySelect/useCategoryCommit.js +2 -2
- package/dist/_internal/components/form/CategorySelect/useCategoryKeyHandler.js +3 -3
- package/dist/_internal/components/form/CategorySelect/useCategorySelect.js +2 -2
- package/dist/_internal/components/form/index.js +3 -3
- package/dist/_internal/components/form/total-form/Fields/CategoryField.js +15 -15
- package/dist/_internal/components/form/total-form/Fields/index.js +16 -16
- package/dist/_internal/components/form/total-form/SubmitButton.js +4 -4
- package/dist/_internal/components/index.js +5 -5
- package/dist/_internal/components/navigation/AppHeader/AppHeader.details.js +4 -4
- package/dist/_internal/components/navigation/index.js +4 -4
- package/dist/_internal/social-login-button/social-login-button.js +4 -4
- package/dist/_internal/user-avatar/user-avatar.js +4 -4
- package/dist/{chunk-6JGPPTLX.js → chunk-2GI7ANEL.js} +3 -3
- package/dist/{chunk-U2ZDN2AL.js → chunk-2PBGVMM2.js} +2 -2
- package/dist/{chunk-ITGG5OKP.js → chunk-5LQ7PKMW.js} +3 -1
- package/dist/chunk-5LQ7PKMW.js.map +1 -0
- package/dist/{chunk-4T7TXRGM.js → chunk-6ZY5XU4J.js} +3 -3
- package/dist/{chunk-CHPZTF34.js → chunk-7KWBGMQC.js} +2 -2
- package/dist/{chunk-6NSIZ2ZO.js → chunk-7MN6I5NK.js} +3 -3
- package/dist/{chunk-PN5CQKA2.js → chunk-AXTLFK4T.js} +2 -2
- package/dist/{chunk-JT6TERQL.js → chunk-BB2OUERT.js} +2 -2
- package/dist/{chunk-XUJG2HFW.js → chunk-BH5BXLGI.js} +2 -2
- package/dist/{chunk-4TXIZ5WI.js → chunk-CJUOBTLL.js} +2 -2
- package/dist/{chunk-NWNSUHEB.js → chunk-CT5K725B.js} +2 -2
- package/dist/{chunk-6OORHAE7.js → chunk-EQ7PWGBN.js} +3 -3
- package/dist/{chunk-EJQW2IYW.js → chunk-FPFUXRD2.js} +2 -2
- package/dist/{chunk-ZRPGVHTR.js → chunk-GGI7GM5O.js} +2 -2
- package/dist/{chunk-KIHQE5YA.js → chunk-GVDJQMFI.js} +2 -2
- package/dist/{chunk-TX5V42AY.js → chunk-HEBQS7QP.js} +3 -3
- package/dist/{chunk-BUQ2JUND.js → chunk-JO6BBSGY.js} +2 -2
- package/dist/{chunk-4ZV7N5CU.js → chunk-JXPHU3K5.js} +4 -4
- package/dist/{chunk-TW3BXVEU.js → chunk-KMXAPYU2.js} +2 -2
- package/dist/{chunk-CJBBGMIA.js → chunk-MNXVXW4T.js} +2 -2
- package/dist/{chunk-OFOYMZRY.js → chunk-NPN7JTRM.js} +22 -47
- package/dist/chunk-NPN7JTRM.js.map +1 -0
- package/dist/{chunk-BPLFM2GL.js → chunk-NYEA7JAI.js} +3 -3
- package/dist/{chunk-O6AZDQJJ.js → chunk-OE2ZVL67.js} +2 -2
- package/dist/{chunk-4523RYE7.js → chunk-OG2FP6SE.js} +2 -2
- package/dist/{chunk-XKV5ZIBG.js → chunk-OGGPQVIG.js} +2 -2
- package/dist/{chunk-SQNW22ZZ.js → chunk-OV4WREMW.js} +3 -3
- package/dist/{chunk-OVLNF4AZ.js → chunk-P7IVAMAU.js} +24 -35
- package/dist/chunk-P7IVAMAU.js.map +1 -0
- package/dist/{chunk-6LGWJFQK.js → chunk-PFDM2OIP.js} +2 -2
- package/dist/chunk-PN4HVM43.js +160 -0
- package/dist/chunk-PN4HVM43.js.map +1 -0
- package/dist/{chunk-WWM2YTW4.js → chunk-R2AZOWEZ.js} +21 -27
- package/dist/chunk-R2AZOWEZ.js.map +1 -0
- package/dist/{chunk-2GB5CIN6.js → chunk-RZVUY2MO.js} +8 -4
- package/dist/chunk-RZVUY2MO.js.map +1 -0
- package/dist/{chunk-YTOYMJPM.js → chunk-SLVQ33AN.js} +15 -5
- package/dist/chunk-SLVQ33AN.js.map +1 -0
- package/dist/{chunk-H5VEJDEK.js → chunk-TVPPA4RD.js} +7 -7
- package/dist/{chunk-ISH42UC7.js → chunk-USJPZ5Q7.js} +2 -2
- package/dist/{chunk-5OIH36KX.js → chunk-V4FKHLEB.js} +2 -2
- package/dist/{chunk-ZUIJ3N7N.js → chunk-V6Z4XS3I.js} +5 -3
- package/dist/chunk-V6Z4XS3I.js.map +1 -0
- package/dist/{chunk-JM64OEU6.js → chunk-WAPJ2DL3.js} +10 -13
- package/dist/chunk-WAPJ2DL3.js.map +1 -0
- package/dist/{chunk-QPLWDSYG.js → chunk-WM6Y2S6W.js} +4 -4
- package/dist/{chunk-EMMPNB6D.js → chunk-X4Z62ZQY.js} +2 -2
- package/dist/{chunk-ZAB65STX.js → chunk-XHHR6G35.js} +2 -2
- package/dist/{chunk-OP65D4UR.js → chunk-XR6WBW23.js} +2 -2
- package/dist/{chunk-WIT3Y7JT.js → chunk-Y2B3I3IH.js} +2 -2
- package/dist/{chunk-R6SNYC3D.js → chunk-Y3KRUT7O.js} +2 -2
- package/dist/{chunk-TPLKJHNN.js → chunk-ZC7RQDZ5.js} +8 -7
- package/dist/chunk-ZC7RQDZ5.js.map +1 -0
- package/dist/{chunk-TFO6GBW7.js → chunk-ZS44PY7T.js} +5 -1
- package/dist/chunk-ZS44PY7T.js.map +1 -0
- package/dist/{chunk-AJ6XEO4E.js → chunk-ZUY65MFH.js} +2 -2
- package/dist/{chunk-4X4KM4ZB.js → chunk-ZZMZF7PZ.js} +2 -2
- package/dist/data-display/CardKit.js +5 -5
- package/dist/data-display/DataViews.js +40 -40
- package/dist/feedback/ConfirmAction.js +4 -4
- package/dist/form/Button.js +3 -3
- package/dist/form/CategorySelect.js +24 -16
- package/dist/form/total-form.js +20 -20
- package/dist/native/chunks/{chunk-ZAQ3AZJM.js → chunk-2R2R35C5.js} +2 -2
- package/dist/native/chunks/{chunk-ATS7STZJ.js → chunk-WL5LXVCK.js} +1 -1
- package/dist/native/chunks/{chunk-ATS7STZJ.js.map → chunk-WL5LXVCK.js.map} +1 -1
- package/dist/native/data-display/EmptyState.js +2 -2
- package/dist/native/data-display/ErrorState.js +1 -1
- package/dist/native/data-display/FleetMap.js +2 -2
- package/dist/native/form/Button.js +1 -1
- package/dist/navigation/AppHeader.js +4 -4
- package/dist/social-login-button.js +4 -4
- package/dist/types/components/form/Button/Button.d.ts +7 -3
- package/dist/types/components/form/Button/Button.d.ts.map +1 -1
- package/dist/types/components/form/Button/Button.metrics.d.ts +25 -0
- package/dist/types/components/form/Button/Button.metrics.d.ts.map +1 -1
- package/dist/types/components/form/Button/Button.types.d.ts +37 -4
- package/dist/types/components/form/Button/Button.types.d.ts.map +1 -1
- package/dist/types/components/form/CategorySelect/CategoryPanel.d.ts.map +1 -1
- package/dist/types/components/form/CategorySelect/CategoryPanelList.d.ts +6 -0
- package/dist/types/components/form/CategorySelect/CategoryPanelList.d.ts.map +1 -1
- package/dist/types/components/form/CategorySelect/CategoryQuickActions.d.ts.map +1 -1
- package/dist/types/components/form/CategorySelect/CategoryRows.d.ts +12 -24
- package/dist/types/components/form/CategorySelect/CategoryRows.d.ts.map +1 -1
- package/dist/types/components/form/CategorySelect/CategorySelect.d.ts +10 -7
- package/dist/types/components/form/CategorySelect/CategorySelect.d.ts.map +1 -1
- package/dist/types/components/form/CategorySelect/CategorySelect.styles.d.ts +11 -0
- package/dist/types/components/form/CategorySelect/CategorySelect.styles.d.ts.map +1 -1
- package/dist/types/components/form/CategorySelect/CategorySelect.types.d.ts +10 -2
- package/dist/types/components/form/CategorySelect/CategorySelect.types.d.ts.map +1 -1
- package/dist/types/components/form/CategorySelect/category-keyboard.d.ts.map +1 -1
- package/dist/types/components/form/CategorySelect/category-tree.d.ts +52 -20
- package/dist/types/components/form/CategorySelect/category-tree.d.ts.map +1 -1
- package/dist/types/components/form/CategorySelect/index.d.ts +1 -1
- package/dist/types/components/form/CategorySelect/index.d.ts.map +1 -1
- package/dist/types/components/form/CategorySelect/useCategorySelect.d.ts +2 -0
- package/dist/types/components/form/CategorySelect/useCategorySelect.d.ts.map +1 -1
- package/dist/types-native/components/form/Button/Button.metrics.d.ts +25 -0
- package/dist/types-native/components/form/Button/Button.metrics.d.ts.map +1 -1
- package/dist/user-avatar.js +4 -4
- package/package.json +1 -1
- package/src/components/form/Button/Button.md +39 -0
- package/src/components/form/Button/Button.metrics.ts +26 -0
- package/src/components/form/Button/Button.tsx +77 -4
- package/src/components/form/Button/Button.types.ts +47 -5
- package/src/components/form/CategorySelect/CategoryPanel.tsx +1 -0
- package/src/components/form/CategorySelect/CategoryPanelList.tsx +42 -34
- package/src/components/form/CategorySelect/CategoryQuickActions.tsx +6 -4
- package/src/components/form/CategorySelect/CategoryRows.tsx +45 -81
- package/src/components/form/CategorySelect/CategorySelect.md +47 -13
- package/src/components/form/CategorySelect/CategorySelect.styles.ts +13 -0
- package/src/components/form/CategorySelect/CategorySelect.tsx +16 -14
- package/src/components/form/CategorySelect/CategorySelect.types.ts +10 -2
- package/src/components/form/CategorySelect/category-keyboard.ts +27 -31
- package/src/components/form/CategorySelect/category-tree.ts +143 -63
- package/src/components/form/CategorySelect/index.ts +4 -0
- package/src/components/form/CategorySelect/useCategorySelect.ts +15 -1
- package/dist/chunk-2GB5CIN6.js.map +0 -1
- package/dist/chunk-ITGG5OKP.js.map +0 -1
- package/dist/chunk-JM64OEU6.js.map +0 -1
- package/dist/chunk-LR6MSKTT.js +0 -124
- package/dist/chunk-LR6MSKTT.js.map +0 -1
- package/dist/chunk-OFOYMZRY.js.map +0 -1
- package/dist/chunk-OVLNF4AZ.js.map +0 -1
- package/dist/chunk-TFO6GBW7.js.map +0 -1
- package/dist/chunk-TPLKJHNN.js.map +0 -1
- package/dist/chunk-WWM2YTW4.js.map +0 -1
- package/dist/chunk-YTOYMJPM.js.map +0 -1
- package/dist/chunk-ZUIJ3N7N.js.map +0 -1
- /package/dist/{chunk-6JGPPTLX.js.map → chunk-2GI7ANEL.js.map} +0 -0
- /package/dist/{chunk-U2ZDN2AL.js.map → chunk-2PBGVMM2.js.map} +0 -0
- /package/dist/{chunk-4T7TXRGM.js.map → chunk-6ZY5XU4J.js.map} +0 -0
- /package/dist/{chunk-CHPZTF34.js.map → chunk-7KWBGMQC.js.map} +0 -0
- /package/dist/{chunk-6NSIZ2ZO.js.map → chunk-7MN6I5NK.js.map} +0 -0
- /package/dist/{chunk-PN5CQKA2.js.map → chunk-AXTLFK4T.js.map} +0 -0
- /package/dist/{chunk-JT6TERQL.js.map → chunk-BB2OUERT.js.map} +0 -0
- /package/dist/{chunk-XUJG2HFW.js.map → chunk-BH5BXLGI.js.map} +0 -0
- /package/dist/{chunk-4TXIZ5WI.js.map → chunk-CJUOBTLL.js.map} +0 -0
- /package/dist/{chunk-NWNSUHEB.js.map → chunk-CT5K725B.js.map} +0 -0
- /package/dist/{chunk-6OORHAE7.js.map → chunk-EQ7PWGBN.js.map} +0 -0
- /package/dist/{chunk-EJQW2IYW.js.map → chunk-FPFUXRD2.js.map} +0 -0
- /package/dist/{chunk-ZRPGVHTR.js.map → chunk-GGI7GM5O.js.map} +0 -0
- /package/dist/{chunk-KIHQE5YA.js.map → chunk-GVDJQMFI.js.map} +0 -0
- /package/dist/{chunk-TX5V42AY.js.map → chunk-HEBQS7QP.js.map} +0 -0
- /package/dist/{chunk-BUQ2JUND.js.map → chunk-JO6BBSGY.js.map} +0 -0
- /package/dist/{chunk-4ZV7N5CU.js.map → chunk-JXPHU3K5.js.map} +0 -0
- /package/dist/{chunk-TW3BXVEU.js.map → chunk-KMXAPYU2.js.map} +0 -0
- /package/dist/{chunk-CJBBGMIA.js.map → chunk-MNXVXW4T.js.map} +0 -0
- /package/dist/{chunk-BPLFM2GL.js.map → chunk-NYEA7JAI.js.map} +0 -0
- /package/dist/{chunk-O6AZDQJJ.js.map → chunk-OE2ZVL67.js.map} +0 -0
- /package/dist/{chunk-4523RYE7.js.map → chunk-OG2FP6SE.js.map} +0 -0
- /package/dist/{chunk-XKV5ZIBG.js.map → chunk-OGGPQVIG.js.map} +0 -0
- /package/dist/{chunk-SQNW22ZZ.js.map → chunk-OV4WREMW.js.map} +0 -0
- /package/dist/{chunk-6LGWJFQK.js.map → chunk-PFDM2OIP.js.map} +0 -0
- /package/dist/{chunk-H5VEJDEK.js.map → chunk-TVPPA4RD.js.map} +0 -0
- /package/dist/{chunk-ISH42UC7.js.map → chunk-USJPZ5Q7.js.map} +0 -0
- /package/dist/{chunk-5OIH36KX.js.map → chunk-V4FKHLEB.js.map} +0 -0
- /package/dist/{chunk-QPLWDSYG.js.map → chunk-WM6Y2S6W.js.map} +0 -0
- /package/dist/{chunk-EMMPNB6D.js.map → chunk-X4Z62ZQY.js.map} +0 -0
- /package/dist/{chunk-ZAB65STX.js.map → chunk-XHHR6G35.js.map} +0 -0
- /package/dist/{chunk-OP65D4UR.js.map → chunk-XR6WBW23.js.map} +0 -0
- /package/dist/{chunk-WIT3Y7JT.js.map → chunk-Y2B3I3IH.js.map} +0 -0
- /package/dist/{chunk-R6SNYC3D.js.map → chunk-Y3KRUT7O.js.map} +0 -0
- /package/dist/{chunk-AJ6XEO4E.js.map → chunk-ZUY65MFH.js.map} +0 -0
- /package/dist/{chunk-4X4KM4ZB.js.map → chunk-ZZMZF7PZ.js.map} +0 -0
- /package/dist/native/chunks/{chunk-ZAQ3AZJM.js.map → chunk-2R2R35C5.js.map} +0 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// src/components/form/CategorySelect/category-tree.ts
|
|
2
|
+
function foldText(text) {
|
|
3
|
+
return text.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase();
|
|
4
|
+
}
|
|
5
|
+
function onParentCycle(startId, parents) {
|
|
6
|
+
const seen = /* @__PURE__ */ new Set([startId]);
|
|
7
|
+
let current = parents.get(startId);
|
|
8
|
+
while (current !== void 0) {
|
|
9
|
+
if (seen.has(current)) return true;
|
|
10
|
+
seen.add(current);
|
|
11
|
+
current = parents.get(current);
|
|
12
|
+
}
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
function buildCategoryGroups(options) {
|
|
16
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
17
|
+
const parents = /* @__PURE__ */ new Map();
|
|
18
|
+
options.forEach((option) => {
|
|
19
|
+
if (nodes.has(option.id)) return;
|
|
20
|
+
nodes.set(option.id, { category: option, subcategories: [] });
|
|
21
|
+
if (option.parentId && option.parentId !== option.id) parents.set(option.id, option.parentId);
|
|
22
|
+
});
|
|
23
|
+
const roots = [];
|
|
24
|
+
nodes.forEach((node, id) => {
|
|
25
|
+
const parentId = parents.get(id);
|
|
26
|
+
const parent = parentId === void 0 ? void 0 : nodes.get(parentId);
|
|
27
|
+
if (parent && !onParentCycle(id, parents)) parent.subcategories.push(node);
|
|
28
|
+
else roots.push(node);
|
|
29
|
+
});
|
|
30
|
+
return roots;
|
|
31
|
+
}
|
|
32
|
+
function isLeafCategory(group) {
|
|
33
|
+
return group.subcategories.length === 0;
|
|
34
|
+
}
|
|
35
|
+
function collectLeafIds(groups) {
|
|
36
|
+
return groups.flatMap((group) => leavesOf(group));
|
|
37
|
+
}
|
|
38
|
+
function leavesOf(group) {
|
|
39
|
+
if (isLeafCategory(group)) return [group.category.id];
|
|
40
|
+
return group.subcategories.flatMap((sub) => leavesOf(sub));
|
|
41
|
+
}
|
|
42
|
+
function collectBranchIds(groups) {
|
|
43
|
+
return groups.flatMap(
|
|
44
|
+
(group) => isLeafCategory(group) ? [] : [group.category.id, ...collectBranchIds(group.subcategories)]
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
function findGroup(groups, id) {
|
|
48
|
+
for (const group of groups) {
|
|
49
|
+
if (group.category.id === id) return group;
|
|
50
|
+
const found = findGroup(group.subcategories, id);
|
|
51
|
+
if (found) return found;
|
|
52
|
+
}
|
|
53
|
+
return void 0;
|
|
54
|
+
}
|
|
55
|
+
function categoryPath(groups, id) {
|
|
56
|
+
for (const group of groups) {
|
|
57
|
+
if (group.category.id === id) return [group.category.name];
|
|
58
|
+
const below = categoryPath(group.subcategories, id);
|
|
59
|
+
if (below) return [group.category.name, ...below];
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
function treeDepth(groups) {
|
|
64
|
+
return groups.reduce(
|
|
65
|
+
(deepest, group) => Math.max(deepest, 1 + treeDepth(group.subcategories)),
|
|
66
|
+
0
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
function categoryCheckState(group, selected) {
|
|
70
|
+
const leaves = leavesOf(group);
|
|
71
|
+
const hits = leaves.filter((id) => selected.has(id)).length;
|
|
72
|
+
if (hits === 0) return "off";
|
|
73
|
+
return hits === leaves.length ? "on" : "partial";
|
|
74
|
+
}
|
|
75
|
+
function toggleCategoryLeaves(group, selected) {
|
|
76
|
+
const next = new Set(selected);
|
|
77
|
+
const leaves = leavesOf(group);
|
|
78
|
+
const turningOff = categoryCheckState(group, selected) === "on";
|
|
79
|
+
leaves.forEach((id) => turningOff ? next.delete(id) : next.add(id));
|
|
80
|
+
return next;
|
|
81
|
+
}
|
|
82
|
+
function toggleLeaf(id, selected) {
|
|
83
|
+
const next = new Set(selected);
|
|
84
|
+
if (!next.delete(id)) next.add(id);
|
|
85
|
+
return next;
|
|
86
|
+
}
|
|
87
|
+
function summarizeSelection(groups, selected) {
|
|
88
|
+
return groups.flatMap((group) => {
|
|
89
|
+
const state = categoryCheckState(group, selected);
|
|
90
|
+
if (state === "off") return [];
|
|
91
|
+
if (state === "on") {
|
|
92
|
+
return [
|
|
93
|
+
{ id: group.category.id, label: group.category.name, whole: !isLeafCategory(group) }
|
|
94
|
+
];
|
|
95
|
+
}
|
|
96
|
+
return summarizeSelection(group.subcategories, selected);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
function removeChip(groups, chipId, selected) {
|
|
100
|
+
const next = new Set(selected);
|
|
101
|
+
const group = findGroup(groups, chipId);
|
|
102
|
+
if (group) {
|
|
103
|
+
leavesOf(group).forEach((id) => next.delete(id));
|
|
104
|
+
return next;
|
|
105
|
+
}
|
|
106
|
+
next.delete(chipId);
|
|
107
|
+
return next;
|
|
108
|
+
}
|
|
109
|
+
function filterCategoryGroups(groups, query) {
|
|
110
|
+
const needle = foldText(query.trim());
|
|
111
|
+
if (!needle) return groups;
|
|
112
|
+
const walk = (nodes) => nodes.flatMap((node) => {
|
|
113
|
+
if (foldText(node.category.name).includes(needle)) return [node];
|
|
114
|
+
const subcategories = walk(node.subcategories);
|
|
115
|
+
return subcategories.length > 0 ? [{ category: node.category, subcategories }] : [];
|
|
116
|
+
});
|
|
117
|
+
return walk(groups);
|
|
118
|
+
}
|
|
119
|
+
function highlightSegments(text, query) {
|
|
120
|
+
const needle = foldText(query.trim());
|
|
121
|
+
if (!needle) return [{ text, match: false }];
|
|
122
|
+
const at = foldText(text).indexOf(needle);
|
|
123
|
+
if (at < 0) return [{ text, match: false }];
|
|
124
|
+
return [
|
|
125
|
+
{ text: text.slice(0, at), match: false },
|
|
126
|
+
{ text: text.slice(at, at + needle.length), match: true },
|
|
127
|
+
{ text: text.slice(at + needle.length), match: false }
|
|
128
|
+
].filter((segment) => segment.text.length > 0);
|
|
129
|
+
}
|
|
130
|
+
function flattenRows(groups, isExpanded) {
|
|
131
|
+
const walk = (nodes, depth, parentId) => nodes.flatMap((node) => {
|
|
132
|
+
const id = node.category.id;
|
|
133
|
+
const branch = !isLeafCategory(node);
|
|
134
|
+
const row = { id, depth, branch, ...parentId ? { parentId } : {} };
|
|
135
|
+
if (!branch || !isExpanded(id)) return [row];
|
|
136
|
+
return [row, ...walk(node.subcategories, depth + 1, id)];
|
|
137
|
+
});
|
|
138
|
+
return walk(groups, 0);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export {
|
|
142
|
+
foldText,
|
|
143
|
+
buildCategoryGroups,
|
|
144
|
+
isLeafCategory,
|
|
145
|
+
collectLeafIds,
|
|
146
|
+
leavesOf,
|
|
147
|
+
collectBranchIds,
|
|
148
|
+
findGroup,
|
|
149
|
+
categoryPath,
|
|
150
|
+
treeDepth,
|
|
151
|
+
categoryCheckState,
|
|
152
|
+
toggleCategoryLeaves,
|
|
153
|
+
toggleLeaf,
|
|
154
|
+
summarizeSelection,
|
|
155
|
+
removeChip,
|
|
156
|
+
filterCategoryGroups,
|
|
157
|
+
highlightSegments,
|
|
158
|
+
flattenRows
|
|
159
|
+
};
|
|
160
|
+
//# sourceMappingURL=chunk-PN4HVM43.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/form/CategorySelect/category-tree.ts"],"sourcesContent":["import type {\n CategoryGroup,\n CategorySelectionChip,\n CategorySelectOption,\n} from './CategorySelect.types';\n\n/**\n * Fold a string to its comparison form: no diacritics, lower case.\n *\n * Portuguese category names are full of them (\"Águas\", \"Cápsulas\", \"Grãos\"), and\n * an admin typing \"agua\" or \"graos\" on a hurried phone keyboard means the same\n * thing. Comparing folded forms on both sides makes the search accent-blind in\n * both directions.\n */\nexport function foldText(text: string): string {\n return text\n .normalize('NFD')\n .replace(/[̀-ͯ]/g, '')\n .toLowerCase();\n}\n\n/**\n * Whether following `parentId` up from `startId` ever comes back to a node it\n * already passed.\n *\n * Two levels made a cycle impossible by construction: a child was attached to a\n * ROOT or promoted, so nothing could ever point back down. An arbitrary depth\n * attaches every node to whichever node its `parentId` names, and a payload\n * where two rows name each other would build a ring that every walk below —\n * flattening, filtering, counting leaves — follows until the stack gives out. So\n * the chain is checked rather than trusted, and a node on a ring is promoted to\n * top level, which is what {@link buildCategoryGroups} already does with a\n * parent it cannot resolve.\n */\nfunction onParentCycle(startId: string, parents: ReadonlyMap<string, string>): boolean {\n const seen = new Set<string>([startId]);\n let current = parents.get(startId);\n while (current !== undefined) {\n if (seen.has(current)) return true;\n seen.add(current);\n current = parents.get(current);\n }\n return false;\n}\n\n/**\n * Nest a flat `parentId` list into a tree of ANY depth, preserving input order\n * at every level.\n *\n * An option whose `parentId` names nothing in the payload is promoted to top\n * level rather than dropped — a category is never invisible just because its\n * parent was filtered out of the response. A duplicate id keeps its first row,\n * so a repeated payload cannot place the same node twice.\n */\nexport function buildCategoryGroups(options: CategorySelectOption[]): CategoryGroup[] {\n const nodes = new Map<string, CategoryGroup>();\n const parents = new Map<string, string>();\n options.forEach((option) => {\n if (nodes.has(option.id)) return;\n nodes.set(option.id, { category: option, subcategories: [] });\n if (option.parentId && option.parentId !== option.id) parents.set(option.id, option.parentId);\n });\n\n const roots: CategoryGroup[] = [];\n nodes.forEach((node, id) => {\n const parentId = parents.get(id);\n const parent = parentId === undefined ? undefined : nodes.get(parentId);\n if (parent && !onParentCycle(id, parents)) parent.subcategories.push(node);\n else roots.push(node);\n });\n return roots;\n}\n\n/**\n * Whether this node IS the leaf, because nothing sits under it.\n *\n * The leaf-only default makes a category a heading and the subcategory the thing\n * you pick — but a childless category has no subcategory to offer instead, so\n * that reading leaves it unpickable and the row does nothing at all. It is the\n * leaf, so it is selectable in its own right, whatever `allowParentSelection`\n * says. Everything below already agreed; only the rows and the activation did\n * not, which is what made a childless category unselectable on the estoque\n * filter while `Marcar tudo` could still select it.\n */\nexport function isLeafCategory(group: CategoryGroup): boolean {\n return group.subcategories.length === 0;\n}\n\n/** Every selectable leaf id across the whole tree, in display order. */\nexport function collectLeafIds(groups: CategoryGroup[]): string[] {\n return groups.flatMap((group) => leavesOf(group));\n}\n\n/**\n * The leaves a node stands for — every leaf BELOW it, or itself when childless.\n *\n * Recursive rather than one level down, so a category three levels deep still\n * stands for the items at the bottom of it and not for the subcategories in\n * between: the value a caller receives stays uniformly leaves, which is the\n * property that lets a chip, a tri-state checkbox and `Marcar tudo` agree.\n */\nexport function leavesOf(group: CategoryGroup): string[] {\n if (isLeafCategory(group)) return [group.category.id];\n return group.subcategories.flatMap((sub) => leavesOf(sub));\n}\n\n/** Every node that can be unfolded, at any depth — what \"Expandir tudo\" opens. */\nexport function collectBranchIds(groups: CategoryGroup[]): string[] {\n return groups.flatMap((group) =>\n isLeafCategory(group) ? [] : [group.category.id, ...collectBranchIds(group.subcategories)],\n );\n}\n\n/** The node carrying `id`, searched at every depth. */\nexport function findGroup(groups: CategoryGroup[], id: string): CategoryGroup | undefined {\n for (const group of groups) {\n if (group.category.id === id) return group;\n const found = findGroup(group.subcategories, id);\n if (found) return found;\n }\n return undefined;\n}\n\n/**\n * The names from the top-level category down to `id`, or `null` when absent.\n *\n * The single-select trigger reads this back as `Pai › Filha › Item`, so a chosen\n * row keeps the context that tells `Massas` (mercearia) from `Massas` (pratos\n * principais) once the panel has closed over it.\n */\nexport function categoryPath(groups: CategoryGroup[], id: string): string[] | null {\n for (const group of groups) {\n if (group.category.id === id) return [group.category.name];\n const below = categoryPath(group.subcategories, id);\n if (below) return [group.category.name, ...below];\n }\n return null;\n}\n\n/**\n * How many levels the deepest branch has. A flat list is 1, the classic\n * category/subcategory tree is 2, anything more is a tree with items in it.\n */\nexport function treeDepth(groups: CategoryGroup[]): number {\n return groups.reduce(\n (deepest, group) => Math.max(deepest, 1 + treeDepth(group.subcategories)),\n 0,\n );\n}\n\nexport type CategoryCheckState = 'off' | 'partial' | 'on';\n\n/** Whether none, some, or all of a category's leaves are selected. */\nexport function categoryCheckState(\n group: CategoryGroup,\n selected: ReadonlySet<string>,\n): CategoryCheckState {\n const leaves = leavesOf(group);\n const hits = leaves.filter((id) => selected.has(id)).length;\n if (hits === 0) return 'off';\n return hits === leaves.length ? 'on' : 'partial';\n}\n\n/** Select every leaf of a category, or clear them all when already complete. */\nexport function toggleCategoryLeaves(\n group: CategoryGroup,\n selected: ReadonlySet<string>,\n): Set<string> {\n const next = new Set(selected);\n const leaves = leavesOf(group);\n const turningOff = categoryCheckState(group, selected) === 'on';\n leaves.forEach((id) => (turningOff ? next.delete(id) : next.add(id)));\n return next;\n}\n\n/** Add an id when absent, remove it when present. */\nexport function toggleLeaf(id: string, selected: ReadonlySet<string>): Set<string> {\n const next = new Set(selected);\n if (!next.delete(id)) next.add(id);\n return next;\n}\n\n/**\n * Collapse a raw id set into the chips a human reads: a node that is fully\n * selected becomes ONE chip bearing its name, rather than one chip per leaf —\n * \"Bebidas\" instead of six drinks.\n *\n * A partly-selected node hands the question down to its own children, so the\n * chip that appears is always the DEEPEST node that is completely selected.\n */\nexport function summarizeSelection(\n groups: CategoryGroup[],\n selected: ReadonlySet<string>,\n): CategorySelectionChip[] {\n return groups.flatMap((group): CategorySelectionChip[] => {\n const state = categoryCheckState(group, selected);\n if (state === 'off') return [];\n if (state === 'on') {\n // `whole` says the chip stands for MORE than itself, so it is the node\n // having children that decides it — not the node being fully selected,\n // which a leaf always is the moment it is picked at all.\n return [\n { id: group.category.id, label: group.category.name, whole: !isLeafCategory(group) },\n ];\n }\n return summarizeSelection(group.subcategories, selected);\n });\n}\n\n/**\n * Drop the leaves a chip stands for. A chip naming a node removes everything\n * under it; a chip naming nothing in the tree removes just that id.\n */\nexport function removeChip(\n groups: CategoryGroup[],\n chipId: string,\n selected: ReadonlySet<string>,\n): Set<string> {\n const next = new Set(selected);\n const group = findGroup(groups, chipId);\n if (group) {\n leavesOf(group).forEach((id) => next.delete(id));\n return next;\n }\n next.delete(chipId);\n return next;\n}\n\n/**\n * Search every level, keeping each hit under its parents.\n *\n * A node that matches keeps its whole subtree, so \"Bebidas\" shows the entire\n * group. A node that does not match keeps only the descendants that do — the hit\n * still arrives with its parents visible above it, which is the context that\n * tells \"Massas\" (mercearia) from \"Massas\" (pratos principais).\n */\nexport function filterCategoryGroups(groups: CategoryGroup[], query: string): CategoryGroup[] {\n const needle = foldText(query.trim());\n if (!needle) return groups;\n const walk = (nodes: CategoryGroup[]): CategoryGroup[] =>\n nodes.flatMap((node) => {\n if (foldText(node.category.name).includes(needle)) return [node];\n const subcategories = walk(node.subcategories);\n return subcategories.length > 0 ? [{ category: node.category, subcategories }] : [];\n });\n return walk(groups);\n}\n\n/** A run of text, flagged when it is the part that matched the query. */\ninterface HighlightSegment {\n text: string;\n match: boolean;\n}\n\n/**\n * Split a label around the query hit so the view can mark it.\n *\n * Slicing uses indices from the FOLDED text but cuts the ORIGINAL, which is safe\n * because folding is per-character here: NFD-stripping combining marks and\n * lower-casing both preserve offsets for the scripts these names use.\n */\nexport function highlightSegments(text: string, query: string): HighlightSegment[] {\n const needle = foldText(query.trim());\n if (!needle) return [{ text, match: false }];\n const at = foldText(text).indexOf(needle);\n if (at < 0) return [{ text, match: false }];\n return [\n { text: text.slice(0, at), match: false },\n { text: text.slice(at, at + needle.length), match: true },\n { text: text.slice(at + needle.length), match: false },\n ].filter((segment) => segment.text.length > 0);\n}\n\n/** A flattened row, so keyboard navigation can walk what is actually on screen. */\nexport interface CategoryRowRef {\n id: string;\n /** 0 for a top-level category, one more for each level below it. */\n depth: number;\n /** True when the row has children to unfold — what → and ← act on. */\n branch: boolean;\n /** Owning node id — lets ← jump from a child back to its parent. */\n parentId?: string;\n}\n\n/** The visible rows, in order, honouring which nodes are expanded. */\nexport function flattenRows(\n groups: CategoryGroup[],\n isExpanded: (categoryId: string) => boolean,\n): CategoryRowRef[] {\n const walk = (nodes: CategoryGroup[], depth: number, parentId?: string): CategoryRowRef[] =>\n nodes.flatMap((node) => {\n const id = node.category.id;\n const branch = !isLeafCategory(node);\n const row: CategoryRowRef = { id, depth, branch, ...(parentId ? { parentId } : {}) };\n if (!branch || !isExpanded(id)) return [row];\n return [row, ...walk(node.subcategories, depth + 1, id)];\n });\n return walk(groups, 0);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
} from "./chunk-
|
|
2
|
+
findGroup
|
|
3
|
+
} from "./chunk-PN4HVM43.js";
|
|
4
4
|
|
|
5
5
|
// src/components/form/CategorySelect/category-keyboard.ts
|
|
6
6
|
function activeRow(context) {
|
|
7
7
|
return context.activeIndex >= 0 ? context.rows[context.activeIndex] : void 0;
|
|
8
8
|
}
|
|
9
9
|
function groupOf(context, id) {
|
|
10
|
-
return context.groups
|
|
10
|
+
return findGroup(context.groups, id);
|
|
11
11
|
}
|
|
12
12
|
function moveCursor(context, delta) {
|
|
13
13
|
const { rows, activeIndex } = context;
|
|
@@ -17,26 +17,22 @@ function moveCursor(context, delta) {
|
|
|
17
17
|
}
|
|
18
18
|
function handleRight(context) {
|
|
19
19
|
const row = activeRow(context);
|
|
20
|
-
if (row?.
|
|
20
|
+
if (row?.branch) context.expand(row.id);
|
|
21
21
|
}
|
|
22
22
|
function handleLeft(context) {
|
|
23
23
|
const row = activeRow(context);
|
|
24
24
|
if (!row) return;
|
|
25
|
-
if (row.
|
|
25
|
+
if (row.branch) {
|
|
26
26
|
context.collapse(row.id);
|
|
27
27
|
return;
|
|
28
28
|
}
|
|
29
29
|
if (!row.parentId) return;
|
|
30
30
|
context.collapse(row.parentId);
|
|
31
|
-
const parentIndex = context.rows.findIndex(
|
|
32
|
-
(candidate) => candidate.kind === "category" && candidate.id === row.parentId
|
|
33
|
-
);
|
|
31
|
+
const parentIndex = context.rows.findIndex((candidate) => candidate.id === row.parentId);
|
|
34
32
|
if (parentIndex >= 0) context.setActiveIndex(parentIndex);
|
|
35
33
|
}
|
|
36
|
-
function
|
|
37
|
-
|
|
38
|
-
if (!group) return;
|
|
39
|
-
if (!context.allowParentSelection && !isLeafCategory(group)) {
|
|
34
|
+
function activateBranchRow(context, categoryId) {
|
|
35
|
+
if (!context.allowParentSelection) {
|
|
40
36
|
context.toggleExpanded(categoryId);
|
|
41
37
|
return;
|
|
42
38
|
}
|
|
@@ -44,17 +40,20 @@ function activateCategoryRow(context, categoryId) {
|
|
|
44
40
|
context.pick(categoryId);
|
|
45
41
|
return;
|
|
46
42
|
}
|
|
47
|
-
context
|
|
43
|
+
const group = groupOf(context, categoryId);
|
|
44
|
+
if (group) context.toggleCategory(group);
|
|
48
45
|
}
|
|
49
|
-
function
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (row.kind === "subcategory") {
|
|
53
|
-
if (context.single) context.pick(row.id);
|
|
54
|
-
else context.toggleSubcategory(row.id);
|
|
46
|
+
function activateRow(context, row) {
|
|
47
|
+
if (row.branch) {
|
|
48
|
+
activateBranchRow(context, row.id);
|
|
55
49
|
return;
|
|
56
50
|
}
|
|
57
|
-
|
|
51
|
+
if (context.single) context.pick(row.id);
|
|
52
|
+
else context.toggleSubcategory(row.id);
|
|
53
|
+
}
|
|
54
|
+
function handleSpace(context) {
|
|
55
|
+
const row = activeRow(context);
|
|
56
|
+
if (row) activateRow(context, row);
|
|
58
57
|
}
|
|
59
58
|
function handleEnter(context) {
|
|
60
59
|
const row = activeRow(context);
|
|
@@ -62,12 +61,7 @@ function handleEnter(context) {
|
|
|
62
61
|
context.commit();
|
|
63
62
|
return;
|
|
64
63
|
}
|
|
65
|
-
if (
|
|
66
|
-
if (row.kind === "subcategory") {
|
|
67
|
-
context.pick(row.id);
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
activateCategoryRow(context, row.id);
|
|
64
|
+
if (row) activateRow(context, row);
|
|
71
65
|
}
|
|
72
66
|
var HANDLERS = {
|
|
73
67
|
ArrowDown: (context) => moveCursor(context, 1),
|
|
@@ -90,4 +84,4 @@ function handleCategoryKeyDown(key, editingSearchText, context) {
|
|
|
90
84
|
export {
|
|
91
85
|
handleCategoryKeyDown
|
|
92
86
|
};
|
|
93
|
-
//# sourceMappingURL=chunk-
|
|
87
|
+
//# sourceMappingURL=chunk-R2AZOWEZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/form/CategorySelect/category-keyboard.ts"],"sourcesContent":["import { findGroup, type CategoryRowRef } from './category-tree';\nimport type { CategoryGroup } from './CategorySelect.types';\n\n/** What a key handler is allowed to do to the panel. */\nexport interface CategoryKeyContext {\n rows: CategoryRowRef[];\n activeIndex: number;\n groups: CategoryGroup[];\n /** Single-select mode picks on Enter/Space instead of toggling a draft. */\n single: boolean;\n /** Categories are headings, not checkboxes, unless parents are selectable. */\n allowParentSelection: boolean;\n setActiveIndex: (next: number) => void;\n toggleExpanded: (categoryId: string) => void;\n expand: (categoryId: string) => void;\n collapse: (categoryId: string) => void;\n toggleCategory: (group: CategoryGroup) => void;\n toggleSubcategory: (id: string) => void;\n commit: () => void;\n cancel: () => void;\n pick: (id: string) => void;\n}\n\n/** The row under the keyboard cursor, if the cursor is on one. */\nfunction activeRow(context: CategoryKeyContext): CategoryRowRef | undefined {\n return context.activeIndex >= 0 ? context.rows[context.activeIndex] : undefined;\n}\n\nfunction groupOf(context: CategoryKeyContext, id: string): CategoryGroup | undefined {\n return findGroup(context.groups, id);\n}\n\nfunction moveCursor(context: CategoryKeyContext, delta: number): void {\n const { rows, activeIndex } = context;\n if (rows.length === 0) return;\n const next =\n delta > 0\n ? Math.min(rows.length - 1, activeIndex + 1)\n : Math.max(0, activeIndex - 1);\n context.setActiveIndex(next);\n}\n\n/** → opens a row that folds. On a leaf it is a no-op: there is nothing deeper. */\nfunction handleRight(context: CategoryKeyContext): void {\n const row = activeRow(context);\n if (row?.branch) context.expand(row.id);\n}\n\n/** ← collapses a foldable row, or walks a leaf back up to its parent. */\nfunction handleLeft(context: CategoryKeyContext): void {\n const row = activeRow(context);\n if (!row) return;\n if (row.branch) {\n context.collapse(row.id);\n return;\n }\n if (!row.parentId) return;\n context.collapse(row.parentId);\n const parentIndex = context.rows.findIndex((candidate) => candidate.id === row.parentId);\n if (parentIndex >= 0) context.setActiveIndex(parentIndex);\n}\n\n/**\n * What a FOLDABLE row does when it is activated — the one answer the click, the\n * Space key and (in single-select) Enter all take, so the three cannot disagree\n * about a row the way they did over a childless category.\n *\n * A row that is only a frame expands instead of marking: it has no control, so\n * \"mark\" has nothing to mean there. A childless category is not a frame — it is\n * the leaf — so it never reaches here.\n */\nfunction activateBranchRow(context: CategoryKeyContext, categoryId: string): void {\n if (!context.allowParentSelection) {\n context.toggleExpanded(categoryId);\n return;\n }\n if (context.single) {\n context.pick(categoryId);\n return;\n }\n const group = groupOf(context, categoryId);\n if (group) context.toggleCategory(group);\n}\n\n/** Mark the row under the cursor — or fold it, when it is only a frame. */\nfunction activateRow(context: CategoryKeyContext, row: CategoryRowRef): void {\n if (row.branch) {\n activateBranchRow(context, row.id);\n return;\n }\n if (context.single) context.pick(row.id);\n else context.toggleSubcategory(row.id);\n}\n\n/** Space marks the row under the cursor. */\nfunction handleSpace(context: CategoryKeyContext): void {\n const row = activeRow(context);\n if (row) activateRow(context, row);\n}\n\nfunction handleEnter(context: CategoryKeyContext): void {\n const row = activeRow(context);\n if (!context.single) {\n context.commit();\n return;\n }\n if (row) activateRow(context, row);\n}\n\n/** Key → behaviour. A map keeps this dispatch flat instead of a long if-chain. */\nconst HANDLERS: Record<string, (context: CategoryKeyContext) => void> = {\n ArrowDown: (context) => moveCursor(context, 1),\n ArrowUp: (context) => moveCursor(context, -1),\n ArrowRight: handleRight,\n ArrowLeft: handleLeft,\n ' ': handleSpace,\n Enter: handleEnter,\n Escape: (context) => context.cancel(),\n};\n\n/**\n * Keys the SEARCH BOX has a claim on: Space types a space, and the horizontal\n * arrows move the caret.\n *\n * They are only yielded while the field actually holds text. The panel opens\n * with focus in an EMPTY search box, so yielding unconditionally — as the\n * prototype does — would make Space and ←/→ dead on arrival for anyone driving\n * the tree from the keyboard, which is most of the point of having it. With no\n * text there is no caret to move and no word to space, so the tree takes them;\n * type one character and they revert to the field.\n */\nconst TEXT_ENTRY_KEYS = new Set([' ', 'ArrowLeft', 'ArrowRight']);\n\n/**\n * Drive the tree from the keyboard.\n *\n * Returns `true` when the key was consumed, so the caller can `preventDefault`\n * only for keys this actually handled — an unhandled key must still reach the\n * search input untouched.\n */\nexport function handleCategoryKeyDown(\n key: string,\n editingSearchText: boolean,\n context: CategoryKeyContext,\n): boolean {\n if (editingSearchText && TEXT_ENTRY_KEYS.has(key)) return false;\n const handler = HANDLERS[key];\n if (!handler) return false;\n handler(context);\n return true;\n}\n"],"mappings":";;;;;AAwBA,SAAS,UAAU,SAAyD;AAC1E,SAAO,QAAQ,eAAe,IAAI,QAAQ,KAAK,QAAQ,WAAW,IAAI;AACxE;AAEA,SAAS,QAAQ,SAA6B,IAAuC;AACnF,SAAO,UAAU,QAAQ,QAAQ,EAAE;AACrC;AAEA,SAAS,WAAW,SAA6B,OAAqB;AACpE,QAAM,EAAE,MAAM,YAAY,IAAI;AAC9B,MAAI,KAAK,WAAW,EAAG;AACvB,QAAM,OACJ,QAAQ,IACJ,KAAK,IAAI,KAAK,SAAS,GAAG,cAAc,CAAC,IACzC,KAAK,IAAI,GAAG,cAAc,CAAC;AACjC,UAAQ,eAAe,IAAI;AAC7B;AAGA,SAAS,YAAY,SAAmC;AACtD,QAAM,MAAM,UAAU,OAAO;AAC7B,MAAI,KAAK,OAAQ,SAAQ,OAAO,IAAI,EAAE;AACxC;AAGA,SAAS,WAAW,SAAmC;AACrD,QAAM,MAAM,UAAU,OAAO;AAC7B,MAAI,CAAC,IAAK;AACV,MAAI,IAAI,QAAQ;AACd,YAAQ,SAAS,IAAI,EAAE;AACvB;AAAA,EACF;AACA,MAAI,CAAC,IAAI,SAAU;AACnB,UAAQ,SAAS,IAAI,QAAQ;AAC7B,QAAM,cAAc,QAAQ,KAAK,UAAU,CAAC,cAAc,UAAU,OAAO,IAAI,QAAQ;AACvF,MAAI,eAAe,EAAG,SAAQ,eAAe,WAAW;AAC1D;AAWA,SAAS,kBAAkB,SAA6B,YAA0B;AAChF,MAAI,CAAC,QAAQ,sBAAsB;AACjC,YAAQ,eAAe,UAAU;AACjC;AAAA,EACF;AACA,MAAI,QAAQ,QAAQ;AAClB,YAAQ,KAAK,UAAU;AACvB;AAAA,EACF;AACA,QAAM,QAAQ,QAAQ,SAAS,UAAU;AACzC,MAAI,MAAO,SAAQ,eAAe,KAAK;AACzC;AAGA,SAAS,YAAY,SAA6B,KAA2B;AAC3E,MAAI,IAAI,QAAQ;AACd,sBAAkB,SAAS,IAAI,EAAE;AACjC;AAAA,EACF;AACA,MAAI,QAAQ,OAAQ,SAAQ,KAAK,IAAI,EAAE;AAAA,MAClC,SAAQ,kBAAkB,IAAI,EAAE;AACvC;AAGA,SAAS,YAAY,SAAmC;AACtD,QAAM,MAAM,UAAU,OAAO;AAC7B,MAAI,IAAK,aAAY,SAAS,GAAG;AACnC;AAEA,SAAS,YAAY,SAAmC;AACtD,QAAM,MAAM,UAAU,OAAO;AAC7B,MAAI,CAAC,QAAQ,QAAQ;AACnB,YAAQ,OAAO;AACf;AAAA,EACF;AACA,MAAI,IAAK,aAAY,SAAS,GAAG;AACnC;AAGA,IAAM,WAAkE;AAAA,EACtE,WAAW,CAAC,YAAY,WAAW,SAAS,CAAC;AAAA,EAC7C,SAAS,CAAC,YAAY,WAAW,SAAS,EAAE;AAAA,EAC5C,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,KAAK;AAAA,EACL,OAAO;AAAA,EACP,QAAQ,CAAC,YAAY,QAAQ,OAAO;AACtC;AAaA,IAAM,kBAAkB,oBAAI,IAAI,CAAC,KAAK,aAAa,YAAY,CAAC;AASzD,SAAS,sBACd,KACA,mBACA,SACS;AACT,MAAI,qBAAqB,gBAAgB,IAAI,GAAG,EAAG,QAAO;AAC1D,QAAM,UAAU,SAAS,GAAG;AAC5B,MAAI,CAAC,QAAS,QAAO;AACrB,UAAQ,OAAO;AACf,SAAO;AACT;","names":[]}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildCategoryGroups,
|
|
3
|
+
collectBranchIds,
|
|
3
4
|
filterCategoryGroups,
|
|
4
5
|
flattenRows,
|
|
5
6
|
toggleCategoryLeaves,
|
|
6
|
-
toggleLeaf
|
|
7
|
-
|
|
7
|
+
toggleLeaf,
|
|
8
|
+
treeDepth
|
|
9
|
+
} from "./chunk-PN4HVM43.js";
|
|
8
10
|
|
|
9
11
|
// src/components/form/CategorySelect/useCategorySelect.ts
|
|
10
12
|
import { useCallback, useMemo, useRef, useState } from "react";
|
|
@@ -23,7 +25,7 @@ function useExpandedCategories(allGroups, searching) {
|
|
|
23
25
|
}, []);
|
|
24
26
|
const setAllExpanded = useCallback(
|
|
25
27
|
(shouldExpand) => {
|
|
26
|
-
setExpanded(shouldExpand ? new Set(allGroups
|
|
28
|
+
setExpanded(shouldExpand ? new Set(collectBranchIds(allGroups)) : /* @__PURE__ */ new Set());
|
|
27
29
|
},
|
|
28
30
|
[allGroups]
|
|
29
31
|
);
|
|
@@ -56,6 +58,7 @@ function useCategorySelect({
|
|
|
56
58
|
() => filterCategoryGroups(allGroups, query),
|
|
57
59
|
[allGroups, query]
|
|
58
60
|
);
|
|
61
|
+
const deepTree = useMemo(() => treeDepth(allGroups) > 2, [allGroups]);
|
|
59
62
|
const expansion = useExpandedCategories(allGroups, query.trim().length > 0);
|
|
60
63
|
const selection = useDraftSelection(selected);
|
|
61
64
|
const rows = useMemo(
|
|
@@ -85,6 +88,7 @@ function useCategorySelect({
|
|
|
85
88
|
query,
|
|
86
89
|
visibleGroups,
|
|
87
90
|
allGroups,
|
|
91
|
+
deepTree,
|
|
88
92
|
rows,
|
|
89
93
|
activeIndex,
|
|
90
94
|
setQuery,
|
|
@@ -98,4 +102,4 @@ function useCategorySelect({
|
|
|
98
102
|
export {
|
|
99
103
|
useCategorySelect
|
|
100
104
|
};
|
|
101
|
-
//# sourceMappingURL=chunk-
|
|
105
|
+
//# sourceMappingURL=chunk-RZVUY2MO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/form/CategorySelect/useCategorySelect.ts"],"sourcesContent":["import { useCallback, useMemo, useRef, useState } from 'react';\n\nimport {\n buildCategoryGroups,\n collectBranchIds,\n filterCategoryGroups,\n flattenRows,\n toggleCategoryLeaves,\n toggleLeaf,\n treeDepth,\n type CategoryRowRef,\n} from './category-tree';\nimport type { CategoryGroup, CategorySelectOption } from './CategorySelect.types';\n\n/** Which categories are unfolded, and the ways that set changes. */\ninterface ExpansionState {\n isExpanded: (categoryId: string) => boolean;\n toggleExpanded: (categoryId: string) => void;\n setAllExpanded: (expanded: boolean) => void;\n expandAll: (groups: CategoryGroup[]) => void;\n}\n\n/**\n * The expanded set.\n *\n * `searching` short-circuits the lookup instead of mutating the set: a query must\n * reveal every hit, but clearing it has to restore whatever the admin had folded,\n * which an overwrite would have destroyed.\n */\nfunction useExpandedCategories(\n allGroups: CategoryGroup[],\n searching: boolean,\n): ExpansionState {\n const [expanded, setExpanded] = useState<ReadonlySet<string>>(() => new Set());\n\n const isExpanded = useCallback(\n (categoryId: string) => searching || expanded.has(categoryId),\n [searching, expanded],\n );\n\n const toggleExpanded = useCallback((categoryId: string) => {\n setExpanded((current) => {\n const next = new Set(current);\n if (!next.delete(categoryId)) next.add(categoryId);\n return next;\n });\n }, []);\n\n // Every foldable node at EVERY depth, so \"Expandir tudo\" opens the tree rather\n // than only its first storey.\n const setAllExpanded = useCallback(\n (shouldExpand: boolean) => {\n setExpanded(shouldExpand ? new Set(collectBranchIds(allGroups)) : new Set());\n },\n [allGroups],\n );\n\n // Opening unfolds the TOP level only. On a two-level tree that is the whole\n // thing on show, which is what the panel has always done; on a deeper one it\n // is the categories and their subcategories, with the items behind one more\n // click — the alternative being the flat list the tree replaces. A search\n // still reveals every hit: `searching` short-circuits the lookup above.\n const expandAll = useCallback((groups: CategoryGroup[]) => {\n setExpanded(new Set(groups.map((group) => group.category.id)));\n }, []);\n\n return { isExpanded, toggleExpanded, setAllExpanded, expandAll };\n}\n\n/** The pending selection and the two ways a row changes it. */\ninterface DraftState {\n draft: ReadonlySet<string>;\n setDraft: (next: ReadonlySet<string>) => void;\n toggleCategory: (group: CategoryGroup) => void;\n toggleSubcategory: (id: string) => void;\n}\n\nfunction useDraftSelection(initial: readonly string[]): DraftState {\n const [draft, setDraftState] = useState<ReadonlySet<string>>(() => new Set(initial));\n\n const toggleCategory = useCallback((group: CategoryGroup) => {\n setDraftState((current) => toggleCategoryLeaves(group, current));\n }, []);\n\n const toggleSubcategory = useCallback((id: string) => {\n setDraftState((current) => toggleLeaf(id, current));\n }, []);\n\n const setDraft = useCallback((next: ReadonlySet<string>) => setDraftState(next), []);\n\n return { draft, setDraft, toggleCategory, toggleSubcategory };\n}\n\n/** Everything the panel and trigger need to render and drive one select. */\nexport interface CategorySelectState extends ExpansionState, DraftState {\n open: boolean;\n query: string;\n /** Groups after the search filter — what is actually on screen. */\n visibleGroups: CategoryGroup[];\n /** All groups, unfiltered — the basis for chips and \"select all\". */\n allGroups: CategoryGroup[];\n /** True when the tree runs deeper than category/subcategory. */\n deepTree: boolean;\n rows: CategoryRowRef[];\n activeIndex: number;\n setQuery: (next: string) => void;\n setActiveIndex: (next: number) => void;\n openPanel: () => void;\n closePanel: () => void;\n triggerRef: React.RefObject<HTMLButtonElement | null>;\n}\n\ninterface UseCategorySelectArgs {\n options: CategorySelectOption[];\n /** The applied selection to seed the draft from each time the panel opens. */\n selected: readonly string[];\n}\n\n/**\n * Panel state for one CategorySelect.\n *\n * The DRAFT is the load-bearing idea. Ticks land here, not on the caller's\n * `value`, so the list behind the panel does not refetch on every click; Apply\n * publishes, dismissing discards. Opening re-seeds the draft from the applied\n * value, which is what makes \"cancel\" mean cancel.\n */\nexport function useCategorySelect({\n options,\n selected,\n}: UseCategorySelectArgs): CategorySelectState {\n const [open, setOpen] = useState(false);\n const [query, setQueryState] = useState('');\n const [activeIndex, setActiveIndex] = useState(-1);\n const triggerRef = useRef<HTMLButtonElement | null>(null);\n\n const allGroups = useMemo(() => buildCategoryGroups(options), [options]);\n const visibleGroups = useMemo(\n () => filterCategoryGroups(allGroups, query),\n [allGroups, query],\n );\n\n const deepTree = useMemo(() => treeDepth(allGroups) > 2, [allGroups]);\n\n const expansion = useExpandedCategories(allGroups, query.trim().length > 0);\n const selection = useDraftSelection(selected);\n const rows = useMemo(\n () => flattenRows(visibleGroups, expansion.isExpanded),\n [visibleGroups, expansion.isExpanded],\n );\n\n const openPanel = useCallback(() => {\n selection.setDraft(new Set(selected));\n expansion.expandAll(allGroups);\n setQueryState('');\n setActiveIndex(-1);\n setOpen(true);\n }, [selected, allGroups, selection, expansion]);\n\n const closePanel = useCallback(() => {\n setOpen(false);\n setQueryState('');\n setActiveIndex(-1);\n }, []);\n\n const setQuery = useCallback((next: string) => {\n setQueryState(next);\n setActiveIndex(-1);\n }, []);\n\n return {\n ...expansion,\n ...selection,\n open,\n query,\n visibleGroups,\n allGroups,\n deepTree,\n rows,\n activeIndex,\n setQuery,\n setActiveIndex,\n openPanel,\n closePanel,\n triggerRef,\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -4,7 +4,10 @@ import {
|
|
|
4
4
|
buttonVariantStyles,
|
|
5
5
|
getColorFromTheme,
|
|
6
6
|
iconAlignmentStyles
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-BB2OUERT.js";
|
|
8
|
+
import {
|
|
9
|
+
BUTTON_ICON_GLYPH_SIZE
|
|
10
|
+
} from "./chunk-5LQ7PKMW.js";
|
|
8
11
|
import {
|
|
9
12
|
resolveButtonProps
|
|
10
13
|
} from "./chunk-TAMIZE4Q.js";
|
|
@@ -42,10 +45,15 @@ var muiVariantFor = (variant) => {
|
|
|
42
45
|
if (variant === "outline") return "outlined";
|
|
43
46
|
return variant === "text" ? "text" : "contained";
|
|
44
47
|
};
|
|
48
|
+
var muiSizeFor = (size) => {
|
|
49
|
+
if (size === "xs" || size === "sm") return "small";
|
|
50
|
+
return size === "md" ? "medium" : "large";
|
|
51
|
+
};
|
|
45
52
|
var muiColorFor = (color) => {
|
|
46
53
|
if (color === "danger") return "error";
|
|
47
54
|
return color === "neutral" ? "inherit" : color;
|
|
48
55
|
};
|
|
56
|
+
var ICON_GLYPH_STYLE = { fontSize: BUTTON_ICON_GLYPH_SIZE };
|
|
49
57
|
function isIconOnly({
|
|
50
58
|
loading,
|
|
51
59
|
icon,
|
|
@@ -53,7 +61,7 @@ function isIconOnly({
|
|
|
53
61
|
}) {
|
|
54
62
|
return !loading && icon != null && children == null;
|
|
55
63
|
}
|
|
56
|
-
var
|
|
64
|
+
var ButtonRoot = React.forwardRef(
|
|
57
65
|
(rawProps, ref) => {
|
|
58
66
|
const {
|
|
59
67
|
variant,
|
|
@@ -76,7 +84,7 @@ var Button = React.forwardRef(
|
|
|
76
84
|
const ownTestId = resolveTestId(others, "button");
|
|
77
85
|
const testId = (suffix) => childTestId(others, suffix, "button");
|
|
78
86
|
const props = withoutTestIdProps(others);
|
|
79
|
-
const iconWithTestId = !loading && icon ? /* @__PURE__ */ jsx("span", { "data-testid": testId("icon"), children: icon }) : void 0;
|
|
87
|
+
const iconWithTestId = !loading && icon ? /* @__PURE__ */ jsx("span", { "data-testid": testId("icon"), style: ICON_GLYPH_STYLE, children: icon }) : void 0;
|
|
80
88
|
const iconOnly = isIconOnly({ loading, icon, children });
|
|
81
89
|
const mergedClassName = [active ? "active" : "", props.className].filter(Boolean).join(" ") || void 0;
|
|
82
90
|
return /* @__PURE__ */ jsx(
|
|
@@ -85,6 +93,7 @@ var Button = React.forwardRef(
|
|
|
85
93
|
ref,
|
|
86
94
|
variant: muiVariantFor(variant),
|
|
87
95
|
color: muiColorFor(color),
|
|
96
|
+
size: muiSizeFor(size),
|
|
88
97
|
customVariant: variant,
|
|
89
98
|
customColor: color,
|
|
90
99
|
glow,
|
|
@@ -106,9 +115,10 @@ var Button = React.forwardRef(
|
|
|
106
115
|
);
|
|
107
116
|
}
|
|
108
117
|
);
|
|
109
|
-
|
|
118
|
+
ButtonRoot.displayName = "Button";
|
|
119
|
+
var Button = ButtonRoot;
|
|
110
120
|
|
|
111
121
|
export {
|
|
112
122
|
Button
|
|
113
123
|
};
|
|
114
|
-
//# sourceMappingURL=chunk-
|
|
124
|
+
//# sourceMappingURL=chunk-SLVQ33AN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/form/Button/Button.tsx"],"sourcesContent":["import MuiButton from '@mui/material/Button/index.js';\nimport CircularProgress from '@mui/material/CircularProgress/index.js';\nimport { styled } from '@mui/material/styles/index.js';\nimport * as React from 'react';\n\nimport { resolveButtonProps } from './Button.helpers';\nimport { BUTTON_ICON_GLYPH_SIZE } from './Button.metrics';\nimport { childTestId, resolveTestId, withoutTestIdProps } from '../../../platform/test-id';\nimport {\n buttonEmphasisStyles,\n buttonVariantStyles,\n getColorFromTheme,\n iconAlignmentStyles,\n buttonSize,\n} from './Button.styles';\nimport type { ButtonComponent, ButtonProps } from './Button.types';\n\nconst StyledButton = styled(MuiButton, {\n shouldForwardProp: (prop) =>\n ['glow', 'pulse', 'loading', 'customVariant', 'customColor', 'ripple'].indexOf(\n prop as string,\n ) === -1,\n})<{\n customVariant?: string;\n customColor?: string;\n glow?: boolean;\n pulse?: boolean;\n ripple?: boolean;\n}>(({ theme, customVariant, customColor = 'primary', glow, pulse }) => {\n const colorPalette = getColorFromTheme(theme, customColor);\n\n return {\n textTransform: 'none',\n fontWeight: 500,\n borderRadius: theme.spacing(1),\n transition: 'all 0.3s ease',\n position: 'relative',\n overflow: 'hidden',\n ...iconAlignmentStyles(theme),\n ...buttonVariantStyles(theme, customVariant, colorPalette, customColor),\n ...buttonEmphasisStyles(colorPalette, glow, pulse),\n };\n});\n\n/** Our six variants collapse onto MUI's three; only outline and text differ. */\nconst muiVariantFor = (variant?: string): 'outlined' | 'text' | 'contained' => {\n if (variant === 'outline') return 'outlined';\n return variant === 'text' ? 'text' : 'contained';\n};\n\ntype MuiButtonColor = 'inherit' | 'primary' | 'secondary' | 'success' | 'warning' | 'info' | 'error';\n\n/**\n * Our five sizes collapse onto MUI's three, so the rendered class NAMES the\n * size the button actually is (12-74).\n *\n * It was never forwarded, so every button — `xs` through `xl` — carried\n * `MuiButton-sizeMedium`. The pixels were right (they come from `buttonSize`'s\n * `sx`, which still decides them) but the DOM said \"medium\" about a large\n * button: a test could never assert the size, and devtools lied to whoever\n * opened them.\n *\n * MUI's own size styles are padding, font-size and the icon glyph. The first\n * two are overridden by that same `sx`; the third is pinned by\n * `BUTTON_ICON_GLYPH_SIZE`, so forwarding this moves nothing on screen.\n */\nconst muiSizeFor = (size: NonNullable<ButtonProps['size']>): 'small' | 'medium' | 'large' => {\n if (size === 'xs' || size === 'sm') return 'small';\n return size === 'md' ? 'medium' : 'large';\n};\n\n/** `danger` and `neutral` are ours; the rest are MUI's own names already. */\nconst muiColorFor = (color: NonNullable<ButtonProps['color']>): MuiButtonColor => {\n if (color === 'danger') return 'error';\n return color === 'neutral' ? 'inherit' : color;\n};\n\n/**\n * The icon slot's font-size, pinned on the wrapper this component already owns\n * (12-74).\n *\n * MUI writes 18/20/22 there off its own `size`, and that prop is now forwarded,\n * so a glyph that takes its size by INHERITANCE would start moving with the\n * button — measured at 18px on `xs` and 22px on `lg` against the 20px every\n * size has always drawn. See {@link BUTTON_ICON_GLYPH_SIZE} for what that does\n * and does not reach.\n *\n * Written as an inline style rather than a selector in `sx` because MUI's own\n * rule sits on the start/end-icon span itself. Beating it from the root means\n * winning a specificity contest at a distance — true in a browser, but not\n * something a test can hold, since jsdom resolves `getComputedStyle` by\n * document order and never by specificity. Inline wins both, and it sits on the\n * one element we already render for this glyph.\n *\n * A number, not `px()`: React writes `font-size: 20px`, which is exactly the\n * declaration MUI was emitting before.\n */\nconst ICON_GLYPH_STYLE = { fontSize: BUTTON_ICON_GLYPH_SIZE } as const;\n\n/**\n * An icon and nothing else — the shape that should render square.\n *\n * A separate predicate rather than three clauses inline: the component function\n * is already at the complexity ceiling, and \"what counts as an icon button\" is\n * a rule worth naming anyway. `loading` disqualifies, because a spinner\n * replaces the children and would otherwise make every loading button square.\n */\nfunction isIconOnly({\n loading,\n icon,\n children,\n}: Pick<ButtonProps, 'loading' | 'icon' | 'children'>): boolean {\n return !loading && icon != null && children == null;\n}\n\n/**\n * The design system's button.\n *\n * Typed as an `OverridableComponent` (12-70) rather than a plain `forwardRef`,\n * which is how MUI models a component that can render as something else. The\n * practical difference is one line at a call site:\n *\n * ```tsx\n * <Button component={Link} to=\"/account/compras/42\">Acompanhar pedido</Button>\n * ```\n *\n * `to` is typed here because the generic carries the target component's own\n * props through. A plain `component?: React.ElementType` — what this was —\n * accepts the component and throws its props away, so `to` typechecked\n * nowhere even though it arrived intact at runtime. A host wanting a router\n * navigation painted as a button had to choose between a type assertion and\n * losing the `href`; both were shipped, and both are the reason this ticket\n * exists.\n */\nconst ButtonRoot = React.forwardRef<HTMLButtonElement, ButtonProps>(\n (rawProps, ref) => {\n const {\n variant,\n color,\n size,\n loading,\n icon,\n iconPosition,\n glow,\n pulse,\n ripple,\n active,\n children,\n disabled,\n onClick,\n onFocus,\n onBlur,\n ...others\n } = resolveButtonProps(rawProps);\n // Every spelling of the test id the shared contract allows, mapped to the\n // one the DOM reads; the native Button does the same in reverse.\n const ownTestId = resolveTestId(others, 'button');\n const testId = (suffix: string) => childTestId(others, suffix, 'button');\n const props = withoutTestIdProps(others);\n\n // Wrap icon with testId if provided\n const iconWithTestId =\n !loading && icon ? (\n <span data-testid={testId('icon')} style={ICON_GLYPH_STYLE}>\n {icon}\n </span>\n ) : undefined;\n\n // Only an icon, so the button is a square rather than a 64px slab.\n const iconOnly = isIconOnly({ loading, icon, children });\n\n const mergedClassName =\n [active ? 'active' : '', props.className].filter(Boolean).join(' ') || undefined;\n\n return (\n <StyledButton\n ref={ref}\n variant={muiVariantFor(variant)}\n color={muiColorFor(color)}\n size={muiSizeFor(size)}\n customVariant={variant}\n customColor={color}\n glow={glow}\n pulse={pulse}\n ripple={ripple}\n disabled={disabled || loading}\n disableRipple={!ripple}\n startIcon={iconPosition === 'left' ? iconWithTestId : undefined}\n endIcon={iconPosition === 'right' ? iconWithTestId : undefined}\n onClick={onClick}\n onFocus={onFocus}\n onBlur={onBlur}\n sx={buttonSize(size, iconOnly)}\n data-testid={ownTestId}\n {...props}\n className={mergedClassName}\n >\n {loading ? (\n <CircularProgress size={16} color=\"inherit\" data-testid={testId('loading')} />\n ) : (\n children\n )}\n </StyledButton>\n );\n },\n);\n\nButtonRoot.displayName = 'Button';\n\n/**\n * The cast is what publishes the generic — `forwardRef` cannot express\n * \"renders as C, and takes C's props\" on its own, so MUI's own components do\n * exactly this. `ButtonRoot` above is the implementation and is not exported.\n */\nexport const Button = ButtonRoot as ButtonComponent;\n"],"mappings":"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","names":[]}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
GridMain
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-NYEA7JAI.js";
|
|
4
4
|
import {
|
|
5
5
|
FilterDialog,
|
|
6
6
|
GridFilterPanel
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-AXTLFK4T.js";
|
|
8
8
|
import {
|
|
9
9
|
DataViewsEmpty
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-V4FKHLEB.js";
|
|
11
11
|
import {
|
|
12
12
|
InlineFilterControls
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-7MN6I5NK.js";
|
|
14
14
|
import {
|
|
15
15
|
GridHeaderRow
|
|
16
16
|
} from "./chunk-JSH3HFDQ.js";
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
} from "./chunk-TQ6FPOPD.js";
|
|
24
24
|
import {
|
|
25
25
|
ShellToolbar
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-X4Z62ZQY.js";
|
|
27
27
|
import {
|
|
28
28
|
resolveSelectionExtra
|
|
29
29
|
} from "./chunk-KX75HM5U.js";
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
} from "./chunk-ZPIMPOHV.js";
|
|
37
37
|
import {
|
|
38
38
|
togglePillValues
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-2PBGVMM2.js";
|
|
40
40
|
import {
|
|
41
41
|
useDataViewsCopy
|
|
42
42
|
} from "./chunk-KHG57NAE.js";
|
|
@@ -237,4 +237,4 @@ function GridShell(props) {
|
|
|
237
237
|
export {
|
|
238
238
|
GridShell
|
|
239
239
|
};
|
|
240
|
-
//# sourceMappingURL=chunk-
|
|
240
|
+
//# sourceMappingURL=chunk-TVPPA4RD.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
handleCategoryKeyDown
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-R2AZOWEZ.js";
|
|
4
4
|
|
|
5
5
|
// src/components/form/CategorySelect/useCategoryKeyHandler.ts
|
|
6
6
|
import { useCallback } from "react";
|
|
@@ -52,4 +52,4 @@ function useCategoryKeyHandler({
|
|
|
52
52
|
export {
|
|
53
53
|
useCategoryKeyHandler
|
|
54
54
|
};
|
|
55
|
-
//# sourceMappingURL=chunk-
|
|
55
|
+
//# sourceMappingURL=chunk-USJPZ5Q7.js.map
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from "./chunk-6DPAWXZ3.js";
|
|
7
7
|
import {
|
|
8
8
|
Button
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-SLVQ33AN.js";
|
|
10
10
|
import {
|
|
11
11
|
Text
|
|
12
12
|
} from "./chunk-343TTIHD.js";
|
|
@@ -77,4 +77,4 @@ function DataViewsEmpty({
|
|
|
77
77
|
export {
|
|
78
78
|
DataViewsEmpty
|
|
79
79
|
};
|
|
80
|
-
//# sourceMappingURL=chunk-
|
|
80
|
+
//# sourceMappingURL=chunk-V4FKHLEB.js.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
+
collectBranchIds,
|
|
2
3
|
collectLeafIds
|
|
3
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-PN4HVM43.js";
|
|
4
5
|
|
|
5
6
|
// src/components/form/CategorySelect/CategoryQuickActions.tsx
|
|
6
7
|
import Box from "@mui/material/Box/index.js";
|
|
@@ -25,7 +26,8 @@ function CategoryQuickActions({
|
|
|
25
26
|
}) {
|
|
26
27
|
const allLeafIds = useMemo(() => collectLeafIds(state.allGroups), [state.allGroups]);
|
|
27
28
|
const everythingPicked = allLeafIds.length > 0 && state.draft.size >= allLeafIds.length;
|
|
28
|
-
const
|
|
29
|
+
const branchIds = useMemo(() => collectBranchIds(state.allGroups), [state.allGroups]);
|
|
30
|
+
const allExpanded = branchIds.length > 0 && branchIds.every((id) => state.isExpanded(id));
|
|
29
31
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
30
32
|
/* @__PURE__ */ jsx(
|
|
31
33
|
Box,
|
|
@@ -57,4 +59,4 @@ function CategoryQuickActions({
|
|
|
57
59
|
export {
|
|
58
60
|
CategoryQuickActions
|
|
59
61
|
};
|
|
60
|
-
//# sourceMappingURL=chunk-
|
|
62
|
+
//# sourceMappingURL=chunk-V6Z4XS3I.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/form/CategorySelect/CategoryQuickActions.tsx"],"sourcesContent":["'use client';\n\nimport Box from '@mui/material/Box/index.js';\nimport { useMemo } from 'react';\n\nimport type { CategorySelectCopy } from '../../../copy';\n\nimport { collectBranchIds, collectLeafIds } from './category-tree';\nimport type { CategorySelectState } from './useCategorySelect';\n\n/** The panel's text-button treatment. */\nconst linkButtonSx = {\n background: 'none',\n border: 0,\n padding: 0,\n font: 'inherit',\n fontSize: 12,\n color: 'primary.main',\n cursor: 'pointer',\n fontWeight: 550,\n '&:hover': { textDecoration: 'underline' },\n '&:disabled': { color: 'text.disabled', cursor: 'default', textDecoration: 'none' },\n} as const;\n\n/**\n * Bulk actions for multi-select: mark everything, and fold everything.\n *\n * Both labels flip to their inverse once the action is spent (\"Desmarcar tudo\",\n * \"Recolher tudo\"), so the control always says what pressing it will do rather\n * than what state the list is in.\n */\nexport function CategoryQuickActions({\n state,\n dataTestId,\n copy,\n}: {\n state: CategorySelectState;\n dataTestId: string;\n /** The words this control renders. REQUIRED — no default copy. */\n copy: CategorySelectCopy;\n}): React.JSX.Element {\n const allLeafIds = useMemo(() => collectLeafIds(state.allGroups), [state.allGroups]);\n const everythingPicked = allLeafIds.length > 0 && state.draft.size >= allLeafIds.length;\n // Measured over every foldable node at every depth: on a deep tree the top\n // storey can be open while the items under it are not, and a label reading\n // \"Recolher tudo\" there would be describing a state the list is not in.\n const branchIds = useMemo(() => collectBranchIds(state.allGroups), [state.allGroups]);\n const allExpanded = branchIds.length > 0 && branchIds.every((id) => state.isExpanded(id));\n\n return (\n <>\n <Box\n component=\"button\"\n type=\"button\"\n disabled={allLeafIds.length === 0}\n data-testid={`${dataTestId}-select-all`}\n sx={linkButtonSx}\n onClick={() => state.setDraft(everythingPicked ? new Set() : new Set(allLeafIds))}\n >\n {everythingPicked ? copy.deselectAll : copy.selectAll}\n </Box>\n <Box component=\"span\" sx={{ color: 'divider' }}>\n ·\n </Box>\n <Box\n component=\"button\"\n type=\"button\"\n data-testid={`${dataTestId}-expand-all`}\n sx={linkButtonSx}\n onClick={() => state.setAllExpanded(!allExpanded)}\n >\n {allExpanded ? copy.collapseAll : copy.expandAll}\n </Box>\n </>\n );\n}\n"],"mappings":";;;;;;AAEA,OAAO,SAAS;AAChB,SAAS,eAAe;AA+CpB,mBACE,KADF;AAvCJ,IAAM,eAAe;AAAA,EACnB,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,MAAM;AAAA,EACN,UAAU;AAAA,EACV,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,WAAW,EAAE,gBAAgB,YAAY;AAAA,EACzC,cAAc,EAAE,OAAO,iBAAiB,QAAQ,WAAW,gBAAgB,OAAO;AACpF;AASO,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AACF,GAKsB;AACpB,QAAM,aAAa,QAAQ,MAAM,eAAe,MAAM,SAAS,GAAG,CAAC,MAAM,SAAS,CAAC;AACnF,QAAM,mBAAmB,WAAW,SAAS,KAAK,MAAM,MAAM,QAAQ,WAAW;AAIjF,QAAM,YAAY,QAAQ,MAAM,iBAAiB,MAAM,SAAS,GAAG,CAAC,MAAM,SAAS,CAAC;AACpF,QAAM,cAAc,UAAU,SAAS,KAAK,UAAU,MAAM,CAAC,OAAO,MAAM,WAAW,EAAE,CAAC;AAExF,SACE,iCACE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,MAAK;AAAA,QACL,UAAU,WAAW,WAAW;AAAA,QAChC,eAAa,GAAG,UAAU;AAAA,QAC1B,IAAI;AAAA,QACJ,SAAS,MAAM,MAAM,SAAS,mBAAmB,oBAAI,IAAI,IAAI,IAAI,IAAI,UAAU,CAAC;AAAA,QAE/E,6BAAmB,KAAK,cAAc,KAAK;AAAA;AAAA,IAC9C;AAAA,IACA,oBAAC,OAAI,WAAU,QAAO,IAAI,EAAE,OAAO,UAAU,GAAG,kBAEhD;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,MAAK;AAAA,QACL,eAAa,GAAG,UAAU;AAAA,QAC1B,IAAI;AAAA,QACJ,SAAS,MAAM,MAAM,eAAe,CAAC,WAAW;AAAA,QAE/C,wBAAc,KAAK,cAAc,KAAK;AAAA;AAAA,IACzC;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -3,25 +3,26 @@ import {
|
|
|
3
3
|
} from "./chunk-LMXWI3DD.js";
|
|
4
4
|
import {
|
|
5
5
|
CategoryPanel
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-ZC7RQDZ5.js";
|
|
7
7
|
import {
|
|
8
8
|
useCategoryCommit
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-XHHR6G35.js";
|
|
10
10
|
import {
|
|
11
11
|
CategoryTrigger
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-GVDJQMFI.js";
|
|
13
13
|
import {
|
|
14
14
|
METRICS
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-ZS44PY7T.js";
|
|
16
16
|
import {
|
|
17
17
|
useCategorySelect
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-RZVUY2MO.js";
|
|
19
19
|
import {
|
|
20
20
|
useCategoryKeyHandler
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-USJPZ5Q7.js";
|
|
22
22
|
import {
|
|
23
|
+
categoryPath,
|
|
23
24
|
summarizeSelection
|
|
24
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-PN4HVM43.js";
|
|
25
26
|
import {
|
|
26
27
|
FormControl,
|
|
27
28
|
FormLabel,
|
|
@@ -42,11 +43,7 @@ function draftChanged(draft, applied) {
|
|
|
42
43
|
}
|
|
43
44
|
function singleLabel(groups, value) {
|
|
44
45
|
if (!value) return void 0;
|
|
45
|
-
|
|
46
|
-
if (asCategory) return asCategory.category.name;
|
|
47
|
-
const owner = groups.find((group) => group.subcategories.some((sub2) => sub2.id === value));
|
|
48
|
-
const sub = owner?.subcategories.find((candidate) => candidate.id === value);
|
|
49
|
-
return sub && owner ? `${owner.category.name} \u203A ${sub.name}` : void 0;
|
|
46
|
+
return categoryPath(groups, value)?.join(" \u203A ");
|
|
50
47
|
}
|
|
51
48
|
function resolveConfig(props) {
|
|
52
49
|
const single = props.mode === "single";
|
|
@@ -158,4 +155,4 @@ function CategorySelect(props) {
|
|
|
158
155
|
export {
|
|
159
156
|
CategorySelect
|
|
160
157
|
};
|
|
161
|
-
//# sourceMappingURL=chunk-
|
|
158
|
+
//# sourceMappingURL=chunk-WAPJ2DL3.js.map
|