@handled-ai/design-system 0.20.16 → 0.20.17
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/components/comment-composer.d.ts +1 -3
- package/dist/components/comment-composer.js +4 -8
- package/dist/components/comment-composer.js.map +1 -1
- package/dist/components/rich-text-toolbar.d.ts +9 -2
- package/dist/components/rich-text-toolbar.js +75 -16
- package/dist/components/rich-text-toolbar.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/prototype/prototype-inbox-view.js +14 -28
- package/dist/prototype/prototype-inbox-view.js.map +1 -1
- package/package.json +1 -1
- package/src/components/__tests__/comment-composer.test.tsx +0 -15
- package/src/components/__tests__/rich-text-toolbar.test.tsx +28 -1
- package/src/components/comment-composer.tsx +4 -18
- package/src/components/rich-text-toolbar.tsx +77 -13
- package/src/prototype/__tests__/detail-view-case-panel-v2.test.tsx +0 -18
- package/src/prototype/__tests__/detail-view-timeline-system-events.test.tsx +0 -52
- package/src/prototype/prototype-inbox-view.tsx +18 -41
|
@@ -20,12 +20,10 @@ interface CommentComposerProps {
|
|
|
20
20
|
avatarUrl?: string | null;
|
|
21
21
|
};
|
|
22
22
|
placeholder?: string;
|
|
23
|
-
/** Compact spacing for the case-panel timeline surface. Defaults preserve the standard composer sizing. */
|
|
24
|
-
density?: "default" | "case-panel";
|
|
25
23
|
/** Hint shown in the footer; defaults to the internal-note reassurance. */
|
|
26
24
|
hint?: string;
|
|
27
25
|
className?: string;
|
|
28
26
|
}
|
|
29
|
-
declare function CommentComposer({ onPost, author, placeholder,
|
|
27
|
+
declare function CommentComposer({ onPost, author, placeholder, hint, className, }: CommentComposerProps): React.JSX.Element;
|
|
30
28
|
|
|
31
29
|
export { CommentComposer, type CommentComposerProps };
|
|
@@ -13,7 +13,6 @@ function CommentComposer({
|
|
|
13
13
|
onPost,
|
|
14
14
|
author,
|
|
15
15
|
placeholder = "Add a comment or internal note\u2026",
|
|
16
|
-
density = "default",
|
|
17
16
|
hint = "Internal note: only your team sees this",
|
|
18
17
|
className
|
|
19
18
|
}) {
|
|
@@ -22,8 +21,6 @@ function CommentComposer({
|
|
|
22
21
|
const [focused, setFocused] = React.useState(false);
|
|
23
22
|
const open = focused || text.length > 0;
|
|
24
23
|
const canPost = text.trim().length > 0;
|
|
25
|
-
const hasDraft = text.length > 0;
|
|
26
|
-
const compact = density === "case-panel";
|
|
27
24
|
const post = () => {
|
|
28
25
|
const value = text.trim();
|
|
29
26
|
if (!value) return;
|
|
@@ -62,11 +59,10 @@ function CommentComposer({
|
|
|
62
59
|
onChange: (e) => setText(e.target.value),
|
|
63
60
|
onFocus: () => setFocused(true),
|
|
64
61
|
placeholder,
|
|
65
|
-
rows:
|
|
62
|
+
rows: open ? 4 : 1,
|
|
66
63
|
className: cn(
|
|
67
|
-
"resize-none rounded-none border-0 bg-transparent px-5 text-[15px] leading-6 shadow-none outline-none placeholder:text-muted-foreground/60 focus-visible:ring-0 focus-visible:ring-offset-0",
|
|
68
|
-
|
|
69
|
-
compact ? hasDraft ? "!min-h-28" : open ? "!min-h-[76px]" : "!min-h-12" : open ? "min-h-32" : "min-h-14"
|
|
64
|
+
"resize-none rounded-none border-0 bg-transparent px-5 py-4 text-[15px] leading-6 shadow-none outline-none placeholder:text-muted-foreground/60 focus-visible:ring-0 focus-visible:ring-offset-0",
|
|
65
|
+
open ? "min-h-32" : "min-h-14"
|
|
70
66
|
),
|
|
71
67
|
onKeyDown: (e) => {
|
|
72
68
|
if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
|
|
@@ -76,7 +72,7 @@ function CommentComposer({
|
|
|
76
72
|
}
|
|
77
73
|
}
|
|
78
74
|
),
|
|
79
|
-
open ? /* @__PURE__ */ jsxs("div", { className:
|
|
75
|
+
open ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 border-t border-border bg-muted/10 px-5 py-4", children: [
|
|
80
76
|
/* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2 text-sm text-muted-foreground", children: [
|
|
81
77
|
/* @__PURE__ */ jsx(Lock, { size: 16, strokeWidth: 1.75 }),
|
|
82
78
|
" ",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/comment-composer.tsx"],"sourcesContent":["\"use client\"\n\n/**\n * comment-composer.tsx — an internal-note composer for the case activity\n * timeline. Posting a comment prepends an `operatorNote` event to the log\n * (wired by the consumer). Collapses to a single line; expands on focus or\n * when it has text. ⌘↵ / Ctrl↵ posts.\n *\n * Presentational: `onPost` does the work (the consumer persists the note and\n * adds it to the timeline). Reuses Avatar / Button / Textarea primitives.\n */\n\nimport * as React from \"react\"\nimport { Lock } from \"lucide-react\"\n\nimport { cn } from \"../lib/utils\"\nimport { getInitials } from \"../lib/user-display\"\nimport { Avatar, AvatarFallback, AvatarImage } from \"./avatar\"\nimport { Button } from \"./button\"\nimport { Textarea } from \"./textarea\"\n\nexport interface CommentComposerProps {\n /** Called with the trimmed note text when the operator posts. */\n onPost: (text: string) => void\n /** Current operator (for the avatar). */\n author?: { name?: string; email?: string; avatarUrl?: string | null }\n placeholder?: string\n /**
|
|
1
|
+
{"version":3,"sources":["../../src/components/comment-composer.tsx"],"sourcesContent":["\"use client\"\n\n/**\n * comment-composer.tsx — an internal-note composer for the case activity\n * timeline. Posting a comment prepends an `operatorNote` event to the log\n * (wired by the consumer). Collapses to a single line; expands on focus or\n * when it has text. ⌘↵ / Ctrl↵ posts.\n *\n * Presentational: `onPost` does the work (the consumer persists the note and\n * adds it to the timeline). Reuses Avatar / Button / Textarea primitives.\n */\n\nimport * as React from \"react\"\nimport { Lock } from \"lucide-react\"\n\nimport { cn } from \"../lib/utils\"\nimport { getInitials } from \"../lib/user-display\"\nimport { Avatar, AvatarFallback, AvatarImage } from \"./avatar\"\nimport { Button } from \"./button\"\nimport { Textarea } from \"./textarea\"\n\nexport interface CommentComposerProps {\n /** Called with the trimmed note text when the operator posts. */\n onPost: (text: string) => void\n /** Current operator (for the avatar). */\n author?: { name?: string; email?: string; avatarUrl?: string | null }\n placeholder?: string\n /** Hint shown in the footer; defaults to the internal-note reassurance. */\n hint?: string\n className?: string\n}\n\nfunction CommentComposer({\n onPost,\n author,\n placeholder = \"Add a comment or internal note…\",\n hint = \"Internal note: only your team sees this\",\n className,\n}: CommentComposerProps) {\n const [text, setText] = React.useState(\"\")\n const [focused, setFocused] = React.useState(false)\n const open = focused || text.length > 0\n const canPost = text.trim().length > 0\n\n const post = () => {\n const value = text.trim()\n if (!value) return\n onPost(value)\n setText(\"\")\n setFocused(false)\n }\n\n return (\n <div\n data-slot=\"comment-composer\"\n data-open={open ? \"true\" : undefined}\n className={cn(\n \"flex items-start gap-4 rounded-xl transition-colors\",\n className\n )}\n >\n <Avatar size=\"sm\" className=\"mt-1\">\n {author?.avatarUrl ? <AvatarImage src={author.avatarUrl} alt={author.name ?? \"You\"} /> : null}\n <AvatarFallback className=\"bg-slate-700 text-[10px] font-semibold uppercase text-white dark:bg-slate-200 dark:text-slate-900\">\n {getInitials({ name: author?.name, email: author?.email })}\n </AvatarFallback>\n </Avatar>\n\n <div\n data-slot=\"comment-composer-shell\"\n className={cn(\n \"min-w-0 flex-1 rounded-xl border border-border bg-background transition-[box-shadow,border-color]\",\n open ? \"overflow-hidden shadow-sm\" : \"shadow-none\"\n )}\n >\n <Textarea\n data-slot=\"comment-composer-input\"\n value={text}\n onChange={(e) => setText(e.target.value)}\n onFocus={() => setFocused(true)}\n placeholder={placeholder}\n rows={open ? 4 : 1}\n className={cn(\n \"resize-none rounded-none border-0 bg-transparent px-5 py-4 text-[15px] leading-6 shadow-none outline-none placeholder:text-muted-foreground/60 focus-visible:ring-0 focus-visible:ring-offset-0\",\n open ? \"min-h-32\" : \"min-h-14\"\n )}\n onKeyDown={(e) => {\n if ((e.metaKey || e.ctrlKey) && e.key === \"Enter\") {\n e.preventDefault()\n post()\n }\n }}\n />\n\n {open ? (\n <div className=\"flex items-center justify-between gap-3 border-t border-border bg-muted/10 px-5 py-4\">\n <span className=\"inline-flex items-center gap-2 text-sm text-muted-foreground\">\n <Lock size={16} strokeWidth={1.75} /> {hint}\n </span>\n <span className=\"flex items-center gap-3\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"px-2 text-sm font-medium text-muted-foreground hover:bg-transparent hover:text-foreground\"\n onClick={() => {\n setText(\"\")\n setFocused(false)\n }}\n >\n Cancel\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n disabled={!canPost}\n onClick={post}\n className=\"rounded-lg bg-foreground px-4 text-sm font-semibold text-background shadow-none hover:bg-foreground/90\"\n >\n Comment\n <kbd className=\"ml-1 rounded px-1 text-[10px] text-background/70\">⌘↵</kbd>\n </Button>\n </span>\n </div>\n ) : null}\n </div>\n </div>\n )\n}\n\nexport { CommentComposer }\n"],"mappings":";AA6DM,SACuB,KADvB;AAjDN,YAAY,WAAW;AACvB,SAAS,YAAY;AAErB,SAAS,UAAU;AACnB,SAAS,mBAAmB;AAC5B,SAAS,QAAQ,gBAAgB,mBAAmB;AACpD,SAAS,cAAc;AACvB,SAAS,gBAAgB;AAazB,SAAS,gBAAgB;AAAA,EACvB;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,OAAO;AAAA,EACP;AACF,GAAyB;AAtCzB;AAuCE,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,EAAE;AACzC,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,KAAK;AAClD,QAAM,OAAO,WAAW,KAAK,SAAS;AACtC,QAAM,UAAU,KAAK,KAAK,EAAE,SAAS;AAErC,QAAM,OAAO,MAAM;AACjB,UAAM,QAAQ,KAAK,KAAK;AACxB,QAAI,CAAC,MAAO;AACZ,WAAO,KAAK;AACZ,YAAQ,EAAE;AACV,eAAW,KAAK;AAAA,EAClB;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,aAAW,OAAO,SAAS;AAAA,MAC3B,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MAEA;AAAA,6BAAC,UAAO,MAAK,MAAK,WAAU,QACzB;AAAA,4CAAQ,aAAY,oBAAC,eAAY,KAAK,OAAO,WAAW,MAAK,YAAO,SAAP,YAAe,OAAO,IAAK;AAAA,UACzF,oBAAC,kBAAe,WAAU,qGACvB,sBAAY,EAAE,MAAM,iCAAQ,MAAM,OAAO,iCAAQ,MAAM,CAAC,GAC3D;AAAA,WACF;AAAA,QAEA;AAAA,UAAC;AAAA;AAAA,YACC,aAAU;AAAA,YACV,WAAW;AAAA,cACT;AAAA,cACA,OAAO,8BAA8B;AAAA,YACvC;AAAA,YAEA;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,aAAU;AAAA,kBACV,OAAO;AAAA,kBACP,UAAU,CAAC,MAAM,QAAQ,EAAE,OAAO,KAAK;AAAA,kBACvC,SAAS,MAAM,WAAW,IAAI;AAAA,kBAC9B;AAAA,kBACA,MAAM,OAAO,IAAI;AAAA,kBACjB,WAAW;AAAA,oBACT;AAAA,oBACA,OAAO,aAAa;AAAA,kBACtB;AAAA,kBACA,WAAW,CAAC,MAAM;AAChB,yBAAK,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,SAAS;AACjD,wBAAE,eAAe;AACjB,2BAAK;AAAA,oBACP;AAAA,kBACF;AAAA;AAAA,cACF;AAAA,cAEC,OACC,qBAAC,SAAI,WAAU,wFACb;AAAA,qCAAC,UAAK,WAAU,gEACd;AAAA,sCAAC,QAAK,MAAM,IAAI,aAAa,MAAM;AAAA,kBAAE;AAAA,kBAAE;AAAA,mBACzC;AAAA,gBACA,qBAAC,UAAK,WAAU,2BACd;AAAA;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAK;AAAA,sBACL,SAAQ;AAAA,sBACR,MAAK;AAAA,sBACL,WAAU;AAAA,sBACV,SAAS,MAAM;AACb,gCAAQ,EAAE;AACV,mCAAW,KAAK;AAAA,sBAClB;AAAA,sBACD;AAAA;AAAA,kBAED;AAAA,kBACA;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAK;AAAA,sBACL,MAAK;AAAA,sBACL,UAAU,CAAC;AAAA,sBACX,SAAS;AAAA,sBACT,WAAU;AAAA,sBACX;AAAA;AAAA,wBAEC,oBAAC,SAAI,WAAU,oDAAmD,0BAAE;AAAA;AAAA;AAAA,kBACtE;AAAA,mBACF;AAAA,iBACF,IACE;AAAA;AAAA;AAAA,QACN;AAAA;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
3
|
type RichTextAction = "undo" | "redo" | "font" | "bold" | "italic" | "underline" | "align" | "list" | "delete";
|
|
4
|
+
interface RichTextFontOption {
|
|
5
|
+
label: string;
|
|
6
|
+
value: string;
|
|
7
|
+
}
|
|
4
8
|
interface RichTextToolbarProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
5
9
|
onAction?: (action: RichTextAction) => void;
|
|
10
|
+
fontOptions?: RichTextFontOption[];
|
|
11
|
+
selectedFontFamily?: string;
|
|
12
|
+
onFontFamilyChange?: (fontFamily: string) => void;
|
|
6
13
|
}
|
|
7
|
-
declare function RichTextToolbar({ onAction, className, ...rest }: RichTextToolbarProps): React.JSX.Element;
|
|
14
|
+
declare function RichTextToolbar({ onAction, className, fontOptions, selectedFontFamily, onFontFamilyChange, ...rest }: RichTextToolbarProps): React.JSX.Element;
|
|
8
15
|
|
|
9
|
-
export { type RichTextAction, RichTextToolbar, type RichTextToolbarProps };
|
|
16
|
+
export { type RichTextAction, type RichTextFontOption, RichTextToolbar, type RichTextToolbarProps };
|
|
@@ -33,6 +33,7 @@ var __objRest = (source, exclude) => {
|
|
|
33
33
|
return target;
|
|
34
34
|
};
|
|
35
35
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
36
|
+
import * as React from "react";
|
|
36
37
|
import { Undo2, Redo2, Bold, Italic, Underline, AlignLeft, List, Trash2, ChevronDown } from "lucide-react";
|
|
37
38
|
import { cn } from "../lib/utils.js";
|
|
38
39
|
function ToolbarButton({
|
|
@@ -55,7 +56,35 @@ function ToolbarButton({
|
|
|
55
56
|
);
|
|
56
57
|
}
|
|
57
58
|
function RichTextToolbar(_a) {
|
|
58
|
-
var _b = _a, {
|
|
59
|
+
var _b = _a, {
|
|
60
|
+
onAction,
|
|
61
|
+
className,
|
|
62
|
+
fontOptions,
|
|
63
|
+
selectedFontFamily,
|
|
64
|
+
onFontFamilyChange
|
|
65
|
+
} = _b, rest = __objRest(_b, [
|
|
66
|
+
"onAction",
|
|
67
|
+
"className",
|
|
68
|
+
"fontOptions",
|
|
69
|
+
"selectedFontFamily",
|
|
70
|
+
"onFontFamilyChange"
|
|
71
|
+
]);
|
|
72
|
+
var _a2, _b2;
|
|
73
|
+
const [fontMenuOpen, setFontMenuOpen] = React.useState(false);
|
|
74
|
+
const fontMenuRef = React.useRef(null);
|
|
75
|
+
const selectedFontOption = (_a2 = fontOptions == null ? void 0 : fontOptions.find((option) => option.value === selectedFontFamily)) != null ? _a2 : fontOptions == null ? void 0 : fontOptions[0];
|
|
76
|
+
const fontLabel = (_b2 = selectedFontOption == null ? void 0 : selectedFontOption.label) != null ? _b2 : "Sans Serif";
|
|
77
|
+
const hasFontMenu = Boolean((fontOptions == null ? void 0 : fontOptions.length) && onFontFamilyChange);
|
|
78
|
+
React.useEffect(() => {
|
|
79
|
+
if (!fontMenuOpen) return;
|
|
80
|
+
function handleDocumentMouseDown(event) {
|
|
81
|
+
var _a3;
|
|
82
|
+
if ((_a3 = fontMenuRef.current) == null ? void 0 : _a3.contains(event.target)) return;
|
|
83
|
+
setFontMenuOpen(false);
|
|
84
|
+
}
|
|
85
|
+
document.addEventListener("mousedown", handleDocumentMouseDown);
|
|
86
|
+
return () => document.removeEventListener("mousedown", handleDocumentMouseDown);
|
|
87
|
+
}, [fontMenuOpen]);
|
|
59
88
|
return /* @__PURE__ */ jsxs(
|
|
60
89
|
"div",
|
|
61
90
|
__spreadProps(__spreadValues({
|
|
@@ -69,21 +98,51 @@ function RichTextToolbar(_a) {
|
|
|
69
98
|
/* @__PURE__ */ jsx(ToolbarButton, { action: "undo", icon: Undo2, label: "Undo", onAction }),
|
|
70
99
|
/* @__PURE__ */ jsx(ToolbarButton, { action: "redo", icon: Redo2, label: "Redo", onAction }),
|
|
71
100
|
/* @__PURE__ */ jsx("div", { className: "w-px h-4 bg-border mx-1", "aria-hidden": "true" }),
|
|
72
|
-
/* @__PURE__ */ jsxs(
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
"
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
101
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", ref: fontMenuRef, children: [
|
|
102
|
+
/* @__PURE__ */ jsxs(
|
|
103
|
+
"button",
|
|
104
|
+
{
|
|
105
|
+
type: "button",
|
|
106
|
+
"data-slot": "rich-text-toolbar-button",
|
|
107
|
+
onClick: () => {
|
|
108
|
+
onAction == null ? void 0 : onAction("font");
|
|
109
|
+
if (hasFontMenu) setFontMenuOpen((open) => !open);
|
|
110
|
+
},
|
|
111
|
+
"aria-label": "Font family",
|
|
112
|
+
"aria-haspopup": "menu",
|
|
113
|
+
"aria-expanded": hasFontMenu ? fontMenuOpen : void 0,
|
|
114
|
+
className: "text-[11px] text-muted-foreground px-1.5 py-0.5 rounded hover:bg-muted/50 cursor-pointer flex items-center gap-1",
|
|
115
|
+
children: [
|
|
116
|
+
fontLabel,
|
|
117
|
+
/* @__PURE__ */ jsx(ChevronDown, { size: 10 })
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
),
|
|
121
|
+
hasFontMenu && fontMenuOpen ? /* @__PURE__ */ jsx(
|
|
122
|
+
"div",
|
|
123
|
+
{
|
|
124
|
+
role: "menu",
|
|
125
|
+
"aria-label": "Font family",
|
|
126
|
+
className: "absolute left-0 bottom-full z-50 mb-1 min-w-32 overflow-hidden rounded-md border border-border bg-background py-1 shadow-md",
|
|
127
|
+
children: fontOptions.map((option) => /* @__PURE__ */ jsx(
|
|
128
|
+
"button",
|
|
129
|
+
{
|
|
130
|
+
type: "button",
|
|
131
|
+
role: "menuitemradio",
|
|
132
|
+
"aria-checked": option.value === selectedFontFamily,
|
|
133
|
+
className: "block w-full px-2.5 py-1.5 text-left text-xs text-foreground hover:bg-muted/60",
|
|
134
|
+
style: { fontFamily: option.value },
|
|
135
|
+
onClick: () => {
|
|
136
|
+
onFontFamilyChange == null ? void 0 : onFontFamilyChange(option.value);
|
|
137
|
+
setFontMenuOpen(false);
|
|
138
|
+
},
|
|
139
|
+
children: option.label
|
|
140
|
+
},
|
|
141
|
+
option.value
|
|
142
|
+
))
|
|
143
|
+
}
|
|
144
|
+
) : null
|
|
145
|
+
] }),
|
|
87
146
|
/* @__PURE__ */ jsx("div", { className: "w-px h-4 bg-border mx-1", "aria-hidden": "true" }),
|
|
88
147
|
/* @__PURE__ */ jsx(ToolbarButton, { action: "bold", icon: Bold, label: "Bold", onAction }),
|
|
89
148
|
/* @__PURE__ */ jsx(ToolbarButton, { action: "italic", icon: Italic, label: "Italic", onAction }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/rich-text-toolbar.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport type { LucideIcon } from \"lucide-react\"\nimport { Undo2, Redo2, Bold, Italic, Underline, AlignLeft, List, Trash2, ChevronDown } from \"lucide-react\"\n\nimport { cn } from \"../lib/utils\"\n\ntype RichTextAction =\n | \"undo\" | \"redo\"\n | \"font\"\n | \"bold\" | \"italic\" | \"underline\"\n | \"align\" | \"list\"\n | \"delete\"\n\ninterface RichTextToolbarProps extends React.HTMLAttributes<HTMLDivElement> {\n onAction?: (action: RichTextAction) => void\n}\n\nfunction ToolbarButton({\n action,\n icon: Icon,\n label,\n extraClassName,\n onAction,\n}: {\n action: RichTextAction\n icon: LucideIcon\n label: string\n extraClassName?: string\n onAction?: (action: RichTextAction) => void\n}) {\n return (\n <button\n type=\"button\"\n data-slot=\"rich-text-toolbar-button\"\n onClick={() => onAction?.(action)}\n aria-label={label}\n className={cn(\"p-1.5 rounded hover:bg-muted/50 cursor-pointer text-muted-foreground\", extraClassName)}\n >\n <Icon size={14} />\n </button>\n )\n}\n\nfunction RichTextToolbar({
|
|
1
|
+
{"version":3,"sources":["../../src/components/rich-text-toolbar.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport type { LucideIcon } from \"lucide-react\"\nimport { Undo2, Redo2, Bold, Italic, Underline, AlignLeft, List, Trash2, ChevronDown } from \"lucide-react\"\n\nimport { cn } from \"../lib/utils\"\n\ntype RichTextAction =\n | \"undo\" | \"redo\"\n | \"font\"\n | \"bold\" | \"italic\" | \"underline\"\n | \"align\" | \"list\"\n | \"delete\"\n\ninterface RichTextFontOption {\n label: string\n value: string\n}\n\ninterface RichTextToolbarProps extends React.HTMLAttributes<HTMLDivElement> {\n onAction?: (action: RichTextAction) => void\n fontOptions?: RichTextFontOption[]\n selectedFontFamily?: string\n onFontFamilyChange?: (fontFamily: string) => void\n}\n\nfunction ToolbarButton({\n action,\n icon: Icon,\n label,\n extraClassName,\n onAction,\n}: {\n action: RichTextAction\n icon: LucideIcon\n label: string\n extraClassName?: string\n onAction?: (action: RichTextAction) => void\n}) {\n return (\n <button\n type=\"button\"\n data-slot=\"rich-text-toolbar-button\"\n onClick={() => onAction?.(action)}\n aria-label={label}\n className={cn(\"p-1.5 rounded hover:bg-muted/50 cursor-pointer text-muted-foreground\", extraClassName)}\n >\n <Icon size={14} />\n </button>\n )\n}\n\nfunction RichTextToolbar({\n onAction,\n className,\n fontOptions,\n selectedFontFamily,\n onFontFamilyChange,\n ...rest\n}: RichTextToolbarProps) {\n const [fontMenuOpen, setFontMenuOpen] = React.useState(false)\n const fontMenuRef = React.useRef<HTMLDivElement | null>(null)\n const selectedFontOption = fontOptions?.find((option) => option.value === selectedFontFamily) ?? fontOptions?.[0]\n const fontLabel = selectedFontOption?.label ?? \"Sans Serif\"\n const hasFontMenu = Boolean(fontOptions?.length && onFontFamilyChange)\n\n React.useEffect(() => {\n if (!fontMenuOpen) return\n\n function handleDocumentMouseDown(event: MouseEvent) {\n if (fontMenuRef.current?.contains(event.target as Node)) return\n setFontMenuOpen(false)\n }\n\n document.addEventListener(\"mousedown\", handleDocumentMouseDown)\n return () => document.removeEventListener(\"mousedown\", handleDocumentMouseDown)\n }, [fontMenuOpen])\n\n return (\n <div\n data-slot=\"rich-text-toolbar\"\n role=\"toolbar\"\n aria-label=\"Rich text formatting\"\n className={cn(\"px-3 py-1.5 flex items-center justify-between\", className)}\n {...rest}\n >\n <div className=\"flex items-center gap-0.5\">\n <ToolbarButton action=\"undo\" icon={Undo2} label=\"Undo\" onAction={onAction} />\n <ToolbarButton action=\"redo\" icon={Redo2} label=\"Redo\" onAction={onAction} />\n\n <div className=\"w-px h-4 bg-border mx-1\" aria-hidden=\"true\" />\n\n <div className=\"relative\" ref={fontMenuRef}>\n <button\n type=\"button\"\n data-slot=\"rich-text-toolbar-button\"\n onClick={() => {\n onAction?.(\"font\")\n if (hasFontMenu) setFontMenuOpen((open) => !open)\n }}\n aria-label=\"Font family\"\n aria-haspopup=\"menu\"\n aria-expanded={hasFontMenu ? fontMenuOpen : undefined}\n className=\"text-[11px] text-muted-foreground px-1.5 py-0.5 rounded hover:bg-muted/50 cursor-pointer flex items-center gap-1\"\n >\n {fontLabel}\n <ChevronDown size={10} />\n </button>\n\n {hasFontMenu && fontMenuOpen ? (\n <div\n role=\"menu\"\n aria-label=\"Font family\"\n className=\"absolute left-0 bottom-full z-50 mb-1 min-w-32 overflow-hidden rounded-md border border-border bg-background py-1 shadow-md\"\n >\n {fontOptions!.map((option) => (\n <button\n key={option.value}\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={option.value === selectedFontFamily}\n className=\"block w-full px-2.5 py-1.5 text-left text-xs text-foreground hover:bg-muted/60\"\n style={{ fontFamily: option.value }}\n onClick={() => {\n onFontFamilyChange?.(option.value)\n setFontMenuOpen(false)\n }}\n >\n {option.label}\n </button>\n ))}\n </div>\n ) : null}\n </div>\n\n <div className=\"w-px h-4 bg-border mx-1\" aria-hidden=\"true\" />\n\n <ToolbarButton action=\"bold\" icon={Bold} label=\"Bold\" onAction={onAction} />\n <ToolbarButton action=\"italic\" icon={Italic} label=\"Italic\" onAction={onAction} />\n <ToolbarButton action=\"underline\" icon={Underline} label=\"Underline\" onAction={onAction} />\n\n <div className=\"w-px h-4 bg-border mx-1\" aria-hidden=\"true\" />\n\n <ToolbarButton action=\"align\" icon={AlignLeft} label=\"Align left\" onAction={onAction} />\n <ToolbarButton action=\"list\" icon={List} label=\"List\" onAction={onAction} />\n </div>\n\n <ToolbarButton action=\"delete\" icon={Trash2} label=\"Delete\" extraClassName=\"hover:text-destructive\" onAction={onAction} />\n </div>\n )\n}\n\nexport { RichTextToolbar, type RichTextToolbarProps, type RichTextAction, type RichTextFontOption }\n"],"mappings":"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","names":["_a","_b"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -70,7 +70,7 @@ export { QuickActionModal, QuickActionPriority, QuickActionTaskDraft, QuickActio
|
|
|
70
70
|
export { ActiveVariant, QuickActionSidebarNav, SidebarNavItem, SidebarNavSection, SidebarUserProfile, UserMenuItem } from './components/quick-action-sidebar-nav.js';
|
|
71
71
|
export { RecommendedAction, RecommendedActionsSection } from './components/recommended-actions-section.js';
|
|
72
72
|
export { ReportCard, ReportCardProps } from './components/report-card.js';
|
|
73
|
-
export { RichTextAction, RichTextToolbar, RichTextToolbarProps } from './components/rich-text-toolbar.js';
|
|
73
|
+
export { RichTextAction, RichTextFontOption, RichTextToolbar, RichTextToolbarProps } from './components/rich-text-toolbar.js';
|
|
74
74
|
export { ScoreAnalysisModal, ScoreAnalysisModalProps, ScoreAnalysisPanel, getScoreLabel } from './components/score-analysis-modal.js';
|
|
75
75
|
export { ScoreBreakdown, ScoreBreakdownProps, ScoreFactor } from './components/score-breakdown.js';
|
|
76
76
|
export { ScoreFeedback, useScoreFeedback } from './components/score-feedback.js';
|
|
@@ -172,9 +172,8 @@ function TimelineSection({
|
|
|
172
172
|
type: "button",
|
|
173
173
|
onClick: () => setShowSystemEvents((prev) => !prev),
|
|
174
174
|
className: cn(
|
|
175
|
-
"inline-flex shrink-0 cursor-pointer items-center rounded-full border transition-colors",
|
|
176
|
-
|
|
177
|
-
showSystemEvents ? isCasePanel ? "border-border bg-muted text-foreground shadow-sm hover:bg-muted/80" : "border-foreground bg-foreground text-background shadow-sm hover:bg-foreground/90" : "border-border bg-background text-muted-foreground shadow-sm hover:bg-muted/40 hover:text-foreground"
|
|
175
|
+
"inline-flex shrink-0 cursor-pointer items-center gap-3 rounded-full border px-3.5 py-2 text-sm font-semibold transition-colors",
|
|
176
|
+
showSystemEvents ? "border-foreground bg-foreground text-background shadow-sm hover:bg-foreground/90" : "border-border bg-background text-muted-foreground shadow-sm hover:bg-muted/40 hover:text-foreground"
|
|
178
177
|
),
|
|
179
178
|
"aria-pressed": showSystemEvents,
|
|
180
179
|
"aria-label": toggleHelp,
|
|
@@ -185,9 +184,8 @@ function TimelineSection({
|
|
|
185
184
|
"span",
|
|
186
185
|
{
|
|
187
186
|
className: cn(
|
|
188
|
-
"relative inline-flex shrink-0 items-center rounded-full p-0.5 transition-colors",
|
|
189
|
-
|
|
190
|
-
showSystemEvents ? "bg-teal-600" : "bg-muted-foreground/25"
|
|
187
|
+
"relative inline-flex h-4 w-8 shrink-0 items-center rounded-full p-0.5 transition-colors",
|
|
188
|
+
showSystemEvents ? "bg-teal-600" : "bg-muted-foreground/30"
|
|
191
189
|
),
|
|
192
190
|
"aria-hidden": "true",
|
|
193
191
|
"data-testid": "system-events-indicator",
|
|
@@ -195,9 +193,8 @@ function TimelineSection({
|
|
|
195
193
|
"span",
|
|
196
194
|
{
|
|
197
195
|
className: cn(
|
|
198
|
-
"block rounded-full bg-white shadow-sm transition-transform",
|
|
199
|
-
|
|
200
|
-
showSystemEvents ? isCasePanel ? "translate-x-3.5" : "translate-x-4" : "translate-x-0"
|
|
196
|
+
"block h-3 w-3 rounded-full bg-white shadow-sm transition-transform",
|
|
197
|
+
showSystemEvents ? "translate-x-4" : "translate-x-0"
|
|
201
198
|
)
|
|
202
199
|
}
|
|
203
200
|
)
|
|
@@ -207,7 +204,7 @@ function TimelineSection({
|
|
|
207
204
|
!showSystemEvents ? /* @__PURE__ */ jsx(
|
|
208
205
|
"span",
|
|
209
206
|
{
|
|
210
|
-
className:
|
|
207
|
+
className: "inline-flex min-w-[22px] items-center justify-center rounded-full bg-muted px-1.5 text-xs font-bold tabular-nums text-muted-foreground",
|
|
211
208
|
"data-testid": "hidden-count-badge",
|
|
212
209
|
children: hiddenCount
|
|
213
210
|
}
|
|
@@ -307,7 +304,6 @@ function DetailView({
|
|
|
307
304
|
timelineSystemEventsHiddenHint,
|
|
308
305
|
timelineSystemEventsVisibleHint
|
|
309
306
|
]);
|
|
310
|
-
const isCasePanelV2 = sectionLayout === "case-panel-v2";
|
|
311
307
|
const [showTimeline, setShowTimeline] = React.useState(false);
|
|
312
308
|
const [extraActions, setExtraActions] = React.useState([]);
|
|
313
309
|
const sysEvtDefaultVisible = (_a = sysEvtConfig == null ? void 0 : sysEvtConfig.defaultVisible) != null ? _a : false;
|
|
@@ -315,13 +311,7 @@ function DetailView({
|
|
|
315
311
|
const [showSystemEvents, setShowSystemEvents] = React.useState(sysEvtDefaultVisible);
|
|
316
312
|
const initialReadDoneRef = React.useRef(false);
|
|
317
313
|
React.useEffect(() => {
|
|
318
|
-
if (isCasePanelV2) {
|
|
319
|
-
setShowSystemEvents(sysEvtDefaultVisible);
|
|
320
|
-
initialReadDoneRef.current = true;
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
314
|
if (!sysEvtStorageKey) {
|
|
324
|
-
setShowSystemEvents(sysEvtDefaultVisible);
|
|
325
315
|
initialReadDoneRef.current = true;
|
|
326
316
|
return;
|
|
327
317
|
}
|
|
@@ -329,22 +319,19 @@ function DetailView({
|
|
|
329
319
|
const stored = localStorage.getItem(sysEvtStorageKey);
|
|
330
320
|
if (stored !== null) {
|
|
331
321
|
setShowSystemEvents(stored === "true");
|
|
332
|
-
} else {
|
|
333
|
-
setShowSystemEvents(sysEvtDefaultVisible);
|
|
334
322
|
}
|
|
335
323
|
} catch (e) {
|
|
336
|
-
setShowSystemEvents(sysEvtDefaultVisible);
|
|
337
324
|
}
|
|
338
325
|
initialReadDoneRef.current = true;
|
|
339
|
-
}, [
|
|
326
|
+
}, [sysEvtStorageKey]);
|
|
340
327
|
React.useEffect(() => {
|
|
341
|
-
if (!sysEvtStorageKey
|
|
328
|
+
if (!sysEvtStorageKey) return;
|
|
342
329
|
if (!initialReadDoneRef.current) return;
|
|
343
330
|
try {
|
|
344
331
|
localStorage.setItem(sysEvtStorageKey, String(showSystemEvents));
|
|
345
332
|
} catch (e) {
|
|
346
333
|
}
|
|
347
|
-
}, [
|
|
334
|
+
}, [showSystemEvents, sysEvtStorageKey]);
|
|
348
335
|
React.useEffect(() => {
|
|
349
336
|
setShowTimeline(false);
|
|
350
337
|
setExtraActions([]);
|
|
@@ -377,6 +364,7 @@ function DetailView({
|
|
|
377
364
|
[suggestedActions]
|
|
378
365
|
);
|
|
379
366
|
const timeChipToneClass = signalData.timeChipTone === "red" ? "border-red-300 bg-red-50 text-red-700 hover:bg-red-50" : signalData.timeChipTone === "amber" ? "border-amber-300 bg-amber-50 text-amber-700 hover:bg-amber-50" : "hover:bg-muted/50";
|
|
367
|
+
const isCasePanelV2 = sectionLayout === "case-panel-v2";
|
|
380
368
|
const metadataChips = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
381
369
|
/* @__PURE__ */ jsx(
|
|
382
370
|
SignalPriorityPopover,
|
|
@@ -538,11 +526,9 @@ function DetailView({
|
|
|
538
526
|
/* @__PURE__ */ jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsx(SignalApproval.Actions, {}) })
|
|
539
527
|
] })
|
|
540
528
|
] }) : null,
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
renderCommentArea ? /* @__PURE__ */ jsx("div", { "data-slot": "case-panel-comment-area", children: renderCommentArea(item) }) : null
|
|
545
|
-
] }),
|
|
529
|
+
renderAfterScore == null ? void 0 : renderAfterScore(item),
|
|
530
|
+
renderPrimaryAction == null ? void 0 : renderPrimaryAction(item),
|
|
531
|
+
renderCommentArea == null ? void 0 : renderCommentArea(item),
|
|
546
532
|
timelineSection
|
|
547
533
|
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
548
534
|
renderAfterScore == null ? void 0 : renderAfterScore(item),
|