@clubmed/trident-ui 2.0.0-beta.6 → 2.0.0-beta.60
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/README.md +8 -0
- package/assets/trident-ui.css +3 -0
- package/chunks/Carousel.context.js +1548 -0
- package/chunks/Carousel.context.js.map +1 -0
- package/chunks/ChatMessage.js +14395 -0
- package/chunks/ChatMessage.js.map +1 -0
- package/chunks/DateField.js +187 -126
- package/chunks/DateField.js.map +1 -1
- package/chunks/Datepicker.js +2064 -0
- package/chunks/Datepicker.js.map +1 -0
- package/chunks/Day.js +1210 -0
- package/chunks/Day.js.map +1 -0
- package/chunks/Range.js +2 -2
- package/chunks/Range.js.map +1 -1
- package/chunks/Tooltip.js +4461 -0
- package/chunks/Tooltip.js.map +1 -0
- package/chunks/addLeadingZeros.js +33 -0
- package/chunks/addLeadingZeros.js.map +1 -0
- package/chunks/chunk.js +11 -4
- package/chunks/client.js +18953 -0
- package/chunks/client.js.map +1 -0
- package/chunks/formatISO.js +29 -0
- package/chunks/formatISO.js.map +1 -0
- package/examples/advanced-toast-demo.js +2 -2
- package/examples/advanced-toast-demo.js.map +1 -1
- package/examples/advanced-toast-green-demo.js +1 -1
- package/examples/advanced-toast-green-demo.js.map +1 -1
- package/examples/advanced-toast-no-dismiss-demo.js +1 -1
- package/examples/advanced-toast-no-dismiss-demo.js.map +1 -1
- package/examples/advanced-toast-red-demo.js +1 -1
- package/examples/advanced-toast-red-demo.js.map +1 -1
- package/examples/advanced-toast-with-details-demo.js +2 -2
- package/examples/advanced-toast-with-details-demo.js.map +1 -1
- package/examples/button-demo.js +2 -2
- package/examples/button-demo.js.map +1 -1
- package/examples/chat-input-demo.d.ts +1 -0
- package/examples/chat-input-demo.js +21 -0
- package/examples/chat-input-demo.js.map +1 -0
- package/examples/chat-message-demo.d.ts +1 -0
- package/examples/chat-message-demo.js +20 -0
- package/examples/chat-message-demo.js.map +1 -0
- package/examples/chat-typing-indicator-demo.d.ts +1 -0
- package/examples/chat-typing-indicator-demo.js +14 -0
- package/examples/chat-typing-indicator-demo.js.map +1 -0
- package/examples/chat-window-csat-demo.d.ts +1 -0
- package/examples/chat-window-csat-demo.js +68 -0
- package/examples/chat-window-csat-demo.js.map +1 -0
- package/examples/chat-window-csat-prompt-demo.d.ts +1 -0
- package/examples/chat-window-csat-prompt-demo.js +40 -0
- package/examples/chat-window-csat-prompt-demo.js.map +1 -0
- package/examples/chat-window-demo.d.ts +1 -0
- package/examples/chat-window-demo.js +69 -0
- package/examples/chat-window-demo.js.map +1 -0
- package/examples/chat-window-quote-demo.d.ts +1 -0
- package/examples/chat-window-quote-demo.js +47 -0
- package/examples/chat-window-quote-demo.js.map +1 -0
- package/examples/chat-window-quotes-demo.d.ts +1 -0
- package/examples/chat-window-quotes-demo.js +67 -0
- package/examples/chat-window-quotes-demo.js.map +1 -0
- package/examples/checkbox-demo.js +17 -15
- package/examples/checkbox-demo.js.map +1 -1
- package/examples/checkbox-disabled-demo.js +12 -15
- package/examples/checkbox-disabled-demo.js.map +1 -1
- package/examples/checkbox-select-demo.d.ts +1 -0
- package/examples/checkbox-select-demo.js +101 -0
- package/examples/checkbox-select-demo.js.map +1 -0
- package/examples/checkboxes-demo.js +1 -1
- package/examples/checkboxes-demo.js.map +1 -1
- package/examples/checkboxes-with-form-control-demo.js +8 -8
- package/examples/checkboxes-with-form-control-demo.js.map +1 -1
- package/examples/choice-expander-demo.js +4 -67
- package/examples/choice-expander-demo.js.map +1 -1
- package/examples/countdown-demo.d.ts +1 -0
- package/examples/countdown-demo.js +39 -0
- package/examples/countdown-demo.js.map +1 -0
- package/examples/datepicker-demo.d.ts +1 -0
- package/examples/datepicker-demo.js +51 -0
- package/examples/datepicker-demo.js.map +1 -0
- package/examples/datepicker-disabled-days-demo.d.ts +1 -0
- package/examples/datepicker-disabled-days-demo.js +42 -0
- package/examples/datepicker-disabled-days-demo.js.map +1 -0
- package/examples/datepicker-duration-tooltip-demo.d.ts +1 -0
- package/examples/datepicker-duration-tooltip-demo.js +68 -0
- package/examples/datepicker-duration-tooltip-demo.js.map +1 -0
- package/examples/datepicker-no-prices-demo.d.ts +1 -0
- package/examples/datepicker-no-prices-demo.js +42 -0
- package/examples/datepicker-no-prices-demo.js.map +1 -0
- package/examples/datepicker-preselected-demo.d.ts +1 -0
- package/examples/datepicker-preselected-demo.js +77 -0
- package/examples/datepicker-preselected-demo.js.map +1 -0
- package/examples/dropdown-demo.js +9 -1
- package/examples/dropdown-demo.js.map +1 -1
- package/examples/elastic-height-demo.js +2 -1
- package/examples/elastic-height-demo.js.map +1 -1
- package/examples/expandable-text-demo.d.ts +1 -0
- package/examples/expandable-text-demo.js +22 -0
- package/examples/expandable-text-demo.js.map +1 -0
- package/examples/floating-label-text-field-demo.d.ts +1 -0
- package/examples/floating-label-text-field-demo.js +32 -0
- package/examples/floating-label-text-field-demo.js.map +1 -0
- package/examples/frame-demo.d.ts +1 -0
- package/examples/frame-demo.js +36 -0
- package/examples/frame-demo.js.map +1 -0
- package/examples/frame-horizontal-demo.d.ts +1 -0
- package/examples/frame-horizontal-demo.js +44 -0
- package/examples/frame-horizontal-demo.js.map +1 -0
- package/examples/frame-no-image-demo.d.ts +1 -0
- package/examples/frame-no-image-demo.js +47 -0
- package/examples/frame-no-image-demo.js.map +1 -0
- package/examples/frame-vertical-demo.d.ts +1 -0
- package/examples/frame-vertical-demo.js +44 -0
- package/examples/frame-vertical-demo.js.map +1 -0
- package/examples/frame-with-tag-demo.d.ts +1 -0
- package/examples/frame-with-tag-demo.js +48 -0
- package/examples/frame-with-tag-demo.js.map +1 -0
- package/examples/radio-demo.js +3 -3
- package/examples/radio-demo.js.map +1 -1
- package/examples/scrollbar-demo.d.ts +1 -0
- package/examples/scrollbar-demo.js +40 -0
- package/examples/scrollbar-demo.js.map +1 -0
- package/examples/slider-frame-demo.d.ts +1 -0
- package/examples/slider-frame-demo.js +38 -0
- package/examples/slider-frame-demo.js.map +1 -0
- package/examples/slider-frame-with-button-demo.d.ts +1 -0
- package/examples/slider-frame-with-button-demo.js +46 -0
- package/examples/slider-frame-with-button-demo.js.map +1 -0
- package/examples/slider-frame-with-tag-demo.d.ts +1 -0
- package/examples/slider-frame-with-tag-demo.js +50 -0
- package/examples/slider-frame-with-tag-demo.js.map +1 -0
- package/examples/switch-demo.js +1 -1
- package/examples/switch-demo.js.map +1 -1
- package/examples/tabs-demo.js +2 -5
- package/examples/tabs-demo.js.map +1 -1
- package/examples/text-field-demo.js +1 -0
- package/examples/text-field-demo.js.map +1 -1
- package/examples/toggle-demo.d.ts +1 -0
- package/examples/toggle-demo.js +43 -0
- package/examples/toggle-demo.js.map +1 -0
- package/examples/tooltip-demo.d.ts +1 -0
- package/examples/tooltip-demo.js +103 -0
- package/examples/tooltip-demo.js.map +1 -0
- package/package.json +10 -3
- package/styles/components.css +99 -0
- package/styles/theme.css +5 -2
- package/styles/ui/button.css +1 -1
- package/styles/ui/chat-button.css +68 -1
- package/styles/ui/controls.css +8 -52
- package/styles/ui/range.css +1 -1
- package/ui/AdvancedToast.js +17 -14
- package/ui/AdvancedToast.js.map +1 -1
- package/ui/BasicToast.js +1 -1
- package/ui/BasicToast.js.map +1 -1
- package/ui/ChatButton.js +1 -1
- package/ui/ChatButton.js.map +1 -1
- package/ui/ChatInput.d.ts +10 -0
- package/ui/ChatInput.js +149 -0
- package/ui/ChatInput.js.map +1 -0
- package/ui/ChatMessage.d.ts +6 -0
- package/ui/ChatMessage.js +2 -0
- package/ui/ChatTypingIndicator.d.ts +2 -0
- package/ui/ChatTypingIndicator.js +30 -0
- package/ui/ChatTypingIndicator.js.map +1 -0
- package/ui/ChatWindow.d.ts +34 -0
- package/ui/ChatWindow.js +142 -0
- package/ui/ChatWindow.js.map +1 -0
- package/ui/ChatWindowCsatPrompt.d.ts +15 -0
- package/ui/ChatWindowCsatPrompt.js +71 -0
- package/ui/ChatWindowCsatPrompt.js.map +1 -0
- package/ui/ChatWindowCsatThankYou.d.ts +9 -0
- package/ui/ChatWindowCsatThankYou.js +29 -0
- package/ui/ChatWindowCsatThankYou.js.map +1 -0
- package/ui/ChatWindowQuote.d.ts +22 -0
- package/ui/ChatWindowQuote.js +141 -0
- package/ui/ChatWindowQuote.js.map +1 -0
- package/ui/ChoiceExpander.js +1 -1
- package/ui/ChoiceExpander.js.map +1 -1
- package/ui/Countdown.d.ts +16 -0
- package/ui/Countdown.js +61 -0
- package/ui/Countdown.js.map +1 -0
- package/ui/Datepicker.d.ts +26 -0
- package/ui/Datepicker.js +3 -0
- package/ui/Day.d.ts +25 -0
- package/ui/Day.js +2 -0
- package/ui/Dropdown.d.ts +20 -3
- package/ui/Dropdown.js +57 -23
- package/ui/Dropdown.js.map +1 -1
- package/ui/ElasticHeight.d.ts +0 -1
- package/ui/ElasticHeight.js +14 -23
- package/ui/ElasticHeight.js.map +1 -1
- package/ui/ExpandableText.d.ts +14 -0
- package/ui/ExpandableText.js +40 -0
- package/ui/ExpandableText.js.map +1 -0
- package/ui/FlipCard.d.ts +7 -0
- package/ui/FlipCard.js +59 -0
- package/ui/FlipCard.js.map +1 -0
- package/ui/Scrollbar.d.ts +14 -0
- package/ui/Scrollbar.js +68 -0
- package/ui/Scrollbar.js.map +1 -0
- package/ui/SidebarLayout.d.ts +3 -1
- package/ui/SidebarLayout.js +32 -24
- package/ui/SidebarLayout.js.map +1 -1
- package/ui/Toggle.d.ts +14 -0
- package/ui/Toggle.js +42 -0
- package/ui/Toggle.js.map +1 -0
- package/ui/Tooltip.d.ts +8 -0
- package/ui/Tooltip.js +2 -0
- package/ui/buttons/Button.type.d.ts +1 -1
- package/ui/buttons/Button.type.js.map +1 -1
- package/ui/carousel/Carousel.context.d.ts +27 -0
- package/ui/carousel/Carousel.context.js +2 -0
- package/ui/carousel/Carousel.d.ts +3 -0
- package/ui/carousel/Carousel.js +12 -0
- package/ui/carousel/Carousel.js.map +1 -0
- package/ui/carousel/CarouselBody.d.ts +6 -0
- package/ui/carousel/CarouselBody.js +21 -0
- package/ui/carousel/CarouselBody.js.map +1 -0
- package/ui/carousel/CarouselItem.d.ts +6 -0
- package/ui/carousel/CarouselItem.js +21 -0
- package/ui/carousel/CarouselItem.js.map +1 -0
- package/ui/carousel/CarouselNext.d.ts +8 -0
- package/ui/carousel/CarouselNext.js +23 -0
- package/ui/carousel/CarouselNext.js.map +1 -0
- package/ui/carousel/CarouselPrevious.d.ts +8 -0
- package/ui/carousel/CarouselPrevious.js +23 -0
- package/ui/carousel/CarouselPrevious.js.map +1 -0
- package/ui/contexts/TridentUIConfig.js +1 -1
- package/ui/forms/CheckboxSelect.d.ts +31 -0
- package/ui/forms/CheckboxSelect.js +119 -0
- package/ui/forms/CheckboxSelect.js.map +1 -0
- package/ui/forms/Filter.d.ts +10 -4
- package/ui/forms/Filter.js +27 -45
- package/ui/forms/Filter.js.map +1 -1
- package/ui/forms/FloatingLabelTextField.d.ts +12 -0
- package/ui/forms/FloatingLabelTextField.js +132 -0
- package/ui/forms/FloatingLabelTextField.js.map +1 -0
- package/ui/forms/FormControl.d.ts +1 -1
- package/ui/forms/FormControl.js +4 -4
- package/ui/forms/FormControl.js.map +1 -1
- package/ui/forms/NumberField.d.ts +34 -34
- package/ui/forms/NumberField.js +15 -16
- package/ui/forms/NumberField.js.map +1 -1
- package/ui/forms/PhoneField.d.ts +5 -10
- package/ui/forms/PhoneField.js +29 -211
- package/ui/forms/PhoneField.js.map +1 -1
- package/ui/forms/PhoneFieldFullInput.d.ts +16 -0
- package/ui/forms/PhoneFieldFullInput.js +110 -0
- package/ui/forms/PhoneFieldFullInput.js.map +1 -0
- package/ui/forms/PhoneFieldSplitInput.d.ts +19 -0
- package/ui/forms/PhoneFieldSplitInput.js +157 -0
- package/ui/forms/PhoneFieldSplitInput.js.map +1 -0
- package/ui/forms/Range.d.ts +1 -1
- package/ui/forms/Select.js +7 -8
- package/ui/forms/Select.js.map +1 -1
- package/ui/forms/Switch.d.ts +5 -3
- package/ui/forms/Switch.js +54 -34
- package/ui/forms/Switch.js.map +1 -1
- package/ui/forms/TextField.js +23 -14
- package/ui/forms/TextField.js.map +1 -1
- package/ui/forms/checkboxes/Checkbox.d.ts +9 -4
- package/ui/forms/checkboxes/Checkbox.js +36 -16
- package/ui/forms/checkboxes/Checkbox.js.map +1 -1
- package/ui/forms/checkboxes/Checkboxes.js +3 -3
- package/ui/forms/checkboxes/Checkboxes.js.map +1 -1
- package/ui/forms/checkboxes/index.d.ts +0 -1
- package/ui/forms/checkboxes/index.js +1 -2
- package/ui/forms/password/Password.js +7 -8
- package/ui/forms/password/Password.js.map +1 -1
- package/ui/forms/radios/Radio.d.ts +2 -1
- package/ui/forms/radios/Radio.js +6 -3
- package/ui/forms/radios/Radio.js.map +1 -1
- package/ui/forms/radios/RadioGroup.js +13 -14
- package/ui/forms/radios/RadioGroup.js.map +1 -1
- package/ui/frame/Frame.d.ts +8 -0
- package/ui/frame/Frame.js +16 -0
- package/ui/frame/Frame.js.map +1 -0
- package/ui/frame/FrameLabelsWrapper.d.ts +5 -0
- package/ui/frame/FrameLabelsWrapper.js +12 -0
- package/ui/frame/FrameLabelsWrapper.js.map +1 -0
- package/ui/frame/FrameTitle.d.ts +7 -0
- package/ui/frame/FrameTitle.js +25 -0
- package/ui/frame/FrameTitle.js.map +1 -0
- package/ui/frame/FrameWrapper.d.ts +6 -0
- package/ui/frame/FrameWrapper.js +14 -0
- package/ui/frame/FrameWrapper.js.map +1 -0
- package/ui/frame/SliderFrame.d.ts +14 -0
- package/ui/frame/SliderFrame.js +50 -0
- package/ui/frame/SliderFrame.js.map +1 -0
- package/ui/helpers/cn.d.ts +2 -0
- package/ui/helpers/cn.js +10 -0
- package/ui/helpers/cn.js.map +1 -0
- package/ui/helpers/phone/defaultPrefixes.d.ts +1 -0
- package/ui/helpers/phone/defaultPrefixes.js +52 -28
- package/ui/helpers/phone/defaultPrefixes.js.map +1 -1
- package/ui/helpers/phone/formatters.d.ts +11 -0
- package/ui/helpers/phone/formatters.js +32 -1
- package/ui/helpers/phone/formatters.js.map +1 -1
- package/ui/helpers/phone/index.d.ts +1 -1
- package/ui/helpers/phone/index.js +3 -3
- package/ui/hooks/useIsClient.d.ts +1 -0
- package/ui/hooks/useIsClient.js +12 -0
- package/ui/hooks/useIsClient.js.map +1 -0
- package/ui/hooks/useValue.d.ts +3 -4
- package/ui/hooks/useValue.js +11 -11
- package/ui/hooks/useValue.js.map +1 -1
- package/ui/tabs/Tabs.js +161 -93
- package/ui/tabs/Tabs.js.map +1 -1
- package/examples/form-checkbox-demo.d.ts +0 -1
- package/examples/form-checkbox-demo.js +0 -30
- package/examples/form-checkbox-demo.js.map +0 -1
- package/examples/form-checkbox-disabled-demo.d.ts +0 -1
- package/examples/form-checkbox-disabled-demo.js +0 -25
- package/examples/form-checkbox-disabled-demo.js.map +0 -1
- package/ui/forms/checkboxes/FormCheckbox.d.ts +0 -12
- package/ui/forms/checkboxes/FormCheckbox.js +0 -38
- package/ui/forms/checkboxes/FormCheckbox.js.map +0 -1
package/ui/ChatInput.js
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Button as e } from "./buttons/Button.js";
|
|
3
|
+
import { twMerge as t } from "./helpers/twMerge.js";
|
|
4
|
+
import { useEffect as n, useRef as r, useState as i } from "react";
|
|
5
|
+
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
6
|
+
//#region lib/ui/ChatInput.tsx
|
|
7
|
+
var s = 12, c = 48, l = 8;
|
|
8
|
+
function u(e) {
|
|
9
|
+
let t = r(e);
|
|
10
|
+
return n(() => {
|
|
11
|
+
t.current = e;
|
|
12
|
+
}), t;
|
|
13
|
+
}
|
|
14
|
+
var d = 3e3;
|
|
15
|
+
function f(e) {
|
|
16
|
+
let [t, a] = i(!1), [o, s] = i(!1), c = r(null), l = r(null), f = r(!1), p = u(e);
|
|
17
|
+
function m() {
|
|
18
|
+
l.current && clearTimeout(l.current);
|
|
19
|
+
}
|
|
20
|
+
function h() {
|
|
21
|
+
m(), l.current = setTimeout(() => {
|
|
22
|
+
f.current = !1, s(!1), c.current?.stop();
|
|
23
|
+
}, d);
|
|
24
|
+
}
|
|
25
|
+
n(() => {
|
|
26
|
+
let e = window.webkitSpeechRecognition ?? window.SpeechRecognition;
|
|
27
|
+
if (!e) return;
|
|
28
|
+
a(!0);
|
|
29
|
+
let t = new e();
|
|
30
|
+
return t.continuous = !0, t.interimResults = !0, t.lang = navigator.language, t.onresult = (e) => {
|
|
31
|
+
let t = Array.from(e.results).map((e) => e[0].transcript).join("");
|
|
32
|
+
p.current(t), h();
|
|
33
|
+
}, t.onend = () => {
|
|
34
|
+
if (m(), f.current) try {
|
|
35
|
+
t.start();
|
|
36
|
+
} catch {}
|
|
37
|
+
else s(!1);
|
|
38
|
+
}, t.onerror = (e) => {
|
|
39
|
+
(e.error === "not-allowed" || e.error === "aborted") && (f.current = !1, s(!1), m());
|
|
40
|
+
}, c.current = t, () => {
|
|
41
|
+
t.abort(), m();
|
|
42
|
+
};
|
|
43
|
+
}, []);
|
|
44
|
+
function g() {
|
|
45
|
+
let e = c.current;
|
|
46
|
+
e && (f.current ? (f.current = !1, e.stop()) : (f.current = !0, s(!0), e.start(), h()));
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
isSupported: t,
|
|
50
|
+
isListening: o,
|
|
51
|
+
toggle: g
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function p(e) {
|
|
55
|
+
let t = r(null);
|
|
56
|
+
return n(() => {
|
|
57
|
+
let n = t.current;
|
|
58
|
+
if (!e) {
|
|
59
|
+
n && (n.style.height = "0px");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
let r, i = null, a = null;
|
|
63
|
+
return navigator.mediaDevices.getUserMedia({
|
|
64
|
+
audio: !0,
|
|
65
|
+
video: !1
|
|
66
|
+
}).then((e) => {
|
|
67
|
+
i = e, a = new AudioContext();
|
|
68
|
+
let n = a.createMediaStreamSource(i), o = a.createAnalyser();
|
|
69
|
+
o.fftSize = 256, n.connect(o);
|
|
70
|
+
let s = new Uint8Array(o.frequencyBinCount);
|
|
71
|
+
function c() {
|
|
72
|
+
o.getByteFrequencyData(s);
|
|
73
|
+
let e = s.reduce((e, t) => e + t, 0) / s.length, n = t.current;
|
|
74
|
+
n && (n.style.height = `${Math.sqrt(e / 255) * 10}px`), r = requestAnimationFrame(c);
|
|
75
|
+
}
|
|
76
|
+
r = requestAnimationFrame(c);
|
|
77
|
+
}), () => {
|
|
78
|
+
cancelAnimationFrame(r), i?.getTracks().forEach((e) => e.stop()), a?.close();
|
|
79
|
+
};
|
|
80
|
+
}, [e]), t;
|
|
81
|
+
}
|
|
82
|
+
function m({ value: n, placeholder: r = "Ask a question?", onValueChange: u, onSubmit: d, onAudioClick: m, className: h, ...g }) {
|
|
83
|
+
let [_, v] = i(!1), y = n.trim(), b = _ || y.length > 0, { isSupported: x, isListening: S, toggle: C } = f(u), w = p(S), T = x || !!m, E = T ? c + l + c : c;
|
|
84
|
+
function D() {
|
|
85
|
+
x ? C() : m?.();
|
|
86
|
+
}
|
|
87
|
+
function O(e) {
|
|
88
|
+
e.preventDefault(), y && d();
|
|
89
|
+
}
|
|
90
|
+
return /* @__PURE__ */ o("form", {
|
|
91
|
+
onSubmit: O,
|
|
92
|
+
onMouseDown: (e) => {
|
|
93
|
+
e.target.tagName !== "TEXTAREA" && e.preventDefault();
|
|
94
|
+
},
|
|
95
|
+
...g,
|
|
96
|
+
style: { paddingBottom: b ? s + l + c : s },
|
|
97
|
+
className: t("relative flex items-start overflow-hidden bg-white border border-light-grey p-12 w-full", "transition-[padding-bottom,border-radius] duration-250 ease-out rounded-b-[24]", b ? "rounded-32" : "rounded-[40]", h),
|
|
98
|
+
children: [/* @__PURE__ */ a("textarea", {
|
|
99
|
+
rows: b ? 3 : 1,
|
|
100
|
+
value: n,
|
|
101
|
+
onChange: (e) => u(e.target.value),
|
|
102
|
+
onKeyDown: (e) => {
|
|
103
|
+
e.key === "Enter" && (e.metaKey || e.ctrlKey) && y && (e.preventDefault(), d());
|
|
104
|
+
},
|
|
105
|
+
onFocus: () => v(!0),
|
|
106
|
+
onBlur: () => v(!1),
|
|
107
|
+
placeholder: r,
|
|
108
|
+
"aria-label": r,
|
|
109
|
+
style: { marginInlineEnd: b ? 0 : E + l },
|
|
110
|
+
className: t("scrollbar-hidden bg-transparent text-b3 py-12 outline-none resize-none w-full px-20 border border-middleGrey", "transition-[margin-inline-end,border-radius] duration-250 ease-out", b ? "text-black rounded-[20]" : "text-darkGrey placeholder:text-darkGrey rounded-[40]")
|
|
111
|
+
}), /* @__PURE__ */ o("div", {
|
|
112
|
+
style: {
|
|
113
|
+
insetInlineStart: b ? `calc(50% - ${E / 2}px)` : `calc(100% - ${s + E}px)`,
|
|
114
|
+
bottom: s
|
|
115
|
+
},
|
|
116
|
+
className: "absolute flex gap-8 items-center transition-[inset-inline-start] duration-250 ease-out",
|
|
117
|
+
children: [/* @__PURE__ */ a(e, {
|
|
118
|
+
type: "submit",
|
|
119
|
+
"aria-label": "Send message",
|
|
120
|
+
disabled: !y,
|
|
121
|
+
variant: "circle",
|
|
122
|
+
color: "saffron",
|
|
123
|
+
icon: "ArrowTailRight",
|
|
124
|
+
iconWidth: "24px"
|
|
125
|
+
}), T && /* @__PURE__ */ a(e, {
|
|
126
|
+
type: "button",
|
|
127
|
+
"aria-label": S ? "Stop recording" : "Use microphone",
|
|
128
|
+
"aria-pressed": S,
|
|
129
|
+
onClick: D,
|
|
130
|
+
variant: "circle",
|
|
131
|
+
theme: S ? "solid" : "outline",
|
|
132
|
+
color: S ? "sienna" : "black",
|
|
133
|
+
icon: "Audio",
|
|
134
|
+
iconWidth: "24px",
|
|
135
|
+
className: t("relative", S && "animate-pulse", !S && "border-lightGrey"),
|
|
136
|
+
children: /* @__PURE__ */ a("span", {
|
|
137
|
+
ref: w,
|
|
138
|
+
"aria-hidden": "true",
|
|
139
|
+
className: "bg-current absolute w-5 bottom-21",
|
|
140
|
+
style: { height: "0px" }
|
|
141
|
+
})
|
|
142
|
+
})]
|
|
143
|
+
})]
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
//#endregion
|
|
147
|
+
export { m as ChatInput };
|
|
148
|
+
|
|
149
|
+
//# sourceMappingURL=ChatInput.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatInput.js","names":[],"sources":["../../lib/ui/ChatInput.tsx"],"sourcesContent":["/// <reference types=\"@types/dom-speech-recognition\" />\n'use client';\n\nimport type { FormHTMLAttributes } from 'react';\nimport { useEffect, useRef, useState } from 'react';\nimport { Button } from './buttons/Button';\nimport { twMerge } from './helpers/twMerge';\n\nexport interface ChatInputProps extends FormHTMLAttributes<HTMLFormElement> {\n value: string;\n placeholder?: string;\n onValueChange: (value: string) => void;\n onSubmit: () => void;\n /** Fallback for when the browser does not support the Web Speech API */\n onAudioClick?: () => void;\n}\n\nconst FORM_PAD = 12; // matches p-12\nconst BTN_SIZE = 48; // matches size-48\nconst BTN_GAP = 8; // matches gap-8\n\n// Keep onValueChange stable inside the recognition handler without re-creating the SR instance\nfunction useCallbackRef<T extends (...args: never[]) => unknown>(fn: T) {\n const ref = useRef(fn);\n useEffect(() => {\n ref.current = fn;\n });\n return ref;\n}\n\nconst SILENCE_TIMEOUT_MS = 3_000;\n\nfunction useSpeechRecognition(onValueChange: (v: string) => void) {\n const [isSupported, setIsSupported] = useState(false);\n const [isListening, setIsListening] = useState(false);\n const recognitionRef = useRef<SpeechRecognition | null>(null);\n const silenceTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const isListeningRef = useRef(false);\n const onValueChangeRef = useCallbackRef(onValueChange);\n\n function clearSilenceTimer() {\n if (silenceTimerRef.current) clearTimeout(silenceTimerRef.current);\n }\n\n function resetSilenceTimer() {\n clearSilenceTimer();\n silenceTimerRef.current = setTimeout(() => {\n isListeningRef.current = false;\n setIsListening(false);\n recognitionRef.current?.stop();\n }, SILENCE_TIMEOUT_MS);\n }\n\n useEffect(() => {\n const SR: typeof SpeechRecognition | undefined =\n (window as Window & { webkitSpeechRecognition?: typeof SpeechRecognition })\n .webkitSpeechRecognition ?? window.SpeechRecognition;\n if (!SR) return;\n\n setIsSupported(true);\n\n const recognition = new SR();\n recognition.continuous = true;\n recognition.interimResults = true;\n recognition.lang = navigator.language;\n\n recognition.onresult = (event: SpeechRecognitionEvent) => {\n const transcript = Array.from(event.results)\n .map((r: SpeechRecognitionResult) => r[0].transcript)\n .join('');\n onValueChangeRef.current(transcript);\n resetSilenceTimer();\n };\n\n recognition.onend = () => {\n clearSilenceTimer();\n // Restart automatically if the user hasn't manually stopped (browser cut us off)\n if (isListeningRef.current) {\n try {\n recognition.start();\n } catch {\n /* already starting */\n }\n } else {\n setIsListening(false);\n }\n };\n\n recognition.onerror = (e: SpeechRecognitionErrorEvent) => {\n if (e.error === 'not-allowed' || e.error === 'aborted') {\n isListeningRef.current = false;\n setIsListening(false);\n clearSilenceTimer();\n }\n };\n\n recognitionRef.current = recognition;\n return () => {\n recognition.abort();\n clearSilenceTimer();\n };\n // SR detection and instance creation only needs to run once on mount\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n function toggle() {\n const recognition = recognitionRef.current;\n if (!recognition) return;\n if (isListeningRef.current) {\n isListeningRef.current = false;\n recognition.stop();\n } else {\n isListeningRef.current = true;\n setIsListening(true);\n recognition.start();\n resetSilenceTimer();\n }\n }\n\n return { isSupported, isListening, toggle };\n}\n\n/**\n * Drives the audio-level indicator by writing directly to the DOM element's\n * style — avoids triggering a React re-render on every animation frame\n * (rule: rerender-use-ref-transient-values).\n *\n * Returns a ref to attach to the indicator <span>.\n */\nfunction useAudioLevel(isListening: boolean) {\n const indicatorRef = useRef<HTMLSpanElement>(null);\n\n useEffect(() => {\n const el = indicatorRef.current;\n\n if (!isListening) {\n if (el) el.style.height = '0px';\n return;\n }\n\n let animId: number;\n let stream: MediaStream | null = null;\n let ctx: AudioContext | null = null;\n\n navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then((s) => {\n stream = s;\n ctx = new AudioContext();\n const source = ctx.createMediaStreamSource(stream);\n const analyser = ctx.createAnalyser();\n analyser.fftSize = 256;\n source.connect(analyser);\n const data = new Uint8Array(analyser.frequencyBinCount);\n\n function tick() {\n analyser.getByteFrequencyData(data);\n const avg = data.reduce((sum, v) => sum + v, 0) / data.length;\n // Square-root curve so quiet sounds register more visibly\n const node = indicatorRef.current;\n if (node) node.style.height = `${Math.sqrt(avg / 255) * 10}px`;\n animId = requestAnimationFrame(tick);\n }\n animId = requestAnimationFrame(tick);\n });\n\n return () => {\n cancelAnimationFrame(animId);\n stream?.getTracks().forEach((t) => t.stop());\n ctx?.close();\n };\n }, [isListening]);\n\n return indicatorRef;\n}\n\nexport function ChatInput({\n value,\n placeholder = 'Ask a question?',\n onValueChange,\n onSubmit,\n onAudioClick,\n className,\n ...props\n}: ChatInputProps) {\n const [isFocused, setIsFocused] = useState(false);\n const trimmed = value.trim();\n const isOpen = isFocused || trimmed.length > 0;\n const { isSupported, isListening, toggle } = useSpeechRecognition(onValueChange);\n const audioIndicatorRef = useAudioLevel(isListening);\n\n const showAudio = isSupported || !!onAudioClick;\n const btnW = showAudio ? BTN_SIZE + BTN_GAP + BTN_SIZE : BTN_SIZE;\n\n function handleAudioClick() {\n if (isSupported) {\n toggle();\n } else {\n onAudioClick?.();\n }\n }\n\n function handleSubmit(e: React.FormEvent) {\n e.preventDefault();\n if (trimmed) onSubmit();\n }\n\n return (\n <form\n onSubmit={handleSubmit}\n onMouseDown={(e) => {\n if ((e.target as HTMLElement).tagName !== 'TEXTAREA') e.preventDefault();\n }}\n {...props}\n style={{\n paddingBottom: isOpen ? FORM_PAD + BTN_GAP + BTN_SIZE : FORM_PAD,\n }}\n className={twMerge(\n 'relative flex items-start overflow-hidden bg-white border border-light-grey p-12 w-full',\n 'transition-[padding-bottom,border-radius] duration-250 ease-out rounded-b-[24]',\n isOpen ? 'rounded-32' : 'rounded-[40]',\n className,\n )}\n >\n <textarea\n rows={isOpen ? 3 : 1}\n value={value}\n onChange={(e) => onValueChange(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey) && trimmed) {\n e.preventDefault();\n onSubmit();\n }\n }}\n onFocus={() => setIsFocused(true)}\n onBlur={() => setIsFocused(false)}\n placeholder={placeholder}\n aria-label={placeholder}\n style={{\n marginInlineEnd: isOpen ? 0 : btnW + BTN_GAP,\n }}\n className={twMerge(\n 'scrollbar-hidden bg-transparent text-b3 py-12 outline-none resize-none w-full px-20 border border-middleGrey',\n 'transition-[margin-inline-end,border-radius] duration-250 ease-out',\n isOpen\n ? 'text-black rounded-[20]'\n : 'text-darkGrey placeholder:text-darkGrey rounded-[40]',\n )}\n />\n\n <div\n style={{\n insetInlineStart: isOpen\n ? `calc(50% - ${btnW / 2}px)`\n : `calc(100% - ${FORM_PAD + btnW}px)`,\n bottom: FORM_PAD,\n }}\n className=\"absolute flex gap-8 items-center transition-[inset-inline-start] duration-250 ease-out\"\n >\n <Button\n type=\"submit\"\n aria-label=\"Send message\"\n disabled={!trimmed}\n variant=\"circle\"\n color=\"saffron\"\n icon=\"ArrowTailRight\"\n iconWidth=\"24px\"\n />\n {showAudio && (\n <Button\n type=\"button\"\n aria-label={isListening ? 'Stop recording' : 'Use microphone'}\n aria-pressed={isListening}\n onClick={handleAudioClick}\n variant=\"circle\"\n theme={isListening ? 'solid' : 'outline'}\n color={isListening ? 'sienna' : 'black'}\n icon=\"Audio\"\n iconWidth=\"24px\"\n className={twMerge(\n 'relative',\n isListening && 'animate-pulse',\n !isListening && 'border-lightGrey',\n )}\n >\n <span\n ref={audioIndicatorRef}\n aria-hidden=\"true\"\n className=\"bg-current absolute w-5 bottom-21\"\n style={{ height: '0px' }}\n />\n </Button>\n )}\n </div>\n </form>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { HTMLAttributes, PropsWithChildren } from 'react';
|
|
2
|
+
export type ChatMessageSender = 'assistant' | 'user';
|
|
3
|
+
export interface ChatMessageProps extends HTMLAttributes<HTMLDivElement> {
|
|
4
|
+
sender: ChatMessageSender;
|
|
5
|
+
}
|
|
6
|
+
export declare const ChatMessage: import('react').NamedExoticComponent<PropsWithChildren<ChatMessageProps>>;
|
|
@@ -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/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/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,34 @@
|
|
|
1
|
+
import { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
import { ChatMessageSender } from './ChatMessage';
|
|
3
|
+
import { ChatWindowCsatPromptLabels } from './ChatWindowCsatPrompt';
|
|
4
|
+
import { ChatWindowCsatThankYouLabels } from './ChatWindowCsatThankYou';
|
|
5
|
+
import { ChatWindowQuoteItem } from './ChatWindowQuote';
|
|
6
|
+
export type { ChatWindowCsatPromptLabels, ChatWindowCsatThankYouLabels, ChatWindowQuoteItem };
|
|
7
|
+
export interface ChatWindowMessage {
|
|
8
|
+
id: string;
|
|
9
|
+
sender: ChatMessageSender;
|
|
10
|
+
content: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ChatWindowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
13
|
+
title?: string;
|
|
14
|
+
greeting?: string;
|
|
15
|
+
messages: ChatWindowMessage[];
|
|
16
|
+
inputValue: string;
|
|
17
|
+
inputPlaceholder?: string;
|
|
18
|
+
isTyping?: boolean;
|
|
19
|
+
onInputChange: (value: string) => void;
|
|
20
|
+
onSubmit: () => void;
|
|
21
|
+
onClose?: () => void;
|
|
22
|
+
onAudioClick?: () => void;
|
|
23
|
+
logo?: ReactNode;
|
|
24
|
+
onFeedbackRate?: (rating: number) => void;
|
|
25
|
+
csatLabels?: ChatWindowCsatPromptLabels;
|
|
26
|
+
csatRenderRatingLabel?: (rating: number) => ReactNode;
|
|
27
|
+
csatThankYouLabels?: ChatWindowCsatThankYouLabels;
|
|
28
|
+
onQuoteCtaClick?: (quote: ChatWindowQuoteItem) => void;
|
|
29
|
+
}
|
|
30
|
+
export interface ChatWindowHandle {
|
|
31
|
+
askForFeedback: () => void;
|
|
32
|
+
showQuotes: (quotes: ChatWindowQuoteItem[]) => void;
|
|
33
|
+
}
|
|
34
|
+
export declare const ChatWindow: import('react').ForwardRefExoticComponent<ChatWindowProps & import('react').RefAttributes<ChatWindowHandle>>;
|
package/ui/ChatWindow.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { twMerge as e } from "./helpers/twMerge.js";
|
|
3
|
+
import { ChatInput as t } from "./ChatInput.js";
|
|
4
|
+
import { t as n } from "../chunks/ChatMessage.js";
|
|
5
|
+
import { ChatTypingIndicator as r } from "./ChatTypingIndicator.js";
|
|
6
|
+
import { ChatWindowCsatPrompt as i } from "./ChatWindowCsatPrompt.js";
|
|
7
|
+
import { ChatWindowCsatThankYou as a } from "./ChatWindowCsatThankYou.js";
|
|
8
|
+
import { ChatWindowQuote as o } from "./ChatWindowQuote.js";
|
|
9
|
+
import { forwardRef as s, useEffect as c, useImperativeHandle as l, useRef as u, useState as d } from "react";
|
|
10
|
+
import { Icon as f } from "@clubmed/trident-icons";
|
|
11
|
+
import { jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
12
|
+
//#region lib/ui/ChatWindow.tsx
|
|
13
|
+
var h = /* @__PURE__ */ p("svg", {
|
|
14
|
+
className: "absolute top-full inset-x-[50%] -translate-x-1/2",
|
|
15
|
+
width: "118",
|
|
16
|
+
height: "27",
|
|
17
|
+
viewBox: "0 0 118 27",
|
|
18
|
+
children: /* @__PURE__ */ p("path", {
|
|
19
|
+
d: "M0 0C10.8283 0 21.1946 5.40419 28.7327 13.2062L30.2673 14.7945C45.991 31.0685 72.009 31.0685 87.7327 14.7945L89.2673 13.2062C96.8054 5.40419 107.172 0 118 0C118 0 80.6853 0 0 0Z",
|
|
20
|
+
fill: "#F6EFE7"
|
|
21
|
+
})
|
|
22
|
+
}), g = s(function({ title: s = "Ask me a question", greeting: g, messages: _, inputValue: v, inputPlaceholder: y, isTyping: b = !1, onInputChange: x, onSubmit: S, onClose: C, onAudioClick: w, logo: T, onFeedbackRate: E, csatLabels: D, csatRenderRatingLabel: O, csatThankYouLabels: k, onQuoteCtaClick: A, className: j, ...M }, N) {
|
|
23
|
+
let P = u(null), [F, I] = d(!1), [L, R] = d(null), [z, B] = d([]);
|
|
24
|
+
l(N, () => ({
|
|
25
|
+
askForFeedback() {
|
|
26
|
+
I(!0);
|
|
27
|
+
},
|
|
28
|
+
showQuotes(e) {
|
|
29
|
+
B((t) => [...t, e]);
|
|
30
|
+
}
|
|
31
|
+
})), c(() => {
|
|
32
|
+
let e = P.current;
|
|
33
|
+
e && e.scrollTo({
|
|
34
|
+
top: e.scrollHeight,
|
|
35
|
+
behavior: "smooth"
|
|
36
|
+
});
|
|
37
|
+
}, [
|
|
38
|
+
_,
|
|
39
|
+
b,
|
|
40
|
+
F,
|
|
41
|
+
L,
|
|
42
|
+
z
|
|
43
|
+
]);
|
|
44
|
+
function V(e) {
|
|
45
|
+
R(String(O ? O(e) : e)), E?.(e);
|
|
46
|
+
}
|
|
47
|
+
let H = F && D && O;
|
|
48
|
+
return /* @__PURE__ */ m("div", {
|
|
49
|
+
...M,
|
|
50
|
+
className: e("flex flex-col gap-20 items-center", j),
|
|
51
|
+
children: [/* @__PURE__ */ m("div", {
|
|
52
|
+
className: "chat-border bg-white flex flex-col h-full w-full rounded-32 overflow-hidden",
|
|
53
|
+
children: [
|
|
54
|
+
/* @__PURE__ */ m("div", {
|
|
55
|
+
className: "relative flex flex-col items-center shrink-0 pt-4 px-4 h-133 overflow-hidden",
|
|
56
|
+
children: [/* @__PURE__ */ p("div", {
|
|
57
|
+
className: "absolute inset-x-4 top-4 h-102 bg-lightSand rounded-[24]",
|
|
58
|
+
children: h
|
|
59
|
+
}), /* @__PURE__ */ m("div", {
|
|
60
|
+
className: "relative flex flex-col flex-1 items-center justify-end pb-8 w-full z-10",
|
|
61
|
+
children: [/* @__PURE__ */ p("div", {
|
|
62
|
+
className: "flex items-center justify-center px-12 py-8 w-full",
|
|
63
|
+
children: /* @__PURE__ */ p("p", {
|
|
64
|
+
className: "font-serif font-bold italic text-h5 text-center text-black line-clamp-2",
|
|
65
|
+
children: s
|
|
66
|
+
})
|
|
67
|
+
}), T ?? /* @__PURE__ */ p("button", {
|
|
68
|
+
type: "button",
|
|
69
|
+
"aria-label": "AI assistant",
|
|
70
|
+
className: "chat-border flex items-center justify-center size-48 rounded-full bg-white",
|
|
71
|
+
children: /* @__PURE__ */ p(f, {
|
|
72
|
+
name: "Sparkles",
|
|
73
|
+
width: "24px"
|
|
74
|
+
})
|
|
75
|
+
})]
|
|
76
|
+
})]
|
|
77
|
+
}),
|
|
78
|
+
/* @__PURE__ */ m("div", {
|
|
79
|
+
ref: P,
|
|
80
|
+
role: "log",
|
|
81
|
+
"aria-label": "Chat messages",
|
|
82
|
+
"aria-live": "polite",
|
|
83
|
+
className: "chat-message-log flex flex-col flex-1 gap-20 overflow-y-auto py-20",
|
|
84
|
+
children: [
|
|
85
|
+
g ? /* @__PURE__ */ p("div", {
|
|
86
|
+
className: "px-20",
|
|
87
|
+
children: /* @__PURE__ */ p("p", {
|
|
88
|
+
className: "text-b3 text-black",
|
|
89
|
+
children: g
|
|
90
|
+
})
|
|
91
|
+
}) : null,
|
|
92
|
+
_.map((e) => /* @__PURE__ */ p(n, {
|
|
93
|
+
sender: e.sender,
|
|
94
|
+
children: e.content
|
|
95
|
+
}, e.id)),
|
|
96
|
+
b && /* @__PURE__ */ p("div", {
|
|
97
|
+
className: "px-12",
|
|
98
|
+
children: /* @__PURE__ */ p(r, {})
|
|
99
|
+
}),
|
|
100
|
+
H && /* @__PURE__ */ p(i, {
|
|
101
|
+
onRate: V,
|
|
102
|
+
labels: D,
|
|
103
|
+
renderRatingLabel: O
|
|
104
|
+
}),
|
|
105
|
+
L && /* @__PURE__ */ p(n, {
|
|
106
|
+
sender: "user",
|
|
107
|
+
children: L
|
|
108
|
+
}),
|
|
109
|
+
L && k && /* @__PURE__ */ p(a, { labels: k }),
|
|
110
|
+
z.map((e, t) => /* @__PURE__ */ p(o, {
|
|
111
|
+
quotes: e,
|
|
112
|
+
onCtaClick: A
|
|
113
|
+
}, t))
|
|
114
|
+
]
|
|
115
|
+
}),
|
|
116
|
+
/* @__PURE__ */ p("div", {
|
|
117
|
+
className: "shrink-0 px-8 pb-8 pt-12",
|
|
118
|
+
children: /* @__PURE__ */ p(t, {
|
|
119
|
+
value: v,
|
|
120
|
+
placeholder: y,
|
|
121
|
+
onValueChange: x,
|
|
122
|
+
onSubmit: S,
|
|
123
|
+
onAudioClick: w
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
]
|
|
127
|
+
}), C && /* @__PURE__ */ p("button", {
|
|
128
|
+
type: "button",
|
|
129
|
+
"aria-label": "Close chat",
|
|
130
|
+
onClick: C,
|
|
131
|
+
className: "flex items-center justify-center size-48 rounded-full bg-black border border-black text-white shrink-0 hover:opacity-80 transition-opacity",
|
|
132
|
+
children: /* @__PURE__ */ p(f, {
|
|
133
|
+
name: "CrossDefault",
|
|
134
|
+
width: "24px"
|
|
135
|
+
})
|
|
136
|
+
})]
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
//#endregion
|
|
140
|
+
export { g as ChatWindow };
|
|
141
|
+
|
|
142
|
+
//# sourceMappingURL=ChatWindow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatWindow.js","names":[],"sources":["../../lib/ui/ChatWindow.tsx"],"sourcesContent":["'use client';\n\nimport { Icon } from '@clubmed/trident-icons';\nimport type { HTMLAttributes, ReactNode } from 'react';\nimport { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';\nimport { twMerge } from './helpers/twMerge';\nimport { ChatInput } from './ChatInput';\nimport { ChatMessage } from './ChatMessage';\nimport type { ChatMessageSender } from './ChatMessage';\nimport { ChatTypingIndicator } from './ChatTypingIndicator';\nimport { ChatWindowCsatPrompt } from './ChatWindowCsatPrompt';\nimport type { ChatWindowCsatPromptLabels } from './ChatWindowCsatPrompt';\nimport { ChatWindowCsatThankYou } from './ChatWindowCsatThankYou';\nimport type { ChatWindowCsatThankYouLabels } from './ChatWindowCsatThankYou';\nimport { ChatWindowQuote } from './ChatWindowQuote';\nimport type { ChatWindowQuoteItem } from './ChatWindowQuote';\n\nexport type { ChatWindowCsatPromptLabels, ChatWindowCsatThankYouLabels, ChatWindowQuoteItem };\n\nexport interface ChatWindowMessage {\n id: string;\n sender: ChatMessageSender;\n content: string;\n}\n\nexport interface ChatWindowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {\n title?: string;\n greeting?: string;\n messages: ChatWindowMessage[];\n inputValue: string;\n inputPlaceholder?: string;\n isTyping?: boolean;\n onInputChange: (value: string) => void;\n onSubmit: () => void;\n onClose?: () => void;\n onAudioClick?: () => void;\n logo?: ReactNode;\n onFeedbackRate?: (rating: number) => void;\n csatLabels?: ChatWindowCsatPromptLabels;\n csatRenderRatingLabel?: (rating: number) => ReactNode;\n csatThankYouLabels?: ChatWindowCsatThankYouLabels;\n onQuoteCtaClick?: (quote: ChatWindowQuoteItem) => void;\n}\n\nexport interface ChatWindowHandle {\n askForFeedback: () => void;\n showQuotes: (quotes: ChatWindowQuoteItem[]) => void;\n}\n\nconst headerCurve = (\n <svg\n className=\"absolute top-full inset-x-[50%] -translate-x-1/2\"\n width=\"118\"\n height=\"27\"\n viewBox=\"0 0 118 27\"\n >\n <path\n d=\"M0 0C10.8283 0 21.1946 5.40419 28.7327 13.2062L30.2673 14.7945C45.991 31.0685 72.009 31.0685 87.7327 14.7945L89.2673 13.2062C96.8054 5.40419 107.172 0 118 0C118 0 80.6853 0 0 0Z\"\n fill=\"#F6EFE7\"\n />\n </svg>\n);\n\nexport const ChatWindow = forwardRef<ChatWindowHandle, ChatWindowProps>(function ChatWindow(\n {\n title = 'Ask me a question',\n greeting,\n messages,\n inputValue,\n inputPlaceholder,\n isTyping = false,\n onInputChange,\n onSubmit,\n onClose,\n onAudioClick,\n logo,\n onFeedbackRate,\n csatLabels,\n csatRenderRatingLabel,\n csatThankYouLabels,\n onQuoteCtaClick,\n className,\n ...props\n },\n ref,\n) {\n const logRef = useRef<HTMLDivElement>(null);\n const [showCsat, setShowCsat] = useState(false);\n const [csatRatingMessage, setCsatRatingMessage] = useState<string | null>(null);\n const [quoteGroups, setQuoteGroups] = useState<ChatWindowQuoteItem[][]>([]);\n\n useImperativeHandle(ref, () => ({\n askForFeedback() {\n setShowCsat(true);\n },\n showQuotes(quotes: ChatWindowQuoteItem[]) {\n setQuoteGroups((prev) => [...prev, quotes]);\n },\n }));\n\n useEffect(() => {\n const el = logRef.current;\n if (el) el.scrollTo({ top: el.scrollHeight, behavior: 'smooth' });\n }, [messages, isTyping, showCsat, csatRatingMessage, quoteGroups]);\n\n function handleRate(rating: number) {\n const label = csatRenderRatingLabel ? String(csatRenderRatingLabel(rating)) : String(rating);\n setCsatRatingMessage(label);\n onFeedbackRate?.(rating);\n }\n\n const csatReady = showCsat && csatLabels && csatRenderRatingLabel;\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 <div className=\"relative flex flex-col items-center shrink-0 pt-4 px-4 h-133 overflow-hidden\">\n <div className=\"absolute inset-x-4 top-4 h-102 bg-lightSand rounded-[24]\">\n {headerCurve}\n </div>\n <div className=\"relative flex flex-col flex-1 items-center justify-end pb-8 w-full z-10\">\n <div className=\"flex items-center justify-center px-12 py-8 w-full\">\n <p className=\"font-serif font-bold italic text-h5 text-center text-black line-clamp-2\">\n {title}\n </p>\n </div>\n {logo ?? (\n <button\n type=\"button\"\n aria-label=\"AI assistant\"\n className=\"chat-border flex items-center justify-center size-48 rounded-full bg-white\"\n >\n <Icon name=\"Sparkles\" width=\"24px\" />\n </button>\n )}\n </div>\n </div>\n\n <div\n ref={logRef}\n role=\"log\"\n aria-label=\"Chat messages\"\n aria-live=\"polite\"\n className=\"chat-message-log flex flex-col flex-1 gap-20 overflow-y-auto py-20\"\n >\n {greeting ? (\n <div className=\"px-20\">\n <p className=\"text-b3 text-black\">{greeting}</p>\n </div>\n ) : null}\n {messages.map((msg) => (\n <ChatMessage key={msg.id} sender={msg.sender}>\n {msg.content}\n </ChatMessage>\n ))}\n {isTyping && (\n <div className=\"px-12\">\n <ChatTypingIndicator />\n </div>\n )}\n {csatReady && (\n <ChatWindowCsatPrompt\n onRate={handleRate}\n labels={csatLabels}\n renderRatingLabel={csatRenderRatingLabel}\n />\n )}\n {csatRatingMessage && <ChatMessage sender=\"user\">{csatRatingMessage}</ChatMessage>}\n {csatRatingMessage && csatThankYouLabels && (\n <ChatWindowCsatThankYou labels={csatThankYouLabels} />\n )}\n {quoteGroups.map((quotes, idx) => (\n <ChatWindowQuote key={idx} quotes={quotes} onCtaClick={onQuoteCtaClick} />\n ))}\n </div>\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 />\n </div>\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\"\n >\n <Icon name=\"CrossDefault\" width=\"24px\" />\n </button>\n )}\n </div>\n );\n});\n"],"mappings":";;;;;;;;;;;;AAiDA,IAAM,IACJ,kBAAC,OAAD;CACE,WAAU;CACV,OAAM;CACN,QAAO;CACP,SAAQ;WAER,kBAAC,QAAD;EACE,GAAE;EACF,MAAK;EACL,CAAA;CACE,CAAA,EAGK,IAAa,EAA8C,SACtE,EACE,WAAQ,qBACR,aACA,aACA,eACA,qBACA,cAAW,IACX,kBACA,aACA,YACA,iBACA,SACA,mBACA,eACA,0BACA,uBACA,oBACA,cACA,GAAG,KAEL,GACA;CACA,IAAM,IAAS,EAAuB,KAAK,EACrC,CAAC,GAAU,KAAe,EAAS,GAAM,EACzC,CAAC,GAAmB,KAAwB,EAAwB,KAAK,EACzE,CAAC,GAAa,KAAkB,EAAkC,EAAE,CAAC;AAW3E,CATA,EAAoB,UAAY;EAC9B,iBAAiB;AACf,KAAY,GAAK;;EAEnB,WAAW,GAA+B;AACxC,MAAgB,MAAS,CAAC,GAAG,GAAM,EAAO,CAAC;;EAE9C,EAAE,EAEH,QAAgB;EACd,IAAM,IAAK,EAAO;AAClB,EAAI,KAAI,EAAG,SAAS;GAAE,KAAK,EAAG;GAAc,UAAU;GAAU,CAAC;IAChE;EAAC;EAAU;EAAU;EAAU;EAAmB;EAAY,CAAC;CAElE,SAAS,EAAW,GAAgB;AAGlC,EADA,EADsC,OAAxB,IAA+B,EAAsB,EAAO,GAAW,EAAO,CACjE,EAC3B,IAAiB,EAAO;;CAG1B,IAAM,IAAY,KAAY,KAAc;AAE5C,QACE,kBAAC,OAAD;EAAK,GAAI;EAAO,WAAW,EAAQ,qCAAqC,EAAU;YAAlF,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBACZ;MACG,CAAA,EACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,KAAD;QAAG,WAAU;kBACV;QACC,CAAA;OACA,CAAA,EACL,KACC,kBAAC,UAAD;OACE,MAAK;OACL,cAAW;OACX,WAAU;iBAEV,kBAAC,GAAD;QAAM,MAAK;QAAW,OAAM;QAAS,CAAA;OAC9B,CAAA,CAAA;;;IAKf,kBAAC,OAAD;KACE,KAAK;KACL,MAAK;KACL,cAAW;KACX,aAAU;KACV,WAAU;eALZ;MAOG,IACC,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,KAAD;QAAG,WAAU;kBAAsB;QAAa,CAAA;OAC5C,CAAA,GACJ;MACH,EAAS,KAAK,MACb,kBAAC,GAAD;OAA0B,QAAQ,EAAI;iBACnC,EAAI;OACO,EAFI,EAAI,GAER,CACd;MACD,KACC,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAuB,CAAA;OACnB,CAAA;MAEP,KACC,kBAAC,GAAD;OACE,QAAQ;OACR,QAAQ;OACR,mBAAmB;OACnB,CAAA;MAEH,KAAqB,kBAAC,GAAD;OAAa,QAAO;iBAAQ;OAAgC,CAAA;MACjF,KAAqB,KACpB,kBAAC,GAAD,EAAwB,QAAQ,GAAsB,CAAA;MAEvD,EAAY,KAAK,GAAQ,MACxB,kBAAC,GAAD;OAAmC;OAAQ,YAAY;OAAmB,EAApD,EAAoD,CAAA;;;IAI9E,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD;MACE,OAAO;MACP,aAAa;MACb,eAAe;MACL;MACI;MACd,CAAA;KACE,CAAA;;MAGP,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 { Icon as n } from "@clubmed/trident-icons";
|
|
5
|
+
import { jsx as r, jsxs as i } from "react/jsx-runtime";
|
|
6
|
+
//#region lib/ui/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__ */ i("div", {
|
|
14
|
+
className: e("flex flex-col gap-20 px-20", c),
|
|
15
|
+
children: [
|
|
16
|
+
/* @__PURE__ */ i("div", {
|
|
17
|
+
className: "flex flex-col gap-4",
|
|
18
|
+
children: [/* @__PURE__ */ r("p", {
|
|
19
|
+
className: "text-b3 text-black",
|
|
20
|
+
children: o.intro
|
|
21
|
+
}), /* @__PURE__ */ i("p", {
|
|
22
|
+
className: "text-b3 text-black",
|
|
23
|
+
children: [
|
|
24
|
+
o.question,
|
|
25
|
+
" ",
|
|
26
|
+
/* @__PURE__ */ r("span", {
|
|
27
|
+
className: "text-b4 text-black",
|
|
28
|
+
children: o.questionNote
|
|
29
|
+
})
|
|
30
|
+
]
|
|
31
|
+
})]
|
|
32
|
+
}),
|
|
33
|
+
/* @__PURE__ */ r("div", {
|
|
34
|
+
role: "group",
|
|
35
|
+
"aria-label": o.groupAriaLabel,
|
|
36
|
+
className: "flex items-center gap-0",
|
|
37
|
+
children: o.starLabels.map((t, i) => {
|
|
38
|
+
let a = i + 1, o = a <= m;
|
|
39
|
+
return /* @__PURE__ */ r("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", h ? "cursor-default" : "hover:bg-lightSand"),
|
|
52
|
+
children: /* @__PURE__ */ r(n, {
|
|
53
|
+
name: o ? "StarFilled" : "StarOutlined",
|
|
54
|
+
width: "24px",
|
|
55
|
+
className: "text-black"
|
|
56
|
+
})
|
|
57
|
+
}, a);
|
|
58
|
+
})
|
|
59
|
+
}),
|
|
60
|
+
m > 0 && /* @__PURE__ */ r("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/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',\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,2EACA,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 { Icon as t } from "@clubmed/trident-icons";
|
|
4
|
+
import { jsx as n, jsxs as r } from "react/jsx-runtime";
|
|
5
|
+
//#region lib/ui/ChatWindowCsatThankYou.tsx
|
|
6
|
+
function i({ labels: i, className: a, ...o }) {
|
|
7
|
+
return /* @__PURE__ */ r("div", {
|
|
8
|
+
...o,
|
|
9
|
+
className: e("flex flex-col gap-20 px-20", a),
|
|
10
|
+
children: [/* @__PURE__ */ r("div", {
|
|
11
|
+
className: "flex items-center gap-4 text-green",
|
|
12
|
+
children: [/* @__PURE__ */ n(t, {
|
|
13
|
+
name: "CheckFilled",
|
|
14
|
+
width: "24px",
|
|
15
|
+
className: "shrink-0"
|
|
16
|
+
}), /* @__PURE__ */ n("p", {
|
|
17
|
+
className: "text-b3 font-semibold text-black",
|
|
18
|
+
children: i.title
|
|
19
|
+
})]
|
|
20
|
+
}), /* @__PURE__ */ n("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/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;
|