@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
package/dist/chunk-LR6MSKTT.js
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
// src/components/form/CategorySelect/category-tree.ts
|
|
2
|
-
function foldText(text) {
|
|
3
|
-
return text.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase();
|
|
4
|
-
}
|
|
5
|
-
function buildCategoryGroups(options) {
|
|
6
|
-
const roots = options.filter((option) => !option.parentId);
|
|
7
|
-
const rootIds = new Set(roots.map((root) => root.id));
|
|
8
|
-
const groups = new Map(
|
|
9
|
-
roots.map((root) => [root.id, { category: root, subcategories: [] }])
|
|
10
|
-
);
|
|
11
|
-
const orphans = [];
|
|
12
|
-
options.forEach((option) => {
|
|
13
|
-
if (!option.parentId) return;
|
|
14
|
-
const parent = groups.get(option.parentId);
|
|
15
|
-
if (parent) {
|
|
16
|
-
parent.subcategories.push(option);
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
if (!rootIds.has(option.id)) orphans.push({ category: option, subcategories: [] });
|
|
20
|
-
});
|
|
21
|
-
return [...groups.values(), ...orphans];
|
|
22
|
-
}
|
|
23
|
-
function isLeafCategory(group) {
|
|
24
|
-
return group.subcategories.length === 0;
|
|
25
|
-
}
|
|
26
|
-
function collectLeafIds(groups) {
|
|
27
|
-
return groups.flatMap((group) => leavesOf(group));
|
|
28
|
-
}
|
|
29
|
-
function leavesOf(group) {
|
|
30
|
-
return isLeafCategory(group) ? [group.category.id] : group.subcategories.map((sub) => sub.id);
|
|
31
|
-
}
|
|
32
|
-
function categoryCheckState(group, selected) {
|
|
33
|
-
const leaves = leavesOf(group);
|
|
34
|
-
const hits = leaves.filter((id) => selected.has(id)).length;
|
|
35
|
-
if (hits === 0) return "off";
|
|
36
|
-
return hits === leaves.length ? "on" : "partial";
|
|
37
|
-
}
|
|
38
|
-
function toggleCategoryLeaves(group, selected) {
|
|
39
|
-
const next = new Set(selected);
|
|
40
|
-
const leaves = leavesOf(group);
|
|
41
|
-
const turningOff = categoryCheckState(group, selected) === "on";
|
|
42
|
-
leaves.forEach((id) => turningOff ? next.delete(id) : next.add(id));
|
|
43
|
-
return next;
|
|
44
|
-
}
|
|
45
|
-
function toggleLeaf(id, selected) {
|
|
46
|
-
const next = new Set(selected);
|
|
47
|
-
if (!next.delete(id)) next.add(id);
|
|
48
|
-
return next;
|
|
49
|
-
}
|
|
50
|
-
function summarizeSelection(groups, selected) {
|
|
51
|
-
return groups.flatMap((group) => {
|
|
52
|
-
const state = categoryCheckState(group, selected);
|
|
53
|
-
if (state === "off") return [];
|
|
54
|
-
if (state === "on") {
|
|
55
|
-
return [{ id: group.category.id, label: group.category.name, whole: true }];
|
|
56
|
-
}
|
|
57
|
-
return group.subcategories.filter((sub) => selected.has(sub.id)).map((sub) => ({ id: sub.id, label: sub.name, whole: false }));
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
function removeChip(groups, chipId, selected) {
|
|
61
|
-
const next = new Set(selected);
|
|
62
|
-
const group = groups.find((candidate) => candidate.category.id === chipId);
|
|
63
|
-
if (group) {
|
|
64
|
-
leavesOf(group).forEach((id) => next.delete(id));
|
|
65
|
-
return next;
|
|
66
|
-
}
|
|
67
|
-
next.delete(chipId);
|
|
68
|
-
return next;
|
|
69
|
-
}
|
|
70
|
-
function filterCategoryGroups(groups, query) {
|
|
71
|
-
const needle = foldText(query.trim());
|
|
72
|
-
if (!needle) return groups;
|
|
73
|
-
return groups.flatMap((group) => {
|
|
74
|
-
if (foldText(group.category.name).includes(needle)) return [group];
|
|
75
|
-
const subcategories = group.subcategories.filter(
|
|
76
|
-
(sub) => foldText(sub.name).includes(needle)
|
|
77
|
-
);
|
|
78
|
-
return subcategories.length > 0 ? [{ category: group.category, subcategories }] : [];
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
function highlightSegments(text, query) {
|
|
82
|
-
const needle = foldText(query.trim());
|
|
83
|
-
if (!needle) return [{ text, match: false }];
|
|
84
|
-
const at = foldText(text).indexOf(needle);
|
|
85
|
-
if (at < 0) return [{ text, match: false }];
|
|
86
|
-
return [
|
|
87
|
-
{ text: text.slice(0, at), match: false },
|
|
88
|
-
{ text: text.slice(at, at + needle.length), match: true },
|
|
89
|
-
{ text: text.slice(at + needle.length), match: false }
|
|
90
|
-
].filter((segment) => segment.text.length > 0);
|
|
91
|
-
}
|
|
92
|
-
function flattenRows(groups, isExpanded) {
|
|
93
|
-
return groups.flatMap((group) => {
|
|
94
|
-
const head = { kind: "category", id: group.category.id };
|
|
95
|
-
if (!isExpanded(group.category.id)) return [head];
|
|
96
|
-
return [
|
|
97
|
-
head,
|
|
98
|
-
...group.subcategories.map(
|
|
99
|
-
(sub) => ({
|
|
100
|
-
kind: "subcategory",
|
|
101
|
-
id: sub.id,
|
|
102
|
-
parentId: group.category.id
|
|
103
|
-
})
|
|
104
|
-
)
|
|
105
|
-
];
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export {
|
|
110
|
-
foldText,
|
|
111
|
-
buildCategoryGroups,
|
|
112
|
-
isLeafCategory,
|
|
113
|
-
collectLeafIds,
|
|
114
|
-
leavesOf,
|
|
115
|
-
categoryCheckState,
|
|
116
|
-
toggleCategoryLeaves,
|
|
117
|
-
toggleLeaf,
|
|
118
|
-
summarizeSelection,
|
|
119
|
-
removeChip,
|
|
120
|
-
filterCategoryGroups,
|
|
121
|
-
highlightSegments,
|
|
122
|
-
flattenRows
|
|
123
|
-
};
|
|
124
|
-
//# sourceMappingURL=chunk-LR6MSKTT.js.map
|
|
@@ -1 +0,0 @@
|
|
|
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(/[\\u0300-\\u036f]/g, '')\n .toLowerCase();\n}\n\n/**\n * Group a flat `parentId` list into top level + children, preserving input order.\n *\n * An option whose `parentId` names no top-level row is promoted to top level\n * rather than dropped — a category is never invisible just because its parent\n * was filtered out of the payload, or because the tree is deeper than two levels.\n */\nexport function buildCategoryGroups(options: CategorySelectOption[]): CategoryGroup[] {\n const roots = options.filter((option) => !option.parentId);\n const rootIds = new Set(roots.map((root) => root.id));\n const groups = new Map<string, CategoryGroup>(\n roots.map((root) => [root.id, { category: root, subcategories: [] }]),\n );\n const orphans: CategoryGroup[] = [];\n\n options.forEach((option) => {\n if (!option.parentId) return;\n const parent = groups.get(option.parentId);\n if (parent) {\n parent.subcategories.push(option);\n return;\n }\n if (!rootIds.has(option.id)) orphans.push({ category: option, subcategories: [] });\n });\n\n return [...groups.values(), ...orphans];\n}\n\n/**\n * Whether this category 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 all groups, in display order. */\nexport function collectLeafIds(groups: CategoryGroup[]): string[] {\n return groups.flatMap((group) => leavesOf(group));\n}\n\n/** The leaves a category stands for — its subcategories, or itself when childless. */\nexport function leavesOf(group: CategoryGroup): string[] {\n return isLeafCategory(group) ? [group.category.id] : group.subcategories.map((sub) => sub.id);\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 category that is fully\n * selected becomes ONE chip bearing the category's name, rather than one chip\n * per subcategory — \"Bebidas\" instead of six drinks.\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 return [{ id: group.category.id, label: group.category.name, whole: true }];\n }\n return group.subcategories\n .filter((sub) => selected.has(sub.id))\n .map((sub) => ({ id: sub.id, label: sub.name, whole: false }));\n });\n}\n\n/**\n * Drop the leaves a chip stands for. A `whole` chip removes the whole category;\n * any other chip removes just that leaf.\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 = groups.find((candidate) => candidate.category.id === 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 across BOTH levels, keeping every hit under its parent.\n *\n * A category that matches keeps all of its subcategories, so \"Bebidas\" shows the\n * whole group. A category that does not match keeps only the subcategories that\n * do — the hit still arrives with its parent visible above it, which is the\n * context that 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 return groups.flatMap((group) => {\n if (foldText(group.category.name).includes(needle)) return [group];\n const subcategories = group.subcategories.filter((sub) =>\n foldText(sub.name).includes(needle),\n );\n return subcategories.length > 0 ? [{ category: group.category, subcategories }] : [];\n });\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 kind: 'category' | 'subcategory';\n id: string;\n /** Owning category id — lets ← jump from a subcategory back to its parent. */\n parentId?: string;\n}\n\n/** The visible rows, in order, honouring which categories are expanded. */\nexport function flattenRows(\n groups: CategoryGroup[],\n isExpanded: (categoryId: string) => boolean,\n): CategoryRowRef[] {\n return groups.flatMap((group) => {\n const head: CategoryRowRef = { kind: 'category', id: group.category.id };\n if (!isExpanded(group.category.id)) return [head];\n return [\n head,\n ...group.subcategories.map(\n (sub): CategoryRowRef => ({\n kind: 'subcategory',\n id: sub.id,\n parentId: group.category.id,\n }),\n ),\n ];\n });\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/form/CategorySelect/CategoryRows.tsx"],"sourcesContent":["'use client';\n\nimport Box from '@mui/material/Box/index.js';\n\nimport type { CategorySelectCopy } from '../../../copy';\nimport { CheckGlyph, DisclosureGlyph } from './CategoryIcons';\nimport { highlightSegments, type CategoryCheckState } from './category-tree';\nimport {\n checkboxBarSx,\n checkboxSx,\n chevronButtonSx,\n markSx,\n METRICS,\n radioSx,\n rowMetaSx,\n rowNameSx,\n rowSx,\n sectionHeadSx,\n} from './CategorySelect.styles';\nimport type { CategorySelectOption } from './CategorySelect.types';\n\n/** A label with the search hit marked. */\nexport function HighlightedName({\n text,\n query,\n}: {\n text: string;\n query: string;\n}): React.JSX.Element {\n const segments = highlightSegments(text, query);\n return (\n <>\n {segments.map((segment, index) =>\n segment.match ? (\n <Box component=\"mark\" key={`${segment.text}-${index}`} sx={markSx}>\n {segment.text}\n </Box>\n ) : (\n <span key={`${segment.text}-${index}`}>{segment.text}</span>\n ),\n )}\n </>\n );\n}\n\n/** Tri-state checkbox. `partial` draws the bar the prototype uses for \"some\". */\nexport function CategoryCheckbox({ state }: { state: CategoryCheckState }): React.JSX.Element {\n return (\n <Box sx={(theme) => checkboxSx(theme, state)} aria-hidden=\"true\">\n {state === 'on' && <CheckGlyph />}\n {state === 'partial' && <Box sx={checkboxBarSx} />}\n </Box>\n );\n}\n\nexport function CategoryRadio({ on }: { on: boolean }): React.JSX.Element {\n return <Box sx={(theme) => radioSx(theme, on)} aria-hidden=\"true\" />;\n}\n\ninterface CategoryHeadRowProps {\n option: CategorySelectOption;\n query: string;\n expanded: boolean;\n active: boolean;\n sheet: boolean;\n /** Absent in leaf-only mode, where the category is a heading with no checkbox. */\n checkState?: CategoryCheckState;\n /** Single-select draws a radio for the control: choosing a row is exclusive. */\n single: boolean;\n /** False for a childless category — there is nothing under it to disclose. */\n expandable: boolean;\n selectedCount: number;\n showCounts: boolean;\n onToggleExpanded: () => void;\n onActivate: () => void;\n dataTestId: string;\n copy: CategorySelectCopy;\n}\n\n/** The trailing number: how many children are picked, else the item count. */\nfunction rowMeta(\n selectedCount: number,\n showCounts: boolean,\n count: number | undefined,\n): { text: string; selected: boolean } | null {\n if (selectedCount > 0) return { text: String(selectedCount), selected: true };\n if (showCounts && count !== undefined) return { text: String(count), selected: false };\n return null;\n}\n\n/** The disclosure chevron, or the space it would have taken on a childless row. */\nfunction HeadRowDisclosure({\n option,\n expanded,\n expandable,\n onToggleExpanded,\n dataTestId,\n copy,\n}: Pick<\n CategoryHeadRowProps,\n 'option' | 'expanded' | 'expandable' | 'onToggleExpanded' | 'dataTestId' | 'copy'\n>): React.JSX.Element {\n if (!expandable) {\n return <Box sx={{ width: METRICS.chevronButton, flex: '0 0 auto' }} />;\n }\n return (\n <Box\n component=\"button\"\n type=\"button\"\n tabIndex={-1}\n aria-label={expanded ? copy.collapseCategory(option.name) : copy.expandCategory(option.name)}\n data-testid={`${dataTestId}-expand-${option.id}`}\n sx={(theme) => chevronButtonSx(theme, expanded)}\n onClick={(event: React.MouseEvent) => {\n event.stopPropagation();\n onToggleExpanded();\n }}\n >\n <DisclosureGlyph />\n </Box>\n );\n}\n\n/** The row's selection control, or the space it would have taken on a heading. */\nfunction HeadRowControl({\n checkState,\n single,\n}: Pick<CategoryHeadRowProps, 'checkState' | 'single'>): React.JSX.Element {\n if (!checkState) return <Box sx={{ width: METRICS.boxSize, flex: '0 0 auto' }} />;\n // Single-select commits the moment a row is chosen, so the control is a radio\n // there: a checkbox would promise the accumulation this mode does not do.\n if (single) return <CategoryRadio on={checkState === 'on'} />;\n return <CategoryCheckbox state={checkState} />;\n}\n\n/**\n * A top-level category row: disclosure chevron, optional checkbox, name, meta.\n *\n * The chevron is its OWN button inside the row button — clicking it only folds,\n * while clicking the row does the row's job (expand as a heading, or tick when\n * the category is selectable). A CHILDLESS category has neither a fold nor a\n * heading to be: it draws no chevron, and it carries the control, because it is\n * itself the leaf.\n */\nexport function CategoryHeadRow({\n option,\n query,\n expanded,\n active,\n sheet,\n checkState,\n single,\n expandable,\n selectedCount,\n showCounts,\n onToggleExpanded,\n onActivate,\n dataTestId,\n copy,\n}: CategoryHeadRowProps): React.JSX.Element {\n const meta = rowMeta(selectedCount, showCounts, option.count);\n return (\n <Box\n component=\"div\"\n role={checkState ? 'option' : 'button'}\n aria-selected={checkState ? checkState === 'on' : undefined}\n aria-expanded={expandable ? expanded : undefined}\n data-testid={`${dataTestId}-category-${option.id}`}\n sx={(theme) => rowSx(theme, active, sheet)}\n onClick={onActivate}\n >\n <HeadRowDisclosure\n option={option}\n expanded={expanded}\n expandable={expandable}\n onToggleExpanded={onToggleExpanded}\n dataTestId={dataTestId}\n copy={copy}\n />\n <HeadRowControl checkState={checkState} single={single} />\n <Box component=\"span\" sx={(theme) => rowNameSx(theme, true)}>\n <HighlightedName text={option.name} query={query} />\n </Box>\n {meta && (\n <Box component=\"span\" sx={(theme) => rowMetaSx(theme, meta.selected)}>\n {meta.text}\n </Box>\n )}\n </Box>\n );\n}\n\ninterface SubcategoryRowProps {\n option: CategorySelectOption;\n query: string;\n selected: boolean;\n active: boolean;\n sheet: boolean;\n /** Single-select mode draws a radio; multi-select draws a checkbox. */\n single: boolean;\n showCounts: boolean;\n onActivate: () => void;\n dataTestId: string;\n}\n\nexport function SubcategoryRow({\n option,\n query,\n selected,\n active,\n sheet,\n single,\n showCounts,\n onActivate,\n dataTestId,\n}: SubcategoryRowProps): React.JSX.Element {\n return (\n <Box\n component=\"div\"\n role=\"option\"\n aria-selected={selected}\n data-testid={`${dataTestId}-option-${option.id}`}\n sx={(theme) => ({ ...rowSx(theme, active, sheet), paddingLeft: '14px' })}\n onClick={onActivate}\n >\n <Box sx={{ width: METRICS.chevronButton, flex: '0 0 auto' }} />\n {single ? (\n <CategoryRadio on={selected} />\n ) : (\n <CategoryCheckbox state={selected ? 'on' : 'off'} />\n )}\n <Box component=\"span\" sx={(theme) => rowNameSx(theme, false)}>\n <HighlightedName text={option.name} query={query} />\n </Box>\n {showCounts && option.count !== undefined && (\n <Box component=\"span\" sx={(theme) => rowMetaSx(theme, false)}>\n {option.count}\n </Box>\n )}\n </Box>\n );\n}\n\n/** The category as a non-interactive heading (single-select, leaf-only). */\nexport function CategorySectionHeading({\n option,\n query,\n}: {\n option: CategorySelectOption;\n query: string;\n}): React.JSX.Element {\n return (\n <Box sx={sectionHeadSx}>\n <HighlightedName text={option.name} query={query} />\n </Box>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/form/CategorySelect/CategoryPanelList.tsx"],"sourcesContent":["'use client';\n\nimport type { CategorySelectCopy } from \"../../../copy\";\nimport Box from '@mui/material/Box/index.js';\nimport Button from '@mui/material/Button/index.js';\nimport Skeleton from '@mui/material/Skeleton/index.js';\n\nimport { categoryCheckState, isLeafCategory } from './category-tree';\nimport {\n CategoryHeadRow,\n CategorySectionHeading,\n SubcategoryRow,\n} from './CategoryRows';\nimport { emptySx, listSx, skeletonSx } from './CategorySelect.styles';\nimport type { CategoryGroup } from './CategorySelect.types';\n\n/** The design sets these labels in sentence case, not MUI's default caps. */\nconst SENTENCE_CASE = { textTransform: 'none' } as const;\n\n/** How many skeleton rows stand in for the catalogue while it loads. */\nconst SKELETON_ROWS = 6;\n\ninterface PanelListProps {\n /** The words this panel renders. REQUIRED — no default copy. */\n copy: CategorySelectCopy;\n groups: CategoryGroup[];\n query: string;\n draft: ReadonlySet<string>;\n rowIds: string[];\n activeIndex: number;\n sheet: boolean;\n single: boolean;\n loading: boolean;\n showCounts: boolean;\n allowParentSelection: boolean;\n isExpanded: (categoryId: string) => boolean;\n onToggleExpanded: (categoryId: string) => void;\n onActivateCategory: (group: CategoryGroup) => void;\n onActivateSubcategory: (id: string) => void;\n onClearQuery: () => void;\n onCreateCategory?: (name?: string) => void;\n listRef: React.RefObject<HTMLDivElement | null>;\n dataTestId: string;\n}\n\nfunction LoadingRows(): React.JSX.Element {\n return (\n <>\n {Array.from({ length: SKELETON_ROWS }, (_unused, index) => (\n <Skeleton key={index} variant=\"rounded\" sx={skeletonSx} />\n ))}\n </>\n );\n}\n\n/** No categories exist at all — an onboarding moment, not an error. */\nfunction EmptyCatalogue({\n onCreateCategory,\n dataTestId,\n copy,\n}: {\n onCreateCategory?: (name?: string) => void;\n dataTestId: string;\n copy: CategorySelectCopy;\n}): React.JSX.Element {\n return (\n <Box sx={emptySx} data-testid={`${dataTestId}-empty-catalogue`}>\n <strong>{copy.emptyTitle}</strong>\n <p>{copy.purpose}</p>\n {onCreateCategory && (\n <Button variant=\"contained\" size=\"small\" sx={SENTENCE_CASE} onClick={() => onCreateCategory()}>\n {copy.createCategory}\n </Button>\n )}\n </Box>\n );\n}\n\n/** The search found nothing — offer creation, or the way back. */\nfunction NoResults({\n query,\n onClearQuery,\n onCreateCategory,\n dataTestId,\n copy,\n}: {\n query: string;\n onClearQuery: () => void;\n onCreateCategory?: (name?: string) => void;\n dataTestId: string;\n copy: CategorySelectCopy;\n}): React.JSX.Element {\n return (\n <Box sx={emptySx} data-testid={`${dataTestId}-no-results`}>\n <strong>{copy.noResults.title(query)}</strong>\n <p>{copy.noResults.hint}</p>\n <Box sx={{ display: 'flex', gap: '6px', justifyContent: 'center' }}>\n <Button variant=\"outlined\" size=\"small\" sx={SENTENCE_CASE} onClick={onClearQuery}>\n {copy.noResults.clearSearch}\n </Button>\n {onCreateCategory && (\n <Button\n variant=\"contained\"\n size=\"small\"\n sx={SENTENCE_CASE}\n data-testid={`${dataTestId}-create`}\n onClick={() => onCreateCategory(query.trim())}\n >\n {copy.noResults.create(query.trim())}\n </Button>\n )}\n </Box>\n </Box>\n );\n}\n\n/** One group: its heading or head row, then whichever children are visible. */\nfunction CategoryGroupRows({\n group,\n props,\n}: {\n group: CategoryGroup;\n props: PanelListProps;\n}): React.JSX.Element {\n const { draft, query, single, allowParentSelection, rowIds, activeIndex, sheet } = props;\n const categoryId = group.category.id;\n const expanded = props.isExpanded(categoryId);\n // A childless category is the leaf, so it is selectable whatever the mode says\n // — the heading reading has no subcategory to point at, and would leave the\n // row inert.\n const selectable = allowParentSelection || isLeafCategory(group);\n const asHeading = single && !selectable;\n const selectedCount = group.subcategories.filter((sub) => draft.has(sub.id)).length;\n\n return (\n <>\n {asHeading ? (\n <CategorySectionHeading option={group.category} query={query} />\n ) : (\n <CategoryHeadRow\n option={group.category}\n query={query}\n expanded={expanded}\n active={rowIds[activeIndex] === categoryId}\n sheet={sheet}\n checkState={selectable ? categoryCheckState(group, draft) : undefined}\n single={single}\n expandable={!isLeafCategory(group)}\n selectedCount={selectedCount}\n showCounts={props.showCounts}\n onToggleExpanded={() => props.onToggleExpanded(categoryId)}\n onActivate={() => props.onActivateCategory(group)}\n dataTestId={props.dataTestId}\n copy={props.copy}\n />\n )}\n {(expanded || asHeading) &&\n group.subcategories.map((sub) => (\n <SubcategoryRow\n key={sub.id}\n option={sub}\n query={query}\n selected={draft.has(sub.id)}\n active={rowIds[activeIndex] === sub.id}\n sheet={sheet}\n single={single}\n showCounts={props.showCounts}\n onActivate={() => props.onActivateSubcategory(sub.id)}\n dataTestId={props.dataTestId}\n />\n ))}\n </>\n );\n}\n\n/** The scrolling body of the panel, including its three non-list states. */\nexport function CategoryPanelList(props: PanelListProps): React.JSX.Element {\n const { groups, loading, query, listRef, sheet, dataTestId, copy } = props;\n\n const body = (): React.JSX.Element => {\n if (loading) return <LoadingRows />;\n if (groups.length === 0 && query.trim().length > 0) {\n return (\n <NoResults copy={copy}\n query={query}\n onClearQuery={props.onClearQuery}\n onCreateCategory={props.onCreateCategory}\n dataTestId={dataTestId}\n />\n );\n }\n if (groups.length === 0) {\n return (\n <EmptyCatalogue copy={copy}\n onCreateCategory={props.onCreateCategory}\n dataTestId={dataTestId}\n />\n );\n }\n return (\n <>\n {groups.map((group) => (\n <CategoryGroupRows key={group.category.id} group={group} props={props} />\n ))}\n </>\n );\n };\n\n return (\n <Box\n ref={listRef}\n role=\"listbox\"\n aria-multiselectable={props.single ? undefined : true}\n tabIndex={-1}\n data-testid={`${dataTestId}-list`}\n sx={listSx(sheet)}\n >\n {body()}\n </Box>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/form/CategorySelect/CategorySelect.styles.ts"],"sourcesContent":["import { alpha, type CSSObject, type Theme } from '@mui/material/styles/index.js';\n\nimport { fieldEdge } from '../../../tokens/field-edge';\n\n/**\n * The prototype's METRICS, verbatim.\n *\n * Every number here is lifted from the approved design and is intentionally\n * absolute rather than derived from `theme.spacing`: the design is specified on\n * a half-pixel type scale (13.5px rows, 1.5px checkbox borders) that an 8px\n * spacing grid cannot express without rounding the look away.\n *\n * COLOURS are the deliberate exception — they come from the theme, because a\n * tenant can white-label the palette (a hard-coded indigo would survive the\n * rebrand and the rest of the screen would not) and because the library ships a\n * dark mode. On the default light indigo theme the tokens below resolve to the\n * prototype's own values.\n */\nexport const METRICS = {\n triggerHeight: 38,\n triggerRadius: 10,\n panelWidth: 340,\n panelRadius: 14,\n listMaxHeight: 290,\n sheetListMaxHeight: 340,\n rowHeight: 36,\n sheetRowHeight: 42,\n rowRadius: 8,\n boxSize: 18,\n chevronButton: 22,\n footerButton: 32,\n sheetFooterButton: 38,\n /** Below this width the panel becomes a bottom sheet. */\n sheetBreakpoint: 480,\n} as const;\n\n/** Accent family, derived so a white-labelled palette carries through. */\nconst accent = (theme: Theme) => ({\n main: theme.palette.primary.main,\n ink: theme.palette.primary.dark,\n soft: alpha(theme.palette.primary.main, 0.08),\n edge: alpha(theme.palette.primary.main, 0.28),\n ring: alpha(theme.palette.primary.main, 0.15),\n});\n\n/** The closed control. `selected` swaps it to the accent-tinted \"has filter\" skin. */\nexport const triggerSx = (theme: Theme, selected: boolean, open: boolean): CSSObject => {\n const brand = accent(theme);\n return {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n height: METRICS.triggerHeight,\n padding: '0 10px 0 12px',\n borderRadius: `${METRICS.triggerRadius}px`,\n border: `1px solid ${open || selected ? brand.edge : fieldEdge(theme)}`,\n background: selected ? brand.soft : theme.palette.background.paper,\n color: selected ? brand.ink : theme.palette.text.primary,\n font: 'inherit',\n fontSize: 13,\n cursor: 'pointer',\n maxWidth: '100%',\n transition: 'border-color .12s, box-shadow .12s',\n boxShadow: open ? `0 0 0 3px ${brand.ring}` : 'none',\n '&:hover': { borderColor: open || selected ? brand.edge : theme.palette.text.disabled },\n '&:focus-visible': {\n outline: 'none',\n borderColor: brand.main,\n boxShadow: `0 0 0 3px ${alpha(brand.main, 0.18)}`,\n },\n '&:disabled': { opacity: 0.5, cursor: 'default' },\n };\n};\n\n/** The count pill inside the trigger. */\nexport const triggerCountSx = (theme: Theme): CSSObject => ({\n fontWeight: 600,\n fontSize: 11,\n lineHeight: 1,\n background: theme.palette.primary.main,\n color: theme.palette.primary.contrastText,\n borderRadius: '999px',\n padding: '3px 6px',\n minWidth: 18,\n textAlign: 'center',\n});\n\n/** The trigger's inline clear (×) and the chevron. */\nexport const triggerClearSx: CSSObject = {\n width: 20,\n height: 20,\n flex: '0 0 auto',\n border: 0,\n background: 'transparent',\n borderRadius: '5px',\n display: 'grid',\n placeItems: 'center',\n cursor: 'pointer',\n color: 'inherit',\n opacity: 0.6,\n padding: 0,\n '&:hover': { background: alpha('#000', 0.08), opacity: 1 },\n};\n\nexport const triggerChevronSx = (open: boolean): CSSObject => ({\n width: 14,\n height: 14,\n flex: '0 0 auto',\n opacity: 0.55,\n transition: 'transform .15s',\n transform: open ? 'rotate(180deg)' : 'none',\n});\n\n/** The floating panel. */\nexport const panelSx = (theme: Theme): CSSObject => ({\n width: METRICS.panelWidth,\n maxWidth: '100%',\n background: theme.palette.background.paper,\n border: `1px solid ${theme.palette.divider}`,\n borderRadius: `${METRICS.panelRadius}px`,\n boxShadow: '0 1px 2px rgba(16,20,35,.05), 0 18px 40px -12px rgba(16,20,35,.28)',\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n});\n\n/** Bottom-sheet skin, applied under {@link METRICS.sheetBreakpoint}. */\nexport const sheetSx = (theme: Theme): CSSObject => ({\n width: '100%',\n maxWidth: 'none',\n borderRadius: '18px 18px 0 0',\n borderBottom: 0,\n position: 'relative',\n paddingTop: '4px',\n '&::before': {\n content: '\"\"',\n position: 'absolute',\n top: 7,\n left: '50%',\n transform: 'translateX(-50%)',\n width: 36,\n height: 4,\n borderRadius: '2px',\n background: theme.palette.divider,\n },\n});\n\nexport const panelHeadSx = (theme: Theme, sheet: boolean): CSSObject => ({\n padding: sheet ? '18px 10px 8px' : '10px 10px 8px',\n borderBottom: `1px solid ${theme.palette.divider}`,\n});\n\nexport const pinnedSx = (theme: Theme): CSSObject => ({\n padding: '9px 10px',\n borderBottom: `1px solid ${theme.palette.divider}`,\n background: alpha(theme.palette.primary.main, 0.03),\n});\n\nexport const pinnedLabelSx = (theme: Theme): CSSObject => ({\n fontWeight: 600,\n fontSize: 10,\n lineHeight: 1,\n letterSpacing: '.08em',\n textTransform: 'uppercase',\n color: theme.palette.text.secondary,\n marginBottom: '7px',\n});\n\nexport const chipWrapSx: CSSObject = {\n display: 'flex',\n flexWrap: 'wrap',\n gap: '5px',\n maxHeight: 74,\n overflow: 'auto',\n};\n\n/** A selected-category chip, in the pinned tray and the applied bar alike. */\nexport const chipSx = (theme: Theme): CSSObject => {\n const brand = accent(theme);\n return {\n display: 'inline-flex',\n alignItems: 'center',\n gap: '5px',\n fontSize: 12,\n background: brand.soft,\n color: brand.ink,\n border: `1px solid ${brand.edge}`,\n borderRadius: '7px',\n padding: '3px 4px 3px 8px',\n '& button': {\n border: 0,\n background: 'transparent',\n color: 'inherit',\n cursor: 'pointer',\n padding: 0,\n width: 16,\n height: 16,\n borderRadius: '4px',\n display: 'grid',\n placeItems: 'center',\n opacity: 0.65,\n fontSize: 13,\n lineHeight: 1,\n '&:hover': { background: alpha(theme.palette.primary.main, 0.16), opacity: 1 },\n },\n };\n};\n\nexport const listSx = (sheet: boolean): CSSObject => ({\n overflow: 'auto',\n maxHeight: sheet ? METRICS.sheetListMaxHeight : METRICS.listMaxHeight,\n padding: '6px 6px 8px',\n scrollPadding: '8px 0',\n overscrollBehavior: 'contain',\n});\n\n/** A row. `active` is the KEYBOARD cursor — deliberately distinct from hover. */\nexport const rowSx = (theme: Theme, active: boolean, sheet: boolean): CSSObject => ({\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n padding: '0 8px',\n height: sheet ? METRICS.sheetRowHeight : METRICS.rowHeight,\n borderRadius: `${METRICS.rowRadius}px`,\n cursor: 'pointer',\n userSelect: 'none',\n position: 'relative',\n width: '100%',\n border: 0,\n textAlign: 'left',\n font: 'inherit',\n background: active ? alpha(theme.palette.primary.main, 0.08) : 'transparent',\n boxShadow: active ? `inset 0 0 0 1px ${alpha(theme.palette.primary.main, 0.3)}` : 'none',\n '&:hover': {\n background: active\n ? alpha(theme.palette.primary.main, 0.08)\n : theme.palette.action.hover,\n },\n '&:focus-visible': { outline: `2px solid ${theme.palette.primary.main}`, outlineOffset: 1 },\n});\n\nexport const rowNameSx = (theme: Theme, isCategory: boolean): CSSObject => ({\n flex: 1,\n minWidth: 0,\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n fontSize: 13.5,\n fontWeight: isCategory ? 600 : 450,\n color: isCategory ? theme.palette.text.primary : theme.palette.text.secondary,\n});\n\nexport const rowMetaSx = (theme: Theme, selected: boolean): CSSObject => ({\n font: '500 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace',\n color: selected ? theme.palette.primary.main : theme.palette.text.secondary,\n fontWeight: selected ? 600 : 500,\n flex: '0 0 auto',\n});\n\n/** The query hit inside a row label. */\nexport const markSx: CSSObject = {\n background: '#fff2a8',\n color: 'inherit',\n borderRadius: '2px',\n padding: '0 1px',\n};\n\nexport const chevronButtonSx = (theme: Theme, open: boolean): CSSObject => ({\n width: METRICS.chevronButton,\n height: METRICS.chevronButton,\n flex: '0 0 auto',\n border: 0,\n background: 'transparent',\n borderRadius: '6px',\n display: 'grid',\n placeItems: 'center',\n cursor: 'pointer',\n color: theme.palette.text.secondary,\n padding: 0,\n '&:hover': { background: theme.palette.action.hover, color: theme.palette.text.primary },\n '& svg': { width: 12, height: 12, transition: 'transform .15s', transform: open ? 'rotate(90deg)' : 'none' },\n});\n\n/** The tri-state checkbox: unchecked, checked, or the partial bar. */\nexport const checkboxSx = (theme: Theme, state: 'off' | 'partial' | 'on'): CSSObject => ({\n width: METRICS.boxSize,\n height: METRICS.boxSize,\n flex: '0 0 auto',\n border: `1.5px solid ${state === 'off' ? theme.palette.divider : theme.palette.primary.main}`,\n borderRadius: '5px',\n background: state === 'off' ? theme.palette.background.paper : theme.palette.primary.main,\n display: 'grid',\n placeItems: 'center',\n transition: 'background .1s, border-color .1s',\n '& svg': { width: 12, height: 12, color: theme.palette.primary.contrastText },\n});\n\n/** The partial bar drawn inside a half-selected checkbox. */\nexport const checkboxBarSx = (theme: Theme): CSSObject => ({\n width: 9,\n height: 2,\n background: theme.palette.primary.contrastText,\n borderRadius: '1px',\n});\n\nexport const radioSx = (theme: Theme, on: boolean): CSSObject => ({\n width: METRICS.boxSize,\n height: METRICS.boxSize,\n flex: '0 0 auto',\n borderRadius: '50%',\n background: theme.palette.background.paper,\n border: on\n ? `5.5px solid ${theme.palette.primary.main}`\n : `1.5px solid ${theme.palette.divider}`,\n boxSizing: 'border-box',\n});\n\nexport const sectionHeadSx = (theme: Theme): CSSObject => ({\n fontWeight: 600,\n fontSize: 10,\n lineHeight: 1,\n letterSpacing: '.08em',\n textTransform: 'uppercase',\n color: theme.palette.text.secondary,\n padding: '9px 8px 6px',\n});\n\nexport const footerSx = (theme: Theme): CSSObject => ({\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: '8px',\n padding: '9px 10px',\n borderTop: `1px solid ${theme.palette.divider}`,\n background: theme.palette.action.hover,\n});\n\nexport const emptySx = (theme: Theme): CSSObject => ({\n padding: '26px 18px',\n textAlign: 'center',\n color: theme.palette.text.secondary,\n '& strong': {\n display: 'block',\n color: theme.palette.text.primary,\n fontSize: 13.5,\n marginBottom: '4px',\n },\n '& p': { margin: '0 0 12px', fontSize: 12.5, lineHeight: 1.5 },\n});\n\n/** Skeleton row used while the catalogue loads. */\nexport const skeletonSx: CSSObject = {\n height: METRICS.rowHeight,\n margin: '4px 6px',\n borderRadius: `${METRICS.rowRadius}px`,\n};\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/form/CategorySelect/CategoryPanel.tsx"],"sourcesContent":["'use client';\n\nimport type { CategorySelectCopy } from \"../../../copy\";\nimport Box from '@mui/material/Box/index.js';\n\nimport { removeChip } from './category-tree';\nimport { CategoryMultiFoot, CategorySingleFoot } from './CategoryPanelFoot';\nimport { CategoryPanelHead, CategoryPinnedTray } from './CategoryPanelHead';\nimport { CategoryPanelList } from './CategoryPanelList';\nimport { CategoryQuickActions } from './CategoryQuickActions';\nimport { panelSx, sheetSx } from './CategorySelect.styles';\nimport type { CategoryGroup, CategorySelectionChip } from './CategorySelect.types';\nimport type { CategorySelectState } from './useCategorySelect';\n\nexport interface CategoryPanelProps {\n /** The words this panel renders. REQUIRED — no default copy. */\n copy: CategorySelectCopy;\n state: CategorySelectState;\n chips: CategorySelectionChip[];\n rowIds: string[];\n /** True when the applied selection differs from the draft. */\n changed: boolean;\n single: boolean;\n sheet: boolean;\n loading: boolean;\n showCounts: boolean;\n allowParentSelection: boolean;\n ariaLabel: string;\n onKeyDown: (event: React.KeyboardEvent) => void;\n onActivateCategory: (group: CategoryGroup) => void;\n onActivateSubcategory: (id: string) => void;\n onApply: () => void;\n onCancel: () => void;\n onCreateCategory?: () => void;\n searchInputRef: React.RefObject<HTMLInputElement | null>;\n listRef: React.RefObject<HTMLDivElement | null>;\n dataTestId: string;\n}\n\n/** Search + pinned selection + tree + footer, in the panel's own chrome. */\nexport function CategoryPanel({\n state,\n chips,\n rowIds,\n changed,\n single,\n sheet,\n loading,\n showCounts,\n allowParentSelection,\n ariaLabel,\n onKeyDown,\n onActivateCategory,\n onActivateSubcategory,\n onApply,\n onCancel,\n onCreateCategory,\n searchInputRef,\n listRef,\n dataTestId,\n copy,\n}: CategoryPanelProps): React.JSX.Element {\n return (\n <Box\n onKeyDown={onKeyDown}\n role=\"dialog\"\n aria-label={ariaLabel}\n data-testid={`${dataTestId}-panel`}\n sx={(theme) => ({ ...panelSx(theme), ...(sheet ? sheetSx(theme) : {}) })}\n >\n <CategoryPanelHead\n copy={copy}\n query={state.query}\n placeholder={single ? copy.search.placeholderSingle : copy.search.placeholderMulti}\n sheet={sheet}\n quickActions={\n single ? undefined : <CategoryQuickActions state={state} dataTestId={dataTestId} copy={copy} />\n }\n onQueryChange={state.setQuery}\n searchInputRef={searchInputRef}\n dataTestId={dataTestId}\n />\n {!single && !loading && (\n <PinnedTray state={state} chips={chips} dataTestId={dataTestId} copy={copy} />\n )}\n <CategoryPanelList copy={copy}\n groups={state.visibleGroups}\n query={state.query}\n draft={state.draft}\n rowIds={rowIds}\n activeIndex={state.activeIndex}\n sheet={sheet}\n single={single}\n loading={loading}\n showCounts={showCounts}\n allowParentSelection={allowParentSelection}\n isExpanded={state.isExpanded}\n onToggleExpanded={state.toggleExpanded}\n onActivateCategory={onActivateCategory}\n onActivateSubcategory={onActivateSubcategory}\n onClearQuery={() => state.setQuery('')}\n onCreateCategory={onCreateCategory}\n listRef={listRef}\n dataTestId={dataTestId}\n />\n <PanelFooter\n state={state}\n changed={changed}\n single={single}\n sheet={sheet}\n copy={copy}\n onApply={onApply}\n onCancel={onCancel}\n dataTestId={dataTestId}\n />\n </Box>\n );\n}\n\n/** The selected-category chips, wired to remove from the draft. */\nfunction PinnedTray({\n state,\n chips,\n dataTestId,\n copy,\n}: Pick<CategoryPanelProps, 'state' | 'chips' | 'dataTestId' | 'copy'>): React.JSX.Element {\n return (\n <CategoryPinnedTray\n copy={copy}\n chips={chips}\n onRemove={(chipId) => state.setDraft(removeChip(state.allGroups, chipId, state.draft))}\n dataTestId={dataTestId}\n />\n );\n}\n\n/** Whichever footer the mode calls for. */\nfunction PanelFooter({\n state,\n changed,\n single,\n sheet,\n onApply,\n onCancel,\n dataTestId,\n copy,\n}: Pick<\n CategoryPanelProps,\n 'state' | 'changed' | 'single' | 'sheet' | 'onApply' | 'onCancel' | 'dataTestId' | 'copy'\n>): React.JSX.Element {\n if (single) {\n return (\n <CategorySingleFoot sheet={sheet} onCancel={onCancel} dataTestId={dataTestId} copy={copy} />\n );\n }\n return (\n <CategoryMultiFoot\n count={state.draft.size}\n changed={changed}\n sheet={sheet}\n copy={copy}\n onClear={() => state.setDraft(new Set())}\n onApply={onApply}\n dataTestId={dataTestId}\n />\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAGA,OAAO,SAAS;AA4DZ,SAa2B,KAb3B;AAvBG,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA0C;AACxC,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,MAAK;AAAA,MACL,cAAY;AAAA,MACZ,eAAa,GAAG,UAAU;AAAA,MAC1B,IAAI,CAAC,WAAW,EAAE,GAAG,QAAQ,KAAK,GAAG,GAAI,QAAQ,QAAQ,KAAK,IAAI,CAAC,EAAG;AAAA,MAEtE;AAAA;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,OAAO,MAAM;AAAA,YACb,aAAa,SAAS,KAAK,OAAO,oBAAoB,KAAK,OAAO;AAAA,YAClE;AAAA,YACA,cACE,SAAS,SAAY,oBAAC,wBAAqB,OAAc,YAAwB,MAAY;AAAA,YAE/F,eAAe,MAAM;AAAA,YACrB;AAAA,YACA;AAAA;AAAA,QACF;AAAA,QACC,CAAC,UAAU,CAAC,WACX,oBAAC,cAAW,OAAc,OAAc,YAAwB,MAAY;AAAA,QAE9E;AAAA,UAAC;AAAA;AAAA,YAAkB;AAAA,YACjB,QAAQ,MAAM;AAAA,YACd,OAAO,MAAM;AAAA,YACb,OAAO,MAAM;AAAA,YACb;AAAA,YACA,aAAa,MAAM;AAAA,YACnB;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA,YAAY,MAAM;AAAA,YAClB,kBAAkB,MAAM;AAAA,YACxB;AAAA,YACA;AAAA,YACA,cAAc,MAAM,MAAM,SAAS,EAAE;AAAA,YACrC;AAAA,YACA;AAAA,YACA;AAAA;AAAA,QACF;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA;AAAA,QACF;AAAA;AAAA;AAAA,EACF;AAEJ;AAGA,SAAS,WAAW;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA2F;AACzF,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA,UAAU,CAAC,WAAW,MAAM,SAAS,WAAW,MAAM,WAAW,QAAQ,MAAM,KAAK,CAAC;AAAA,MACrF;AAAA;AAAA,EACF;AAEJ;AAGA,SAAS,YAAY;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAGsB;AACpB,MAAI,QAAQ;AACV,WACE,oBAAC,sBAAmB,OAAc,UAAoB,YAAwB,MAAY;AAAA,EAE9F;AACA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,MAAM,MAAM;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS,MAAM,MAAM,SAAS,oBAAI,IAAI,CAAC;AAAA,MACvC;AAAA,MACA;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/form/CategorySelect/category-keyboard.ts"],"sourcesContent":["import { isLeafCategory, 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 context.groups.find((group) => group.category.id === 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 category. On a subcategory it is a no-op: there is nothing deeper. */\nfunction handleRight(context: CategoryKeyContext): void {\n const row = activeRow(context);\n if (row?.kind === 'category') context.expand(row.id);\n}\n\n/** ← collapses a category, or walks a subcategory back up to its parent. */\nfunction handleLeft(context: CategoryKeyContext): void {\n const row = activeRow(context);\n if (!row) return;\n if (row.kind === 'category') {\n context.collapse(row.id);\n return;\n }\n if (!row.parentId) return;\n context.collapse(row.parentId);\n const parentIndex = context.rows.findIndex(\n (candidate) => candidate.kind === 'category' && candidate.id === row.parentId,\n );\n if (parentIndex >= 0) context.setActiveIndex(parentIndex);\n}\n\n/**\n * What a category 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 category that is only a HEADING expands instead of marking: the row has no\n * control, so \"mark\" has nothing to mean there. A childless category is not a\n * heading — it is the leaf — so it marks.\n */\nfunction activateCategoryRow(context: CategoryKeyContext, categoryId: string): void {\n const group = groupOf(context, categoryId);\n if (!group) return;\n if (!context.allowParentSelection && !isLeafCategory(group)) {\n context.toggleExpanded(categoryId);\n return;\n }\n if (context.single) {\n context.pick(categoryId);\n return;\n }\n context.toggleCategory(group);\n}\n\n/** Space marks the row under the cursor. */\nfunction handleSpace(context: CategoryKeyContext): void {\n const row = activeRow(context);\n if (!row) return;\n if (row.kind === 'subcategory') {\n if (context.single) context.pick(row.id);\n else context.toggleSubcategory(row.id);\n return;\n }\n activateCategoryRow(context, row.id);\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) return;\n if (row.kind === 'subcategory') {\n context.pick(row.id);\n return;\n }\n activateCategoryRow(context, row.id);\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,QAAQ,OAAO,KAAK,CAAC,UAAU,MAAM,SAAS,OAAO,EAAE;AAChE;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,SAAS,WAAY,SAAQ,OAAO,IAAI,EAAE;AACrD;AAGA,SAAS,WAAW,SAAmC;AACrD,QAAM,MAAM,UAAU,OAAO;AAC7B,MAAI,CAAC,IAAK;AACV,MAAI,IAAI,SAAS,YAAY;AAC3B,YAAQ,SAAS,IAAI,EAAE;AACvB;AAAA,EACF;AACA,MAAI,CAAC,IAAI,SAAU;AACnB,UAAQ,SAAS,IAAI,QAAQ;AAC7B,QAAM,cAAc,QAAQ,KAAK;AAAA,IAC/B,CAAC,cAAc,UAAU,SAAS,cAAc,UAAU,OAAO,IAAI;AAAA,EACvE;AACA,MAAI,eAAe,EAAG,SAAQ,eAAe,WAAW;AAC1D;AAWA,SAAS,oBAAoB,SAA6B,YAA0B;AAClF,QAAM,QAAQ,QAAQ,SAAS,UAAU;AACzC,MAAI,CAAC,MAAO;AACZ,MAAI,CAAC,QAAQ,wBAAwB,CAAC,eAAe,KAAK,GAAG;AAC3D,YAAQ,eAAe,UAAU;AACjC;AAAA,EACF;AACA,MAAI,QAAQ,QAAQ;AAClB,YAAQ,KAAK,UAAU;AACvB;AAAA,EACF;AACA,UAAQ,eAAe,KAAK;AAC9B;AAGA,SAAS,YAAY,SAAmC;AACtD,QAAM,MAAM,UAAU,OAAO;AAC7B,MAAI,CAAC,IAAK;AACV,MAAI,IAAI,SAAS,eAAe;AAC9B,QAAI,QAAQ,OAAQ,SAAQ,KAAK,IAAI,EAAE;AAAA,QAClC,SAAQ,kBAAkB,IAAI,EAAE;AACrC;AAAA,EACF;AACA,sBAAoB,SAAS,IAAI,EAAE;AACrC;AAEA,SAAS,YAAY,SAAmC;AACtD,QAAM,MAAM,UAAU,OAAO;AAC7B,MAAI,CAAC,QAAQ,QAAQ;AACnB,YAAQ,OAAO;AACf;AAAA,EACF;AACA,MAAI,CAAC,IAAK;AACV,MAAI,IAAI,SAAS,eAAe;AAC9B,YAAQ,KAAK,IAAI,EAAE;AACnB;AAAA,EACF;AACA,sBAAoB,SAAS,IAAI,EAAE;AACrC;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 +0,0 @@
|
|
|
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 { childTestId, resolveTestId, withoutTestIdProps } from '../../../platform/test-id';\nimport {\n buttonEmphasisStyles,\n buttonVariantStyles,\n getColorFromTheme,\n iconAlignmentStyles,\n buttonSize,\n} from './Button.styles';\nimport type { 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/** `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 * 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\nexport const Button = 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 ? <span data-testid={testId('icon')}>{icon}</span> : 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 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\nButton.displayName = 'Button';\n"],"mappings":";;;;;;;;;;;;;;;;;AAAA,OAAO,eAAe;AACtB,OAAO,sBAAsB;AAC7B,SAAS,cAAc;AACvB,YAAY,WAAW;AAkGE;AArFzB,IAAM,eAAe,OAAO,WAAW;AAAA,EACrC,mBAAmB,CAAC,SAClB,CAAC,QAAQ,SAAS,WAAW,iBAAiB,eAAe,QAAQ,EAAE;AAAA,IACrE;AAAA,EACF,MAAM;AACV,CAAC,EAME,CAAC,EAAE,OAAO,eAAe,cAAc,WAAW,MAAM,MAAM,MAAM;AACrE,QAAM,eAAe,kBAAkB,OAAO,WAAW;AAEzD,SAAO;AAAA,IACL,eAAe;AAAA,IACf,YAAY;AAAA,IACZ,cAAc,MAAM,QAAQ,CAAC;AAAA,IAC7B,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,UAAU;AAAA,IACV,GAAG,oBAAoB,KAAK;AAAA,IAC5B,GAAG,oBAAoB,OAAO,eAAe,cAAc,WAAW;AAAA,IACtE,GAAG,qBAAqB,cAAc,MAAM,KAAK;AAAA,EACnD;AACF,CAAC;AAGD,IAAM,gBAAgB,CAAC,YAAwD;AAC7E,MAAI,YAAY,UAAW,QAAO;AAClC,SAAO,YAAY,SAAS,SAAS;AACvC;AAKA,IAAM,cAAc,CAAC,UAA6D;AAChF,MAAI,UAAU,SAAU,QAAO;AAC/B,SAAO,UAAU,YAAY,YAAY;AAC3C;AAUA,SAAS,WAAW;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AACF,GAAgE;AAC9D,SAAO,CAAC,WAAW,QAAQ,QAAQ,YAAY;AACjD;AAEO,IAAM,SAAe;AAAA,EAC1B,CAAC,UAAU,QAAQ;AACjB,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAG;AAAA,IACL,IAAI,mBAAmB,QAAQ;AAG/B,UAAM,YAAY,cAAc,QAAQ,QAAQ;AAChD,UAAM,SAAS,CAAC,WAAmB,YAAY,QAAQ,QAAQ,QAAQ;AACvE,UAAM,QAAQ,mBAAmB,MAAM;AAGvC,UAAM,iBACJ,CAAC,WAAW,OAAO,oBAAC,UAAK,eAAa,OAAO,MAAM,GAAI,gBAAK,IAAU;AAGxE,UAAM,WAAW,WAAW,EAAE,SAAS,MAAM,SAAS,CAAC;AAEvD,UAAM,kBACJ,CAAC,SAAS,WAAW,IAAI,MAAM,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,KAAK;AAEzE,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,SAAS,cAAc,OAAO;AAAA,QAC9B,OAAO,YAAY,KAAK;AAAA,QACxB,eAAe;AAAA,QACf,aAAa;AAAA,QACb;AAAA,QACA;AAAA,QACA;AAAA,QACA,UAAU,YAAY;AAAA,QACtB,eAAe,CAAC;AAAA,QAChB,WAAW,iBAAiB,SAAS,iBAAiB;AAAA,QACtD,SAAS,iBAAiB,UAAU,iBAAiB;AAAA,QACrD;AAAA,QACA;AAAA,QACA;AAAA,QACA,IAAI,WAAW,MAAM,QAAQ;AAAA,QAC7B,eAAa;AAAA,QACZ,GAAG;AAAA,QACJ,WAAW;AAAA,QAEV,oBACC,oBAAC,oBAAiB,MAAM,IAAI,OAAM,WAAU,eAAa,OAAO,SAAS,GAAG,IAE5E;AAAA;AAAA,IAEJ;AAAA,EAEJ;AACF;AAEA,OAAO,cAAc;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
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 { 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 const allExpanded =\n state.allGroups.length > 0 &&\n state.allGroups.every((group) => state.isExpanded(group.category.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;AA6CpB,mBACE,KADF;AArCJ,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;AACjF,QAAM,cACJ,MAAM,UAAU,SAAS,KACzB,MAAM,UAAU,MAAM,CAAC,UAAU,MAAM,WAAW,MAAM,SAAS,EAAE,CAAC;AAEtE,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":[]}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|