@clubmed/trident-ui 2.1.0-beta.2 → 2.1.0-beta.4

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.
Files changed (109) hide show
  1. package/README.md +1 -0
  2. package/chunks/ChatMessage.js +282 -265
  3. package/chunks/ChatMessage.js.map +1 -1
  4. package/examples/chat-body-demo.js +4 -2
  5. package/examples/chat-body-demo.js.map +1 -1
  6. package/examples/chat-button-demo.js +2 -1
  7. package/examples/chat-button-demo.js.map +1 -1
  8. package/examples/chat-button-offset-demo.js +2 -1
  9. package/examples/chat-button-offset-demo.js.map +1 -1
  10. package/examples/chat-header-demo.js +2 -1
  11. package/examples/chat-header-demo.js.map +1 -1
  12. package/examples/chat-input-demo.js +2 -1
  13. package/examples/chat-input-demo.js.map +1 -1
  14. package/examples/chat-input-streaming-demo.d.ts +1 -0
  15. package/examples/chat-input-streaming-demo.js +30 -0
  16. package/examples/chat-input-streaming-demo.js.map +1 -0
  17. package/examples/chat-input-submit-on-enter-demo.d.ts +1 -0
  18. package/examples/chat-input-submit-on-enter-demo.js +23 -0
  19. package/examples/chat-input-submit-on-enter-demo.js.map +1 -0
  20. package/examples/chat-message-actions-demo.d.ts +1 -0
  21. package/examples/chat-message-actions-demo.js +29 -0
  22. package/examples/chat-message-actions-demo.js.map +1 -0
  23. package/examples/chat-message-demo.js +1 -0
  24. package/examples/chat-message-demo.js.map +1 -1
  25. package/examples/chat-message-editor-demo.d.ts +1 -0
  26. package/examples/chat-message-editor-demo.js +30 -0
  27. package/examples/chat-message-editor-demo.js.map +1 -0
  28. package/examples/chat-message-with-actions-demo.d.ts +1 -0
  29. package/examples/chat-message-with-actions-demo.js +26 -0
  30. package/examples/chat-message-with-actions-demo.js.map +1 -0
  31. package/examples/chat-typing-indicator-demo.js +2 -1
  32. package/examples/chat-typing-indicator-demo.js.map +1 -1
  33. package/examples/chat-window-csat-demo.js +2 -1
  34. package/examples/chat-window-csat-demo.js.map +1 -1
  35. package/examples/chat-window-csat-prompt-demo.js +2 -1
  36. package/examples/chat-window-csat-prompt-demo.js.map +1 -1
  37. package/examples/chat-window-demo.js +2 -1
  38. package/examples/chat-window-demo.js.map +1 -1
  39. package/examples/chat-window-quote-demo.js +2 -1
  40. package/examples/chat-window-quote-demo.js.map +1 -1
  41. package/examples/chat-window-quotes-demo.js +2 -1
  42. package/examples/chat-window-quotes-demo.js.map +1 -1
  43. package/package.json +1 -1
  44. package/ui/ChatBody.d.ts +1 -25
  45. package/ui/ChatBody.js +2 -88
  46. package/ui/ChatButton.d.ts +1 -6
  47. package/ui/ChatButton.js +2 -33
  48. package/ui/ChatHeader.d.ts +1 -6
  49. package/ui/ChatHeader.js +2 -43
  50. package/ui/ChatInput.d.ts +1 -10
  51. package/ui/ChatInput.js +2 -148
  52. package/ui/ChatMessage.d.ts +1 -6
  53. package/ui/ChatMessage.js +1 -0
  54. package/ui/ChatTypingIndicator.d.ts +1 -2
  55. package/ui/ChatTypingIndicator.js +2 -30
  56. package/ui/ChatWindow.d.ts +1 -34
  57. package/ui/ChatWindow.js +2 -57
  58. package/ui/ChatWindowCsatPrompt.d.ts +1 -15
  59. package/ui/ChatWindowCsatPrompt.js +2 -70
  60. package/ui/ChatWindowCsatThankYou.d.ts +1 -9
  61. package/ui/ChatWindowCsatThankYou.js +2 -28
  62. package/ui/ChatWindowQuote.d.ts +1 -22
  63. package/ui/ChatWindowQuote.js +2 -140
  64. package/ui/chat/ChatBody.d.ts +31 -0
  65. package/ui/chat/ChatBody.js +92 -0
  66. package/ui/chat/ChatBody.js.map +1 -0
  67. package/ui/chat/ChatButton.d.ts +6 -0
  68. package/ui/chat/ChatButton.js +33 -0
  69. package/ui/chat/ChatButton.js.map +1 -0
  70. package/ui/chat/ChatHeader.d.ts +6 -0
  71. package/ui/chat/ChatHeader.js +43 -0
  72. package/ui/chat/ChatHeader.js.map +1 -0
  73. package/ui/chat/ChatInput.d.ts +28 -0
  74. package/ui/chat/ChatInput.js +165 -0
  75. package/ui/chat/ChatInput.js.map +1 -0
  76. package/ui/chat/ChatMessage.d.ts +17 -0
  77. package/ui/chat/ChatMessage.js +2 -0
  78. package/ui/chat/ChatMessageActions.d.ts +23 -0
  79. package/ui/chat/ChatMessageActions.js +69 -0
  80. package/ui/chat/ChatMessageActions.js.map +1 -0
  81. package/ui/chat/ChatMessageEditor.d.ts +9 -0
  82. package/ui/chat/ChatMessageEditor.js +57 -0
  83. package/ui/chat/ChatMessageEditor.js.map +1 -0
  84. package/ui/chat/ChatTypingIndicator.d.ts +2 -0
  85. package/ui/chat/ChatTypingIndicator.js +30 -0
  86. package/ui/chat/ChatTypingIndicator.js.map +1 -0
  87. package/ui/chat/ChatWindow.d.ts +37 -0
  88. package/ui/chat/ChatWindow.js +65 -0
  89. package/ui/chat/ChatWindow.js.map +1 -0
  90. package/ui/chat/ChatWindowCsatPrompt.d.ts +15 -0
  91. package/ui/chat/ChatWindowCsatPrompt.js +71 -0
  92. package/ui/chat/ChatWindowCsatPrompt.js.map +1 -0
  93. package/ui/chat/ChatWindowCsatThankYou.d.ts +9 -0
  94. package/ui/chat/ChatWindowCsatThankYou.js +29 -0
  95. package/ui/chat/ChatWindowCsatThankYou.js.map +1 -0
  96. package/ui/chat/ChatWindowQuote.d.ts +22 -0
  97. package/ui/chat/ChatWindowQuote.js +141 -0
  98. package/ui/chat/ChatWindowQuote.js.map +1 -0
  99. package/ui/tabs/Tabs.js +6 -6
  100. package/ui/tabs/Tabs.js.map +1 -1
  101. package/ui/ChatBody.js.map +0 -1
  102. package/ui/ChatButton.js.map +0 -1
  103. package/ui/ChatHeader.js.map +0 -1
  104. package/ui/ChatInput.js.map +0 -1
  105. package/ui/ChatTypingIndicator.js.map +0 -1
  106. package/ui/ChatWindow.js.map +0 -1
  107. package/ui/ChatWindowCsatPrompt.js.map +0 -1
  108. package/ui/ChatWindowCsatThankYou.js.map +0 -1
  109. package/ui/ChatWindowQuote.js.map +0 -1
