@almadar/ui 5.160.0 → 5.162.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/avl/index.cjs +924 -891
- package/dist/avl/index.js +914 -881
- package/dist/components/index.cjs +944 -934
- package/dist/components/index.d.cts +156 -31
- package/dist/components/index.d.ts +156 -31
- package/dist/components/index.js +931 -924
- package/dist/hooks/index.cjs +25 -49
- package/dist/hooks/index.js +25 -49
- package/dist/locales/index.cjs +75 -147
- package/dist/locales/index.js +75 -147
- package/dist/providers/index.cjs +911 -882
- package/dist/providers/index.js +901 -872
- package/dist/runtime/index.cjs +920 -887
- package/dist/runtime/index.js +910 -877
- package/locales/ar.json +25 -49
- package/locales/en.json +25 -49
- package/locales/sl.json +25 -49
- package/package.json +5 -5
package/dist/runtime/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import { mergeEntityFrame, isCircuitEvent, applyListenPayloadMapping, schemaToIR
|
|
|
10
10
|
import { clsx } from 'clsx';
|
|
11
11
|
import { twMerge } from 'tailwind-merge';
|
|
12
12
|
import * as LucideIcons2 from 'lucide-react';
|
|
13
|
-
import { Loader2, X, Lightbulb, CheckCircle, List, Printer, ChevronRight, ChevronLeft, GitBranch, Pencil, Eye, Plus, ArrowRight, Trash, Code, FileText, WrapText, Check, Copy, RotateCcw, Play, Terminal, XCircle, AlertTriangle, Trash2, Link2, ArrowLeft, ZoomOut, ZoomIn, Download, DollarSign, ChevronDown, Menu as Menu$1, Package, Calendar, MoreHorizontal, Image as Image$1, Upload, HelpCircle, PauseCircle, Search,
|
|
13
|
+
import { Loader2, X, Lightbulb, CheckCircle, List, Printer, ChevronRight, ChevronLeft, GitBranch, Pencil, Eye, Plus, ArrowRight, Trash, Code, FileText, WrapText, Check, Copy, RotateCcw, Play, Terminal, XCircle, AlertTriangle, Trash2, Link2, ArrowLeft, ZoomOut, ZoomIn, Download, DollarSign, ChevronDown, Menu as Menu$1, Package, Calendar, MoreHorizontal, Image as Image$1, Upload, HelpCircle, PauseCircle, Search, Bold, Italic, Underline, Strikethrough, Heading1, Heading2, Heading3, ListOrdered, Quote, Link as Link$1, Minus, Eraser, TrendingUp, TrendingDown, AlertCircle, Circle, Clock, CheckCircle2, ChevronUp } from 'lucide-react';
|
|
14
14
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
15
15
|
import { createPortal } from 'react-dom';
|
|
16
16
|
import { useTranslate } from '@almadar/ui/hooks';
|
|
@@ -25829,8 +25829,9 @@ function DataGrid({
|
|
|
25829
25829
|
const titleField = fieldDefs.find((f3) => f3.variant === "h3" || f3.variant === "h4") ?? fieldDefs[0];
|
|
25830
25830
|
const badgeFields = fieldDefs.filter((f3) => f3.variant === "badge" && f3 !== titleField);
|
|
25831
25831
|
const bodyFields = fieldDefs.filter((f3) => f3 !== titleField && !badgeFields.includes(f3));
|
|
25832
|
-
const
|
|
25833
|
-
const
|
|
25832
|
+
const inlineCap = Math.min(maxInlineActions ?? 1, 1);
|
|
25833
|
+
const inlineCardActions = actionDefs.filter((a) => a.variant === "primary").slice(0, inlineCap);
|
|
25834
|
+
const menuCardActions = actionDefs.filter((a) => !inlineCardActions.includes(a));
|
|
25834
25835
|
const fireAction = (action, itemData) => {
|
|
25835
25836
|
if (action.navigatesTo) {
|
|
25836
25837
|
const url = action.navigatesTo.replace(
|
|
@@ -25850,6 +25851,9 @@ function DataGrid({
|
|
|
25850
25851
|
e.stopPropagation();
|
|
25851
25852
|
fireAction(action, itemData);
|
|
25852
25853
|
};
|
|
25854
|
+
const cardClickAction = actionDefs.find((a) => a.variant !== "danger");
|
|
25855
|
+
const handleCardClick = cardClickAction ? (itemData) => () => fireAction(cardClickAction, itemData) : void 0;
|
|
25856
|
+
const stopCardClick = handleCardClick ? (e) => e.stopPropagation() : void 0;
|
|
25853
25857
|
const hasRenderProp = typeof children === "function";
|
|
25854
25858
|
useEffect(() => {
|
|
25855
25859
|
if (data.length > 0 && !hasRenderProp && fieldDefs.length === 0) {
|
|
@@ -25921,55 +25925,40 @@ function DataGrid({
|
|
|
25921
25925
|
{
|
|
25922
25926
|
"data-entity-row": true,
|
|
25923
25927
|
"data-entity-id": id,
|
|
25924
|
-
|
|
25928
|
+
onClick: handleCardClick?.(itemData),
|
|
25929
|
+
className: cn("relative group/rowactions", handleCardClick && "cursor-pointer", isSelected && "ring-2 ring-primary rounded-lg"),
|
|
25925
25930
|
children: [
|
|
25926
25931
|
children(itemData, index),
|
|
25927
|
-
actionDefs.length > 0 && /* @__PURE__ */
|
|
25928
|
-
/* @__PURE__ */ jsxs(
|
|
25929
|
-
|
|
25930
|
-
|
|
25931
|
-
|
|
25932
|
-
|
|
25933
|
-
|
|
25934
|
-
|
|
25935
|
-
|
|
25936
|
-
|
|
25937
|
-
|
|
25938
|
-
|
|
25939
|
-
|
|
25940
|
-
|
|
25941
|
-
|
|
25942
|
-
|
|
25943
|
-
|
|
25944
|
-
)),
|
|
25945
|
-
maxInlineActions != null && actionDefs.length > maxInlineActions && /* @__PURE__ */ jsx(
|
|
25946
|
-
Menu,
|
|
25947
|
-
{
|
|
25948
|
-
position: "bottom-end",
|
|
25949
|
-
trigger: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
|
|
25950
|
-
items: actionDefs.slice(maxInlineActions).map((action) => ({
|
|
25951
|
-
label: action.label,
|
|
25952
|
-
icon: action.icon,
|
|
25953
|
-
event: action.event,
|
|
25954
|
-
onClick: () => fireAction(action, itemData)
|
|
25955
|
-
}))
|
|
25956
|
-
}
|
|
25957
|
-
)
|
|
25958
|
-
] }),
|
|
25959
|
-
/* @__PURE__ */ 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__ */ jsx(
|
|
25932
|
+
actionDefs.length > 0 && /* @__PURE__ */ 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__ */ jsxs(HStack, { gap: "xs", className: "rounded-md border border-border bg-card/95 backdrop-blur-sm shadow-sm p-0.5", children: [
|
|
25933
|
+
inlineCardActions.map((action, idx) => /* @__PURE__ */ jsxs(
|
|
25934
|
+
Button,
|
|
25935
|
+
{
|
|
25936
|
+
variant: "primary",
|
|
25937
|
+
size: "sm",
|
|
25938
|
+
onClick: handleActionClick(action, itemData),
|
|
25939
|
+
"data-testid": `action-${action.event}`,
|
|
25940
|
+
"data-row-id": String(itemData.id),
|
|
25941
|
+
children: [
|
|
25942
|
+
action.icon && renderIconInput(action.icon, { size: "xs", className: "mr-1" }),
|
|
25943
|
+
action.label
|
|
25944
|
+
]
|
|
25945
|
+
},
|
|
25946
|
+
idx
|
|
25947
|
+
)),
|
|
25948
|
+
menuCardActions.length > 0 && /* @__PURE__ */ jsx(
|
|
25960
25949
|
Menu,
|
|
25961
25950
|
{
|
|
25962
25951
|
position: "bottom-end",
|
|
25963
25952
|
trigger: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
|
|
25964
|
-
items:
|
|
25953
|
+
items: menuCardActions.map((action) => ({
|
|
25965
25954
|
label: action.label,
|
|
25966
25955
|
icon: action.icon,
|
|
25967
|
-
|
|
25956
|
+
variant: action.variant === "danger" ? "danger" : "default",
|
|
25968
25957
|
onClick: () => fireAction(action, itemData)
|
|
25969
25958
|
}))
|
|
25970
25959
|
}
|
|
25971
|
-
)
|
|
25972
|
-
] })
|
|
25960
|
+
)
|
|
25961
|
+
] }) })
|
|
25973
25962
|
]
|
|
25974
25963
|
},
|
|
25975
25964
|
id
|
|
@@ -25983,12 +25972,14 @@ function DataGrid({
|
|
|
25983
25972
|
{
|
|
25984
25973
|
"data-entity-row": true,
|
|
25985
25974
|
"data-entity-id": id,
|
|
25975
|
+
onClick: handleCardClick?.(itemData),
|
|
25986
25976
|
className: cn(
|
|
25987
25977
|
"bg-card rounded-lg",
|
|
25988
25978
|
"border border-border",
|
|
25989
25979
|
"shadow-elevation-card hover:shadow-elevation-dialog",
|
|
25990
25980
|
"hover:border-primary transition-all",
|
|
25991
25981
|
"flex flex-col",
|
|
25982
|
+
handleCardClick && "cursor-pointer",
|
|
25992
25983
|
isSelected && "ring-2 ring-primary border-primary"
|
|
25993
25984
|
),
|
|
25994
25985
|
children: [
|
|
@@ -26038,53 +26029,35 @@ function DataGrid({
|
|
|
26038
26029
|
] }, field.name);
|
|
26039
26030
|
}) })
|
|
26040
26031
|
] }),
|
|
26041
|
-
|
|
26042
|
-
|
|
26032
|
+
actionDefs.length > 0 && /* @__PURE__ */ jsxs(HStack, { gap: "xs", onClick: stopCardClick, className: "flex-shrink-0", children: [
|
|
26033
|
+
inlineCardActions.map((action, idx) => /* @__PURE__ */ jsx(
|
|
26043
26034
|
Button,
|
|
26044
26035
|
{
|
|
26045
|
-
variant:
|
|
26036
|
+
variant: "primary",
|
|
26046
26037
|
size: "sm",
|
|
26047
26038
|
onClick: handleActionClick(action, itemData),
|
|
26048
26039
|
"data-testid": `action-${action.event}`,
|
|
26049
26040
|
"data-row-id": String(itemData.id),
|
|
26050
26041
|
"aria-label": action.label,
|
|
26051
26042
|
title: action.label,
|
|
26052
|
-
className: cn(
|
|
26053
|
-
action.variant === "primary" ? void 0 : "text-muted-foreground hover:text-foreground",
|
|
26054
|
-
action.icon && "px-2"
|
|
26055
|
-
),
|
|
26043
|
+
className: cn(action.icon && "px-2"),
|
|
26056
26044
|
children: action.icon ? renderIconInput(action.icon, { size: "xs" }) : action.label
|
|
26057
26045
|
},
|
|
26058
26046
|
idx
|
|
26059
26047
|
)),
|
|
26060
|
-
|
|
26048
|
+
menuCardActions.length > 0 && /* @__PURE__ */ jsx(
|
|
26061
26049
|
Menu,
|
|
26062
26050
|
{
|
|
26063
26051
|
position: "bottom-end",
|
|
26064
26052
|
trigger: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
|
|
26065
|
-
items:
|
|
26053
|
+
items: menuCardActions.map((action) => ({
|
|
26066
26054
|
label: action.label,
|
|
26067
26055
|
icon: action.icon,
|
|
26068
|
-
|
|
26056
|
+
variant: action.variant === "danger" ? "danger" : "default",
|
|
26069
26057
|
onClick: () => fireAction(action, itemData)
|
|
26070
26058
|
}))
|
|
26071
26059
|
}
|
|
26072
|
-
)
|
|
26073
|
-
dangerActions.map((action, idx) => /* @__PURE__ */ jsx(
|
|
26074
|
-
Button,
|
|
26075
|
-
{
|
|
26076
|
-
variant: "ghost",
|
|
26077
|
-
size: "sm",
|
|
26078
|
-
onClick: handleActionClick(action, itemData),
|
|
26079
|
-
"data-testid": `action-${action.event}`,
|
|
26080
|
-
"data-row-id": String(itemData.id),
|
|
26081
|
-
"aria-label": action.label,
|
|
26082
|
-
title: action.label,
|
|
26083
|
-
className: "text-error hover:text-error hover:bg-error/10 px-2",
|
|
26084
|
-
children: action.icon ? renderIconInput(action.icon, { size: "xs" }) : action.label
|
|
26085
|
-
},
|
|
26086
|
-
`danger-${idx}`
|
|
26087
|
-
))
|
|
26060
|
+
)
|
|
26088
26061
|
] })
|
|
26089
26062
|
] }) }),
|
|
26090
26063
|
bodyFields.length > 0 && /* @__PURE__ */ jsx(Box, { className: "px-4 pt-2 pb-4 flex-1", children: /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
|
|
@@ -26326,54 +26299,59 @@ function DataList({
|
|
|
26326
26299
|
if (!itemActions || itemActions.length === 0) return null;
|
|
26327
26300
|
const inline = maxInlineActions != null ? itemActions.slice(0, maxInlineActions) : itemActions;
|
|
26328
26301
|
const overflow = maxInlineActions != null ? itemActions.slice(maxInlineActions) : [];
|
|
26329
|
-
return
|
|
26330
|
-
|
|
26331
|
-
|
|
26332
|
-
|
|
26333
|
-
|
|
26334
|
-
|
|
26335
|
-
|
|
26336
|
-
|
|
26337
|
-
|
|
26338
|
-
|
|
26339
|
-
action
|
|
26340
|
-
|
|
26341
|
-
|
|
26342
|
-
|
|
26343
|
-
|
|
26344
|
-
|
|
26345
|
-
|
|
26346
|
-
|
|
26347
|
-
|
|
26348
|
-
|
|
26349
|
-
|
|
26350
|
-
|
|
26351
|
-
|
|
26352
|
-
|
|
26353
|
-
|
|
26354
|
-
|
|
26355
|
-
|
|
26356
|
-
|
|
26357
|
-
|
|
26358
|
-
|
|
26359
|
-
|
|
26360
|
-
|
|
26361
|
-
|
|
26362
|
-
|
|
26363
|
-
|
|
26364
|
-
|
|
26365
|
-
|
|
26366
|
-
|
|
26367
|
-
|
|
26368
|
-
|
|
26302
|
+
return (
|
|
26303
|
+
// stopPropagation at the cluster: the "⋯" trigger opens the overflow
|
|
26304
|
+
// menu without also firing the row's default click (inline buttons
|
|
26305
|
+
// already stop it in handleActionClick; the Menu panel is portaled).
|
|
26306
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "xs", onClick: rowClickEvent ? (e) => e.stopPropagation() : void 0, className: "flex-shrink-0", children: [
|
|
26307
|
+
inline.map((action, idx) => /* @__PURE__ */ jsxs(
|
|
26308
|
+
Button,
|
|
26309
|
+
{
|
|
26310
|
+
variant: action.variant ?? "ghost",
|
|
26311
|
+
size: "sm",
|
|
26312
|
+
onClick: handleActionClick(action, itemData),
|
|
26313
|
+
"data-testid": `action-${action.event}`,
|
|
26314
|
+
"data-row-id": String(itemData.id),
|
|
26315
|
+
className: cn(
|
|
26316
|
+
action.variant === "danger" && "text-error hover:bg-error/10",
|
|
26317
|
+
// Must sit on the Button itself: the variant's own text colour
|
|
26318
|
+
// beats an inherited one from the row wrapper.
|
|
26319
|
+
onPrimary && "!text-primary-foreground hover:bg-primary-foreground/15"
|
|
26320
|
+
),
|
|
26321
|
+
children: [
|
|
26322
|
+
action.icon && renderIconInput2(action.icon, { size: "xs", className: "mr-1" }),
|
|
26323
|
+
action.label
|
|
26324
|
+
]
|
|
26325
|
+
},
|
|
26326
|
+
idx
|
|
26327
|
+
)),
|
|
26328
|
+
overflow.length > 0 && /* @__PURE__ */ jsx(
|
|
26329
|
+
Menu,
|
|
26330
|
+
{
|
|
26331
|
+
position: "bottom-end",
|
|
26332
|
+
trigger: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
|
|
26333
|
+
items: overflow.map((action) => ({
|
|
26334
|
+
label: action.label,
|
|
26335
|
+
icon: action.icon,
|
|
26336
|
+
variant: action.variant === "danger" ? "danger" : "default",
|
|
26337
|
+
onClick: () => eventBus.emit(`UI:${action.event}`, {
|
|
26338
|
+
id: itemData.id,
|
|
26339
|
+
row: itemData
|
|
26340
|
+
})
|
|
26341
|
+
}))
|
|
26342
|
+
}
|
|
26343
|
+
)
|
|
26344
|
+
] })
|
|
26345
|
+
);
|
|
26369
26346
|
};
|
|
26347
|
+
const rowClickEvent = itemClickEvent || itemActions?.find((a) => a.variant !== "danger")?.event;
|
|
26370
26348
|
const handleRowClick = (itemData) => () => {
|
|
26371
|
-
if (!
|
|
26349
|
+
if (!rowClickEvent) return;
|
|
26372
26350
|
const payload = {
|
|
26373
26351
|
id: itemData.id,
|
|
26374
26352
|
row: itemData
|
|
26375
26353
|
};
|
|
26376
|
-
eventBus.emit(`UI:${
|
|
26354
|
+
eventBus.emit(`UI:${rowClickEvent}`, payload);
|
|
26377
26355
|
};
|
|
26378
26356
|
if (isLoading) {
|
|
26379
26357
|
return /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: t("loading.items") }) });
|
|
@@ -26421,10 +26399,10 @@ function DataList({
|
|
|
26421
26399
|
{
|
|
26422
26400
|
"data-entity-row": true,
|
|
26423
26401
|
"data-entity-id": id,
|
|
26424
|
-
onClick:
|
|
26402
|
+
onClick: rowClickEvent ? handleRowClick(itemData) : void 0,
|
|
26425
26403
|
className: cn(
|
|
26426
26404
|
"flex px-4 group/rowactions",
|
|
26427
|
-
|
|
26405
|
+
rowClickEvent && "cursor-pointer",
|
|
26428
26406
|
isSent ? "justify-end" : "justify-start"
|
|
26429
26407
|
),
|
|
26430
26408
|
children: /* @__PURE__ */ jsxs(
|
|
@@ -26500,7 +26478,7 @@ function DataList({
|
|
|
26500
26478
|
const id2 = itemData.id || String(index);
|
|
26501
26479
|
const actions = renderItemActions(itemData);
|
|
26502
26480
|
return wrapDnd(
|
|
26503
|
-
/* @__PURE__ */ jsxs(Box, { "data-entity-row": true, "data-entity-id": id2, onClick:
|
|
26481
|
+
/* @__PURE__ */ 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: [
|
|
26504
26482
|
children(itemData, index),
|
|
26505
26483
|
actions && /* @__PURE__ */ 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: [
|
|
26506
26484
|
/* @__PURE__ */ 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 }),
|
|
@@ -26512,7 +26490,6 @@ function DataList({
|
|
|
26512
26490
|
items: (itemActions ?? []).map((action) => ({
|
|
26513
26491
|
label: action.label,
|
|
26514
26492
|
icon: action.icon,
|
|
26515
|
-
event: action.event,
|
|
26516
26493
|
variant: action.variant === "danger" ? "danger" : "default",
|
|
26517
26494
|
onClick: () => eventBus.emit(`UI:${action.event}`, {
|
|
26518
26495
|
id: itemData.id,
|
|
@@ -26529,7 +26506,7 @@ function DataList({
|
|
|
26529
26506
|
const id = itemData.id || String(index);
|
|
26530
26507
|
const titleValue = getNestedValue(itemData, titleField?.name ?? "");
|
|
26531
26508
|
return wrapDnd(
|
|
26532
|
-
/* @__PURE__ */ jsxs(Box, { "data-entity-row": true, "data-entity-id": id, onClick:
|
|
26509
|
+
/* @__PURE__ */ jsxs(Box, { "data-entity-row": true, "data-entity-id": id, onClick: rowClickEvent ? handleRowClick(itemData) : void 0, className: cn(rowClickEvent && "cursor-pointer"), children: [
|
|
26533
26510
|
/* @__PURE__ */ jsxs(
|
|
26534
26511
|
Box,
|
|
26535
26512
|
{
|
|
@@ -31741,7 +31718,7 @@ var init_MapView = __esm({
|
|
|
31741
31718
|
shadowSize: [41, 41]
|
|
31742
31719
|
});
|
|
31743
31720
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
31744
|
-
const { useEffect: useEffect63, useRef: useRef63, useCallback: useCallback90, useState:
|
|
31721
|
+
const { useEffect: useEffect63, useRef: useRef63, useCallback: useCallback90, useState: useState94 } = React85__default;
|
|
31745
31722
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
31746
31723
|
const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
31747
31724
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
@@ -31786,7 +31763,7 @@ var init_MapView = __esm({
|
|
|
31786
31763
|
showAttribution = true
|
|
31787
31764
|
}) {
|
|
31788
31765
|
const eventBus = useEventBus2();
|
|
31789
|
-
const [clickedPosition, setClickedPosition] =
|
|
31766
|
+
const [clickedPosition, setClickedPosition] = useState94(null);
|
|
31790
31767
|
const handleMapClick = useCallback90((lat, lng) => {
|
|
31791
31768
|
if (showClickedPin) {
|
|
31792
31769
|
setClickedPosition({ lat, lng });
|
|
@@ -36010,741 +35987,775 @@ var init_PositionedCanvas = __esm({
|
|
|
36010
35987
|
PositionedCanvas.displayName = "PositionedCanvas";
|
|
36011
35988
|
}
|
|
36012
35989
|
});
|
|
36013
|
-
function
|
|
36014
|
-
|
|
36015
|
-
|
|
36016
|
-
|
|
36017
|
-
|
|
36018
|
-
|
|
36019
|
-
if (!Array.isArray(raw) || raw.length === 0) return [createBlock("paragraph")];
|
|
36020
|
-
return raw.map((row) => {
|
|
36021
|
-
const entity = row;
|
|
36022
|
-
const rawType = entity.type;
|
|
36023
|
-
const type = typeof rawType === "string" && BLOCK_TYPES.has(rawType) ? rawType : "paragraph";
|
|
36024
|
-
const id = typeof entity.id === "string" && entity.id ? entity.id : nextBlockId(type);
|
|
36025
|
-
return { ...entity, id, type };
|
|
36026
|
-
});
|
|
36027
|
-
}
|
|
36028
|
-
function createBlock(type) {
|
|
36029
|
-
switch (type) {
|
|
36030
|
-
case "bullet-list":
|
|
36031
|
-
case "numbered-list":
|
|
36032
|
-
return {
|
|
36033
|
-
id: nextBlockId(type),
|
|
36034
|
-
type,
|
|
36035
|
-
children: [
|
|
36036
|
-
{ id: nextBlockId("li"), type: "paragraph", content: "" }
|
|
36037
|
-
]
|
|
36038
|
-
};
|
|
36039
|
-
case "image":
|
|
36040
|
-
return {
|
|
36041
|
-
id: nextBlockId(type),
|
|
36042
|
-
type,
|
|
36043
|
-
content: "",
|
|
36044
|
-
metadata: { url: "", caption: "" }
|
|
36045
|
-
};
|
|
36046
|
-
case "code":
|
|
36047
|
-
return {
|
|
36048
|
-
id: nextBlockId(type),
|
|
36049
|
-
type,
|
|
36050
|
-
content: "",
|
|
36051
|
-
metadata: { language: "plaintext" }
|
|
36052
|
-
};
|
|
36053
|
-
case "divider":
|
|
36054
|
-
return { id: nextBlockId(type), type };
|
|
36055
|
-
default:
|
|
36056
|
-
return { id: nextBlockId(type), type, content: "" };
|
|
35990
|
+
function safeUrl(raw, kinds) {
|
|
35991
|
+
const url = raw.trim();
|
|
35992
|
+
if (url.startsWith("#") || url.startsWith("/")) return url;
|
|
35993
|
+
const lower = url.toLowerCase();
|
|
35994
|
+
for (const kind of kinds) {
|
|
35995
|
+
if (lower.startsWith(kind)) return url;
|
|
36057
35996
|
}
|
|
35997
|
+
return null;
|
|
36058
35998
|
}
|
|
36059
|
-
function
|
|
36060
|
-
|
|
36061
|
-
|
|
36062
|
-
|
|
36063
|
-
|
|
36064
|
-
|
|
36065
|
-
|
|
36066
|
-
return {
|
|
36067
|
-
...block,
|
|
36068
|
-
id: nextBlockId(block.type),
|
|
36069
|
-
children: block.children?.map((child) => ({
|
|
36070
|
-
...child,
|
|
36071
|
-
id: nextBlockId("li")
|
|
36072
|
-
})),
|
|
36073
|
-
metadata: block.metadata ? { ...block.metadata } : void 0
|
|
36074
|
-
};
|
|
36075
|
-
}
|
|
36076
|
-
function insertAfter(blocks, targetId, inserted) {
|
|
36077
|
-
const idx = blocks.findIndex((b) => b.id === targetId);
|
|
36078
|
-
if (idx === -1) return [...blocks, inserted];
|
|
36079
|
-
const next = blocks.slice();
|
|
36080
|
-
next.splice(idx + 1, 0, inserted);
|
|
36081
|
-
return next;
|
|
36082
|
-
}
|
|
36083
|
-
function changeBlockType(block, type) {
|
|
36084
|
-
if (block.type === type) return block;
|
|
36085
|
-
if (type === "bullet-list" || type === "numbered-list") {
|
|
36086
|
-
if (block.children && block.children.length > 0) {
|
|
36087
|
-
return { ...block, type };
|
|
35999
|
+
function sanitizeNode(el) {
|
|
36000
|
+
const children = Array.from(el.children);
|
|
36001
|
+
for (const child of children) {
|
|
36002
|
+
const tag = child.tagName.toLowerCase();
|
|
36003
|
+
if (DROP_TAGS.has(tag)) {
|
|
36004
|
+
child.remove();
|
|
36005
|
+
continue;
|
|
36088
36006
|
}
|
|
36089
|
-
const
|
|
36090
|
-
|
|
36091
|
-
|
|
36092
|
-
|
|
36093
|
-
|
|
36094
|
-
|
|
36095
|
-
|
|
36096
|
-
|
|
36097
|
-
|
|
36098
|
-
|
|
36099
|
-
|
|
36007
|
+
const allowed = ALLOWED_ATTRS[tag];
|
|
36008
|
+
if (!allowed) {
|
|
36009
|
+
sanitizeNode(child);
|
|
36010
|
+
child.replaceWith(...Array.from(child.childNodes));
|
|
36011
|
+
continue;
|
|
36012
|
+
}
|
|
36013
|
+
for (const attr of Array.from(child.attributes)) {
|
|
36014
|
+
const name = attr.name.toLowerCase();
|
|
36015
|
+
if (!allowed.includes(name)) {
|
|
36016
|
+
child.removeAttribute(attr.name);
|
|
36017
|
+
continue;
|
|
36018
|
+
}
|
|
36019
|
+
if (name === "href") {
|
|
36020
|
+
const url = safeUrl(attr.value, ["http://", "https://", "mailto:"]);
|
|
36021
|
+
if (url === null) child.removeAttribute(attr.name);
|
|
36022
|
+
else child.setAttribute("href", url);
|
|
36023
|
+
}
|
|
36024
|
+
if (name === "src") {
|
|
36025
|
+
const url = safeUrl(attr.value, ["http://", "https://", "data:image/"]);
|
|
36026
|
+
if (url === null) child.remove();
|
|
36027
|
+
else child.setAttribute("src", url);
|
|
36028
|
+
}
|
|
36029
|
+
}
|
|
36030
|
+
sanitizeNode(child);
|
|
36100
36031
|
}
|
|
36101
|
-
|
|
36102
|
-
|
|
36103
|
-
|
|
36104
|
-
|
|
36105
|
-
|
|
36106
|
-
|
|
36107
|
-
|
|
36032
|
+
}
|
|
36033
|
+
function sanitizeRichHtml(html) {
|
|
36034
|
+
if (!html || typeof html !== "string") return "";
|
|
36035
|
+
if (typeof window === "undefined" || typeof DOMParser === "undefined") return "";
|
|
36036
|
+
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
36037
|
+
sanitizeNode(doc.body);
|
|
36038
|
+
if (!doc.body.querySelector("img, hr") && (doc.body.textContent ?? "").trim().length === 0) {
|
|
36039
|
+
return "";
|
|
36108
36040
|
}
|
|
36109
|
-
|
|
36110
|
-
|
|
36111
|
-
|
|
36112
|
-
|
|
36113
|
-
|
|
36114
|
-
|
|
36115
|
-
|
|
36041
|
+
return doc.body.innerHTML.replace(//g, "");
|
|
36042
|
+
}
|
|
36043
|
+
function htmlIsEmpty(el) {
|
|
36044
|
+
if (el.querySelector("img, hr")) return false;
|
|
36045
|
+
return (el.textContent ?? "").trim().length === 0;
|
|
36046
|
+
}
|
|
36047
|
+
function closestBlock(node, root) {
|
|
36048
|
+
let cur = node;
|
|
36049
|
+
while (cur && cur !== root) {
|
|
36050
|
+
if (cur instanceof Element && BLOCK_TAGS.has(cur.tagName)) return cur;
|
|
36051
|
+
cur = cur.parentNode;
|
|
36116
36052
|
}
|
|
36117
|
-
|
|
36118
|
-
return { id: block.id, type, content: seed };
|
|
36053
|
+
return null;
|
|
36119
36054
|
}
|
|
36120
|
-
function
|
|
36121
|
-
|
|
36122
|
-
|
|
36123
|
-
|
|
36124
|
-
|
|
36125
|
-
|
|
36126
|
-
|
|
36127
|
-
|
|
36128
|
-
|
|
36055
|
+
function RichTextStyles() {
|
|
36056
|
+
return /* @__PURE__ */ jsx("style", { children: RICH_TEXT_CSS });
|
|
36057
|
+
}
|
|
36058
|
+
function readToolbarState(root) {
|
|
36059
|
+
const sel = window.getSelection();
|
|
36060
|
+
if (!sel || sel.rangeCount === 0) return IDLE_TOOLBAR;
|
|
36061
|
+
const anchor = sel.anchorNode;
|
|
36062
|
+
if (!anchor || !root.contains(anchor)) return IDLE_TOOLBAR;
|
|
36063
|
+
let block = null;
|
|
36064
|
+
let link = false;
|
|
36065
|
+
let node = anchor;
|
|
36066
|
+
while (node && node !== root) {
|
|
36067
|
+
if (node instanceof Element) {
|
|
36068
|
+
const tag = node.tagName.toLowerCase();
|
|
36069
|
+
if (!block && (tag === "h1" || tag === "h2" || tag === "h3" || tag === "blockquote" || tag === "pre")) {
|
|
36070
|
+
block = tag;
|
|
36129
36071
|
}
|
|
36072
|
+
if (tag === "a") link = true;
|
|
36130
36073
|
}
|
|
36131
|
-
|
|
36132
|
-
|
|
36133
|
-
|
|
36134
|
-
|
|
36135
|
-
|
|
36136
|
-
|
|
36137
|
-
|
|
36138
|
-
|
|
36139
|
-
|
|
36140
|
-
|
|
36141
|
-
|
|
36142
|
-
|
|
36143
|
-
"inline-flex items-center justify-center",
|
|
36144
|
-
"h-6 w-6 rounded-sm p-0 gap-0",
|
|
36145
|
-
"text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
36146
|
-
"opacity-0 group-hover:opacity-100 focus-visible:opacity-100",
|
|
36147
|
-
"transition-opacity"
|
|
36148
|
-
),
|
|
36149
|
-
onClick: () => setOpen((v) => !v),
|
|
36150
|
-
children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", className: "w-3.5 h-3.5" })
|
|
36151
|
-
}
|
|
36152
|
-
),
|
|
36153
|
-
open && /* @__PURE__ */ jsxs(
|
|
36154
|
-
Box,
|
|
36155
|
-
{
|
|
36156
|
-
role: "menu",
|
|
36157
|
-
className: cn(
|
|
36158
|
-
"absolute right-0 z-10 mt-1 w-44",
|
|
36159
|
-
"rounded-container border border-border bg-popover shadow-elevation-popover",
|
|
36160
|
-
"py-1 text-sm"
|
|
36161
|
-
),
|
|
36162
|
-
children: [
|
|
36163
|
-
/* @__PURE__ */ jsx(Box, { className: "px-2 py-1 text-xs uppercase tracking-wide text-muted-foreground", children: t(BLOCK_TYPE_LABEL_KEY[block.type]) }),
|
|
36164
|
-
/* @__PURE__ */ jsxs(
|
|
36165
|
-
Button,
|
|
36166
|
-
{
|
|
36167
|
-
type: "button",
|
|
36168
|
-
variant: "ghost",
|
|
36169
|
-
role: "menuitem",
|
|
36170
|
-
className: "flex w-full items-center gap-2 px-2 py-1.5 text-left justify-start rounded-none",
|
|
36171
|
-
onClick: () => {
|
|
36172
|
-
onDuplicate();
|
|
36173
|
-
setOpen(false);
|
|
36174
|
-
},
|
|
36175
|
-
children: [
|
|
36176
|
-
/* @__PURE__ */ jsx(Icon, { name: "plus", className: "w-3.5 h-3.5" }),
|
|
36177
|
-
" ",
|
|
36178
|
-
t("richBlockEditor.duplicate")
|
|
36179
|
-
]
|
|
36180
|
-
}
|
|
36181
|
-
),
|
|
36182
|
-
/* @__PURE__ */ jsxs(
|
|
36183
|
-
Button,
|
|
36184
|
-
{
|
|
36185
|
-
type: "button",
|
|
36186
|
-
variant: "ghost",
|
|
36187
|
-
role: "menuitem",
|
|
36188
|
-
className: "flex w-full items-center gap-2 px-2 py-1.5 text-left text-destructive hover:bg-muted justify-start rounded-none",
|
|
36189
|
-
onClick: () => {
|
|
36190
|
-
onDelete();
|
|
36191
|
-
setOpen(false);
|
|
36192
|
-
},
|
|
36193
|
-
children: [
|
|
36194
|
-
/* @__PURE__ */ jsx(Icon, { name: "trash", className: "w-3.5 h-3.5" }),
|
|
36195
|
-
" ",
|
|
36196
|
-
t("common.delete")
|
|
36197
|
-
]
|
|
36198
|
-
}
|
|
36199
|
-
),
|
|
36200
|
-
CHANGEABLE_TYPES.includes(block.type) && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
36201
|
-
/* @__PURE__ */ jsx(Box, { className: "my-1 border-t border-border" }),
|
|
36202
|
-
/* @__PURE__ */ jsx(Box, { className: "px-2 py-1 text-xs uppercase tracking-wide text-muted-foreground", children: t("richBlockEditor.turnInto") }),
|
|
36203
|
-
CHANGEABLE_TYPES.filter((bt) => bt !== block.type).map((bt) => /* @__PURE__ */ jsx(
|
|
36204
|
-
Button,
|
|
36205
|
-
{
|
|
36206
|
-
type: "button",
|
|
36207
|
-
variant: "ghost",
|
|
36208
|
-
role: "menuitem",
|
|
36209
|
-
className: "flex w-full items-center gap-2 px-2 py-1.5 text-left justify-start rounded-none",
|
|
36210
|
-
onClick: () => {
|
|
36211
|
-
onChangeType(bt);
|
|
36212
|
-
setOpen(false);
|
|
36213
|
-
},
|
|
36214
|
-
children: t(BLOCK_TYPE_LABEL_KEY[bt])
|
|
36215
|
-
},
|
|
36216
|
-
bt
|
|
36217
|
-
))
|
|
36218
|
-
] })
|
|
36219
|
-
]
|
|
36220
|
-
}
|
|
36221
|
-
)
|
|
36222
|
-
] });
|
|
36074
|
+
node = node.parentNode;
|
|
36075
|
+
}
|
|
36076
|
+
return {
|
|
36077
|
+
bold: document.queryCommandState("bold"),
|
|
36078
|
+
italic: document.queryCommandState("italic"),
|
|
36079
|
+
underline: document.queryCommandState("underline"),
|
|
36080
|
+
strikeThrough: document.queryCommandState("strikeThrough"),
|
|
36081
|
+
block,
|
|
36082
|
+
bullets: document.queryCommandState("insertUnorderedList"),
|
|
36083
|
+
numbers: document.queryCommandState("insertOrderedList"),
|
|
36084
|
+
link
|
|
36085
|
+
};
|
|
36223
36086
|
}
|
|
36224
|
-
function
|
|
36225
|
-
tag,
|
|
36226
|
-
value,
|
|
36227
|
-
readOnly,
|
|
36228
|
-
placeholder,
|
|
36229
|
-
className,
|
|
36230
|
-
ariaLabel,
|
|
36231
|
-
onValueChange
|
|
36232
|
-
}) {
|
|
36233
|
-
const ref = useRef(null);
|
|
36234
|
-
useEffect(() => {
|
|
36235
|
-
const el = ref.current;
|
|
36236
|
-
if (!el) return;
|
|
36237
|
-
const isFocused = document.activeElement === el;
|
|
36238
|
-
if (!isFocused && el.textContent !== value) {
|
|
36239
|
-
el.textContent = value;
|
|
36240
|
-
}
|
|
36241
|
-
}, [value]);
|
|
36242
|
-
const handleInput = useCallback(
|
|
36243
|
-
(e) => {
|
|
36244
|
-
onValueChange(e.currentTarget.textContent ?? "");
|
|
36245
|
-
},
|
|
36246
|
-
[onValueChange]
|
|
36247
|
-
);
|
|
36087
|
+
function ToolbarButton({ icon: IconCmp, label, active, onExec }) {
|
|
36248
36088
|
return /* @__PURE__ */ jsx(
|
|
36249
|
-
|
|
36089
|
+
Button,
|
|
36250
36090
|
{
|
|
36251
|
-
|
|
36252
|
-
|
|
36253
|
-
|
|
36254
|
-
|
|
36255
|
-
|
|
36256
|
-
|
|
36257
|
-
"aria-multiline": "true",
|
|
36258
|
-
"data-placeholder": placeholder,
|
|
36091
|
+
type: "button",
|
|
36092
|
+
variant: "ghost",
|
|
36093
|
+
size: "sm",
|
|
36094
|
+
"aria-label": label,
|
|
36095
|
+
"aria-pressed": active,
|
|
36096
|
+
title: label,
|
|
36259
36097
|
className: cn(
|
|
36260
|
-
"
|
|
36261
|
-
"
|
|
36262
|
-
className
|
|
36098
|
+
"h-8 w-8 p-0 gap-0 justify-center",
|
|
36099
|
+
active && "bg-muted text-foreground"
|
|
36263
36100
|
),
|
|
36264
|
-
|
|
36101
|
+
onMouseDown: (e) => {
|
|
36102
|
+
e.preventDefault();
|
|
36103
|
+
onExec();
|
|
36104
|
+
},
|
|
36105
|
+
children: /* @__PURE__ */ jsx(IconCmp, { size: 15 })
|
|
36265
36106
|
}
|
|
36266
36107
|
);
|
|
36267
36108
|
}
|
|
36268
|
-
|
|
36269
|
-
|
|
36270
|
-
|
|
36271
|
-
|
|
36272
|
-
|
|
36273
|
-
|
|
36274
|
-
|
|
36275
|
-
|
|
36276
|
-
|
|
36277
|
-
|
|
36278
|
-
|
|
36279
|
-
|
|
36280
|
-
|
|
36281
|
-
|
|
36282
|
-
|
|
36283
|
-
|
|
36284
|
-
|
|
36285
|
-
|
|
36286
|
-
|
|
36287
|
-
|
|
36288
|
-
|
|
36289
|
-
|
|
36290
|
-
|
|
36291
|
-
|
|
36292
|
-
|
|
36293
|
-
|
|
36294
|
-
|
|
36295
|
-
|
|
36296
|
-
|
|
36297
|
-
|
|
36298
|
-
|
|
36299
|
-
|
|
36300
|
-
|
|
36301
|
-
|
|
36302
|
-
|
|
36303
|
-
|
|
36304
|
-
|
|
36305
|
-
|
|
36306
|
-
|
|
36307
|
-
|
|
36308
|
-
|
|
36309
|
-
|
|
36310
|
-
|
|
36311
|
-
|
|
36312
|
-
|
|
36313
|
-
|
|
36314
|
-
|
|
36315
|
-
|
|
36316
|
-
|
|
36317
|
-
|
|
36318
|
-
|
|
36319
|
-
|
|
36320
|
-
|
|
36321
|
-
|
|
36322
|
-
|
|
36323
|
-
|
|
36324
|
-
|
|
36325
|
-
|
|
36326
|
-
|
|
36327
|
-
|
|
36328
|
-
|
|
36329
|
-
|
|
36330
|
-
|
|
36331
|
-
|
|
36332
|
-
|
|
36333
|
-
|
|
36334
|
-
|
|
36335
|
-
|
|
36336
|
-
|
|
36337
|
-
|
|
36338
|
-
|
|
36339
|
-
|
|
36340
|
-
|
|
36341
|
-
|
|
36342
|
-
|
|
36343
|
-
|
|
36344
|
-
|
|
36345
|
-
|
|
36346
|
-
|
|
36347
|
-
|
|
36348
|
-
|
|
36349
|
-
|
|
36350
|
-
|
|
36351
|
-
|
|
36352
|
-
|
|
36353
|
-
|
|
36354
|
-
|
|
36355
|
-
|
|
36356
|
-
|
|
36357
|
-
|
|
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
|
-
|
|
36397
|
-
|
|
36398
|
-
|
|
36399
|
-
|
|
36400
|
-
|
|
36401
|
-
|
|
36402
|
-
|
|
36403
|
-
|
|
36404
|
-
|
|
36405
|
-
|
|
36406
|
-
|
|
36407
|
-
|
|
36408
|
-
|
|
36409
|
-
|
|
36410
|
-
|
|
36411
|
-
|
|
36412
|
-
|
|
36413
|
-
|
|
36109
|
+
var CHANGE_DEBOUNCE_MS, ALLOWED_ATTRS, DROP_TAGS, MD_BLOCK_PREFIXES, BLOCK_TAGS, MD_INLINE_RULES, RICH_TEXT_CSS, IDLE_TOOLBAR, RichTextEditor;
|
|
36110
|
+
var init_RichTextEditor = __esm({
|
|
36111
|
+
"components/core/molecules/RichTextEditor.tsx"() {
|
|
36112
|
+
"use client";
|
|
36113
|
+
init_cn();
|
|
36114
|
+
init_Box();
|
|
36115
|
+
init_Button();
|
|
36116
|
+
init_useEventBus();
|
|
36117
|
+
CHANGE_DEBOUNCE_MS = 400;
|
|
36118
|
+
ALLOWED_ATTRS = {
|
|
36119
|
+
p: [],
|
|
36120
|
+
h1: [],
|
|
36121
|
+
h2: [],
|
|
36122
|
+
h3: [],
|
|
36123
|
+
h4: [],
|
|
36124
|
+
ul: [],
|
|
36125
|
+
ol: [],
|
|
36126
|
+
li: [],
|
|
36127
|
+
blockquote: [],
|
|
36128
|
+
pre: [],
|
|
36129
|
+
code: [],
|
|
36130
|
+
b: [],
|
|
36131
|
+
strong: [],
|
|
36132
|
+
i: [],
|
|
36133
|
+
em: [],
|
|
36134
|
+
u: [],
|
|
36135
|
+
s: [],
|
|
36136
|
+
strike: [],
|
|
36137
|
+
br: [],
|
|
36138
|
+
hr: [],
|
|
36139
|
+
div: [],
|
|
36140
|
+
span: [],
|
|
36141
|
+
a: ["href"],
|
|
36142
|
+
img: ["src", "alt"]
|
|
36143
|
+
};
|
|
36144
|
+
DROP_TAGS = /* @__PURE__ */ new Set(["script", "style", "iframe", "object", "embed", "link", "meta", "form", "input", "button", "textarea", "select"]);
|
|
36145
|
+
MD_BLOCK_PREFIXES = {
|
|
36146
|
+
"#": { kind: "block", tag: "h1" },
|
|
36147
|
+
"##": { kind: "block", tag: "h2" },
|
|
36148
|
+
"###": { kind: "block", tag: "h3" },
|
|
36149
|
+
">": { kind: "block", tag: "blockquote" },
|
|
36150
|
+
"```": { kind: "block", tag: "pre" },
|
|
36151
|
+
"-": { kind: "list", command: "insertUnorderedList" },
|
|
36152
|
+
"*": { kind: "list", command: "insertUnorderedList" },
|
|
36153
|
+
"1.": { kind: "list", command: "insertOrderedList" }
|
|
36154
|
+
};
|
|
36155
|
+
BLOCK_TAGS = /* @__PURE__ */ new Set(["P", "DIV", "LI", "H1", "H2", "H3", "H4", "BLOCKQUOTE", "PRE"]);
|
|
36156
|
+
MD_INLINE_RULES = [
|
|
36157
|
+
{ pattern: /\*\*([^*\n]+)\*\*$/, wrap: "strong" },
|
|
36158
|
+
{ pattern: /(^|[^*])\*([^*\n]+)\*$/, wrap: "em" },
|
|
36159
|
+
{ pattern: /`([^`\n]+)`$/, wrap: "code" }
|
|
36160
|
+
];
|
|
36161
|
+
RICH_TEXT_CSS = `
|
|
36162
|
+
.almadar-rich-text { line-height: 1.7; color: var(--color-foreground); }
|
|
36163
|
+
.almadar-rich-text h1 { font-size: 1.875rem; font-weight: 700; line-height: 1.25; margin: 1.25em 0 0.4em; }
|
|
36164
|
+
.almadar-rich-text h2 { font-size: 1.5rem; font-weight: 650; line-height: 1.3; margin: 1.1em 0 0.4em; }
|
|
36165
|
+
.almadar-rich-text h3 { font-size: 1.25rem; font-weight: 600; line-height: 1.35; margin: 1em 0 0.35em; }
|
|
36166
|
+
.almadar-rich-text h1:first-child, .almadar-rich-text h2:first-child, .almadar-rich-text h3:first-child { margin-top: 0; }
|
|
36167
|
+
.almadar-rich-text p { margin: 0.5em 0; }
|
|
36168
|
+
.almadar-rich-text ul { list-style: disc; padding-inline-start: 1.5rem; margin: 0.5em 0; }
|
|
36169
|
+
.almadar-rich-text ol { list-style: decimal; padding-inline-start: 1.5rem; margin: 0.5em 0; }
|
|
36170
|
+
.almadar-rich-text li { margin: 0.25em 0; }
|
|
36171
|
+
.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; }
|
|
36172
|
+
.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; }
|
|
36173
|
+
.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; }
|
|
36174
|
+
.almadar-rich-text pre code { background: transparent; padding: 0; }
|
|
36175
|
+
.almadar-rich-text a { color: var(--color-primary); text-decoration: underline; }
|
|
36176
|
+
.almadar-rich-text hr { border: none; border-top: 1px solid var(--color-border); margin: 1.25em 0; }
|
|
36177
|
+
.almadar-rich-text img { max-width: 100%; border-radius: 0.375rem; }
|
|
36178
|
+
`;
|
|
36179
|
+
IDLE_TOOLBAR = {
|
|
36180
|
+
bold: false,
|
|
36181
|
+
italic: false,
|
|
36182
|
+
underline: false,
|
|
36183
|
+
strikeThrough: false,
|
|
36184
|
+
block: null,
|
|
36185
|
+
bullets: false,
|
|
36186
|
+
numbers: false,
|
|
36187
|
+
link: false
|
|
36188
|
+
};
|
|
36189
|
+
RichTextEditor = ({
|
|
36190
|
+
value,
|
|
36191
|
+
onChange,
|
|
36192
|
+
changeEvent,
|
|
36193
|
+
readOnly = false,
|
|
36194
|
+
placeholder,
|
|
36195
|
+
showToolbar = true,
|
|
36196
|
+
className
|
|
36197
|
+
}) => {
|
|
36198
|
+
const { t } = useTranslate();
|
|
36199
|
+
const ref = useRef(null);
|
|
36200
|
+
const [toolbar, setToolbar] = useState(IDLE_TOOLBAR);
|
|
36201
|
+
const [empty, setEmpty] = useState(() => !value || !sanitizeRichHtml(value).trim());
|
|
36202
|
+
const eventBus = useEventBus();
|
|
36203
|
+
const onChangeRef = useRef(onChange);
|
|
36204
|
+
const changeEventRef = useRef(changeEvent);
|
|
36205
|
+
useEffect(() => {
|
|
36206
|
+
onChangeRef.current = onChange;
|
|
36207
|
+
changeEventRef.current = changeEvent;
|
|
36208
|
+
}, [onChange, changeEvent]);
|
|
36209
|
+
const emitTimerRef = useRef(null);
|
|
36210
|
+
const emitNow = useCallback(() => {
|
|
36211
|
+
const el = ref.current;
|
|
36212
|
+
if (!el) return;
|
|
36213
|
+
const html = sanitizeRichHtml(el.innerHTML);
|
|
36214
|
+
onChangeRef.current?.(html);
|
|
36215
|
+
const evt = changeEventRef.current;
|
|
36216
|
+
if (evt) eventBus.emit(`UI:${evt}`, { value: html });
|
|
36217
|
+
}, [eventBus]);
|
|
36218
|
+
const scheduleEmit = useCallback(() => {
|
|
36219
|
+
if (emitTimerRef.current) clearTimeout(emitTimerRef.current);
|
|
36220
|
+
emitTimerRef.current = setTimeout(() => {
|
|
36221
|
+
emitTimerRef.current = null;
|
|
36222
|
+
emitNow();
|
|
36223
|
+
}, CHANGE_DEBOUNCE_MS);
|
|
36224
|
+
}, [emitNow]);
|
|
36225
|
+
const flushEmit = useCallback(() => {
|
|
36226
|
+
if (!emitTimerRef.current) return;
|
|
36227
|
+
clearTimeout(emitTimerRef.current);
|
|
36228
|
+
emitTimerRef.current = null;
|
|
36229
|
+
emitNow();
|
|
36230
|
+
}, [emitNow]);
|
|
36231
|
+
useEffect(() => () => {
|
|
36232
|
+
if (emitTimerRef.current) {
|
|
36233
|
+
clearTimeout(emitTimerRef.current);
|
|
36234
|
+
emitTimerRef.current = null;
|
|
36235
|
+
emitNow();
|
|
36236
|
+
}
|
|
36237
|
+
}, [emitNow]);
|
|
36238
|
+
useEffect(() => {
|
|
36239
|
+
if (readOnly) return;
|
|
36240
|
+
const el = ref.current;
|
|
36241
|
+
if (!el) return;
|
|
36242
|
+
if (document.activeElement === el) return;
|
|
36243
|
+
const next = sanitizeRichHtml(value ?? "") || "<p><br></p>";
|
|
36244
|
+
if (el.innerHTML !== next) {
|
|
36245
|
+
el.innerHTML = next;
|
|
36246
|
+
setEmpty(htmlIsEmpty(el));
|
|
36247
|
+
}
|
|
36248
|
+
}, [readOnly, value]);
|
|
36249
|
+
useEffect(() => {
|
|
36250
|
+
if (readOnly) return;
|
|
36251
|
+
document.execCommand("defaultParagraphSeparator", false, "p");
|
|
36252
|
+
}, [readOnly]);
|
|
36253
|
+
useEffect(() => {
|
|
36254
|
+
if (readOnly || !showToolbar) return;
|
|
36255
|
+
const onSelectionChange = () => {
|
|
36256
|
+
const el = ref.current;
|
|
36257
|
+
if (!el) return;
|
|
36258
|
+
setToolbar(readToolbarState(el));
|
|
36414
36259
|
};
|
|
36415
|
-
|
|
36416
|
-
|
|
36417
|
-
|
|
36418
|
-
|
|
36419
|
-
|
|
36420
|
-
|
|
36421
|
-
|
|
36422
|
-
|
|
36423
|
-
|
|
36424
|
-
|
|
36425
|
-
|
|
36426
|
-
|
|
36427
|
-
|
|
36428
|
-
|
|
36429
|
-
|
|
36430
|
-
|
|
36431
|
-
!
|
|
36432
|
-
|
|
36433
|
-
|
|
36434
|
-
|
|
36435
|
-
|
|
36436
|
-
|
|
36437
|
-
|
|
36438
|
-
|
|
36439
|
-
|
|
36440
|
-
|
|
36441
|
-
|
|
36442
|
-
|
|
36443
|
-
|
|
36444
|
-
|
|
36445
|
-
|
|
36446
|
-
|
|
36447
|
-
|
|
36448
|
-
|
|
36449
|
-
|
|
36450
|
-
|
|
36451
|
-
|
|
36452
|
-
|
|
36453
|
-
|
|
36454
|
-
|
|
36455
|
-
|
|
36456
|
-
|
|
36457
|
-
|
|
36458
|
-
|
|
36459
|
-
|
|
36460
|
-
|
|
36461
|
-
|
|
36260
|
+
document.addEventListener("selectionchange", onSelectionChange);
|
|
36261
|
+
return () => document.removeEventListener("selectionchange", onSelectionChange);
|
|
36262
|
+
}, [readOnly, showToolbar]);
|
|
36263
|
+
const tryInlineMarkdown = useCallback(() => {
|
|
36264
|
+
const root = ref.current;
|
|
36265
|
+
if (!root) return false;
|
|
36266
|
+
const sel = window.getSelection();
|
|
36267
|
+
if (!sel || !sel.isCollapsed || sel.rangeCount === 0) return false;
|
|
36268
|
+
const node = sel.anchorNode;
|
|
36269
|
+
if (!node || node.nodeType !== Node.TEXT_NODE || !root.contains(node)) return false;
|
|
36270
|
+
if (node.parentElement?.closest("code, pre")) return false;
|
|
36271
|
+
const text = (node.textContent ?? "").slice(0, sel.anchorOffset);
|
|
36272
|
+
for (const rule of MD_INLINE_RULES) {
|
|
36273
|
+
const m = rule.pattern.exec(text);
|
|
36274
|
+
if (!m) continue;
|
|
36275
|
+
const inner = rule.wrap === "em" ? m[2] : m[1];
|
|
36276
|
+
if (!inner || !inner.trim()) continue;
|
|
36277
|
+
const lead = rule.wrap === "em" ? m[1]?.length ?? 0 : 0;
|
|
36278
|
+
const range = document.createRange();
|
|
36279
|
+
range.setStart(node, m.index + lead);
|
|
36280
|
+
range.setEnd(node, sel.anchorOffset);
|
|
36281
|
+
const el = document.createElement(rule.wrap);
|
|
36282
|
+
el.textContent = inner;
|
|
36283
|
+
range.deleteContents();
|
|
36284
|
+
range.insertNode(el);
|
|
36285
|
+
const after = document.createTextNode("\u200B");
|
|
36286
|
+
el.parentNode?.insertBefore(after, el.nextSibling);
|
|
36287
|
+
const caret = document.createRange();
|
|
36288
|
+
caret.setStart(after, 1);
|
|
36289
|
+
caret.collapse(true);
|
|
36290
|
+
sel.removeAllRanges();
|
|
36291
|
+
sel.addRange(caret);
|
|
36292
|
+
return true;
|
|
36293
|
+
}
|
|
36294
|
+
return false;
|
|
36295
|
+
}, []);
|
|
36296
|
+
const tryBlockMarkdown = useCallback(() => {
|
|
36297
|
+
const root = ref.current;
|
|
36298
|
+
if (!root) return false;
|
|
36299
|
+
const sel = window.getSelection();
|
|
36300
|
+
if (!sel || !sel.isCollapsed || sel.rangeCount === 0) return false;
|
|
36301
|
+
const node = sel.anchorNode;
|
|
36302
|
+
if (!node || node.nodeType !== Node.TEXT_NODE || !root.contains(node)) return false;
|
|
36303
|
+
const block = closestBlock(node, root);
|
|
36304
|
+
if (block ? block.firstChild !== node : root.firstChild !== node) return false;
|
|
36305
|
+
const prefix = (node.textContent ?? "").slice(0, sel.anchorOffset);
|
|
36306
|
+
const rule = MD_BLOCK_PREFIXES[prefix];
|
|
36307
|
+
if (!rule) return false;
|
|
36308
|
+
if (node.parentElement?.closest("pre, code")) return false;
|
|
36309
|
+
if (rule.kind === "list" && node.parentElement?.closest("li")) return false;
|
|
36310
|
+
node.deleteData(0, prefix.length);
|
|
36311
|
+
const caret = document.createRange();
|
|
36312
|
+
caret.setStart(node, 0);
|
|
36313
|
+
caret.collapse(true);
|
|
36314
|
+
sel.removeAllRanges();
|
|
36315
|
+
sel.addRange(caret);
|
|
36316
|
+
if (rule.kind === "list") {
|
|
36317
|
+
const container = block ?? node.parentElement;
|
|
36318
|
+
const list = document.createElement(rule.command === "insertOrderedList" ? "ol" : "ul");
|
|
36319
|
+
const li = document.createElement("li");
|
|
36320
|
+
if (block) {
|
|
36321
|
+
while (block.firstChild) li.appendChild(block.firstChild);
|
|
36322
|
+
list.appendChild(li);
|
|
36323
|
+
block.replaceWith(list);
|
|
36324
|
+
} else if (container) {
|
|
36325
|
+
li.appendChild(node);
|
|
36326
|
+
list.appendChild(li);
|
|
36327
|
+
container.appendChild(list);
|
|
36328
|
+
}
|
|
36329
|
+
if (!li.firstChild) li.appendChild(document.createElement("br"));
|
|
36330
|
+
const liCaret = document.createRange();
|
|
36331
|
+
liCaret.selectNodeContents(li);
|
|
36332
|
+
liCaret.collapse(true);
|
|
36333
|
+
sel.removeAllRanges();
|
|
36334
|
+
sel.addRange(liCaret);
|
|
36335
|
+
} else {
|
|
36336
|
+
document.execCommand("formatBlock", false, rule.tag);
|
|
36337
|
+
}
|
|
36338
|
+
return true;
|
|
36339
|
+
}, []);
|
|
36340
|
+
const afterEdit = useCallback(() => {
|
|
36341
|
+
const el = ref.current;
|
|
36342
|
+
if (!el) return;
|
|
36343
|
+
tryInlineMarkdown();
|
|
36344
|
+
setEmpty(htmlIsEmpty(el));
|
|
36345
|
+
setToolbar(readToolbarState(el));
|
|
36346
|
+
scheduleEmit();
|
|
36347
|
+
}, [scheduleEmit, tryInlineMarkdown]);
|
|
36348
|
+
const handleKeyDown = useCallback((e) => {
|
|
36349
|
+
if (e.key === " " && tryBlockMarkdown()) {
|
|
36350
|
+
e.preventDefault();
|
|
36351
|
+
afterEdit();
|
|
36352
|
+
}
|
|
36353
|
+
}, [afterEdit, tryBlockMarkdown]);
|
|
36354
|
+
const execInline = useCallback((command) => {
|
|
36355
|
+
document.execCommand(command, false);
|
|
36356
|
+
afterEdit();
|
|
36357
|
+
}, [afterEdit]);
|
|
36358
|
+
const execBlock = useCallback((tag) => {
|
|
36359
|
+
const next = toolbar.block === tag ? "p" : tag;
|
|
36360
|
+
document.execCommand("formatBlock", false, next);
|
|
36361
|
+
afterEdit();
|
|
36362
|
+
}, [afterEdit, toolbar.block]);
|
|
36363
|
+
const execList = useCallback((command) => {
|
|
36364
|
+
document.execCommand(command, false);
|
|
36365
|
+
afterEdit();
|
|
36366
|
+
}, [afterEdit]);
|
|
36367
|
+
const execLink = useCallback(() => {
|
|
36368
|
+
if (toolbar.link) {
|
|
36369
|
+
document.execCommand("unlink", false);
|
|
36370
|
+
afterEdit();
|
|
36371
|
+
return;
|
|
36372
|
+
}
|
|
36373
|
+
const url = window.prompt(t("richTextEditor.linkPrompt"));
|
|
36374
|
+
if (!url) return;
|
|
36375
|
+
const safe = safeUrl(url, ["http://", "https://", "mailto:"]) ?? `https://${url}`;
|
|
36376
|
+
document.execCommand("createLink", false, safe);
|
|
36377
|
+
afterEdit();
|
|
36378
|
+
}, [afterEdit, t, toolbar.link]);
|
|
36379
|
+
const execRule = useCallback(() => {
|
|
36380
|
+
document.execCommand("insertHorizontalRule", false);
|
|
36381
|
+
afterEdit();
|
|
36382
|
+
}, [afterEdit]);
|
|
36383
|
+
if (readOnly) {
|
|
36384
|
+
return /* @__PURE__ */ jsxs(Box, { className: cn("almadar-rich-text max-w-none", className), children: [
|
|
36385
|
+
/* @__PURE__ */ jsx(RichTextStyles, {}),
|
|
36386
|
+
/* @__PURE__ */ jsx(Box, { dangerouslySetInnerHTML: { __html: sanitizeRichHtml(value ?? "") } })
|
|
36462
36387
|
] });
|
|
36463
36388
|
}
|
|
36464
|
-
|
|
36465
|
-
|
|
36466
|
-
|
|
36467
|
-
return /* @__PURE__ */ jsxs(
|
|
36389
|
+
return /* @__PURE__ */ jsxs(Box, { className: cn("flex flex-col gap-2", className), children: [
|
|
36390
|
+
/* @__PURE__ */ jsx(RichTextStyles, {}),
|
|
36391
|
+
showToolbar && /* @__PURE__ */ jsxs(
|
|
36468
36392
|
Box,
|
|
36469
36393
|
{
|
|
36470
|
-
|
|
36394
|
+
role: "toolbar",
|
|
36395
|
+
"aria-label": t("richTextEditor.editorToolbar"),
|
|
36471
36396
|
className: cn(
|
|
36472
|
-
"
|
|
36473
|
-
|
|
36397
|
+
"sticky top-0 z-10 flex flex-wrap items-center gap-0.5 self-start",
|
|
36398
|
+
"rounded-md border border-border bg-background/95 px-1 py-0.5 shadow-sm"
|
|
36474
36399
|
),
|
|
36475
36400
|
children: [
|
|
36476
|
-
|
|
36477
|
-
|
|
36478
|
-
|
|
36479
|
-
|
|
36480
|
-
|
|
36481
|
-
|
|
36482
|
-
|
|
36483
|
-
|
|
36484
|
-
|
|
36485
|
-
|
|
36486
|
-
|
|
36487
|
-
|
|
36488
|
-
|
|
36489
|
-
|
|
36490
|
-
|
|
36491
|
-
|
|
36492
|
-
type: "button",
|
|
36493
|
-
variant: "ghost",
|
|
36494
|
-
"aria-label": t("richBlockEditor.aria.removeListItem"),
|
|
36495
|
-
className: cn(
|
|
36496
|
-
"h-5 w-5 shrink-0 rounded-sm text-muted-foreground p-0 gap-0",
|
|
36497
|
-
"opacity-0 group-hover/item:opacity-100 hover:bg-muted hover:text-foreground"
|
|
36498
|
-
),
|
|
36499
|
-
onClick: () => removeListItem(child.id),
|
|
36500
|
-
children: /* @__PURE__ */ jsx(Icon, { name: "trash", className: "w-3 h-3" })
|
|
36501
|
-
}
|
|
36502
|
-
)
|
|
36503
|
-
] }, child.id)),
|
|
36504
|
-
!readOnly && showAffordances && /* @__PURE__ */ jsx(Box, { as: "li", className: "list-none pl-0", children: /* @__PURE__ */ jsxs(
|
|
36505
|
-
Button,
|
|
36506
|
-
{
|
|
36507
|
-
type: "button",
|
|
36508
|
-
variant: "ghost",
|
|
36509
|
-
className: cn(
|
|
36510
|
-
"inline-flex items-center gap-1 text-xs text-muted-foreground",
|
|
36511
|
-
"hover:text-foreground p-0 h-auto"
|
|
36512
|
-
),
|
|
36513
|
-
onClick: addListItem,
|
|
36514
|
-
children: [
|
|
36515
|
-
/* @__PURE__ */ jsx(Icon, { name: "plus", className: "w-3 h-3" }),
|
|
36516
|
-
" ",
|
|
36517
|
-
t("richBlockEditor.addItem")
|
|
36518
|
-
]
|
|
36519
|
-
}
|
|
36520
|
-
) })
|
|
36401
|
+
/* @__PURE__ */ jsx(ToolbarButton, { icon: Bold, label: t("richTextEditor.bold"), active: toolbar.bold, onExec: () => execInline("bold") }),
|
|
36402
|
+
/* @__PURE__ */ jsx(ToolbarButton, { icon: Italic, label: t("richTextEditor.italic"), active: toolbar.italic, onExec: () => execInline("italic") }),
|
|
36403
|
+
/* @__PURE__ */ jsx(ToolbarButton, { icon: Underline, label: t("richTextEditor.underline"), active: toolbar.underline, onExec: () => execInline("underline") }),
|
|
36404
|
+
/* @__PURE__ */ jsx(ToolbarButton, { icon: Strikethrough, label: t("richTextEditor.strikethrough"), active: toolbar.strikeThrough, onExec: () => execInline("strikeThrough") }),
|
|
36405
|
+
/* @__PURE__ */ jsx(Box, { className: "mx-1 h-5 w-px bg-border" }),
|
|
36406
|
+
/* @__PURE__ */ jsx(ToolbarButton, { icon: Heading1, label: t("richTextEditor.heading1"), active: toolbar.block === "h1", onExec: () => execBlock("h1") }),
|
|
36407
|
+
/* @__PURE__ */ jsx(ToolbarButton, { icon: Heading2, label: t("richTextEditor.heading2"), active: toolbar.block === "h2", onExec: () => execBlock("h2") }),
|
|
36408
|
+
/* @__PURE__ */ jsx(ToolbarButton, { icon: Heading3, label: t("richTextEditor.heading3"), active: toolbar.block === "h3", onExec: () => execBlock("h3") }),
|
|
36409
|
+
/* @__PURE__ */ jsx(Box, { className: "mx-1 h-5 w-px bg-border" }),
|
|
36410
|
+
/* @__PURE__ */ jsx(ToolbarButton, { icon: List, label: t("richTextEditor.bulletList"), active: toolbar.bullets, onExec: () => execList("insertUnorderedList") }),
|
|
36411
|
+
/* @__PURE__ */ jsx(ToolbarButton, { icon: ListOrdered, label: t("richTextEditor.numberedList"), active: toolbar.numbers, onExec: () => execList("insertOrderedList") }),
|
|
36412
|
+
/* @__PURE__ */ jsx(Box, { className: "mx-1 h-5 w-px bg-border" }),
|
|
36413
|
+
/* @__PURE__ */ jsx(ToolbarButton, { icon: Quote, label: t("richTextEditor.quote"), active: toolbar.block === "blockquote", onExec: () => execBlock("blockquote") }),
|
|
36414
|
+
/* @__PURE__ */ jsx(ToolbarButton, { icon: Code, label: t("richTextEditor.code"), active: toolbar.block === "pre", onExec: () => execBlock("pre") }),
|
|
36415
|
+
/* @__PURE__ */ jsx(ToolbarButton, { icon: Link$1, label: toolbar.link ? t("richTextEditor.removeLink") : t("richTextEditor.link"), active: toolbar.link, onExec: execLink }),
|
|
36416
|
+
/* @__PURE__ */ jsx(ToolbarButton, { icon: Minus, label: t("richTextEditor.divider"), onExec: execRule })
|
|
36521
36417
|
]
|
|
36522
36418
|
}
|
|
36523
|
-
)
|
|
36524
|
-
|
|
36525
|
-
|
|
36526
|
-
default:
|
|
36527
|
-
return /* @__PURE__ */ jsx(
|
|
36528
|
-
Editable,
|
|
36419
|
+
),
|
|
36420
|
+
/* @__PURE__ */ jsx(
|
|
36421
|
+
Box,
|
|
36529
36422
|
{
|
|
36530
|
-
|
|
36531
|
-
|
|
36532
|
-
|
|
36533
|
-
|
|
36534
|
-
|
|
36535
|
-
|
|
36536
|
-
|
|
36423
|
+
ref,
|
|
36424
|
+
contentEditable: true,
|
|
36425
|
+
suppressContentEditableWarning: true,
|
|
36426
|
+
role: "textbox",
|
|
36427
|
+
"aria-multiline": "true",
|
|
36428
|
+
"aria-label": t("richTextEditor.editorSurface"),
|
|
36429
|
+
"data-placeholder": placeholder ?? t("richTextEditor.placeholder"),
|
|
36430
|
+
"data-empty": empty ? "true" : "false",
|
|
36431
|
+
className: cn(
|
|
36432
|
+
"almadar-rich-text max-w-none min-h-[8rem] outline-none",
|
|
36433
|
+
"data-[empty=true]:before:content-[attr(data-placeholder)]",
|
|
36434
|
+
"data-[empty=true]:before:text-muted-foreground/60",
|
|
36435
|
+
"data-[empty=true]:before:pointer-events-none",
|
|
36436
|
+
"data-[empty=true]:before:absolute",
|
|
36437
|
+
"relative cursor-text"
|
|
36438
|
+
),
|
|
36439
|
+
onInput: afterEdit,
|
|
36440
|
+
onKeyDown: handleKeyDown,
|
|
36441
|
+
onBlur: flushEmit
|
|
36537
36442
|
}
|
|
36538
|
-
)
|
|
36539
|
-
|
|
36443
|
+
)
|
|
36444
|
+
] });
|
|
36445
|
+
};
|
|
36446
|
+
RichTextEditor.displayName = "RichTextEditor";
|
|
36447
|
+
}
|
|
36448
|
+
});
|
|
36449
|
+
function renderIconInput4(icon, props) {
|
|
36450
|
+
return typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsx(Icon, { icon, ...props });
|
|
36451
|
+
}
|
|
36452
|
+
function DocumentPanel({
|
|
36453
|
+
id,
|
|
36454
|
+
title,
|
|
36455
|
+
subtitle,
|
|
36456
|
+
value,
|
|
36457
|
+
actions,
|
|
36458
|
+
maxInlineActions,
|
|
36459
|
+
editing = false,
|
|
36460
|
+
titleCommitEvent,
|
|
36461
|
+
contentChangeEvent,
|
|
36462
|
+
editEvent,
|
|
36463
|
+
doneEvent,
|
|
36464
|
+
editLabel,
|
|
36465
|
+
doneLabel,
|
|
36466
|
+
autosaveHint,
|
|
36467
|
+
placeholder,
|
|
36468
|
+
className
|
|
36469
|
+
}) {
|
|
36470
|
+
const eventBus = useEventBus();
|
|
36471
|
+
const { t } = useTranslate();
|
|
36472
|
+
const [titleDraft, setTitleDraft] = useState(null);
|
|
36473
|
+
const recordId = id ?? "";
|
|
36474
|
+
const actionDefs = actions ?? [];
|
|
36475
|
+
const inlineCap = Math.min(maxInlineActions ?? 1, 1);
|
|
36476
|
+
const inlineActions = actionDefs.filter((a) => a.variant === "primary").slice(0, inlineCap);
|
|
36477
|
+
const menuActions = actionDefs.filter((a) => !inlineActions.includes(a));
|
|
36478
|
+
const fireAction = (action) => {
|
|
36479
|
+
if (!action.event) return;
|
|
36480
|
+
eventBus.emit(`UI:${action.event}`, { id: recordId, title: title ?? "" });
|
|
36540
36481
|
};
|
|
36541
|
-
|
|
36482
|
+
const commitTitle = () => {
|
|
36483
|
+
if (!titleCommitEvent) return;
|
|
36484
|
+
const next = (titleDraft ?? "").trim();
|
|
36485
|
+
setTitleDraft(null);
|
|
36486
|
+
if (!next || next === title) return;
|
|
36487
|
+
eventBus.emit(`UI:${titleCommitEvent}`, { title: next, id: recordId });
|
|
36488
|
+
};
|
|
36489
|
+
const untitled = t("documentPanel.untitled") || "Untitled";
|
|
36490
|
+
const titleNode = titleCommitEvent && titleDraft !== null ? /* @__PURE__ */ jsx(
|
|
36491
|
+
Input,
|
|
36492
|
+
{
|
|
36493
|
+
value: titleDraft,
|
|
36494
|
+
autoFocus: true,
|
|
36495
|
+
"aria-label": t("common.title"),
|
|
36496
|
+
className: "h-auto py-1 text-3xl font-bold tracking-tight",
|
|
36497
|
+
onFocus: (e) => e.currentTarget.select(),
|
|
36498
|
+
onChange: (e) => setTitleDraft(e.target.value),
|
|
36499
|
+
onBlur: commitTitle,
|
|
36500
|
+
onKeyDown: (e) => {
|
|
36501
|
+
if (e.key === "Enter") {
|
|
36502
|
+
e.preventDefault();
|
|
36503
|
+
commitTitle();
|
|
36504
|
+
} else if (e.key === "Escape") {
|
|
36505
|
+
e.preventDefault();
|
|
36506
|
+
setTitleDraft(null);
|
|
36507
|
+
}
|
|
36508
|
+
},
|
|
36509
|
+
"data-testid": "document-title-input"
|
|
36510
|
+
}
|
|
36511
|
+
) : titleCommitEvent ? /* @__PURE__ */ jsx(
|
|
36542
36512
|
Box,
|
|
36543
36513
|
{
|
|
36544
|
-
|
|
36545
|
-
|
|
36546
|
-
|
|
36547
|
-
),
|
|
36548
|
-
|
|
36549
|
-
|
|
36550
|
-
|
|
36551
|
-
|
|
36552
|
-
|
|
36514
|
+
role: "button",
|
|
36515
|
+
tabIndex: 0,
|
|
36516
|
+
className: "cursor-text rounded px-1 -mx-1 transition-colors hover:bg-muted/40",
|
|
36517
|
+
onClick: () => setTitleDraft(title ?? ""),
|
|
36518
|
+
onKeyDown: (e) => {
|
|
36519
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
36520
|
+
e.preventDefault();
|
|
36521
|
+
setTitleDraft(title ?? "");
|
|
36522
|
+
}
|
|
36523
|
+
},
|
|
36524
|
+
"data-testid": "document-title-editable",
|
|
36525
|
+
children: /* @__PURE__ */ jsx(Typography, { variant: "h2", weight: "bold", className: cn(!title && "text-muted-foreground"), children: title || untitled })
|
|
36526
|
+
}
|
|
36527
|
+
) : /* @__PURE__ */ jsx(Typography, { variant: "h2", weight: "bold", children: title || untitled });
|
|
36528
|
+
const emitWithId = (event) => () => {
|
|
36529
|
+
if (event) eventBus.emit(`UI:${event}`, { id: recordId });
|
|
36530
|
+
};
|
|
36531
|
+
return /* @__PURE__ */ jsx(Card, { variant: "elevated", className: cn("w-full", className), children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", className: "p-6 sm:p-8", children: [
|
|
36532
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "justify-between items-start", children: [
|
|
36533
|
+
/* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "flex-1 min-w-0", children: [
|
|
36534
|
+
titleNode,
|
|
36535
|
+
subtitle ? /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", children: subtitle }) : null
|
|
36536
|
+
] }),
|
|
36537
|
+
/* @__PURE__ */ jsx(HStack, { gap: "xs", className: "flex-shrink-0 pt-1 items-center", children: editing ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
36538
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", className: "hidden sm:block", children: autosaveHint ?? (t("documentPanel.autosaveHint") || "") }),
|
|
36539
|
+
/* @__PURE__ */ jsx(Button, { variant: "primary", size: "sm", onClick: emitWithId(doneEvent), "data-testid": "document-done", children: doneLabel ?? (t("documentPanel.done") || "Done") })
|
|
36540
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
36541
|
+
editEvent && /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", onClick: emitWithId(editEvent), "data-testid": "document-edit", children: [
|
|
36542
|
+
/* @__PURE__ */ jsx(Icon, { name: "edit", size: "xs", className: "mr-1" }),
|
|
36543
|
+
editLabel ?? (t("documentPanel.edit") || "Edit")
|
|
36544
|
+
] }),
|
|
36545
|
+
inlineActions.map((action, idx) => /* @__PURE__ */ jsxs(
|
|
36546
|
+
Button,
|
|
36553
36547
|
{
|
|
36554
|
-
|
|
36555
|
-
|
|
36556
|
-
|
|
36557
|
-
|
|
36558
|
-
|
|
36548
|
+
variant: "primary",
|
|
36549
|
+
size: "sm",
|
|
36550
|
+
onClick: () => fireAction(action),
|
|
36551
|
+
"data-testid": `action-${action.event}`,
|
|
36552
|
+
children: [
|
|
36553
|
+
action.icon && renderIconInput4(action.icon, { size: "xs", className: "mr-1" }),
|
|
36554
|
+
action.label
|
|
36555
|
+
]
|
|
36556
|
+
},
|
|
36557
|
+
idx
|
|
36558
|
+
)),
|
|
36559
|
+
menuActions.length > 0 && /* @__PURE__ */ jsx(
|
|
36560
|
+
Menu,
|
|
36561
|
+
{
|
|
36562
|
+
position: "bottom-end",
|
|
36563
|
+
trigger: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
|
|
36564
|
+
items: menuActions.map((action) => ({
|
|
36565
|
+
label: action.label,
|
|
36566
|
+
icon: action.icon,
|
|
36567
|
+
variant: action.variant === "danger" ? "danger" : "default",
|
|
36568
|
+
onClick: () => fireAction(action)
|
|
36569
|
+
}))
|
|
36559
36570
|
}
|
|
36560
|
-
)
|
|
36561
|
-
|
|
36562
|
-
|
|
36563
|
-
}
|
|
36564
|
-
|
|
36571
|
+
)
|
|
36572
|
+
] }) })
|
|
36573
|
+
] }),
|
|
36574
|
+
editing ? /* @__PURE__ */ jsx(RichTextEditor, { value, changeEvent: contentChangeEvent, placeholder }) : /* @__PURE__ */ jsx(
|
|
36575
|
+
Box,
|
|
36576
|
+
{
|
|
36577
|
+
onClick: emitWithId(editEvent),
|
|
36578
|
+
className: cn(
|
|
36579
|
+
"rounded-md px-1 -mx-1 min-h-[16rem]",
|
|
36580
|
+
editEvent && "cursor-text transition-colors hover:bg-muted/30"
|
|
36581
|
+
),
|
|
36582
|
+
"data-testid": "document-body",
|
|
36583
|
+
children: value && value !== "" ? /* @__PURE__ */ jsx(RichTextEditor, { value, readOnly: true }) : /* @__PURE__ */ jsx(Typography, { variant: "body", color: "muted", children: placeholder ?? (t("documentPanel.placeholder") || "") })
|
|
36584
|
+
}
|
|
36585
|
+
)
|
|
36586
|
+
] }) });
|
|
36565
36587
|
}
|
|
36566
|
-
var
|
|
36567
|
-
|
|
36568
|
-
"components/core/molecules/RichBlockEditor.tsx"() {
|
|
36588
|
+
var init_DocumentPanel = __esm({
|
|
36589
|
+
"components/core/molecules/DocumentPanel.tsx"() {
|
|
36569
36590
|
"use client";
|
|
36570
36591
|
init_cn();
|
|
36571
|
-
|
|
36592
|
+
init_useEventBus();
|
|
36593
|
+
init_Box();
|
|
36594
|
+
init_Stack();
|
|
36572
36595
|
init_Typography();
|
|
36573
36596
|
init_Button();
|
|
36574
|
-
|
|
36575
|
-
init_Divider();
|
|
36576
|
-
init_Input();
|
|
36597
|
+
init_Card();
|
|
36577
36598
|
init_Icon();
|
|
36578
|
-
|
|
36579
|
-
|
|
36580
|
-
|
|
36581
|
-
|
|
36582
|
-
|
|
36583
|
-
|
|
36584
|
-
|
|
36585
|
-
|
|
36586
|
-
|
|
36587
|
-
|
|
36588
|
-
|
|
36589
|
-
|
|
36590
|
-
|
|
36591
|
-
|
|
36592
|
-
|
|
36593
|
-
|
|
36594
|
-
|
|
36595
|
-
|
|
36596
|
-
|
|
36597
|
-
|
|
36598
|
-
|
|
36599
|
-
|
|
36600
|
-
|
|
36601
|
-
|
|
36602
|
-
|
|
36603
|
-
|
|
36604
|
-
|
|
36605
|
-
|
|
36606
|
-
|
|
36607
|
-
|
|
36608
|
-
|
|
36609
|
-
|
|
36610
|
-
|
|
36611
|
-
|
|
36612
|
-
|
|
36613
|
-
|
|
36614
|
-
|
|
36615
|
-
|
|
36616
|
-
|
|
36617
|
-
|
|
36618
|
-
|
|
36619
|
-
|
|
36620
|
-
|
|
36621
|
-
|
|
36622
|
-
|
|
36623
|
-
|
|
36624
|
-
|
|
36625
|
-
|
|
36626
|
-
|
|
36627
|
-
|
|
36628
|
-
|
|
36629
|
-
|
|
36630
|
-
|
|
36631
|
-
|
|
36632
|
-
|
|
36633
|
-
|
|
36634
|
-
|
|
36635
|
-
|
|
36636
|
-
|
|
36637
|
-
const [blocks, setBlocks] = useState(
|
|
36638
|
-
() => normalizeBlocks(initialBlocks)
|
|
36639
|
-
);
|
|
36640
|
-
const onChangeRef = useRef(onChange);
|
|
36641
|
-
useEffect(() => {
|
|
36642
|
-
onChangeRef.current = onChange;
|
|
36643
|
-
}, [onChange]);
|
|
36644
|
-
const eventBus = useEventBus();
|
|
36645
|
-
const changeEventRef = useRef(changeEvent);
|
|
36646
|
-
useEffect(() => {
|
|
36647
|
-
changeEventRef.current = changeEvent;
|
|
36648
|
-
}, [changeEvent]);
|
|
36649
|
-
const commit = useCallback((next) => {
|
|
36650
|
-
setBlocks(next);
|
|
36651
|
-
onChangeRef.current?.(next);
|
|
36652
|
-
const evt = changeEventRef.current;
|
|
36653
|
-
if (evt) eventBus.emit(`UI:${evt}`, { blocks: next });
|
|
36654
|
-
}, [eventBus]);
|
|
36655
|
-
const handleAppend = useCallback(
|
|
36656
|
-
(type) => {
|
|
36657
|
-
if (readOnly) return;
|
|
36658
|
-
commit([...blocks, createBlock(type)]);
|
|
36659
|
-
},
|
|
36660
|
-
[blocks, commit, readOnly]
|
|
36661
|
-
);
|
|
36662
|
-
const handleUpdate = useCallback(
|
|
36663
|
-
(id, updater) => {
|
|
36664
|
-
commit(replaceBlock(blocks, id, updater));
|
|
36665
|
-
},
|
|
36666
|
-
[blocks, commit]
|
|
36667
|
-
);
|
|
36668
|
-
const handleDelete = useCallback(
|
|
36669
|
-
(id) => {
|
|
36670
|
-
const next = removeBlock(blocks, id);
|
|
36671
|
-
commit(next.length > 0 ? next : [createBlock("paragraph")]);
|
|
36672
|
-
},
|
|
36673
|
-
[blocks, commit]
|
|
36674
|
-
);
|
|
36675
|
-
const handleDuplicate = useCallback(
|
|
36676
|
-
(id) => {
|
|
36677
|
-
const target = blocks.find((b) => b.id === id);
|
|
36678
|
-
if (!target) return;
|
|
36679
|
-
commit(insertAfter(blocks, id, duplicateBlock(target)));
|
|
36680
|
-
},
|
|
36681
|
-
[blocks, commit]
|
|
36599
|
+
init_Input();
|
|
36600
|
+
init_Menu();
|
|
36601
|
+
init_RichTextEditor();
|
|
36602
|
+
}
|
|
36603
|
+
});
|
|
36604
|
+
function renderIconInput5(icon, props) {
|
|
36605
|
+
return typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsx(Icon, { icon, ...props });
|
|
36606
|
+
}
|
|
36607
|
+
function fieldName(field) {
|
|
36608
|
+
return field.name ?? field.key ?? "";
|
|
36609
|
+
}
|
|
36610
|
+
function relationLabel(value) {
|
|
36611
|
+
if (value === null || typeof value !== "object" || Array.isArray(value) || value instanceof Date) return null;
|
|
36612
|
+
for (const key of ["name", "title", "label"]) {
|
|
36613
|
+
const candidate = value[key];
|
|
36614
|
+
if (typeof candidate === "string" && candidate !== "") return candidate;
|
|
36615
|
+
}
|
|
36616
|
+
const id = value.id;
|
|
36617
|
+
return id !== void 0 && id !== null ? String(id) : null;
|
|
36618
|
+
}
|
|
36619
|
+
function fieldKind(field, value) {
|
|
36620
|
+
if (field.kind) return field.kind;
|
|
36621
|
+
if (field.options && field.options.length > 0) return "select";
|
|
36622
|
+
if (typeof value === "boolean" || field.format === "boolean") return "boolean";
|
|
36623
|
+
if (Array.isArray(value) || relationLabel(value ?? null) !== null) return "readonly";
|
|
36624
|
+
return "text";
|
|
36625
|
+
}
|
|
36626
|
+
function DocumentDetails({
|
|
36627
|
+
entity,
|
|
36628
|
+
fields,
|
|
36629
|
+
metaCommitEvent,
|
|
36630
|
+
title,
|
|
36631
|
+
className
|
|
36632
|
+
}) {
|
|
36633
|
+
const eventBus = useEventBus();
|
|
36634
|
+
const { t } = useTranslate();
|
|
36635
|
+
const [fieldDraft, setFieldDraft] = useState(null);
|
|
36636
|
+
const recordId = entity?.id !== void 0 && entity?.id !== null ? String(entity.id) : "";
|
|
36637
|
+
const fieldDefs = (fields ?? []).filter((f3) => fieldName(f3) !== "");
|
|
36638
|
+
if (fieldDefs.length === 0) return null;
|
|
36639
|
+
const commitField = (name, next, current) => {
|
|
36640
|
+
setFieldDraft(null);
|
|
36641
|
+
if (!metaCommitEvent || next === current) return;
|
|
36642
|
+
eventBus.emit(`UI:${metaCommitEvent}`, { id: recordId, patch: { id: recordId, [name]: next } });
|
|
36643
|
+
};
|
|
36644
|
+
const renderValue = (field) => {
|
|
36645
|
+
const name = fieldName(field);
|
|
36646
|
+
const raw = getNestedValue(entity ?? {}, name);
|
|
36647
|
+
const kind = fieldKind(field, raw);
|
|
36648
|
+
const label = field.label ?? field.header ?? humanizeFieldName(name);
|
|
36649
|
+
if (kind === "boolean") {
|
|
36650
|
+
return /* @__PURE__ */ jsx(
|
|
36651
|
+
Switch,
|
|
36652
|
+
{
|
|
36653
|
+
checked: Boolean(raw),
|
|
36654
|
+
disabled: !metaCommitEvent,
|
|
36655
|
+
"aria-label": label,
|
|
36656
|
+
onChange: (checked) => commitField(name, checked, Boolean(raw))
|
|
36657
|
+
}
|
|
36682
36658
|
);
|
|
36683
|
-
|
|
36684
|
-
|
|
36685
|
-
|
|
36686
|
-
|
|
36687
|
-
|
|
36688
|
-
|
|
36689
|
-
|
|
36659
|
+
}
|
|
36660
|
+
if (kind === "select") {
|
|
36661
|
+
return /* @__PURE__ */ jsx(
|
|
36662
|
+
Select,
|
|
36663
|
+
{
|
|
36664
|
+
value: raw !== void 0 && raw !== null ? String(raw) : "",
|
|
36665
|
+
disabled: !metaCommitEvent,
|
|
36666
|
+
"aria-label": label,
|
|
36667
|
+
className: "h-8 w-full",
|
|
36668
|
+
options: (field.options ?? []).map((opt) => ({ value: opt, label: humanizeEnumValue(opt) })),
|
|
36669
|
+
onChange: (e) => commitField(name, e.target.value, raw)
|
|
36670
|
+
}
|
|
36690
36671
|
);
|
|
36691
|
-
|
|
36692
|
-
|
|
36672
|
+
}
|
|
36673
|
+
if (kind === "readonly" || !metaCommitEvent) {
|
|
36674
|
+
if (Array.isArray(raw)) {
|
|
36675
|
+
if (raw.length === 0) {
|
|
36676
|
+
return /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", children: "\u2014" });
|
|
36677
|
+
}
|
|
36678
|
+
return /* @__PURE__ */ jsx(HStack, { gap: "xs", className: "flex-wrap", children: raw.map((item, i) => /* @__PURE__ */ jsx(Badge, { variant: "default", children: relationLabel(item) ?? humanizeEnumValue(String(item)) }, i)) });
|
|
36679
|
+
}
|
|
36680
|
+
const shown2 = raw === void 0 || raw === null || raw === "" ? "\u2014" : relationLabel(raw) ?? formatValue(raw, field.format);
|
|
36681
|
+
return /* @__PURE__ */ jsx(Typography, { variant: "small", className: "break-words", children: shown2 });
|
|
36682
|
+
}
|
|
36683
|
+
if (fieldDraft?.name === name) {
|
|
36684
|
+
return /* @__PURE__ */ jsx(
|
|
36685
|
+
Input,
|
|
36693
36686
|
{
|
|
36694
|
-
|
|
36695
|
-
|
|
36696
|
-
|
|
36697
|
-
|
|
36698
|
-
|
|
36699
|
-
|
|
36700
|
-
|
|
36701
|
-
|
|
36702
|
-
|
|
36703
|
-
|
|
36704
|
-
|
|
36705
|
-
|
|
36706
|
-
|
|
36707
|
-
|
|
36708
|
-
|
|
36709
|
-
|
|
36710
|
-
return /* @__PURE__ */ jsxs(
|
|
36711
|
-
Button,
|
|
36712
|
-
{
|
|
36713
|
-
type: "button",
|
|
36714
|
-
variant: "ghost",
|
|
36715
|
-
size: "sm",
|
|
36716
|
-
"aria-label": t("richBlockEditor.insertEntry", { label: entryLabel }),
|
|
36717
|
-
title: entryLabel,
|
|
36718
|
-
onClick: () => handleAppend(entry.type),
|
|
36719
|
-
children: [
|
|
36720
|
-
/* @__PURE__ */ jsx(Icon2, { size: 14 }),
|
|
36721
|
-
/* @__PURE__ */ jsx(Typography, { as: "span", variant: "caption", className: "ml-1 hidden text-xs sm:inline", children: entryLabel })
|
|
36722
|
-
]
|
|
36723
|
-
},
|
|
36724
|
-
entry.type
|
|
36725
|
-
);
|
|
36726
|
-
})
|
|
36727
|
-
}
|
|
36728
|
-
),
|
|
36729
|
-
/* @__PURE__ */ jsx(Box, { className: "flex flex-col gap-1 px-3 py-3", children: blocks.map((block) => /* @__PURE__ */ jsx(
|
|
36730
|
-
BlockRow,
|
|
36731
|
-
{
|
|
36732
|
-
block,
|
|
36733
|
-
readOnly,
|
|
36734
|
-
showAffordances: enableBlocks,
|
|
36735
|
-
placeholder,
|
|
36736
|
-
onUpdate: (updater) => handleUpdate(block.id, updater),
|
|
36737
|
-
onDelete: () => handleDelete(block.id),
|
|
36738
|
-
onDuplicate: () => handleDuplicate(block.id),
|
|
36739
|
-
onChangeType: (type) => handleChangeType(block.id, type)
|
|
36740
|
-
},
|
|
36741
|
-
block.id
|
|
36742
|
-
)) })
|
|
36743
|
-
]
|
|
36687
|
+
value: fieldDraft.value,
|
|
36688
|
+
autoFocus: true,
|
|
36689
|
+
"aria-label": label,
|
|
36690
|
+
className: "h-8 w-full",
|
|
36691
|
+
onChange: (e) => setFieldDraft({ name, value: e.target.value }),
|
|
36692
|
+
onBlur: () => commitField(name, fieldDraft.value.trim(), raw),
|
|
36693
|
+
onKeyDown: (e) => {
|
|
36694
|
+
if (e.key === "Enter") {
|
|
36695
|
+
e.preventDefault();
|
|
36696
|
+
commitField(name, fieldDraft.value.trim(), raw);
|
|
36697
|
+
} else if (e.key === "Escape") {
|
|
36698
|
+
e.preventDefault();
|
|
36699
|
+
setFieldDraft(null);
|
|
36700
|
+
}
|
|
36701
|
+
},
|
|
36702
|
+
"data-testid": `document-property-input-${name}`
|
|
36744
36703
|
}
|
|
36745
36704
|
);
|
|
36746
|
-
}
|
|
36747
|
-
|
|
36705
|
+
}
|
|
36706
|
+
const shown = raw === void 0 || raw === null || raw === "" ? "\u2014" : relationLabel(raw) ?? formatValue(raw, field.format);
|
|
36707
|
+
return /* @__PURE__ */ jsx(
|
|
36708
|
+
Box,
|
|
36709
|
+
{
|
|
36710
|
+
role: "button",
|
|
36711
|
+
tabIndex: 0,
|
|
36712
|
+
className: cn(
|
|
36713
|
+
"cursor-text rounded px-1 -mx-1 transition-colors hover:bg-muted/40",
|
|
36714
|
+
(raw === void 0 || raw === null || raw === "") && "text-muted-foreground"
|
|
36715
|
+
),
|
|
36716
|
+
onClick: () => setFieldDraft({ name, value: raw !== void 0 && raw !== null ? String(raw) : "" }),
|
|
36717
|
+
onKeyDown: (e) => {
|
|
36718
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
36719
|
+
e.preventDefault();
|
|
36720
|
+
setFieldDraft({ name, value: raw !== void 0 && raw !== null ? String(raw) : "" });
|
|
36721
|
+
}
|
|
36722
|
+
},
|
|
36723
|
+
"data-testid": `document-property-${name}`,
|
|
36724
|
+
children: /* @__PURE__ */ jsx(Typography, { variant: "small", className: "break-words", children: shown })
|
|
36725
|
+
}
|
|
36726
|
+
);
|
|
36727
|
+
};
|
|
36728
|
+
return /* @__PURE__ */ jsx(Card, { variant: "bordered", className: cn("w-full", className), children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", className: "p-4", children: [
|
|
36729
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", weight: "medium", className: "uppercase tracking-wide", children: title ?? (t("documentDetails.title") || "Details") }),
|
|
36730
|
+
/* @__PURE__ */ jsx(VStack, { gap: "sm", children: fieldDefs.map((field) => {
|
|
36731
|
+
const name = fieldName(field);
|
|
36732
|
+
const label = field.label ?? field.header ?? humanizeFieldName(name);
|
|
36733
|
+
return /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
|
|
36734
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
|
|
36735
|
+
field.icon && renderIconInput5(field.icon, { size: "xs", className: "text-muted-foreground" }),
|
|
36736
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", children: label })
|
|
36737
|
+
] }),
|
|
36738
|
+
/* @__PURE__ */ jsx(Box, { className: "min-w-0", children: renderValue(field) })
|
|
36739
|
+
] }, name);
|
|
36740
|
+
}) })
|
|
36741
|
+
] }) });
|
|
36742
|
+
}
|
|
36743
|
+
var init_DocumentDetails = __esm({
|
|
36744
|
+
"components/core/molecules/DocumentDetails.tsx"() {
|
|
36745
|
+
"use client";
|
|
36746
|
+
init_cn();
|
|
36747
|
+
init_format();
|
|
36748
|
+
init_getNestedValue();
|
|
36749
|
+
init_useEventBus();
|
|
36750
|
+
init_Box();
|
|
36751
|
+
init_Stack();
|
|
36752
|
+
init_Typography();
|
|
36753
|
+
init_Badge();
|
|
36754
|
+
init_Card();
|
|
36755
|
+
init_Icon();
|
|
36756
|
+
init_Input();
|
|
36757
|
+
init_Select();
|
|
36758
|
+
init_Switch();
|
|
36748
36759
|
}
|
|
36749
36760
|
});
|
|
36750
36761
|
function collectInitiallyCollapsed(nodes, acc) {
|
|
@@ -41693,8 +41704,8 @@ var init_DataTable = __esm({
|
|
|
41693
41704
|
DataTable.displayName = "DataTable";
|
|
41694
41705
|
}
|
|
41695
41706
|
});
|
|
41696
|
-
function getBadgeVariant(
|
|
41697
|
-
const name =
|
|
41707
|
+
function getBadgeVariant(fieldName2, value) {
|
|
41708
|
+
const name = fieldName2.toLowerCase();
|
|
41698
41709
|
const val = String(value).toLowerCase();
|
|
41699
41710
|
if (name.includes("status")) {
|
|
41700
41711
|
if (val.includes("complete") || val.includes("done") || val.includes("active"))
|
|
@@ -41710,12 +41721,12 @@ function getBadgeVariant(fieldName, value) {
|
|
|
41710
41721
|
}
|
|
41711
41722
|
return "default";
|
|
41712
41723
|
}
|
|
41713
|
-
function formatFieldValue2(value,
|
|
41724
|
+
function formatFieldValue2(value, fieldName2) {
|
|
41714
41725
|
if (typeof value === "number") {
|
|
41715
|
-
if (
|
|
41726
|
+
if (fieldName2.toLowerCase().includes("progress") || fieldName2.toLowerCase().includes("percent")) {
|
|
41716
41727
|
return `${value}%`;
|
|
41717
41728
|
}
|
|
41718
|
-
if (
|
|
41729
|
+
if (fieldName2.toLowerCase().includes("budget") || fieldName2.toLowerCase().includes("cost")) {
|
|
41719
41730
|
return `$${value.toLocaleString()}`;
|
|
41720
41731
|
}
|
|
41721
41732
|
return value.toLocaleString();
|
|
@@ -41725,7 +41736,7 @@ function formatFieldValue2(value, fieldName) {
|
|
|
41725
41736
|
}
|
|
41726
41737
|
return String(value);
|
|
41727
41738
|
}
|
|
41728
|
-
function renderRichFieldValue(value,
|
|
41739
|
+
function renderRichFieldValue(value, fieldName2, fieldType, meta) {
|
|
41729
41740
|
if (value === void 0 || value === null) return "\u2014";
|
|
41730
41741
|
const str = String(value);
|
|
41731
41742
|
switch (fieldType) {
|
|
@@ -41736,7 +41747,7 @@ function renderRichFieldValue(value, fieldName, fieldType, meta) {
|
|
|
41736
41747
|
"img",
|
|
41737
41748
|
{
|
|
41738
41749
|
src: str,
|
|
41739
|
-
alt: formatFieldLabel(
|
|
41750
|
+
alt: formatFieldLabel(fieldName2),
|
|
41740
41751
|
className: "max-w-full max-h-64 rounded-md object-contain",
|
|
41741
41752
|
loading: "lazy"
|
|
41742
41753
|
}
|
|
@@ -41871,9 +41882,9 @@ function renderRichFieldValue(value, fieldName, fieldType, meta) {
|
|
|
41871
41882
|
return /* @__PURE__ */ jsx("a", { href: `tel:${str}`, className: "text-primary hover:underline", children: str });
|
|
41872
41883
|
default:
|
|
41873
41884
|
if (meta?.values && meta.values.length > 0 && meta.values.includes(str)) {
|
|
41874
|
-
return /* @__PURE__ */ jsx(Badge, { variant: getBadgeVariant(
|
|
41885
|
+
return /* @__PURE__ */ jsx(Badge, { variant: getBadgeVariant(fieldName2, str), children: humanizeEnumValue(str) });
|
|
41875
41886
|
}
|
|
41876
|
-
return formatFieldValue2(value,
|
|
41887
|
+
return formatFieldValue2(value, fieldName2);
|
|
41877
41888
|
}
|
|
41878
41889
|
}
|
|
41879
41890
|
function normalizeFieldDefs(fields) {
|
|
@@ -42716,7 +42727,21 @@ var init_Form = __esm({
|
|
|
42716
42727
|
const normalizedInitialData = React85__default.useMemo(() => {
|
|
42717
42728
|
const entityRowAsInitial = isPlainEntityRow(entity) ? entity : void 0;
|
|
42718
42729
|
const callerInitial = initialData !== null && typeof initialData === "object" && !Array.isArray(initialData) ? initialData : {};
|
|
42719
|
-
|
|
42730
|
+
const merged = entityRowAsInitial !== void 0 ? { ...entityRowAsInitial, ...callerInitial } : callerInitial;
|
|
42731
|
+
const toId = (value) => {
|
|
42732
|
+
if (value === null || value === void 0 || typeof value !== "object" || value instanceof Date) return value;
|
|
42733
|
+
if (Array.isArray(value)) {
|
|
42734
|
+
return value.map(
|
|
42735
|
+
(item) => item !== null && typeof item === "object" && !Array.isArray(item) && !(item instanceof Date) && item.id !== void 0 && item.id !== null ? String(item.id) : item
|
|
42736
|
+
);
|
|
42737
|
+
}
|
|
42738
|
+
return value.id !== void 0 && value.id !== null ? String(value.id) : value;
|
|
42739
|
+
};
|
|
42740
|
+
const normalized = {};
|
|
42741
|
+
for (const [key, value] of Object.entries(merged)) {
|
|
42742
|
+
normalized[key] = key === "id" ? value : toId(value);
|
|
42743
|
+
}
|
|
42744
|
+
return normalized;
|
|
42720
42745
|
}, [entity, initialData]);
|
|
42721
42746
|
const entityDerivedFields = React85__default.useMemo(() => {
|
|
42722
42747
|
if (fields && fields.length > 0) return void 0;
|
|
@@ -42845,8 +42870,8 @@ var init_Form = __esm({
|
|
|
42845
42870
|
checkViolations(name, newFormData);
|
|
42846
42871
|
};
|
|
42847
42872
|
const isFieldVisible = React85__default.useCallback(
|
|
42848
|
-
(
|
|
42849
|
-
const condition = conditionalFields[
|
|
42873
|
+
(fieldName2) => {
|
|
42874
|
+
const condition = conditionalFields[fieldName2];
|
|
42850
42875
|
if (!condition) return true;
|
|
42851
42876
|
return Boolean(evaluateFormExpression(condition, evalContext));
|
|
42852
42877
|
},
|
|
@@ -42892,9 +42917,9 @@ var init_Form = __esm({
|
|
|
42892
42917
|
};
|
|
42893
42918
|
const handleInvalid = (e) => {
|
|
42894
42919
|
const target = e.target;
|
|
42895
|
-
const
|
|
42920
|
+
const fieldName2 = target.getAttribute("data-field-name") ?? target.name ?? "";
|
|
42896
42921
|
const fieldMessage = target.validationMessage || "Invalid value";
|
|
42897
|
-
debug("forms", "invalid", { mode: formMode, fieldName, fieldMessage });
|
|
42922
|
+
debug("forms", "invalid", { mode: formMode, fieldName: fieldName2, fieldMessage });
|
|
42898
42923
|
queueMicrotask(() => {
|
|
42899
42924
|
const form = formRef.current;
|
|
42900
42925
|
if (!form) return;
|
|
@@ -42930,22 +42955,22 @@ var init_Form = __esm({
|
|
|
42930
42955
|
};
|
|
42931
42956
|
const renderField = React85__default.useCallback(
|
|
42932
42957
|
(field) => {
|
|
42933
|
-
const
|
|
42934
|
-
if (!
|
|
42935
|
-
if (!isFieldVisible(
|
|
42958
|
+
const fieldName2 = field.name || field.field;
|
|
42959
|
+
if (!fieldName2) return null;
|
|
42960
|
+
if (!isFieldVisible(fieldName2)) {
|
|
42936
42961
|
return null;
|
|
42937
42962
|
}
|
|
42938
42963
|
const inputType = determineInputType(field);
|
|
42939
|
-
const label = field.label ||
|
|
42940
|
-
const currentValue = formData[
|
|
42941
|
-
return /* @__PURE__ */ jsxs(VStack, { gap: "xs", "data-field":
|
|
42964
|
+
const label = field.label || fieldName2.charAt(0).toUpperCase() + fieldName2.slice(1).replace(/([A-Z])/g, " $1");
|
|
42965
|
+
const currentValue = formData[fieldName2] ?? field.defaultValue ?? "";
|
|
42966
|
+
return /* @__PURE__ */ jsxs(VStack, { gap: "xs", "data-field": fieldName2, children: [
|
|
42942
42967
|
inputType !== "checkbox" && /* @__PURE__ */ jsxs(Typography, { as: "label", variant: "label", weight: "bold", children: [
|
|
42943
42968
|
label,
|
|
42944
42969
|
field.required && /* @__PURE__ */ jsx(Typography, { as: "span", color: "error", className: "ml-1", children: "*" })
|
|
42945
42970
|
] }),
|
|
42946
|
-
renderFieldInput(field,
|
|
42971
|
+
renderFieldInput(field, fieldName2, inputType, currentValue, label),
|
|
42947
42972
|
field.hint && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", children: field.hint })
|
|
42948
|
-
] },
|
|
42973
|
+
] }, fieldName2);
|
|
42949
42974
|
},
|
|
42950
42975
|
[formData, isFieldVisible, relationsData, relationsLoading, isLoading]
|
|
42951
42976
|
);
|
|
@@ -42974,8 +42999,8 @@ var init_Form = __esm({
|
|
|
42974
42999
|
}
|
|
42975
43000
|
return field;
|
|
42976
43001
|
}).map((field) => {
|
|
42977
|
-
const
|
|
42978
|
-
const override = fieldOverrides?.find((o) => o.name ===
|
|
43002
|
+
const fieldName2 = field.name || field.field;
|
|
43003
|
+
const override = fieldOverrides?.find((o) => o.name === fieldName2);
|
|
42979
43004
|
if (!override) return field;
|
|
42980
43005
|
return {
|
|
42981
43006
|
...field,
|
|
@@ -43031,11 +43056,11 @@ var init_Form = __esm({
|
|
|
43031
43056
|
] }, section.id);
|
|
43032
43057
|
}).filter(Boolean);
|
|
43033
43058
|
}, [sections, isSectionVisible, collapsedSections, renderField, gap]);
|
|
43034
|
-
function renderFieldInput(field,
|
|
43059
|
+
function renderFieldInput(field, fieldName2, inputType, currentValue, label) {
|
|
43035
43060
|
const commonProps = {
|
|
43036
|
-
id:
|
|
43037
|
-
name:
|
|
43038
|
-
"data-field-name":
|
|
43061
|
+
id: fieldName2,
|
|
43062
|
+
name: fieldName2,
|
|
43063
|
+
"data-field-name": fieldName2,
|
|
43039
43064
|
required: field.required,
|
|
43040
43065
|
disabled: isLoading,
|
|
43041
43066
|
placeholder: field.placeholder,
|
|
@@ -43049,7 +43074,7 @@ var init_Form = __esm({
|
|
|
43049
43074
|
...commonProps,
|
|
43050
43075
|
label: label + (field.required ? " *" : ""),
|
|
43051
43076
|
checked: Boolean(currentValue),
|
|
43052
|
-
onChange: (e) => handleChange(
|
|
43077
|
+
onChange: (e) => handleChange(fieldName2, e.target.checked)
|
|
43053
43078
|
}
|
|
43054
43079
|
);
|
|
43055
43080
|
case "textarea":
|
|
@@ -43058,7 +43083,7 @@ var init_Form = __esm({
|
|
|
43058
43083
|
{
|
|
43059
43084
|
...commonProps,
|
|
43060
43085
|
value: String(currentValue),
|
|
43061
|
-
onChange: (e) => handleChange(
|
|
43086
|
+
onChange: (e) => handleChange(fieldName2, e.target.value),
|
|
43062
43087
|
minLength: field.min,
|
|
43063
43088
|
maxLength: field.max
|
|
43064
43089
|
}
|
|
@@ -43071,14 +43096,14 @@ var init_Form = __esm({
|
|
|
43071
43096
|
...commonProps,
|
|
43072
43097
|
options,
|
|
43073
43098
|
value: String(currentValue),
|
|
43074
|
-
onValueChange: (v) => handleChange(
|
|
43099
|
+
onValueChange: (v) => handleChange(fieldName2, v),
|
|
43075
43100
|
placeholder: field.placeholder || `Select ${label}...`
|
|
43076
43101
|
}
|
|
43077
43102
|
);
|
|
43078
43103
|
}
|
|
43079
43104
|
case "relation": {
|
|
43080
|
-
const relationOptions = relationsData[
|
|
43081
|
-
const relationLoading = relationsLoading[
|
|
43105
|
+
const relationOptions = relationsData[fieldName2] || [];
|
|
43106
|
+
const relationLoading = relationsLoading[fieldName2] || false;
|
|
43082
43107
|
if (field.relation?.cardinality !== void 0 && MANY_CARDINALITIES.includes(field.relation.cardinality)) {
|
|
43083
43108
|
const selectedValues = Array.isArray(currentValue) ? currentValue.map((v) => String(v)) : [];
|
|
43084
43109
|
return /* @__PURE__ */ jsx(
|
|
@@ -43090,7 +43115,7 @@ var init_Form = __esm({
|
|
|
43090
43115
|
clearable: true,
|
|
43091
43116
|
options: [...relationOptions],
|
|
43092
43117
|
value: selectedValues,
|
|
43093
|
-
onValueChange: (value) => handleChange(
|
|
43118
|
+
onValueChange: (value) => handleChange(fieldName2, Array.isArray(value) ? value : [value]),
|
|
43094
43119
|
placeholder: field.placeholder || `Select ${label}...`
|
|
43095
43120
|
}
|
|
43096
43121
|
);
|
|
@@ -43100,7 +43125,7 @@ var init_Form = __esm({
|
|
|
43100
43125
|
{
|
|
43101
43126
|
...commonProps,
|
|
43102
43127
|
value: currentValue ? String(currentValue) : void 0,
|
|
43103
|
-
onChange: (value) => handleChange(
|
|
43128
|
+
onChange: (value) => handleChange(fieldName2, value),
|
|
43104
43129
|
options: relationOptions,
|
|
43105
43130
|
isLoading: relationLoading,
|
|
43106
43131
|
placeholder: field.placeholder || `Select ${label}...`,
|
|
@@ -43117,7 +43142,7 @@ var init_Form = __esm({
|
|
|
43117
43142
|
placeholder: field.placeholder,
|
|
43118
43143
|
disabled: isLoading,
|
|
43119
43144
|
value: arrayValue,
|
|
43120
|
-
onChange: (next) => handleChange(
|
|
43145
|
+
onChange: (next) => handleChange(fieldName2, [...next])
|
|
43121
43146
|
}
|
|
43122
43147
|
);
|
|
43123
43148
|
}
|
|
@@ -43129,7 +43154,7 @@ var init_Form = __esm({
|
|
|
43129
43154
|
type: "number",
|
|
43130
43155
|
value: currentValue !== void 0 && currentValue !== "" ? String(currentValue) : "",
|
|
43131
43156
|
onChange: (e) => handleChange(
|
|
43132
|
-
|
|
43157
|
+
fieldName2,
|
|
43133
43158
|
e.target.value ? Number(e.target.value) : void 0
|
|
43134
43159
|
),
|
|
43135
43160
|
min: field.min,
|
|
@@ -43146,7 +43171,7 @@ var init_Form = __esm({
|
|
|
43146
43171
|
icon: DollarSign,
|
|
43147
43172
|
value: currentValue !== void 0 && currentValue !== "" ? String(currentValue) : "",
|
|
43148
43173
|
onChange: (e) => handleChange(
|
|
43149
|
-
|
|
43174
|
+
fieldName2,
|
|
43150
43175
|
e.target.value ? Number(e.target.value) : void 0
|
|
43151
43176
|
),
|
|
43152
43177
|
min: field.min,
|
|
@@ -43164,7 +43189,7 @@ var init_Form = __esm({
|
|
|
43164
43189
|
const f3 = files[0];
|
|
43165
43190
|
if (!f3) return;
|
|
43166
43191
|
const reader = new FileReader();
|
|
43167
|
-
reader.onload = () => handleChange(
|
|
43192
|
+
reader.onload = () => handleChange(fieldName2, {
|
|
43168
43193
|
name: f3.name,
|
|
43169
43194
|
mimeType: f3.type,
|
|
43170
43195
|
sizeBytes: f3.size,
|
|
@@ -43181,7 +43206,7 @@ var init_Form = __esm({
|
|
|
43181
43206
|
...commonProps,
|
|
43182
43207
|
type: "date",
|
|
43183
43208
|
value: formatDateValue(currentValue),
|
|
43184
|
-
onChange: (e) => handleChange(
|
|
43209
|
+
onChange: (e) => handleChange(fieldName2, e.target.value)
|
|
43185
43210
|
}
|
|
43186
43211
|
);
|
|
43187
43212
|
case "datetime-local":
|
|
@@ -43191,7 +43216,7 @@ var init_Form = __esm({
|
|
|
43191
43216
|
...commonProps,
|
|
43192
43217
|
type: "datetime-local",
|
|
43193
43218
|
value: formatDateTimeValue(currentValue),
|
|
43194
|
-
onChange: (e) => handleChange(
|
|
43219
|
+
onChange: (e) => handleChange(fieldName2, e.target.value)
|
|
43195
43220
|
}
|
|
43196
43221
|
);
|
|
43197
43222
|
case "email":
|
|
@@ -43201,7 +43226,7 @@ var init_Form = __esm({
|
|
|
43201
43226
|
...commonProps,
|
|
43202
43227
|
type: "email",
|
|
43203
43228
|
value: String(currentValue),
|
|
43204
|
-
onChange: (e) => handleChange(
|
|
43229
|
+
onChange: (e) => handleChange(fieldName2, e.target.value),
|
|
43205
43230
|
minLength: field.min,
|
|
43206
43231
|
maxLength: field.max
|
|
43207
43232
|
}
|
|
@@ -43213,7 +43238,7 @@ var init_Form = __esm({
|
|
|
43213
43238
|
...commonProps,
|
|
43214
43239
|
type: "url",
|
|
43215
43240
|
value: String(currentValue),
|
|
43216
|
-
onChange: (e) => handleChange(
|
|
43241
|
+
onChange: (e) => handleChange(fieldName2, e.target.value),
|
|
43217
43242
|
minLength: field.min,
|
|
43218
43243
|
maxLength: field.max
|
|
43219
43244
|
}
|
|
@@ -43225,7 +43250,7 @@ var init_Form = __esm({
|
|
|
43225
43250
|
...commonProps,
|
|
43226
43251
|
type: "password",
|
|
43227
43252
|
value: String(currentValue),
|
|
43228
|
-
onChange: (e) => handleChange(
|
|
43253
|
+
onChange: (e) => handleChange(fieldName2, e.target.value),
|
|
43229
43254
|
minLength: field.min,
|
|
43230
43255
|
maxLength: field.max
|
|
43231
43256
|
}
|
|
@@ -43238,7 +43263,7 @@ var init_Form = __esm({
|
|
|
43238
43263
|
...commonProps,
|
|
43239
43264
|
type: "text",
|
|
43240
43265
|
value: String(currentValue),
|
|
43241
|
-
onChange: (e) => handleChange(
|
|
43266
|
+
onChange: (e) => handleChange(fieldName2, e.target.value),
|
|
43242
43267
|
minLength: field.min,
|
|
43243
43268
|
maxLength: field.max
|
|
43244
43269
|
}
|
|
@@ -43595,7 +43620,7 @@ function entityFieldsFromListItem(item) {
|
|
|
43595
43620
|
}
|
|
43596
43621
|
return result;
|
|
43597
43622
|
}
|
|
43598
|
-
function getStatusStyle(
|
|
43623
|
+
function getStatusStyle(fieldName2, value) {
|
|
43599
43624
|
const val = String(value).toLowerCase();
|
|
43600
43625
|
if (val.includes("complete") || val.includes("done"))
|
|
43601
43626
|
return STATUS_STYLES.complete;
|
|
@@ -43611,12 +43636,12 @@ function getStatusStyle(fieldName, value) {
|
|
|
43611
43636
|
if (val.includes("low")) return STATUS_STYLES.low;
|
|
43612
43637
|
return STATUS_STYLES.default;
|
|
43613
43638
|
}
|
|
43614
|
-
function formatValue3(value,
|
|
43639
|
+
function formatValue3(value, fieldName2) {
|
|
43615
43640
|
if (typeof value === "number") {
|
|
43616
|
-
if (
|
|
43641
|
+
if (fieldName2.toLowerCase().includes("progress") || fieldName2.toLowerCase().includes("percent")) {
|
|
43617
43642
|
return `${value}%`;
|
|
43618
43643
|
}
|
|
43619
|
-
if (
|
|
43644
|
+
if (fieldName2.toLowerCase().includes("budget") || fieldName2.toLowerCase().includes("cost")) {
|
|
43620
43645
|
return new Intl.NumberFormat("en-US", {
|
|
43621
43646
|
style: "currency",
|
|
43622
43647
|
currency: "USD",
|
|
@@ -43639,8 +43664,8 @@ function formatValue3(value, fieldName) {
|
|
|
43639
43664
|
}
|
|
43640
43665
|
return String(value);
|
|
43641
43666
|
}
|
|
43642
|
-
function formatFieldLabel2(
|
|
43643
|
-
return humanizeFieldName(
|
|
43667
|
+
function formatFieldLabel2(fieldName2) {
|
|
43668
|
+
return humanizeFieldName(fieldName2).replace(/\sId$/, "").trim();
|
|
43644
43669
|
}
|
|
43645
43670
|
var STATUS_STYLES, StatusBadge, ProgressIndicator, List3;
|
|
43646
43671
|
var init_List = __esm({
|
|
@@ -43708,9 +43733,9 @@ var init_List = __esm({
|
|
|
43708
43733
|
};
|
|
43709
43734
|
StatusBadge = ({
|
|
43710
43735
|
value,
|
|
43711
|
-
fieldName
|
|
43736
|
+
fieldName: fieldName2
|
|
43712
43737
|
}) => {
|
|
43713
|
-
const style = getStatusStyle(
|
|
43738
|
+
const style = getStatusStyle(fieldName2, value);
|
|
43714
43739
|
return /* @__PURE__ */ jsxs(
|
|
43715
43740
|
Typography,
|
|
43716
43741
|
{
|
|
@@ -46642,8 +46667,8 @@ var init_StatCard = __esm({
|
|
|
46642
46667
|
return items.length;
|
|
46643
46668
|
}
|
|
46644
46669
|
if (field.includes(":")) {
|
|
46645
|
-
const [
|
|
46646
|
-
return items.filter((item) => item[
|
|
46670
|
+
const [fieldName2, fieldValue] = field.split(":");
|
|
46671
|
+
return items.filter((item) => item[fieldName2] === fieldValue).length;
|
|
46647
46672
|
}
|
|
46648
46673
|
const fieldExistsOnItems = items.some((item) => field in item);
|
|
46649
46674
|
if (fieldExistsOnItems) {
|
|
@@ -48163,6 +48188,8 @@ var init_component_registry_generated = __esm({
|
|
|
48163
48188
|
init_DocSearch();
|
|
48164
48189
|
init_DocSidebar();
|
|
48165
48190
|
init_DocTOC();
|
|
48191
|
+
init_DocumentDetails();
|
|
48192
|
+
init_DocumentPanel();
|
|
48166
48193
|
init_DocumentViewer();
|
|
48167
48194
|
init_DrawFxLayer();
|
|
48168
48195
|
init_DrawGroup();
|
|
@@ -48272,7 +48299,7 @@ var init_component_registry_generated = __esm({
|
|
|
48272
48299
|
init_RelationSelect();
|
|
48273
48300
|
init_RepeatableFormSection();
|
|
48274
48301
|
init_ReplyTree();
|
|
48275
|
-
|
|
48302
|
+
init_RichTextEditor();
|
|
48276
48303
|
init_RuntimeDebugger2();
|
|
48277
48304
|
init_ScaledDiagram();
|
|
48278
48305
|
init_ScoreDisplay();
|
|
@@ -48435,6 +48462,8 @@ var init_component_registry_generated = __esm({
|
|
|
48435
48462
|
"DocSearch": DocSearch,
|
|
48436
48463
|
"DocSidebar": DocSidebar,
|
|
48437
48464
|
"DocTOC": DocTOC,
|
|
48465
|
+
"DocumentDetails": DocumentDetails,
|
|
48466
|
+
"DocumentPanel": DocumentPanel,
|
|
48438
48467
|
"DocumentViewer": DocumentViewer,
|
|
48439
48468
|
"DrawFxLayer": DrawFxLayer,
|
|
48440
48469
|
"DrawGroup": DrawGroup,
|
|
@@ -48546,7 +48575,7 @@ var init_component_registry_generated = __esm({
|
|
|
48546
48575
|
"RelationSelect": RelationSelect,
|
|
48547
48576
|
"RepeatableFormSection": RepeatableFormSection,
|
|
48548
48577
|
"ReplyTree": ReplyTree,
|
|
48549
|
-
"
|
|
48578
|
+
"RichTextEditor": RichTextEditor,
|
|
48550
48579
|
"RuntimeDebugger": RuntimeDebugger,
|
|
48551
48580
|
"ScaledDiagram": ScaledDiagram,
|
|
48552
48581
|
"ScoreDisplay": ScoreDisplay,
|
|
@@ -48700,10 +48729,10 @@ function enrichFormFields(fields, entityDef) {
|
|
|
48700
48729
|
}
|
|
48701
48730
|
if (field && typeof field === "object" && !Array.isArray(field) && !React85__default.isValidElement(field) && !(field instanceof Date)) {
|
|
48702
48731
|
const obj = field;
|
|
48703
|
-
const
|
|
48704
|
-
if (!
|
|
48732
|
+
const fieldName2 = typeof obj.name === "string" ? obj.name : typeof obj.field === "string" ? obj.field : void 0;
|
|
48733
|
+
if (!fieldName2) return field;
|
|
48705
48734
|
if (obj.type || obj.inputType) return field;
|
|
48706
|
-
const entityField = fieldMap.get(
|
|
48735
|
+
const entityField = fieldMap.get(fieldName2);
|
|
48707
48736
|
if (!entityField) return field;
|
|
48708
48737
|
const enriched = { ...obj, type: entityField.type };
|
|
48709
48738
|
if (entityField.required && !("required" in obj)) {
|
|
@@ -48753,9 +48782,9 @@ function enrichDetailFields(fields, entityDef) {
|
|
|
48753
48782
|
}
|
|
48754
48783
|
if (field && typeof field === "object" && !Array.isArray(field) && !React85__default.isValidElement(field) && !(field instanceof Date)) {
|
|
48755
48784
|
const obj = field;
|
|
48756
|
-
const
|
|
48757
|
-
if (!
|
|
48758
|
-
const meta = metaFor(
|
|
48785
|
+
const fieldName2 = typeof obj.key === "string" ? obj.key : typeof obj.name === "string" ? obj.name : void 0;
|
|
48786
|
+
if (!fieldName2 || obj.type) return field;
|
|
48787
|
+
const meta = metaFor(fieldName2);
|
|
48759
48788
|
return meta ? { ...obj, ...meta } : field;
|
|
48760
48789
|
}
|
|
48761
48790
|
return field;
|
|
@@ -49594,9 +49623,9 @@ function UISlotRenderer({
|
|
|
49594
49623
|
"ui-slot-renderer relative min-h-full",
|
|
49595
49624
|
className
|
|
49596
49625
|
), children: [
|
|
49597
|
-
/* @__PURE__ */ jsxs(Box, { className: "flex min-h-full", children: [
|
|
49598
|
-
/* @__PURE__ */ jsx(UISlotComponent, { slot: "sidebar", className: "ui-slot-sidebar min-w-0 shrink-0" }),
|
|
49599
|
-
/* @__PURE__ */ jsx(UISlotComponent, { slot: "main", className: "ui-slot-main flex-1 min-h-[200px]" })
|
|
49626
|
+
/* @__PURE__ */ jsxs(Box, { className: "flex min-h-full flex-col lg:flex-row", children: [
|
|
49627
|
+
/* @__PURE__ */ jsx(UISlotComponent, { slot: "sidebar", className: "ui-slot-sidebar min-w-0 lg:shrink-0" }),
|
|
49628
|
+
/* @__PURE__ */ jsx(UISlotComponent, { slot: "main", className: "ui-slot-main flex-1 min-w-0 min-h-[200px]" })
|
|
49600
49629
|
] }),
|
|
49601
49630
|
/* @__PURE__ */ jsx(UISlotComponent, { slot: "modal", portal: true }),
|
|
49602
49631
|
/* @__PURE__ */ jsx(UISlotComponent, { slot: "drawer", portal: true }),
|
|
@@ -50119,7 +50148,7 @@ function buildTraitRenderConfig(binding, traitConfigsByName, payload) {
|
|
|
50119
50148
|
}
|
|
50120
50149
|
return void 0;
|
|
50121
50150
|
}
|
|
50122
|
-
function createSharedEntityWriter(binding, tick, traitStatesRef, emit) {
|
|
50151
|
+
function createSharedEntityWriter(binding, tick, traitStatesRef, emit, traitConfigsByName) {
|
|
50123
50152
|
return (scratch) => {
|
|
50124
50153
|
const traitName = binding.trait.name;
|
|
50125
50154
|
const currentState = traitStatesRef.current.get(traitName)?.currentState ?? "";
|
|
@@ -50128,9 +50157,13 @@ function createSharedEntityWriter(binding, tick, traitStatesRef, emit) {
|
|
|
50128
50157
|
const writes = [];
|
|
50129
50158
|
const ctx = createMinimalContext(scratchEntity, {}, currentState);
|
|
50130
50159
|
const declaredDefaults = collectDeclaredConfigDefaults(binding.trait);
|
|
50131
|
-
const
|
|
50132
|
-
|
|
50133
|
-
|
|
50160
|
+
const resolvedByName = traitConfigsByName?.[traitName];
|
|
50161
|
+
const callSiteRaw = getBindingConfig(binding);
|
|
50162
|
+
const callSiteConfig = callSiteRaw ? Object.fromEntries(
|
|
50163
|
+
Object.entries(callSiteRaw).filter(([, v]) => !containsConfigForward(v))
|
|
50164
|
+
) : void 0;
|
|
50165
|
+
if (declaredDefaults || resolvedByName || callSiteConfig) {
|
|
50166
|
+
ctx.config = { ...declaredDefaults ?? {}, ...resolvedByName ?? {}, ...callSiteConfig ?? {} };
|
|
50134
50167
|
}
|
|
50135
50168
|
ctx.mutateEntity = (changes) => {
|
|
50136
50169
|
for (const [field, value] of Object.entries(changes)) {
|
|
@@ -50733,7 +50766,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
50733
50766
|
const interval = entries[0].tick.interval;
|
|
50734
50767
|
const onDue = timedTick(`tick:shared:${group.storeKey}@${String(interval)}`, () => {
|
|
50735
50768
|
const writers = entries.map(
|
|
50736
|
-
({ binding, tick }) => createSharedEntityWriter(binding, tick, traitStatesRef, emitFromSharedWriter)
|
|
50769
|
+
({ binding, tick }) => createSharedEntityWriter(binding, tick, traitStatesRef, emitFromSharedWriter, traitConfigsByName)
|
|
50737
50770
|
);
|
|
50738
50771
|
runTickFrame(group.storeKey, writers, sharedEntityStore);
|
|
50739
50772
|
});
|