@almadar/ui 5.160.0 → 5.162.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/dist/avl/index.cjs +924 -891
- package/dist/avl/index.js +914 -881
- package/dist/components/index.cjs +944 -934
- package/dist/components/index.d.cts +156 -31
- package/dist/components/index.d.ts +156 -31
- package/dist/components/index.js +931 -924
- package/dist/hooks/index.cjs +25 -49
- package/dist/hooks/index.js +25 -49
- package/dist/locales/index.cjs +75 -147
- package/dist/locales/index.js +75 -147
- package/dist/providers/index.cjs +911 -882
- package/dist/providers/index.js +901 -872
- package/dist/runtime/index.cjs +920 -887
- package/dist/runtime/index.js +910 -877
- package/locales/ar.json +25 -49
- package/locales/en.json +25 -49
- package/locales/sl.json +25 -49
- package/package.json +5 -5
package/dist/avl/index.cjs
CHANGED
|
@@ -28612,8 +28612,9 @@ function DataGrid({
|
|
|
28612
28612
|
const titleField = fieldDefs.find((f3) => f3.variant === "h3" || f3.variant === "h4") ?? fieldDefs[0];
|
|
28613
28613
|
const badgeFields = fieldDefs.filter((f3) => f3.variant === "badge" && f3 !== titleField);
|
|
28614
28614
|
const bodyFields = fieldDefs.filter((f3) => f3 !== titleField && !badgeFields.includes(f3));
|
|
28615
|
-
const
|
|
28616
|
-
const
|
|
28615
|
+
const inlineCap = Math.min(maxInlineActions ?? 1, 1);
|
|
28616
|
+
const inlineCardActions = actionDefs.filter((a) => a.variant === "primary").slice(0, inlineCap);
|
|
28617
|
+
const menuCardActions = actionDefs.filter((a) => !inlineCardActions.includes(a));
|
|
28617
28618
|
const fireAction = (action, itemData) => {
|
|
28618
28619
|
if (action.navigatesTo) {
|
|
28619
28620
|
const url = action.navigatesTo.replace(
|
|
@@ -28633,6 +28634,9 @@ function DataGrid({
|
|
|
28633
28634
|
e.stopPropagation();
|
|
28634
28635
|
fireAction(action, itemData);
|
|
28635
28636
|
};
|
|
28637
|
+
const cardClickAction = actionDefs.find((a) => a.variant !== "danger");
|
|
28638
|
+
const handleCardClick = cardClickAction ? (itemData) => () => fireAction(cardClickAction, itemData) : void 0;
|
|
28639
|
+
const stopCardClick = handleCardClick ? (e) => e.stopPropagation() : void 0;
|
|
28636
28640
|
const hasRenderProp = typeof children === "function";
|
|
28637
28641
|
React94.useEffect(() => {
|
|
28638
28642
|
if (data.length > 0 && !hasRenderProp && fieldDefs.length === 0) {
|
|
@@ -28704,55 +28708,40 @@ function DataGrid({
|
|
|
28704
28708
|
{
|
|
28705
28709
|
"data-entity-row": true,
|
|
28706
28710
|
"data-entity-id": id,
|
|
28707
|
-
|
|
28711
|
+
onClick: handleCardClick?.(itemData),
|
|
28712
|
+
className: cn("relative group/rowactions", handleCardClick && "cursor-pointer", isSelected && "ring-2 ring-primary rounded-lg"),
|
|
28708
28713
|
children: [
|
|
28709
28714
|
children(itemData, index),
|
|
28710
|
-
actionDefs.length > 0 && /* @__PURE__ */ jsxRuntime.
|
|
28711
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
28712
|
-
|
|
28713
|
-
|
|
28714
|
-
|
|
28715
|
-
|
|
28716
|
-
|
|
28717
|
-
|
|
28718
|
-
|
|
28719
|
-
|
|
28720
|
-
|
|
28721
|
-
|
|
28722
|
-
|
|
28723
|
-
|
|
28724
|
-
|
|
28725
|
-
|
|
28726
|
-
|
|
28727
|
-
)),
|
|
28728
|
-
maxInlineActions != null && actionDefs.length > maxInlineActions && /* @__PURE__ */ jsxRuntime.jsx(
|
|
28729
|
-
Menu,
|
|
28730
|
-
{
|
|
28731
|
-
position: "bottom-end",
|
|
28732
|
-
trigger: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
|
|
28733
|
-
items: actionDefs.slice(maxInlineActions).map((action) => ({
|
|
28734
|
-
label: action.label,
|
|
28735
|
-
icon: action.icon,
|
|
28736
|
-
event: action.event,
|
|
28737
|
-
onClick: () => fireAction(action, itemData)
|
|
28738
|
-
}))
|
|
28739
|
-
}
|
|
28740
|
-
)
|
|
28741
|
-
] }),
|
|
28742
|
-
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "hidden [@media(pointer:coarse)]:block rounded-md border border-border bg-card/95 backdrop-blur-sm shadow-sm p-0.5", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
28715
|
+
actionDefs.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Box, { onClick: stopCardClick, className: "absolute top-2 right-2 z-10 opacity-0 group-hover/rowactions:opacity-100 focus-within:opacity-100 [@media(pointer:coarse)]:opacity-100 transition-opacity duration-fast", children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "rounded-md border border-border bg-card/95 backdrop-blur-sm shadow-sm p-0.5", children: [
|
|
28716
|
+
inlineCardActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
28717
|
+
Button,
|
|
28718
|
+
{
|
|
28719
|
+
variant: "primary",
|
|
28720
|
+
size: "sm",
|
|
28721
|
+
onClick: handleActionClick(action, itemData),
|
|
28722
|
+
"data-testid": `action-${action.event}`,
|
|
28723
|
+
"data-row-id": String(itemData.id),
|
|
28724
|
+
children: [
|
|
28725
|
+
action.icon && renderIconInput(action.icon, { size: "xs", className: "mr-1" }),
|
|
28726
|
+
action.label
|
|
28727
|
+
]
|
|
28728
|
+
},
|
|
28729
|
+
idx
|
|
28730
|
+
)),
|
|
28731
|
+
menuCardActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
28743
28732
|
Menu,
|
|
28744
28733
|
{
|
|
28745
28734
|
position: "bottom-end",
|
|
28746
28735
|
trigger: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
|
|
28747
|
-
items:
|
|
28736
|
+
items: menuCardActions.map((action) => ({
|
|
28748
28737
|
label: action.label,
|
|
28749
28738
|
icon: action.icon,
|
|
28750
|
-
|
|
28739
|
+
variant: action.variant === "danger" ? "danger" : "default",
|
|
28751
28740
|
onClick: () => fireAction(action, itemData)
|
|
28752
28741
|
}))
|
|
28753
28742
|
}
|
|
28754
|
-
)
|
|
28755
|
-
] })
|
|
28743
|
+
)
|
|
28744
|
+
] }) })
|
|
28756
28745
|
]
|
|
28757
28746
|
},
|
|
28758
28747
|
id
|
|
@@ -28766,12 +28755,14 @@ function DataGrid({
|
|
|
28766
28755
|
{
|
|
28767
28756
|
"data-entity-row": true,
|
|
28768
28757
|
"data-entity-id": id,
|
|
28758
|
+
onClick: handleCardClick?.(itemData),
|
|
28769
28759
|
className: cn(
|
|
28770
28760
|
"bg-card rounded-lg",
|
|
28771
28761
|
"border border-border",
|
|
28772
28762
|
"shadow-elevation-card hover:shadow-elevation-dialog",
|
|
28773
28763
|
"hover:border-primary transition-all",
|
|
28774
28764
|
"flex flex-col",
|
|
28765
|
+
handleCardClick && "cursor-pointer",
|
|
28775
28766
|
isSelected && "ring-2 ring-primary border-primary"
|
|
28776
28767
|
),
|
|
28777
28768
|
children: [
|
|
@@ -28821,53 +28812,35 @@ function DataGrid({
|
|
|
28821
28812
|
] }, field.name);
|
|
28822
28813
|
}) })
|
|
28823
28814
|
] }),
|
|
28824
|
-
|
|
28825
|
-
|
|
28815
|
+
actionDefs.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", onClick: stopCardClick, className: "flex-shrink-0", children: [
|
|
28816
|
+
inlineCardActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
28826
28817
|
Button,
|
|
28827
28818
|
{
|
|
28828
|
-
variant:
|
|
28819
|
+
variant: "primary",
|
|
28829
28820
|
size: "sm",
|
|
28830
28821
|
onClick: handleActionClick(action, itemData),
|
|
28831
28822
|
"data-testid": `action-${action.event}`,
|
|
28832
28823
|
"data-row-id": String(itemData.id),
|
|
28833
28824
|
"aria-label": action.label,
|
|
28834
28825
|
title: action.label,
|
|
28835
|
-
className: cn(
|
|
28836
|
-
action.variant === "primary" ? void 0 : "text-muted-foreground hover:text-foreground",
|
|
28837
|
-
action.icon && "px-2"
|
|
28838
|
-
),
|
|
28826
|
+
className: cn(action.icon && "px-2"),
|
|
28839
28827
|
children: action.icon ? renderIconInput(action.icon, { size: "xs" }) : action.label
|
|
28840
28828
|
},
|
|
28841
28829
|
idx
|
|
28842
28830
|
)),
|
|
28843
|
-
|
|
28831
|
+
menuCardActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
28844
28832
|
Menu,
|
|
28845
28833
|
{
|
|
28846
28834
|
position: "bottom-end",
|
|
28847
28835
|
trigger: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
|
|
28848
|
-
items:
|
|
28836
|
+
items: menuCardActions.map((action) => ({
|
|
28849
28837
|
label: action.label,
|
|
28850
28838
|
icon: action.icon,
|
|
28851
|
-
|
|
28839
|
+
variant: action.variant === "danger" ? "danger" : "default",
|
|
28852
28840
|
onClick: () => fireAction(action, itemData)
|
|
28853
28841
|
}))
|
|
28854
28842
|
}
|
|
28855
|
-
)
|
|
28856
|
-
dangerActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
28857
|
-
Button,
|
|
28858
|
-
{
|
|
28859
|
-
variant: "ghost",
|
|
28860
|
-
size: "sm",
|
|
28861
|
-
onClick: handleActionClick(action, itemData),
|
|
28862
|
-
"data-testid": `action-${action.event}`,
|
|
28863
|
-
"data-row-id": String(itemData.id),
|
|
28864
|
-
"aria-label": action.label,
|
|
28865
|
-
title: action.label,
|
|
28866
|
-
className: "text-error hover:text-error hover:bg-error/10 px-2",
|
|
28867
|
-
children: action.icon ? renderIconInput(action.icon, { size: "xs" }) : action.label
|
|
28868
|
-
},
|
|
28869
|
-
`danger-${idx}`
|
|
28870
|
-
))
|
|
28843
|
+
)
|
|
28871
28844
|
] })
|
|
28872
28845
|
] }) }),
|
|
28873
28846
|
bodyFields.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "px-4 pt-2 pb-4 flex-1", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
|
|
@@ -29109,54 +29082,59 @@ function DataList({
|
|
|
29109
29082
|
if (!itemActions || itemActions.length === 0) return null;
|
|
29110
29083
|
const inline = maxInlineActions != null ? itemActions.slice(0, maxInlineActions) : itemActions;
|
|
29111
29084
|
const overflow = maxInlineActions != null ? itemActions.slice(maxInlineActions) : [];
|
|
29112
|
-
return
|
|
29113
|
-
|
|
29114
|
-
|
|
29115
|
-
|
|
29116
|
-
|
|
29117
|
-
|
|
29118
|
-
|
|
29119
|
-
|
|
29120
|
-
|
|
29121
|
-
|
|
29122
|
-
action
|
|
29123
|
-
|
|
29124
|
-
|
|
29125
|
-
|
|
29126
|
-
|
|
29127
|
-
|
|
29128
|
-
|
|
29129
|
-
|
|
29130
|
-
|
|
29131
|
-
|
|
29132
|
-
|
|
29133
|
-
|
|
29134
|
-
|
|
29135
|
-
|
|
29136
|
-
|
|
29137
|
-
|
|
29138
|
-
|
|
29139
|
-
|
|
29140
|
-
|
|
29141
|
-
|
|
29142
|
-
|
|
29143
|
-
|
|
29144
|
-
|
|
29145
|
-
|
|
29146
|
-
|
|
29147
|
-
|
|
29148
|
-
|
|
29149
|
-
|
|
29150
|
-
|
|
29151
|
-
|
|
29085
|
+
return (
|
|
29086
|
+
// stopPropagation at the cluster: the "⋯" trigger opens the overflow
|
|
29087
|
+
// menu without also firing the row's default click (inline buttons
|
|
29088
|
+
// already stop it in handleActionClick; the Menu panel is portaled).
|
|
29089
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", onClick: rowClickEvent ? (e) => e.stopPropagation() : void 0, className: "flex-shrink-0", children: [
|
|
29090
|
+
inline.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
29091
|
+
Button,
|
|
29092
|
+
{
|
|
29093
|
+
variant: action.variant ?? "ghost",
|
|
29094
|
+
size: "sm",
|
|
29095
|
+
onClick: handleActionClick(action, itemData),
|
|
29096
|
+
"data-testid": `action-${action.event}`,
|
|
29097
|
+
"data-row-id": String(itemData.id),
|
|
29098
|
+
className: cn(
|
|
29099
|
+
action.variant === "danger" && "text-error hover:bg-error/10",
|
|
29100
|
+
// Must sit on the Button itself: the variant's own text colour
|
|
29101
|
+
// beats an inherited one from the row wrapper.
|
|
29102
|
+
onPrimary && "!text-primary-foreground hover:bg-primary-foreground/15"
|
|
29103
|
+
),
|
|
29104
|
+
children: [
|
|
29105
|
+
action.icon && renderIconInput2(action.icon, { size: "xs", className: "mr-1" }),
|
|
29106
|
+
action.label
|
|
29107
|
+
]
|
|
29108
|
+
},
|
|
29109
|
+
idx
|
|
29110
|
+
)),
|
|
29111
|
+
overflow.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
29112
|
+
Menu,
|
|
29113
|
+
{
|
|
29114
|
+
position: "bottom-end",
|
|
29115
|
+
trigger: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
|
|
29116
|
+
items: overflow.map((action) => ({
|
|
29117
|
+
label: action.label,
|
|
29118
|
+
icon: action.icon,
|
|
29119
|
+
variant: action.variant === "danger" ? "danger" : "default",
|
|
29120
|
+
onClick: () => eventBus.emit(`UI:${action.event}`, {
|
|
29121
|
+
id: itemData.id,
|
|
29122
|
+
row: itemData
|
|
29123
|
+
})
|
|
29124
|
+
}))
|
|
29125
|
+
}
|
|
29126
|
+
)
|
|
29127
|
+
] })
|
|
29128
|
+
);
|
|
29152
29129
|
};
|
|
29130
|
+
const rowClickEvent = itemClickEvent || itemActions?.find((a) => a.variant !== "danger")?.event;
|
|
29153
29131
|
const handleRowClick = (itemData) => () => {
|
|
29154
|
-
if (!
|
|
29132
|
+
if (!rowClickEvent) return;
|
|
29155
29133
|
const payload = {
|
|
29156
29134
|
id: itemData.id,
|
|
29157
29135
|
row: itemData
|
|
29158
29136
|
};
|
|
29159
|
-
eventBus.emit(`UI:${
|
|
29137
|
+
eventBus.emit(`UI:${rowClickEvent}`, payload);
|
|
29160
29138
|
};
|
|
29161
29139
|
if (isLoading) {
|
|
29162
29140
|
return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "secondary", children: t("loading.items") }) });
|
|
@@ -29204,10 +29182,10 @@ function DataList({
|
|
|
29204
29182
|
{
|
|
29205
29183
|
"data-entity-row": true,
|
|
29206
29184
|
"data-entity-id": id,
|
|
29207
|
-
onClick:
|
|
29185
|
+
onClick: rowClickEvent ? handleRowClick(itemData) : void 0,
|
|
29208
29186
|
className: cn(
|
|
29209
29187
|
"flex px-4 group/rowactions",
|
|
29210
|
-
|
|
29188
|
+
rowClickEvent && "cursor-pointer",
|
|
29211
29189
|
isSent ? "justify-end" : "justify-start"
|
|
29212
29190
|
),
|
|
29213
29191
|
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -29283,7 +29261,7 @@ function DataList({
|
|
|
29283
29261
|
const id2 = itemData.id || String(index);
|
|
29284
29262
|
const actions = renderItemActions(itemData);
|
|
29285
29263
|
return wrapDnd(
|
|
29286
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Box, { "data-entity-row": true, "data-entity-id": id2, onClick:
|
|
29264
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Box, { "data-entity-row": true, "data-entity-id": id2, onClick: rowClickEvent ? handleRowClick(itemData) : void 0, className: cn("relative group/rowactions", rowClickEvent && "cursor-pointer"), children: [
|
|
29287
29265
|
children(itemData, index),
|
|
29288
29266
|
actions && /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "absolute top-2 right-2 z-10 opacity-0 group-hover/rowactions:opacity-100 focus-within:opacity-100 [@media(pointer:coarse)]:opacity-100 transition-opacity duration-fast", children: [
|
|
29289
29267
|
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "rounded-md border border-border bg-card/95 backdrop-blur-sm shadow-sm p-0.5 [@media(pointer:coarse)]:hidden", children: actions }),
|
|
@@ -29295,7 +29273,6 @@ function DataList({
|
|
|
29295
29273
|
items: (itemActions ?? []).map((action) => ({
|
|
29296
29274
|
label: action.label,
|
|
29297
29275
|
icon: action.icon,
|
|
29298
|
-
event: action.event,
|
|
29299
29276
|
variant: action.variant === "danger" ? "danger" : "default",
|
|
29300
29277
|
onClick: () => eventBus.emit(`UI:${action.event}`, {
|
|
29301
29278
|
id: itemData.id,
|
|
@@ -29312,7 +29289,7 @@ function DataList({
|
|
|
29312
29289
|
const id = itemData.id || String(index);
|
|
29313
29290
|
const titleValue = getNestedValue(itemData, titleField?.name ?? "");
|
|
29314
29291
|
return wrapDnd(
|
|
29315
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Box, { "data-entity-row": true, "data-entity-id": id, onClick:
|
|
29292
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Box, { "data-entity-row": true, "data-entity-id": id, onClick: rowClickEvent ? handleRowClick(itemData) : void 0, className: cn(rowClickEvent && "cursor-pointer"), children: [
|
|
29316
29293
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
29317
29294
|
Box,
|
|
29318
29295
|
{
|
|
@@ -34588,7 +34565,7 @@ var init_MapView = __esm({
|
|
|
34588
34565
|
shadowSize: [41, 41]
|
|
34589
34566
|
});
|
|
34590
34567
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
34591
|
-
const { useEffect: useEffect67, useRef: useRef65, useCallback: useCallback96, useState:
|
|
34568
|
+
const { useEffect: useEffect67, useRef: useRef65, useCallback: useCallback96, useState: useState102 } = React94__namespace.default;
|
|
34592
34569
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
34593
34570
|
const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
34594
34571
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
@@ -34633,7 +34610,7 @@ var init_MapView = __esm({
|
|
|
34633
34610
|
showAttribution = true
|
|
34634
34611
|
}) {
|
|
34635
34612
|
const eventBus = useEventBus2();
|
|
34636
|
-
const [clickedPosition, setClickedPosition] =
|
|
34613
|
+
const [clickedPosition, setClickedPosition] = useState102(null);
|
|
34637
34614
|
const handleMapClick = useCallback96((lat, lng) => {
|
|
34638
34615
|
if (showClickedPin) {
|
|
34639
34616
|
setClickedPosition({ lat, lng });
|
|
@@ -38995,741 +38972,775 @@ var init_PositionedCanvas = __esm({
|
|
|
38995
38972
|
PositionedCanvas.displayName = "PositionedCanvas";
|
|
38996
38973
|
}
|
|
38997
38974
|
});
|
|
38998
|
-
function
|
|
38999
|
-
|
|
39000
|
-
|
|
39001
|
-
|
|
38975
|
+
function safeUrl(raw, kinds) {
|
|
38976
|
+
const url = raw.trim();
|
|
38977
|
+
if (url.startsWith("#") || url.startsWith("/")) return url;
|
|
38978
|
+
const lower = url.toLowerCase();
|
|
38979
|
+
for (const kind of kinds) {
|
|
38980
|
+
if (lower.startsWith(kind)) return url;
|
|
38981
|
+
}
|
|
38982
|
+
return null;
|
|
39002
38983
|
}
|
|
39003
|
-
function
|
|
39004
|
-
|
|
39005
|
-
|
|
39006
|
-
const
|
|
39007
|
-
|
|
39008
|
-
|
|
39009
|
-
|
|
39010
|
-
|
|
39011
|
-
|
|
38984
|
+
function sanitizeNode(el) {
|
|
38985
|
+
const children = Array.from(el.children);
|
|
38986
|
+
for (const child of children) {
|
|
38987
|
+
const tag = child.tagName.toLowerCase();
|
|
38988
|
+
if (DROP_TAGS.has(tag)) {
|
|
38989
|
+
child.remove();
|
|
38990
|
+
continue;
|
|
38991
|
+
}
|
|
38992
|
+
const allowed = ALLOWED_ATTRS[tag];
|
|
38993
|
+
if (!allowed) {
|
|
38994
|
+
sanitizeNode(child);
|
|
38995
|
+
child.replaceWith(...Array.from(child.childNodes));
|
|
38996
|
+
continue;
|
|
38997
|
+
}
|
|
38998
|
+
for (const attr of Array.from(child.attributes)) {
|
|
38999
|
+
const name = attr.name.toLowerCase();
|
|
39000
|
+
if (!allowed.includes(name)) {
|
|
39001
|
+
child.removeAttribute(attr.name);
|
|
39002
|
+
continue;
|
|
39003
|
+
}
|
|
39004
|
+
if (name === "href") {
|
|
39005
|
+
const url = safeUrl(attr.value, ["http://", "https://", "mailto:"]);
|
|
39006
|
+
if (url === null) child.removeAttribute(attr.name);
|
|
39007
|
+
else child.setAttribute("href", url);
|
|
39008
|
+
}
|
|
39009
|
+
if (name === "src") {
|
|
39010
|
+
const url = safeUrl(attr.value, ["http://", "https://", "data:image/"]);
|
|
39011
|
+
if (url === null) child.remove();
|
|
39012
|
+
else child.setAttribute("src", url);
|
|
39013
|
+
}
|
|
39014
|
+
}
|
|
39015
|
+
sanitizeNode(child);
|
|
39016
|
+
}
|
|
39012
39017
|
}
|
|
39013
|
-
function
|
|
39014
|
-
|
|
39015
|
-
|
|
39016
|
-
|
|
39017
|
-
|
|
39018
|
-
|
|
39019
|
-
|
|
39020
|
-
children: [
|
|
39021
|
-
{ id: nextBlockId("li"), type: "paragraph", content: "" }
|
|
39022
|
-
]
|
|
39023
|
-
};
|
|
39024
|
-
case "image":
|
|
39025
|
-
return {
|
|
39026
|
-
id: nextBlockId(type),
|
|
39027
|
-
type,
|
|
39028
|
-
content: "",
|
|
39029
|
-
metadata: { url: "", caption: "" }
|
|
39030
|
-
};
|
|
39031
|
-
case "code":
|
|
39032
|
-
return {
|
|
39033
|
-
id: nextBlockId(type),
|
|
39034
|
-
type,
|
|
39035
|
-
content: "",
|
|
39036
|
-
metadata: { language: "plaintext" }
|
|
39037
|
-
};
|
|
39038
|
-
case "divider":
|
|
39039
|
-
return { id: nextBlockId(type), type };
|
|
39040
|
-
default:
|
|
39041
|
-
return { id: nextBlockId(type), type, content: "" };
|
|
39018
|
+
function sanitizeRichHtml(html) {
|
|
39019
|
+
if (!html || typeof html !== "string") return "";
|
|
39020
|
+
if (typeof window === "undefined" || typeof DOMParser === "undefined") return "";
|
|
39021
|
+
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
39022
|
+
sanitizeNode(doc.body);
|
|
39023
|
+
if (!doc.body.querySelector("img, hr") && (doc.body.textContent ?? "").trim().length === 0) {
|
|
39024
|
+
return "";
|
|
39042
39025
|
}
|
|
39026
|
+
return doc.body.innerHTML.replace(//g, "");
|
|
39043
39027
|
}
|
|
39044
|
-
function
|
|
39045
|
-
|
|
39028
|
+
function htmlIsEmpty(el) {
|
|
39029
|
+
if (el.querySelector("img, hr")) return false;
|
|
39030
|
+
return (el.textContent ?? "").trim().length === 0;
|
|
39031
|
+
}
|
|
39032
|
+
function closestBlock(node, root) {
|
|
39033
|
+
let cur = node;
|
|
39034
|
+
while (cur && cur !== root) {
|
|
39035
|
+
if (cur instanceof Element && BLOCK_TAGS.has(cur.tagName)) return cur;
|
|
39036
|
+
cur = cur.parentNode;
|
|
39037
|
+
}
|
|
39038
|
+
return null;
|
|
39046
39039
|
}
|
|
39047
|
-
function
|
|
39048
|
-
return
|
|
39040
|
+
function RichTextStyles() {
|
|
39041
|
+
return /* @__PURE__ */ jsxRuntime.jsx("style", { children: RICH_TEXT_CSS });
|
|
39049
39042
|
}
|
|
39050
|
-
function
|
|
39043
|
+
function readToolbarState(root) {
|
|
39044
|
+
const sel = window.getSelection();
|
|
39045
|
+
if (!sel || sel.rangeCount === 0) return IDLE_TOOLBAR;
|
|
39046
|
+
const anchor = sel.anchorNode;
|
|
39047
|
+
if (!anchor || !root.contains(anchor)) return IDLE_TOOLBAR;
|
|
39048
|
+
let block = null;
|
|
39049
|
+
let link = false;
|
|
39050
|
+
let node = anchor;
|
|
39051
|
+
while (node && node !== root) {
|
|
39052
|
+
if (node instanceof Element) {
|
|
39053
|
+
const tag = node.tagName.toLowerCase();
|
|
39054
|
+
if (!block && (tag === "h1" || tag === "h2" || tag === "h3" || tag === "blockquote" || tag === "pre")) {
|
|
39055
|
+
block = tag;
|
|
39056
|
+
}
|
|
39057
|
+
if (tag === "a") link = true;
|
|
39058
|
+
}
|
|
39059
|
+
node = node.parentNode;
|
|
39060
|
+
}
|
|
39051
39061
|
return {
|
|
39052
|
-
|
|
39053
|
-
|
|
39054
|
-
|
|
39055
|
-
|
|
39056
|
-
|
|
39057
|
-
|
|
39058
|
-
|
|
39062
|
+
bold: document.queryCommandState("bold"),
|
|
39063
|
+
italic: document.queryCommandState("italic"),
|
|
39064
|
+
underline: document.queryCommandState("underline"),
|
|
39065
|
+
strikeThrough: document.queryCommandState("strikeThrough"),
|
|
39066
|
+
block,
|
|
39067
|
+
bullets: document.queryCommandState("insertUnorderedList"),
|
|
39068
|
+
numbers: document.queryCommandState("insertOrderedList"),
|
|
39069
|
+
link
|
|
39059
39070
|
};
|
|
39060
39071
|
}
|
|
39061
|
-
function
|
|
39062
|
-
|
|
39063
|
-
|
|
39064
|
-
|
|
39065
|
-
|
|
39066
|
-
|
|
39067
|
-
|
|
39068
|
-
|
|
39069
|
-
|
|
39070
|
-
|
|
39071
|
-
|
|
39072
|
-
|
|
39072
|
+
function ToolbarButton({ icon: IconCmp, label, active, onExec }) {
|
|
39073
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
39074
|
+
Button,
|
|
39075
|
+
{
|
|
39076
|
+
type: "button",
|
|
39077
|
+
variant: "ghost",
|
|
39078
|
+
size: "sm",
|
|
39079
|
+
"aria-label": label,
|
|
39080
|
+
"aria-pressed": active,
|
|
39081
|
+
title: label,
|
|
39082
|
+
className: cn(
|
|
39083
|
+
"h-8 w-8 p-0 gap-0 justify-center",
|
|
39084
|
+
active && "bg-muted text-foreground"
|
|
39085
|
+
),
|
|
39086
|
+
onMouseDown: (e) => {
|
|
39087
|
+
e.preventDefault();
|
|
39088
|
+
onExec();
|
|
39089
|
+
},
|
|
39090
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(IconCmp, { size: 15 })
|
|
39073
39091
|
}
|
|
39074
|
-
|
|
39075
|
-
|
|
39076
|
-
|
|
39077
|
-
|
|
39078
|
-
|
|
39079
|
-
|
|
39080
|
-
|
|
39081
|
-
|
|
39082
|
-
|
|
39083
|
-
|
|
39084
|
-
|
|
39085
|
-
|
|
39086
|
-
|
|
39087
|
-
|
|
39088
|
-
|
|
39089
|
-
|
|
39090
|
-
|
|
39091
|
-
|
|
39092
|
-
|
|
39093
|
-
|
|
39094
|
-
|
|
39095
|
-
|
|
39096
|
-
|
|
39097
|
-
|
|
39098
|
-
|
|
39099
|
-
|
|
39092
|
+
);
|
|
39093
|
+
}
|
|
39094
|
+
var CHANGE_DEBOUNCE_MS, ALLOWED_ATTRS, DROP_TAGS, MD_BLOCK_PREFIXES, BLOCK_TAGS, MD_INLINE_RULES, RICH_TEXT_CSS, IDLE_TOOLBAR, RichTextEditor;
|
|
39095
|
+
var init_RichTextEditor = __esm({
|
|
39096
|
+
"components/core/molecules/RichTextEditor.tsx"() {
|
|
39097
|
+
"use client";
|
|
39098
|
+
init_cn();
|
|
39099
|
+
init_Box();
|
|
39100
|
+
init_Button();
|
|
39101
|
+
init_useEventBus();
|
|
39102
|
+
CHANGE_DEBOUNCE_MS = 400;
|
|
39103
|
+
ALLOWED_ATTRS = {
|
|
39104
|
+
p: [],
|
|
39105
|
+
h1: [],
|
|
39106
|
+
h2: [],
|
|
39107
|
+
h3: [],
|
|
39108
|
+
h4: [],
|
|
39109
|
+
ul: [],
|
|
39110
|
+
ol: [],
|
|
39111
|
+
li: [],
|
|
39112
|
+
blockquote: [],
|
|
39113
|
+
pre: [],
|
|
39114
|
+
code: [],
|
|
39115
|
+
b: [],
|
|
39116
|
+
strong: [],
|
|
39117
|
+
i: [],
|
|
39118
|
+
em: [],
|
|
39119
|
+
u: [],
|
|
39120
|
+
s: [],
|
|
39121
|
+
strike: [],
|
|
39122
|
+
br: [],
|
|
39123
|
+
hr: [],
|
|
39124
|
+
div: [],
|
|
39125
|
+
span: [],
|
|
39126
|
+
a: ["href"],
|
|
39127
|
+
img: ["src", "alt"]
|
|
39128
|
+
};
|
|
39129
|
+
DROP_TAGS = /* @__PURE__ */ new Set(["script", "style", "iframe", "object", "embed", "link", "meta", "form", "input", "button", "textarea", "select"]);
|
|
39130
|
+
MD_BLOCK_PREFIXES = {
|
|
39131
|
+
"#": { kind: "block", tag: "h1" },
|
|
39132
|
+
"##": { kind: "block", tag: "h2" },
|
|
39133
|
+
"###": { kind: "block", tag: "h3" },
|
|
39134
|
+
">": { kind: "block", tag: "blockquote" },
|
|
39135
|
+
"```": { kind: "block", tag: "pre" },
|
|
39136
|
+
"-": { kind: "list", command: "insertUnorderedList" },
|
|
39137
|
+
"*": { kind: "list", command: "insertUnorderedList" },
|
|
39138
|
+
"1.": { kind: "list", command: "insertOrderedList" }
|
|
39139
|
+
};
|
|
39140
|
+
BLOCK_TAGS = /* @__PURE__ */ new Set(["P", "DIV", "LI", "H1", "H2", "H3", "H4", "BLOCKQUOTE", "PRE"]);
|
|
39141
|
+
MD_INLINE_RULES = [
|
|
39142
|
+
{ pattern: /\*\*([^*\n]+)\*\*$/, wrap: "strong" },
|
|
39143
|
+
{ pattern: /(^|[^*])\*([^*\n]+)\*$/, wrap: "em" },
|
|
39144
|
+
{ pattern: /`([^`\n]+)`$/, wrap: "code" }
|
|
39145
|
+
];
|
|
39146
|
+
RICH_TEXT_CSS = `
|
|
39147
|
+
.almadar-rich-text { line-height: 1.7; color: var(--color-foreground); }
|
|
39148
|
+
.almadar-rich-text h1 { font-size: 1.875rem; font-weight: 700; line-height: 1.25; margin: 1.25em 0 0.4em; }
|
|
39149
|
+
.almadar-rich-text h2 { font-size: 1.5rem; font-weight: 650; line-height: 1.3; margin: 1.1em 0 0.4em; }
|
|
39150
|
+
.almadar-rich-text h3 { font-size: 1.25rem; font-weight: 600; line-height: 1.35; margin: 1em 0 0.35em; }
|
|
39151
|
+
.almadar-rich-text h1:first-child, .almadar-rich-text h2:first-child, .almadar-rich-text h3:first-child { margin-top: 0; }
|
|
39152
|
+
.almadar-rich-text p { margin: 0.5em 0; }
|
|
39153
|
+
.almadar-rich-text ul { list-style: disc; padding-inline-start: 1.5rem; margin: 0.5em 0; }
|
|
39154
|
+
.almadar-rich-text ol { list-style: decimal; padding-inline-start: 1.5rem; margin: 0.5em 0; }
|
|
39155
|
+
.almadar-rich-text li { margin: 0.25em 0; }
|
|
39156
|
+
.almadar-rich-text blockquote { border-inline-start: 3px solid var(--color-primary); padding-inline-start: 1rem; font-style: italic; color: var(--color-muted-foreground); margin: 0.75em 0; }
|
|
39157
|
+
.almadar-rich-text pre { background: var(--color-muted); border: 1px solid var(--color-border); border-radius: 0.375rem; padding: 0.75rem 1rem; font-family: ui-monospace, monospace; font-size: 0.875em; white-space: pre-wrap; margin: 0.75em 0; }
|
|
39158
|
+
.almadar-rich-text code { font-family: ui-monospace, monospace; font-size: 0.875em; background: var(--color-muted); border-radius: 0.25rem; padding: 0.1em 0.35em; }
|
|
39159
|
+
.almadar-rich-text pre code { background: transparent; padding: 0; }
|
|
39160
|
+
.almadar-rich-text a { color: var(--color-primary); text-decoration: underline; }
|
|
39161
|
+
.almadar-rich-text hr { border: none; border-top: 1px solid var(--color-border); margin: 1.25em 0; }
|
|
39162
|
+
.almadar-rich-text img { max-width: 100%; border-radius: 0.375rem; }
|
|
39163
|
+
`;
|
|
39164
|
+
IDLE_TOOLBAR = {
|
|
39165
|
+
bold: false,
|
|
39166
|
+
italic: false,
|
|
39167
|
+
underline: false,
|
|
39168
|
+
strikeThrough: false,
|
|
39169
|
+
block: null,
|
|
39170
|
+
bullets: false,
|
|
39171
|
+
numbers: false,
|
|
39172
|
+
link: false
|
|
39173
|
+
};
|
|
39174
|
+
RichTextEditor = ({
|
|
39175
|
+
value,
|
|
39176
|
+
onChange,
|
|
39177
|
+
changeEvent,
|
|
39178
|
+
readOnly = false,
|
|
39179
|
+
placeholder,
|
|
39180
|
+
showToolbar = true,
|
|
39181
|
+
className
|
|
39182
|
+
}) => {
|
|
39183
|
+
const { t } = hooks.useTranslate();
|
|
39184
|
+
const ref = React94.useRef(null);
|
|
39185
|
+
const [toolbar, setToolbar] = React94.useState(IDLE_TOOLBAR);
|
|
39186
|
+
const [empty, setEmpty] = React94.useState(() => !value || !sanitizeRichHtml(value).trim());
|
|
39187
|
+
const eventBus = useEventBus();
|
|
39188
|
+
const onChangeRef = React94.useRef(onChange);
|
|
39189
|
+
const changeEventRef = React94.useRef(changeEvent);
|
|
39190
|
+
React94.useEffect(() => {
|
|
39191
|
+
onChangeRef.current = onChange;
|
|
39192
|
+
changeEventRef.current = changeEvent;
|
|
39193
|
+
}, [onChange, changeEvent]);
|
|
39194
|
+
const emitTimerRef = React94.useRef(null);
|
|
39195
|
+
const emitNow = React94.useCallback(() => {
|
|
39196
|
+
const el = ref.current;
|
|
39197
|
+
if (!el) return;
|
|
39198
|
+
const html = sanitizeRichHtml(el.innerHTML);
|
|
39199
|
+
onChangeRef.current?.(html);
|
|
39200
|
+
const evt = changeEventRef.current;
|
|
39201
|
+
if (evt) eventBus.emit(`UI:${evt}`, { value: html });
|
|
39202
|
+
}, [eventBus]);
|
|
39203
|
+
const scheduleEmit = React94.useCallback(() => {
|
|
39204
|
+
if (emitTimerRef.current) clearTimeout(emitTimerRef.current);
|
|
39205
|
+
emitTimerRef.current = setTimeout(() => {
|
|
39206
|
+
emitTimerRef.current = null;
|
|
39207
|
+
emitNow();
|
|
39208
|
+
}, CHANGE_DEBOUNCE_MS);
|
|
39209
|
+
}, [emitNow]);
|
|
39210
|
+
const flushEmit = React94.useCallback(() => {
|
|
39211
|
+
if (!emitTimerRef.current) return;
|
|
39212
|
+
clearTimeout(emitTimerRef.current);
|
|
39213
|
+
emitTimerRef.current = null;
|
|
39214
|
+
emitNow();
|
|
39215
|
+
}, [emitNow]);
|
|
39216
|
+
React94.useEffect(() => () => {
|
|
39217
|
+
if (emitTimerRef.current) {
|
|
39218
|
+
clearTimeout(emitTimerRef.current);
|
|
39219
|
+
emitTimerRef.current = null;
|
|
39220
|
+
emitNow();
|
|
39221
|
+
}
|
|
39222
|
+
}, [emitNow]);
|
|
39223
|
+
React94.useEffect(() => {
|
|
39224
|
+
if (readOnly) return;
|
|
39225
|
+
const el = ref.current;
|
|
39226
|
+
if (!el) return;
|
|
39227
|
+
if (document.activeElement === el) return;
|
|
39228
|
+
const next = sanitizeRichHtml(value ?? "") || "<p><br></p>";
|
|
39229
|
+
if (el.innerHTML !== next) {
|
|
39230
|
+
el.innerHTML = next;
|
|
39231
|
+
setEmpty(htmlIsEmpty(el));
|
|
39232
|
+
}
|
|
39233
|
+
}, [readOnly, value]);
|
|
39234
|
+
React94.useEffect(() => {
|
|
39235
|
+
if (readOnly) return;
|
|
39236
|
+
document.execCommand("defaultParagraphSeparator", false, "p");
|
|
39237
|
+
}, [readOnly]);
|
|
39238
|
+
React94.useEffect(() => {
|
|
39239
|
+
if (readOnly || !showToolbar) return;
|
|
39240
|
+
const onSelectionChange = () => {
|
|
39241
|
+
const el = ref.current;
|
|
39242
|
+
if (!el) return;
|
|
39243
|
+
setToolbar(readToolbarState(el));
|
|
39244
|
+
};
|
|
39245
|
+
document.addEventListener("selectionchange", onSelectionChange);
|
|
39246
|
+
return () => document.removeEventListener("selectionchange", onSelectionChange);
|
|
39247
|
+
}, [readOnly, showToolbar]);
|
|
39248
|
+
const tryInlineMarkdown = React94.useCallback(() => {
|
|
39249
|
+
const root = ref.current;
|
|
39250
|
+
if (!root) return false;
|
|
39251
|
+
const sel = window.getSelection();
|
|
39252
|
+
if (!sel || !sel.isCollapsed || sel.rangeCount === 0) return false;
|
|
39253
|
+
const node = sel.anchorNode;
|
|
39254
|
+
if (!node || node.nodeType !== Node.TEXT_NODE || !root.contains(node)) return false;
|
|
39255
|
+
if (node.parentElement?.closest("code, pre")) return false;
|
|
39256
|
+
const text = (node.textContent ?? "").slice(0, sel.anchorOffset);
|
|
39257
|
+
for (const rule of MD_INLINE_RULES) {
|
|
39258
|
+
const m = rule.pattern.exec(text);
|
|
39259
|
+
if (!m) continue;
|
|
39260
|
+
const inner = rule.wrap === "em" ? m[2] : m[1];
|
|
39261
|
+
if (!inner || !inner.trim()) continue;
|
|
39262
|
+
const lead = rule.wrap === "em" ? m[1]?.length ?? 0 : 0;
|
|
39263
|
+
const range = document.createRange();
|
|
39264
|
+
range.setStart(node, m.index + lead);
|
|
39265
|
+
range.setEnd(node, sel.anchorOffset);
|
|
39266
|
+
const el = document.createElement(rule.wrap);
|
|
39267
|
+
el.textContent = inner;
|
|
39268
|
+
range.deleteContents();
|
|
39269
|
+
range.insertNode(el);
|
|
39270
|
+
const after = document.createTextNode("\u200B");
|
|
39271
|
+
el.parentNode?.insertBefore(after, el.nextSibling);
|
|
39272
|
+
const caret = document.createRange();
|
|
39273
|
+
caret.setStart(after, 1);
|
|
39274
|
+
caret.collapse(true);
|
|
39275
|
+
sel.removeAllRanges();
|
|
39276
|
+
sel.addRange(caret);
|
|
39277
|
+
return true;
|
|
39278
|
+
}
|
|
39279
|
+
return false;
|
|
39280
|
+
}, []);
|
|
39281
|
+
const tryBlockMarkdown = React94.useCallback(() => {
|
|
39282
|
+
const root = ref.current;
|
|
39283
|
+
if (!root) return false;
|
|
39284
|
+
const sel = window.getSelection();
|
|
39285
|
+
if (!sel || !sel.isCollapsed || sel.rangeCount === 0) return false;
|
|
39286
|
+
const node = sel.anchorNode;
|
|
39287
|
+
if (!node || node.nodeType !== Node.TEXT_NODE || !root.contains(node)) return false;
|
|
39288
|
+
const block = closestBlock(node, root);
|
|
39289
|
+
if (block ? block.firstChild !== node : root.firstChild !== node) return false;
|
|
39290
|
+
const prefix = (node.textContent ?? "").slice(0, sel.anchorOffset);
|
|
39291
|
+
const rule = MD_BLOCK_PREFIXES[prefix];
|
|
39292
|
+
if (!rule) return false;
|
|
39293
|
+
if (node.parentElement?.closest("pre, code")) return false;
|
|
39294
|
+
if (rule.kind === "list" && node.parentElement?.closest("li")) return false;
|
|
39295
|
+
node.deleteData(0, prefix.length);
|
|
39296
|
+
const caret = document.createRange();
|
|
39297
|
+
caret.setStart(node, 0);
|
|
39298
|
+
caret.collapse(true);
|
|
39299
|
+
sel.removeAllRanges();
|
|
39300
|
+
sel.addRange(caret);
|
|
39301
|
+
if (rule.kind === "list") {
|
|
39302
|
+
const container = block ?? node.parentElement;
|
|
39303
|
+
const list = document.createElement(rule.command === "insertOrderedList" ? "ol" : "ul");
|
|
39304
|
+
const li = document.createElement("li");
|
|
39305
|
+
if (block) {
|
|
39306
|
+
while (block.firstChild) li.appendChild(block.firstChild);
|
|
39307
|
+
list.appendChild(li);
|
|
39308
|
+
block.replaceWith(list);
|
|
39309
|
+
} else if (container) {
|
|
39310
|
+
li.appendChild(node);
|
|
39311
|
+
list.appendChild(li);
|
|
39312
|
+
container.appendChild(list);
|
|
39313
|
+
}
|
|
39314
|
+
if (!li.firstChild) li.appendChild(document.createElement("br"));
|
|
39315
|
+
const liCaret = document.createRange();
|
|
39316
|
+
liCaret.selectNodeContents(li);
|
|
39317
|
+
liCaret.collapse(true);
|
|
39318
|
+
sel.removeAllRanges();
|
|
39319
|
+
sel.addRange(liCaret);
|
|
39320
|
+
} else {
|
|
39321
|
+
document.execCommand("formatBlock", false, rule.tag);
|
|
39322
|
+
}
|
|
39323
|
+
return true;
|
|
39324
|
+
}, []);
|
|
39325
|
+
const afterEdit = React94.useCallback(() => {
|
|
39326
|
+
const el = ref.current;
|
|
39327
|
+
if (!el) return;
|
|
39328
|
+
tryInlineMarkdown();
|
|
39329
|
+
setEmpty(htmlIsEmpty(el));
|
|
39330
|
+
setToolbar(readToolbarState(el));
|
|
39331
|
+
scheduleEmit();
|
|
39332
|
+
}, [scheduleEmit, tryInlineMarkdown]);
|
|
39333
|
+
const handleKeyDown = React94.useCallback((e) => {
|
|
39334
|
+
if (e.key === " " && tryBlockMarkdown()) {
|
|
39335
|
+
e.preventDefault();
|
|
39336
|
+
afterEdit();
|
|
39337
|
+
}
|
|
39338
|
+
}, [afterEdit, tryBlockMarkdown]);
|
|
39339
|
+
const execInline = React94.useCallback((command) => {
|
|
39340
|
+
document.execCommand(command, false);
|
|
39341
|
+
afterEdit();
|
|
39342
|
+
}, [afterEdit]);
|
|
39343
|
+
const execBlock = React94.useCallback((tag) => {
|
|
39344
|
+
const next = toolbar.block === tag ? "p" : tag;
|
|
39345
|
+
document.execCommand("formatBlock", false, next);
|
|
39346
|
+
afterEdit();
|
|
39347
|
+
}, [afterEdit, toolbar.block]);
|
|
39348
|
+
const execList = React94.useCallback((command) => {
|
|
39349
|
+
document.execCommand(command, false);
|
|
39350
|
+
afterEdit();
|
|
39351
|
+
}, [afterEdit]);
|
|
39352
|
+
const execLink = React94.useCallback(() => {
|
|
39353
|
+
if (toolbar.link) {
|
|
39354
|
+
document.execCommand("unlink", false);
|
|
39355
|
+
afterEdit();
|
|
39356
|
+
return;
|
|
39357
|
+
}
|
|
39358
|
+
const url = window.prompt(t("richTextEditor.linkPrompt"));
|
|
39359
|
+
if (!url) return;
|
|
39360
|
+
const safe = safeUrl(url, ["http://", "https://", "mailto:"]) ?? `https://${url}`;
|
|
39361
|
+
document.execCommand("createLink", false, safe);
|
|
39362
|
+
afterEdit();
|
|
39363
|
+
}, [afterEdit, t, toolbar.link]);
|
|
39364
|
+
const execRule = React94.useCallback(() => {
|
|
39365
|
+
document.execCommand("insertHorizontalRule", false);
|
|
39366
|
+
afterEdit();
|
|
39367
|
+
}, [afterEdit]);
|
|
39368
|
+
if (readOnly) {
|
|
39369
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: cn("almadar-rich-text max-w-none", className), children: [
|
|
39370
|
+
/* @__PURE__ */ jsxRuntime.jsx(RichTextStyles, {}),
|
|
39371
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { dangerouslySetInnerHTML: { __html: sanitizeRichHtml(value ?? "") } })
|
|
39372
|
+
] });
|
|
39373
|
+
}
|
|
39374
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: cn("flex flex-col gap-2", className), children: [
|
|
39375
|
+
/* @__PURE__ */ jsxRuntime.jsx(RichTextStyles, {}),
|
|
39376
|
+
showToolbar && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
39377
|
+
Box,
|
|
39378
|
+
{
|
|
39379
|
+
role: "toolbar",
|
|
39380
|
+
"aria-label": t("richTextEditor.editorToolbar"),
|
|
39381
|
+
className: cn(
|
|
39382
|
+
"sticky top-0 z-10 flex flex-wrap items-center gap-0.5 self-start",
|
|
39383
|
+
"rounded-md border border-border bg-background/95 px-1 py-0.5 shadow-sm"
|
|
39384
|
+
),
|
|
39385
|
+
children: [
|
|
39386
|
+
/* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Bold, label: t("richTextEditor.bold"), active: toolbar.bold, onExec: () => execInline("bold") }),
|
|
39387
|
+
/* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Italic, label: t("richTextEditor.italic"), active: toolbar.italic, onExec: () => execInline("italic") }),
|
|
39388
|
+
/* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Underline, label: t("richTextEditor.underline"), active: toolbar.underline, onExec: () => execInline("underline") }),
|
|
39389
|
+
/* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Strikethrough, label: t("richTextEditor.strikethrough"), active: toolbar.strikeThrough, onExec: () => execInline("strikeThrough") }),
|
|
39390
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mx-1 h-5 w-px bg-border" }),
|
|
39391
|
+
/* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Heading1, label: t("richTextEditor.heading1"), active: toolbar.block === "h1", onExec: () => execBlock("h1") }),
|
|
39392
|
+
/* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Heading2, label: t("richTextEditor.heading2"), active: toolbar.block === "h2", onExec: () => execBlock("h2") }),
|
|
39393
|
+
/* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Heading3, label: t("richTextEditor.heading3"), active: toolbar.block === "h3", onExec: () => execBlock("h3") }),
|
|
39394
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mx-1 h-5 w-px bg-border" }),
|
|
39395
|
+
/* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.List, label: t("richTextEditor.bulletList"), active: toolbar.bullets, onExec: () => execList("insertUnorderedList") }),
|
|
39396
|
+
/* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.ListOrdered, label: t("richTextEditor.numberedList"), active: toolbar.numbers, onExec: () => execList("insertOrderedList") }),
|
|
39397
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mx-1 h-5 w-px bg-border" }),
|
|
39398
|
+
/* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Quote, label: t("richTextEditor.quote"), active: toolbar.block === "blockquote", onExec: () => execBlock("blockquote") }),
|
|
39399
|
+
/* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Code, label: t("richTextEditor.code"), active: toolbar.block === "pre", onExec: () => execBlock("pre") }),
|
|
39400
|
+
/* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Link, label: toolbar.link ? t("richTextEditor.removeLink") : t("richTextEditor.link"), active: toolbar.link, onExec: execLink }),
|
|
39401
|
+
/* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Minus, label: t("richTextEditor.divider"), onExec: execRule })
|
|
39402
|
+
]
|
|
39403
|
+
}
|
|
39404
|
+
),
|
|
39405
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
39406
|
+
Box,
|
|
39407
|
+
{
|
|
39408
|
+
ref,
|
|
39409
|
+
contentEditable: true,
|
|
39410
|
+
suppressContentEditableWarning: true,
|
|
39411
|
+
role: "textbox",
|
|
39412
|
+
"aria-multiline": "true",
|
|
39413
|
+
"aria-label": t("richTextEditor.editorSurface"),
|
|
39414
|
+
"data-placeholder": placeholder ?? t("richTextEditor.placeholder"),
|
|
39415
|
+
"data-empty": empty ? "true" : "false",
|
|
39416
|
+
className: cn(
|
|
39417
|
+
"almadar-rich-text max-w-none min-h-[8rem] outline-none",
|
|
39418
|
+
"data-[empty=true]:before:content-[attr(data-placeholder)]",
|
|
39419
|
+
"data-[empty=true]:before:text-muted-foreground/60",
|
|
39420
|
+
"data-[empty=true]:before:pointer-events-none",
|
|
39421
|
+
"data-[empty=true]:before:absolute",
|
|
39422
|
+
"relative cursor-text"
|
|
39423
|
+
),
|
|
39424
|
+
onInput: afterEdit,
|
|
39425
|
+
onKeyDown: handleKeyDown,
|
|
39426
|
+
onBlur: flushEmit
|
|
39427
|
+
}
|
|
39428
|
+
)
|
|
39429
|
+
] });
|
|
39100
39430
|
};
|
|
39431
|
+
RichTextEditor.displayName = "RichTextEditor";
|
|
39101
39432
|
}
|
|
39102
|
-
|
|
39103
|
-
|
|
39433
|
+
});
|
|
39434
|
+
function renderIconInput4(icon, props) {
|
|
39435
|
+
return typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon, ...props });
|
|
39104
39436
|
}
|
|
39105
|
-
function
|
|
39437
|
+
function DocumentPanel({
|
|
39438
|
+
id,
|
|
39439
|
+
title,
|
|
39440
|
+
subtitle,
|
|
39441
|
+
value,
|
|
39442
|
+
actions,
|
|
39443
|
+
maxInlineActions,
|
|
39444
|
+
editing = false,
|
|
39445
|
+
titleCommitEvent,
|
|
39446
|
+
contentChangeEvent,
|
|
39447
|
+
editEvent,
|
|
39448
|
+
doneEvent,
|
|
39449
|
+
editLabel,
|
|
39450
|
+
doneLabel,
|
|
39451
|
+
autosaveHint,
|
|
39452
|
+
placeholder,
|
|
39453
|
+
className
|
|
39454
|
+
}) {
|
|
39455
|
+
const eventBus = useEventBus();
|
|
39106
39456
|
const { t } = hooks.useTranslate();
|
|
39107
|
-
const [
|
|
39108
|
-
const
|
|
39109
|
-
|
|
39110
|
-
|
|
39111
|
-
|
|
39112
|
-
|
|
39113
|
-
|
|
39114
|
-
|
|
39457
|
+
const [titleDraft, setTitleDraft] = React94.useState(null);
|
|
39458
|
+
const recordId = id ?? "";
|
|
39459
|
+
const actionDefs = actions ?? [];
|
|
39460
|
+
const inlineCap = Math.min(maxInlineActions ?? 1, 1);
|
|
39461
|
+
const inlineActions = actionDefs.filter((a) => a.variant === "primary").slice(0, inlineCap);
|
|
39462
|
+
const menuActions = actionDefs.filter((a) => !inlineActions.includes(a));
|
|
39463
|
+
const fireAction = (action) => {
|
|
39464
|
+
if (!action.event) return;
|
|
39465
|
+
eventBus.emit(`UI:${action.event}`, { id: recordId, title: title ?? "" });
|
|
39466
|
+
};
|
|
39467
|
+
const commitTitle = () => {
|
|
39468
|
+
if (!titleCommitEvent) return;
|
|
39469
|
+
const next = (titleDraft ?? "").trim();
|
|
39470
|
+
setTitleDraft(null);
|
|
39471
|
+
if (!next || next === title) return;
|
|
39472
|
+
eventBus.emit(`UI:${titleCommitEvent}`, { title: next, id: recordId });
|
|
39473
|
+
};
|
|
39474
|
+
const untitled = t("documentPanel.untitled") || "Untitled";
|
|
39475
|
+
const titleNode = titleCommitEvent && titleDraft !== null ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
39476
|
+
Input,
|
|
39477
|
+
{
|
|
39478
|
+
value: titleDraft,
|
|
39479
|
+
autoFocus: true,
|
|
39480
|
+
"aria-label": t("common.title"),
|
|
39481
|
+
className: "h-auto py-1 text-3xl font-bold tracking-tight",
|
|
39482
|
+
onFocus: (e) => e.currentTarget.select(),
|
|
39483
|
+
onChange: (e) => setTitleDraft(e.target.value),
|
|
39484
|
+
onBlur: commitTitle,
|
|
39485
|
+
onKeyDown: (e) => {
|
|
39486
|
+
if (e.key === "Enter") {
|
|
39487
|
+
e.preventDefault();
|
|
39488
|
+
commitTitle();
|
|
39489
|
+
} else if (e.key === "Escape") {
|
|
39490
|
+
e.preventDefault();
|
|
39491
|
+
setTitleDraft(null);
|
|
39492
|
+
}
|
|
39493
|
+
},
|
|
39494
|
+
"data-testid": "document-title-input"
|
|
39115
39495
|
}
|
|
39116
|
-
|
|
39117
|
-
|
|
39118
|
-
|
|
39119
|
-
|
|
39120
|
-
|
|
39121
|
-
|
|
39122
|
-
|
|
39123
|
-
{
|
|
39124
|
-
|
|
39125
|
-
|
|
39126
|
-
|
|
39127
|
-
|
|
39128
|
-
|
|
39129
|
-
|
|
39130
|
-
|
|
39131
|
-
|
|
39132
|
-
|
|
39133
|
-
|
|
39134
|
-
|
|
39135
|
-
|
|
39136
|
-
|
|
39137
|
-
|
|
39138
|
-
|
|
39496
|
+
) : titleCommitEvent ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
39497
|
+
Box,
|
|
39498
|
+
{
|
|
39499
|
+
role: "button",
|
|
39500
|
+
tabIndex: 0,
|
|
39501
|
+
className: "cursor-text rounded px-1 -mx-1 transition-colors hover:bg-muted/40",
|
|
39502
|
+
onClick: () => setTitleDraft(title ?? ""),
|
|
39503
|
+
onKeyDown: (e) => {
|
|
39504
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
39505
|
+
e.preventDefault();
|
|
39506
|
+
setTitleDraft(title ?? "");
|
|
39507
|
+
}
|
|
39508
|
+
},
|
|
39509
|
+
"data-testid": "document-title-editable",
|
|
39510
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", weight: "bold", className: cn(!title && "text-muted-foreground"), children: title || untitled })
|
|
39511
|
+
}
|
|
39512
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", weight: "bold", children: title || untitled });
|
|
39513
|
+
const emitWithId = (event) => () => {
|
|
39514
|
+
if (event) eventBus.emit(`UI:${event}`, { id: recordId });
|
|
39515
|
+
};
|
|
39516
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Card, { variant: "elevated", className: cn("w-full", className), children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", className: "p-6 sm:p-8", children: [
|
|
39517
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "justify-between items-start", children: [
|
|
39518
|
+
/* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", className: "flex-1 min-w-0", children: [
|
|
39519
|
+
titleNode,
|
|
39520
|
+
subtitle ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: subtitle }) : null
|
|
39521
|
+
] }),
|
|
39522
|
+
/* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", className: "flex-shrink-0 pt-1 items-center", children: editing ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
39523
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "muted", className: "hidden sm:block", children: autosaveHint ?? (t("documentPanel.autosaveHint") || "") }),
|
|
39524
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", size: "sm", onClick: emitWithId(doneEvent), "data-testid": "document-done", children: doneLabel ?? (t("documentPanel.done") || "Done") })
|
|
39525
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
39526
|
+
editEvent && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "ghost", size: "sm", onClick: emitWithId(editEvent), "data-testid": "document-edit", children: [
|
|
39527
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "edit", size: "xs", className: "mr-1" }),
|
|
39528
|
+
editLabel ?? (t("documentPanel.edit") || "Edit")
|
|
39529
|
+
] }),
|
|
39530
|
+
inlineActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
39531
|
+
Button,
|
|
39532
|
+
{
|
|
39533
|
+
variant: "primary",
|
|
39534
|
+
size: "sm",
|
|
39535
|
+
onClick: () => fireAction(action),
|
|
39536
|
+
"data-testid": `action-${action.event}`,
|
|
39537
|
+
children: [
|
|
39538
|
+
action.icon && renderIconInput4(action.icon, { size: "xs", className: "mr-1" }),
|
|
39539
|
+
action.label
|
|
39540
|
+
]
|
|
39541
|
+
},
|
|
39542
|
+
idx
|
|
39543
|
+
)),
|
|
39544
|
+
menuActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
39545
|
+
Menu,
|
|
39546
|
+
{
|
|
39547
|
+
position: "bottom-end",
|
|
39548
|
+
trigger: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
|
|
39549
|
+
items: menuActions.map((action) => ({
|
|
39550
|
+
label: action.label,
|
|
39551
|
+
icon: action.icon,
|
|
39552
|
+
variant: action.variant === "danger" ? "danger" : "default",
|
|
39553
|
+
onClick: () => fireAction(action)
|
|
39554
|
+
}))
|
|
39555
|
+
}
|
|
39556
|
+
)
|
|
39557
|
+
] }) })
|
|
39558
|
+
] }),
|
|
39559
|
+
editing ? /* @__PURE__ */ jsxRuntime.jsx(RichTextEditor, { value, changeEvent: contentChangeEvent, placeholder }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
39139
39560
|
Box,
|
|
39140
39561
|
{
|
|
39141
|
-
|
|
39562
|
+
onClick: emitWithId(editEvent),
|
|
39142
39563
|
className: cn(
|
|
39143
|
-
"
|
|
39144
|
-
"
|
|
39145
|
-
"py-1 text-sm"
|
|
39564
|
+
"rounded-md px-1 -mx-1 min-h-[16rem]",
|
|
39565
|
+
editEvent && "cursor-text transition-colors hover:bg-muted/30"
|
|
39146
39566
|
),
|
|
39147
|
-
|
|
39148
|
-
|
|
39149
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
39150
|
-
Button,
|
|
39151
|
-
{
|
|
39152
|
-
type: "button",
|
|
39153
|
-
variant: "ghost",
|
|
39154
|
-
role: "menuitem",
|
|
39155
|
-
className: "flex w-full items-center gap-2 px-2 py-1.5 text-left justify-start rounded-none",
|
|
39156
|
-
onClick: () => {
|
|
39157
|
-
onDuplicate();
|
|
39158
|
-
setOpen(false);
|
|
39159
|
-
},
|
|
39160
|
-
children: [
|
|
39161
|
-
/* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "plus", className: "w-3.5 h-3.5" }),
|
|
39162
|
-
" ",
|
|
39163
|
-
t("richBlockEditor.duplicate")
|
|
39164
|
-
]
|
|
39165
|
-
}
|
|
39166
|
-
),
|
|
39167
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
39168
|
-
Button,
|
|
39169
|
-
{
|
|
39170
|
-
type: "button",
|
|
39171
|
-
variant: "ghost",
|
|
39172
|
-
role: "menuitem",
|
|
39173
|
-
className: "flex w-full items-center gap-2 px-2 py-1.5 text-left text-destructive hover:bg-muted justify-start rounded-none",
|
|
39174
|
-
onClick: () => {
|
|
39175
|
-
onDelete();
|
|
39176
|
-
setOpen(false);
|
|
39177
|
-
},
|
|
39178
|
-
children: [
|
|
39179
|
-
/* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "trash", className: "w-3.5 h-3.5" }),
|
|
39180
|
-
" ",
|
|
39181
|
-
t("common.delete")
|
|
39182
|
-
]
|
|
39183
|
-
}
|
|
39184
|
-
),
|
|
39185
|
-
CHANGEABLE_TYPES.includes(block.type) && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
39186
|
-
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "my-1 border-t border-border" }),
|
|
39187
|
-
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "px-2 py-1 text-xs uppercase tracking-wide text-muted-foreground", children: t("richBlockEditor.turnInto") }),
|
|
39188
|
-
CHANGEABLE_TYPES.filter((bt) => bt !== block.type).map((bt) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
39189
|
-
Button,
|
|
39190
|
-
{
|
|
39191
|
-
type: "button",
|
|
39192
|
-
variant: "ghost",
|
|
39193
|
-
role: "menuitem",
|
|
39194
|
-
className: "flex w-full items-center gap-2 px-2 py-1.5 text-left justify-start rounded-none",
|
|
39195
|
-
onClick: () => {
|
|
39196
|
-
onChangeType(bt);
|
|
39197
|
-
setOpen(false);
|
|
39198
|
-
},
|
|
39199
|
-
children: t(BLOCK_TYPE_LABEL_KEY[bt])
|
|
39200
|
-
},
|
|
39201
|
-
bt
|
|
39202
|
-
))
|
|
39203
|
-
] })
|
|
39204
|
-
]
|
|
39567
|
+
"data-testid": "document-body",
|
|
39568
|
+
children: value && value !== "" ? /* @__PURE__ */ jsxRuntime.jsx(RichTextEditor, { value, readOnly: true }) : /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "muted", children: placeholder ?? (t("documentPanel.placeholder") || "") })
|
|
39205
39569
|
}
|
|
39206
39570
|
)
|
|
39207
|
-
] });
|
|
39571
|
+
] }) });
|
|
39208
39572
|
}
|
|
39209
|
-
|
|
39210
|
-
|
|
39211
|
-
|
|
39212
|
-
|
|
39213
|
-
|
|
39214
|
-
|
|
39215
|
-
|
|
39216
|
-
|
|
39217
|
-
|
|
39218
|
-
|
|
39219
|
-
|
|
39220
|
-
|
|
39221
|
-
|
|
39222
|
-
|
|
39223
|
-
|
|
39224
|
-
|
|
39225
|
-
|
|
39226
|
-
},
|
|
39227
|
-
const handleInput = React94.useCallback(
|
|
39228
|
-
(e) => {
|
|
39229
|
-
onValueChange(e.currentTarget.textContent ?? "");
|
|
39230
|
-
},
|
|
39231
|
-
[onValueChange]
|
|
39232
|
-
);
|
|
39233
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
39234
|
-
Box,
|
|
39235
|
-
{
|
|
39236
|
-
as: tag,
|
|
39237
|
-
ref,
|
|
39238
|
-
contentEditable: !readOnly,
|
|
39239
|
-
suppressContentEditableWarning: true,
|
|
39240
|
-
role: readOnly ? void 0 : "textbox",
|
|
39241
|
-
"aria-label": ariaLabel,
|
|
39242
|
-
"aria-multiline": "true",
|
|
39243
|
-
"data-placeholder": placeholder,
|
|
39244
|
-
className: cn(
|
|
39245
|
-
"outline-none focus-visible:ring-1 focus-visible:ring-ring rounded-sm",
|
|
39246
|
-
"empty:before:content-[attr(data-placeholder)] empty:before:text-muted-foreground/60",
|
|
39247
|
-
className
|
|
39248
|
-
),
|
|
39249
|
-
onInput: handleInput
|
|
39250
|
-
}
|
|
39251
|
-
);
|
|
39573
|
+
var init_DocumentPanel = __esm({
|
|
39574
|
+
"components/core/molecules/DocumentPanel.tsx"() {
|
|
39575
|
+
"use client";
|
|
39576
|
+
init_cn();
|
|
39577
|
+
init_useEventBus();
|
|
39578
|
+
init_Box();
|
|
39579
|
+
init_Stack();
|
|
39580
|
+
init_Typography();
|
|
39581
|
+
init_Button();
|
|
39582
|
+
init_Card();
|
|
39583
|
+
init_Icon();
|
|
39584
|
+
init_Input();
|
|
39585
|
+
init_Menu();
|
|
39586
|
+
init_RichTextEditor();
|
|
39587
|
+
}
|
|
39588
|
+
});
|
|
39589
|
+
function renderIconInput5(icon, props) {
|
|
39590
|
+
return typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon, ...props });
|
|
39252
39591
|
}
|
|
39253
|
-
function
|
|
39254
|
-
|
|
39255
|
-
|
|
39256
|
-
|
|
39257
|
-
|
|
39258
|
-
|
|
39259
|
-
|
|
39260
|
-
|
|
39261
|
-
|
|
39592
|
+
function fieldName(field) {
|
|
39593
|
+
return field.name ?? field.key ?? "";
|
|
39594
|
+
}
|
|
39595
|
+
function relationLabel(value) {
|
|
39596
|
+
if (value === null || typeof value !== "object" || Array.isArray(value) || value instanceof Date) return null;
|
|
39597
|
+
for (const key of ["name", "title", "label"]) {
|
|
39598
|
+
const candidate = value[key];
|
|
39599
|
+
if (typeof candidate === "string" && candidate !== "") return candidate;
|
|
39600
|
+
}
|
|
39601
|
+
const id = value.id;
|
|
39602
|
+
return id !== void 0 && id !== null ? String(id) : null;
|
|
39603
|
+
}
|
|
39604
|
+
function fieldKind(field, value) {
|
|
39605
|
+
if (field.kind) return field.kind;
|
|
39606
|
+
if (field.options && field.options.length > 0) return "select";
|
|
39607
|
+
if (typeof value === "boolean" || field.format === "boolean") return "boolean";
|
|
39608
|
+
if (Array.isArray(value) || relationLabel(value ?? null) !== null) return "readonly";
|
|
39609
|
+
return "text";
|
|
39610
|
+
}
|
|
39611
|
+
function DocumentDetails({
|
|
39612
|
+
entity,
|
|
39613
|
+
fields,
|
|
39614
|
+
metaCommitEvent,
|
|
39615
|
+
title,
|
|
39616
|
+
className
|
|
39262
39617
|
}) {
|
|
39618
|
+
const eventBus = useEventBus();
|
|
39263
39619
|
const { t } = hooks.useTranslate();
|
|
39264
|
-
const
|
|
39265
|
-
|
|
39266
|
-
|
|
39267
|
-
);
|
|
39268
|
-
const
|
|
39269
|
-
(
|
|
39270
|
-
|
|
39271
|
-
|
|
39272
|
-
|
|
39273
|
-
|
|
39274
|
-
|
|
39275
|
-
|
|
39276
|
-
|
|
39277
|
-
|
|
39278
|
-
|
|
39279
|
-
|
|
39280
|
-
|
|
39281
|
-
|
|
39282
|
-
|
|
39283
|
-
|
|
39284
|
-
|
|
39285
|
-
|
|
39286
|
-
|
|
39287
|
-
|
|
39288
|
-
|
|
39289
|
-
|
|
39290
|
-
|
|
39291
|
-
|
|
39292
|
-
|
|
39293
|
-
|
|
39294
|
-
|
|
39295
|
-
|
|
39296
|
-
|
|
39297
|
-
|
|
39298
|
-
|
|
39299
|
-
|
|
39300
|
-
|
|
39301
|
-
}
|
|
39302
|
-
|
|
39303
|
-
|
|
39304
|
-
|
|
39305
|
-
|
|
39306
|
-
|
|
39307
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
39308
|
-
Editable,
|
|
39309
|
-
{
|
|
39310
|
-
tag: "h1",
|
|
39311
|
-
value: block.content ?? "",
|
|
39312
|
-
readOnly,
|
|
39313
|
-
placeholder: placeholder ?? t("richBlockEditor.placeholder.heading1"),
|
|
39314
|
-
ariaLabel: t("richBlockEditor.aria.heading1Block"),
|
|
39315
|
-
className: "text-3xl font-bold leading-tight",
|
|
39316
|
-
onValueChange: setContent
|
|
39317
|
-
}
|
|
39318
|
-
);
|
|
39319
|
-
case "heading-2":
|
|
39320
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
39321
|
-
Editable,
|
|
39322
|
-
{
|
|
39323
|
-
tag: "h2",
|
|
39324
|
-
value: block.content ?? "",
|
|
39325
|
-
readOnly,
|
|
39326
|
-
placeholder: placeholder ?? t("richBlockEditor.placeholder.heading2"),
|
|
39327
|
-
ariaLabel: t("richBlockEditor.aria.heading2Block"),
|
|
39328
|
-
className: "text-2xl font-semibold leading-tight",
|
|
39329
|
-
onValueChange: setContent
|
|
39330
|
-
}
|
|
39331
|
-
);
|
|
39332
|
-
case "heading-3":
|
|
39333
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
39334
|
-
Editable,
|
|
39335
|
-
{
|
|
39336
|
-
tag: "h3",
|
|
39337
|
-
value: block.content ?? "",
|
|
39338
|
-
readOnly,
|
|
39339
|
-
placeholder: placeholder ?? t("richBlockEditor.placeholder.heading3"),
|
|
39340
|
-
ariaLabel: t("richBlockEditor.aria.heading3Block"),
|
|
39341
|
-
className: "text-xl font-semibold leading-tight",
|
|
39342
|
-
onValueChange: setContent
|
|
39343
|
-
}
|
|
39344
|
-
);
|
|
39345
|
-
case "quote":
|
|
39346
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
39347
|
-
Editable,
|
|
39348
|
-
{
|
|
39349
|
-
tag: "blockquote",
|
|
39350
|
-
value: block.content ?? "",
|
|
39351
|
-
readOnly,
|
|
39352
|
-
placeholder: placeholder ?? t("richBlockEditor.placeholder.quote"),
|
|
39353
|
-
ariaLabel: t("richBlockEditor.aria.quoteBlock"),
|
|
39354
|
-
className: "border-l-4 border-primary/60 pl-4 italic text-muted-foreground",
|
|
39355
|
-
onValueChange: setContent
|
|
39356
|
-
}
|
|
39357
|
-
);
|
|
39358
|
-
case "code":
|
|
39359
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "rounded-md border border-border bg-muted/40", children: [
|
|
39360
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center justify-between border-b border-border px-3 py-1 text-xs text-muted-foreground", children: [
|
|
39361
|
-
/* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", variant: "caption", className: "uppercase tracking-wide", children: t("richBlockEditor.blockType.code") }),
|
|
39362
|
-
!readOnly && /* @__PURE__ */ jsxRuntime.jsx(
|
|
39363
|
-
Input,
|
|
39364
|
-
{
|
|
39365
|
-
inputType: "text",
|
|
39366
|
-
value: String(block.metadata?.language ?? "plaintext"),
|
|
39367
|
-
"aria-label": t("richBlockEditor.aria.codeLanguage"),
|
|
39368
|
-
className: cn(
|
|
39369
|
-
"h-6 w-32 rounded-sm border border-border bg-background",
|
|
39370
|
-
"px-2 text-xs outline-none focus:ring-1 focus:ring-ring"
|
|
39371
|
-
),
|
|
39372
|
-
onChange: (e) => setMetadata("language", e.target.value)
|
|
39373
|
-
}
|
|
39374
|
-
),
|
|
39375
|
-
readOnly && /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", variant: "caption", className: "text-xs", children: String(block.metadata?.language ?? "plaintext") })
|
|
39376
|
-
] }),
|
|
39377
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
39378
|
-
Editable,
|
|
39379
|
-
{
|
|
39380
|
-
tag: "pre",
|
|
39381
|
-
value: block.content ?? "",
|
|
39382
|
-
readOnly,
|
|
39383
|
-
placeholder: placeholder ?? t("richBlockEditor.placeholder.code"),
|
|
39384
|
-
ariaLabel: t("richBlockEditor.aria.codeBlock"),
|
|
39385
|
-
className: "block whitespace-pre-wrap p-3 font-mono text-sm leading-relaxed",
|
|
39386
|
-
onValueChange: setContent
|
|
39387
|
-
}
|
|
39388
|
-
)
|
|
39389
|
-
] });
|
|
39390
|
-
case "divider":
|
|
39391
|
-
return /* @__PURE__ */ jsxRuntime.jsx(Divider, { className: "my-2" });
|
|
39392
|
-
case "image": {
|
|
39393
|
-
const url = String(block.metadata?.url ?? "");
|
|
39394
|
-
const caption = String(block.metadata?.caption ?? "");
|
|
39395
|
-
const imgProps = {
|
|
39396
|
-
src: url,
|
|
39397
|
-
alt: caption || t("richBlockEditor.embeddedImage"),
|
|
39398
|
-
className: "max-h-96 w-full rounded-md border border-border object-contain"
|
|
39399
|
-
};
|
|
39400
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "space-y-2", children: [
|
|
39401
|
-
url ? /* @__PURE__ */ jsxRuntime.jsx(Box, { as: "img", ...imgProps }) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
39402
|
-
Box,
|
|
39403
|
-
{
|
|
39404
|
-
className: cn(
|
|
39405
|
-
"flex h-32 items-center justify-center",
|
|
39406
|
-
"rounded-md border border-dashed border-border",
|
|
39407
|
-
"text-sm text-muted-foreground"
|
|
39408
|
-
),
|
|
39409
|
-
children: [
|
|
39410
|
-
/* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "image", className: "mr-2 w-4 h-4" }),
|
|
39411
|
-
" ",
|
|
39412
|
-
t("richBlockEditor.noImageUrl")
|
|
39413
|
-
]
|
|
39414
|
-
}
|
|
39415
|
-
),
|
|
39416
|
-
!readOnly && /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex flex-col gap-2 sm:flex-row", children: [
|
|
39417
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
39418
|
-
Input,
|
|
39419
|
-
{
|
|
39420
|
-
inputType: "url",
|
|
39421
|
-
value: url,
|
|
39422
|
-
placeholder: "https://example.com/image.png",
|
|
39423
|
-
"aria-label": t("richBlockEditor.aria.imageUrl"),
|
|
39424
|
-
className: cn(
|
|
39425
|
-
"h-8 flex-1 rounded-sm border border-border bg-background",
|
|
39426
|
-
"px-2 text-sm outline-none focus:ring-1 focus:ring-ring"
|
|
39427
|
-
),
|
|
39428
|
-
onChange: (e) => setMetadata("url", e.target.value)
|
|
39429
|
-
}
|
|
39430
|
-
),
|
|
39431
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
39432
|
-
Input,
|
|
39433
|
-
{
|
|
39434
|
-
inputType: "text",
|
|
39435
|
-
value: caption,
|
|
39436
|
-
placeholder: t("richBlockEditor.placeholder.caption"),
|
|
39437
|
-
"aria-label": t("richBlockEditor.aria.imageCaption"),
|
|
39438
|
-
className: cn(
|
|
39439
|
-
"h-8 flex-1 rounded-sm border border-border bg-background",
|
|
39440
|
-
"px-2 text-sm outline-none focus:ring-1 focus:ring-ring"
|
|
39441
|
-
),
|
|
39442
|
-
onChange: (e) => setMetadata("caption", e.target.value)
|
|
39443
|
-
}
|
|
39444
|
-
)
|
|
39445
|
-
] }),
|
|
39446
|
-
readOnly && caption && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-center text-muted-foreground", children: caption })
|
|
39447
|
-
] });
|
|
39620
|
+
const [fieldDraft, setFieldDraft] = React94.useState(null);
|
|
39621
|
+
const recordId = entity?.id !== void 0 && entity?.id !== null ? String(entity.id) : "";
|
|
39622
|
+
const fieldDefs = (fields ?? []).filter((f3) => fieldName(f3) !== "");
|
|
39623
|
+
if (fieldDefs.length === 0) return null;
|
|
39624
|
+
const commitField = (name, next, current) => {
|
|
39625
|
+
setFieldDraft(null);
|
|
39626
|
+
if (!metaCommitEvent || next === current) return;
|
|
39627
|
+
eventBus.emit(`UI:${metaCommitEvent}`, { id: recordId, patch: { id: recordId, [name]: next } });
|
|
39628
|
+
};
|
|
39629
|
+
const renderValue = (field) => {
|
|
39630
|
+
const name = fieldName(field);
|
|
39631
|
+
const raw = getNestedValue(entity ?? {}, name);
|
|
39632
|
+
const kind = fieldKind(field, raw);
|
|
39633
|
+
const label = field.label ?? field.header ?? humanizeFieldName(name);
|
|
39634
|
+
if (kind === "boolean") {
|
|
39635
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
39636
|
+
Switch,
|
|
39637
|
+
{
|
|
39638
|
+
checked: Boolean(raw),
|
|
39639
|
+
disabled: !metaCommitEvent,
|
|
39640
|
+
"aria-label": label,
|
|
39641
|
+
onChange: (checked) => commitField(name, checked, Boolean(raw))
|
|
39642
|
+
}
|
|
39643
|
+
);
|
|
39644
|
+
}
|
|
39645
|
+
if (kind === "select") {
|
|
39646
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
39647
|
+
Select,
|
|
39648
|
+
{
|
|
39649
|
+
value: raw !== void 0 && raw !== null ? String(raw) : "",
|
|
39650
|
+
disabled: !metaCommitEvent,
|
|
39651
|
+
"aria-label": label,
|
|
39652
|
+
className: "h-8 w-full",
|
|
39653
|
+
options: (field.options ?? []).map((opt) => ({ value: opt, label: humanizeEnumValue(opt) })),
|
|
39654
|
+
onChange: (e) => commitField(name, e.target.value, raw)
|
|
39655
|
+
}
|
|
39656
|
+
);
|
|
39657
|
+
}
|
|
39658
|
+
if (kind === "readonly" || !metaCommitEvent) {
|
|
39659
|
+
if (Array.isArray(raw)) {
|
|
39660
|
+
if (raw.length === 0) {
|
|
39661
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: "\u2014" });
|
|
39662
|
+
}
|
|
39663
|
+
return /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", className: "flex-wrap", children: raw.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", children: relationLabel(item) ?? humanizeEnumValue(String(item)) }, i)) });
|
|
39448
39664
|
}
|
|
39449
|
-
|
|
39450
|
-
|
|
39451
|
-
|
|
39452
|
-
|
|
39453
|
-
|
|
39454
|
-
|
|
39455
|
-
|
|
39456
|
-
|
|
39457
|
-
|
|
39458
|
-
|
|
39459
|
-
|
|
39460
|
-
|
|
39461
|
-
|
|
39462
|
-
|
|
39463
|
-
|
|
39464
|
-
|
|
39465
|
-
|
|
39466
|
-
|
|
39467
|
-
|
|
39468
|
-
|
|
39469
|
-
|
|
39470
|
-
|
|
39471
|
-
|
|
39472
|
-
|
|
39473
|
-
|
|
39474
|
-
|
|
39475
|
-
|
|
39476
|
-
|
|
39477
|
-
|
|
39478
|
-
|
|
39479
|
-
|
|
39480
|
-
|
|
39481
|
-
|
|
39482
|
-
|
|
39483
|
-
|
|
39484
|
-
|
|
39485
|
-
|
|
39486
|
-
|
|
39487
|
-
|
|
39488
|
-
|
|
39489
|
-
|
|
39490
|
-
Button,
|
|
39491
|
-
{
|
|
39492
|
-
type: "button",
|
|
39493
|
-
variant: "ghost",
|
|
39494
|
-
className: cn(
|
|
39495
|
-
"inline-flex items-center gap-1 text-xs text-muted-foreground",
|
|
39496
|
-
"hover:text-foreground p-0 h-auto"
|
|
39497
|
-
),
|
|
39498
|
-
onClick: addListItem,
|
|
39499
|
-
children: [
|
|
39500
|
-
/* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "plus", className: "w-3 h-3" }),
|
|
39501
|
-
" ",
|
|
39502
|
-
t("richBlockEditor.addItem")
|
|
39503
|
-
]
|
|
39504
|
-
}
|
|
39505
|
-
) })
|
|
39506
|
-
]
|
|
39665
|
+
const shown2 = raw === void 0 || raw === null || raw === "" ? "\u2014" : relationLabel(raw) ?? formatValue(raw, field.format);
|
|
39666
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "break-words", children: shown2 });
|
|
39667
|
+
}
|
|
39668
|
+
if (fieldDraft?.name === name) {
|
|
39669
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
39670
|
+
Input,
|
|
39671
|
+
{
|
|
39672
|
+
value: fieldDraft.value,
|
|
39673
|
+
autoFocus: true,
|
|
39674
|
+
"aria-label": label,
|
|
39675
|
+
className: "h-8 w-full",
|
|
39676
|
+
onChange: (e) => setFieldDraft({ name, value: e.target.value }),
|
|
39677
|
+
onBlur: () => commitField(name, fieldDraft.value.trim(), raw),
|
|
39678
|
+
onKeyDown: (e) => {
|
|
39679
|
+
if (e.key === "Enter") {
|
|
39680
|
+
e.preventDefault();
|
|
39681
|
+
commitField(name, fieldDraft.value.trim(), raw);
|
|
39682
|
+
} else if (e.key === "Escape") {
|
|
39683
|
+
e.preventDefault();
|
|
39684
|
+
setFieldDraft(null);
|
|
39685
|
+
}
|
|
39686
|
+
},
|
|
39687
|
+
"data-testid": `document-property-input-${name}`
|
|
39688
|
+
}
|
|
39689
|
+
);
|
|
39690
|
+
}
|
|
39691
|
+
const shown = raw === void 0 || raw === null || raw === "" ? "\u2014" : relationLabel(raw) ?? formatValue(raw, field.format);
|
|
39692
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
39693
|
+
Box,
|
|
39694
|
+
{
|
|
39695
|
+
role: "button",
|
|
39696
|
+
tabIndex: 0,
|
|
39697
|
+
className: cn(
|
|
39698
|
+
"cursor-text rounded px-1 -mx-1 transition-colors hover:bg-muted/40",
|
|
39699
|
+
(raw === void 0 || raw === null || raw === "") && "text-muted-foreground"
|
|
39700
|
+
),
|
|
39701
|
+
onClick: () => setFieldDraft({ name, value: raw !== void 0 && raw !== null ? String(raw) : "" }),
|
|
39702
|
+
onKeyDown: (e) => {
|
|
39703
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
39704
|
+
e.preventDefault();
|
|
39705
|
+
setFieldDraft({ name, value: raw !== void 0 && raw !== null ? String(raw) : "" });
|
|
39507
39706
|
}
|
|
39508
|
-
|
|
39707
|
+
},
|
|
39708
|
+
"data-testid": `document-property-${name}`,
|
|
39709
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "break-words", children: shown })
|
|
39509
39710
|
}
|
|
39510
|
-
|
|
39511
|
-
default:
|
|
39512
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
39513
|
-
Editable,
|
|
39514
|
-
{
|
|
39515
|
-
tag: "p",
|
|
39516
|
-
value: block.content ?? "",
|
|
39517
|
-
readOnly,
|
|
39518
|
-
placeholder: placeholder ?? t("richBlockEditor.placeholder.paragraph"),
|
|
39519
|
-
ariaLabel: t("richBlockEditor.aria.paragraphBlock"),
|
|
39520
|
-
className: "leading-7",
|
|
39521
|
-
onValueChange: setContent
|
|
39522
|
-
}
|
|
39523
|
-
);
|
|
39524
|
-
}
|
|
39711
|
+
);
|
|
39525
39712
|
};
|
|
39526
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
39527
|
-
|
|
39528
|
-
{
|
|
39529
|
-
|
|
39530
|
-
|
|
39531
|
-
|
|
39532
|
-
|
|
39533
|
-
|
|
39534
|
-
|
|
39535
|
-
|
|
39536
|
-
|
|
39537
|
-
|
|
39538
|
-
|
|
39539
|
-
|
|
39540
|
-
readOnly,
|
|
39541
|
-
onDelete,
|
|
39542
|
-
onDuplicate,
|
|
39543
|
-
onChangeType
|
|
39544
|
-
}
|
|
39545
|
-
) }),
|
|
39546
|
-
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "min-w-0 flex-1", children: renderBody() })
|
|
39547
|
-
]
|
|
39548
|
-
}
|
|
39549
|
-
);
|
|
39713
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Card, { variant: "bordered", className: cn("w-full", className), children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", className: "p-4", children: [
|
|
39714
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", weight: "medium", className: "uppercase tracking-wide", children: title ?? (t("documentDetails.title") || "Details") }),
|
|
39715
|
+
/* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "sm", children: fieldDefs.map((field) => {
|
|
39716
|
+
const name = fieldName(field);
|
|
39717
|
+
const label = field.label ?? field.header ?? humanizeFieldName(name);
|
|
39718
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
|
|
39719
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "items-center", children: [
|
|
39720
|
+
field.icon && renderIconInput5(field.icon, { size: "xs", className: "text-muted-foreground" }),
|
|
39721
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "muted", children: label })
|
|
39722
|
+
] }),
|
|
39723
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "min-w-0", children: renderValue(field) })
|
|
39724
|
+
] }, name);
|
|
39725
|
+
}) })
|
|
39726
|
+
] }) });
|
|
39550
39727
|
}
|
|
39551
|
-
var
|
|
39552
|
-
|
|
39553
|
-
"components/core/molecules/RichBlockEditor.tsx"() {
|
|
39728
|
+
var init_DocumentDetails = __esm({
|
|
39729
|
+
"components/core/molecules/DocumentDetails.tsx"() {
|
|
39554
39730
|
"use client";
|
|
39555
39731
|
init_cn();
|
|
39556
|
-
|
|
39557
|
-
|
|
39558
|
-
|
|
39732
|
+
init_format();
|
|
39733
|
+
init_getNestedValue();
|
|
39734
|
+
init_useEventBus();
|
|
39559
39735
|
init_Box();
|
|
39560
|
-
|
|
39561
|
-
|
|
39736
|
+
init_Stack();
|
|
39737
|
+
init_Typography();
|
|
39738
|
+
init_Badge();
|
|
39739
|
+
init_Card();
|
|
39562
39740
|
init_Icon();
|
|
39563
|
-
|
|
39564
|
-
|
|
39565
|
-
|
|
39566
|
-
{ type: "heading-1", labelKey: "richBlockEditor.toolbar.h1", icon: LucideIcons2.Heading1 },
|
|
39567
|
-
{ type: "heading-2", labelKey: "richBlockEditor.toolbar.h2", icon: LucideIcons2.Heading2 },
|
|
39568
|
-
{ type: "heading-3", labelKey: "richBlockEditor.toolbar.h3", icon: LucideIcons2.Heading3 },
|
|
39569
|
-
{ type: "bullet-list", labelKey: "richBlockEditor.toolbar.bulletList", icon: LucideIcons2.List },
|
|
39570
|
-
{ type: "numbered-list", labelKey: "richBlockEditor.toolbar.numbered", icon: LucideIcons2.ListOrdered },
|
|
39571
|
-
{ type: "quote", labelKey: "richBlockEditor.toolbar.quote", icon: LucideIcons2.Quote },
|
|
39572
|
-
{ type: "code", labelKey: "richBlockEditor.toolbar.code", icon: LucideIcons2.Code },
|
|
39573
|
-
{ type: "divider", labelKey: "richBlockEditor.toolbar.divider", icon: LucideIcons2.Minus },
|
|
39574
|
-
{ type: "image", labelKey: "richBlockEditor.toolbar.image", icon: LucideIcons2.Image }
|
|
39575
|
-
];
|
|
39576
|
-
BLOCK_TYPE_LABEL_KEY = {
|
|
39577
|
-
paragraph: "richBlockEditor.blockType.paragraph",
|
|
39578
|
-
"heading-1": "richBlockEditor.blockType.heading1",
|
|
39579
|
-
"heading-2": "richBlockEditor.blockType.heading2",
|
|
39580
|
-
"heading-3": "richBlockEditor.blockType.heading3",
|
|
39581
|
-
"bullet-list": "richBlockEditor.blockType.bulletList",
|
|
39582
|
-
"numbered-list": "richBlockEditor.blockType.numberedList",
|
|
39583
|
-
quote: "richBlockEditor.blockType.quote",
|
|
39584
|
-
code: "richBlockEditor.blockType.code",
|
|
39585
|
-
divider: "richBlockEditor.blockType.divider",
|
|
39586
|
-
image: "richBlockEditor.blockType.image"
|
|
39587
|
-
};
|
|
39588
|
-
CHANGEABLE_TYPES = [
|
|
39589
|
-
"paragraph",
|
|
39590
|
-
"heading-1",
|
|
39591
|
-
"heading-2",
|
|
39592
|
-
"heading-3",
|
|
39593
|
-
"bullet-list",
|
|
39594
|
-
"numbered-list",
|
|
39595
|
-
"quote",
|
|
39596
|
-
"code"
|
|
39597
|
-
];
|
|
39598
|
-
_idSeq = 0;
|
|
39599
|
-
BLOCK_TYPES = /* @__PURE__ */ new Set([
|
|
39600
|
-
"paragraph",
|
|
39601
|
-
"heading-1",
|
|
39602
|
-
"heading-2",
|
|
39603
|
-
"heading-3",
|
|
39604
|
-
"bullet-list",
|
|
39605
|
-
"numbered-list",
|
|
39606
|
-
"quote",
|
|
39607
|
-
"code",
|
|
39608
|
-
"divider",
|
|
39609
|
-
"image"
|
|
39610
|
-
]);
|
|
39611
|
-
RichBlockEditor = ({
|
|
39612
|
-
initialBlocks,
|
|
39613
|
-
onChange,
|
|
39614
|
-
changeEvent,
|
|
39615
|
-
readOnly = false,
|
|
39616
|
-
placeholder,
|
|
39617
|
-
enableBlocks = true,
|
|
39618
|
-
showToolbar = true,
|
|
39619
|
-
className
|
|
39620
|
-
}) => {
|
|
39621
|
-
const { t } = hooks.useTranslate();
|
|
39622
|
-
const [blocks, setBlocks] = React94.useState(
|
|
39623
|
-
() => normalizeBlocks(initialBlocks)
|
|
39624
|
-
);
|
|
39625
|
-
const onChangeRef = React94.useRef(onChange);
|
|
39626
|
-
React94.useEffect(() => {
|
|
39627
|
-
onChangeRef.current = onChange;
|
|
39628
|
-
}, [onChange]);
|
|
39629
|
-
const eventBus = useEventBus();
|
|
39630
|
-
const changeEventRef = React94.useRef(changeEvent);
|
|
39631
|
-
React94.useEffect(() => {
|
|
39632
|
-
changeEventRef.current = changeEvent;
|
|
39633
|
-
}, [changeEvent]);
|
|
39634
|
-
const commit = React94.useCallback((next) => {
|
|
39635
|
-
setBlocks(next);
|
|
39636
|
-
onChangeRef.current?.(next);
|
|
39637
|
-
const evt = changeEventRef.current;
|
|
39638
|
-
if (evt) eventBus.emit(`UI:${evt}`, { blocks: next });
|
|
39639
|
-
}, [eventBus]);
|
|
39640
|
-
const handleAppend = React94.useCallback(
|
|
39641
|
-
(type) => {
|
|
39642
|
-
if (readOnly) return;
|
|
39643
|
-
commit([...blocks, createBlock(type)]);
|
|
39644
|
-
},
|
|
39645
|
-
[blocks, commit, readOnly]
|
|
39646
|
-
);
|
|
39647
|
-
const handleUpdate = React94.useCallback(
|
|
39648
|
-
(id, updater) => {
|
|
39649
|
-
commit(replaceBlock(blocks, id, updater));
|
|
39650
|
-
},
|
|
39651
|
-
[blocks, commit]
|
|
39652
|
-
);
|
|
39653
|
-
const handleDelete = React94.useCallback(
|
|
39654
|
-
(id) => {
|
|
39655
|
-
const next = removeBlock(blocks, id);
|
|
39656
|
-
commit(next.length > 0 ? next : [createBlock("paragraph")]);
|
|
39657
|
-
},
|
|
39658
|
-
[blocks, commit]
|
|
39659
|
-
);
|
|
39660
|
-
const handleDuplicate = React94.useCallback(
|
|
39661
|
-
(id) => {
|
|
39662
|
-
const target = blocks.find((b) => b.id === id);
|
|
39663
|
-
if (!target) return;
|
|
39664
|
-
commit(insertAfter(blocks, id, duplicateBlock(target)));
|
|
39665
|
-
},
|
|
39666
|
-
[blocks, commit]
|
|
39667
|
-
);
|
|
39668
|
-
const handleChangeType = React94.useCallback(
|
|
39669
|
-
(id, type) => {
|
|
39670
|
-
commit(
|
|
39671
|
-
replaceBlock(blocks, id, (b) => changeBlockType(b, type))
|
|
39672
|
-
);
|
|
39673
|
-
},
|
|
39674
|
-
[blocks, commit]
|
|
39675
|
-
);
|
|
39676
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
39677
|
-
Card,
|
|
39678
|
-
{
|
|
39679
|
-
variant: "bordered",
|
|
39680
|
-
padding: "none",
|
|
39681
|
-
className: cn("flex flex-col text-card-foreground", className),
|
|
39682
|
-
children: [
|
|
39683
|
-
enableBlocks && showToolbar && !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
|
|
39684
|
-
Box,
|
|
39685
|
-
{
|
|
39686
|
-
role: "toolbar",
|
|
39687
|
-
"aria-label": t("richBlockEditor.editorToolbar"),
|
|
39688
|
-
className: cn(
|
|
39689
|
-
"flex flex-wrap items-center gap-1",
|
|
39690
|
-
"border-b border-border bg-muted/30 px-2 py-2"
|
|
39691
|
-
),
|
|
39692
|
-
children: TOOLBAR_ENTRIES.map((entry) => {
|
|
39693
|
-
const Icon2 = entry.icon;
|
|
39694
|
-
const entryLabel = t(entry.labelKey);
|
|
39695
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
39696
|
-
Button,
|
|
39697
|
-
{
|
|
39698
|
-
type: "button",
|
|
39699
|
-
variant: "ghost",
|
|
39700
|
-
size: "sm",
|
|
39701
|
-
"aria-label": t("richBlockEditor.insertEntry", { label: entryLabel }),
|
|
39702
|
-
title: entryLabel,
|
|
39703
|
-
onClick: () => handleAppend(entry.type),
|
|
39704
|
-
children: [
|
|
39705
|
-
/* @__PURE__ */ jsxRuntime.jsx(Icon2, { size: 14 }),
|
|
39706
|
-
/* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", variant: "caption", className: "ml-1 hidden text-xs sm:inline", children: entryLabel })
|
|
39707
|
-
]
|
|
39708
|
-
},
|
|
39709
|
-
entry.type
|
|
39710
|
-
);
|
|
39711
|
-
})
|
|
39712
|
-
}
|
|
39713
|
-
),
|
|
39714
|
-
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex flex-col gap-1 px-3 py-3", children: blocks.map((block) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
39715
|
-
BlockRow,
|
|
39716
|
-
{
|
|
39717
|
-
block,
|
|
39718
|
-
readOnly,
|
|
39719
|
-
showAffordances: enableBlocks,
|
|
39720
|
-
placeholder,
|
|
39721
|
-
onUpdate: (updater) => handleUpdate(block.id, updater),
|
|
39722
|
-
onDelete: () => handleDelete(block.id),
|
|
39723
|
-
onDuplicate: () => handleDuplicate(block.id),
|
|
39724
|
-
onChangeType: (type) => handleChangeType(block.id, type)
|
|
39725
|
-
},
|
|
39726
|
-
block.id
|
|
39727
|
-
)) })
|
|
39728
|
-
]
|
|
39729
|
-
}
|
|
39730
|
-
);
|
|
39731
|
-
};
|
|
39732
|
-
RichBlockEditor.displayName = "RichBlockEditor";
|
|
39741
|
+
init_Input();
|
|
39742
|
+
init_Select();
|
|
39743
|
+
init_Switch();
|
|
39733
39744
|
}
|
|
39734
39745
|
});
|
|
39735
39746
|
function collectInitiallyCollapsed(nodes, acc) {
|
|
@@ -44269,8 +44280,8 @@ var init_DataTable = __esm({
|
|
|
44269
44280
|
DataTable.displayName = "DataTable";
|
|
44270
44281
|
}
|
|
44271
44282
|
});
|
|
44272
|
-
function getBadgeVariant(
|
|
44273
|
-
const name =
|
|
44283
|
+
function getBadgeVariant(fieldName2, value) {
|
|
44284
|
+
const name = fieldName2.toLowerCase();
|
|
44274
44285
|
const val = String(value).toLowerCase();
|
|
44275
44286
|
if (name.includes("status")) {
|
|
44276
44287
|
if (val.includes("complete") || val.includes("done") || val.includes("active"))
|
|
@@ -44286,12 +44297,12 @@ function getBadgeVariant(fieldName, value) {
|
|
|
44286
44297
|
}
|
|
44287
44298
|
return "default";
|
|
44288
44299
|
}
|
|
44289
|
-
function formatFieldValue2(value,
|
|
44300
|
+
function formatFieldValue2(value, fieldName2) {
|
|
44290
44301
|
if (typeof value === "number") {
|
|
44291
|
-
if (
|
|
44302
|
+
if (fieldName2.toLowerCase().includes("progress") || fieldName2.toLowerCase().includes("percent")) {
|
|
44292
44303
|
return `${value}%`;
|
|
44293
44304
|
}
|
|
44294
|
-
if (
|
|
44305
|
+
if (fieldName2.toLowerCase().includes("budget") || fieldName2.toLowerCase().includes("cost")) {
|
|
44295
44306
|
return `$${value.toLocaleString()}`;
|
|
44296
44307
|
}
|
|
44297
44308
|
return value.toLocaleString();
|
|
@@ -44301,7 +44312,7 @@ function formatFieldValue2(value, fieldName) {
|
|
|
44301
44312
|
}
|
|
44302
44313
|
return String(value);
|
|
44303
44314
|
}
|
|
44304
|
-
function renderRichFieldValue(value,
|
|
44315
|
+
function renderRichFieldValue(value, fieldName2, fieldType, meta) {
|
|
44305
44316
|
if (value === void 0 || value === null) return "\u2014";
|
|
44306
44317
|
const str = String(value);
|
|
44307
44318
|
switch (fieldType) {
|
|
@@ -44312,7 +44323,7 @@ function renderRichFieldValue(value, fieldName, fieldType, meta) {
|
|
|
44312
44323
|
"img",
|
|
44313
44324
|
{
|
|
44314
44325
|
src: str,
|
|
44315
|
-
alt: formatFieldLabel(
|
|
44326
|
+
alt: formatFieldLabel(fieldName2),
|
|
44316
44327
|
className: "max-w-full max-h-64 rounded-md object-contain",
|
|
44317
44328
|
loading: "lazy"
|
|
44318
44329
|
}
|
|
@@ -44447,9 +44458,9 @@ function renderRichFieldValue(value, fieldName, fieldType, meta) {
|
|
|
44447
44458
|
return /* @__PURE__ */ jsxRuntime.jsx("a", { href: `tel:${str}`, className: "text-primary hover:underline", children: str });
|
|
44448
44459
|
default:
|
|
44449
44460
|
if (meta?.values && meta.values.length > 0 && meta.values.includes(str)) {
|
|
44450
|
-
return /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: getBadgeVariant(
|
|
44461
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: getBadgeVariant(fieldName2, str), children: humanizeEnumValue(str) });
|
|
44451
44462
|
}
|
|
44452
|
-
return formatFieldValue2(value,
|
|
44463
|
+
return formatFieldValue2(value, fieldName2);
|
|
44453
44464
|
}
|
|
44454
44465
|
}
|
|
44455
44466
|
function normalizeFieldDefs(fields) {
|
|
@@ -45292,7 +45303,21 @@ var init_Form = __esm({
|
|
|
45292
45303
|
const normalizedInitialData = React94__namespace.default.useMemo(() => {
|
|
45293
45304
|
const entityRowAsInitial = isPlainEntityRow(entity) ? entity : void 0;
|
|
45294
45305
|
const callerInitial = initialData !== null && typeof initialData === "object" && !Array.isArray(initialData) ? initialData : {};
|
|
45295
|
-
|
|
45306
|
+
const merged = entityRowAsInitial !== void 0 ? { ...entityRowAsInitial, ...callerInitial } : callerInitial;
|
|
45307
|
+
const toId = (value) => {
|
|
45308
|
+
if (value === null || value === void 0 || typeof value !== "object" || value instanceof Date) return value;
|
|
45309
|
+
if (Array.isArray(value)) {
|
|
45310
|
+
return value.map(
|
|
45311
|
+
(item) => item !== null && typeof item === "object" && !Array.isArray(item) && !(item instanceof Date) && item.id !== void 0 && item.id !== null ? String(item.id) : item
|
|
45312
|
+
);
|
|
45313
|
+
}
|
|
45314
|
+
return value.id !== void 0 && value.id !== null ? String(value.id) : value;
|
|
45315
|
+
};
|
|
45316
|
+
const normalized = {};
|
|
45317
|
+
for (const [key, value] of Object.entries(merged)) {
|
|
45318
|
+
normalized[key] = key === "id" ? value : toId(value);
|
|
45319
|
+
}
|
|
45320
|
+
return normalized;
|
|
45296
45321
|
}, [entity, initialData]);
|
|
45297
45322
|
const entityDerivedFields = React94__namespace.default.useMemo(() => {
|
|
45298
45323
|
if (fields && fields.length > 0) return void 0;
|
|
@@ -45421,8 +45446,8 @@ var init_Form = __esm({
|
|
|
45421
45446
|
checkViolations(name, newFormData);
|
|
45422
45447
|
};
|
|
45423
45448
|
const isFieldVisible = React94__namespace.default.useCallback(
|
|
45424
|
-
(
|
|
45425
|
-
const condition = conditionalFields[
|
|
45449
|
+
(fieldName2) => {
|
|
45450
|
+
const condition = conditionalFields[fieldName2];
|
|
45426
45451
|
if (!condition) return true;
|
|
45427
45452
|
return Boolean(evaluateFormExpression(condition, evalContext));
|
|
45428
45453
|
},
|
|
@@ -45468,9 +45493,9 @@ var init_Form = __esm({
|
|
|
45468
45493
|
};
|
|
45469
45494
|
const handleInvalid = (e) => {
|
|
45470
45495
|
const target = e.target;
|
|
45471
|
-
const
|
|
45496
|
+
const fieldName2 = target.getAttribute("data-field-name") ?? target.name ?? "";
|
|
45472
45497
|
const fieldMessage = target.validationMessage || "Invalid value";
|
|
45473
|
-
debug("forms", "invalid", { mode: formMode, fieldName, fieldMessage });
|
|
45498
|
+
debug("forms", "invalid", { mode: formMode, fieldName: fieldName2, fieldMessage });
|
|
45474
45499
|
queueMicrotask(() => {
|
|
45475
45500
|
const form = formRef.current;
|
|
45476
45501
|
if (!form) return;
|
|
@@ -45506,22 +45531,22 @@ var init_Form = __esm({
|
|
|
45506
45531
|
};
|
|
45507
45532
|
const renderField = React94__namespace.default.useCallback(
|
|
45508
45533
|
(field) => {
|
|
45509
|
-
const
|
|
45510
|
-
if (!
|
|
45511
|
-
if (!isFieldVisible(
|
|
45534
|
+
const fieldName2 = field.name || field.field;
|
|
45535
|
+
if (!fieldName2) return null;
|
|
45536
|
+
if (!isFieldVisible(fieldName2)) {
|
|
45512
45537
|
return null;
|
|
45513
45538
|
}
|
|
45514
45539
|
const inputType = determineInputType(field);
|
|
45515
|
-
const label = field.label ||
|
|
45516
|
-
const currentValue = formData[
|
|
45517
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", "data-field":
|
|
45540
|
+
const label = field.label || fieldName2.charAt(0).toUpperCase() + fieldName2.slice(1).replace(/([A-Z])/g, " $1");
|
|
45541
|
+
const currentValue = formData[fieldName2] ?? field.defaultValue ?? "";
|
|
45542
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", "data-field": fieldName2, children: [
|
|
45518
45543
|
inputType !== "checkbox" && /* @__PURE__ */ jsxRuntime.jsxs(Typography, { as: "label", variant: "label", weight: "bold", children: [
|
|
45519
45544
|
label,
|
|
45520
45545
|
field.required && /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", color: "error", className: "ml-1", children: "*" })
|
|
45521
45546
|
] }),
|
|
45522
|
-
renderFieldInput(field,
|
|
45547
|
+
renderFieldInput(field, fieldName2, inputType, currentValue, label),
|
|
45523
45548
|
field.hint && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "muted", children: field.hint })
|
|
45524
|
-
] },
|
|
45549
|
+
] }, fieldName2);
|
|
45525
45550
|
},
|
|
45526
45551
|
[formData, isFieldVisible, relationsData, relationsLoading, isLoading]
|
|
45527
45552
|
);
|
|
@@ -45550,8 +45575,8 @@ var init_Form = __esm({
|
|
|
45550
45575
|
}
|
|
45551
45576
|
return field;
|
|
45552
45577
|
}).map((field) => {
|
|
45553
|
-
const
|
|
45554
|
-
const override = fieldOverrides?.find((o) => o.name ===
|
|
45578
|
+
const fieldName2 = field.name || field.field;
|
|
45579
|
+
const override = fieldOverrides?.find((o) => o.name === fieldName2);
|
|
45555
45580
|
if (!override) return field;
|
|
45556
45581
|
return {
|
|
45557
45582
|
...field,
|
|
@@ -45607,11 +45632,11 @@ var init_Form = __esm({
|
|
|
45607
45632
|
] }, section.id);
|
|
45608
45633
|
}).filter(Boolean);
|
|
45609
45634
|
}, [sections, isSectionVisible, collapsedSections, renderField, gap]);
|
|
45610
|
-
function renderFieldInput(field,
|
|
45635
|
+
function renderFieldInput(field, fieldName2, inputType, currentValue, label) {
|
|
45611
45636
|
const commonProps = {
|
|
45612
|
-
id:
|
|
45613
|
-
name:
|
|
45614
|
-
"data-field-name":
|
|
45637
|
+
id: fieldName2,
|
|
45638
|
+
name: fieldName2,
|
|
45639
|
+
"data-field-name": fieldName2,
|
|
45615
45640
|
required: field.required,
|
|
45616
45641
|
disabled: isLoading,
|
|
45617
45642
|
placeholder: field.placeholder,
|
|
@@ -45625,7 +45650,7 @@ var init_Form = __esm({
|
|
|
45625
45650
|
...commonProps,
|
|
45626
45651
|
label: label + (field.required ? " *" : ""),
|
|
45627
45652
|
checked: Boolean(currentValue),
|
|
45628
|
-
onChange: (e) => handleChange(
|
|
45653
|
+
onChange: (e) => handleChange(fieldName2, e.target.checked)
|
|
45629
45654
|
}
|
|
45630
45655
|
);
|
|
45631
45656
|
case "textarea":
|
|
@@ -45634,7 +45659,7 @@ var init_Form = __esm({
|
|
|
45634
45659
|
{
|
|
45635
45660
|
...commonProps,
|
|
45636
45661
|
value: String(currentValue),
|
|
45637
|
-
onChange: (e) => handleChange(
|
|
45662
|
+
onChange: (e) => handleChange(fieldName2, e.target.value),
|
|
45638
45663
|
minLength: field.min,
|
|
45639
45664
|
maxLength: field.max
|
|
45640
45665
|
}
|
|
@@ -45647,14 +45672,14 @@ var init_Form = __esm({
|
|
|
45647
45672
|
...commonProps,
|
|
45648
45673
|
options,
|
|
45649
45674
|
value: String(currentValue),
|
|
45650
|
-
onValueChange: (v) => handleChange(
|
|
45675
|
+
onValueChange: (v) => handleChange(fieldName2, v),
|
|
45651
45676
|
placeholder: field.placeholder || `Select ${label}...`
|
|
45652
45677
|
}
|
|
45653
45678
|
);
|
|
45654
45679
|
}
|
|
45655
45680
|
case "relation": {
|
|
45656
|
-
const relationOptions = relationsData[
|
|
45657
|
-
const relationLoading = relationsLoading[
|
|
45681
|
+
const relationOptions = relationsData[fieldName2] || [];
|
|
45682
|
+
const relationLoading = relationsLoading[fieldName2] || false;
|
|
45658
45683
|
if (field.relation?.cardinality !== void 0 && MANY_CARDINALITIES.includes(field.relation.cardinality)) {
|
|
45659
45684
|
const selectedValues = Array.isArray(currentValue) ? currentValue.map((v) => String(v)) : [];
|
|
45660
45685
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -45666,7 +45691,7 @@ var init_Form = __esm({
|
|
|
45666
45691
|
clearable: true,
|
|
45667
45692
|
options: [...relationOptions],
|
|
45668
45693
|
value: selectedValues,
|
|
45669
|
-
onValueChange: (value) => handleChange(
|
|
45694
|
+
onValueChange: (value) => handleChange(fieldName2, Array.isArray(value) ? value : [value]),
|
|
45670
45695
|
placeholder: field.placeholder || `Select ${label}...`
|
|
45671
45696
|
}
|
|
45672
45697
|
);
|
|
@@ -45676,7 +45701,7 @@ var init_Form = __esm({
|
|
|
45676
45701
|
{
|
|
45677
45702
|
...commonProps,
|
|
45678
45703
|
value: currentValue ? String(currentValue) : void 0,
|
|
45679
|
-
onChange: (value) => handleChange(
|
|
45704
|
+
onChange: (value) => handleChange(fieldName2, value),
|
|
45680
45705
|
options: relationOptions,
|
|
45681
45706
|
isLoading: relationLoading,
|
|
45682
45707
|
placeholder: field.placeholder || `Select ${label}...`,
|
|
@@ -45693,7 +45718,7 @@ var init_Form = __esm({
|
|
|
45693
45718
|
placeholder: field.placeholder,
|
|
45694
45719
|
disabled: isLoading,
|
|
45695
45720
|
value: arrayValue,
|
|
45696
|
-
onChange: (next) => handleChange(
|
|
45721
|
+
onChange: (next) => handleChange(fieldName2, [...next])
|
|
45697
45722
|
}
|
|
45698
45723
|
);
|
|
45699
45724
|
}
|
|
@@ -45705,7 +45730,7 @@ var init_Form = __esm({
|
|
|
45705
45730
|
type: "number",
|
|
45706
45731
|
value: currentValue !== void 0 && currentValue !== "" ? String(currentValue) : "",
|
|
45707
45732
|
onChange: (e) => handleChange(
|
|
45708
|
-
|
|
45733
|
+
fieldName2,
|
|
45709
45734
|
e.target.value ? Number(e.target.value) : void 0
|
|
45710
45735
|
),
|
|
45711
45736
|
min: field.min,
|
|
@@ -45722,7 +45747,7 @@ var init_Form = __esm({
|
|
|
45722
45747
|
icon: LucideIcons2.DollarSign,
|
|
45723
45748
|
value: currentValue !== void 0 && currentValue !== "" ? String(currentValue) : "",
|
|
45724
45749
|
onChange: (e) => handleChange(
|
|
45725
|
-
|
|
45750
|
+
fieldName2,
|
|
45726
45751
|
e.target.value ? Number(e.target.value) : void 0
|
|
45727
45752
|
),
|
|
45728
45753
|
min: field.min,
|
|
@@ -45740,7 +45765,7 @@ var init_Form = __esm({
|
|
|
45740
45765
|
const f3 = files[0];
|
|
45741
45766
|
if (!f3) return;
|
|
45742
45767
|
const reader = new FileReader();
|
|
45743
|
-
reader.onload = () => handleChange(
|
|
45768
|
+
reader.onload = () => handleChange(fieldName2, {
|
|
45744
45769
|
name: f3.name,
|
|
45745
45770
|
mimeType: f3.type,
|
|
45746
45771
|
sizeBytes: f3.size,
|
|
@@ -45757,7 +45782,7 @@ var init_Form = __esm({
|
|
|
45757
45782
|
...commonProps,
|
|
45758
45783
|
type: "date",
|
|
45759
45784
|
value: formatDateValue(currentValue),
|
|
45760
|
-
onChange: (e) => handleChange(
|
|
45785
|
+
onChange: (e) => handleChange(fieldName2, e.target.value)
|
|
45761
45786
|
}
|
|
45762
45787
|
);
|
|
45763
45788
|
case "datetime-local":
|
|
@@ -45767,7 +45792,7 @@ var init_Form = __esm({
|
|
|
45767
45792
|
...commonProps,
|
|
45768
45793
|
type: "datetime-local",
|
|
45769
45794
|
value: formatDateTimeValue(currentValue),
|
|
45770
|
-
onChange: (e) => handleChange(
|
|
45795
|
+
onChange: (e) => handleChange(fieldName2, e.target.value)
|
|
45771
45796
|
}
|
|
45772
45797
|
);
|
|
45773
45798
|
case "email":
|
|
@@ -45777,7 +45802,7 @@ var init_Form = __esm({
|
|
|
45777
45802
|
...commonProps,
|
|
45778
45803
|
type: "email",
|
|
45779
45804
|
value: String(currentValue),
|
|
45780
|
-
onChange: (e) => handleChange(
|
|
45805
|
+
onChange: (e) => handleChange(fieldName2, e.target.value),
|
|
45781
45806
|
minLength: field.min,
|
|
45782
45807
|
maxLength: field.max
|
|
45783
45808
|
}
|
|
@@ -45789,7 +45814,7 @@ var init_Form = __esm({
|
|
|
45789
45814
|
...commonProps,
|
|
45790
45815
|
type: "url",
|
|
45791
45816
|
value: String(currentValue),
|
|
45792
|
-
onChange: (e) => handleChange(
|
|
45817
|
+
onChange: (e) => handleChange(fieldName2, e.target.value),
|
|
45793
45818
|
minLength: field.min,
|
|
45794
45819
|
maxLength: field.max
|
|
45795
45820
|
}
|
|
@@ -45801,7 +45826,7 @@ var init_Form = __esm({
|
|
|
45801
45826
|
...commonProps,
|
|
45802
45827
|
type: "password",
|
|
45803
45828
|
value: String(currentValue),
|
|
45804
|
-
onChange: (e) => handleChange(
|
|
45829
|
+
onChange: (e) => handleChange(fieldName2, e.target.value),
|
|
45805
45830
|
minLength: field.min,
|
|
45806
45831
|
maxLength: field.max
|
|
45807
45832
|
}
|
|
@@ -45814,7 +45839,7 @@ var init_Form = __esm({
|
|
|
45814
45839
|
...commonProps,
|
|
45815
45840
|
type: "text",
|
|
45816
45841
|
value: String(currentValue),
|
|
45817
|
-
onChange: (e) => handleChange(
|
|
45842
|
+
onChange: (e) => handleChange(fieldName2, e.target.value),
|
|
45818
45843
|
minLength: field.min,
|
|
45819
45844
|
maxLength: field.max
|
|
45820
45845
|
}
|
|
@@ -46171,7 +46196,7 @@ function entityFieldsFromListItem(item) {
|
|
|
46171
46196
|
}
|
|
46172
46197
|
return result;
|
|
46173
46198
|
}
|
|
46174
|
-
function getStatusStyle(
|
|
46199
|
+
function getStatusStyle(fieldName2, value) {
|
|
46175
46200
|
const val = String(value).toLowerCase();
|
|
46176
46201
|
if (val.includes("complete") || val.includes("done"))
|
|
46177
46202
|
return STATUS_STYLES.complete;
|
|
@@ -46187,12 +46212,12 @@ function getStatusStyle(fieldName, value) {
|
|
|
46187
46212
|
if (val.includes("low")) return STATUS_STYLES.low;
|
|
46188
46213
|
return STATUS_STYLES.default;
|
|
46189
46214
|
}
|
|
46190
|
-
function formatValue3(value,
|
|
46215
|
+
function formatValue3(value, fieldName2) {
|
|
46191
46216
|
if (typeof value === "number") {
|
|
46192
|
-
if (
|
|
46217
|
+
if (fieldName2.toLowerCase().includes("progress") || fieldName2.toLowerCase().includes("percent")) {
|
|
46193
46218
|
return `${value}%`;
|
|
46194
46219
|
}
|
|
46195
|
-
if (
|
|
46220
|
+
if (fieldName2.toLowerCase().includes("budget") || fieldName2.toLowerCase().includes("cost")) {
|
|
46196
46221
|
return new Intl.NumberFormat("en-US", {
|
|
46197
46222
|
style: "currency",
|
|
46198
46223
|
currency: "USD",
|
|
@@ -46215,8 +46240,8 @@ function formatValue3(value, fieldName) {
|
|
|
46215
46240
|
}
|
|
46216
46241
|
return String(value);
|
|
46217
46242
|
}
|
|
46218
|
-
function formatFieldLabel2(
|
|
46219
|
-
return humanizeFieldName(
|
|
46243
|
+
function formatFieldLabel2(fieldName2) {
|
|
46244
|
+
return humanizeFieldName(fieldName2).replace(/\sId$/, "").trim();
|
|
46220
46245
|
}
|
|
46221
46246
|
var STATUS_STYLES, StatusBadge, ProgressIndicator, List3;
|
|
46222
46247
|
var init_List = __esm({
|
|
@@ -46284,9 +46309,9 @@ var init_List = __esm({
|
|
|
46284
46309
|
};
|
|
46285
46310
|
StatusBadge = ({
|
|
46286
46311
|
value,
|
|
46287
|
-
fieldName
|
|
46312
|
+
fieldName: fieldName2
|
|
46288
46313
|
}) => {
|
|
46289
|
-
const style = getStatusStyle(
|
|
46314
|
+
const style = getStatusStyle(fieldName2, value);
|
|
46290
46315
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
46291
46316
|
Typography,
|
|
46292
46317
|
{
|
|
@@ -49218,8 +49243,8 @@ var init_StatCard = __esm({
|
|
|
49218
49243
|
return items.length;
|
|
49219
49244
|
}
|
|
49220
49245
|
if (field.includes(":")) {
|
|
49221
|
-
const [
|
|
49222
|
-
return items.filter((item) => item[
|
|
49246
|
+
const [fieldName2, fieldValue] = field.split(":");
|
|
49247
|
+
return items.filter((item) => item[fieldName2] === fieldValue).length;
|
|
49223
49248
|
}
|
|
49224
49249
|
const fieldExistsOnItems = items.some((item) => field in item);
|
|
49225
49250
|
if (fieldExistsOnItems) {
|
|
@@ -50739,6 +50764,8 @@ var init_component_registry_generated = __esm({
|
|
|
50739
50764
|
init_DocSearch();
|
|
50740
50765
|
init_DocSidebar();
|
|
50741
50766
|
init_DocTOC();
|
|
50767
|
+
init_DocumentDetails();
|
|
50768
|
+
init_DocumentPanel();
|
|
50742
50769
|
init_DocumentViewer();
|
|
50743
50770
|
init_DrawFxLayer();
|
|
50744
50771
|
init_DrawGroup();
|
|
@@ -50848,7 +50875,7 @@ var init_component_registry_generated = __esm({
|
|
|
50848
50875
|
init_RelationSelect();
|
|
50849
50876
|
init_RepeatableFormSection();
|
|
50850
50877
|
init_ReplyTree();
|
|
50851
|
-
|
|
50878
|
+
init_RichTextEditor();
|
|
50852
50879
|
init_RuntimeDebugger2();
|
|
50853
50880
|
init_ScaledDiagram();
|
|
50854
50881
|
init_ScoreDisplay();
|
|
@@ -51011,6 +51038,8 @@ var init_component_registry_generated = __esm({
|
|
|
51011
51038
|
"DocSearch": DocSearch,
|
|
51012
51039
|
"DocSidebar": DocSidebar,
|
|
51013
51040
|
"DocTOC": DocTOC,
|
|
51041
|
+
"DocumentDetails": DocumentDetails,
|
|
51042
|
+
"DocumentPanel": DocumentPanel,
|
|
51014
51043
|
"DocumentViewer": DocumentViewer,
|
|
51015
51044
|
"DrawFxLayer": DrawFxLayer,
|
|
51016
51045
|
"DrawGroup": DrawGroup,
|
|
@@ -51122,7 +51151,7 @@ var init_component_registry_generated = __esm({
|
|
|
51122
51151
|
"RelationSelect": RelationSelect,
|
|
51123
51152
|
"RepeatableFormSection": RepeatableFormSection,
|
|
51124
51153
|
"ReplyTree": ReplyTree,
|
|
51125
|
-
"
|
|
51154
|
+
"RichTextEditor": RichTextEditor,
|
|
51126
51155
|
"RuntimeDebugger": RuntimeDebugger,
|
|
51127
51156
|
"ScaledDiagram": ScaledDiagram,
|
|
51128
51157
|
"ScoreDisplay": ScoreDisplay,
|
|
@@ -51276,10 +51305,10 @@ function enrichFormFields(fields, entityDef) {
|
|
|
51276
51305
|
}
|
|
51277
51306
|
if (field && typeof field === "object" && !Array.isArray(field) && !React94__namespace.default.isValidElement(field) && !(field instanceof Date)) {
|
|
51278
51307
|
const obj = field;
|
|
51279
|
-
const
|
|
51280
|
-
if (!
|
|
51308
|
+
const fieldName2 = typeof obj.name === "string" ? obj.name : typeof obj.field === "string" ? obj.field : void 0;
|
|
51309
|
+
if (!fieldName2) return field;
|
|
51281
51310
|
if (obj.type || obj.inputType) return field;
|
|
51282
|
-
const entityField = fieldMap.get(
|
|
51311
|
+
const entityField = fieldMap.get(fieldName2);
|
|
51283
51312
|
if (!entityField) return field;
|
|
51284
51313
|
const enriched = { ...obj, type: entityField.type };
|
|
51285
51314
|
if (entityField.required && !("required" in obj)) {
|
|
@@ -51329,9 +51358,9 @@ function enrichDetailFields(fields, entityDef) {
|
|
|
51329
51358
|
}
|
|
51330
51359
|
if (field && typeof field === "object" && !Array.isArray(field) && !React94__namespace.default.isValidElement(field) && !(field instanceof Date)) {
|
|
51331
51360
|
const obj = field;
|
|
51332
|
-
const
|
|
51333
|
-
if (!
|
|
51334
|
-
const meta = metaFor(
|
|
51361
|
+
const fieldName2 = typeof obj.key === "string" ? obj.key : typeof obj.name === "string" ? obj.name : void 0;
|
|
51362
|
+
if (!fieldName2 || obj.type) return field;
|
|
51363
|
+
const meta = metaFor(fieldName2);
|
|
51335
51364
|
return meta ? { ...obj, ...meta } : field;
|
|
51336
51365
|
}
|
|
51337
51366
|
return field;
|
|
@@ -52170,9 +52199,9 @@ function UISlotRenderer({
|
|
|
52170
52199
|
"ui-slot-renderer relative min-h-full",
|
|
52171
52200
|
className
|
|
52172
52201
|
), children: [
|
|
52173
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex min-h-full", children: [
|
|
52174
|
-
/* @__PURE__ */ jsxRuntime.jsx(UISlotComponent, { slot: "sidebar", className: "ui-slot-sidebar min-w-0 shrink-0" }),
|
|
52175
|
-
/* @__PURE__ */ jsxRuntime.jsx(UISlotComponent, { slot: "main", className: "ui-slot-main flex-1 min-h-[200px]" })
|
|
52202
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex min-h-full flex-col lg:flex-row", children: [
|
|
52203
|
+
/* @__PURE__ */ jsxRuntime.jsx(UISlotComponent, { slot: "sidebar", className: "ui-slot-sidebar min-w-0 lg:shrink-0" }),
|
|
52204
|
+
/* @__PURE__ */ jsxRuntime.jsx(UISlotComponent, { slot: "main", className: "ui-slot-main flex-1 min-w-0 min-h-[200px]" })
|
|
52176
52205
|
] }),
|
|
52177
52206
|
/* @__PURE__ */ jsxRuntime.jsx(UISlotComponent, { slot: "modal", portal: true }),
|
|
52178
52207
|
/* @__PURE__ */ jsxRuntime.jsx(UISlotComponent, { slot: "drawer", portal: true }),
|
|
@@ -56069,7 +56098,7 @@ function buildTraitRenderConfig(binding, traitConfigsByName, payload) {
|
|
|
56069
56098
|
}
|
|
56070
56099
|
return void 0;
|
|
56071
56100
|
}
|
|
56072
|
-
function createSharedEntityWriter(binding, tick, traitStatesRef, emit) {
|
|
56101
|
+
function createSharedEntityWriter(binding, tick, traitStatesRef, emit, traitConfigsByName) {
|
|
56073
56102
|
return (scratch) => {
|
|
56074
56103
|
const traitName = binding.trait.name;
|
|
56075
56104
|
const currentState = traitStatesRef.current.get(traitName)?.currentState ?? "";
|
|
@@ -56078,9 +56107,13 @@ function createSharedEntityWriter(binding, tick, traitStatesRef, emit) {
|
|
|
56078
56107
|
const writes = [];
|
|
56079
56108
|
const ctx = evaluator.createMinimalContext(scratchEntity, {}, currentState);
|
|
56080
56109
|
const declaredDefaults = runtime.collectDeclaredConfigDefaults(binding.trait);
|
|
56081
|
-
const
|
|
56082
|
-
|
|
56083
|
-
|
|
56110
|
+
const resolvedByName = traitConfigsByName?.[traitName];
|
|
56111
|
+
const callSiteRaw = getBindingConfig(binding);
|
|
56112
|
+
const callSiteConfig = callSiteRaw ? Object.fromEntries(
|
|
56113
|
+
Object.entries(callSiteRaw).filter(([, v]) => !containsConfigForward(v))
|
|
56114
|
+
) : void 0;
|
|
56115
|
+
if (declaredDefaults || resolvedByName || callSiteConfig) {
|
|
56116
|
+
ctx.config = { ...declaredDefaults ?? {}, ...resolvedByName ?? {}, ...callSiteConfig ?? {} };
|
|
56084
56117
|
}
|
|
56085
56118
|
ctx.mutateEntity = (changes) => {
|
|
56086
56119
|
for (const [field, value] of Object.entries(changes)) {
|
|
@@ -56683,7 +56716,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
56683
56716
|
const interval = entries[0].tick.interval;
|
|
56684
56717
|
const onDue = timedTick(`tick:shared:${group.storeKey}@${String(interval)}`, () => {
|
|
56685
56718
|
const writers = entries.map(
|
|
56686
|
-
({ binding, tick }) => createSharedEntityWriter(binding, tick, traitStatesRef, emitFromSharedWriter)
|
|
56719
|
+
({ binding, tick }) => createSharedEntityWriter(binding, tick, traitStatesRef, emitFromSharedWriter, traitConfigsByName)
|
|
56687
56720
|
);
|
|
56688
56721
|
runTickFrame(group.storeKey, writers, sharedEntityStore);
|
|
56689
56722
|
});
|
|
@@ -59196,11 +59229,11 @@ function OrbInspector({ node, schema, editable = false, userType = "builder", th
|
|
|
59196
59229
|
const handleAddField = React94.useCallback(() => {
|
|
59197
59230
|
eventBus.emit("UI:ADD_FIELD", {});
|
|
59198
59231
|
}, [eventBus]);
|
|
59199
|
-
const handleUpdateField = React94.useCallback((
|
|
59200
|
-
eventBus.emit("UI:UPDATE_FIELD", { fieldName, updates });
|
|
59232
|
+
const handleUpdateField = React94.useCallback((fieldName2, updates) => {
|
|
59233
|
+
eventBus.emit("UI:UPDATE_FIELD", { fieldName: fieldName2, updates });
|
|
59201
59234
|
}, [eventBus]);
|
|
59202
|
-
const handleRemoveField = React94.useCallback((
|
|
59203
|
-
eventBus.emit("UI:REMOVE_FIELD", { fieldName });
|
|
59235
|
+
const handleRemoveField = React94.useCallback((fieldName2) => {
|
|
59236
|
+
eventBus.emit("UI:REMOVE_FIELD", { fieldName: fieldName2 });
|
|
59204
59237
|
}, [eventBus]);
|
|
59205
59238
|
const handleGuardChange = React94.useCallback((guardExpr) => {
|
|
59206
59239
|
if (!editable) return;
|