@@ -0,0 +1,23 @@
1
+ export interface ChatMessageLabels {
2
+ replay: string;
3
+ edit: string;
4
+ copy: string;
5
+ /** Replaces the copy label while the copy confirmation is shown */
6
+ copied: string;
7
+ /** Cancels the in-place edition of a message */
8
+ cancel: string;
9
+ /** Submits the edited message */
10
+ send: string;
11
+ }
12
+ export interface ChatMessageActionsProps {
13
+ onReplay?: () => void;
14
+ onEdit?: () => void;
15
+ /** May be async: the confirmation is shown once the returned promise is resolved */
16
+ onCopy?: () => void | Promise<void>;
17
+ labels?: Partial<ChatMessageLabels>;
18
+ }
19
+ /**
20
+ * Actions of a user message. They are revealed when the message is hovered or focused (the parent
21
+ * must be a `group/message`), and always visible on devices without hover.
22
+ */
23
+ export declare function ChatMessageActions({ onReplay, onEdit, onCopy, labels, }: Readonly<ChatMessageActionsProps>): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,69 @@
1
+ "use client";
2
+ import { useEffect as e, useRef as t, useState as n } from "react";
3
+ import { jsx as r, jsxs as i } from "react/jsx-runtime";
4
+ import { Icon as a } from "@clubmed/trident-icons";
5
+ //#region lib/ui/chat/ChatMessageActions.tsx
6
+ var o = 2e3, s = "flex size-32 items-center justify-center rounded-full text-darkGrey outline-none hover:bg-lightSand hover:text-black focus-visible:ring-4 focus-visible:ring-black";
7
+ function c(r) {
8
+ let [i, a] = n(!1), s = t(void 0);
9
+ e(() => () => clearTimeout(s.current), []);
10
+ async function c() {
11
+ try {
12
+ await r?.();
13
+ } catch {
14
+ return;
15
+ }
16
+ a(!0), clearTimeout(s.current), s.current = setTimeout(() => a(!1), o);
17
+ }
18
+ return {
19
+ copied: i,
20
+ copy: c
21
+ };
22
+ }
23
+ function l({ onReplay: e, onEdit: t, onCopy: n, labels: o }) {
24
+ let { replay: l = "Replay", edit: u = "Edit", copy: d = "Copy", copied: f = "Copied" } = o ?? {}, { copied: p, copy: m } = c(n);
25
+ return /* @__PURE__ */ i("div", {
26
+ role: "toolbar",
27
+ "aria-label": "Message actions",
28
+ className: "flex gap-4 opacity-0 transition-opacity duration-250 group-focus-within/message:opacity-100 group-hover/message:opacity-100 [@media(hover:none)]:opacity-100",
29
+ children: [
30
+ e && /* @__PURE__ */ r("button", {
31
+ type: "button",
32
+ "aria-label": l,
33
+ title: l,
34
+ onClick: e,
35
+ className: s,
36
+ children: /* @__PURE__ */ r(a, {
37
+ name: "Reload",
38
+ width: "16px"
39
+ })
40
+ }),
41
+ t && /* @__PURE__ */ r("button", {
42
+ type: "button",
43
+ "aria-label": u,
44
+ title: u,
45
+ onClick: t,
46
+ className: s,
47
+ children: /* @__PURE__ */ r(a, {
48
+ name: "Edit",
49
+ width: "16px"
50
+ })
51
+ }),
52
+ n && /* @__PURE__ */ r("button", {
53
+ type: "button",
54
+ "aria-label": p ? f : d,
55
+ title: p ? f : d,
56
+ onClick: m,
57
+ className: s,
58
+ children: /* @__PURE__ */ r(a, {
59
+ name: p ? "CheckDefault" : "Clipboard",
60
+ width: "16px"
61
+ })
62
+ })
63
+ ]
64
+ });
65
+ }
66
+ //#endregion
67
+ export { l as ChatMessageActions };
68
+
69
+ //# sourceMappingURL=ChatMessageActions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatMessageActions.js","names":[],"sources":["../../../lib/ui/chat/ChatMessageActions.tsx"],"sourcesContent":["'use client';\n\nimport { Icon } from '@clubmed/trident-icons';\nimport { useEffect, useRef, useState } from 'react';\n\nexport interface ChatMessageLabels {\n replay: string;\n edit: string;\n copy: string;\n /** Replaces the copy label while the copy confirmation is shown */\n copied: string;\n /** Cancels the in-place edition of a message */\n cancel: string;\n /** Submits the edited message */\n send: string;\n}\n\nexport interface ChatMessageActionsProps {\n onReplay?: () => void;\n onEdit?: () => void;\n /** May be async: the confirmation is shown once the returned promise is resolved */\n onCopy?: () => void | Promise<void>;\n labels?: Partial<ChatMessageLabels>;\n}\n\nconst COPIED_DURATION_MS = 2000;\n\nconst BUTTON_CLASSES =\n 'flex size-32 items-center justify-center rounded-full text-darkGrey outline-none hover:bg-lightSand hover:text-black focus-visible:ring-4 focus-visible:ring-black';\n\n/** Shows a confirmation for a short time once the copy succeeded. */\nfunction useCopyConfirmation(onCopy?: () => void | Promise<void>) {\n const [copied, setCopied] = useState(false);\n const timer = useRef<ReturnType<typeof setTimeout>>(undefined);\n\n useEffect(() => () => clearTimeout(timer.current), []);\n\n async function copy() {\n try {\n await onCopy?.();\n } catch {\n return;\n }\n\n setCopied(true);\n clearTimeout(timer.current);\n timer.current = setTimeout(() => setCopied(false), COPIED_DURATION_MS);\n }\n\n return { copied, copy };\n}\n\n/**\n * Actions of a user message. They are revealed when the message is hovered or focused (the parent\n * must be a `group/message`), and always visible on devices without hover.\n */\nexport function ChatMessageActions({\n onReplay,\n onEdit,\n onCopy,\n labels,\n}: Readonly<ChatMessageActionsProps>) {\n const {\n replay = 'Replay',\n edit = 'Edit',\n copy = 'Copy',\n copied: copiedLabel = 'Copied',\n } = labels ?? {};\n const { copied, copy: handleCopy } = useCopyConfirmation(onCopy);\n\n return (\n <div\n role=\"toolbar\"\n aria-label=\"Message actions\"\n className=\"flex gap-4 opacity-0 transition-opacity duration-250 group-focus-within/message:opacity-100 group-hover/message:opacity-100 [@media(hover:none)]:opacity-100\"\n >\n {onReplay && (\n <button\n type=\"button\"\n aria-label={replay}\n title={replay}\n onClick={onReplay}\n className={BUTTON_CLASSES}\n >\n <Icon name=\"Reload\" width=\"16px\" />\n </button>\n )}\n {onEdit && (\n <button\n type=\"button\"\n aria-label={edit}\n title={edit}\n onClick={onEdit}\n className={BUTTON_CLASSES}\n >\n <Icon name=\"Edit\" width=\"16px\" />\n </button>\n )}\n {onCopy && (\n <button\n type=\"button\"\n aria-label={copied ? copiedLabel : copy}\n title={copied ? copiedLabel : copy}\n onClick={handleCopy}\n className={BUTTON_CLASSES}\n >\n <Icon name={copied ? 'CheckDefault' : 'Clipboard'} width=\"16px\" />\n </button>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;AAyBA,IAAM,IAAqB,KAErB,IACJ;AAGF,SAAS,EAAoB,GAAqC;CAChE,IAAM,CAAC,GAAQ,KAAa,EAAS,GAAM,EACrC,IAAQ,EAAsC,KAAA,EAAU;AAE9D,eAAsB,aAAa,EAAM,QAAQ,EAAE,EAAE,CAAC;CAEtD,eAAe,IAAO;AACpB,MAAI;AACF,SAAM,KAAU;UACV;AACN;;AAKF,EAFA,EAAU,GAAK,EACf,aAAa,EAAM,QAAQ,EAC3B,EAAM,UAAU,iBAAiB,EAAU,GAAM,EAAE,EAAmB;;AAGxE,QAAO;EAAE;EAAQ;EAAM;;AAOzB,SAAgB,EAAmB,EACjC,aACA,WACA,WACA,aACoC;CACpC,IAAM,EACJ,YAAS,UACT,UAAO,QACP,UAAO,QACP,QAAQ,IAAc,aACpB,KAAU,EAAE,EACV,EAAE,WAAQ,MAAM,MAAe,EAAoB,EAAO;AAEhE,QACE,kBAAC,OAAD;EACE,MAAK;EACL,cAAW;EACX,WAAU;YAHZ;GAKG,KACC,kBAAC,UAAD;IACE,MAAK;IACL,cAAY;IACZ,OAAO;IACP,SAAS;IACT,WAAW;cAEX,kBAAC,GAAD;KAAM,MAAK;KAAS,OAAM;KAAS,CAAA;IAC5B,CAAA;GAEV,KACC,kBAAC,UAAD;IACE,MAAK;IACL,cAAY;IACZ,OAAO;IACP,SAAS;IACT,WAAW;cAEX,kBAAC,GAAD;KAAM,MAAK;KAAO,OAAM;KAAS,CAAA;IAC1B,CAAA;GAEV,KACC,kBAAC,UAAD;IACE,MAAK;IACL,cAAY,IAAS,IAAc;IACnC,OAAO,IAAS,IAAc;IAC9B,SAAS;IACT,WAAW;cAEX,kBAAC,GAAD;KAAM,MAAM,IAAS,iBAAiB;KAAa,OAAM;KAAS,CAAA;IAC3D,CAAA"}
@@ -0,0 +1,9 @@
1
+ import { ChatMessageLabels } from './ChatMessageActions';
2
+ export interface ChatMessageEditorProps {
3
+ value: string;
4
+ labels?: Partial<ChatMessageLabels>;
5
+ onCancel: () => void;
6
+ onSubmit: (value: string) => void;
7
+ }
8
+ /** In-place editor of a user message: edit the text, then cancel or send it again. */
9
+ export declare function ChatMessageEditor({ value, labels, onCancel, onSubmit, }: Readonly<ChatMessageEditorProps>): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,57 @@
1
+ "use client";
2
+ import { Button as e } from "../buttons/Button.js";
3
+ import { useEffect as t, useRef as n, useState as r } from "react";
4
+ import { jsx as i, jsxs as a } from "react/jsx-runtime";
5
+ //#region lib/ui/chat/ChatMessageEditor.tsx
6
+ function o({ value: o, labels: s, onCancel: c, onSubmit: l }) {
7
+ let { cancel: u = "Cancel", send: d = "Send" } = s ?? {}, [f, p] = r(o), m = n(null), h = f.trim(), g = h.length > 0 && h !== o.trim();
8
+ t(() => {
9
+ let e = m.current;
10
+ e && (e.style.height = "auto", e.style.height = `${e.scrollHeight}px`);
11
+ }, [f]), t(() => {
12
+ let e = m.current;
13
+ e?.focus(), e?.setSelectionRange(e.value.length, e.value.length);
14
+ }, []);
15
+ function _() {
16
+ g && l(h);
17
+ }
18
+ function v(e) {
19
+ e.preventDefault(), _();
20
+ }
21
+ function y(e) {
22
+ e.key === "Escape" ? (e.preventDefault(), c()) : e.key === "Enter" && (e.metaKey || e.ctrlKey) && (e.preventDefault(), _());
23
+ }
24
+ return /* @__PURE__ */ a("form", {
25
+ onSubmit: v,
26
+ className: "bg-lightSand flex w-full flex-col gap-12 rounded-32 px-24 py-16",
27
+ children: [/* @__PURE__ */ i("textarea", {
28
+ ref: m,
29
+ value: f,
30
+ onChange: (e) => p(e.target.value),
31
+ onKeyDown: y,
32
+ "aria-label": "Edit message",
33
+ rows: 1,
34
+ className: "text-b3 scrollbar-hidden max-h-[40vh] w-full resize-none bg-transparent text-black outline-none"
35
+ }), /* @__PURE__ */ a("div", {
36
+ className: "flex justify-end gap-8",
37
+ children: [/* @__PURE__ */ i(e, {
38
+ type: "button",
39
+ size: "small",
40
+ theme: "outline",
41
+ color: "black",
42
+ onClick: c,
43
+ children: u
44
+ }), /* @__PURE__ */ i(e, {
45
+ type: "submit",
46
+ size: "small",
47
+ color: "black",
48
+ disabled: !g,
49
+ children: d
50
+ })]
51
+ })]
52
+ });
53
+ }
54
+ //#endregion
55
+ export { o as ChatMessageEditor };
56
+
57
+ //# sourceMappingURL=ChatMessageEditor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatMessageEditor.js","names":[],"sources":["../../../lib/ui/chat/ChatMessageEditor.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef, useState } from 'react';\nimport type { FormEvent, KeyboardEvent } from 'react';\nimport { Button } from '../buttons/Button';\nimport type { ChatMessageLabels } from './ChatMessageActions';\n\nexport interface ChatMessageEditorProps {\n value: string;\n labels?: Partial<ChatMessageLabels>;\n onCancel: () => void;\n onSubmit: (value: string) => void;\n}\n\n/** In-place editor of a user message: edit the text, then cancel or send it again. */\nexport function ChatMessageEditor({\n value,\n labels,\n onCancel,\n onSubmit,\n}: Readonly<ChatMessageEditorProps>) {\n const { cancel = 'Cancel', send = 'Send' } = labels ?? {};\n const [draft, setDraft] = useState(value);\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const trimmed = draft.trim();\n const canSubmit = trimmed.length > 0 && trimmed !== value.trim();\n\n useEffect(() => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n\n textarea.style.height = 'auto';\n textarea.style.height = `${textarea.scrollHeight}px`;\n }, [draft]);\n\n useEffect(() => {\n const textarea = textareaRef.current;\n textarea?.focus();\n textarea?.setSelectionRange(textarea.value.length, textarea.value.length);\n }, []);\n\n function submit() {\n if (canSubmit) onSubmit(trimmed);\n }\n\n function handleSubmit(e: FormEvent) {\n e.preventDefault();\n submit();\n }\n\n function handleKeyDown(e: KeyboardEvent<HTMLTextAreaElement>) {\n if (e.key === 'Escape') {\n e.preventDefault();\n onCancel();\n } else if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n submit();\n }\n }\n\n return (\n <form\n onSubmit={handleSubmit}\n className=\"bg-lightSand flex w-full flex-col gap-12 rounded-32 px-24 py-16\"\n >\n <textarea\n ref={textareaRef}\n value={draft}\n onChange={(e) => setDraft(e.target.value)}\n onKeyDown={handleKeyDown}\n aria-label=\"Edit message\"\n rows={1}\n className=\"text-b3 scrollbar-hidden max-h-[40vh] w-full resize-none bg-transparent text-black outline-none\"\n />\n <div className=\"flex justify-end gap-8\">\n <Button type=\"button\" size=\"small\" theme=\"outline\" color=\"black\" onClick={onCancel}>\n {cancel}\n </Button>\n <Button type=\"submit\" size=\"small\" color=\"black\" disabled={!canSubmit}>\n {send}\n </Button>\n </div>\n </form>\n );\n}\n"],"mappings":";;;;;AAeA,SAAgB,EAAkB,EAChC,UACA,WACA,aACA,eACmC;CACnC,IAAM,EAAE,YAAS,UAAU,UAAO,WAAW,KAAU,EAAE,EACnD,CAAC,GAAO,KAAY,EAAS,EAAM,EACnC,IAAc,EAA4B,KAAK,EAC/C,IAAU,EAAM,MAAM,EACtB,IAAY,EAAQ,SAAS,KAAK,MAAY,EAAM,MAAM;AAUhE,CARA,QAAgB;EACd,IAAM,IAAW,EAAY;AACxB,QAEL,EAAS,MAAM,SAAS,QACxB,EAAS,MAAM,SAAS,GAAG,EAAS,aAAa;IAChD,CAAC,EAAM,CAAC,EAEX,QAAgB;EACd,IAAM,IAAW,EAAY;AAE7B,EADA,GAAU,OAAO,EACjB,GAAU,kBAAkB,EAAS,MAAM,QAAQ,EAAS,MAAM,OAAO;IACxE,EAAE,CAAC;CAEN,SAAS,IAAS;AAChB,EAAI,KAAW,EAAS,EAAQ;;CAGlC,SAAS,EAAa,GAAc;AAElC,EADA,EAAE,gBAAgB,EAClB,GAAQ;;CAGV,SAAS,EAAc,GAAuC;AAC5D,EAAI,EAAE,QAAQ,YACZ,EAAE,gBAAgB,EAClB,GAAU,IACD,EAAE,QAAQ,YAAY,EAAE,WAAW,EAAE,aAC9C,EAAE,gBAAgB,EAClB,GAAQ;;AAIZ,QACE,kBAAC,QAAD;EACE,UAAU;EACV,WAAU;YAFZ,CAIE,kBAAC,YAAD;GACE,KAAK;GACL,OAAO;GACP,WAAW,MAAM,EAAS,EAAE,OAAO,MAAM;GACzC,WAAW;GACX,cAAW;GACX,MAAM;GACN,WAAU;GACV,CAAA,EACF,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,GAAD;IAAQ,MAAK;IAAS,MAAK;IAAQ,OAAM;IAAU,OAAM;IAAQ,SAAS;cACvE;IACM,CAAA,EACT,kBAAC,GAAD;IAAQ,MAAK;IAAS,MAAK;IAAQ,OAAM;IAAQ,UAAU,CAAC;cACzD;IACM,CAAA,CAAA"}
@@ -0,0 +1,2 @@
1
+ import { HTMLAttributes } from 'react';
2
+ export declare const ChatTypingIndicator: import('react').NamedExoticComponent<HTMLAttributes<HTMLDivElement>>;
@@ -0,0 +1,30 @@
1
+ import { twMerge as e } from "../helpers/twMerge.js";
2
+ import { memo as t } from "react";
3
+ import { jsx as n, jsxs as r } from "react/jsx-runtime";
4
+ //#region lib/ui/chat/ChatTypingIndicator.tsx
5
+ var i = t(function({ className: t, ...i }) {
6
+ return /* @__PURE__ */ r("div", {
7
+ role: "status",
8
+ "aria-label": "Typing",
9
+ ...i,
10
+ className: e("flex items-center gap-4 h-34 w-66 overflow-hidden rounded-pill bg-pearl px-20", t),
11
+ children: [
12
+ /* @__PURE__ */ n("span", {
13
+ "aria-hidden": "true",
14
+ className: "size-6 rounded-full bg-black animate-bounce [animation-delay:-300ms]"
15
+ }),
16
+ /* @__PURE__ */ n("span", {
17
+ "aria-hidden": "true",
18
+ className: "size-6 rounded-full bg-black animate-bounce [animation-delay:-150ms]"
19
+ }),
20
+ /* @__PURE__ */ n("span", {
21
+ "aria-hidden": "true",
22
+ className: "size-6 rounded-full bg-black animate-bounce"
23
+ })
24
+ ]
25
+ });
26
+ });
27
+ //#endregion
28
+ export { i as ChatTypingIndicator };
29
+
30
+ //# sourceMappingURL=ChatTypingIndicator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatTypingIndicator.js","names":[],"sources":["../../../lib/ui/chat/ChatTypingIndicator.tsx"],"sourcesContent":["import { memo, type HTMLAttributes } from 'react';\nimport { twMerge } from '../helpers/twMerge';\n\nexport const ChatTypingIndicator = memo(function ChatTypingIndicator({\n className,\n ...props\n}: HTMLAttributes<HTMLDivElement>) {\n return (\n <div\n role=\"status\"\n aria-label=\"Typing\"\n {...props}\n className={twMerge(\n 'flex items-center gap-4 h-34 w-66 overflow-hidden rounded-pill bg-pearl px-20',\n className,\n )}\n >\n <span\n aria-hidden=\"true\"\n className=\"size-6 rounded-full bg-black animate-bounce [animation-delay:-300ms]\"\n />\n <span\n aria-hidden=\"true\"\n className=\"size-6 rounded-full bg-black animate-bounce [animation-delay:-150ms]\"\n />\n <span aria-hidden=\"true\" className=\"size-6 rounded-full bg-black animate-bounce\" />\n </div>\n );\n});\n"],"mappings":";;;;AAGA,IAAa,IAAsB,EAAK,SAA6B,EACnE,cACA,GAAG,KAC8B;AACjC,QACE,kBAAC,OAAD;EACE,MAAK;EACL,cAAW;EACX,GAAI;EACJ,WAAW,EACT,iFACA,EACD;YAPH;GASE,kBAAC,QAAD;IACE,eAAY;IACZ,WAAU;IACV,CAAA;GACF,kBAAC,QAAD;IACE,eAAY;IACZ,WAAU;IACV,CAAA;GACF,kBAAC,QAAD;IAAM,eAAY;IAAO,WAAU;IAAgD,CAAA;GAC/E;;EAER"}
@@ -0,0 +1,37 @@
1
+ import { ReactNode } from 'react';
2
+ import { ChatMessageSender } from './ChatMessage';
3
+ import { ChatWindowCsatPromptLabels } from './ChatWindowCsatPrompt';
4
+ import { ChatWindowCsatThankYouLabels } from './ChatWindowCsatThankYou';
5
+ import { ChatWindowQuoteItem } from './ChatWindowQuote';
6
+ import { ChatHeaderProps } from './ChatHeader';
7
+ import { ChatBodyProps } from './ChatBody';
8
+ import { ChatInputProps } from './ChatInput';
9
+ export type { ChatWindowCsatPromptLabels, ChatWindowCsatThankYouLabels, ChatWindowQuoteItem };
10
+ export interface ChatWindowMessage {
11
+ id: string;
12
+ sender: ChatMessageSender;
13
+ content: ReactNode;
14
+ }
15
+ interface ChatWindowInputProps {
16
+ inputValue: ChatInputProps['value'];
17
+ inputPlaceholder?: ChatInputProps['placeholder'];
18
+ onInputChange: ChatInputProps['onValueChange'];
19
+ onSubmit: ChatInputProps['onSubmit'];
20
+ onAudioClick?: ChatInputProps['onAudioClick'];
21
+ submitKey?: ChatInputProps['submitKey'];
22
+ isStreaming?: ChatInputProps['isStreaming'];
23
+ onStop?: ChatInputProps['onStop'];
24
+ }
25
+ interface ChatWindowBaseProps extends ChatHeaderProps {
26
+ onClose?: () => void;
27
+ }
28
+ export type ChatWindowProps = ChatWindowBaseProps & (({
29
+ children: ReactNode;
30
+ } & Partial<ChatBodyProps> & Partial<ChatWindowInputProps>) | (ChatBodyProps & ChatWindowInputProps & {
31
+ children?: undefined;
32
+ }));
33
+ export interface ChatWindowHandle {
34
+ askForFeedback: () => void;
35
+ showQuotes: (quotes: ChatWindowQuoteItem[]) => void;
36
+ }
37
+ export declare const ChatWindow: import('react').ForwardRefExoticComponent<ChatWindowProps & import('react').RefAttributes<ChatWindowHandle>>;
@@ -0,0 +1,65 @@
1
+ "use client";
2
+ import { twMerge as e } from "../helpers/twMerge.js";
3
+ import { ChatBody as t } from "./ChatBody.js";
4
+ import { ChatInput as n } from "./ChatInput.js";
5
+ import { ChatHeader as r } from "./ChatHeader.js";
6
+ import { forwardRef as i } from "react";
7
+ import { Fragment as a, jsx as o, jsxs as s } from "react/jsx-runtime";
8
+ import { Icon as c } from "@clubmed/trident-icons";
9
+ //#region lib/ui/chat/ChatWindow.tsx
10
+ var l = i(function({ title: i, onClose: l, logo: u, className: d, children: f, inputValue: p, inputPlaceholder: m, onInputChange: h, onSubmit: g, onAudioClick: _, submitKey: v, isStreaming: y, onStop: b, onMessageReplay: x, onMessageEdit: S, onMessageCopy: C, messageActionLabels: w, greeting: T, messages: E, isTyping: D, onFeedbackRate: O, csatLabels: k, csatRenderRatingLabel: A, csatThankYouLabels: j, onQuoteCtaClick: M, ...N }, P) {
11
+ return /* @__PURE__ */ s("div", {
12
+ ...N,
13
+ className: e("flex flex-col gap-20 items-center", d),
14
+ children: [/* @__PURE__ */ o("div", {
15
+ className: "chat-border bg-white flex flex-col h-full w-full rounded-32 overflow-hidden",
16
+ children: f ?? /* @__PURE__ */ s(a, { children: [
17
+ /* @__PURE__ */ o(r, {
18
+ title: i,
19
+ logo: u
20
+ }),
21
+ /* @__PURE__ */ o(t, {
22
+ greeting: T,
23
+ messages: E,
24
+ isTyping: D,
25
+ onMessageReplay: x,
26
+ onMessageEdit: S,
27
+ onMessageCopy: C,
28
+ messageActionLabels: w,
29
+ onFeedbackRate: O,
30
+ csatLabels: k,
31
+ csatRenderRatingLabel: A,
32
+ csatThankYouLabels: j,
33
+ onQuoteCtaClick: M,
34
+ ref: P
35
+ }),
36
+ /* @__PURE__ */ o("div", {
37
+ className: "shrink-0 px-8 pb-8 pt-12",
38
+ children: /* @__PURE__ */ o(n, {
39
+ value: p,
40
+ placeholder: m,
41
+ onValueChange: h,
42
+ onSubmit: g,
43
+ onAudioClick: _,
44
+ submitKey: v,
45
+ isStreaming: y,
46
+ onStop: b
47
+ })
48
+ })
49
+ ] })
50
+ }), l && /* @__PURE__ */ o("button", {
51
+ type: "button",
52
+ "aria-label": "Close chat",
53
+ onClick: l,
54
+ className: "flex items-center justify-center size-48 rounded-full bg-black border border-black text-white shrink-0 hover:opacity-80 transition-opacity outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2",
55
+ children: /* @__PURE__ */ o(c, {
56
+ name: "CrossDefault",
57
+ width: "24px"
58
+ })
59
+ })]
60
+ });
61
+ });
62
+ //#endregion
63
+ export { l as ChatWindow };
64
+
65
+ //# sourceMappingURL=ChatWindow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatWindow.js","names":[],"sources":["../../../lib/ui/chat/ChatWindow.tsx"],"sourcesContent":["'use client';\n\nimport { Icon } from '@clubmed/trident-icons';\nimport { forwardRef, type ReactNode } from 'react';\nimport { twMerge } from '../helpers/twMerge';\nimport type { ChatMessageSender } from './ChatMessage';\nimport type { ChatWindowCsatPromptLabels } from './ChatWindowCsatPrompt';\nimport type { ChatWindowCsatThankYouLabels } from './ChatWindowCsatThankYou';\nimport type { ChatWindowQuoteItem } from './ChatWindowQuote';\nimport { ChatHeader, type ChatHeaderProps } from './ChatHeader';\nimport { ChatBody, type ChatBodyProps } from './ChatBody';\nimport { ChatInput, type ChatInputProps } from './ChatInput';\n\nexport type { ChatWindowCsatPromptLabels, ChatWindowCsatThankYouLabels, ChatWindowQuoteItem };\n\nexport interface ChatWindowMessage {\n id: string;\n sender: ChatMessageSender;\n content: ReactNode;\n}\n\ninterface ChatWindowInputProps {\n inputValue: ChatInputProps['value'];\n inputPlaceholder?: ChatInputProps['placeholder'];\n onInputChange: ChatInputProps['onValueChange'];\n onSubmit: ChatInputProps['onSubmit'];\n onAudioClick?: ChatInputProps['onAudioClick'];\n submitKey?: ChatInputProps['submitKey'];\n isStreaming?: ChatInputProps['isStreaming'];\n onStop?: ChatInputProps['onStop'];\n}\n\ninterface ChatWindowBaseProps extends ChatHeaderProps {\n onClose?: () => void;\n}\n\n// Either compose the panel from `ChatHeader`/`ChatBody`/`ChatInput` children,\n// or fall back to the opaque props-only form — in which case the message-log\n// and input props become required.\nexport type ChatWindowProps = ChatWindowBaseProps &\n (\n | ({ children: ReactNode } & Partial<ChatBodyProps> & Partial<ChatWindowInputProps>)\n | (ChatBodyProps & ChatWindowInputProps & { children?: undefined })\n );\n\nexport interface ChatWindowHandle {\n askForFeedback: () => void;\n showQuotes: (quotes: ChatWindowQuoteItem[]) => void;\n}\n\nexport const ChatWindow = forwardRef<ChatWindowHandle, ChatWindowProps>(function ChatWindow(\n {\n title,\n onClose,\n logo,\n className,\n children,\n inputValue,\n inputPlaceholder,\n onInputChange,\n onSubmit,\n onAudioClick,\n submitKey,\n isStreaming,\n onStop,\n onMessageReplay,\n onMessageEdit,\n onMessageCopy,\n messageActionLabels,\n greeting,\n messages,\n isTyping,\n onFeedbackRate,\n csatLabels,\n csatRenderRatingLabel,\n csatThankYouLabels,\n onQuoteCtaClick,\n ...props\n },\n ref,\n) {\n return (\n <div {...props} className={twMerge('flex flex-col gap-20 items-center', className)}>\n <div className=\"chat-border bg-white flex flex-col h-full w-full rounded-32 overflow-hidden\">\n {children ?? (\n <>\n <ChatHeader title={title} logo={logo} />\n <ChatBody\n greeting={greeting}\n messages={messages!}\n isTyping={isTyping}\n onMessageReplay={onMessageReplay}\n onMessageEdit={onMessageEdit}\n onMessageCopy={onMessageCopy}\n messageActionLabels={messageActionLabels}\n onFeedbackRate={onFeedbackRate}\n csatLabels={csatLabels}\n csatRenderRatingLabel={csatRenderRatingLabel}\n csatThankYouLabels={csatThankYouLabels}\n onQuoteCtaClick={onQuoteCtaClick}\n ref={ref}\n />\n <div className=\"shrink-0 px-8 pb-8 pt-12\">\n <ChatInput\n value={inputValue!}\n placeholder={inputPlaceholder}\n onValueChange={onInputChange!}\n onSubmit={onSubmit!}\n onAudioClick={onAudioClick}\n submitKey={submitKey}\n isStreaming={isStreaming}\n onStop={onStop}\n />\n </div>\n </>\n )}\n </div>\n\n {onClose && (\n <button\n type=\"button\"\n aria-label=\"Close chat\"\n onClick={onClose}\n className=\"flex items-center justify-center size-48 rounded-full bg-black border border-black text-white shrink-0 hover:opacity-80 transition-opacity outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2\"\n >\n <Icon name=\"CrossDefault\" width=\"24px\" />\n </button>\n )}\n </div>\n );\n});\n"],"mappings":";;;;;;;;;AAkDA,IAAa,IAAa,EAA8C,SACtE,EACE,UACA,YACA,SACA,cACA,aACA,eACA,qBACA,kBACA,aACA,iBACA,cACA,gBACA,WACA,oBACA,kBACA,kBACA,wBACA,aACA,aACA,aACA,mBACA,eACA,0BACA,uBACA,oBACA,GAAG,KAEL,GACA;AACA,QACE,kBAAC,OAAD;EAAK,GAAI;EAAO,WAAW,EAAQ,qCAAqC,EAAU;YAAlF,CACE,kBAAC,OAAD;GAAK,WAAU;aACZ,KACC,kBAAA,GAAA,EAAA,UAAA;IACE,kBAAC,GAAD;KAAmB;KAAa;KAAQ,CAAA;IACxC,kBAAC,GAAD;KACY;KACA;KACA;KACO;KACF;KACA;KACM;KACL;KACJ;KACW;KACH;KACH;KACZ;KACL,CAAA;IACF,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD;MACE,OAAO;MACP,aAAa;MACb,eAAe;MACL;MACI;MACH;MACE;MACL;MACR,CAAA;KACE,CAAA;IACL,EAAA,CAAA;GAED,CAAA,EAEL,KACC,kBAAC,UAAD;GACE,MAAK;GACL,cAAW;GACX,SAAS;GACT,WAAU;aAEV,kBAAC,GAAD;IAAM,MAAK;IAAe,OAAM;IAAS,CAAA;GAClC,CAAA,CAAA;;EAIf"}
@@ -0,0 +1,15 @@
1
+ import { ReactNode } from 'react';
2
+ export interface ChatWindowCsatPromptLabels {
3
+ intro: string;
4
+ question: string;
5
+ questionNote: string;
6
+ groupAriaLabel: string;
7
+ starLabels: [string, string, string, string, string];
8
+ }
9
+ export interface ChatWindowCsatPromptProps {
10
+ onRate: (rating: number) => void;
11
+ labels: ChatWindowCsatPromptLabels;
12
+ renderRatingLabel: (rating: number) => ReactNode;
13
+ className?: string;
14
+ }
15
+ export declare function ChatWindowCsatPrompt({ onRate, labels, renderRatingLabel, className, }: ChatWindowCsatPromptProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,71 @@
1
+ "use client";
2
+ import { twMerge as e } from "../helpers/twMerge.js";
3
+ import { useState as t } from "react";
4
+ import { jsx as n, jsxs as r } from "react/jsx-runtime";
5
+ import { Icon as i } from "@clubmed/trident-icons";
6
+ //#region lib/ui/chat/ChatWindowCsatPrompt.tsx
7
+ function a({ onRate: a, labels: o, renderRatingLabel: s, className: c }) {
8
+ let [l, u] = t(0), [d, f] = t(0);
9
+ function p(e) {
10
+ d > 0 || (f(e), a(e));
11
+ }
12
+ let m = l || d, h = d > 0;
13
+ return /* @__PURE__ */ r("div", {
14
+ className: e("flex flex-col gap-20 px-20", c),
15
+ children: [
16
+ /* @__PURE__ */ r("div", {
17
+ className: "flex flex-col gap-4",
18
+ children: [/* @__PURE__ */ n("p", {
19
+ className: "text-b3 text-black",
20
+ children: o.intro
21
+ }), /* @__PURE__ */ r("p", {
22
+ className: "text-b3 text-black",
23
+ children: [
24
+ o.question,
25
+ " ",
26
+ /* @__PURE__ */ n("span", {
27
+ className: "text-b4 text-black",
28
+ children: o.questionNote
29
+ })
30
+ ]
31
+ })]
32
+ }),
33
+ /* @__PURE__ */ n("div", {
34
+ role: "group",
35
+ "aria-label": o.groupAriaLabel,
36
+ className: "flex items-center gap-0",
37
+ children: o.starLabels.map((t, r) => {
38
+ let a = r + 1, o = a <= m;
39
+ return /* @__PURE__ */ n("button", {
40
+ type: "button",
41
+ "aria-label": `${a} star${a > 1 ? "s" : ""} — ${t}`,
42
+ "aria-pressed": d === a,
43
+ "aria-disabled": h,
44
+ onClick: () => p(a),
45
+ onMouseEnter: () => {
46
+ h || u(a);
47
+ },
48
+ onMouseLeave: () => {
49
+ h || u(0);
50
+ },
51
+ className: e("flex items-center justify-center size-48 rounded-full transition-colors outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2", h ? "cursor-default" : "hover:bg-lightSand"),
52
+ children: /* @__PURE__ */ n(i, {
53
+ name: o ? "StarFilled" : "StarOutlined",
54
+ width: "24px",
55
+ className: "text-black"
56
+ })
57
+ }, a);
58
+ })
59
+ }),
60
+ m > 0 && /* @__PURE__ */ n("p", {
61
+ className: "text-b4 text-darkGrey",
62
+ "aria-live": "polite",
63
+ children: s(m)
64
+ })
65
+ ]
66
+ });
67
+ }
68
+ //#endregion
69
+ export { a as ChatWindowCsatPrompt };
70
+
71
+ //# sourceMappingURL=ChatWindowCsatPrompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatWindowCsatPrompt.js","names":[],"sources":["../../../lib/ui/chat/ChatWindowCsatPrompt.tsx"],"sourcesContent":["'use client';\n\nimport { Icon } from '@clubmed/trident-icons';\nimport type { ReactNode } from 'react';\nimport { useState } from 'react';\nimport { twMerge } from '../helpers/twMerge';\n\nexport interface ChatWindowCsatPromptLabels {\n intro: string;\n question: string;\n questionNote: string;\n groupAriaLabel: string;\n starLabels: [string, string, string, string, string];\n}\n\nexport interface ChatWindowCsatPromptProps {\n onRate: (rating: number) => void;\n labels: ChatWindowCsatPromptLabels;\n renderRatingLabel: (rating: number) => ReactNode;\n className?: string;\n}\n\nexport function ChatWindowCsatPrompt({\n onRate,\n labels,\n renderRatingLabel,\n className,\n}: ChatWindowCsatPromptProps) {\n const [hovered, setHovered] = useState(0);\n const [selected, setSelected] = useState(0);\n\n function handleSelect(rating: number) {\n if (selected > 0) return;\n setSelected(rating);\n onRate(rating);\n }\n\n const activeRating = hovered || selected;\n const isLocked = selected > 0;\n\n return (\n <div className={twMerge('flex flex-col gap-20 px-20', className)}>\n <div className=\"flex flex-col gap-4\">\n <p className=\"text-b3 text-black\">{labels.intro}</p>\n <p className=\"text-b3 text-black\">\n {labels.question} <span className=\"text-b4 text-black\">{labels.questionNote}</span>\n </p>\n </div>\n\n <div role=\"group\" aria-label={labels.groupAriaLabel} className=\"flex items-center gap-0\">\n {labels.starLabels.map((label, i) => {\n const rating = i + 1;\n const isFilled = rating <= activeRating;\n return (\n <button\n key={rating}\n type=\"button\"\n aria-label={`${rating} star${rating > 1 ? 's' : ''} — ${label}`}\n aria-pressed={selected === rating}\n aria-disabled={isLocked}\n onClick={() => handleSelect(rating)}\n onMouseEnter={() => {\n if (!isLocked) setHovered(rating);\n }}\n onMouseLeave={() => {\n if (!isLocked) setHovered(0);\n }}\n className={twMerge(\n 'flex items-center justify-center size-48 rounded-full transition-colors outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2',\n isLocked ? 'cursor-default' : 'hover:bg-lightSand',\n )}\n >\n <Icon\n name={isFilled ? 'StarFilled' : 'StarOutlined'}\n width=\"24px\"\n className=\"text-black\"\n />\n </button>\n );\n })}\n </div>\n\n {activeRating > 0 && (\n <p className=\"text-b4 text-darkGrey\" aria-live=\"polite\">\n {renderRatingLabel(activeRating)}\n </p>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;AAsBA,SAAgB,EAAqB,EACnC,WACA,WACA,sBACA,gBAC4B;CAC5B,IAAM,CAAC,GAAS,KAAc,EAAS,EAAE,EACnC,CAAC,GAAU,KAAe,EAAS,EAAE;CAE3C,SAAS,EAAa,GAAgB;AAChC,MAAW,MACf,EAAY,EAAO,EACnB,EAAO,EAAO;;CAGhB,IAAM,IAAe,KAAW,GAC1B,IAAW,IAAW;AAE5B,QACE,kBAAC,OAAD;EAAK,WAAW,EAAQ,8BAA8B,EAAU;YAAhE;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,KAAD;KAAG,WAAU;eAAsB,EAAO;KAAU,CAAA,EACpD,kBAAC,KAAD;KAAG,WAAU;eAAb;MACG,EAAO;MAAS;MAAC,kBAAC,QAAD;OAAM,WAAU;iBAAsB,EAAO;OAAoB,CAAA;;;;GAIvF,kBAAC,OAAD;IAAK,MAAK;IAAQ,cAAY,EAAO;IAAgB,WAAU;cAC5D,EAAO,WAAW,KAAK,GAAO,MAAM;KACnC,IAAM,IAAS,IAAI,GACb,IAAW,KAAU;AAC3B,YACE,kBAAC,UAAD;MAEE,MAAK;MACL,cAAY,GAAG,EAAO,OAAO,IAAS,IAAI,MAAM,GAAG,KAAK;MACxD,gBAAc,MAAa;MAC3B,iBAAe;MACf,eAAe,EAAa,EAAO;MACnC,oBAAoB;AAClB,OAAK,KAAU,EAAW,EAAO;;MAEnC,oBAAoB;AAClB,OAAK,KAAU,EAAW,EAAE;;MAE9B,WAAW,EACT,kKACA,IAAW,mBAAmB,qBAC/B;gBAED,kBAAC,GAAD;OACE,MAAM,IAAW,eAAe;OAChC,OAAM;OACN,WAAU;OACV,CAAA;MACK,EAtBF,EAsBE;;IAGT,CAAA;GAEL,IAAe,KACd,kBAAC,KAAD;IAAG,WAAU;IAAwB,aAAU;cAC5C,EAAkB,EAAA;IACjB,CAAA"}
@@ -0,0 +1,9 @@
1
+ import { HTMLAttributes } from 'react';
2
+ export interface ChatWindowCsatThankYouLabels {
3
+ title: string;
4
+ body: string;
5
+ }
6
+ export interface ChatWindowCsatThankYouProps extends HTMLAttributes<HTMLDivElement> {
7
+ labels: ChatWindowCsatThankYouLabels;
8
+ }
9
+ export declare function ChatWindowCsatThankYou({ labels, className, ...props }: ChatWindowCsatThankYouProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,29 @@
1
+ "use client";
2
+ import { twMerge as e } from "../helpers/twMerge.js";
3
+ import { jsx as t, jsxs as n } from "react/jsx-runtime";
4
+ import { Icon as r } from "@clubmed/trident-icons";
5
+ //#region lib/ui/chat/ChatWindowCsatThankYou.tsx
6
+ function i({ labels: i, className: a, ...o }) {
7
+ return /* @__PURE__ */ n("div", {
8
+ ...o,
9
+ className: e("flex flex-col gap-20 px-20", a),
10
+ children: [/* @__PURE__ */ n("div", {
11
+ className: "flex items-center gap-4 text-green",
12
+ children: [/* @__PURE__ */ t(r, {
13
+ name: "CheckFilled",
14
+ width: "24px",
15
+ className: "shrink-0"
16
+ }), /* @__PURE__ */ t("p", {
17
+ className: "text-b3 font-semibold text-black",
18
+ children: i.title
19
+ })]
20
+ }), /* @__PURE__ */ t("p", {
21
+ className: "text-b3 text-black",
22
+ children: i.body
23
+ })]
24
+ });
25
+ }
26
+ //#endregion
27
+ export { i as ChatWindowCsatThankYou };
28
+
29
+ //# sourceMappingURL=ChatWindowCsatThankYou.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatWindowCsatThankYou.js","names":[],"sources":["../../../lib/ui/chat/ChatWindowCsatThankYou.tsx"],"sourcesContent":["'use client';\n\nimport { Icon } from '@clubmed/trident-icons';\nimport type { HTMLAttributes } from 'react';\nimport { twMerge } from '../helpers/twMerge';\n\nexport interface ChatWindowCsatThankYouLabels {\n title: string;\n body: string;\n}\n\nexport interface ChatWindowCsatThankYouProps extends HTMLAttributes<HTMLDivElement> {\n labels: ChatWindowCsatThankYouLabels;\n}\n\nexport function ChatWindowCsatThankYou({\n labels,\n className,\n ...props\n}: ChatWindowCsatThankYouProps) {\n return (\n <div {...props} className={twMerge('flex flex-col gap-20 px-20', className)}>\n <div className=\"flex items-center gap-4 text-green\">\n <Icon name=\"CheckFilled\" width=\"24px\" className=\"shrink-0\" />\n <p className=\"text-b3 font-semibold text-black\">{labels.title}</p>\n </div>\n <p className=\"text-b3 text-black\">{labels.body}</p>\n </div>\n );\n}\n"],"mappings":";;;;;AAeA,SAAgB,EAAuB,EACrC,WACA,cACA,GAAG,KAC2B;AAC9B,QACE,kBAAC,OAAD;EAAK,GAAI;EAAO,WAAW,EAAQ,8BAA8B,EAAU;YAA3E,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,GAAD;IAAM,MAAK;IAAc,OAAM;IAAO,WAAU;IAAa,CAAA,EAC7D,kBAAC,KAAD;IAAG,WAAU;cAAoC,EAAO;IAAU,CAAA,CAAA;MAEpE,kBAAC,KAAD;GAAG,WAAU;aAAsB,EAAO;GAAS,CAAA,CAAA"}
@@ -0,0 +1,22 @@
1
+ export interface ChatWindowQuoteItem {
2
+ id: string;
3
+ resortName: string;
4
+ country: string;
5
+ imageUrl: string;
6
+ /** Date range, e.g. "15 June 2026 to 24 June 2026" */
7
+ dateRange: string;
8
+ /** Pax info, e.g. "for 2 adults 2 children" */
9
+ pax: string;
10
+ /** CTA button label. Defaults to "See prices" */
11
+ ctaLabel?: string;
12
+ /** Bullet-point package items. Shown in single-quote inline mode only */
13
+ packageIncludes?: string[];
14
+ /** Free-text paragraph shown in single-quote inline mode only */
15
+ additionalInfo?: string;
16
+ }
17
+ export interface ChatWindowQuoteProps {
18
+ quotes: ChatWindowQuoteItem[];
19
+ onCtaClick?: (quote: ChatWindowQuoteItem) => void;
20
+ className?: string;
21
+ }
22
+ export declare function ChatWindowQuote({ quotes, onCtaClick, className }: ChatWindowQuoteProps): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,141 @@
1
+ "use client";
2
+ import { Button as e } from "../buttons/Button.js";
3
+ import { twMerge as t } from "../helpers/twMerge.js";
4
+ import { Scrollbar as n } from "../Scrollbar.js";
5
+ import { jsx as r, jsxs as i } from "react/jsx-runtime";
6
+ //#region lib/ui/chat/ChatWindowQuote.tsx
7
+ function a({ quote: t, onCtaClick: n }) {
8
+ return /* @__PURE__ */ i("article", {
9
+ "aria-label": t.resortName,
10
+ className: "flex flex-col bg-white border border-lightGrey rounded-32 shadow-[0px_2px_14px_0px_rgba(0,0,0,0.25)] overflow-hidden p-24 gap-16",
11
+ children: [/* @__PURE__ */ i("div", {
12
+ className: "relative h-210 w-full overflow-hidden rounded-16",
13
+ children: [
14
+ /* @__PURE__ */ r("img", {
15
+ src: t.imageUrl,
16
+ alt: "",
17
+ className: "absolute inset-0 size-full object-cover"
18
+ }),
19
+ /* @__PURE__ */ r("div", { className: "absolute inset-0 bg-gradient-to-b from-transparent from-50% to-[rgba(36,36,36,0.4)]" }),
20
+ /* @__PURE__ */ i("div", {
21
+ className: "absolute bottom-20 inset-x-0 flex flex-col items-center text-white text-center px-20",
22
+ children: [/* @__PURE__ */ r("p", {
23
+ className: "text-b2 font-semibold",
24
+ children: t.resortName
25
+ }), /* @__PURE__ */ r("p", {
26
+ className: "text-b3",
27
+ children: t.country
28
+ })]
29
+ })
30
+ ]
31
+ }), /* @__PURE__ */ i("div", {
32
+ className: "flex flex-col gap-16 items-center",
33
+ children: [
34
+ /* @__PURE__ */ i("p", {
35
+ className: "text-b4 font-semibold text-black text-center",
36
+ children: [
37
+ t.resortName,
38
+ ", ",
39
+ t.dateRange,
40
+ ", ",
41
+ t.pax
42
+ ]
43
+ }),
44
+ /* @__PURE__ */ r(e, {
45
+ color: "saffron",
46
+ onClick: () => n?.(t),
47
+ children: t.ctaLabel ?? "See prices"
48
+ }),
49
+ t.packageIncludes && t.packageIncludes.length > 0 && /* @__PURE__ */ i("div", {
50
+ className: "flex flex-col gap-4 w-full",
51
+ children: [/* @__PURE__ */ r("p", {
52
+ className: "text-b3 font-semibold text-black",
53
+ children: "Your All-Inclusive package includes:"
54
+ }), /* @__PURE__ */ r("ul", {
55
+ className: "flex flex-col gap-2",
56
+ children: t.packageIncludes.map((e) => /* @__PURE__ */ i("li", {
57
+ className: "text-b3 text-black",
58
+ children: ["• ", e]
59
+ }, e))
60
+ })]
61
+ }),
62
+ t.additionalInfo && /* @__PURE__ */ r("p", {
63
+ className: "text-b3 text-black",
64
+ children: t.additionalInfo
65
+ })
66
+ ]
67
+ })]
68
+ });
69
+ }
70
+ function o({ quote: t, onCtaClick: n }) {
71
+ return /* @__PURE__ */ i("article", {
72
+ "aria-label": t.resortName,
73
+ className: "flex flex-col gap-12 items-center shrink-0 w-200",
74
+ children: [/* @__PURE__ */ i("div", {
75
+ className: "relative size-200 rounded-16 overflow-hidden",
76
+ children: [
77
+ /* @__PURE__ */ r("img", {
78
+ src: t.imageUrl,
79
+ alt: "",
80
+ className: "absolute inset-0 size-full object-cover"
81
+ }),
82
+ /* @__PURE__ */ r("div", { className: "absolute inset-0 rounded-16 bg-gradient-to-b from-transparent from-50% to-[rgba(36,36,36,0.4)]" }),
83
+ /* @__PURE__ */ i("div", {
84
+ className: "absolute bottom-20 inset-x-0 flex flex-col items-center text-white text-center px-8",
85
+ children: [/* @__PURE__ */ r("p", {
86
+ className: "text-b4 font-semibold",
87
+ children: t.resortName
88
+ }), /* @__PURE__ */ r("p", {
89
+ className: "text-b5",
90
+ children: t.country
91
+ })]
92
+ })
93
+ ]
94
+ }), /* @__PURE__ */ i("div", {
95
+ className: "flex flex-col gap-8 items-center",
96
+ children: [/* @__PURE__ */ i("p", {
97
+ className: "text-b5 font-semibold text-black text-center",
98
+ children: [
99
+ t.resortName,
100
+ /* @__PURE__ */ r("br", {}),
101
+ t.dateRange,
102
+ ",",
103
+ /* @__PURE__ */ r("br", {}),
104
+ t.pax
105
+ ]
106
+ }), /* @__PURE__ */ r(e, {
107
+ color: "black",
108
+ onClick: () => n?.(t),
109
+ children: t.ctaLabel ?? "See prices"
110
+ })]
111
+ })]
112
+ });
113
+ }
114
+ function s({ quotes: e, onCtaClick: i, className: s }) {
115
+ return e.length === 0 ? null : e.length === 1 ? /* @__PURE__ */ r("div", {
116
+ className: t(s),
117
+ children: /* @__PURE__ */ r(a, {
118
+ quote: e[0],
119
+ onCtaClick: i
120
+ })
121
+ }) : /* @__PURE__ */ r("div", {
122
+ className: t("flex flex-col gap-8", s),
123
+ children: /* @__PURE__ */ r(n, {
124
+ timelineName: "--chat-quote-scroller",
125
+ scrollerClassName: "pb-24",
126
+ scrollbarWidth: "60%",
127
+ "aria-label": "Quote options",
128
+ children: /* @__PURE__ */ r("div", {
129
+ className: "flex gap-12",
130
+ children: e.map((e) => /* @__PURE__ */ r(o, {
131
+ quote: e,
132
+ onCtaClick: i
133
+ }, e.id))
134
+ })
135
+ })
136
+ });
137
+ }
138
+ //#endregion
139
+ export { s as ChatWindowQuote };
140
+
141
+ //# sourceMappingURL=ChatWindowQuote.js.map