@burojs/ui-shadcn 0.4.0 → 0.6.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/README.md +22 -5
- package/dist/bulk-summary-CwSd6Yjb.cjs +108 -0
- package/dist/bulk-summary-CwSd6Yjb.cjs.map +1 -0
- package/dist/bulk-summary-GxMDP4bw.js +108 -0
- package/dist/bulk-summary-GxMDP4bw.js.map +1 -0
- package/dist/button-BxnnE28H.cjs +55 -0
- package/dist/button-BxnnE28H.cjs.map +1 -0
- package/dist/button-MWVeZaqj.js +44 -0
- package/dist/button-MWVeZaqj.js.map +1 -0
- package/dist/candidates-gate-Bb70LPAy.js +135 -0
- package/dist/candidates-gate-Bb70LPAy.js.map +1 -0
- package/dist/candidates-gate-D33jXLHO.cjs +153 -0
- package/dist/candidates-gate-D33jXLHO.cjs.map +1 -0
- package/dist/candidates-relation-picker-B116Bmqi.js +31 -0
- package/dist/candidates-relation-picker-B116Bmqi.js.map +1 -0
- package/dist/candidates-relation-picker-Cy0rTxvI.cjs +31 -0
- package/dist/candidates-relation-picker-Cy0rTxvI.cjs.map +1 -0
- package/dist/check-confirm-dialog--gPFnEeg.cjs +1547 -0
- package/dist/check-confirm-dialog--gPFnEeg.cjs.map +1 -0
- package/dist/check-confirm-dialog-bRT8lt0d.js +1245 -0
- package/dist/check-confirm-dialog-bRT8lt0d.js.map +1 -0
- package/dist/dialog-B_rt9S1c.js +63 -0
- package/dist/dialog-B_rt9S1c.js.map +1 -0
- package/dist/dialog-DRNUq9EA.cjs +112 -0
- package/dist/dialog-DRNUq9EA.cjs.map +1 -0
- package/dist/filter-builder-Bhn-dhZV.js +1621 -0
- package/dist/filter-builder-Bhn-dhZV.js.map +1 -0
- package/dist/filter-builder-nXqtM_h4.cjs +1789 -0
- package/dist/filter-builder-nXqtM_h4.cjs.map +1 -0
- package/dist/flow-runtime-D3avVH0I.cjs +255 -0
- package/dist/flow-runtime-D3avVH0I.cjs.map +1 -0
- package/dist/flow-runtime-s6lkRPKF.js +252 -0
- package/dist/flow-runtime-s6lkRPKF.js.map +1 -0
- package/dist/flow-screen-CCvgDI9G.js +704 -0
- package/dist/flow-screen-CCvgDI9G.js.map +1 -0
- package/dist/flow-screen-Ci_Sik4Y.cjs +708 -0
- package/dist/flow-screen-Ci_Sik4Y.cjs.map +1 -0
- package/dist/index.cjs +3420 -11986
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1843 -2015
- package/dist/index.d.ts +1849 -2021
- package/dist/index.js +3090 -11675
- package/dist/index.js.map +1 -1
- package/dist/inline-flow-host-B6FI0ctQ.js +112 -0
- package/dist/inline-flow-host-B6FI0ctQ.js.map +1 -0
- package/dist/inline-flow-host-DV1et3c6.cjs +112 -0
- package/dist/inline-flow-host-DV1et3c6.cjs.map +1 -0
- package/dist/input-BI0czGyI.cjs +23 -0
- package/dist/input-BI0czGyI.cjs.map +1 -0
- package/dist/input-CUPStzZO.js +17 -0
- package/dist/input-CUPStzZO.js.map +1 -0
- package/dist/label-66W1qbke.js +29 -0
- package/dist/label-66W1qbke.js.map +1 -0
- package/dist/label-w6JYf2cF.cjs +36 -0
- package/dist/label-w6JYf2cF.cjs.map +1 -0
- package/dist/markdown/index.cjs +1 -1
- package/dist/markdown/index.d.cts +2 -3
- package/dist/markdown/index.d.ts +2 -3
- package/dist/markdown/index.js +1 -1
- package/dist/page-flow-route-hdnULWqd.cjs +277 -0
- package/dist/page-flow-route-hdnULWqd.cjs.map +1 -0
- package/dist/page-flow-route-i3ZfJ1sd.js +277 -0
- package/dist/page-flow-route-i3ZfJ1sd.js.map +1 -0
- package/dist/place-node-C5SmIAYU.cjs +8139 -0
- package/dist/place-node-C5SmIAYU.cjs.map +1 -0
- package/dist/place-node-NraNYKPx.js +7597 -0
- package/dist/place-node-NraNYKPx.js.map +1 -0
- package/dist/related-list-cards-BWZ9GfOG.js +42 -0
- package/dist/related-list-cards-BWZ9GfOG.js.map +1 -0
- package/dist/related-list-cards-CFq3meUn.cjs +42 -0
- package/dist/related-list-cards-CFq3meUn.cjs.map +1 -0
- package/dist/related-list-timeline-CCzl71aA.cjs +142 -0
- package/dist/related-list-timeline-CCzl71aA.cjs.map +1 -0
- package/dist/related-list-timeline-yrPsjA3F.js +142 -0
- package/dist/related-list-timeline-yrPsjA3F.js.map +1 -0
- package/dist/related-rows-ea43qhhy.cjs +208 -0
- package/dist/related-rows-ea43qhhy.cjs.map +1 -0
- package/dist/related-rows-o0_t-Yce.js +185 -0
- package/dist/related-rows-o0_t-Yce.js.map +1 -0
- package/dist/relation-create-IA40t1hi.cjs +364 -0
- package/dist/relation-create-IA40t1hi.cjs.map +1 -0
- package/dist/relation-create-h6F9B4C4.js +339 -0
- package/dist/relation-create-h6F9B4C4.js.map +1 -0
- package/dist/relation-many-picker-C8zFEjwd.cjs +467 -0
- package/dist/relation-many-picker-C8zFEjwd.cjs.map +1 -0
- package/dist/relation-many-picker-bvkjkv-k.js +466 -0
- package/dist/relation-many-picker-bvkjkv-k.js.map +1 -0
- package/dist/relation-picker-Z0ZJJ5uS.js +493 -0
- package/dist/relation-picker-Z0ZJJ5uS.js.map +1 -0
- package/dist/relation-picker-dialog-0IzkjmiA.cjs +304 -0
- package/dist/relation-picker-dialog-0IzkjmiA.cjs.map +1 -0
- package/dist/relation-picker-dialog-B6nIiJBf.js +303 -0
- package/dist/relation-picker-dialog-B6nIiJBf.js.map +1 -0
- package/dist/relation-picker-gag0qSR5.cjs +516 -0
- package/dist/relation-picker-gag0qSR5.cjs.map +1 -0
- package/dist/resource-page-router-3RUf9d1j.js +1588 -0
- package/dist/resource-page-router-3RUf9d1j.js.map +1 -0
- package/dist/resource-page-router-PXfMW9nh.cjs +1785 -0
- package/dist/resource-page-router-PXfMW9nh.cjs.map +1 -0
- package/dist/{rich-text-IDFvyvRE.js → rich-text-DFerywgz.js} +7 -4
- package/dist/{rich-text-IDFvyvRE.js.map → rich-text-DFerywgz.js.map} +1 -1
- package/dist/{rich-text-lndbYqzR.cjs → rich-text-vTyVjDoa.cjs} +25 -22
- package/dist/{rich-text-lndbYqzR.cjs.map → rich-text-vTyVjDoa.cjs.map} +1 -1
- package/dist/search-access-CPPmfuvy.js +654 -0
- package/dist/search-access-CPPmfuvy.js.map +1 -0
- package/dist/search-access-Dugvttlg.cjs +791 -0
- package/dist/search-access-Dugvttlg.cjs.map +1 -0
- package/dist/shell-BPlk8hEa.js +205 -0
- package/dist/shell-BPlk8hEa.js.map +1 -0
- package/dist/shell-DxhlsPwm.cjs +271 -0
- package/dist/shell-DxhlsPwm.cjs.map +1 -0
- package/dist/skeleton-U2noefj5.js +13 -0
- package/dist/skeleton-U2noefj5.js.map +1 -0
- package/dist/skeleton-_1gP7wan.cjs +18 -0
- package/dist/skeleton-_1gP7wan.cjs.map +1 -0
- package/dist/styles.css +241 -9
- package/dist/toaster-C02XbB9r.js +135 -0
- package/dist/toaster-C02XbB9r.js.map +1 -0
- package/dist/toaster-Cr51X7bz.cjs +195 -0
- package/dist/toaster-Cr51X7bz.cjs.map +1 -0
- package/dist/use-dialog-filter-state-C--oMAZG.js +87 -0
- package/dist/use-dialog-filter-state-C--oMAZG.js.map +1 -0
- package/dist/use-dialog-filter-state-Dhe8PcB5.cjs +92 -0
- package/dist/use-dialog-filter-state-Dhe8PcB5.cjs.map +1 -0
- package/dist/{use-theme-BwPWinB2.cjs → use-theme-DFlpXils.cjs} +12 -1
- package/dist/use-theme-DFlpXils.cjs.map +1 -0
- package/dist/{use-theme-IHmtrhEt.js → use-theme-dnE3Od3I.js} +7 -2
- package/dist/use-theme-dnE3Od3I.js.map +1 -0
- package/package.json +16 -14
- package/dist/use-theme-BwPWinB2.cjs.map +0 -1
- package/dist/use-theme-IHmtrhEt.js.map +0 -1
|
@@ -0,0 +1,1245 @@
|
|
|
1
|
+
import { l as cn } from "./use-theme-dnE3Od3I.js";
|
|
2
|
+
import { d as PopoverTrigger, n as toast, o as useLinkComponent, s as Popover, u as PopoverContent } from "./toaster-C02XbB9r.js";
|
|
3
|
+
import { t as Button } from "./button-MWVeZaqj.js";
|
|
4
|
+
import { a as DialogFooter, i as DialogDescription, o as DialogHeader, r as DialogContent, s as DialogTitle, t as Dialog } from "./dialog-B_rt9S1c.js";
|
|
5
|
+
import { FRAMEWORK_TEXT_KEYS, canonicalEntityNodeKey, checkFailedDefault, compileFlow, declaredText, docAddressToArticle, hasRecordPage, hrefFor, humanizeKey, isBuiltinItemAction, normalizeEntityRef, recordEntries, resolveContentSlot, resolveDocSee } from "@burojs/core";
|
|
6
|
+
import { buildDI, conjoinHostPage, modeForSurface, resolveRoute, useApp, useAvailabilityResolver, useComponent, useDoc, useDocPanel, useDocTopics, useEntryGate, useFlowRowResolver, useHostPage, useI18nCaps, useOptionalRouterAdapter, useOptionalT, usePresentBlock, usePresentFlow, useProjectedTitle, useQuick, useQuickEntry, useTestIds, useUIInstance } from "@burojs/react";
|
|
7
|
+
import { IconArrowRight, IconArrowUpRight, IconArrowsLeftRight, IconBan, IconBell, IconBox, IconBoxMultiple, IconBuilding, IconCheck, IconChevronRight, IconCircle, IconCircleCaretRight, IconCircleCheck, IconCircleX, IconClipboardList, IconClock, IconCopy, IconDots, IconEye, IconFileOff, IconFilePencil, IconFileText, IconFileX, IconGitBranch, IconHammer, IconHash, IconHistory, IconInfoHexagon, IconKey, IconKeyOff, IconLink, IconList, IconLoader2, IconMapPin, IconNetwork, IconPackage, IconPackageExport, IconPackageImport, IconPackageOff, IconPackages, IconPencil, IconPlayerPlay, IconPlayerTrackNext, IconPlus, IconQrcode, IconRefresh, IconRotate, IconScript, IconSettings, IconShieldCheck, IconTag, IconTool, IconTrash, IconTruck, IconUserPlus, IconUsers, IconX } from "@tabler/icons-react";
|
|
8
|
+
import * as React from "react";
|
|
9
|
+
import { Component, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
10
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
12
|
+
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
13
|
+
import { evaluateCondition, isFlowAction } from "@burojs/core/internal";
|
|
14
|
+
//#region src/doc/doc-info.tsx
|
|
15
|
+
function DocInfo({ address, name, testId, className }) {
|
|
16
|
+
const doc = useDoc(address);
|
|
17
|
+
const caps = useI18nCaps();
|
|
18
|
+
const t = useOptionalT();
|
|
19
|
+
const { openPanel } = useDocPanel();
|
|
20
|
+
const [open, setOpen] = useState(false);
|
|
21
|
+
const see = resolveDocSee(caps, address);
|
|
22
|
+
const { scope: articleScope, topicId: ownTopicId } = docAddressToArticle(see ?? address);
|
|
23
|
+
const topics = useDocTopics(articleScope);
|
|
24
|
+
const hasOwnArticle = ownTopicId === void 0 ? topics.length > 0 : topics.some((x) => x.id === ownTopicId);
|
|
25
|
+
if (!doc && !hasOwnArticle) return null;
|
|
26
|
+
const id = testId ?? `doc-info:${address}`;
|
|
27
|
+
const newTabNote = t("docPanel.opensInNewTab", { defaultValue: "(opens in a new tab)" });
|
|
28
|
+
const hasSummary = doc?.summary !== void 0;
|
|
29
|
+
const hasLinks = doc?.links !== void 0 && doc.links.length > 0;
|
|
30
|
+
return jsxs(Popover, {
|
|
31
|
+
open,
|
|
32
|
+
onOpenChange: setOpen,
|
|
33
|
+
children: [jsx(PopoverTrigger, {
|
|
34
|
+
asChild: true,
|
|
35
|
+
children: jsx("button", {
|
|
36
|
+
type: "button",
|
|
37
|
+
"aria-haspopup": "dialog",
|
|
38
|
+
"aria-label": t("docPanel.openFor", {
|
|
39
|
+
params: { name },
|
|
40
|
+
defaultValue: `Help: ${name}`
|
|
41
|
+
}),
|
|
42
|
+
"data-testid": id,
|
|
43
|
+
className: cn("inline-flex size-6 min-h-[24px] min-w-[24px] shrink-0 items-center justify-center rounded-full", "text-muted-foreground", "transition-colors duration-150", "hover:bg-muted hover:text-foreground", "data-[state=open]:bg-muted data-[state=open]:text-foreground", "focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background", className),
|
|
44
|
+
children: jsx(IconInfoHexagon, {
|
|
45
|
+
className: "size-3.5",
|
|
46
|
+
"aria-hidden": "true"
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
}), jsxs(PopoverContent, {
|
|
50
|
+
role: "dialog",
|
|
51
|
+
"aria-label": name,
|
|
52
|
+
className: cn("w-auto min-w-[16rem] max-w-[22rem]", "max-h-[min(24rem,60vh)] overflow-y-auto overscroll-contain", "space-y-2.5"),
|
|
53
|
+
"data-testid": `${id}:content`,
|
|
54
|
+
children: [
|
|
55
|
+
hasSummary && jsx("p", {
|
|
56
|
+
className: "text-[0.8125rem] leading-[1.6] text-foreground",
|
|
57
|
+
children: doc?.summary
|
|
58
|
+
}),
|
|
59
|
+
hasOwnArticle && jsxs("button", {
|
|
60
|
+
type: "button",
|
|
61
|
+
"data-testid": `${id}:more`,
|
|
62
|
+
onClick: () => {
|
|
63
|
+
setOpen(false);
|
|
64
|
+
openPanel(ownTopicId === void 0 ? { scopes: [articleScope] } : {
|
|
65
|
+
scopes: [articleScope],
|
|
66
|
+
topicId: ownTopicId
|
|
67
|
+
}, id);
|
|
68
|
+
},
|
|
69
|
+
className: cn("inline-flex items-center gap-1 rounded-sm text-[0.8125rem] font-medium text-primary", "hover:underline underline-offset-2", "focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"),
|
|
70
|
+
children: [t("docPanel.readMore", { defaultValue: "Read more" }), jsx(IconArrowRight, {
|
|
71
|
+
className: "size-3.5 shrink-0",
|
|
72
|
+
"aria-hidden": "true"
|
|
73
|
+
})]
|
|
74
|
+
}),
|
|
75
|
+
hasLinks && jsx("div", {
|
|
76
|
+
className: cn("flex flex-col items-start gap-1.5", hasSummary && "border-t border-border/60 pt-2.5"),
|
|
77
|
+
children: doc?.links?.map((link) => jsxs("a", {
|
|
78
|
+
href: link.href,
|
|
79
|
+
target: "_blank",
|
|
80
|
+
rel: "noopener noreferrer",
|
|
81
|
+
className: cn("group inline-flex max-w-full items-center gap-1 rounded-sm", "text-[0.8125rem] font-medium text-primary", "hover:underline underline-offset-2", "focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"),
|
|
82
|
+
children: [
|
|
83
|
+
jsx("span", {
|
|
84
|
+
className: "min-w-0 truncate",
|
|
85
|
+
children: link.label
|
|
86
|
+
}),
|
|
87
|
+
jsx(IconArrowUpRight, {
|
|
88
|
+
className: "size-3.5 shrink-0 transition-transform duration-150 group-hover:-translate-y-px group-hover:translate-x-px",
|
|
89
|
+
"aria-hidden": "true"
|
|
90
|
+
}),
|
|
91
|
+
jsx("span", {
|
|
92
|
+
className: "sr-only",
|
|
93
|
+
children: newTabNote
|
|
94
|
+
})
|
|
95
|
+
]
|
|
96
|
+
}, link.href))
|
|
97
|
+
})
|
|
98
|
+
]
|
|
99
|
+
})]
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
103
|
+
//#region src/primitives/tooltip.tsx
|
|
104
|
+
const TooltipProvider = TooltipPrimitive.Provider;
|
|
105
|
+
const Tooltip = TooltipPrimitive.Root;
|
|
106
|
+
const TooltipTrigger = TooltipPrimitive.Trigger;
|
|
107
|
+
const TooltipContent = React.forwardRef(({ className, sideOffset = 4, ...props }, ref) => jsx(TooltipPrimitive.Portal, { children: jsx(TooltipPrimitive.Content, {
|
|
108
|
+
ref,
|
|
109
|
+
sideOffset,
|
|
110
|
+
className: cn("z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md duration-[var(--duration-overlay)] ease-[var(--ease-overlay)] animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", className),
|
|
111
|
+
...props
|
|
112
|
+
}) }));
|
|
113
|
+
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region src/primitives/dropdown-menu.tsx
|
|
116
|
+
const DropdownMenu = DropdownMenuPrimitive.Root;
|
|
117
|
+
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
|
118
|
+
const DropdownMenuGroup = DropdownMenuPrimitive.Group;
|
|
119
|
+
const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
|
120
|
+
const DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
|
121
|
+
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
|
122
|
+
const DropdownMenuSubTrigger = React.forwardRef(({ className, inset, children, ...props }, ref) => jsxs(DropdownMenuPrimitive.SubTrigger, {
|
|
123
|
+
ref,
|
|
124
|
+
className: cn("flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", inset && "pl-8", className),
|
|
125
|
+
...props,
|
|
126
|
+
children: [children, jsx(IconChevronRight, { className: "ml-auto" })]
|
|
127
|
+
}));
|
|
128
|
+
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
|
|
129
|
+
const DropdownMenuSubContent = React.forwardRef(({ className, ...props }, ref) => jsx(DropdownMenuPrimitive.SubContent, {
|
|
130
|
+
ref,
|
|
131
|
+
className: cn("z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg duration-[var(--duration-overlay)] ease-[var(--ease-overlay)] data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", className),
|
|
132
|
+
...props
|
|
133
|
+
}));
|
|
134
|
+
DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
|
|
135
|
+
const DropdownMenuContent = React.forwardRef(({ className, sideOffset = 4, ...props }, ref) => jsx(DropdownMenuPrimitive.Portal, { children: jsx(DropdownMenuPrimitive.Content, {
|
|
136
|
+
ref,
|
|
137
|
+
sideOffset,
|
|
138
|
+
className: cn("z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md duration-[var(--duration-overlay)] ease-[var(--ease-overlay)] data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", className),
|
|
139
|
+
...props
|
|
140
|
+
}) }));
|
|
141
|
+
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
|
142
|
+
const DropdownMenuItem = React.forwardRef(({ className, inset, ...props }, ref) => jsx(DropdownMenuPrimitive.Item, {
|
|
143
|
+
ref,
|
|
144
|
+
className: cn("relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", inset && "pl-8", className),
|
|
145
|
+
...props
|
|
146
|
+
}));
|
|
147
|
+
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
|
148
|
+
const DropdownMenuCheckboxItem = React.forwardRef(({ className, children, checked, ...props }, ref) => jsxs(DropdownMenuPrimitive.CheckboxItem, {
|
|
149
|
+
ref,
|
|
150
|
+
className: cn("relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className),
|
|
151
|
+
checked,
|
|
152
|
+
...props,
|
|
153
|
+
children: [jsx("span", {
|
|
154
|
+
className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center",
|
|
155
|
+
children: jsx(DropdownMenuPrimitive.ItemIndicator, { children: jsx(IconCheck, { className: "h-4 w-4" }) })
|
|
156
|
+
}), children]
|
|
157
|
+
}));
|
|
158
|
+
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
|
|
159
|
+
const DropdownMenuRadioItem = React.forwardRef(({ className, children, ...props }, ref) => jsxs(DropdownMenuPrimitive.RadioItem, {
|
|
160
|
+
ref,
|
|
161
|
+
className: cn("relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className),
|
|
162
|
+
...props,
|
|
163
|
+
children: [jsx("span", {
|
|
164
|
+
className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center",
|
|
165
|
+
children: jsx(DropdownMenuPrimitive.ItemIndicator, { children: jsx(IconCircle, { className: "h-2 w-2 fill-current" }) })
|
|
166
|
+
}), children]
|
|
167
|
+
}));
|
|
168
|
+
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
|
|
169
|
+
const DropdownMenuLabel = React.forwardRef(({ className, inset, ...props }, ref) => jsx(DropdownMenuPrimitive.Label, {
|
|
170
|
+
ref,
|
|
171
|
+
className: cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className),
|
|
172
|
+
...props
|
|
173
|
+
}));
|
|
174
|
+
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
|
175
|
+
const DropdownMenuSeparator = React.forwardRef(({ className, ...props }, ref) => jsx(DropdownMenuPrimitive.Separator, {
|
|
176
|
+
ref,
|
|
177
|
+
className: cn("-mx-1 my-1 h-px bg-muted", className),
|
|
178
|
+
...props
|
|
179
|
+
}));
|
|
180
|
+
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
|
181
|
+
function DropdownMenuShortcut({ className, ...props }) {
|
|
182
|
+
return jsx("span", {
|
|
183
|
+
className: cn("ml-auto text-xs tracking-widest opacity-60", className),
|
|
184
|
+
...props
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
|
|
188
|
+
//#endregion
|
|
189
|
+
//#region src/primitives/icon.tsx
|
|
190
|
+
const REGISTRY = {
|
|
191
|
+
Play: IconPlayerPlay,
|
|
192
|
+
PlayCircle: IconCircleCaretRight,
|
|
193
|
+
StepForward: IconPlayerTrackNext,
|
|
194
|
+
RotateCcw: IconRotate,
|
|
195
|
+
RefreshCw: IconRefresh,
|
|
196
|
+
Ban: IconBan,
|
|
197
|
+
XCircle: IconCircleX,
|
|
198
|
+
Trash2: IconTrash,
|
|
199
|
+
Copy: IconCopy,
|
|
200
|
+
Hash: IconHash,
|
|
201
|
+
QrCode: IconQrcode,
|
|
202
|
+
Network: IconNetwork,
|
|
203
|
+
GitBranch: IconGitBranch,
|
|
204
|
+
Link: IconLink,
|
|
205
|
+
Link2: IconLink,
|
|
206
|
+
Pencil: IconPencil,
|
|
207
|
+
Plus: IconPlus,
|
|
208
|
+
Eye: IconEye,
|
|
209
|
+
ScrollText: IconScript,
|
|
210
|
+
History: IconHistory,
|
|
211
|
+
MoreHorizontal: IconDots,
|
|
212
|
+
Circle: IconCircle,
|
|
213
|
+
CircleCheck: IconCircleCheck,
|
|
214
|
+
Clock: IconClock,
|
|
215
|
+
Package: IconPackage,
|
|
216
|
+
PackagePlus: IconPackageImport,
|
|
217
|
+
PackageMinus: IconPackageExport,
|
|
218
|
+
PackageCheck: IconPackages,
|
|
219
|
+
PackageX: IconPackageOff,
|
|
220
|
+
PackageOpen: IconBox,
|
|
221
|
+
ArrowRightLeft: IconArrowsLeftRight,
|
|
222
|
+
FileText: IconFileText,
|
|
223
|
+
FileEdit: IconFilePencil,
|
|
224
|
+
FileX: IconFileX,
|
|
225
|
+
FileX2: IconFileOff,
|
|
226
|
+
Truck: IconTruck,
|
|
227
|
+
Wrench: IconTool,
|
|
228
|
+
Hammer: IconHammer,
|
|
229
|
+
Settings: IconSettings,
|
|
230
|
+
Bell: IconBell,
|
|
231
|
+
Building2: IconBuilding,
|
|
232
|
+
MapPin: IconMapPin,
|
|
233
|
+
Users: IconUsers,
|
|
234
|
+
UserPlus: IconUserPlus,
|
|
235
|
+
Boxes: IconBoxMultiple,
|
|
236
|
+
Tag: IconTag,
|
|
237
|
+
ShieldCheck: IconShieldCheck,
|
|
238
|
+
Key: IconKey,
|
|
239
|
+
KeyOff: IconKeyOff,
|
|
240
|
+
ClipboardList: IconClipboardList,
|
|
241
|
+
List: IconList,
|
|
242
|
+
Check: IconCheck,
|
|
243
|
+
X: IconX
|
|
244
|
+
};
|
|
245
|
+
function registerIcons(map) {
|
|
246
|
+
Object.assign(REGISTRY, map);
|
|
247
|
+
}
|
|
248
|
+
function hasIcon(name) {
|
|
249
|
+
return Object.hasOwn(REGISTRY, name);
|
|
250
|
+
}
|
|
251
|
+
function Icon({ name, className }) {
|
|
252
|
+
const Cmp = (name ? REGISTRY[name] : void 0) ?? IconCircle;
|
|
253
|
+
return jsx(Cmp, {
|
|
254
|
+
className: cn("h-4 w-4", className),
|
|
255
|
+
"aria-hidden": "true"
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
//#endregion
|
|
259
|
+
//#region src/flows/load-runtime.tsx
|
|
260
|
+
function retrying(importer) {
|
|
261
|
+
let loading;
|
|
262
|
+
return () => {
|
|
263
|
+
loading ??= importer().catch((error) => {
|
|
264
|
+
loading = void 0;
|
|
265
|
+
throw error;
|
|
266
|
+
});
|
|
267
|
+
return loading;
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
const loadFlowRuntime = retrying(() => import("./flow-runtime-s6lkRPKF.js"));
|
|
271
|
+
const loadFlowScreen = retrying(() => import("./flow-screen-CCvgDI9G.js"));
|
|
272
|
+
const loadPageFlowRoute = retrying(() => import("./page-flow-route-i3ZfJ1sd.js"));
|
|
273
|
+
const loadInlineFlowHost = retrying(() => import("./inline-flow-host-B6FI0ctQ.js"));
|
|
274
|
+
const loadBulkSummary = retrying(() => import("./bulk-summary-GxMDP4bw.js"));
|
|
275
|
+
function preloadFlowRuntime() {
|
|
276
|
+
loadFlowRuntime().catch(() => void 0);
|
|
277
|
+
}
|
|
278
|
+
function useLoaded(loader) {
|
|
279
|
+
const [attempt, setAttempt] = useState(0);
|
|
280
|
+
const [state, setState] = useState({});
|
|
281
|
+
useEffect(() => {
|
|
282
|
+
let live = true;
|
|
283
|
+
loader().then((module) => {
|
|
284
|
+
if (live) setState({ module });
|
|
285
|
+
}, (error) => {
|
|
286
|
+
if (live) setState({ error });
|
|
287
|
+
});
|
|
288
|
+
return () => {
|
|
289
|
+
live = false;
|
|
290
|
+
};
|
|
291
|
+
}, [loader, attempt]);
|
|
292
|
+
const retry = useCallback(() => {
|
|
293
|
+
setState({});
|
|
294
|
+
setAttempt((value) => value + 1);
|
|
295
|
+
}, []);
|
|
296
|
+
return {
|
|
297
|
+
...state,
|
|
298
|
+
retry
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
var FlowBoundary = class extends Component {
|
|
302
|
+
state = { error: void 0 };
|
|
303
|
+
static getDerivedStateFromError(error) {
|
|
304
|
+
return { error: error ?? new Error("unknown") };
|
|
305
|
+
}
|
|
306
|
+
componentDidCatch(error, info) {
|
|
307
|
+
if (process.env.NODE_ENV !== "production") console.error("[burojs] A flow failed to render.", error, info.componentStack);
|
|
308
|
+
}
|
|
309
|
+
render() {
|
|
310
|
+
if (this.state.error !== void 0) return this.props.fallback(this.state.error, () => this.setState({ error: void 0 }));
|
|
311
|
+
return this.props.children;
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
function loadFailedText(t) {
|
|
315
|
+
const value = t("block.failed");
|
|
316
|
+
return value === "block.failed" ? "This section could not be loaded." : value;
|
|
317
|
+
}
|
|
318
|
+
function FailedRun({ onDone }) {
|
|
319
|
+
const t = useOptionalT();
|
|
320
|
+
useEffect(() => {
|
|
321
|
+
toast(loadFailedText(t));
|
|
322
|
+
onDone();
|
|
323
|
+
}, [t, onDone]);
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
function RuntimeFlowRunner(props) {
|
|
327
|
+
const loaded = useLoaded(loadFlowRuntime);
|
|
328
|
+
if (loaded.error !== void 0) return jsx(FailedRun, { onDone: props.onClose });
|
|
329
|
+
if (loaded.module === void 0) return null;
|
|
330
|
+
const Runner = loaded.module.FlowRunner;
|
|
331
|
+
return jsx(FlowBoundary, {
|
|
332
|
+
fallback: () => jsx(FailedRun, { onDone: props.onClose }),
|
|
333
|
+
children: jsx(Runner, { ...props })
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
function RuntimeAppFlowRunner(props) {
|
|
337
|
+
const loaded = useLoaded(loadFlowRuntime);
|
|
338
|
+
if (loaded.error !== void 0) return jsx(FailedRun, { onDone: props.onClose });
|
|
339
|
+
if (loaded.module === void 0) return null;
|
|
340
|
+
const Runner = loaded.module.AppFlowRunner;
|
|
341
|
+
return jsx(FlowBoundary, {
|
|
342
|
+
fallback: () => jsx(FailedRun, { onDone: props.onClose }),
|
|
343
|
+
children: jsx(Runner, { ...props })
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
function RuntimeBulkFlowRunner(props) {
|
|
347
|
+
const loaded = useLoaded(loadFlowRuntime);
|
|
348
|
+
const done = useCallback(() => props.onFinish(Object.freeze({ outcome: "failed" }), void 0), [props.onFinish]);
|
|
349
|
+
if (loaded.error !== void 0) return jsx(FailedRun, { onDone: done });
|
|
350
|
+
if (loaded.module === void 0) return null;
|
|
351
|
+
const Runner = loaded.module.BulkFlowRunner;
|
|
352
|
+
return jsx(FlowBoundary, {
|
|
353
|
+
fallback: () => jsx(FailedRun, { onDone: done }),
|
|
354
|
+
children: jsx(Runner, { ...props })
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
function FlowLoadFailed({ onRetry }) {
|
|
358
|
+
const t = useOptionalT();
|
|
359
|
+
const instance = useUIInstance();
|
|
360
|
+
const retryText = t("actions.retry") === "actions.retry" ? "Retry" : t("actions.retry");
|
|
361
|
+
const closeText = t("flow.close") === "flow.close" ? "Close" : t("flow.close");
|
|
362
|
+
return jsxs("div", {
|
|
363
|
+
role: "alert",
|
|
364
|
+
"data-buro-flow-load": "failed",
|
|
365
|
+
className: "flex flex-col gap-3 text-sm",
|
|
366
|
+
children: [jsx("p", { children: loadFailedText(t) }), jsxs("div", {
|
|
367
|
+
className: "flex justify-end gap-2",
|
|
368
|
+
children: [jsx(Button, {
|
|
369
|
+
type: "button",
|
|
370
|
+
variant: "ghost",
|
|
371
|
+
onClick: () => instance.dismiss(),
|
|
372
|
+
children: closeText
|
|
373
|
+
}), jsx(Button, {
|
|
374
|
+
type: "button",
|
|
375
|
+
onClick: onRetry,
|
|
376
|
+
children: retryText
|
|
377
|
+
})]
|
|
378
|
+
})]
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
//#endregion
|
|
382
|
+
//#region src/actions/action-inner.tsx
|
|
383
|
+
function ActionInner({ icon, label }) {
|
|
384
|
+
return jsxs(Fragment$1, { children: [icon ? jsx(Icon, { name: icon }) : null, label] });
|
|
385
|
+
}
|
|
386
|
+
//#endregion
|
|
387
|
+
//#region src/actions/action-control.tsx
|
|
388
|
+
const warned = new Set();
|
|
389
|
+
function resolveControlChrome(args) {
|
|
390
|
+
const { density, icon, label, actionName, surface } = args;
|
|
391
|
+
if (density === "compact" && icon == null) {
|
|
392
|
+
const key = `${actionName}@${surface}`;
|
|
393
|
+
if (!warned.has(key)) {
|
|
394
|
+
warned.add(key);
|
|
395
|
+
console.warn(`[buro] action "${actionName}" is declared 'compact' on surface "${surface}" but has no icon; rendering it with its label instead. Give the action an \`icon\`, or declare 'regular'.`);
|
|
396
|
+
}
|
|
397
|
+
return {
|
|
398
|
+
compact: false,
|
|
399
|
+
size: "sm",
|
|
400
|
+
className: void 0,
|
|
401
|
+
ariaLabel: void 0,
|
|
402
|
+
content: jsx(ActionInner, {
|
|
403
|
+
icon,
|
|
404
|
+
label
|
|
405
|
+
})
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
if (density === "compact") return {
|
|
409
|
+
compact: true,
|
|
410
|
+
size: "icon",
|
|
411
|
+
className: surface === "row" ? "size-7" : "h-8 w-8",
|
|
412
|
+
ariaLabel: typeof label === "string" ? label : humanizeKey(actionName),
|
|
413
|
+
content: jsx(Icon, { name: icon })
|
|
414
|
+
};
|
|
415
|
+
return {
|
|
416
|
+
compact: false,
|
|
417
|
+
size: "sm",
|
|
418
|
+
className: void 0,
|
|
419
|
+
ariaLabel: void 0,
|
|
420
|
+
content: jsx(ActionInner, {
|
|
421
|
+
icon,
|
|
422
|
+
label
|
|
423
|
+
})
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
function withActionTooltip(control, label, compact, reason) {
|
|
427
|
+
if (!compact && reason === void 0) return control;
|
|
428
|
+
const tooltip = jsxs(Tooltip, { children: [jsx(TooltipTrigger, {
|
|
429
|
+
asChild: true,
|
|
430
|
+
children: control
|
|
431
|
+
}), jsxs(TooltipContent, { children: [label, reason === void 0 ? null : jsx("span", {
|
|
432
|
+
className: "block opacity-80",
|
|
433
|
+
children: reason
|
|
434
|
+
})] })] });
|
|
435
|
+
return compact ? tooltip : jsx(TooltipProvider, { children: tooltip });
|
|
436
|
+
}
|
|
437
|
+
//#endregion
|
|
438
|
+
//#region src/flows/chrome.ts
|
|
439
|
+
const text = (t, key, fallback, params) => {
|
|
440
|
+
const value = t(key, params === void 0 ? void 0 : { params });
|
|
441
|
+
return value === key ? fallback : value;
|
|
442
|
+
};
|
|
443
|
+
function findDeclared(def, name) {
|
|
444
|
+
const actions = def?.actions ?? {};
|
|
445
|
+
for (const place of [
|
|
446
|
+
"item",
|
|
447
|
+
"resource",
|
|
448
|
+
"bulk"
|
|
449
|
+
]) {
|
|
450
|
+
const list = actions[place];
|
|
451
|
+
if (!Array.isArray(list)) continue;
|
|
452
|
+
const found = list.find((entry) => entry.name === name);
|
|
453
|
+
if (found !== void 0) return found;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
function useFlowChrome(resource, actionName, children) {
|
|
457
|
+
const app = useApp();
|
|
458
|
+
const declared = (resource === "" ? void 0 : findDeclared(app.registry.get(resource), actionName)) ?? {};
|
|
459
|
+
const label = useResolvedLabel(declared, actionName, children, resource);
|
|
460
|
+
return {
|
|
461
|
+
label,
|
|
462
|
+
labelText: typeof label === "string" ? label : humanizeKey(actionName),
|
|
463
|
+
icon: resolveActionIcon(declared, actionName),
|
|
464
|
+
destructive: declared.variant === "destructive"
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
//#endregion
|
|
468
|
+
//#region src/flows/flow-action-button.tsx
|
|
469
|
+
const DESTRUCTIVE_ITEM$1 = "text-destructive focus:bg-destructive/10 focus:text-destructive data-[highlighted]:text-destructive";
|
|
470
|
+
function FlowControl(props) {
|
|
471
|
+
const { state, chrome, testid, resource, actionName, scope, asMenuItem, density } = props;
|
|
472
|
+
const Anchor = useLinkComponent();
|
|
473
|
+
if (state === "absent") return null;
|
|
474
|
+
const buttonVariant = props.variant ?? (chrome.destructive ? "destructive" : "outline");
|
|
475
|
+
if (state === "disabled") return jsx(DisabledAction, {
|
|
476
|
+
reason: props.reason,
|
|
477
|
+
label: chrome.label,
|
|
478
|
+
icon: chrome.icon,
|
|
479
|
+
testid,
|
|
480
|
+
size: props.size,
|
|
481
|
+
variant: buttonVariant,
|
|
482
|
+
className: props.className,
|
|
483
|
+
asMenuItem,
|
|
484
|
+
resource,
|
|
485
|
+
density,
|
|
486
|
+
actionName,
|
|
487
|
+
surface: scope
|
|
488
|
+
});
|
|
489
|
+
const running = state === "running" || props.active === true;
|
|
490
|
+
const busy = state === "undecided" || state === "pending" || running;
|
|
491
|
+
if (props.href !== void 0 && !busy) {
|
|
492
|
+
if (asMenuItem === true) return jsx(DropdownMenuItem, {
|
|
493
|
+
asChild: true,
|
|
494
|
+
className: cn(chrome.destructive && DESTRUCTIVE_ITEM$1),
|
|
495
|
+
children: jsx(Anchor, {
|
|
496
|
+
href: props.href,
|
|
497
|
+
"data-testid": testid,
|
|
498
|
+
"data-buro-flow-state": state,
|
|
499
|
+
onClick: (event) => event.stopPropagation(),
|
|
500
|
+
children: jsx(ActionInner, {
|
|
501
|
+
icon: chrome.icon,
|
|
502
|
+
label: chrome.label
|
|
503
|
+
})
|
|
504
|
+
})
|
|
505
|
+
});
|
|
506
|
+
const linkChrome = resolveControlChrome({
|
|
507
|
+
density: density ?? "regular",
|
|
508
|
+
icon: chrome.icon,
|
|
509
|
+
label: chrome.label,
|
|
510
|
+
actionName,
|
|
511
|
+
surface: scope
|
|
512
|
+
});
|
|
513
|
+
return withActionTooltip(jsx(Button, {
|
|
514
|
+
asChild: true,
|
|
515
|
+
variant: buttonVariant,
|
|
516
|
+
size: linkChrome.size,
|
|
517
|
+
"aria-label": linkChrome.ariaLabel,
|
|
518
|
+
className: cn(linkChrome.className, props.className),
|
|
519
|
+
children: jsx(Anchor, {
|
|
520
|
+
href: props.href,
|
|
521
|
+
"data-testid": testid,
|
|
522
|
+
"data-buro-flow-state": state,
|
|
523
|
+
onClick: (event) => event.stopPropagation(),
|
|
524
|
+
children: linkChrome.content
|
|
525
|
+
})
|
|
526
|
+
}), chrome.label, linkChrome.compact);
|
|
527
|
+
}
|
|
528
|
+
if (asMenuItem === true) return jsx(DropdownMenuItem, {
|
|
529
|
+
"data-testid": testid,
|
|
530
|
+
"data-buro-flow-state": state,
|
|
531
|
+
disabled: busy,
|
|
532
|
+
"aria-busy": busy ? "true" : void 0,
|
|
533
|
+
className: cn(chrome.destructive && DESTRUCTIVE_ITEM$1),
|
|
534
|
+
onFocus: props.onIntent,
|
|
535
|
+
onPointerEnter: props.onIntent,
|
|
536
|
+
onSelect: () => {
|
|
537
|
+
if (!busy) props.onRun();
|
|
538
|
+
},
|
|
539
|
+
children: jsx(ActionInner, {
|
|
540
|
+
icon: chrome.icon,
|
|
541
|
+
label: chrome.label
|
|
542
|
+
})
|
|
543
|
+
});
|
|
544
|
+
const controlChrome = resolveControlChrome({
|
|
545
|
+
density: density ?? "regular",
|
|
546
|
+
icon: chrome.icon,
|
|
547
|
+
label: chrome.label,
|
|
548
|
+
actionName,
|
|
549
|
+
surface: scope
|
|
550
|
+
});
|
|
551
|
+
return jsxs(Fragment$1, { children: [withActionTooltip(jsxs(Button, {
|
|
552
|
+
type: "button",
|
|
553
|
+
variant: buttonVariant,
|
|
554
|
+
size: controlChrome.size,
|
|
555
|
+
"aria-label": controlChrome.ariaLabel,
|
|
556
|
+
"data-testid": testid,
|
|
557
|
+
"data-buro-flow-state": state,
|
|
558
|
+
disabled: busy,
|
|
559
|
+
"aria-busy": busy ? "true" : void 0,
|
|
560
|
+
onPointerEnter: props.onIntent,
|
|
561
|
+
onFocus: props.onIntent,
|
|
562
|
+
onClick: () => {
|
|
563
|
+
if (!busy) props.onRun();
|
|
564
|
+
},
|
|
565
|
+
className: cn(controlChrome.className, props.className),
|
|
566
|
+
children: [running ? jsx(IconLoader2, {
|
|
567
|
+
className: "h-4 w-4 animate-spin",
|
|
568
|
+
"aria-hidden": "true"
|
|
569
|
+
}) : null, controlChrome.content]
|
|
570
|
+
}), chrome.label, controlChrome.compact), jsx(DocInfo, {
|
|
571
|
+
address: `${resource}/actions/${actionName}`,
|
|
572
|
+
name: chrome.labelText,
|
|
573
|
+
testId: `${testid}:info`
|
|
574
|
+
})] });
|
|
575
|
+
}
|
|
576
|
+
function FlowActionButton(props) {
|
|
577
|
+
const { resource, actionName, record, scope = "row", children } = props;
|
|
578
|
+
const control = useFlowRowResolver(resource)(actionName, record);
|
|
579
|
+
const chrome = useFlowChrome(resource, actionName, children);
|
|
580
|
+
const tid = useTestIds();
|
|
581
|
+
const testid = makeTestId(scope, resource, actionName, record?.id ?? "", tid);
|
|
582
|
+
const [running, setRunning] = useState(false);
|
|
583
|
+
const runRecord = control.record ?? record;
|
|
584
|
+
const hosted = useHostedFlow(resource, actionName, runRecord, chrome.labelText);
|
|
585
|
+
const onRun = () => {
|
|
586
|
+
if (running) return;
|
|
587
|
+
if (hosted.pageHref !== void 0) return;
|
|
588
|
+
const closed = hosted.present();
|
|
589
|
+
if (closed !== null) {
|
|
590
|
+
setRunning(true);
|
|
591
|
+
closed.finally(() => setRunning(false));
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
if (!hosted.hasHost) setRunning(true);
|
|
595
|
+
};
|
|
596
|
+
return jsxs(Fragment$1, { children: [jsx(FlowControl, {
|
|
597
|
+
...props,
|
|
598
|
+
state: conjoinHostPage(control.state, hosted.hostPage),
|
|
599
|
+
...control.reason === void 0 ? {} : { reason: control.reason },
|
|
600
|
+
testid,
|
|
601
|
+
scope,
|
|
602
|
+
chrome,
|
|
603
|
+
active: running,
|
|
604
|
+
...hosted.pageHref === void 0 ? {} : { href: hosted.pageHref },
|
|
605
|
+
onRun,
|
|
606
|
+
onIntent: preloadFlowRuntime
|
|
607
|
+
}), running && !hosted.hasHost && hosted.pageHref === void 0 ? jsx(RuntimeFlowRunner, {
|
|
608
|
+
resource,
|
|
609
|
+
actionName,
|
|
610
|
+
...runRecord === void 0 ? {} : { record: runRecord },
|
|
611
|
+
scope,
|
|
612
|
+
onClose: () => setRunning(false)
|
|
613
|
+
}) : null] });
|
|
614
|
+
}
|
|
615
|
+
function useHostedFlow(resource, actionName, record, label) {
|
|
616
|
+
const app = useApp();
|
|
617
|
+
const def = app.registry.get(resource);
|
|
618
|
+
const compiled = useMemo(() => {
|
|
619
|
+
try {
|
|
620
|
+
return def === void 0 ? null : compileFlow(def, actionName);
|
|
621
|
+
} catch {
|
|
622
|
+
return null;
|
|
623
|
+
}
|
|
624
|
+
}, [def, actionName]);
|
|
625
|
+
const title = useProjectedTitle(resource, record?.id, record);
|
|
626
|
+
const presentFlow = usePresentFlow();
|
|
627
|
+
const hasHost = compiled?.hasHost === true;
|
|
628
|
+
const recordId = record?.[compiled?.idField ?? "id"] ?? record?.id;
|
|
629
|
+
const hostPage = useHostPage(compiled?.flow.host);
|
|
630
|
+
const pageHref = hostPage !== void 0 ? hostPage.access === "allowed" ? hostPage.href : void 0 : compiled?.flow.host === "page" && (typeof recordId === "string" || typeof recordId === "number") && String(recordId) !== "" ? hrefFor(app.registry, resource, {
|
|
631
|
+
action: "flow",
|
|
632
|
+
id: String(recordId),
|
|
633
|
+
flow: actionName
|
|
634
|
+
}) : void 0;
|
|
635
|
+
return {
|
|
636
|
+
hasHost,
|
|
637
|
+
pageHref,
|
|
638
|
+
hostPage,
|
|
639
|
+
present: () => !hasHost || compiled === null || pageHref !== void 0 ? null : presentFlow({
|
|
640
|
+
target: {
|
|
641
|
+
resource,
|
|
642
|
+
action: actionName
|
|
643
|
+
},
|
|
644
|
+
host: compiled.flow.host === "sheet" ? "sheet" : "dialog",
|
|
645
|
+
label,
|
|
646
|
+
...record === void 0 ? {} : { record },
|
|
647
|
+
...record === void 0 || title === "" ? {} : { title }
|
|
648
|
+
})
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
function FlowMenuItem(props) {
|
|
652
|
+
const chrome = useFlowChrome(props.resource, props.actionName);
|
|
653
|
+
const tid = useTestIds();
|
|
654
|
+
const testid = makeTestId(props.scope, props.resource, props.actionName, props.record?.id ?? "", tid);
|
|
655
|
+
const runRecord = props.control.record ?? props.record;
|
|
656
|
+
const hosted = useHostedFlow(props.resource, props.actionName, runRecord, chrome.labelText);
|
|
657
|
+
return jsx(FlowControl, {
|
|
658
|
+
state: conjoinHostPage(props.control.state, hosted.hostPage),
|
|
659
|
+
...props.control.reason === void 0 ? {} : { reason: props.control.reason },
|
|
660
|
+
resource: props.resource,
|
|
661
|
+
actionName: props.actionName,
|
|
662
|
+
testid,
|
|
663
|
+
scope: props.scope,
|
|
664
|
+
chrome,
|
|
665
|
+
asMenuItem: true,
|
|
666
|
+
...props.active === true ? { active: true } : {},
|
|
667
|
+
...hosted.pageHref === void 0 ? {} : { href: hosted.pageHref },
|
|
668
|
+
onRun: () => {
|
|
669
|
+
if (hosted.pageHref !== void 0) return;
|
|
670
|
+
if (hosted.present() === null && !hosted.hasHost) props.onRun();
|
|
671
|
+
},
|
|
672
|
+
onIntent: preloadFlowRuntime
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
//#endregion
|
|
676
|
+
//#region src/lib/external-href.ts
|
|
677
|
+
const EXTERNAL_HREF = /^(?:https?:|mailto:)/i;
|
|
678
|
+
function isExternalHref(href) {
|
|
679
|
+
return EXTERNAL_HREF.test(href);
|
|
680
|
+
}
|
|
681
|
+
function openExternal(href) {
|
|
682
|
+
window.open(href, "_blank", "noopener noreferrer");
|
|
683
|
+
}
|
|
684
|
+
function toBrowserHref(appPath, currentPath) {
|
|
685
|
+
if (typeof window === "undefined" || isExternalHref(appPath) || !appPath.startsWith("/")) return appPath;
|
|
686
|
+
const here = window.location.pathname;
|
|
687
|
+
if (currentPath === "") return appPath;
|
|
688
|
+
const basename = currentPath === "/" ? here.replace(/\/$/u, "") : here.endsWith(currentPath) ? here.slice(0, here.length - currentPath.length).replace(/\/$/u, "") : void 0;
|
|
689
|
+
if (basename === void 0) return appPath;
|
|
690
|
+
if (basename === "") return appPath;
|
|
691
|
+
return `${basename}${appPath}`;
|
|
692
|
+
}
|
|
693
|
+
function useToBrowserHref() {
|
|
694
|
+
const pathname = useOptionalRouterAdapter()?.pathname;
|
|
695
|
+
return useCallback((appPath) => pathname === void 0 ? appPath : toBrowserHref(appPath, pathname), [pathname]);
|
|
696
|
+
}
|
|
697
|
+
//#endregion
|
|
698
|
+
//#region src/list-kit/list-slots.ts
|
|
699
|
+
function canonicalResourceName(resource) {
|
|
700
|
+
return canonicalEntityNodeKey({
|
|
701
|
+
name: resource,
|
|
702
|
+
ref: normalizeEntityRef(resource)
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
function listSlot(region, resource) {
|
|
706
|
+
return `list.${region}.${canonicalResourceName(resource)}`;
|
|
707
|
+
}
|
|
708
|
+
function useListSlot(region, resource) {
|
|
709
|
+
return useComponent(listSlot(region, resource));
|
|
710
|
+
}
|
|
711
|
+
//#endregion
|
|
712
|
+
//#region src/actions/builtin-edit-route.ts
|
|
713
|
+
const LINK = Object.freeze({ kind: "link" });
|
|
714
|
+
const NONE = Object.freeze({ kind: "none" });
|
|
715
|
+
function sameResource(a, b) {
|
|
716
|
+
try {
|
|
717
|
+
return canonicalResourceName(a) === canonicalResourceName(b);
|
|
718
|
+
} catch {
|
|
719
|
+
return false;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
function useBuiltinEditRoute(action, resource, recordId) {
|
|
723
|
+
const app = useApp();
|
|
724
|
+
const entry = useQuickEntry();
|
|
725
|
+
const quick = useQuick();
|
|
726
|
+
const gate = useEntryGate();
|
|
727
|
+
if (!isBuiltinItemAction(action, "edit")) return LINK;
|
|
728
|
+
const def = app.registry.get(resource);
|
|
729
|
+
if (def === void 0) return LINK;
|
|
730
|
+
const entries = recordEntries(def);
|
|
731
|
+
const id = recordId === null || recordId === void 0 ? "" : String(recordId);
|
|
732
|
+
const top = entry?.stack.top;
|
|
733
|
+
if (entry !== null && top !== void 0 && top.mode === "show" && top.recordId === id && sameResource(top.resource, resource) && entries.quick.edit !== null) {
|
|
734
|
+
if (entries.quick.edit === entry.host) return {
|
|
735
|
+
kind: "quick",
|
|
736
|
+
open: () => {
|
|
737
|
+
if (gate(resource, "edit").state !== "open") return;
|
|
738
|
+
entry.scope.request(() => entry.stack.replaceTop({
|
|
739
|
+
...top,
|
|
740
|
+
mode: "edit"
|
|
741
|
+
}));
|
|
742
|
+
}
|
|
743
|
+
};
|
|
744
|
+
return {
|
|
745
|
+
kind: "quick",
|
|
746
|
+
open: () => void quick.edit(resource, id)
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
if (entries.page.edit) return LINK;
|
|
750
|
+
if (entries.quick.edit === null) return NONE;
|
|
751
|
+
return {
|
|
752
|
+
kind: "quick",
|
|
753
|
+
open: () => void quick.edit(resource, id)
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
//#endregion
|
|
757
|
+
//#region src/actions/action-button.tsx
|
|
758
|
+
function DisabledAction({ reason, label, icon, testid, size, variant, className, asMenuItem, resource, density = "regular", actionName, surface }) {
|
|
759
|
+
const i18nCaps = useI18nCaps(resource);
|
|
760
|
+
const text = reason ? resolveContentSlot(i18nCaps, [reason], {
|
|
761
|
+
resource,
|
|
762
|
+
default: reason
|
|
763
|
+
}) : resolveContentSlot(i18nCaps, ["availability.denied"], {
|
|
764
|
+
resource,
|
|
765
|
+
default: "This action isn't available"
|
|
766
|
+
});
|
|
767
|
+
const onClick = (e) => {
|
|
768
|
+
e.preventDefault();
|
|
769
|
+
e.stopPropagation();
|
|
770
|
+
toast(text);
|
|
771
|
+
};
|
|
772
|
+
if (asMenuItem) return jsx(DropdownMenuItem, {
|
|
773
|
+
"aria-disabled": "true",
|
|
774
|
+
"data-testid": testid,
|
|
775
|
+
onSelect: (e) => e.preventDefault(),
|
|
776
|
+
onClick,
|
|
777
|
+
className: "opacity-60",
|
|
778
|
+
children: jsx(ActionInner, {
|
|
779
|
+
icon,
|
|
780
|
+
label
|
|
781
|
+
})
|
|
782
|
+
});
|
|
783
|
+
const chrome = resolveControlChrome({
|
|
784
|
+
density,
|
|
785
|
+
icon,
|
|
786
|
+
label,
|
|
787
|
+
actionName: actionName ?? testid ?? "",
|
|
788
|
+
surface: surface ?? "unknown"
|
|
789
|
+
});
|
|
790
|
+
return withActionTooltip(jsx(Button, {
|
|
791
|
+
type: "button",
|
|
792
|
+
size: size ?? chrome.size,
|
|
793
|
+
variant: variant ?? "outline",
|
|
794
|
+
className: cn(chrome.className, className),
|
|
795
|
+
"data-testid": testid,
|
|
796
|
+
"aria-disabled": "true",
|
|
797
|
+
"aria-label": chrome.ariaLabel,
|
|
798
|
+
onClick,
|
|
799
|
+
children: chrome.content
|
|
800
|
+
}), label, chrome.compact, text);
|
|
801
|
+
}
|
|
802
|
+
const DESTRUCTIVE_ITEM = "text-destructive focus:bg-destructive/10 focus:text-destructive data-[highlighted]:text-destructive";
|
|
803
|
+
const DEFAULT_ACTION_ICONS = {
|
|
804
|
+
create: "Plus",
|
|
805
|
+
edit: "Pencil",
|
|
806
|
+
delete: "Trash2",
|
|
807
|
+
show: "Eye",
|
|
808
|
+
auditLog: "ScrollText",
|
|
809
|
+
refresh: "RefreshCw",
|
|
810
|
+
copy: "Copy",
|
|
811
|
+
cancel: "X"
|
|
812
|
+
};
|
|
813
|
+
function resolveActionIcon(action, actionName) {
|
|
814
|
+
return action.icon ?? DEFAULT_ACTION_ICONS[camelizeActionName(actionName)];
|
|
815
|
+
}
|
|
816
|
+
function camelizeActionName(actionName) {
|
|
817
|
+
return actionName.replace(/[-_]+([a-zA-Z0-9])/g, (_m, c) => c.toUpperCase());
|
|
818
|
+
}
|
|
819
|
+
function useResolvedLabel(action, actionName, children, resource) {
|
|
820
|
+
const caps = useI18nCaps(resource);
|
|
821
|
+
if (children != null) return children;
|
|
822
|
+
const raw = `actions.${actionName}`;
|
|
823
|
+
const own = `resources.${resource}.${raw}`;
|
|
824
|
+
const ownLabel = resolveContentSlot(caps, [[`${own}.label`, own]]);
|
|
825
|
+
if (ownLabel !== void 0) return ownLabel;
|
|
826
|
+
const declared = declaredText(action.kind === "flow" ? "flow" : "action", action, "label");
|
|
827
|
+
if (declared !== void 0) return declared;
|
|
828
|
+
const sharedLabel = resolveContentSlot(caps, [[`${raw}.label`, raw]]);
|
|
829
|
+
if (sharedLabel !== void 0) return sharedLabel;
|
|
830
|
+
const camel = `actions.${camelizeActionName(actionName)}`;
|
|
831
|
+
if (caps.has(camel)) return caps.t(camel);
|
|
832
|
+
return humanizeKey(actionName);
|
|
833
|
+
}
|
|
834
|
+
function findAction(actions, name) {
|
|
835
|
+
if (!Array.isArray(actions)) return void 0;
|
|
836
|
+
return actions.find((a) => "name" in a && a.name === name);
|
|
837
|
+
}
|
|
838
|
+
function actionResourceHandle(resource) {
|
|
839
|
+
return {
|
|
840
|
+
kind: "resource",
|
|
841
|
+
ref: normalizeEntityRef(resource)
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
function ActionButton(props) {
|
|
845
|
+
const def = useApp().registry.get(props.resource);
|
|
846
|
+
const scope = props.scope ?? "row";
|
|
847
|
+
const availability = useAvailabilityResolver(props.resource, modeForSurface(scope))(props.actionName, props.record);
|
|
848
|
+
const action = findAction(def?.actions?.item, props.actionName) ?? findAction(def?.actions?.resource, props.actionName);
|
|
849
|
+
if (!action) return null;
|
|
850
|
+
if (isFlowAction(action)) return jsx(FlowActionButton, { ...props });
|
|
851
|
+
if (availability.state === "absent" || availability.state === "hidden") return null;
|
|
852
|
+
if (!actionWhenHolds(action, props.record)) return null;
|
|
853
|
+
if (action.kind === "link") return jsx(LinkActionButton, {
|
|
854
|
+
...props,
|
|
855
|
+
action,
|
|
856
|
+
availability
|
|
857
|
+
});
|
|
858
|
+
if (action.kind === "modal") return jsx(ModalActionButton, {
|
|
859
|
+
...props,
|
|
860
|
+
action,
|
|
861
|
+
availability
|
|
862
|
+
});
|
|
863
|
+
return null;
|
|
864
|
+
}
|
|
865
|
+
function actionWhenHolds(action, record) {
|
|
866
|
+
const when = action.when;
|
|
867
|
+
if (when === void 0) return true;
|
|
868
|
+
return evaluateCondition(when, record === void 0 ? {} : { record: {
|
|
869
|
+
status: "ready",
|
|
870
|
+
value: record,
|
|
871
|
+
refreshing: false
|
|
872
|
+
} }, { mode: "show" }) === true;
|
|
873
|
+
}
|
|
874
|
+
function presentModalAction(args) {
|
|
875
|
+
const { present, action, resource, actionName, record, recordId, label } = args;
|
|
876
|
+
const result = present({
|
|
877
|
+
block: action.block,
|
|
878
|
+
props: action.props ?? {},
|
|
879
|
+
resource,
|
|
880
|
+
record,
|
|
881
|
+
host: action.host === "sheet" ? "sheet" : "dialog",
|
|
882
|
+
...typeof label === "string" ? { label } : {},
|
|
883
|
+
activation: { surface: {
|
|
884
|
+
type: "item",
|
|
885
|
+
resource: normalizeEntityRef(resource),
|
|
886
|
+
recordId,
|
|
887
|
+
record
|
|
888
|
+
} }
|
|
889
|
+
});
|
|
890
|
+
if (!result.ok) console.error(`[buro/ui-shadcn] modal action "${actionName}" failed to open block "${action.block.namespace}:${action.block.id}": ${result.code}`);
|
|
891
|
+
}
|
|
892
|
+
function makeTestId(scope, resource, actionName, recordId, tid) {
|
|
893
|
+
if (scope === "row") return tid.listRowAction(resource, recordId, actionName);
|
|
894
|
+
if (scope === "list" && recordId === "") return tid.listFlow(resource, actionName);
|
|
895
|
+
return tid.formAction(resource, scope, actionName);
|
|
896
|
+
}
|
|
897
|
+
const reportedDeadRoutes = new Set();
|
|
898
|
+
function deadRecordRoute(registry, action, to) {
|
|
899
|
+
if (isBuiltinItemAction(action) || to === null || typeof to !== "object") return null;
|
|
900
|
+
const target = to;
|
|
901
|
+
if (typeof target.resource !== "string") return null;
|
|
902
|
+
if (target.action !== "show" && target.action !== "edit" && target.action !== "create") return null;
|
|
903
|
+
let def;
|
|
904
|
+
try {
|
|
905
|
+
def = registry.get(target.resource);
|
|
906
|
+
} catch {
|
|
907
|
+
return null;
|
|
908
|
+
}
|
|
909
|
+
if (def === void 0 || hasRecordPage(def, target.action)) return null;
|
|
910
|
+
return {
|
|
911
|
+
resource: target.resource,
|
|
912
|
+
action: target.action
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
function reportDeadRecordRoute(name, resource, action) {
|
|
916
|
+
if (process.env.NODE_ENV === "production") return;
|
|
917
|
+
const key = `${name}\u0000${resource}\u0000${action}`;
|
|
918
|
+
if (reportedDeadRoutes.has(key)) return;
|
|
919
|
+
reportedDeadRoutes.add(key);
|
|
920
|
+
console.error(`[burojs] The action "${name}" links to ${resource}'s ${action} page, and ${resource} has none (views.show.page: false).`);
|
|
921
|
+
}
|
|
922
|
+
function LinkActionButton({ resource, actionName, record, scope = "row", variant, size, density, className, children, asMenuItem, action, availability }) {
|
|
923
|
+
const tid = useTestIds();
|
|
924
|
+
const app = useApp();
|
|
925
|
+
const toBrowser = useToBrowserHref();
|
|
926
|
+
const safeRecord = record ?? { id: "" };
|
|
927
|
+
const isDestructive = action.variant === "destructive";
|
|
928
|
+
const buttonVariant = variant ?? (isDestructive ? "destructive" : "outline");
|
|
929
|
+
const recordId = record?.id ?? "";
|
|
930
|
+
const testid = makeTestId(scope, resource, actionName, recordId, tid);
|
|
931
|
+
const label = useResolvedLabel(action, actionName, children, resource);
|
|
932
|
+
const editRoute = useBuiltinEditRoute(action, resource, recordId);
|
|
933
|
+
const to = typeof action.to === "function" ? action.to(buildDI(actionResourceHandle(resource), safeRecord)) : action.to;
|
|
934
|
+
const href = resolveRoute(to, app.registry);
|
|
935
|
+
const target = action.target ?? "_self";
|
|
936
|
+
const deadPage = deadRecordRoute(app.registry, action, to);
|
|
937
|
+
const deadResource = deadPage?.resource;
|
|
938
|
+
const deadAction = deadPage?.action;
|
|
939
|
+
useEffect(() => {
|
|
940
|
+
if (deadResource !== void 0 && deadAction !== void 0) reportDeadRecordRoute(actionName, deadResource, deadAction);
|
|
941
|
+
}, [
|
|
942
|
+
deadResource,
|
|
943
|
+
deadAction,
|
|
944
|
+
actionName
|
|
945
|
+
]);
|
|
946
|
+
const rel = target === "_blank" ? "noopener noreferrer" : void 0;
|
|
947
|
+
const RouterLink = useLinkComponent();
|
|
948
|
+
const Anchor = target === "_blank" || isExternalHref(href) ? "a" : RouterLink;
|
|
949
|
+
const anchorHref = target === "_blank" ? toBrowser(href) : href;
|
|
950
|
+
if (editRoute.kind === "none") return null;
|
|
951
|
+
if (availability.state === "disabled") return jsx(DisabledAction, {
|
|
952
|
+
reason: availability.reason,
|
|
953
|
+
label,
|
|
954
|
+
icon: resolveActionIcon(action, actionName),
|
|
955
|
+
testid,
|
|
956
|
+
size,
|
|
957
|
+
variant: buttonVariant,
|
|
958
|
+
className,
|
|
959
|
+
asMenuItem,
|
|
960
|
+
resource,
|
|
961
|
+
density,
|
|
962
|
+
actionName,
|
|
963
|
+
surface: scope
|
|
964
|
+
});
|
|
965
|
+
if (editRoute.kind === "quick") {
|
|
966
|
+
if (asMenuItem) return jsx(DropdownMenuItem, {
|
|
967
|
+
"data-testid": testid,
|
|
968
|
+
onSelect: () => editRoute.open(),
|
|
969
|
+
children: jsx(ActionInner, {
|
|
970
|
+
icon: resolveActionIcon(action, actionName),
|
|
971
|
+
label
|
|
972
|
+
})
|
|
973
|
+
});
|
|
974
|
+
const quickChrome = resolveControlChrome({
|
|
975
|
+
density: density ?? "regular",
|
|
976
|
+
icon: resolveActionIcon(action, actionName),
|
|
977
|
+
label,
|
|
978
|
+
actionName,
|
|
979
|
+
surface: scope
|
|
980
|
+
});
|
|
981
|
+
return withActionTooltip(jsx(Button, {
|
|
982
|
+
type: "button",
|
|
983
|
+
variant: buttonVariant,
|
|
984
|
+
size: quickChrome.size,
|
|
985
|
+
"aria-label": quickChrome.ariaLabel,
|
|
986
|
+
className: cn(quickChrome.className, className),
|
|
987
|
+
"data-testid": testid,
|
|
988
|
+
onClick: (e) => {
|
|
989
|
+
e.stopPropagation();
|
|
990
|
+
editRoute.open();
|
|
991
|
+
},
|
|
992
|
+
children: quickChrome.content
|
|
993
|
+
}), label, quickChrome.compact);
|
|
994
|
+
}
|
|
995
|
+
if (asMenuItem) return jsx(DropdownMenuItem, {
|
|
996
|
+
asChild: true,
|
|
997
|
+
className: cn(isDestructive && DESTRUCTIVE_ITEM),
|
|
998
|
+
children: jsx(Anchor, {
|
|
999
|
+
href: anchorHref,
|
|
1000
|
+
target,
|
|
1001
|
+
rel,
|
|
1002
|
+
"data-testid": testid,
|
|
1003
|
+
onClick: (e) => e.stopPropagation(),
|
|
1004
|
+
children: jsx(ActionInner, {
|
|
1005
|
+
icon: resolveActionIcon(action, actionName),
|
|
1006
|
+
label
|
|
1007
|
+
})
|
|
1008
|
+
})
|
|
1009
|
+
});
|
|
1010
|
+
const chrome = resolveControlChrome({
|
|
1011
|
+
density: density ?? "regular",
|
|
1012
|
+
icon: resolveActionIcon(action, actionName),
|
|
1013
|
+
label,
|
|
1014
|
+
actionName,
|
|
1015
|
+
surface: scope
|
|
1016
|
+
});
|
|
1017
|
+
return withActionTooltip(jsx(Button, {
|
|
1018
|
+
asChild: true,
|
|
1019
|
+
variant: buttonVariant,
|
|
1020
|
+
size: chrome.size,
|
|
1021
|
+
"aria-label": chrome.ariaLabel,
|
|
1022
|
+
className: cn(chrome.className, className),
|
|
1023
|
+
children: jsx(Anchor, {
|
|
1024
|
+
href: anchorHref,
|
|
1025
|
+
target,
|
|
1026
|
+
rel,
|
|
1027
|
+
"data-testid": testid,
|
|
1028
|
+
onClick: (e) => e.stopPropagation(),
|
|
1029
|
+
children: chrome.content
|
|
1030
|
+
})
|
|
1031
|
+
}), label, chrome.compact);
|
|
1032
|
+
}
|
|
1033
|
+
function ModalActionButton({ resource, actionName, record, scope = "row", variant, size, density, className, children, asMenuItem, action, availability }) {
|
|
1034
|
+
const tid = useTestIds();
|
|
1035
|
+
const present = usePresentBlock();
|
|
1036
|
+
const safeRecord = record ?? { id: "" };
|
|
1037
|
+
const isDestructive = action.variant === "destructive";
|
|
1038
|
+
const buttonVariant = variant ?? (isDestructive ? "destructive" : "outline");
|
|
1039
|
+
const recordId = record?.id ?? "";
|
|
1040
|
+
const testid = makeTestId(scope, resource, actionName, recordId, tid);
|
|
1041
|
+
const label = useResolvedLabel(action, actionName, children, resource);
|
|
1042
|
+
if (availability.state === "disabled") return jsx(DisabledAction, {
|
|
1043
|
+
reason: availability.reason,
|
|
1044
|
+
label,
|
|
1045
|
+
icon: resolveActionIcon(action, actionName),
|
|
1046
|
+
testid,
|
|
1047
|
+
size,
|
|
1048
|
+
variant: buttonVariant,
|
|
1049
|
+
className,
|
|
1050
|
+
asMenuItem,
|
|
1051
|
+
resource,
|
|
1052
|
+
density,
|
|
1053
|
+
actionName,
|
|
1054
|
+
surface: scope
|
|
1055
|
+
});
|
|
1056
|
+
function openModal() {
|
|
1057
|
+
presentModalAction({
|
|
1058
|
+
present,
|
|
1059
|
+
action,
|
|
1060
|
+
resource,
|
|
1061
|
+
actionName,
|
|
1062
|
+
record: safeRecord,
|
|
1063
|
+
recordId,
|
|
1064
|
+
label
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1067
|
+
if (asMenuItem) return jsx(DropdownMenuItem, {
|
|
1068
|
+
"data-testid": testid,
|
|
1069
|
+
className: cn(isDestructive && DESTRUCTIVE_ITEM),
|
|
1070
|
+
onSelect: openModal,
|
|
1071
|
+
children: jsx(ActionInner, {
|
|
1072
|
+
icon: resolveActionIcon(action, actionName),
|
|
1073
|
+
label
|
|
1074
|
+
})
|
|
1075
|
+
});
|
|
1076
|
+
const chrome = resolveControlChrome({
|
|
1077
|
+
density: density ?? "regular",
|
|
1078
|
+
icon: resolveActionIcon(action, actionName),
|
|
1079
|
+
label,
|
|
1080
|
+
actionName,
|
|
1081
|
+
surface: scope
|
|
1082
|
+
});
|
|
1083
|
+
return withActionTooltip(jsx(Button, {
|
|
1084
|
+
type: "button",
|
|
1085
|
+
variant: buttonVariant,
|
|
1086
|
+
size: chrome.size,
|
|
1087
|
+
"aria-label": chrome.ariaLabel,
|
|
1088
|
+
"data-testid": testid,
|
|
1089
|
+
onClick: (e) => {
|
|
1090
|
+
e.stopPropagation();
|
|
1091
|
+
openModal();
|
|
1092
|
+
},
|
|
1093
|
+
className: cn(chrome.className, className),
|
|
1094
|
+
children: chrome.content
|
|
1095
|
+
}), label, chrome.compact);
|
|
1096
|
+
}
|
|
1097
|
+
//#endregion
|
|
1098
|
+
//#region src/checks/check-confirm-dialog.tsx
|
|
1099
|
+
function translateOrFallback(t, key, fallback, params, resource) {
|
|
1100
|
+
const translated = t(key, {
|
|
1101
|
+
params,
|
|
1102
|
+
...resource === void 0 ? {} : { resource }
|
|
1103
|
+
});
|
|
1104
|
+
return translated === key ? fallback : translated;
|
|
1105
|
+
}
|
|
1106
|
+
function issueText(issue, t, resource) {
|
|
1107
|
+
const params = issue.params;
|
|
1108
|
+
const checkId = params?.checkId;
|
|
1109
|
+
const ownCheck = typeof checkId === "string" && issue.message.key === `checks.${checkId}`;
|
|
1110
|
+
const declared = typeof issue.message.default === "string" ? issue.message.default : ownCheck ? void 0 : issue.message.key;
|
|
1111
|
+
if (issue.params?.resolved === true) return declared ?? issue.message.key;
|
|
1112
|
+
if (!ownCheck) return translateOrFallback(t, issue.message.key, declared ?? "", params, resource);
|
|
1113
|
+
const miss = "\0";
|
|
1114
|
+
const bundled = [`${issue.message.key}.message`, issue.message.key].map((key) => translateOrFallback(t, key, miss, params, resource)).find((text) => text !== miss);
|
|
1115
|
+
if (bundled !== void 0) return bundled;
|
|
1116
|
+
if (declared !== void 0) return declared;
|
|
1117
|
+
const name = String(params?.name ?? checkId);
|
|
1118
|
+
return translateOrFallback(t, FRAMEWORK_TEXT_KEYS.checkFailed, checkFailedDefault(name), {
|
|
1119
|
+
...params,
|
|
1120
|
+
name
|
|
1121
|
+
}, void 0);
|
|
1122
|
+
}
|
|
1123
|
+
function checkIdOf(issue, index) {
|
|
1124
|
+
const id = issue.params?.checkId;
|
|
1125
|
+
return typeof id === "string" ? id : `${index}`;
|
|
1126
|
+
}
|
|
1127
|
+
const SEVERITY_CLASSNAME = {
|
|
1128
|
+
error: "text-destructive",
|
|
1129
|
+
warning: "text-warning-emphasis",
|
|
1130
|
+
info: "text-muted-foreground"
|
|
1131
|
+
};
|
|
1132
|
+
function ChecksList({ issues, testid, onRowClick, resource }) {
|
|
1133
|
+
const t = useOptionalT();
|
|
1134
|
+
return issues.map((issue, index) => {
|
|
1135
|
+
const checkId = checkIdOf(issue, index);
|
|
1136
|
+
const anchor = issue.path[0];
|
|
1137
|
+
const anchorable = typeof anchor === "string";
|
|
1138
|
+
const text = issueText(issue, t, resource);
|
|
1139
|
+
const className = cn("text-sm", SEVERITY_CLASSNAME[issue.severity]);
|
|
1140
|
+
return anchorable ? jsx("li", { children: jsx("button", {
|
|
1141
|
+
type: "button",
|
|
1142
|
+
"data-testid": `${testid}:item:${checkId}`,
|
|
1143
|
+
className: cn(className, "text-left underline-offset-2 hover:underline"),
|
|
1144
|
+
onClick: () => onRowClick(anchor),
|
|
1145
|
+
children: text
|
|
1146
|
+
}) }, `${checkId}-${index}`) : jsx("li", {
|
|
1147
|
+
"data-testid": `${testid}:item:${checkId}`,
|
|
1148
|
+
className,
|
|
1149
|
+
children: text
|
|
1150
|
+
}, `${checkId}-${index}`);
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1153
|
+
function CheckConfirmDialog({ state, testid, onAnchorFocus, title, proceedLabel, tone }) {
|
|
1154
|
+
const t = useOptionalT();
|
|
1155
|
+
const { resource, open, busy, header, blockers, confirmable, proceed, cancel } = state;
|
|
1156
|
+
const blocked = blockers.length > 0;
|
|
1157
|
+
const bound = resource === void 0 ? {} : { resource };
|
|
1158
|
+
const heading = title ?? t("checks.dialogTitle", {
|
|
1159
|
+
defaultValue: "Please review",
|
|
1160
|
+
...bound
|
|
1161
|
+
});
|
|
1162
|
+
const restoreFocusRef = useRef(null);
|
|
1163
|
+
useEffect(() => {
|
|
1164
|
+
if (typeof document === "undefined") return;
|
|
1165
|
+
if (open) {
|
|
1166
|
+
const active = document.activeElement;
|
|
1167
|
+
if (active instanceof HTMLElement && active !== document.body) restoreFocusRef.current = active;
|
|
1168
|
+
return;
|
|
1169
|
+
}
|
|
1170
|
+
const target = restoreFocusRef.current;
|
|
1171
|
+
restoreFocusRef.current = null;
|
|
1172
|
+
if (!target || !document.contains(target)) return;
|
|
1173
|
+
const id = requestAnimationFrame(() => target.focus());
|
|
1174
|
+
return () => cancelAnimationFrame(id);
|
|
1175
|
+
}, [open]);
|
|
1176
|
+
const onRowClick = (field) => {
|
|
1177
|
+
onAnchorFocus?.(field);
|
|
1178
|
+
cancel();
|
|
1179
|
+
};
|
|
1180
|
+
return jsx(Dialog, {
|
|
1181
|
+
open,
|
|
1182
|
+
onOpenChange: (next) => {
|
|
1183
|
+
if (!next) cancel();
|
|
1184
|
+
},
|
|
1185
|
+
children: jsxs(DialogContent, {
|
|
1186
|
+
"data-testid": `${testid}:dialog`,
|
|
1187
|
+
className: "flex max-h-[85vh] flex-col overflow-hidden",
|
|
1188
|
+
children: [
|
|
1189
|
+
jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: heading }), header !== null ? jsx(DialogDescription, { children: header }) : jsx(DialogDescription, {
|
|
1190
|
+
className: "sr-only",
|
|
1191
|
+
children: heading
|
|
1192
|
+
})] }),
|
|
1193
|
+
jsxs("ul", {
|
|
1194
|
+
className: "flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto overscroll-contain",
|
|
1195
|
+
children: [
|
|
1196
|
+
blocked && jsx("li", {
|
|
1197
|
+
className: "text-xs font-semibold uppercase tracking-wider text-destructive",
|
|
1198
|
+
children: t("checks.blocked", {
|
|
1199
|
+
defaultValue: "Cannot continue",
|
|
1200
|
+
...bound
|
|
1201
|
+
})
|
|
1202
|
+
}),
|
|
1203
|
+
jsx(ChecksList, {
|
|
1204
|
+
issues: blockers,
|
|
1205
|
+
testid,
|
|
1206
|
+
onRowClick,
|
|
1207
|
+
resource
|
|
1208
|
+
}),
|
|
1209
|
+
jsx(ChecksList, {
|
|
1210
|
+
issues: confirmable,
|
|
1211
|
+
testid,
|
|
1212
|
+
onRowClick,
|
|
1213
|
+
resource
|
|
1214
|
+
})
|
|
1215
|
+
]
|
|
1216
|
+
}),
|
|
1217
|
+
jsxs(DialogFooter, { children: [jsx(Button, {
|
|
1218
|
+
type: "button",
|
|
1219
|
+
variant: "outline",
|
|
1220
|
+
onClick: cancel,
|
|
1221
|
+
"data-testid": `${testid}:cancel`,
|
|
1222
|
+
disabled: busy,
|
|
1223
|
+
children: t("checks.back", {
|
|
1224
|
+
defaultValue: "Go back",
|
|
1225
|
+
...bound
|
|
1226
|
+
})
|
|
1227
|
+
}), !blocked && jsx(Button, {
|
|
1228
|
+
type: "button",
|
|
1229
|
+
variant: tone === "destructive" ? "destructive" : "default",
|
|
1230
|
+
onClick: proceed,
|
|
1231
|
+
"data-testid": `${testid}:proceed`,
|
|
1232
|
+
disabled: busy,
|
|
1233
|
+
children: proceedLabel ?? t("checks.proceed", {
|
|
1234
|
+
defaultValue: "Continue anyway",
|
|
1235
|
+
...bound
|
|
1236
|
+
})
|
|
1237
|
+
})] })
|
|
1238
|
+
]
|
|
1239
|
+
})
|
|
1240
|
+
});
|
|
1241
|
+
}
|
|
1242
|
+
//#endregion
|
|
1243
|
+
export { hasIcon as A, DropdownMenuRadioItem as B, loadFailedText as C, preloadFlowRuntime as D, loadPageFlowRoute as E, DropdownMenuGroup as F, DropdownMenuSubTrigger as G, DropdownMenuShortcut as H, DropdownMenuItem as I, TooltipContent as J, DropdownMenuTrigger as K, DropdownMenuLabel as L, DropdownMenu as M, DropdownMenuCheckboxItem as N, useLoaded as O, DropdownMenuContent as P, DropdownMenuPortal as R, loadBulkSummary as S, loadInlineFlowHost as T, DropdownMenuSub as U, DropdownMenuSeparator as V, DropdownMenuSubContent as W, TooltipTrigger as X, TooltipProvider as Y, DocInfo as Z, FlowBoundary as _, modeForSurface as a, RuntimeBulkFlowRunner as b, useListSlot as c, useToBrowserHref as d, FlowActionButton as f, ActionInner as g, useFlowChrome as h, makeTestId as i, registerIcons as j, Icon as k, isExternalHref as l, text as m, ActionButton as n, canonicalResourceName as o, FlowMenuItem as p, Tooltip as q, DisabledAction as r, listSlot as s, CheckConfirmDialog as t, openExternal as u, FlowLoadFailed as v, loadFlowScreen as w, RuntimeFlowRunner as x, RuntimeAppFlowRunner as y, DropdownMenuRadioGroup as z };
|
|
1244
|
+
|
|
1245
|
+
//# sourceMappingURL=check-confirm-dialog-bRT8lt0d.js.map
|