@almadar/ui 5.160.0 → 5.161.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/runtime/index.cjs
CHANGED
|
@@ -25903,8 +25903,9 @@ function DataGrid({
|
|
|
25903
25903
|
const titleField = fieldDefs.find((f3) => f3.variant === "h3" || f3.variant === "h4") ?? fieldDefs[0];
|
|
25904
25904
|
const badgeFields = fieldDefs.filter((f3) => f3.variant === "badge" && f3 !== titleField);
|
|
25905
25905
|
const bodyFields = fieldDefs.filter((f3) => f3 !== titleField && !badgeFields.includes(f3));
|
|
25906
|
-
const
|
|
25907
|
-
const
|
|
25906
|
+
const inlineCap = Math.min(maxInlineActions ?? 1, 1);
|
|
25907
|
+
const inlineCardActions = actionDefs.filter((a) => a.variant === "primary").slice(0, inlineCap);
|
|
25908
|
+
const menuCardActions = actionDefs.filter((a) => !inlineCardActions.includes(a));
|
|
25908
25909
|
const fireAction = (action, itemData) => {
|
|
25909
25910
|
if (action.navigatesTo) {
|
|
25910
25911
|
const url = action.navigatesTo.replace(
|
|
@@ -25924,6 +25925,9 @@ function DataGrid({
|
|
|
25924
25925
|
e.stopPropagation();
|
|
25925
25926
|
fireAction(action, itemData);
|
|
25926
25927
|
};
|
|
25928
|
+
const cardClickAction = actionDefs.find((a) => a.variant !== "danger");
|
|
25929
|
+
const handleCardClick = cardClickAction ? (itemData) => () => fireAction(cardClickAction, itemData) : void 0;
|
|
25930
|
+
const stopCardClick = handleCardClick ? (e) => e.stopPropagation() : void 0;
|
|
25927
25931
|
const hasRenderProp = typeof children === "function";
|
|
25928
25932
|
React85.useEffect(() => {
|
|
25929
25933
|
if (data.length > 0 && !hasRenderProp && fieldDefs.length === 0) {
|
|
@@ -25995,55 +25999,40 @@ function DataGrid({
|
|
|
25995
25999
|
{
|
|
25996
26000
|
"data-entity-row": true,
|
|
25997
26001
|
"data-entity-id": id,
|
|
25998
|
-
|
|
26002
|
+
onClick: handleCardClick?.(itemData),
|
|
26003
|
+
className: cn("relative group/rowactions", handleCardClick && "cursor-pointer", isSelected && "ring-2 ring-primary rounded-lg"),
|
|
25999
26004
|
children: [
|
|
26000
26005
|
children(itemData, index),
|
|
26001
|
-
actionDefs.length > 0 && /* @__PURE__ */ jsxRuntime.
|
|
26002
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
26003
|
-
|
|
26004
|
-
|
|
26005
|
-
|
|
26006
|
-
|
|
26007
|
-
|
|
26008
|
-
|
|
26009
|
-
|
|
26010
|
-
|
|
26011
|
-
|
|
26012
|
-
|
|
26013
|
-
|
|
26014
|
-
|
|
26015
|
-
|
|
26016
|
-
|
|
26017
|
-
|
|
26018
|
-
)),
|
|
26019
|
-
maxInlineActions != null && actionDefs.length > maxInlineActions && /* @__PURE__ */ jsxRuntime.jsx(
|
|
26020
|
-
Menu,
|
|
26021
|
-
{
|
|
26022
|
-
position: "bottom-end",
|
|
26023
|
-
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" }) }),
|
|
26024
|
-
items: actionDefs.slice(maxInlineActions).map((action) => ({
|
|
26025
|
-
label: action.label,
|
|
26026
|
-
icon: action.icon,
|
|
26027
|
-
event: action.event,
|
|
26028
|
-
onClick: () => fireAction(action, itemData)
|
|
26029
|
-
}))
|
|
26030
|
-
}
|
|
26031
|
-
)
|
|
26032
|
-
] }),
|
|
26033
|
-
/* @__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(
|
|
26006
|
+
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: [
|
|
26007
|
+
inlineCardActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
26008
|
+
Button,
|
|
26009
|
+
{
|
|
26010
|
+
variant: "primary",
|
|
26011
|
+
size: "sm",
|
|
26012
|
+
onClick: handleActionClick(action, itemData),
|
|
26013
|
+
"data-testid": `action-${action.event}`,
|
|
26014
|
+
"data-row-id": String(itemData.id),
|
|
26015
|
+
children: [
|
|
26016
|
+
action.icon && renderIconInput(action.icon, { size: "xs", className: "mr-1" }),
|
|
26017
|
+
action.label
|
|
26018
|
+
]
|
|
26019
|
+
},
|
|
26020
|
+
idx
|
|
26021
|
+
)),
|
|
26022
|
+
menuCardActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
26034
26023
|
Menu,
|
|
26035
26024
|
{
|
|
26036
26025
|
position: "bottom-end",
|
|
26037
26026
|
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" }) }),
|
|
26038
|
-
items:
|
|
26027
|
+
items: menuCardActions.map((action) => ({
|
|
26039
26028
|
label: action.label,
|
|
26040
26029
|
icon: action.icon,
|
|
26041
|
-
|
|
26030
|
+
variant: action.variant === "danger" ? "danger" : "default",
|
|
26042
26031
|
onClick: () => fireAction(action, itemData)
|
|
26043
26032
|
}))
|
|
26044
26033
|
}
|
|
26045
|
-
)
|
|
26046
|
-
] })
|
|
26034
|
+
)
|
|
26035
|
+
] }) })
|
|
26047
26036
|
]
|
|
26048
26037
|
},
|
|
26049
26038
|
id
|
|
@@ -26057,12 +26046,14 @@ function DataGrid({
|
|
|
26057
26046
|
{
|
|
26058
26047
|
"data-entity-row": true,
|
|
26059
26048
|
"data-entity-id": id,
|
|
26049
|
+
onClick: handleCardClick?.(itemData),
|
|
26060
26050
|
className: cn(
|
|
26061
26051
|
"bg-card rounded-lg",
|
|
26062
26052
|
"border border-border",
|
|
26063
26053
|
"shadow-elevation-card hover:shadow-elevation-dialog",
|
|
26064
26054
|
"hover:border-primary transition-all",
|
|
26065
26055
|
"flex flex-col",
|
|
26056
|
+
handleCardClick && "cursor-pointer",
|
|
26066
26057
|
isSelected && "ring-2 ring-primary border-primary"
|
|
26067
26058
|
),
|
|
26068
26059
|
children: [
|
|
@@ -26112,53 +26103,35 @@ function DataGrid({
|
|
|
26112
26103
|
] }, field.name);
|
|
26113
26104
|
}) })
|
|
26114
26105
|
] }),
|
|
26115
|
-
|
|
26116
|
-
|
|
26106
|
+
actionDefs.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", onClick: stopCardClick, className: "flex-shrink-0", children: [
|
|
26107
|
+
inlineCardActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
26117
26108
|
Button,
|
|
26118
26109
|
{
|
|
26119
|
-
variant:
|
|
26110
|
+
variant: "primary",
|
|
26120
26111
|
size: "sm",
|
|
26121
26112
|
onClick: handleActionClick(action, itemData),
|
|
26122
26113
|
"data-testid": `action-${action.event}`,
|
|
26123
26114
|
"data-row-id": String(itemData.id),
|
|
26124
26115
|
"aria-label": action.label,
|
|
26125
26116
|
title: action.label,
|
|
26126
|
-
className: cn(
|
|
26127
|
-
action.variant === "primary" ? void 0 : "text-muted-foreground hover:text-foreground",
|
|
26128
|
-
action.icon && "px-2"
|
|
26129
|
-
),
|
|
26117
|
+
className: cn(action.icon && "px-2"),
|
|
26130
26118
|
children: action.icon ? renderIconInput(action.icon, { size: "xs" }) : action.label
|
|
26131
26119
|
},
|
|
26132
26120
|
idx
|
|
26133
26121
|
)),
|
|
26134
|
-
|
|
26122
|
+
menuCardActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
26135
26123
|
Menu,
|
|
26136
26124
|
{
|
|
26137
26125
|
position: "bottom-end",
|
|
26138
26126
|
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" }) }),
|
|
26139
|
-
items:
|
|
26127
|
+
items: menuCardActions.map((action) => ({
|
|
26140
26128
|
label: action.label,
|
|
26141
26129
|
icon: action.icon,
|
|
26142
|
-
|
|
26130
|
+
variant: action.variant === "danger" ? "danger" : "default",
|
|
26143
26131
|
onClick: () => fireAction(action, itemData)
|
|
26144
26132
|
}))
|
|
26145
26133
|
}
|
|
26146
|
-
)
|
|
26147
|
-
dangerActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
26148
|
-
Button,
|
|
26149
|
-
{
|
|
26150
|
-
variant: "ghost",
|
|
26151
|
-
size: "sm",
|
|
26152
|
-
onClick: handleActionClick(action, itemData),
|
|
26153
|
-
"data-testid": `action-${action.event}`,
|
|
26154
|
-
"data-row-id": String(itemData.id),
|
|
26155
|
-
"aria-label": action.label,
|
|
26156
|
-
title: action.label,
|
|
26157
|
-
className: "text-error hover:text-error hover:bg-error/10 px-2",
|
|
26158
|
-
children: action.icon ? renderIconInput(action.icon, { size: "xs" }) : action.label
|
|
26159
|
-
},
|
|
26160
|
-
`danger-${idx}`
|
|
26161
|
-
))
|
|
26134
|
+
)
|
|
26162
26135
|
] })
|
|
26163
26136
|
] }) }),
|
|
26164
26137
|
bodyFields.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "px-4 pt-2 pb-4 flex-1", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
|
|
@@ -26400,54 +26373,59 @@ function DataList({
|
|
|
26400
26373
|
if (!itemActions || itemActions.length === 0) return null;
|
|
26401
26374
|
const inline = maxInlineActions != null ? itemActions.slice(0, maxInlineActions) : itemActions;
|
|
26402
26375
|
const overflow = maxInlineActions != null ? itemActions.slice(maxInlineActions) : [];
|
|
26403
|
-
return
|
|
26404
|
-
|
|
26405
|
-
|
|
26406
|
-
|
|
26407
|
-
|
|
26408
|
-
|
|
26409
|
-
|
|
26410
|
-
|
|
26411
|
-
|
|
26412
|
-
|
|
26413
|
-
action
|
|
26414
|
-
|
|
26415
|
-
|
|
26416
|
-
|
|
26417
|
-
|
|
26418
|
-
|
|
26419
|
-
|
|
26420
|
-
|
|
26421
|
-
|
|
26422
|
-
|
|
26423
|
-
|
|
26424
|
-
|
|
26425
|
-
|
|
26426
|
-
|
|
26427
|
-
|
|
26428
|
-
|
|
26429
|
-
|
|
26430
|
-
|
|
26431
|
-
|
|
26432
|
-
|
|
26433
|
-
|
|
26434
|
-
|
|
26435
|
-
|
|
26436
|
-
|
|
26437
|
-
|
|
26438
|
-
|
|
26439
|
-
|
|
26440
|
-
|
|
26441
|
-
|
|
26442
|
-
|
|
26376
|
+
return (
|
|
26377
|
+
// stopPropagation at the cluster: the "⋯" trigger opens the overflow
|
|
26378
|
+
// menu without also firing the row's default click (inline buttons
|
|
26379
|
+
// already stop it in handleActionClick; the Menu panel is portaled).
|
|
26380
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", onClick: rowClickEvent ? (e) => e.stopPropagation() : void 0, className: "flex-shrink-0", children: [
|
|
26381
|
+
inline.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
26382
|
+
Button,
|
|
26383
|
+
{
|
|
26384
|
+
variant: action.variant ?? "ghost",
|
|
26385
|
+
size: "sm",
|
|
26386
|
+
onClick: handleActionClick(action, itemData),
|
|
26387
|
+
"data-testid": `action-${action.event}`,
|
|
26388
|
+
"data-row-id": String(itemData.id),
|
|
26389
|
+
className: cn(
|
|
26390
|
+
action.variant === "danger" && "text-error hover:bg-error/10",
|
|
26391
|
+
// Must sit on the Button itself: the variant's own text colour
|
|
26392
|
+
// beats an inherited one from the row wrapper.
|
|
26393
|
+
onPrimary && "!text-primary-foreground hover:bg-primary-foreground/15"
|
|
26394
|
+
),
|
|
26395
|
+
children: [
|
|
26396
|
+
action.icon && renderIconInput2(action.icon, { size: "xs", className: "mr-1" }),
|
|
26397
|
+
action.label
|
|
26398
|
+
]
|
|
26399
|
+
},
|
|
26400
|
+
idx
|
|
26401
|
+
)),
|
|
26402
|
+
overflow.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
26403
|
+
Menu,
|
|
26404
|
+
{
|
|
26405
|
+
position: "bottom-end",
|
|
26406
|
+
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" }) }),
|
|
26407
|
+
items: overflow.map((action) => ({
|
|
26408
|
+
label: action.label,
|
|
26409
|
+
icon: action.icon,
|
|
26410
|
+
variant: action.variant === "danger" ? "danger" : "default",
|
|
26411
|
+
onClick: () => eventBus.emit(`UI:${action.event}`, {
|
|
26412
|
+
id: itemData.id,
|
|
26413
|
+
row: itemData
|
|
26414
|
+
})
|
|
26415
|
+
}))
|
|
26416
|
+
}
|
|
26417
|
+
)
|
|
26418
|
+
] })
|
|
26419
|
+
);
|
|
26443
26420
|
};
|
|
26421
|
+
const rowClickEvent = itemClickEvent || itemActions?.find((a) => a.variant !== "danger")?.event;
|
|
26444
26422
|
const handleRowClick = (itemData) => () => {
|
|
26445
|
-
if (!
|
|
26423
|
+
if (!rowClickEvent) return;
|
|
26446
26424
|
const payload = {
|
|
26447
26425
|
id: itemData.id,
|
|
26448
26426
|
row: itemData
|
|
26449
26427
|
};
|
|
26450
|
-
eventBus.emit(`UI:${
|
|
26428
|
+
eventBus.emit(`UI:${rowClickEvent}`, payload);
|
|
26451
26429
|
};
|
|
26452
26430
|
if (isLoading) {
|
|
26453
26431
|
return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "secondary", children: t("loading.items") }) });
|
|
@@ -26495,10 +26473,10 @@ function DataList({
|
|
|
26495
26473
|
{
|
|
26496
26474
|
"data-entity-row": true,
|
|
26497
26475
|
"data-entity-id": id,
|
|
26498
|
-
onClick:
|
|
26476
|
+
onClick: rowClickEvent ? handleRowClick(itemData) : void 0,
|
|
26499
26477
|
className: cn(
|
|
26500
26478
|
"flex px-4 group/rowactions",
|
|
26501
|
-
|
|
26479
|
+
rowClickEvent && "cursor-pointer",
|
|
26502
26480
|
isSent ? "justify-end" : "justify-start"
|
|
26503
26481
|
),
|
|
26504
26482
|
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -26574,7 +26552,7 @@ function DataList({
|
|
|
26574
26552
|
const id2 = itemData.id || String(index);
|
|
26575
26553
|
const actions = renderItemActions(itemData);
|
|
26576
26554
|
return wrapDnd(
|
|
26577
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Box, { "data-entity-row": true, "data-entity-id": id2, onClick:
|
|
26555
|
+
/* @__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: [
|
|
26578
26556
|
children(itemData, index),
|
|
26579
26557
|
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: [
|
|
26580
26558
|
/* @__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 }),
|
|
@@ -26586,7 +26564,6 @@ function DataList({
|
|
|
26586
26564
|
items: (itemActions ?? []).map((action) => ({
|
|
26587
26565
|
label: action.label,
|
|
26588
26566
|
icon: action.icon,
|
|
26589
|
-
event: action.event,
|
|
26590
26567
|
variant: action.variant === "danger" ? "danger" : "default",
|
|
26591
26568
|
onClick: () => eventBus.emit(`UI:${action.event}`, {
|
|
26592
26569
|
id: itemData.id,
|
|
@@ -26603,7 +26580,7 @@ function DataList({
|
|
|
26603
26580
|
const id = itemData.id || String(index);
|
|
26604
26581
|
const titleValue = getNestedValue(itemData, titleField?.name ?? "");
|
|
26605
26582
|
return wrapDnd(
|
|
26606
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Box, { "data-entity-row": true, "data-entity-id": id, onClick:
|
|
26583
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Box, { "data-entity-row": true, "data-entity-id": id, onClick: rowClickEvent ? handleRowClick(itemData) : void 0, className: cn(rowClickEvent && "cursor-pointer"), children: [
|
|
26607
26584
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
26608
26585
|
Box,
|
|
26609
26586
|
{
|
|
@@ -31815,7 +31792,7 @@ var init_MapView = __esm({
|
|
|
31815
31792
|
shadowSize: [41, 41]
|
|
31816
31793
|
});
|
|
31817
31794
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
31818
|
-
const { useEffect: useEffect63, useRef: useRef63, useCallback: useCallback90, useState:
|
|
31795
|
+
const { useEffect: useEffect63, useRef: useRef63, useCallback: useCallback90, useState: useState94 } = React85__namespace.default;
|
|
31819
31796
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
31820
31797
|
const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
31821
31798
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
@@ -31860,7 +31837,7 @@ var init_MapView = __esm({
|
|
|
31860
31837
|
showAttribution = true
|
|
31861
31838
|
}) {
|
|
31862
31839
|
const eventBus = useEventBus2();
|
|
31863
|
-
const [clickedPosition, setClickedPosition] =
|
|
31840
|
+
const [clickedPosition, setClickedPosition] = useState94(null);
|
|
31864
31841
|
const handleMapClick = useCallback90((lat, lng) => {
|
|
31865
31842
|
if (showClickedPin) {
|
|
31866
31843
|
setClickedPosition({ lat, lng });
|
|
@@ -36084,741 +36061,775 @@ var init_PositionedCanvas = __esm({
|
|
|
36084
36061
|
PositionedCanvas.displayName = "PositionedCanvas";
|
|
36085
36062
|
}
|
|
36086
36063
|
});
|
|
36087
|
-
function
|
|
36088
|
-
|
|
36089
|
-
|
|
36090
|
-
|
|
36064
|
+
function safeUrl(raw, kinds) {
|
|
36065
|
+
const url = raw.trim();
|
|
36066
|
+
if (url.startsWith("#") || url.startsWith("/")) return url;
|
|
36067
|
+
const lower = url.toLowerCase();
|
|
36068
|
+
for (const kind of kinds) {
|
|
36069
|
+
if (lower.startsWith(kind)) return url;
|
|
36070
|
+
}
|
|
36071
|
+
return null;
|
|
36091
36072
|
}
|
|
36092
|
-
function
|
|
36093
|
-
|
|
36094
|
-
|
|
36095
|
-
const
|
|
36096
|
-
|
|
36097
|
-
|
|
36098
|
-
|
|
36099
|
-
|
|
36100
|
-
|
|
36073
|
+
function sanitizeNode(el) {
|
|
36074
|
+
const children = Array.from(el.children);
|
|
36075
|
+
for (const child of children) {
|
|
36076
|
+
const tag = child.tagName.toLowerCase();
|
|
36077
|
+
if (DROP_TAGS.has(tag)) {
|
|
36078
|
+
child.remove();
|
|
36079
|
+
continue;
|
|
36080
|
+
}
|
|
36081
|
+
const allowed = ALLOWED_ATTRS[tag];
|
|
36082
|
+
if (!allowed) {
|
|
36083
|
+
sanitizeNode(child);
|
|
36084
|
+
child.replaceWith(...Array.from(child.childNodes));
|
|
36085
|
+
continue;
|
|
36086
|
+
}
|
|
36087
|
+
for (const attr of Array.from(child.attributes)) {
|
|
36088
|
+
const name = attr.name.toLowerCase();
|
|
36089
|
+
if (!allowed.includes(name)) {
|
|
36090
|
+
child.removeAttribute(attr.name);
|
|
36091
|
+
continue;
|
|
36092
|
+
}
|
|
36093
|
+
if (name === "href") {
|
|
36094
|
+
const url = safeUrl(attr.value, ["http://", "https://", "mailto:"]);
|
|
36095
|
+
if (url === null) child.removeAttribute(attr.name);
|
|
36096
|
+
else child.setAttribute("href", url);
|
|
36097
|
+
}
|
|
36098
|
+
if (name === "src") {
|
|
36099
|
+
const url = safeUrl(attr.value, ["http://", "https://", "data:image/"]);
|
|
36100
|
+
if (url === null) child.remove();
|
|
36101
|
+
else child.setAttribute("src", url);
|
|
36102
|
+
}
|
|
36103
|
+
}
|
|
36104
|
+
sanitizeNode(child);
|
|
36105
|
+
}
|
|
36101
36106
|
}
|
|
36102
|
-
function
|
|
36103
|
-
|
|
36104
|
-
|
|
36105
|
-
|
|
36106
|
-
|
|
36107
|
-
|
|
36108
|
-
|
|
36109
|
-
children: [
|
|
36110
|
-
{ id: nextBlockId("li"), type: "paragraph", content: "" }
|
|
36111
|
-
]
|
|
36112
|
-
};
|
|
36113
|
-
case "image":
|
|
36114
|
-
return {
|
|
36115
|
-
id: nextBlockId(type),
|
|
36116
|
-
type,
|
|
36117
|
-
content: "",
|
|
36118
|
-
metadata: { url: "", caption: "" }
|
|
36119
|
-
};
|
|
36120
|
-
case "code":
|
|
36121
|
-
return {
|
|
36122
|
-
id: nextBlockId(type),
|
|
36123
|
-
type,
|
|
36124
|
-
content: "",
|
|
36125
|
-
metadata: { language: "plaintext" }
|
|
36126
|
-
};
|
|
36127
|
-
case "divider":
|
|
36128
|
-
return { id: nextBlockId(type), type };
|
|
36129
|
-
default:
|
|
36130
|
-
return { id: nextBlockId(type), type, content: "" };
|
|
36107
|
+
function sanitizeRichHtml(html) {
|
|
36108
|
+
if (!html || typeof html !== "string") return "";
|
|
36109
|
+
if (typeof window === "undefined" || typeof DOMParser === "undefined") return "";
|
|
36110
|
+
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
36111
|
+
sanitizeNode(doc.body);
|
|
36112
|
+
if (!doc.body.querySelector("img, hr") && (doc.body.textContent ?? "").trim().length === 0) {
|
|
36113
|
+
return "";
|
|
36131
36114
|
}
|
|
36115
|
+
return doc.body.innerHTML.replace(//g, "");
|
|
36132
36116
|
}
|
|
36133
|
-
function
|
|
36134
|
-
|
|
36117
|
+
function htmlIsEmpty(el) {
|
|
36118
|
+
if (el.querySelector("img, hr")) return false;
|
|
36119
|
+
return (el.textContent ?? "").trim().length === 0;
|
|
36120
|
+
}
|
|
36121
|
+
function closestBlock(node, root) {
|
|
36122
|
+
let cur = node;
|
|
36123
|
+
while (cur && cur !== root) {
|
|
36124
|
+
if (cur instanceof Element && BLOCK_TAGS.has(cur.tagName)) return cur;
|
|
36125
|
+
cur = cur.parentNode;
|
|
36126
|
+
}
|
|
36127
|
+
return null;
|
|
36135
36128
|
}
|
|
36136
|
-
function
|
|
36137
|
-
return
|
|
36129
|
+
function RichTextStyles() {
|
|
36130
|
+
return /* @__PURE__ */ jsxRuntime.jsx("style", { children: RICH_TEXT_CSS });
|
|
36138
36131
|
}
|
|
36139
|
-
function
|
|
36132
|
+
function readToolbarState(root) {
|
|
36133
|
+
const sel = window.getSelection();
|
|
36134
|
+
if (!sel || sel.rangeCount === 0) return IDLE_TOOLBAR;
|
|
36135
|
+
const anchor = sel.anchorNode;
|
|
36136
|
+
if (!anchor || !root.contains(anchor)) return IDLE_TOOLBAR;
|
|
36137
|
+
let block = null;
|
|
36138
|
+
let link = false;
|
|
36139
|
+
let node = anchor;
|
|
36140
|
+
while (node && node !== root) {
|
|
36141
|
+
if (node instanceof Element) {
|
|
36142
|
+
const tag = node.tagName.toLowerCase();
|
|
36143
|
+
if (!block && (tag === "h1" || tag === "h2" || tag === "h3" || tag === "blockquote" || tag === "pre")) {
|
|
36144
|
+
block = tag;
|
|
36145
|
+
}
|
|
36146
|
+
if (tag === "a") link = true;
|
|
36147
|
+
}
|
|
36148
|
+
node = node.parentNode;
|
|
36149
|
+
}
|
|
36140
36150
|
return {
|
|
36141
|
-
|
|
36142
|
-
|
|
36143
|
-
|
|
36144
|
-
|
|
36145
|
-
|
|
36146
|
-
|
|
36147
|
-
|
|
36151
|
+
bold: document.queryCommandState("bold"),
|
|
36152
|
+
italic: document.queryCommandState("italic"),
|
|
36153
|
+
underline: document.queryCommandState("underline"),
|
|
36154
|
+
strikeThrough: document.queryCommandState("strikeThrough"),
|
|
36155
|
+
block,
|
|
36156
|
+
bullets: document.queryCommandState("insertUnorderedList"),
|
|
36157
|
+
numbers: document.queryCommandState("insertOrderedList"),
|
|
36158
|
+
link
|
|
36148
36159
|
};
|
|
36149
36160
|
}
|
|
36150
|
-
function
|
|
36151
|
-
|
|
36152
|
-
|
|
36153
|
-
|
|
36154
|
-
|
|
36155
|
-
|
|
36156
|
-
|
|
36157
|
-
|
|
36158
|
-
|
|
36159
|
-
|
|
36160
|
-
|
|
36161
|
-
|
|
36161
|
+
function ToolbarButton({ icon: IconCmp, label, active, onExec }) {
|
|
36162
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
36163
|
+
Button,
|
|
36164
|
+
{
|
|
36165
|
+
type: "button",
|
|
36166
|
+
variant: "ghost",
|
|
36167
|
+
size: "sm",
|
|
36168
|
+
"aria-label": label,
|
|
36169
|
+
"aria-pressed": active,
|
|
36170
|
+
title: label,
|
|
36171
|
+
className: cn(
|
|
36172
|
+
"h-8 w-8 p-0 gap-0 justify-center",
|
|
36173
|
+
active && "bg-muted text-foreground"
|
|
36174
|
+
),
|
|
36175
|
+
onMouseDown: (e) => {
|
|
36176
|
+
e.preventDefault();
|
|
36177
|
+
onExec();
|
|
36178
|
+
},
|
|
36179
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(IconCmp, { size: 15 })
|
|
36162
36180
|
}
|
|
36163
|
-
|
|
36164
|
-
|
|
36165
|
-
|
|
36166
|
-
|
|
36167
|
-
|
|
36168
|
-
|
|
36169
|
-
|
|
36170
|
-
|
|
36171
|
-
|
|
36172
|
-
|
|
36173
|
-
|
|
36174
|
-
|
|
36175
|
-
|
|
36176
|
-
|
|
36177
|
-
|
|
36178
|
-
|
|
36179
|
-
|
|
36180
|
-
|
|
36181
|
-
|
|
36182
|
-
|
|
36183
|
-
|
|
36184
|
-
|
|
36185
|
-
|
|
36186
|
-
|
|
36187
|
-
|
|
36188
|
-
|
|
36181
|
+
);
|
|
36182
|
+
}
|
|
36183
|
+
var CHANGE_DEBOUNCE_MS, ALLOWED_ATTRS, DROP_TAGS, MD_BLOCK_PREFIXES, BLOCK_TAGS, MD_INLINE_RULES, RICH_TEXT_CSS, IDLE_TOOLBAR, RichTextEditor;
|
|
36184
|
+
var init_RichTextEditor = __esm({
|
|
36185
|
+
"components/core/molecules/RichTextEditor.tsx"() {
|
|
36186
|
+
"use client";
|
|
36187
|
+
init_cn();
|
|
36188
|
+
init_Box();
|
|
36189
|
+
init_Button();
|
|
36190
|
+
init_useEventBus();
|
|
36191
|
+
CHANGE_DEBOUNCE_MS = 400;
|
|
36192
|
+
ALLOWED_ATTRS = {
|
|
36193
|
+
p: [],
|
|
36194
|
+
h1: [],
|
|
36195
|
+
h2: [],
|
|
36196
|
+
h3: [],
|
|
36197
|
+
h4: [],
|
|
36198
|
+
ul: [],
|
|
36199
|
+
ol: [],
|
|
36200
|
+
li: [],
|
|
36201
|
+
blockquote: [],
|
|
36202
|
+
pre: [],
|
|
36203
|
+
code: [],
|
|
36204
|
+
b: [],
|
|
36205
|
+
strong: [],
|
|
36206
|
+
i: [],
|
|
36207
|
+
em: [],
|
|
36208
|
+
u: [],
|
|
36209
|
+
s: [],
|
|
36210
|
+
strike: [],
|
|
36211
|
+
br: [],
|
|
36212
|
+
hr: [],
|
|
36213
|
+
div: [],
|
|
36214
|
+
span: [],
|
|
36215
|
+
a: ["href"],
|
|
36216
|
+
img: ["src", "alt"]
|
|
36217
|
+
};
|
|
36218
|
+
DROP_TAGS = /* @__PURE__ */ new Set(["script", "style", "iframe", "object", "embed", "link", "meta", "form", "input", "button", "textarea", "select"]);
|
|
36219
|
+
MD_BLOCK_PREFIXES = {
|
|
36220
|
+
"#": { kind: "block", tag: "h1" },
|
|
36221
|
+
"##": { kind: "block", tag: "h2" },
|
|
36222
|
+
"###": { kind: "block", tag: "h3" },
|
|
36223
|
+
">": { kind: "block", tag: "blockquote" },
|
|
36224
|
+
"```": { kind: "block", tag: "pre" },
|
|
36225
|
+
"-": { kind: "list", command: "insertUnorderedList" },
|
|
36226
|
+
"*": { kind: "list", command: "insertUnorderedList" },
|
|
36227
|
+
"1.": { kind: "list", command: "insertOrderedList" }
|
|
36228
|
+
};
|
|
36229
|
+
BLOCK_TAGS = /* @__PURE__ */ new Set(["P", "DIV", "LI", "H1", "H2", "H3", "H4", "BLOCKQUOTE", "PRE"]);
|
|
36230
|
+
MD_INLINE_RULES = [
|
|
36231
|
+
{ pattern: /\*\*([^*\n]+)\*\*$/, wrap: "strong" },
|
|
36232
|
+
{ pattern: /(^|[^*])\*([^*\n]+)\*$/, wrap: "em" },
|
|
36233
|
+
{ pattern: /`([^`\n]+)`$/, wrap: "code" }
|
|
36234
|
+
];
|
|
36235
|
+
RICH_TEXT_CSS = `
|
|
36236
|
+
.almadar-rich-text { line-height: 1.7; color: var(--color-foreground); }
|
|
36237
|
+
.almadar-rich-text h1 { font-size: 1.875rem; font-weight: 700; line-height: 1.25; margin: 1.25em 0 0.4em; }
|
|
36238
|
+
.almadar-rich-text h2 { font-size: 1.5rem; font-weight: 650; line-height: 1.3; margin: 1.1em 0 0.4em; }
|
|
36239
|
+
.almadar-rich-text h3 { font-size: 1.25rem; font-weight: 600; line-height: 1.35; margin: 1em 0 0.35em; }
|
|
36240
|
+
.almadar-rich-text h1:first-child, .almadar-rich-text h2:first-child, .almadar-rich-text h3:first-child { margin-top: 0; }
|
|
36241
|
+
.almadar-rich-text p { margin: 0.5em 0; }
|
|
36242
|
+
.almadar-rich-text ul { list-style: disc; padding-inline-start: 1.5rem; margin: 0.5em 0; }
|
|
36243
|
+
.almadar-rich-text ol { list-style: decimal; padding-inline-start: 1.5rem; margin: 0.5em 0; }
|
|
36244
|
+
.almadar-rich-text li { margin: 0.25em 0; }
|
|
36245
|
+
.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; }
|
|
36246
|
+
.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; }
|
|
36247
|
+
.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; }
|
|
36248
|
+
.almadar-rich-text pre code { background: transparent; padding: 0; }
|
|
36249
|
+
.almadar-rich-text a { color: var(--color-primary); text-decoration: underline; }
|
|
36250
|
+
.almadar-rich-text hr { border: none; border-top: 1px solid var(--color-border); margin: 1.25em 0; }
|
|
36251
|
+
.almadar-rich-text img { max-width: 100%; border-radius: 0.375rem; }
|
|
36252
|
+
`;
|
|
36253
|
+
IDLE_TOOLBAR = {
|
|
36254
|
+
bold: false,
|
|
36255
|
+
italic: false,
|
|
36256
|
+
underline: false,
|
|
36257
|
+
strikeThrough: false,
|
|
36258
|
+
block: null,
|
|
36259
|
+
bullets: false,
|
|
36260
|
+
numbers: false,
|
|
36261
|
+
link: false
|
|
36262
|
+
};
|
|
36263
|
+
RichTextEditor = ({
|
|
36264
|
+
value,
|
|
36265
|
+
onChange,
|
|
36266
|
+
changeEvent,
|
|
36267
|
+
readOnly = false,
|
|
36268
|
+
placeholder,
|
|
36269
|
+
showToolbar = true,
|
|
36270
|
+
className
|
|
36271
|
+
}) => {
|
|
36272
|
+
const { t } = hooks.useTranslate();
|
|
36273
|
+
const ref = React85.useRef(null);
|
|
36274
|
+
const [toolbar, setToolbar] = React85.useState(IDLE_TOOLBAR);
|
|
36275
|
+
const [empty, setEmpty] = React85.useState(() => !value || !sanitizeRichHtml(value).trim());
|
|
36276
|
+
const eventBus = useEventBus();
|
|
36277
|
+
const onChangeRef = React85.useRef(onChange);
|
|
36278
|
+
const changeEventRef = React85.useRef(changeEvent);
|
|
36279
|
+
React85.useEffect(() => {
|
|
36280
|
+
onChangeRef.current = onChange;
|
|
36281
|
+
changeEventRef.current = changeEvent;
|
|
36282
|
+
}, [onChange, changeEvent]);
|
|
36283
|
+
const emitTimerRef = React85.useRef(null);
|
|
36284
|
+
const emitNow = React85.useCallback(() => {
|
|
36285
|
+
const el = ref.current;
|
|
36286
|
+
if (!el) return;
|
|
36287
|
+
const html = sanitizeRichHtml(el.innerHTML);
|
|
36288
|
+
onChangeRef.current?.(html);
|
|
36289
|
+
const evt = changeEventRef.current;
|
|
36290
|
+
if (evt) eventBus.emit(`UI:${evt}`, { value: html });
|
|
36291
|
+
}, [eventBus]);
|
|
36292
|
+
const scheduleEmit = React85.useCallback(() => {
|
|
36293
|
+
if (emitTimerRef.current) clearTimeout(emitTimerRef.current);
|
|
36294
|
+
emitTimerRef.current = setTimeout(() => {
|
|
36295
|
+
emitTimerRef.current = null;
|
|
36296
|
+
emitNow();
|
|
36297
|
+
}, CHANGE_DEBOUNCE_MS);
|
|
36298
|
+
}, [emitNow]);
|
|
36299
|
+
const flushEmit = React85.useCallback(() => {
|
|
36300
|
+
if (!emitTimerRef.current) return;
|
|
36301
|
+
clearTimeout(emitTimerRef.current);
|
|
36302
|
+
emitTimerRef.current = null;
|
|
36303
|
+
emitNow();
|
|
36304
|
+
}, [emitNow]);
|
|
36305
|
+
React85.useEffect(() => () => {
|
|
36306
|
+
if (emitTimerRef.current) {
|
|
36307
|
+
clearTimeout(emitTimerRef.current);
|
|
36308
|
+
emitTimerRef.current = null;
|
|
36309
|
+
emitNow();
|
|
36310
|
+
}
|
|
36311
|
+
}, [emitNow]);
|
|
36312
|
+
React85.useEffect(() => {
|
|
36313
|
+
if (readOnly) return;
|
|
36314
|
+
const el = ref.current;
|
|
36315
|
+
if (!el) return;
|
|
36316
|
+
if (document.activeElement === el) return;
|
|
36317
|
+
const next = sanitizeRichHtml(value ?? "") || "<p><br></p>";
|
|
36318
|
+
if (el.innerHTML !== next) {
|
|
36319
|
+
el.innerHTML = next;
|
|
36320
|
+
setEmpty(htmlIsEmpty(el));
|
|
36321
|
+
}
|
|
36322
|
+
}, [readOnly, value]);
|
|
36323
|
+
React85.useEffect(() => {
|
|
36324
|
+
if (readOnly) return;
|
|
36325
|
+
document.execCommand("defaultParagraphSeparator", false, "p");
|
|
36326
|
+
}, [readOnly]);
|
|
36327
|
+
React85.useEffect(() => {
|
|
36328
|
+
if (readOnly || !showToolbar) return;
|
|
36329
|
+
const onSelectionChange = () => {
|
|
36330
|
+
const el = ref.current;
|
|
36331
|
+
if (!el) return;
|
|
36332
|
+
setToolbar(readToolbarState(el));
|
|
36333
|
+
};
|
|
36334
|
+
document.addEventListener("selectionchange", onSelectionChange);
|
|
36335
|
+
return () => document.removeEventListener("selectionchange", onSelectionChange);
|
|
36336
|
+
}, [readOnly, showToolbar]);
|
|
36337
|
+
const tryInlineMarkdown = React85.useCallback(() => {
|
|
36338
|
+
const root = ref.current;
|
|
36339
|
+
if (!root) return false;
|
|
36340
|
+
const sel = window.getSelection();
|
|
36341
|
+
if (!sel || !sel.isCollapsed || sel.rangeCount === 0) return false;
|
|
36342
|
+
const node = sel.anchorNode;
|
|
36343
|
+
if (!node || node.nodeType !== Node.TEXT_NODE || !root.contains(node)) return false;
|
|
36344
|
+
if (node.parentElement?.closest("code, pre")) return false;
|
|
36345
|
+
const text = (node.textContent ?? "").slice(0, sel.anchorOffset);
|
|
36346
|
+
for (const rule of MD_INLINE_RULES) {
|
|
36347
|
+
const m = rule.pattern.exec(text);
|
|
36348
|
+
if (!m) continue;
|
|
36349
|
+
const inner = rule.wrap === "em" ? m[2] : m[1];
|
|
36350
|
+
if (!inner || !inner.trim()) continue;
|
|
36351
|
+
const lead = rule.wrap === "em" ? m[1]?.length ?? 0 : 0;
|
|
36352
|
+
const range = document.createRange();
|
|
36353
|
+
range.setStart(node, m.index + lead);
|
|
36354
|
+
range.setEnd(node, sel.anchorOffset);
|
|
36355
|
+
const el = document.createElement(rule.wrap);
|
|
36356
|
+
el.textContent = inner;
|
|
36357
|
+
range.deleteContents();
|
|
36358
|
+
range.insertNode(el);
|
|
36359
|
+
const after = document.createTextNode("\u200B");
|
|
36360
|
+
el.parentNode?.insertBefore(after, el.nextSibling);
|
|
36361
|
+
const caret = document.createRange();
|
|
36362
|
+
caret.setStart(after, 1);
|
|
36363
|
+
caret.collapse(true);
|
|
36364
|
+
sel.removeAllRanges();
|
|
36365
|
+
sel.addRange(caret);
|
|
36366
|
+
return true;
|
|
36367
|
+
}
|
|
36368
|
+
return false;
|
|
36369
|
+
}, []);
|
|
36370
|
+
const tryBlockMarkdown = React85.useCallback(() => {
|
|
36371
|
+
const root = ref.current;
|
|
36372
|
+
if (!root) return false;
|
|
36373
|
+
const sel = window.getSelection();
|
|
36374
|
+
if (!sel || !sel.isCollapsed || sel.rangeCount === 0) return false;
|
|
36375
|
+
const node = sel.anchorNode;
|
|
36376
|
+
if (!node || node.nodeType !== Node.TEXT_NODE || !root.contains(node)) return false;
|
|
36377
|
+
const block = closestBlock(node, root);
|
|
36378
|
+
if (block ? block.firstChild !== node : root.firstChild !== node) return false;
|
|
36379
|
+
const prefix = (node.textContent ?? "").slice(0, sel.anchorOffset);
|
|
36380
|
+
const rule = MD_BLOCK_PREFIXES[prefix];
|
|
36381
|
+
if (!rule) return false;
|
|
36382
|
+
if (node.parentElement?.closest("pre, code")) return false;
|
|
36383
|
+
if (rule.kind === "list" && node.parentElement?.closest("li")) return false;
|
|
36384
|
+
node.deleteData(0, prefix.length);
|
|
36385
|
+
const caret = document.createRange();
|
|
36386
|
+
caret.setStart(node, 0);
|
|
36387
|
+
caret.collapse(true);
|
|
36388
|
+
sel.removeAllRanges();
|
|
36389
|
+
sel.addRange(caret);
|
|
36390
|
+
if (rule.kind === "list") {
|
|
36391
|
+
const container = block ?? node.parentElement;
|
|
36392
|
+
const list = document.createElement(rule.command === "insertOrderedList" ? "ol" : "ul");
|
|
36393
|
+
const li = document.createElement("li");
|
|
36394
|
+
if (block) {
|
|
36395
|
+
while (block.firstChild) li.appendChild(block.firstChild);
|
|
36396
|
+
list.appendChild(li);
|
|
36397
|
+
block.replaceWith(list);
|
|
36398
|
+
} else if (container) {
|
|
36399
|
+
li.appendChild(node);
|
|
36400
|
+
list.appendChild(li);
|
|
36401
|
+
container.appendChild(list);
|
|
36402
|
+
}
|
|
36403
|
+
if (!li.firstChild) li.appendChild(document.createElement("br"));
|
|
36404
|
+
const liCaret = document.createRange();
|
|
36405
|
+
liCaret.selectNodeContents(li);
|
|
36406
|
+
liCaret.collapse(true);
|
|
36407
|
+
sel.removeAllRanges();
|
|
36408
|
+
sel.addRange(liCaret);
|
|
36409
|
+
} else {
|
|
36410
|
+
document.execCommand("formatBlock", false, rule.tag);
|
|
36411
|
+
}
|
|
36412
|
+
return true;
|
|
36413
|
+
}, []);
|
|
36414
|
+
const afterEdit = React85.useCallback(() => {
|
|
36415
|
+
const el = ref.current;
|
|
36416
|
+
if (!el) return;
|
|
36417
|
+
tryInlineMarkdown();
|
|
36418
|
+
setEmpty(htmlIsEmpty(el));
|
|
36419
|
+
setToolbar(readToolbarState(el));
|
|
36420
|
+
scheduleEmit();
|
|
36421
|
+
}, [scheduleEmit, tryInlineMarkdown]);
|
|
36422
|
+
const handleKeyDown = React85.useCallback((e) => {
|
|
36423
|
+
if (e.key === " " && tryBlockMarkdown()) {
|
|
36424
|
+
e.preventDefault();
|
|
36425
|
+
afterEdit();
|
|
36426
|
+
}
|
|
36427
|
+
}, [afterEdit, tryBlockMarkdown]);
|
|
36428
|
+
const execInline = React85.useCallback((command) => {
|
|
36429
|
+
document.execCommand(command, false);
|
|
36430
|
+
afterEdit();
|
|
36431
|
+
}, [afterEdit]);
|
|
36432
|
+
const execBlock = React85.useCallback((tag) => {
|
|
36433
|
+
const next = toolbar.block === tag ? "p" : tag;
|
|
36434
|
+
document.execCommand("formatBlock", false, next);
|
|
36435
|
+
afterEdit();
|
|
36436
|
+
}, [afterEdit, toolbar.block]);
|
|
36437
|
+
const execList = React85.useCallback((command) => {
|
|
36438
|
+
document.execCommand(command, false);
|
|
36439
|
+
afterEdit();
|
|
36440
|
+
}, [afterEdit]);
|
|
36441
|
+
const execLink = React85.useCallback(() => {
|
|
36442
|
+
if (toolbar.link) {
|
|
36443
|
+
document.execCommand("unlink", false);
|
|
36444
|
+
afterEdit();
|
|
36445
|
+
return;
|
|
36446
|
+
}
|
|
36447
|
+
const url = window.prompt(t("richTextEditor.linkPrompt"));
|
|
36448
|
+
if (!url) return;
|
|
36449
|
+
const safe = safeUrl(url, ["http://", "https://", "mailto:"]) ?? `https://${url}`;
|
|
36450
|
+
document.execCommand("createLink", false, safe);
|
|
36451
|
+
afterEdit();
|
|
36452
|
+
}, [afterEdit, t, toolbar.link]);
|
|
36453
|
+
const execRule = React85.useCallback(() => {
|
|
36454
|
+
document.execCommand("insertHorizontalRule", false);
|
|
36455
|
+
afterEdit();
|
|
36456
|
+
}, [afterEdit]);
|
|
36457
|
+
if (readOnly) {
|
|
36458
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: cn("almadar-rich-text max-w-none", className), children: [
|
|
36459
|
+
/* @__PURE__ */ jsxRuntime.jsx(RichTextStyles, {}),
|
|
36460
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { dangerouslySetInnerHTML: { __html: sanitizeRichHtml(value ?? "") } })
|
|
36461
|
+
] });
|
|
36462
|
+
}
|
|
36463
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: cn("flex flex-col gap-2", className), children: [
|
|
36464
|
+
/* @__PURE__ */ jsxRuntime.jsx(RichTextStyles, {}),
|
|
36465
|
+
showToolbar && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
36466
|
+
Box,
|
|
36467
|
+
{
|
|
36468
|
+
role: "toolbar",
|
|
36469
|
+
"aria-label": t("richTextEditor.editorToolbar"),
|
|
36470
|
+
className: cn(
|
|
36471
|
+
"sticky top-0 z-10 flex flex-wrap items-center gap-0.5 self-start",
|
|
36472
|
+
"rounded-md border border-border bg-background/95 px-1 py-0.5 shadow-sm"
|
|
36473
|
+
),
|
|
36474
|
+
children: [
|
|
36475
|
+
/* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Bold, label: t("richTextEditor.bold"), active: toolbar.bold, onExec: () => execInline("bold") }),
|
|
36476
|
+
/* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Italic, label: t("richTextEditor.italic"), active: toolbar.italic, onExec: () => execInline("italic") }),
|
|
36477
|
+
/* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Underline, label: t("richTextEditor.underline"), active: toolbar.underline, onExec: () => execInline("underline") }),
|
|
36478
|
+
/* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Strikethrough, label: t("richTextEditor.strikethrough"), active: toolbar.strikeThrough, onExec: () => execInline("strikeThrough") }),
|
|
36479
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mx-1 h-5 w-px bg-border" }),
|
|
36480
|
+
/* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Heading1, label: t("richTextEditor.heading1"), active: toolbar.block === "h1", onExec: () => execBlock("h1") }),
|
|
36481
|
+
/* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Heading2, label: t("richTextEditor.heading2"), active: toolbar.block === "h2", onExec: () => execBlock("h2") }),
|
|
36482
|
+
/* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Heading3, label: t("richTextEditor.heading3"), active: toolbar.block === "h3", onExec: () => execBlock("h3") }),
|
|
36483
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mx-1 h-5 w-px bg-border" }),
|
|
36484
|
+
/* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.List, label: t("richTextEditor.bulletList"), active: toolbar.bullets, onExec: () => execList("insertUnorderedList") }),
|
|
36485
|
+
/* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.ListOrdered, label: t("richTextEditor.numberedList"), active: toolbar.numbers, onExec: () => execList("insertOrderedList") }),
|
|
36486
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mx-1 h-5 w-px bg-border" }),
|
|
36487
|
+
/* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Quote, label: t("richTextEditor.quote"), active: toolbar.block === "blockquote", onExec: () => execBlock("blockquote") }),
|
|
36488
|
+
/* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Code, label: t("richTextEditor.code"), active: toolbar.block === "pre", onExec: () => execBlock("pre") }),
|
|
36489
|
+
/* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Link, label: toolbar.link ? t("richTextEditor.removeLink") : t("richTextEditor.link"), active: toolbar.link, onExec: execLink }),
|
|
36490
|
+
/* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Minus, label: t("richTextEditor.divider"), onExec: execRule })
|
|
36491
|
+
]
|
|
36492
|
+
}
|
|
36493
|
+
),
|
|
36494
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
36495
|
+
Box,
|
|
36496
|
+
{
|
|
36497
|
+
ref,
|
|
36498
|
+
contentEditable: true,
|
|
36499
|
+
suppressContentEditableWarning: true,
|
|
36500
|
+
role: "textbox",
|
|
36501
|
+
"aria-multiline": "true",
|
|
36502
|
+
"aria-label": t("richTextEditor.editorSurface"),
|
|
36503
|
+
"data-placeholder": placeholder ?? t("richTextEditor.placeholder"),
|
|
36504
|
+
"data-empty": empty ? "true" : "false",
|
|
36505
|
+
className: cn(
|
|
36506
|
+
"almadar-rich-text max-w-none min-h-[8rem] outline-none",
|
|
36507
|
+
"data-[empty=true]:before:content-[attr(data-placeholder)]",
|
|
36508
|
+
"data-[empty=true]:before:text-muted-foreground/60",
|
|
36509
|
+
"data-[empty=true]:before:pointer-events-none",
|
|
36510
|
+
"data-[empty=true]:before:absolute",
|
|
36511
|
+
"relative cursor-text"
|
|
36512
|
+
),
|
|
36513
|
+
onInput: afterEdit,
|
|
36514
|
+
onKeyDown: handleKeyDown,
|
|
36515
|
+
onBlur: flushEmit
|
|
36516
|
+
}
|
|
36517
|
+
)
|
|
36518
|
+
] });
|
|
36189
36519
|
};
|
|
36520
|
+
RichTextEditor.displayName = "RichTextEditor";
|
|
36190
36521
|
}
|
|
36191
|
-
|
|
36192
|
-
|
|
36522
|
+
});
|
|
36523
|
+
function renderIconInput4(icon, props) {
|
|
36524
|
+
return typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon, ...props });
|
|
36193
36525
|
}
|
|
36194
|
-
function
|
|
36526
|
+
function DocumentPanel({
|
|
36527
|
+
entity,
|
|
36528
|
+
title,
|
|
36529
|
+
subtitle,
|
|
36530
|
+
value,
|
|
36531
|
+
actions,
|
|
36532
|
+
maxInlineActions,
|
|
36533
|
+
editing = false,
|
|
36534
|
+
titleCommitEvent,
|
|
36535
|
+
contentChangeEvent,
|
|
36536
|
+
editEvent,
|
|
36537
|
+
doneEvent,
|
|
36538
|
+
editLabel,
|
|
36539
|
+
doneLabel,
|
|
36540
|
+
autosaveHint,
|
|
36541
|
+
placeholder,
|
|
36542
|
+
className
|
|
36543
|
+
}) {
|
|
36544
|
+
const eventBus = useEventBus();
|
|
36195
36545
|
const { t } = hooks.useTranslate();
|
|
36196
|
-
const [
|
|
36197
|
-
const
|
|
36198
|
-
|
|
36199
|
-
|
|
36200
|
-
|
|
36201
|
-
|
|
36202
|
-
|
|
36203
|
-
|
|
36546
|
+
const [titleDraft, setTitleDraft] = React85.useState(null);
|
|
36547
|
+
const recordId = entity?.id !== void 0 && entity?.id !== null ? String(entity.id) : "";
|
|
36548
|
+
const actionDefs = actions ?? [];
|
|
36549
|
+
const inlineCap = Math.min(maxInlineActions ?? 1, 1);
|
|
36550
|
+
const inlineActions = actionDefs.filter((a) => a.variant === "primary").slice(0, inlineCap);
|
|
36551
|
+
const menuActions = actionDefs.filter((a) => !inlineActions.includes(a));
|
|
36552
|
+
const fireAction = (action) => {
|
|
36553
|
+
if (!action.event) return;
|
|
36554
|
+
eventBus.emit(`UI:${action.event}`, { id: recordId, row: entity });
|
|
36555
|
+
};
|
|
36556
|
+
const commitTitle = () => {
|
|
36557
|
+
if (!titleCommitEvent) return;
|
|
36558
|
+
const next = (titleDraft ?? "").trim();
|
|
36559
|
+
setTitleDraft(null);
|
|
36560
|
+
if (!next || next === title) return;
|
|
36561
|
+
eventBus.emit(`UI:${titleCommitEvent}`, { title: next, id: recordId });
|
|
36562
|
+
};
|
|
36563
|
+
const untitled = t("documentPanel.untitled") || "Untitled";
|
|
36564
|
+
const titleNode = titleCommitEvent && titleDraft !== null ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
36565
|
+
Input,
|
|
36566
|
+
{
|
|
36567
|
+
value: titleDraft,
|
|
36568
|
+
autoFocus: true,
|
|
36569
|
+
"aria-label": t("common.title"),
|
|
36570
|
+
className: "h-auto py-1 text-3xl font-bold tracking-tight",
|
|
36571
|
+
onFocus: (e) => e.currentTarget.select(),
|
|
36572
|
+
onChange: (e) => setTitleDraft(e.target.value),
|
|
36573
|
+
onBlur: commitTitle,
|
|
36574
|
+
onKeyDown: (e) => {
|
|
36575
|
+
if (e.key === "Enter") {
|
|
36576
|
+
e.preventDefault();
|
|
36577
|
+
commitTitle();
|
|
36578
|
+
} else if (e.key === "Escape") {
|
|
36579
|
+
e.preventDefault();
|
|
36580
|
+
setTitleDraft(null);
|
|
36581
|
+
}
|
|
36582
|
+
},
|
|
36583
|
+
"data-testid": "document-title-input"
|
|
36204
36584
|
}
|
|
36205
|
-
|
|
36206
|
-
|
|
36207
|
-
|
|
36208
|
-
|
|
36209
|
-
|
|
36210
|
-
|
|
36211
|
-
|
|
36212
|
-
{
|
|
36213
|
-
|
|
36214
|
-
|
|
36215
|
-
|
|
36216
|
-
|
|
36217
|
-
|
|
36218
|
-
|
|
36219
|
-
|
|
36220
|
-
|
|
36221
|
-
|
|
36222
|
-
|
|
36223
|
-
|
|
36224
|
-
|
|
36225
|
-
|
|
36226
|
-
|
|
36227
|
-
|
|
36585
|
+
) : titleCommitEvent ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
36586
|
+
Box,
|
|
36587
|
+
{
|
|
36588
|
+
role: "button",
|
|
36589
|
+
tabIndex: 0,
|
|
36590
|
+
className: "cursor-text rounded px-1 -mx-1 transition-colors hover:bg-muted/40",
|
|
36591
|
+
onClick: () => setTitleDraft(title ?? ""),
|
|
36592
|
+
onKeyDown: (e) => {
|
|
36593
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
36594
|
+
e.preventDefault();
|
|
36595
|
+
setTitleDraft(title ?? "");
|
|
36596
|
+
}
|
|
36597
|
+
},
|
|
36598
|
+
"data-testid": "document-title-editable",
|
|
36599
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", weight: "bold", className: cn(!title && "text-muted-foreground"), children: title || untitled })
|
|
36600
|
+
}
|
|
36601
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", weight: "bold", children: title || untitled });
|
|
36602
|
+
const emitWithId = (event) => () => {
|
|
36603
|
+
if (event) eventBus.emit(`UI:${event}`, { id: recordId });
|
|
36604
|
+
};
|
|
36605
|
+
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: [
|
|
36606
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "justify-between items-start", children: [
|
|
36607
|
+
/* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", className: "flex-1 min-w-0", children: [
|
|
36608
|
+
titleNode,
|
|
36609
|
+
subtitle ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: subtitle }) : null
|
|
36610
|
+
] }),
|
|
36611
|
+
/* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", className: "flex-shrink-0 pt-1 items-center", children: editing ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
36612
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "muted", className: "hidden sm:block", children: autosaveHint ?? (t("documentPanel.autosaveHint") || "") }),
|
|
36613
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", size: "sm", onClick: emitWithId(doneEvent), "data-testid": "document-done", children: doneLabel ?? (t("documentPanel.done") || "Done") })
|
|
36614
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
36615
|
+
editEvent && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "ghost", size: "sm", onClick: emitWithId(editEvent), "data-testid": "document-edit", children: [
|
|
36616
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "edit", size: "xs", className: "mr-1" }),
|
|
36617
|
+
editLabel ?? (t("documentPanel.edit") || "Edit")
|
|
36618
|
+
] }),
|
|
36619
|
+
inlineActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
36620
|
+
Button,
|
|
36621
|
+
{
|
|
36622
|
+
variant: "primary",
|
|
36623
|
+
size: "sm",
|
|
36624
|
+
onClick: () => fireAction(action),
|
|
36625
|
+
"data-testid": `action-${action.event}`,
|
|
36626
|
+
children: [
|
|
36627
|
+
action.icon && renderIconInput4(action.icon, { size: "xs", className: "mr-1" }),
|
|
36628
|
+
action.label
|
|
36629
|
+
]
|
|
36630
|
+
},
|
|
36631
|
+
idx
|
|
36632
|
+
)),
|
|
36633
|
+
menuActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
36634
|
+
Menu,
|
|
36635
|
+
{
|
|
36636
|
+
position: "bottom-end",
|
|
36637
|
+
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" }) }),
|
|
36638
|
+
items: menuActions.map((action) => ({
|
|
36639
|
+
label: action.label,
|
|
36640
|
+
icon: action.icon,
|
|
36641
|
+
variant: action.variant === "danger" ? "danger" : "default",
|
|
36642
|
+
onClick: () => fireAction(action)
|
|
36643
|
+
}))
|
|
36644
|
+
}
|
|
36645
|
+
)
|
|
36646
|
+
] }) })
|
|
36647
|
+
] }),
|
|
36648
|
+
editing ? /* @__PURE__ */ jsxRuntime.jsx(RichTextEditor, { value, changeEvent: contentChangeEvent, placeholder }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
36228
36649
|
Box,
|
|
36229
36650
|
{
|
|
36230
|
-
|
|
36651
|
+
onClick: emitWithId(editEvent),
|
|
36231
36652
|
className: cn(
|
|
36232
|
-
"
|
|
36233
|
-
"
|
|
36234
|
-
"py-1 text-sm"
|
|
36653
|
+
"rounded-md px-1 -mx-1 min-h-[16rem]",
|
|
36654
|
+
editEvent && "cursor-text transition-colors hover:bg-muted/30"
|
|
36235
36655
|
),
|
|
36236
|
-
|
|
36237
|
-
|
|
36238
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
36239
|
-
Button,
|
|
36240
|
-
{
|
|
36241
|
-
type: "button",
|
|
36242
|
-
variant: "ghost",
|
|
36243
|
-
role: "menuitem",
|
|
36244
|
-
className: "flex w-full items-center gap-2 px-2 py-1.5 text-left justify-start rounded-none",
|
|
36245
|
-
onClick: () => {
|
|
36246
|
-
onDuplicate();
|
|
36247
|
-
setOpen(false);
|
|
36248
|
-
},
|
|
36249
|
-
children: [
|
|
36250
|
-
/* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "plus", className: "w-3.5 h-3.5" }),
|
|
36251
|
-
" ",
|
|
36252
|
-
t("richBlockEditor.duplicate")
|
|
36253
|
-
]
|
|
36254
|
-
}
|
|
36255
|
-
),
|
|
36256
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
36257
|
-
Button,
|
|
36258
|
-
{
|
|
36259
|
-
type: "button",
|
|
36260
|
-
variant: "ghost",
|
|
36261
|
-
role: "menuitem",
|
|
36262
|
-
className: "flex w-full items-center gap-2 px-2 py-1.5 text-left text-destructive hover:bg-muted justify-start rounded-none",
|
|
36263
|
-
onClick: () => {
|
|
36264
|
-
onDelete();
|
|
36265
|
-
setOpen(false);
|
|
36266
|
-
},
|
|
36267
|
-
children: [
|
|
36268
|
-
/* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "trash", className: "w-3.5 h-3.5" }),
|
|
36269
|
-
" ",
|
|
36270
|
-
t("common.delete")
|
|
36271
|
-
]
|
|
36272
|
-
}
|
|
36273
|
-
),
|
|
36274
|
-
CHANGEABLE_TYPES.includes(block.type) && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
36275
|
-
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "my-1 border-t border-border" }),
|
|
36276
|
-
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "px-2 py-1 text-xs uppercase tracking-wide text-muted-foreground", children: t("richBlockEditor.turnInto") }),
|
|
36277
|
-
CHANGEABLE_TYPES.filter((bt) => bt !== block.type).map((bt) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
36278
|
-
Button,
|
|
36279
|
-
{
|
|
36280
|
-
type: "button",
|
|
36281
|
-
variant: "ghost",
|
|
36282
|
-
role: "menuitem",
|
|
36283
|
-
className: "flex w-full items-center gap-2 px-2 py-1.5 text-left justify-start rounded-none",
|
|
36284
|
-
onClick: () => {
|
|
36285
|
-
onChangeType(bt);
|
|
36286
|
-
setOpen(false);
|
|
36287
|
-
},
|
|
36288
|
-
children: t(BLOCK_TYPE_LABEL_KEY[bt])
|
|
36289
|
-
},
|
|
36290
|
-
bt
|
|
36291
|
-
))
|
|
36292
|
-
] })
|
|
36293
|
-
]
|
|
36656
|
+
"data-testid": "document-body",
|
|
36657
|
+
children: value && value !== "" ? /* @__PURE__ */ jsxRuntime.jsx(RichTextEditor, { value, readOnly: true }) : /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "muted", children: placeholder ?? (t("documentPanel.placeholder") || "") })
|
|
36294
36658
|
}
|
|
36295
36659
|
)
|
|
36296
|
-
] });
|
|
36660
|
+
] }) });
|
|
36297
36661
|
}
|
|
36298
|
-
|
|
36299
|
-
|
|
36300
|
-
|
|
36301
|
-
|
|
36302
|
-
|
|
36303
|
-
|
|
36304
|
-
|
|
36305
|
-
|
|
36306
|
-
|
|
36307
|
-
|
|
36308
|
-
|
|
36309
|
-
|
|
36310
|
-
|
|
36311
|
-
|
|
36312
|
-
|
|
36313
|
-
|
|
36314
|
-
|
|
36315
|
-
},
|
|
36316
|
-
const handleInput = React85.useCallback(
|
|
36317
|
-
(e) => {
|
|
36318
|
-
onValueChange(e.currentTarget.textContent ?? "");
|
|
36319
|
-
},
|
|
36320
|
-
[onValueChange]
|
|
36321
|
-
);
|
|
36322
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
36323
|
-
Box,
|
|
36324
|
-
{
|
|
36325
|
-
as: tag,
|
|
36326
|
-
ref,
|
|
36327
|
-
contentEditable: !readOnly,
|
|
36328
|
-
suppressContentEditableWarning: true,
|
|
36329
|
-
role: readOnly ? void 0 : "textbox",
|
|
36330
|
-
"aria-label": ariaLabel,
|
|
36331
|
-
"aria-multiline": "true",
|
|
36332
|
-
"data-placeholder": placeholder,
|
|
36333
|
-
className: cn(
|
|
36334
|
-
"outline-none focus-visible:ring-1 focus-visible:ring-ring rounded-sm",
|
|
36335
|
-
"empty:before:content-[attr(data-placeholder)] empty:before:text-muted-foreground/60",
|
|
36336
|
-
className
|
|
36337
|
-
),
|
|
36338
|
-
onInput: handleInput
|
|
36339
|
-
}
|
|
36340
|
-
);
|
|
36662
|
+
var init_DocumentPanel = __esm({
|
|
36663
|
+
"components/core/molecules/DocumentPanel.tsx"() {
|
|
36664
|
+
"use client";
|
|
36665
|
+
init_cn();
|
|
36666
|
+
init_useEventBus();
|
|
36667
|
+
init_Box();
|
|
36668
|
+
init_Stack();
|
|
36669
|
+
init_Typography();
|
|
36670
|
+
init_Button();
|
|
36671
|
+
init_Card();
|
|
36672
|
+
init_Icon();
|
|
36673
|
+
init_Input();
|
|
36674
|
+
init_Menu();
|
|
36675
|
+
init_RichTextEditor();
|
|
36676
|
+
}
|
|
36677
|
+
});
|
|
36678
|
+
function renderIconInput5(icon, props) {
|
|
36679
|
+
return typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon, ...props });
|
|
36341
36680
|
}
|
|
36342
|
-
function
|
|
36343
|
-
|
|
36344
|
-
|
|
36345
|
-
|
|
36346
|
-
|
|
36347
|
-
|
|
36348
|
-
|
|
36349
|
-
|
|
36350
|
-
|
|
36681
|
+
function fieldName(field) {
|
|
36682
|
+
return field.name ?? field.key ?? "";
|
|
36683
|
+
}
|
|
36684
|
+
function relationLabel(value) {
|
|
36685
|
+
if (value === null || typeof value !== "object" || Array.isArray(value) || value instanceof Date) return null;
|
|
36686
|
+
for (const key of ["name", "title", "label"]) {
|
|
36687
|
+
const candidate = value[key];
|
|
36688
|
+
if (typeof candidate === "string" && candidate !== "") return candidate;
|
|
36689
|
+
}
|
|
36690
|
+
const id = value.id;
|
|
36691
|
+
return id !== void 0 && id !== null ? String(id) : null;
|
|
36692
|
+
}
|
|
36693
|
+
function fieldKind(field, value) {
|
|
36694
|
+
if (field.kind) return field.kind;
|
|
36695
|
+
if (field.options && field.options.length > 0) return "select";
|
|
36696
|
+
if (typeof value === "boolean" || field.format === "boolean") return "boolean";
|
|
36697
|
+
if (Array.isArray(value) || relationLabel(value ?? null) !== null) return "readonly";
|
|
36698
|
+
return "text";
|
|
36699
|
+
}
|
|
36700
|
+
function DocumentDetails({
|
|
36701
|
+
entity,
|
|
36702
|
+
fields,
|
|
36703
|
+
metaCommitEvent,
|
|
36704
|
+
title,
|
|
36705
|
+
className
|
|
36351
36706
|
}) {
|
|
36707
|
+
const eventBus = useEventBus();
|
|
36352
36708
|
const { t } = hooks.useTranslate();
|
|
36353
|
-
const
|
|
36354
|
-
|
|
36355
|
-
|
|
36356
|
-
);
|
|
36357
|
-
const
|
|
36358
|
-
(
|
|
36359
|
-
|
|
36360
|
-
|
|
36361
|
-
|
|
36362
|
-
|
|
36363
|
-
|
|
36364
|
-
|
|
36365
|
-
|
|
36366
|
-
|
|
36367
|
-
|
|
36368
|
-
|
|
36369
|
-
|
|
36370
|
-
|
|
36371
|
-
|
|
36372
|
-
|
|
36373
|
-
|
|
36374
|
-
|
|
36375
|
-
|
|
36376
|
-
|
|
36377
|
-
|
|
36378
|
-
|
|
36379
|
-
|
|
36380
|
-
|
|
36381
|
-
|
|
36382
|
-
|
|
36383
|
-
|
|
36384
|
-
|
|
36385
|
-
|
|
36386
|
-
|
|
36387
|
-
|
|
36388
|
-
|
|
36389
|
-
|
|
36390
|
-
}
|
|
36391
|
-
|
|
36392
|
-
|
|
36393
|
-
|
|
36394
|
-
|
|
36395
|
-
|
|
36396
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
36397
|
-
Editable,
|
|
36398
|
-
{
|
|
36399
|
-
tag: "h1",
|
|
36400
|
-
value: block.content ?? "",
|
|
36401
|
-
readOnly,
|
|
36402
|
-
placeholder: placeholder ?? t("richBlockEditor.placeholder.heading1"),
|
|
36403
|
-
ariaLabel: t("richBlockEditor.aria.heading1Block"),
|
|
36404
|
-
className: "text-3xl font-bold leading-tight",
|
|
36405
|
-
onValueChange: setContent
|
|
36406
|
-
}
|
|
36407
|
-
);
|
|
36408
|
-
case "heading-2":
|
|
36409
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
36410
|
-
Editable,
|
|
36411
|
-
{
|
|
36412
|
-
tag: "h2",
|
|
36413
|
-
value: block.content ?? "",
|
|
36414
|
-
readOnly,
|
|
36415
|
-
placeholder: placeholder ?? t("richBlockEditor.placeholder.heading2"),
|
|
36416
|
-
ariaLabel: t("richBlockEditor.aria.heading2Block"),
|
|
36417
|
-
className: "text-2xl font-semibold leading-tight",
|
|
36418
|
-
onValueChange: setContent
|
|
36419
|
-
}
|
|
36420
|
-
);
|
|
36421
|
-
case "heading-3":
|
|
36422
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
36423
|
-
Editable,
|
|
36424
|
-
{
|
|
36425
|
-
tag: "h3",
|
|
36426
|
-
value: block.content ?? "",
|
|
36427
|
-
readOnly,
|
|
36428
|
-
placeholder: placeholder ?? t("richBlockEditor.placeholder.heading3"),
|
|
36429
|
-
ariaLabel: t("richBlockEditor.aria.heading3Block"),
|
|
36430
|
-
className: "text-xl font-semibold leading-tight",
|
|
36431
|
-
onValueChange: setContent
|
|
36432
|
-
}
|
|
36433
|
-
);
|
|
36434
|
-
case "quote":
|
|
36435
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
36436
|
-
Editable,
|
|
36437
|
-
{
|
|
36438
|
-
tag: "blockquote",
|
|
36439
|
-
value: block.content ?? "",
|
|
36440
|
-
readOnly,
|
|
36441
|
-
placeholder: placeholder ?? t("richBlockEditor.placeholder.quote"),
|
|
36442
|
-
ariaLabel: t("richBlockEditor.aria.quoteBlock"),
|
|
36443
|
-
className: "border-l-4 border-primary/60 pl-4 italic text-muted-foreground",
|
|
36444
|
-
onValueChange: setContent
|
|
36445
|
-
}
|
|
36446
|
-
);
|
|
36447
|
-
case "code":
|
|
36448
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "rounded-md border border-border bg-muted/40", children: [
|
|
36449
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center justify-between border-b border-border px-3 py-1 text-xs text-muted-foreground", children: [
|
|
36450
|
-
/* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", variant: "caption", className: "uppercase tracking-wide", children: t("richBlockEditor.blockType.code") }),
|
|
36451
|
-
!readOnly && /* @__PURE__ */ jsxRuntime.jsx(
|
|
36452
|
-
Input,
|
|
36453
|
-
{
|
|
36454
|
-
inputType: "text",
|
|
36455
|
-
value: String(block.metadata?.language ?? "plaintext"),
|
|
36456
|
-
"aria-label": t("richBlockEditor.aria.codeLanguage"),
|
|
36457
|
-
className: cn(
|
|
36458
|
-
"h-6 w-32 rounded-sm border border-border bg-background",
|
|
36459
|
-
"px-2 text-xs outline-none focus:ring-1 focus:ring-ring"
|
|
36460
|
-
),
|
|
36461
|
-
onChange: (e) => setMetadata("language", e.target.value)
|
|
36462
|
-
}
|
|
36463
|
-
),
|
|
36464
|
-
readOnly && /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", variant: "caption", className: "text-xs", children: String(block.metadata?.language ?? "plaintext") })
|
|
36465
|
-
] }),
|
|
36466
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
36467
|
-
Editable,
|
|
36468
|
-
{
|
|
36469
|
-
tag: "pre",
|
|
36470
|
-
value: block.content ?? "",
|
|
36471
|
-
readOnly,
|
|
36472
|
-
placeholder: placeholder ?? t("richBlockEditor.placeholder.code"),
|
|
36473
|
-
ariaLabel: t("richBlockEditor.aria.codeBlock"),
|
|
36474
|
-
className: "block whitespace-pre-wrap p-3 font-mono text-sm leading-relaxed",
|
|
36475
|
-
onValueChange: setContent
|
|
36476
|
-
}
|
|
36477
|
-
)
|
|
36478
|
-
] });
|
|
36479
|
-
case "divider":
|
|
36480
|
-
return /* @__PURE__ */ jsxRuntime.jsx(Divider, { className: "my-2" });
|
|
36481
|
-
case "image": {
|
|
36482
|
-
const url = String(block.metadata?.url ?? "");
|
|
36483
|
-
const caption = String(block.metadata?.caption ?? "");
|
|
36484
|
-
const imgProps = {
|
|
36485
|
-
src: url,
|
|
36486
|
-
alt: caption || t("richBlockEditor.embeddedImage"),
|
|
36487
|
-
className: "max-h-96 w-full rounded-md border border-border object-contain"
|
|
36488
|
-
};
|
|
36489
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "space-y-2", children: [
|
|
36490
|
-
url ? /* @__PURE__ */ jsxRuntime.jsx(Box, { as: "img", ...imgProps }) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
36491
|
-
Box,
|
|
36492
|
-
{
|
|
36493
|
-
className: cn(
|
|
36494
|
-
"flex h-32 items-center justify-center",
|
|
36495
|
-
"rounded-md border border-dashed border-border",
|
|
36496
|
-
"text-sm text-muted-foreground"
|
|
36497
|
-
),
|
|
36498
|
-
children: [
|
|
36499
|
-
/* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "image", className: "mr-2 w-4 h-4" }),
|
|
36500
|
-
" ",
|
|
36501
|
-
t("richBlockEditor.noImageUrl")
|
|
36502
|
-
]
|
|
36503
|
-
}
|
|
36504
|
-
),
|
|
36505
|
-
!readOnly && /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex flex-col gap-2 sm:flex-row", children: [
|
|
36506
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
36507
|
-
Input,
|
|
36508
|
-
{
|
|
36509
|
-
inputType: "url",
|
|
36510
|
-
value: url,
|
|
36511
|
-
placeholder: "https://example.com/image.png",
|
|
36512
|
-
"aria-label": t("richBlockEditor.aria.imageUrl"),
|
|
36513
|
-
className: cn(
|
|
36514
|
-
"h-8 flex-1 rounded-sm border border-border bg-background",
|
|
36515
|
-
"px-2 text-sm outline-none focus:ring-1 focus:ring-ring"
|
|
36516
|
-
),
|
|
36517
|
-
onChange: (e) => setMetadata("url", e.target.value)
|
|
36518
|
-
}
|
|
36519
|
-
),
|
|
36520
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
36521
|
-
Input,
|
|
36522
|
-
{
|
|
36523
|
-
inputType: "text",
|
|
36524
|
-
value: caption,
|
|
36525
|
-
placeholder: t("richBlockEditor.placeholder.caption"),
|
|
36526
|
-
"aria-label": t("richBlockEditor.aria.imageCaption"),
|
|
36527
|
-
className: cn(
|
|
36528
|
-
"h-8 flex-1 rounded-sm border border-border bg-background",
|
|
36529
|
-
"px-2 text-sm outline-none focus:ring-1 focus:ring-ring"
|
|
36530
|
-
),
|
|
36531
|
-
onChange: (e) => setMetadata("caption", e.target.value)
|
|
36532
|
-
}
|
|
36533
|
-
)
|
|
36534
|
-
] }),
|
|
36535
|
-
readOnly && caption && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-center text-muted-foreground", children: caption })
|
|
36536
|
-
] });
|
|
36709
|
+
const [fieldDraft, setFieldDraft] = React85.useState(null);
|
|
36710
|
+
const recordId = entity?.id !== void 0 && entity?.id !== null ? String(entity.id) : "";
|
|
36711
|
+
const fieldDefs = (fields ?? []).filter((f3) => fieldName(f3) !== "");
|
|
36712
|
+
if (fieldDefs.length === 0) return null;
|
|
36713
|
+
const commitField = (name, next, current) => {
|
|
36714
|
+
setFieldDraft(null);
|
|
36715
|
+
if (!metaCommitEvent || next === current) return;
|
|
36716
|
+
eventBus.emit(`UI:${metaCommitEvent}`, { id: recordId, patch: { id: recordId, [name]: next } });
|
|
36717
|
+
};
|
|
36718
|
+
const renderValue = (field) => {
|
|
36719
|
+
const name = fieldName(field);
|
|
36720
|
+
const raw = getNestedValue(entity ?? {}, name);
|
|
36721
|
+
const kind = fieldKind(field, raw);
|
|
36722
|
+
const label = field.label ?? field.header ?? humanizeFieldName(name);
|
|
36723
|
+
if (kind === "boolean") {
|
|
36724
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
36725
|
+
Switch,
|
|
36726
|
+
{
|
|
36727
|
+
checked: Boolean(raw),
|
|
36728
|
+
disabled: !metaCommitEvent,
|
|
36729
|
+
"aria-label": label,
|
|
36730
|
+
onChange: (checked) => commitField(name, checked, Boolean(raw))
|
|
36731
|
+
}
|
|
36732
|
+
);
|
|
36733
|
+
}
|
|
36734
|
+
if (kind === "select") {
|
|
36735
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
36736
|
+
Select,
|
|
36737
|
+
{
|
|
36738
|
+
value: raw !== void 0 && raw !== null ? String(raw) : "",
|
|
36739
|
+
disabled: !metaCommitEvent,
|
|
36740
|
+
"aria-label": label,
|
|
36741
|
+
className: "h-8 w-full",
|
|
36742
|
+
options: (field.options ?? []).map((opt) => ({ value: opt, label: humanizeEnumValue(opt) })),
|
|
36743
|
+
onChange: (e) => commitField(name, e.target.value, raw)
|
|
36744
|
+
}
|
|
36745
|
+
);
|
|
36746
|
+
}
|
|
36747
|
+
if (kind === "readonly" || !metaCommitEvent) {
|
|
36748
|
+
if (Array.isArray(raw)) {
|
|
36749
|
+
if (raw.length === 0) {
|
|
36750
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: "\u2014" });
|
|
36751
|
+
}
|
|
36752
|
+
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)) });
|
|
36537
36753
|
}
|
|
36538
|
-
|
|
36539
|
-
|
|
36540
|
-
|
|
36541
|
-
|
|
36542
|
-
|
|
36543
|
-
|
|
36544
|
-
|
|
36545
|
-
|
|
36546
|
-
|
|
36547
|
-
|
|
36548
|
-
|
|
36549
|
-
|
|
36550
|
-
|
|
36551
|
-
|
|
36552
|
-
|
|
36553
|
-
|
|
36554
|
-
|
|
36555
|
-
|
|
36556
|
-
|
|
36557
|
-
|
|
36558
|
-
|
|
36559
|
-
|
|
36560
|
-
|
|
36561
|
-
|
|
36562
|
-
|
|
36563
|
-
|
|
36564
|
-
|
|
36565
|
-
|
|
36566
|
-
|
|
36567
|
-
|
|
36568
|
-
|
|
36569
|
-
|
|
36570
|
-
|
|
36571
|
-
|
|
36572
|
-
|
|
36573
|
-
|
|
36574
|
-
|
|
36575
|
-
|
|
36576
|
-
|
|
36577
|
-
|
|
36578
|
-
|
|
36579
|
-
Button,
|
|
36580
|
-
{
|
|
36581
|
-
type: "button",
|
|
36582
|
-
variant: "ghost",
|
|
36583
|
-
className: cn(
|
|
36584
|
-
"inline-flex items-center gap-1 text-xs text-muted-foreground",
|
|
36585
|
-
"hover:text-foreground p-0 h-auto"
|
|
36586
|
-
),
|
|
36587
|
-
onClick: addListItem,
|
|
36588
|
-
children: [
|
|
36589
|
-
/* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "plus", className: "w-3 h-3" }),
|
|
36590
|
-
" ",
|
|
36591
|
-
t("richBlockEditor.addItem")
|
|
36592
|
-
]
|
|
36593
|
-
}
|
|
36594
|
-
) })
|
|
36595
|
-
]
|
|
36754
|
+
const shown2 = raw === void 0 || raw === null || raw === "" ? "\u2014" : relationLabel(raw) ?? formatValue(raw, field.format);
|
|
36755
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "break-words", children: shown2 });
|
|
36756
|
+
}
|
|
36757
|
+
if (fieldDraft?.name === name) {
|
|
36758
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
36759
|
+
Input,
|
|
36760
|
+
{
|
|
36761
|
+
value: fieldDraft.value,
|
|
36762
|
+
autoFocus: true,
|
|
36763
|
+
"aria-label": label,
|
|
36764
|
+
className: "h-8 w-full",
|
|
36765
|
+
onChange: (e) => setFieldDraft({ name, value: e.target.value }),
|
|
36766
|
+
onBlur: () => commitField(name, fieldDraft.value.trim(), raw),
|
|
36767
|
+
onKeyDown: (e) => {
|
|
36768
|
+
if (e.key === "Enter") {
|
|
36769
|
+
e.preventDefault();
|
|
36770
|
+
commitField(name, fieldDraft.value.trim(), raw);
|
|
36771
|
+
} else if (e.key === "Escape") {
|
|
36772
|
+
e.preventDefault();
|
|
36773
|
+
setFieldDraft(null);
|
|
36774
|
+
}
|
|
36775
|
+
},
|
|
36776
|
+
"data-testid": `document-property-input-${name}`
|
|
36777
|
+
}
|
|
36778
|
+
);
|
|
36779
|
+
}
|
|
36780
|
+
const shown = raw === void 0 || raw === null || raw === "" ? "\u2014" : relationLabel(raw) ?? formatValue(raw, field.format);
|
|
36781
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
36782
|
+
Box,
|
|
36783
|
+
{
|
|
36784
|
+
role: "button",
|
|
36785
|
+
tabIndex: 0,
|
|
36786
|
+
className: cn(
|
|
36787
|
+
"cursor-text rounded px-1 -mx-1 transition-colors hover:bg-muted/40",
|
|
36788
|
+
(raw === void 0 || raw === null || raw === "") && "text-muted-foreground"
|
|
36789
|
+
),
|
|
36790
|
+
onClick: () => setFieldDraft({ name, value: raw !== void 0 && raw !== null ? String(raw) : "" }),
|
|
36791
|
+
onKeyDown: (e) => {
|
|
36792
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
36793
|
+
e.preventDefault();
|
|
36794
|
+
setFieldDraft({ name, value: raw !== void 0 && raw !== null ? String(raw) : "" });
|
|
36596
36795
|
}
|
|
36597
|
-
|
|
36796
|
+
},
|
|
36797
|
+
"data-testid": `document-property-${name}`,
|
|
36798
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "break-words", children: shown })
|
|
36598
36799
|
}
|
|
36599
|
-
|
|
36600
|
-
default:
|
|
36601
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
36602
|
-
Editable,
|
|
36603
|
-
{
|
|
36604
|
-
tag: "p",
|
|
36605
|
-
value: block.content ?? "",
|
|
36606
|
-
readOnly,
|
|
36607
|
-
placeholder: placeholder ?? t("richBlockEditor.placeholder.paragraph"),
|
|
36608
|
-
ariaLabel: t("richBlockEditor.aria.paragraphBlock"),
|
|
36609
|
-
className: "leading-7",
|
|
36610
|
-
onValueChange: setContent
|
|
36611
|
-
}
|
|
36612
|
-
);
|
|
36613
|
-
}
|
|
36800
|
+
);
|
|
36614
36801
|
};
|
|
36615
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
36616
|
-
|
|
36617
|
-
{
|
|
36618
|
-
|
|
36619
|
-
|
|
36620
|
-
|
|
36621
|
-
|
|
36622
|
-
|
|
36623
|
-
|
|
36624
|
-
|
|
36625
|
-
|
|
36626
|
-
|
|
36627
|
-
|
|
36628
|
-
|
|
36629
|
-
readOnly,
|
|
36630
|
-
onDelete,
|
|
36631
|
-
onDuplicate,
|
|
36632
|
-
onChangeType
|
|
36633
|
-
}
|
|
36634
|
-
) }),
|
|
36635
|
-
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "min-w-0 flex-1", children: renderBody() })
|
|
36636
|
-
]
|
|
36637
|
-
}
|
|
36638
|
-
);
|
|
36802
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Card, { variant: "bordered", className: cn("w-full", className), children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", className: "p-4", children: [
|
|
36803
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", weight: "medium", className: "uppercase tracking-wide", children: title ?? (t("documentDetails.title") || "Details") }),
|
|
36804
|
+
/* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "sm", children: fieldDefs.map((field) => {
|
|
36805
|
+
const name = fieldName(field);
|
|
36806
|
+
const label = field.label ?? field.header ?? humanizeFieldName(name);
|
|
36807
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
|
|
36808
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "items-center", children: [
|
|
36809
|
+
field.icon && renderIconInput5(field.icon, { size: "xs", className: "text-muted-foreground" }),
|
|
36810
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "muted", children: label })
|
|
36811
|
+
] }),
|
|
36812
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "min-w-0", children: renderValue(field) })
|
|
36813
|
+
] }, name);
|
|
36814
|
+
}) })
|
|
36815
|
+
] }) });
|
|
36639
36816
|
}
|
|
36640
|
-
var
|
|
36641
|
-
|
|
36642
|
-
"components/core/molecules/RichBlockEditor.tsx"() {
|
|
36817
|
+
var init_DocumentDetails = __esm({
|
|
36818
|
+
"components/core/molecules/DocumentDetails.tsx"() {
|
|
36643
36819
|
"use client";
|
|
36644
36820
|
init_cn();
|
|
36645
|
-
|
|
36646
|
-
|
|
36647
|
-
|
|
36821
|
+
init_format();
|
|
36822
|
+
init_getNestedValue();
|
|
36823
|
+
init_useEventBus();
|
|
36648
36824
|
init_Box();
|
|
36649
|
-
|
|
36650
|
-
|
|
36825
|
+
init_Stack();
|
|
36826
|
+
init_Typography();
|
|
36827
|
+
init_Badge();
|
|
36828
|
+
init_Card();
|
|
36651
36829
|
init_Icon();
|
|
36652
|
-
|
|
36653
|
-
|
|
36654
|
-
|
|
36655
|
-
{ type: "heading-1", labelKey: "richBlockEditor.toolbar.h1", icon: LucideIcons2.Heading1 },
|
|
36656
|
-
{ type: "heading-2", labelKey: "richBlockEditor.toolbar.h2", icon: LucideIcons2.Heading2 },
|
|
36657
|
-
{ type: "heading-3", labelKey: "richBlockEditor.toolbar.h3", icon: LucideIcons2.Heading3 },
|
|
36658
|
-
{ type: "bullet-list", labelKey: "richBlockEditor.toolbar.bulletList", icon: LucideIcons2.List },
|
|
36659
|
-
{ type: "numbered-list", labelKey: "richBlockEditor.toolbar.numbered", icon: LucideIcons2.ListOrdered },
|
|
36660
|
-
{ type: "quote", labelKey: "richBlockEditor.toolbar.quote", icon: LucideIcons2.Quote },
|
|
36661
|
-
{ type: "code", labelKey: "richBlockEditor.toolbar.code", icon: LucideIcons2.Code },
|
|
36662
|
-
{ type: "divider", labelKey: "richBlockEditor.toolbar.divider", icon: LucideIcons2.Minus },
|
|
36663
|
-
{ type: "image", labelKey: "richBlockEditor.toolbar.image", icon: LucideIcons2.Image }
|
|
36664
|
-
];
|
|
36665
|
-
BLOCK_TYPE_LABEL_KEY = {
|
|
36666
|
-
paragraph: "richBlockEditor.blockType.paragraph",
|
|
36667
|
-
"heading-1": "richBlockEditor.blockType.heading1",
|
|
36668
|
-
"heading-2": "richBlockEditor.blockType.heading2",
|
|
36669
|
-
"heading-3": "richBlockEditor.blockType.heading3",
|
|
36670
|
-
"bullet-list": "richBlockEditor.blockType.bulletList",
|
|
36671
|
-
"numbered-list": "richBlockEditor.blockType.numberedList",
|
|
36672
|
-
quote: "richBlockEditor.blockType.quote",
|
|
36673
|
-
code: "richBlockEditor.blockType.code",
|
|
36674
|
-
divider: "richBlockEditor.blockType.divider",
|
|
36675
|
-
image: "richBlockEditor.blockType.image"
|
|
36676
|
-
};
|
|
36677
|
-
CHANGEABLE_TYPES = [
|
|
36678
|
-
"paragraph",
|
|
36679
|
-
"heading-1",
|
|
36680
|
-
"heading-2",
|
|
36681
|
-
"heading-3",
|
|
36682
|
-
"bullet-list",
|
|
36683
|
-
"numbered-list",
|
|
36684
|
-
"quote",
|
|
36685
|
-
"code"
|
|
36686
|
-
];
|
|
36687
|
-
_idSeq = 0;
|
|
36688
|
-
BLOCK_TYPES = /* @__PURE__ */ new Set([
|
|
36689
|
-
"paragraph",
|
|
36690
|
-
"heading-1",
|
|
36691
|
-
"heading-2",
|
|
36692
|
-
"heading-3",
|
|
36693
|
-
"bullet-list",
|
|
36694
|
-
"numbered-list",
|
|
36695
|
-
"quote",
|
|
36696
|
-
"code",
|
|
36697
|
-
"divider",
|
|
36698
|
-
"image"
|
|
36699
|
-
]);
|
|
36700
|
-
RichBlockEditor = ({
|
|
36701
|
-
initialBlocks,
|
|
36702
|
-
onChange,
|
|
36703
|
-
changeEvent,
|
|
36704
|
-
readOnly = false,
|
|
36705
|
-
placeholder,
|
|
36706
|
-
enableBlocks = true,
|
|
36707
|
-
showToolbar = true,
|
|
36708
|
-
className
|
|
36709
|
-
}) => {
|
|
36710
|
-
const { t } = hooks.useTranslate();
|
|
36711
|
-
const [blocks, setBlocks] = React85.useState(
|
|
36712
|
-
() => normalizeBlocks(initialBlocks)
|
|
36713
|
-
);
|
|
36714
|
-
const onChangeRef = React85.useRef(onChange);
|
|
36715
|
-
React85.useEffect(() => {
|
|
36716
|
-
onChangeRef.current = onChange;
|
|
36717
|
-
}, [onChange]);
|
|
36718
|
-
const eventBus = useEventBus();
|
|
36719
|
-
const changeEventRef = React85.useRef(changeEvent);
|
|
36720
|
-
React85.useEffect(() => {
|
|
36721
|
-
changeEventRef.current = changeEvent;
|
|
36722
|
-
}, [changeEvent]);
|
|
36723
|
-
const commit = React85.useCallback((next) => {
|
|
36724
|
-
setBlocks(next);
|
|
36725
|
-
onChangeRef.current?.(next);
|
|
36726
|
-
const evt = changeEventRef.current;
|
|
36727
|
-
if (evt) eventBus.emit(`UI:${evt}`, { blocks: next });
|
|
36728
|
-
}, [eventBus]);
|
|
36729
|
-
const handleAppend = React85.useCallback(
|
|
36730
|
-
(type) => {
|
|
36731
|
-
if (readOnly) return;
|
|
36732
|
-
commit([...blocks, createBlock(type)]);
|
|
36733
|
-
},
|
|
36734
|
-
[blocks, commit, readOnly]
|
|
36735
|
-
);
|
|
36736
|
-
const handleUpdate = React85.useCallback(
|
|
36737
|
-
(id, updater) => {
|
|
36738
|
-
commit(replaceBlock(blocks, id, updater));
|
|
36739
|
-
},
|
|
36740
|
-
[blocks, commit]
|
|
36741
|
-
);
|
|
36742
|
-
const handleDelete = React85.useCallback(
|
|
36743
|
-
(id) => {
|
|
36744
|
-
const next = removeBlock(blocks, id);
|
|
36745
|
-
commit(next.length > 0 ? next : [createBlock("paragraph")]);
|
|
36746
|
-
},
|
|
36747
|
-
[blocks, commit]
|
|
36748
|
-
);
|
|
36749
|
-
const handleDuplicate = React85.useCallback(
|
|
36750
|
-
(id) => {
|
|
36751
|
-
const target = blocks.find((b) => b.id === id);
|
|
36752
|
-
if (!target) return;
|
|
36753
|
-
commit(insertAfter(blocks, id, duplicateBlock(target)));
|
|
36754
|
-
},
|
|
36755
|
-
[blocks, commit]
|
|
36756
|
-
);
|
|
36757
|
-
const handleChangeType = React85.useCallback(
|
|
36758
|
-
(id, type) => {
|
|
36759
|
-
commit(
|
|
36760
|
-
replaceBlock(blocks, id, (b) => changeBlockType(b, type))
|
|
36761
|
-
);
|
|
36762
|
-
},
|
|
36763
|
-
[blocks, commit]
|
|
36764
|
-
);
|
|
36765
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
36766
|
-
Card,
|
|
36767
|
-
{
|
|
36768
|
-
variant: "bordered",
|
|
36769
|
-
padding: "none",
|
|
36770
|
-
className: cn("flex flex-col text-card-foreground", className),
|
|
36771
|
-
children: [
|
|
36772
|
-
enableBlocks && showToolbar && !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
|
|
36773
|
-
Box,
|
|
36774
|
-
{
|
|
36775
|
-
role: "toolbar",
|
|
36776
|
-
"aria-label": t("richBlockEditor.editorToolbar"),
|
|
36777
|
-
className: cn(
|
|
36778
|
-
"flex flex-wrap items-center gap-1",
|
|
36779
|
-
"border-b border-border bg-muted/30 px-2 py-2"
|
|
36780
|
-
),
|
|
36781
|
-
children: TOOLBAR_ENTRIES.map((entry) => {
|
|
36782
|
-
const Icon2 = entry.icon;
|
|
36783
|
-
const entryLabel = t(entry.labelKey);
|
|
36784
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
36785
|
-
Button,
|
|
36786
|
-
{
|
|
36787
|
-
type: "button",
|
|
36788
|
-
variant: "ghost",
|
|
36789
|
-
size: "sm",
|
|
36790
|
-
"aria-label": t("richBlockEditor.insertEntry", { label: entryLabel }),
|
|
36791
|
-
title: entryLabel,
|
|
36792
|
-
onClick: () => handleAppend(entry.type),
|
|
36793
|
-
children: [
|
|
36794
|
-
/* @__PURE__ */ jsxRuntime.jsx(Icon2, { size: 14 }),
|
|
36795
|
-
/* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", variant: "caption", className: "ml-1 hidden text-xs sm:inline", children: entryLabel })
|
|
36796
|
-
]
|
|
36797
|
-
},
|
|
36798
|
-
entry.type
|
|
36799
|
-
);
|
|
36800
|
-
})
|
|
36801
|
-
}
|
|
36802
|
-
),
|
|
36803
|
-
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex flex-col gap-1 px-3 py-3", children: blocks.map((block) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
36804
|
-
BlockRow,
|
|
36805
|
-
{
|
|
36806
|
-
block,
|
|
36807
|
-
readOnly,
|
|
36808
|
-
showAffordances: enableBlocks,
|
|
36809
|
-
placeholder,
|
|
36810
|
-
onUpdate: (updater) => handleUpdate(block.id, updater),
|
|
36811
|
-
onDelete: () => handleDelete(block.id),
|
|
36812
|
-
onDuplicate: () => handleDuplicate(block.id),
|
|
36813
|
-
onChangeType: (type) => handleChangeType(block.id, type)
|
|
36814
|
-
},
|
|
36815
|
-
block.id
|
|
36816
|
-
)) })
|
|
36817
|
-
]
|
|
36818
|
-
}
|
|
36819
|
-
);
|
|
36820
|
-
};
|
|
36821
|
-
RichBlockEditor.displayName = "RichBlockEditor";
|
|
36830
|
+
init_Input();
|
|
36831
|
+
init_Select();
|
|
36832
|
+
init_Switch();
|
|
36822
36833
|
}
|
|
36823
36834
|
});
|
|
36824
36835
|
function collectInitiallyCollapsed(nodes, acc) {
|
|
@@ -41767,8 +41778,8 @@ var init_DataTable = __esm({
|
|
|
41767
41778
|
DataTable.displayName = "DataTable";
|
|
41768
41779
|
}
|
|
41769
41780
|
});
|
|
41770
|
-
function getBadgeVariant(
|
|
41771
|
-
const name =
|
|
41781
|
+
function getBadgeVariant(fieldName2, value) {
|
|
41782
|
+
const name = fieldName2.toLowerCase();
|
|
41772
41783
|
const val = String(value).toLowerCase();
|
|
41773
41784
|
if (name.includes("status")) {
|
|
41774
41785
|
if (val.includes("complete") || val.includes("done") || val.includes("active"))
|
|
@@ -41784,12 +41795,12 @@ function getBadgeVariant(fieldName, value) {
|
|
|
41784
41795
|
}
|
|
41785
41796
|
return "default";
|
|
41786
41797
|
}
|
|
41787
|
-
function formatFieldValue2(value,
|
|
41798
|
+
function formatFieldValue2(value, fieldName2) {
|
|
41788
41799
|
if (typeof value === "number") {
|
|
41789
|
-
if (
|
|
41800
|
+
if (fieldName2.toLowerCase().includes("progress") || fieldName2.toLowerCase().includes("percent")) {
|
|
41790
41801
|
return `${value}%`;
|
|
41791
41802
|
}
|
|
41792
|
-
if (
|
|
41803
|
+
if (fieldName2.toLowerCase().includes("budget") || fieldName2.toLowerCase().includes("cost")) {
|
|
41793
41804
|
return `$${value.toLocaleString()}`;
|
|
41794
41805
|
}
|
|
41795
41806
|
return value.toLocaleString();
|
|
@@ -41799,7 +41810,7 @@ function formatFieldValue2(value, fieldName) {
|
|
|
41799
41810
|
}
|
|
41800
41811
|
return String(value);
|
|
41801
41812
|
}
|
|
41802
|
-
function renderRichFieldValue(value,
|
|
41813
|
+
function renderRichFieldValue(value, fieldName2, fieldType, meta) {
|
|
41803
41814
|
if (value === void 0 || value === null) return "\u2014";
|
|
41804
41815
|
const str = String(value);
|
|
41805
41816
|
switch (fieldType) {
|
|
@@ -41810,7 +41821,7 @@ function renderRichFieldValue(value, fieldName, fieldType, meta) {
|
|
|
41810
41821
|
"img",
|
|
41811
41822
|
{
|
|
41812
41823
|
src: str,
|
|
41813
|
-
alt: formatFieldLabel(
|
|
41824
|
+
alt: formatFieldLabel(fieldName2),
|
|
41814
41825
|
className: "max-w-full max-h-64 rounded-md object-contain",
|
|
41815
41826
|
loading: "lazy"
|
|
41816
41827
|
}
|
|
@@ -41945,9 +41956,9 @@ function renderRichFieldValue(value, fieldName, fieldType, meta) {
|
|
|
41945
41956
|
return /* @__PURE__ */ jsxRuntime.jsx("a", { href: `tel:${str}`, className: "text-primary hover:underline", children: str });
|
|
41946
41957
|
default:
|
|
41947
41958
|
if (meta?.values && meta.values.length > 0 && meta.values.includes(str)) {
|
|
41948
|
-
return /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: getBadgeVariant(
|
|
41959
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: getBadgeVariant(fieldName2, str), children: humanizeEnumValue(str) });
|
|
41949
41960
|
}
|
|
41950
|
-
return formatFieldValue2(value,
|
|
41961
|
+
return formatFieldValue2(value, fieldName2);
|
|
41951
41962
|
}
|
|
41952
41963
|
}
|
|
41953
41964
|
function normalizeFieldDefs(fields) {
|
|
@@ -42790,7 +42801,21 @@ var init_Form = __esm({
|
|
|
42790
42801
|
const normalizedInitialData = React85__namespace.default.useMemo(() => {
|
|
42791
42802
|
const entityRowAsInitial = isPlainEntityRow(entity) ? entity : void 0;
|
|
42792
42803
|
const callerInitial = initialData !== null && typeof initialData === "object" && !Array.isArray(initialData) ? initialData : {};
|
|
42793
|
-
|
|
42804
|
+
const merged = entityRowAsInitial !== void 0 ? { ...entityRowAsInitial, ...callerInitial } : callerInitial;
|
|
42805
|
+
const toId = (value) => {
|
|
42806
|
+
if (value === null || value === void 0 || typeof value !== "object" || value instanceof Date) return value;
|
|
42807
|
+
if (Array.isArray(value)) {
|
|
42808
|
+
return value.map(
|
|
42809
|
+
(item) => item !== null && typeof item === "object" && !Array.isArray(item) && !(item instanceof Date) && item.id !== void 0 && item.id !== null ? String(item.id) : item
|
|
42810
|
+
);
|
|
42811
|
+
}
|
|
42812
|
+
return value.id !== void 0 && value.id !== null ? String(value.id) : value;
|
|
42813
|
+
};
|
|
42814
|
+
const normalized = {};
|
|
42815
|
+
for (const [key, value] of Object.entries(merged)) {
|
|
42816
|
+
normalized[key] = key === "id" ? value : toId(value);
|
|
42817
|
+
}
|
|
42818
|
+
return normalized;
|
|
42794
42819
|
}, [entity, initialData]);
|
|
42795
42820
|
const entityDerivedFields = React85__namespace.default.useMemo(() => {
|
|
42796
42821
|
if (fields && fields.length > 0) return void 0;
|
|
@@ -42919,8 +42944,8 @@ var init_Form = __esm({
|
|
|
42919
42944
|
checkViolations(name, newFormData);
|
|
42920
42945
|
};
|
|
42921
42946
|
const isFieldVisible = React85__namespace.default.useCallback(
|
|
42922
|
-
(
|
|
42923
|
-
const condition = conditionalFields[
|
|
42947
|
+
(fieldName2) => {
|
|
42948
|
+
const condition = conditionalFields[fieldName2];
|
|
42924
42949
|
if (!condition) return true;
|
|
42925
42950
|
return Boolean(evaluateFormExpression(condition, evalContext));
|
|
42926
42951
|
},
|
|
@@ -42966,9 +42991,9 @@ var init_Form = __esm({
|
|
|
42966
42991
|
};
|
|
42967
42992
|
const handleInvalid = (e) => {
|
|
42968
42993
|
const target = e.target;
|
|
42969
|
-
const
|
|
42994
|
+
const fieldName2 = target.getAttribute("data-field-name") ?? target.name ?? "";
|
|
42970
42995
|
const fieldMessage = target.validationMessage || "Invalid value";
|
|
42971
|
-
debug("forms", "invalid", { mode: formMode, fieldName, fieldMessage });
|
|
42996
|
+
debug("forms", "invalid", { mode: formMode, fieldName: fieldName2, fieldMessage });
|
|
42972
42997
|
queueMicrotask(() => {
|
|
42973
42998
|
const form = formRef.current;
|
|
42974
42999
|
if (!form) return;
|
|
@@ -43004,22 +43029,22 @@ var init_Form = __esm({
|
|
|
43004
43029
|
};
|
|
43005
43030
|
const renderField = React85__namespace.default.useCallback(
|
|
43006
43031
|
(field) => {
|
|
43007
|
-
const
|
|
43008
|
-
if (!
|
|
43009
|
-
if (!isFieldVisible(
|
|
43032
|
+
const fieldName2 = field.name || field.field;
|
|
43033
|
+
if (!fieldName2) return null;
|
|
43034
|
+
if (!isFieldVisible(fieldName2)) {
|
|
43010
43035
|
return null;
|
|
43011
43036
|
}
|
|
43012
43037
|
const inputType = determineInputType(field);
|
|
43013
|
-
const label = field.label ||
|
|
43014
|
-
const currentValue = formData[
|
|
43015
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", "data-field":
|
|
43038
|
+
const label = field.label || fieldName2.charAt(0).toUpperCase() + fieldName2.slice(1).replace(/([A-Z])/g, " $1");
|
|
43039
|
+
const currentValue = formData[fieldName2] ?? field.defaultValue ?? "";
|
|
43040
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", "data-field": fieldName2, children: [
|
|
43016
43041
|
inputType !== "checkbox" && /* @__PURE__ */ jsxRuntime.jsxs(Typography, { as: "label", variant: "label", weight: "bold", children: [
|
|
43017
43042
|
label,
|
|
43018
43043
|
field.required && /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", color: "error", className: "ml-1", children: "*" })
|
|
43019
43044
|
] }),
|
|
43020
|
-
renderFieldInput(field,
|
|
43045
|
+
renderFieldInput(field, fieldName2, inputType, currentValue, label),
|
|
43021
43046
|
field.hint && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "muted", children: field.hint })
|
|
43022
|
-
] },
|
|
43047
|
+
] }, fieldName2);
|
|
43023
43048
|
},
|
|
43024
43049
|
[formData, isFieldVisible, relationsData, relationsLoading, isLoading]
|
|
43025
43050
|
);
|
|
@@ -43048,8 +43073,8 @@ var init_Form = __esm({
|
|
|
43048
43073
|
}
|
|
43049
43074
|
return field;
|
|
43050
43075
|
}).map((field) => {
|
|
43051
|
-
const
|
|
43052
|
-
const override = fieldOverrides?.find((o) => o.name ===
|
|
43076
|
+
const fieldName2 = field.name || field.field;
|
|
43077
|
+
const override = fieldOverrides?.find((o) => o.name === fieldName2);
|
|
43053
43078
|
if (!override) return field;
|
|
43054
43079
|
return {
|
|
43055
43080
|
...field,
|
|
@@ -43105,11 +43130,11 @@ var init_Form = __esm({
|
|
|
43105
43130
|
] }, section.id);
|
|
43106
43131
|
}).filter(Boolean);
|
|
43107
43132
|
}, [sections, isSectionVisible, collapsedSections, renderField, gap]);
|
|
43108
|
-
function renderFieldInput(field,
|
|
43133
|
+
function renderFieldInput(field, fieldName2, inputType, currentValue, label) {
|
|
43109
43134
|
const commonProps = {
|
|
43110
|
-
id:
|
|
43111
|
-
name:
|
|
43112
|
-
"data-field-name":
|
|
43135
|
+
id: fieldName2,
|
|
43136
|
+
name: fieldName2,
|
|
43137
|
+
"data-field-name": fieldName2,
|
|
43113
43138
|
required: field.required,
|
|
43114
43139
|
disabled: isLoading,
|
|
43115
43140
|
placeholder: field.placeholder,
|
|
@@ -43123,7 +43148,7 @@ var init_Form = __esm({
|
|
|
43123
43148
|
...commonProps,
|
|
43124
43149
|
label: label + (field.required ? " *" : ""),
|
|
43125
43150
|
checked: Boolean(currentValue),
|
|
43126
|
-
onChange: (e) => handleChange(
|
|
43151
|
+
onChange: (e) => handleChange(fieldName2, e.target.checked)
|
|
43127
43152
|
}
|
|
43128
43153
|
);
|
|
43129
43154
|
case "textarea":
|
|
@@ -43132,7 +43157,7 @@ var init_Form = __esm({
|
|
|
43132
43157
|
{
|
|
43133
43158
|
...commonProps,
|
|
43134
43159
|
value: String(currentValue),
|
|
43135
|
-
onChange: (e) => handleChange(
|
|
43160
|
+
onChange: (e) => handleChange(fieldName2, e.target.value),
|
|
43136
43161
|
minLength: field.min,
|
|
43137
43162
|
maxLength: field.max
|
|
43138
43163
|
}
|
|
@@ -43145,14 +43170,14 @@ var init_Form = __esm({
|
|
|
43145
43170
|
...commonProps,
|
|
43146
43171
|
options,
|
|
43147
43172
|
value: String(currentValue),
|
|
43148
|
-
onValueChange: (v) => handleChange(
|
|
43173
|
+
onValueChange: (v) => handleChange(fieldName2, v),
|
|
43149
43174
|
placeholder: field.placeholder || `Select ${label}...`
|
|
43150
43175
|
}
|
|
43151
43176
|
);
|
|
43152
43177
|
}
|
|
43153
43178
|
case "relation": {
|
|
43154
|
-
const relationOptions = relationsData[
|
|
43155
|
-
const relationLoading = relationsLoading[
|
|
43179
|
+
const relationOptions = relationsData[fieldName2] || [];
|
|
43180
|
+
const relationLoading = relationsLoading[fieldName2] || false;
|
|
43156
43181
|
if (field.relation?.cardinality !== void 0 && MANY_CARDINALITIES.includes(field.relation.cardinality)) {
|
|
43157
43182
|
const selectedValues = Array.isArray(currentValue) ? currentValue.map((v) => String(v)) : [];
|
|
43158
43183
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -43164,7 +43189,7 @@ var init_Form = __esm({
|
|
|
43164
43189
|
clearable: true,
|
|
43165
43190
|
options: [...relationOptions],
|
|
43166
43191
|
value: selectedValues,
|
|
43167
|
-
onValueChange: (value) => handleChange(
|
|
43192
|
+
onValueChange: (value) => handleChange(fieldName2, Array.isArray(value) ? value : [value]),
|
|
43168
43193
|
placeholder: field.placeholder || `Select ${label}...`
|
|
43169
43194
|
}
|
|
43170
43195
|
);
|
|
@@ -43174,7 +43199,7 @@ var init_Form = __esm({
|
|
|
43174
43199
|
{
|
|
43175
43200
|
...commonProps,
|
|
43176
43201
|
value: currentValue ? String(currentValue) : void 0,
|
|
43177
|
-
onChange: (value) => handleChange(
|
|
43202
|
+
onChange: (value) => handleChange(fieldName2, value),
|
|
43178
43203
|
options: relationOptions,
|
|
43179
43204
|
isLoading: relationLoading,
|
|
43180
43205
|
placeholder: field.placeholder || `Select ${label}...`,
|
|
@@ -43191,7 +43216,7 @@ var init_Form = __esm({
|
|
|
43191
43216
|
placeholder: field.placeholder,
|
|
43192
43217
|
disabled: isLoading,
|
|
43193
43218
|
value: arrayValue,
|
|
43194
|
-
onChange: (next) => handleChange(
|
|
43219
|
+
onChange: (next) => handleChange(fieldName2, [...next])
|
|
43195
43220
|
}
|
|
43196
43221
|
);
|
|
43197
43222
|
}
|
|
@@ -43203,7 +43228,7 @@ var init_Form = __esm({
|
|
|
43203
43228
|
type: "number",
|
|
43204
43229
|
value: currentValue !== void 0 && currentValue !== "" ? String(currentValue) : "",
|
|
43205
43230
|
onChange: (e) => handleChange(
|
|
43206
|
-
|
|
43231
|
+
fieldName2,
|
|
43207
43232
|
e.target.value ? Number(e.target.value) : void 0
|
|
43208
43233
|
),
|
|
43209
43234
|
min: field.min,
|
|
@@ -43220,7 +43245,7 @@ var init_Form = __esm({
|
|
|
43220
43245
|
icon: LucideIcons2.DollarSign,
|
|
43221
43246
|
value: currentValue !== void 0 && currentValue !== "" ? String(currentValue) : "",
|
|
43222
43247
|
onChange: (e) => handleChange(
|
|
43223
|
-
|
|
43248
|
+
fieldName2,
|
|
43224
43249
|
e.target.value ? Number(e.target.value) : void 0
|
|
43225
43250
|
),
|
|
43226
43251
|
min: field.min,
|
|
@@ -43238,7 +43263,7 @@ var init_Form = __esm({
|
|
|
43238
43263
|
const f3 = files[0];
|
|
43239
43264
|
if (!f3) return;
|
|
43240
43265
|
const reader = new FileReader();
|
|
43241
|
-
reader.onload = () => handleChange(
|
|
43266
|
+
reader.onload = () => handleChange(fieldName2, {
|
|
43242
43267
|
name: f3.name,
|
|
43243
43268
|
mimeType: f3.type,
|
|
43244
43269
|
sizeBytes: f3.size,
|
|
@@ -43255,7 +43280,7 @@ var init_Form = __esm({
|
|
|
43255
43280
|
...commonProps,
|
|
43256
43281
|
type: "date",
|
|
43257
43282
|
value: formatDateValue(currentValue),
|
|
43258
|
-
onChange: (e) => handleChange(
|
|
43283
|
+
onChange: (e) => handleChange(fieldName2, e.target.value)
|
|
43259
43284
|
}
|
|
43260
43285
|
);
|
|
43261
43286
|
case "datetime-local":
|
|
@@ -43265,7 +43290,7 @@ var init_Form = __esm({
|
|
|
43265
43290
|
...commonProps,
|
|
43266
43291
|
type: "datetime-local",
|
|
43267
43292
|
value: formatDateTimeValue(currentValue),
|
|
43268
|
-
onChange: (e) => handleChange(
|
|
43293
|
+
onChange: (e) => handleChange(fieldName2, e.target.value)
|
|
43269
43294
|
}
|
|
43270
43295
|
);
|
|
43271
43296
|
case "email":
|
|
@@ -43275,7 +43300,7 @@ var init_Form = __esm({
|
|
|
43275
43300
|
...commonProps,
|
|
43276
43301
|
type: "email",
|
|
43277
43302
|
value: String(currentValue),
|
|
43278
|
-
onChange: (e) => handleChange(
|
|
43303
|
+
onChange: (e) => handleChange(fieldName2, e.target.value),
|
|
43279
43304
|
minLength: field.min,
|
|
43280
43305
|
maxLength: field.max
|
|
43281
43306
|
}
|
|
@@ -43287,7 +43312,7 @@ var init_Form = __esm({
|
|
|
43287
43312
|
...commonProps,
|
|
43288
43313
|
type: "url",
|
|
43289
43314
|
value: String(currentValue),
|
|
43290
|
-
onChange: (e) => handleChange(
|
|
43315
|
+
onChange: (e) => handleChange(fieldName2, e.target.value),
|
|
43291
43316
|
minLength: field.min,
|
|
43292
43317
|
maxLength: field.max
|
|
43293
43318
|
}
|
|
@@ -43299,7 +43324,7 @@ var init_Form = __esm({
|
|
|
43299
43324
|
...commonProps,
|
|
43300
43325
|
type: "password",
|
|
43301
43326
|
value: String(currentValue),
|
|
43302
|
-
onChange: (e) => handleChange(
|
|
43327
|
+
onChange: (e) => handleChange(fieldName2, e.target.value),
|
|
43303
43328
|
minLength: field.min,
|
|
43304
43329
|
maxLength: field.max
|
|
43305
43330
|
}
|
|
@@ -43312,7 +43337,7 @@ var init_Form = __esm({
|
|
|
43312
43337
|
...commonProps,
|
|
43313
43338
|
type: "text",
|
|
43314
43339
|
value: String(currentValue),
|
|
43315
|
-
onChange: (e) => handleChange(
|
|
43340
|
+
onChange: (e) => handleChange(fieldName2, e.target.value),
|
|
43316
43341
|
minLength: field.min,
|
|
43317
43342
|
maxLength: field.max
|
|
43318
43343
|
}
|
|
@@ -43669,7 +43694,7 @@ function entityFieldsFromListItem(item) {
|
|
|
43669
43694
|
}
|
|
43670
43695
|
return result;
|
|
43671
43696
|
}
|
|
43672
|
-
function getStatusStyle(
|
|
43697
|
+
function getStatusStyle(fieldName2, value) {
|
|
43673
43698
|
const val = String(value).toLowerCase();
|
|
43674
43699
|
if (val.includes("complete") || val.includes("done"))
|
|
43675
43700
|
return STATUS_STYLES.complete;
|
|
@@ -43685,12 +43710,12 @@ function getStatusStyle(fieldName, value) {
|
|
|
43685
43710
|
if (val.includes("low")) return STATUS_STYLES.low;
|
|
43686
43711
|
return STATUS_STYLES.default;
|
|
43687
43712
|
}
|
|
43688
|
-
function formatValue3(value,
|
|
43713
|
+
function formatValue3(value, fieldName2) {
|
|
43689
43714
|
if (typeof value === "number") {
|
|
43690
|
-
if (
|
|
43715
|
+
if (fieldName2.toLowerCase().includes("progress") || fieldName2.toLowerCase().includes("percent")) {
|
|
43691
43716
|
return `${value}%`;
|
|
43692
43717
|
}
|
|
43693
|
-
if (
|
|
43718
|
+
if (fieldName2.toLowerCase().includes("budget") || fieldName2.toLowerCase().includes("cost")) {
|
|
43694
43719
|
return new Intl.NumberFormat("en-US", {
|
|
43695
43720
|
style: "currency",
|
|
43696
43721
|
currency: "USD",
|
|
@@ -43713,8 +43738,8 @@ function formatValue3(value, fieldName) {
|
|
|
43713
43738
|
}
|
|
43714
43739
|
return String(value);
|
|
43715
43740
|
}
|
|
43716
|
-
function formatFieldLabel2(
|
|
43717
|
-
return humanizeFieldName(
|
|
43741
|
+
function formatFieldLabel2(fieldName2) {
|
|
43742
|
+
return humanizeFieldName(fieldName2).replace(/\sId$/, "").trim();
|
|
43718
43743
|
}
|
|
43719
43744
|
var STATUS_STYLES, StatusBadge, ProgressIndicator, List3;
|
|
43720
43745
|
var init_List = __esm({
|
|
@@ -43782,9 +43807,9 @@ var init_List = __esm({
|
|
|
43782
43807
|
};
|
|
43783
43808
|
StatusBadge = ({
|
|
43784
43809
|
value,
|
|
43785
|
-
fieldName
|
|
43810
|
+
fieldName: fieldName2
|
|
43786
43811
|
}) => {
|
|
43787
|
-
const style = getStatusStyle(
|
|
43812
|
+
const style = getStatusStyle(fieldName2, value);
|
|
43788
43813
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
43789
43814
|
Typography,
|
|
43790
43815
|
{
|
|
@@ -46716,8 +46741,8 @@ var init_StatCard = __esm({
|
|
|
46716
46741
|
return items.length;
|
|
46717
46742
|
}
|
|
46718
46743
|
if (field.includes(":")) {
|
|
46719
|
-
const [
|
|
46720
|
-
return items.filter((item) => item[
|
|
46744
|
+
const [fieldName2, fieldValue] = field.split(":");
|
|
46745
|
+
return items.filter((item) => item[fieldName2] === fieldValue).length;
|
|
46721
46746
|
}
|
|
46722
46747
|
const fieldExistsOnItems = items.some((item) => field in item);
|
|
46723
46748
|
if (fieldExistsOnItems) {
|
|
@@ -48237,6 +48262,8 @@ var init_component_registry_generated = __esm({
|
|
|
48237
48262
|
init_DocSearch();
|
|
48238
48263
|
init_DocSidebar();
|
|
48239
48264
|
init_DocTOC();
|
|
48265
|
+
init_DocumentDetails();
|
|
48266
|
+
init_DocumentPanel();
|
|
48240
48267
|
init_DocumentViewer();
|
|
48241
48268
|
init_DrawFxLayer();
|
|
48242
48269
|
init_DrawGroup();
|
|
@@ -48346,7 +48373,7 @@ var init_component_registry_generated = __esm({
|
|
|
48346
48373
|
init_RelationSelect();
|
|
48347
48374
|
init_RepeatableFormSection();
|
|
48348
48375
|
init_ReplyTree();
|
|
48349
|
-
|
|
48376
|
+
init_RichTextEditor();
|
|
48350
48377
|
init_RuntimeDebugger2();
|
|
48351
48378
|
init_ScaledDiagram();
|
|
48352
48379
|
init_ScoreDisplay();
|
|
@@ -48509,6 +48536,8 @@ var init_component_registry_generated = __esm({
|
|
|
48509
48536
|
"DocSearch": DocSearch,
|
|
48510
48537
|
"DocSidebar": DocSidebar,
|
|
48511
48538
|
"DocTOC": DocTOC,
|
|
48539
|
+
"DocumentDetails": DocumentDetails,
|
|
48540
|
+
"DocumentPanel": DocumentPanel,
|
|
48512
48541
|
"DocumentViewer": DocumentViewer,
|
|
48513
48542
|
"DrawFxLayer": DrawFxLayer,
|
|
48514
48543
|
"DrawGroup": DrawGroup,
|
|
@@ -48620,7 +48649,7 @@ var init_component_registry_generated = __esm({
|
|
|
48620
48649
|
"RelationSelect": RelationSelect,
|
|
48621
48650
|
"RepeatableFormSection": RepeatableFormSection,
|
|
48622
48651
|
"ReplyTree": ReplyTree,
|
|
48623
|
-
"
|
|
48652
|
+
"RichTextEditor": RichTextEditor,
|
|
48624
48653
|
"RuntimeDebugger": RuntimeDebugger,
|
|
48625
48654
|
"ScaledDiagram": ScaledDiagram,
|
|
48626
48655
|
"ScoreDisplay": ScoreDisplay,
|
|
@@ -48774,10 +48803,10 @@ function enrichFormFields(fields, entityDef) {
|
|
|
48774
48803
|
}
|
|
48775
48804
|
if (field && typeof field === "object" && !Array.isArray(field) && !React85__namespace.default.isValidElement(field) && !(field instanceof Date)) {
|
|
48776
48805
|
const obj = field;
|
|
48777
|
-
const
|
|
48778
|
-
if (!
|
|
48806
|
+
const fieldName2 = typeof obj.name === "string" ? obj.name : typeof obj.field === "string" ? obj.field : void 0;
|
|
48807
|
+
if (!fieldName2) return field;
|
|
48779
48808
|
if (obj.type || obj.inputType) return field;
|
|
48780
|
-
const entityField = fieldMap.get(
|
|
48809
|
+
const entityField = fieldMap.get(fieldName2);
|
|
48781
48810
|
if (!entityField) return field;
|
|
48782
48811
|
const enriched = { ...obj, type: entityField.type };
|
|
48783
48812
|
if (entityField.required && !("required" in obj)) {
|
|
@@ -48827,9 +48856,9 @@ function enrichDetailFields(fields, entityDef) {
|
|
|
48827
48856
|
}
|
|
48828
48857
|
if (field && typeof field === "object" && !Array.isArray(field) && !React85__namespace.default.isValidElement(field) && !(field instanceof Date)) {
|
|
48829
48858
|
const obj = field;
|
|
48830
|
-
const
|
|
48831
|
-
if (!
|
|
48832
|
-
const meta = metaFor(
|
|
48859
|
+
const fieldName2 = typeof obj.key === "string" ? obj.key : typeof obj.name === "string" ? obj.name : void 0;
|
|
48860
|
+
if (!fieldName2 || obj.type) return field;
|
|
48861
|
+
const meta = metaFor(fieldName2);
|
|
48833
48862
|
return meta ? { ...obj, ...meta } : field;
|
|
48834
48863
|
}
|
|
48835
48864
|
return field;
|
|
@@ -49668,9 +49697,9 @@ function UISlotRenderer({
|
|
|
49668
49697
|
"ui-slot-renderer relative min-h-full",
|
|
49669
49698
|
className
|
|
49670
49699
|
), children: [
|
|
49671
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex min-h-full", children: [
|
|
49672
|
-
/* @__PURE__ */ jsxRuntime.jsx(UISlotComponent, { slot: "sidebar", className: "ui-slot-sidebar min-w-0 shrink-0" }),
|
|
49673
|
-
/* @__PURE__ */ jsxRuntime.jsx(UISlotComponent, { slot: "main", className: "ui-slot-main flex-1 min-h-[200px]" })
|
|
49700
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex min-h-full flex-col lg:flex-row", children: [
|
|
49701
|
+
/* @__PURE__ */ jsxRuntime.jsx(UISlotComponent, { slot: "sidebar", className: "ui-slot-sidebar min-w-0 lg:shrink-0" }),
|
|
49702
|
+
/* @__PURE__ */ jsxRuntime.jsx(UISlotComponent, { slot: "main", className: "ui-slot-main flex-1 min-w-0 min-h-[200px]" })
|
|
49674
49703
|
] }),
|
|
49675
49704
|
/* @__PURE__ */ jsxRuntime.jsx(UISlotComponent, { slot: "modal", portal: true }),
|
|
49676
49705
|
/* @__PURE__ */ jsxRuntime.jsx(UISlotComponent, { slot: "drawer", portal: true }),
|
|
@@ -50193,7 +50222,7 @@ function buildTraitRenderConfig(binding, traitConfigsByName, payload) {
|
|
|
50193
50222
|
}
|
|
50194
50223
|
return void 0;
|
|
50195
50224
|
}
|
|
50196
|
-
function createSharedEntityWriter(binding, tick, traitStatesRef, emit) {
|
|
50225
|
+
function createSharedEntityWriter(binding, tick, traitStatesRef, emit, traitConfigsByName) {
|
|
50197
50226
|
return (scratch) => {
|
|
50198
50227
|
const traitName = binding.trait.name;
|
|
50199
50228
|
const currentState = traitStatesRef.current.get(traitName)?.currentState ?? "";
|
|
@@ -50202,9 +50231,13 @@ function createSharedEntityWriter(binding, tick, traitStatesRef, emit) {
|
|
|
50202
50231
|
const writes = [];
|
|
50203
50232
|
const ctx = evaluator.createMinimalContext(scratchEntity, {}, currentState);
|
|
50204
50233
|
const declaredDefaults = runtime.collectDeclaredConfigDefaults(binding.trait);
|
|
50205
|
-
const
|
|
50206
|
-
|
|
50207
|
-
|
|
50234
|
+
const resolvedByName = traitConfigsByName?.[traitName];
|
|
50235
|
+
const callSiteRaw = getBindingConfig(binding);
|
|
50236
|
+
const callSiteConfig = callSiteRaw ? Object.fromEntries(
|
|
50237
|
+
Object.entries(callSiteRaw).filter(([, v]) => !containsConfigForward(v))
|
|
50238
|
+
) : void 0;
|
|
50239
|
+
if (declaredDefaults || resolvedByName || callSiteConfig) {
|
|
50240
|
+
ctx.config = { ...declaredDefaults ?? {}, ...resolvedByName ?? {}, ...callSiteConfig ?? {} };
|
|
50208
50241
|
}
|
|
50209
50242
|
ctx.mutateEntity = (changes) => {
|
|
50210
50243
|
for (const [field, value] of Object.entries(changes)) {
|
|
@@ -50807,7 +50840,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
50807
50840
|
const interval = entries[0].tick.interval;
|
|
50808
50841
|
const onDue = timedTick(`tick:shared:${group.storeKey}@${String(interval)}`, () => {
|
|
50809
50842
|
const writers = entries.map(
|
|
50810
|
-
({ binding, tick }) => createSharedEntityWriter(binding, tick, traitStatesRef, emitFromSharedWriter)
|
|
50843
|
+
({ binding, tick }) => createSharedEntityWriter(binding, tick, traitStatesRef, emitFromSharedWriter, traitConfigsByName)
|
|
50811
50844
|
);
|
|
50812
50845
|
runTickFrame(group.storeKey, writers, sharedEntityStore);
|
|
50813
50846
|
});
|