@clubmed/trident-ui 2.0.0-beta.55 → 2.0.0-beta.57

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.
@@ -0,0 +1 @@
1
+ export default function ChatWindowCsatDemo(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,68 @@
1
+ "use client";
2
+ import { ChatWindow as e } from "../ui/ChatWindow.js";
3
+ import { useRef as t } from "react";
4
+ import { jsx as n, jsxs as r } from "react/jsx-runtime";
5
+ //#region lib/examples/chat-window-csat-demo.tsx
6
+ var i = [
7
+ {
8
+ id: "1",
9
+ sender: "assistant",
10
+ content: "Hi. Ask me anything about Club Med. I'm here to help you find the right information quickly."
11
+ },
12
+ {
13
+ id: "2",
14
+ sender: "user",
15
+ content: "My son is 14 years old, what activities are available for him during our stay?"
16
+ },
17
+ {
18
+ id: "3",
19
+ sender: "assistant",
20
+ content: "Great question! For teenagers aged 13–17, Club Med offers the Passworld programme — supervised sports, creative workshops, and evening events designed specifically for that age group."
21
+ }
22
+ ], a = {
23
+ intro: "Your opinion matters! Tell us what you think. Your feedback helps us improve.",
24
+ question: "Following our conversation, from 1 to 5, how satisfied are you with the level of service you received,",
25
+ questionNote: "1 star being \"Very dissatisfied\" and 5 stars \"Very satisfied\".",
26
+ groupAriaLabel: "Rate your experience from 1 to 5 stars",
27
+ starLabels: [
28
+ "Very dissatisfied",
29
+ "Dissatisfied",
30
+ "Neutral",
31
+ "Satisfied",
32
+ "Very satisfied"
33
+ ]
34
+ }, o = {
35
+ title: "Thank you for your feedback!",
36
+ body: "Your opinion means a lot to us. Thanks to your responses, we keep improving our services to offer you an even more exceptional Club Med experience."
37
+ };
38
+ function s(e) {
39
+ return `${e} star${e > 1 ? "s" : ""} — ${a.starLabels[e - 1]}`;
40
+ }
41
+ function c() {
42
+ let c = t(null);
43
+ return /* @__PURE__ */ r("div", {
44
+ className: "flex flex-col items-center gap-12",
45
+ children: [/* @__PURE__ */ n(e, {
46
+ ref: c,
47
+ messages: i,
48
+ inputValue: "",
49
+ onInputChange: () => {},
50
+ onSubmit: () => {},
51
+ csatLabels: a,
52
+ csatRenderRatingLabel: s,
53
+ csatThankYouLabels: o,
54
+ onFeedbackRate: (e) => console.log("Feedback rating:", e),
55
+ onClose: () => {},
56
+ className: "w-380 h-660"
57
+ }), /* @__PURE__ */ n("button", {
58
+ type: "button",
59
+ onClick: () => c.current?.askForFeedback(),
60
+ className: "px-16 py-8 bg-saffron rounded-pill text-black text-b4 font-semibold hover:opacity-80 transition-opacity",
61
+ children: "Trigger feedback prompt"
62
+ })]
63
+ });
64
+ }
65
+ //#endregion
66
+ export { c as default };
67
+
68
+ //# sourceMappingURL=chat-window-csat-demo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-window-csat-demo.js","names":[],"sources":["../../lib/examples/chat-window-csat-demo.tsx"],"sourcesContent":["'use client';\n\nimport { useRef } from 'react';\nimport { ChatWindow } from '@/ui/ChatWindow';\nimport type {\n ChatWindowCsatPromptLabels,\n ChatWindowCsatThankYouLabels,\n ChatWindowHandle,\n ChatWindowMessage,\n} from '@/ui/ChatWindow';\n\nconst messages: ChatWindowMessage[] = [\n {\n id: '1',\n sender: 'assistant',\n content:\n \"Hi. Ask me anything about Club Med. I'm here to help you find the right information quickly.\",\n },\n {\n id: '2',\n sender: 'user',\n content: 'My son is 14 years old, what activities are available for him during our stay?',\n },\n {\n id: '3',\n sender: 'assistant',\n content:\n 'Great question! For teenagers aged 13–17, Club Med offers the Passworld programme — supervised sports, creative workshops, and evening events designed specifically for that age group.',\n },\n];\n\nconst csatLabels: ChatWindowCsatPromptLabels = {\n intro: 'Your opinion matters! Tell us what you think. Your feedback helps us improve.',\n question:\n 'Following our conversation, from 1 to 5, how satisfied are you with the level of service you received,',\n questionNote: '1 star being \"Very dissatisfied\" and 5 stars \"Very satisfied\".',\n groupAriaLabel: 'Rate your experience from 1 to 5 stars',\n starLabels: ['Very dissatisfied', 'Dissatisfied', 'Neutral', 'Satisfied', 'Very satisfied'],\n};\n\nconst csatThankYouLabels: ChatWindowCsatThankYouLabels = {\n title: 'Thank you for your feedback!',\n body: 'Your opinion means a lot to us. Thanks to your responses, we keep improving our services to offer you an even more exceptional Club Med experience.',\n};\n\nfunction csatRenderRatingLabel(rating: number) {\n return `${rating} star${rating > 1 ? 's' : ''} — ${csatLabels.starLabels[rating - 1]}`;\n}\n\nexport default function ChatWindowCsatDemo() {\n const chatRef = useRef<ChatWindowHandle>(null);\n\n return (\n <div className=\"flex flex-col items-center gap-12\">\n <ChatWindow\n ref={chatRef}\n messages={messages}\n inputValue=\"\"\n onInputChange={() => {}}\n onSubmit={() => {}}\n csatLabels={csatLabels}\n csatRenderRatingLabel={csatRenderRatingLabel}\n csatThankYouLabels={csatThankYouLabels}\n onFeedbackRate={(rating) => console.log('Feedback rating:', rating)}\n onClose={() => {}}\n className=\"w-380 h-660\"\n />\n <button\n type=\"button\"\n onClick={() => chatRef.current?.askForFeedback()}\n className=\"px-16 py-8 bg-saffron rounded-pill text-black text-b4 font-semibold hover:opacity-80 transition-opacity\"\n >\n Trigger feedback prompt\n </button>\n </div>\n );\n}\n"],"mappings":";;;;;AAWA,IAAM,IAAgC;CACpC;EACE,IAAI;EACJ,QAAQ;EACR,SACE;EACH;CACD;EACE,IAAI;EACJ,QAAQ;EACR,SAAS;EACV;CACD;EACE,IAAI;EACJ,QAAQ;EACR,SACE;;CAEL,EAEK,IAAyC;CAC7C,OAAO;CACP,UACE;CACF,cAAc;CACd,gBAAgB;CAChB,YAAY;EAAC;EAAqB;EAAgB;EAAW;EAAa;;CAC3E,EAEK,IAAmD;CACvD,OAAO;CACP,MAAM;CACP;AAED,SAAS,EAAsB,GAAgB;AAC7C,QAAO,GAAG,EAAO,OAAO,IAAS,IAAI,MAAM,GAAG,KAAK,EAAW,WAAW,IAAS;;AAGpF,SAAwB,IAAqB;CAC3C,IAAM,IAAU,EAAyB,KAAK;AAE9C,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,GAAD;GACE,KAAK;GACK;GACV,YAAW;GACX,qBAAqB;GACrB,gBAAgB;GACJ;GACW;GACH;GACpB,iBAAiB,MAAW,QAAQ,IAAI,oBAAoB,EAAO;GACnE,eAAe;GACf,WAAU;GACV,CAAA,EACF,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,EAAQ,SAAS,gBAAgB;GAChD,WAAU;aACX;GAEQ,CAAA,CAAA"}
@@ -0,0 +1 @@
1
+ export default function ChatWindowCsatPromptDemo(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,40 @@
1
+ "use client";
2
+ import { ChatWindowCsatPrompt as e } from "../ui/ChatWindowCsatPrompt.js";
3
+ import { useState as t } from "react";
4
+ import { jsx as n, jsxs as r } from "react/jsx-runtime";
5
+ //#region lib/examples/chat-window-csat-prompt-demo.tsx
6
+ var i = {
7
+ intro: "Your opinion matters! Tell us what you think. Your feedback helps us improve.",
8
+ question: "Following our conversation, from 1 to 5, how satisfied are you with the level of service you received,",
9
+ questionNote: "1 star being \"Very dissatisfied\" and 5 stars \"Very satisfied\".",
10
+ groupAriaLabel: "Rate your experience from 1 to 5 stars",
11
+ starLabels: [
12
+ "Very dissatisfied",
13
+ "Dissatisfied",
14
+ "Neutral",
15
+ "Satisfied",
16
+ "Very satisfied"
17
+ ]
18
+ };
19
+ function a(e) {
20
+ let t = i.starLabels;
21
+ return `${e} star${e > 1 ? "s" : ""} — ${t[e - 1]}`;
22
+ }
23
+ function o() {
24
+ let [o, s] = t(null);
25
+ return /* @__PURE__ */ r("div", {
26
+ className: "flex flex-col gap-20 w-380",
27
+ children: [/* @__PURE__ */ n(e, {
28
+ labels: i,
29
+ renderRatingLabel: a,
30
+ onRate: s
31
+ }), o !== null && /* @__PURE__ */ r("p", {
32
+ className: "text-b4 text-darkGrey text-center",
33
+ children: ["onRate called with: ", /* @__PURE__ */ n("strong", { children: o })]
34
+ })]
35
+ });
36
+ }
37
+ //#endregion
38
+ export { o as default };
39
+
40
+ //# sourceMappingURL=chat-window-csat-prompt-demo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-window-csat-prompt-demo.js","names":[],"sources":["../../lib/examples/chat-window-csat-prompt-demo.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { ChatWindowCsatPrompt } from '@/ui/ChatWindowCsatPrompt';\nimport type { ChatWindowCsatPromptLabels } from '@/ui/ChatWindowCsatPrompt';\n\nconst defaultCsatLabels: ChatWindowCsatPromptLabels = {\n intro: 'Your opinion matters! Tell us what you think. Your feedback helps us improve.',\n question:\n 'Following our conversation, from 1 to 5, how satisfied are you with the level of service you received,',\n questionNote: '1 star being \"Very dissatisfied\" and 5 stars \"Very satisfied\".',\n groupAriaLabel: 'Rate your experience from 1 to 5 stars',\n starLabels: ['Very dissatisfied', 'Dissatisfied', 'Neutral', 'Satisfied', 'Very satisfied'],\n};\n\nfunction defaultRenderRatingLabel(rating: number) {\n const labels = defaultCsatLabels.starLabels;\n return `${rating} star${rating > 1 ? 's' : ''} — ${labels[rating - 1]}`;\n}\n\nexport default function ChatWindowCsatPromptDemo() {\n const [lastRating, setLastRating] = useState<number | null>(null);\n\n return (\n <div className=\"flex flex-col gap-20 w-380\">\n <ChatWindowCsatPrompt\n labels={defaultCsatLabels}\n renderRatingLabel={defaultRenderRatingLabel}\n onRate={setLastRating}\n />\n {lastRating !== null && (\n <p className=\"text-b4 text-darkGrey text-center\">\n onRate called with: <strong>{lastRating}</strong>\n </p>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;AAMA,IAAM,IAAgD;CACpD,OAAO;CACP,UACE;CACF,cAAc;CACd,gBAAgB;CAChB,YAAY;EAAC;EAAqB;EAAgB;EAAW;EAAa;;CAC3E;AAED,SAAS,EAAyB,GAAgB;CAChD,IAAM,IAAS,EAAkB;AACjC,QAAO,GAAG,EAAO,OAAO,IAAS,IAAI,MAAM,GAAG,KAAK,EAAO,IAAS;;AAGrE,SAAwB,IAA2B;CACjD,IAAM,CAAC,GAAY,KAAiB,EAAwB,KAAK;AAEjE,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,GAAD;GACE,QAAQ;GACR,mBAAmB;GACnB,QAAQ;GACR,CAAA,EACD,MAAe,QACd,kBAAC,KAAD;GAAG,WAAU;aAAb,CAAiD,wBAC3B,kBAAC,UAAD,EAAA,UAAS,GAAoB,CAAA,CAAA"}
@@ -1,10 +1,29 @@
1
1
  "use client";
2
2
  import { ChatWindow as e } from "../ui/ChatWindow.js";
3
- import { useState as t } from "react";
4
- import { jsx as n } from "react/jsx-runtime";
3
+ import { useRef as t, useState as n } from "react";
4
+ import { jsx as r } from "react/jsx-runtime";
5
5
  //#region lib/examples/chat-window-demo.tsx
6
- function r() {
7
- let [r, i] = t([{
6
+ var i = {
7
+ intro: "Your opinion matters! Tell us what you think. Your feedback helps us improve.",
8
+ question: "Following our conversation, from 1 to 5, how satisfied are you with the level of service you received,",
9
+ questionNote: "1 star being \"Very dissatisfied\" and 5 stars \"Very satisfied\".",
10
+ groupAriaLabel: "Rate your experience from 1 to 5 stars",
11
+ starLabels: [
12
+ "Very dissatisfied",
13
+ "Dissatisfied",
14
+ "Neutral",
15
+ "Satisfied",
16
+ "Very satisfied"
17
+ ]
18
+ }, a = {
19
+ title: "Thank you for your feedback!",
20
+ body: "Your opinion means a lot to us. Thanks to your responses, we keep improving our services to offer you an even more exceptional Club Med experience."
21
+ };
22
+ function o(e) {
23
+ return `${e} star${e > 1 ? "s" : ""} — ${i.starLabels[e - 1]}`;
24
+ }
25
+ function s() {
26
+ let s = t(null), [c, l] = n([{
8
27
  id: "1",
9
28
  sender: "assistant",
10
29
  content: "Hi. Ask me anything about Club Med. I'm here to help you find the right information quickly."
@@ -12,34 +31,39 @@ function r() {
12
31
  id: "2",
13
32
  sender: "user",
14
33
  content: "My son is 14 years old, and I would like to find activities for him during our stay."
15
- }]), [a, o] = t(""), [s, c] = t(!1);
16
- function l() {
17
- if (!a.trim()) return;
34
+ }]), [u, d] = n(""), [f, p] = n(!1);
35
+ function m() {
36
+ if (!u.trim()) return;
18
37
  let e = {
19
38
  id: Date.now().toString(),
20
39
  sender: "user",
21
- content: a
40
+ content: u
22
41
  };
23
- i((t) => [...t, e]), o(""), c(!0), setTimeout(() => {
24
- c(!1), i((e) => [...e, {
42
+ l((t) => [...t, e]), d(""), p(!0), setTimeout(() => {
43
+ p(!1), l((e) => [...e, {
25
44
  id: (Date.now() + 1).toString(),
26
45
  sender: "assistant",
27
46
  content: "Let me look into that for you…"
28
47
  }]);
29
48
  }, 2e3);
30
49
  }
31
- return /* @__PURE__ */ n(e, {
32
- messages: r,
33
- inputValue: a,
34
- isTyping: s,
35
- onInputChange: o,
36
- onSubmit: l,
50
+ return /* @__PURE__ */ r(e, {
51
+ ref: s,
52
+ messages: c,
53
+ inputValue: u,
54
+ isTyping: f,
55
+ onInputChange: d,
56
+ onSubmit: m,
37
57
  onClose: () => {},
38
58
  onAudioClick: () => {},
59
+ onFeedbackRate: (e) => console.log("Feedback rating:", e),
60
+ csatLabels: i,
61
+ csatRenderRatingLabel: o,
62
+ csatThankYouLabels: a,
39
63
  className: "w-380 h-660"
40
64
  });
41
65
  }
42
66
  //#endregion
43
- export { r as default };
67
+ export { s as default };
44
68
 
45
69
  //# sourceMappingURL=chat-window-demo.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"chat-window-demo.js","names":[],"sources":["../../lib/examples/chat-window-demo.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { ChatWindow } from '@/ui/ChatWindow';\nimport type { ChatWindowMessage } from '@/ui/ChatWindow';\n\nexport default function ChatWindowDemo() {\n const [messages, setMessages] = useState<ChatWindowMessage[]>([\n {\n id: '1',\n sender: 'assistant',\n content:\n \"Hi. Ask me anything about Club Med. I'm here to help you find the right information quickly.\",\n },\n {\n id: '2',\n sender: 'user',\n content:\n 'My son is 14 years old, and I would like to find activities for him during our stay.',\n },\n ]);\n const [inputValue, setInputValue] = useState('');\n const [isTyping, setIsTyping] = useState(false);\n\n function handleSubmit() {\n if (!inputValue.trim()) return;\n const userMsg: ChatWindowMessage = {\n id: Date.now().toString(),\n sender: 'user',\n content: inputValue,\n };\n setMessages((prev) => [...prev, userMsg]);\n setInputValue('');\n setIsTyping(true);\n setTimeout(() => {\n setIsTyping(false);\n setMessages((prev) => [\n ...prev,\n {\n id: (Date.now() + 1).toString(),\n sender: 'assistant',\n content: 'Let me look into that for you…',\n },\n ]);\n }, 2000);\n }\n\n return (\n <ChatWindow\n messages={messages}\n inputValue={inputValue}\n isTyping={isTyping}\n onInputChange={setInputValue}\n onSubmit={handleSubmit}\n onClose={() => {}}\n onAudioClick={() => {}}\n className=\"w-380 h-660\"\n />\n );\n}\n"],"mappings":";;;;;AAMA,SAAwB,IAAiB;CACvC,IAAM,CAAC,GAAU,KAAe,EAA8B,CAC5D;EACE,IAAI;EACJ,QAAQ;EACR,SACE;EACH,EACD;EACE,IAAI;EACJ,QAAQ;EACR,SACE;EACH,CACF,CAAC,EACI,CAAC,GAAY,KAAiB,EAAS,GAAG,EAC1C,CAAC,GAAU,KAAe,EAAS,GAAM;CAE/C,SAAS,IAAe;AACtB,MAAI,CAAC,EAAW,MAAM,CAAE;EACxB,IAAM,IAA6B;GACjC,IAAI,KAAK,KAAK,CAAC,UAAU;GACzB,QAAQ;GACR,SAAS;GACV;AAID,EAHA,GAAa,MAAS,CAAC,GAAG,GAAM,EAAQ,CAAC,EACzC,EAAc,GAAG,EACjB,EAAY,GAAK,EACjB,iBAAiB;AAEf,GADA,EAAY,GAAM,EAClB,GAAa,MAAS,CACpB,GAAG,GACH;IACE,KAAK,KAAK,KAAK,GAAG,GAAG,UAAU;IAC/B,QAAQ;IACR,SAAS;IACV,CACF,CAAC;KACD,IAAK;;AAGV,QACE,kBAAC,GAAD;EACY;EACE;EACF;EACV,eAAe;EACf,UAAU;EACV,eAAe;EACf,oBAAoB;EACpB,WAAU;EACV,CAAA"}
1
+ {"version":3,"file":"chat-window-demo.js","names":[],"sources":["../../lib/examples/chat-window-demo.tsx"],"sourcesContent":["'use client';\n\nimport { useRef, useState } from 'react';\nimport { ChatWindow } from '@/ui/ChatWindow';\nimport type {\n ChatWindowCsatPromptLabels,\n ChatWindowCsatThankYouLabels,\n ChatWindowHandle,\n ChatWindowMessage,\n} from '@/ui/ChatWindow';\n\nconst csatLabels: ChatWindowCsatPromptLabels = {\n intro: 'Your opinion matters! Tell us what you think. Your feedback helps us improve.',\n question:\n 'Following our conversation, from 1 to 5, how satisfied are you with the level of service you received,',\n questionNote: '1 star being \"Very dissatisfied\" and 5 stars \"Very satisfied\".',\n groupAriaLabel: 'Rate your experience from 1 to 5 stars',\n starLabels: ['Very dissatisfied', 'Dissatisfied', 'Neutral', 'Satisfied', 'Very satisfied'],\n};\n\nconst csatThankYouLabels: ChatWindowCsatThankYouLabels = {\n title: 'Thank you for your feedback!',\n body: 'Your opinion means a lot to us. Thanks to your responses, we keep improving our services to offer you an even more exceptional Club Med experience.',\n};\n\nfunction csatRenderRatingLabel(rating: number) {\n return `${rating} star${rating > 1 ? 's' : ''} — ${csatLabels.starLabels[rating - 1]}`;\n}\n\nexport default function ChatWindowDemo() {\n const chatRef = useRef<ChatWindowHandle>(null);\n const [messages, setMessages] = useState<ChatWindowMessage[]>([\n {\n id: '1',\n sender: 'assistant',\n content:\n \"Hi. Ask me anything about Club Med. I'm here to help you find the right information quickly.\",\n },\n {\n id: '2',\n sender: 'user',\n content:\n 'My son is 14 years old, and I would like to find activities for him during our stay.',\n },\n ]);\n const [inputValue, setInputValue] = useState('');\n const [isTyping, setIsTyping] = useState(false);\n\n function handleSubmit() {\n if (!inputValue.trim()) return;\n const userMsg: ChatWindowMessage = {\n id: Date.now().toString(),\n sender: 'user',\n content: inputValue,\n };\n setMessages((prev) => [...prev, userMsg]);\n setInputValue('');\n setIsTyping(true);\n setTimeout(() => {\n setIsTyping(false);\n setMessages((prev) => [\n ...prev,\n {\n id: (Date.now() + 1).toString(),\n sender: 'assistant',\n content: 'Let me look into that for you…',\n },\n ]);\n }, 2000);\n }\n\n return (\n <ChatWindow\n ref={chatRef}\n messages={messages}\n inputValue={inputValue}\n isTyping={isTyping}\n onInputChange={setInputValue}\n onSubmit={handleSubmit}\n onClose={() => {}}\n onAudioClick={() => {}}\n onFeedbackRate={(rating) => console.log('Feedback rating:', rating)}\n csatLabels={csatLabels}\n csatRenderRatingLabel={csatRenderRatingLabel}\n csatThankYouLabels={csatThankYouLabels}\n className=\"w-380 h-660\"\n />\n );\n}\n"],"mappings":";;;;;AAWA,IAAM,IAAyC;CAC7C,OAAO;CACP,UACE;CACF,cAAc;CACd,gBAAgB;CAChB,YAAY;EAAC;EAAqB;EAAgB;EAAW;EAAa;;CAC3E,EAEK,IAAmD;CACvD,OAAO;CACP,MAAM;CACP;AAED,SAAS,EAAsB,GAAgB;AAC7C,QAAO,GAAG,EAAO,OAAO,IAAS,IAAI,MAAM,GAAG,KAAK,EAAW,WAAW,IAAS;;AAGpF,SAAwB,IAAiB;CACvC,IAAM,IAAU,EAAyB,KAAK,EACxC,CAAC,GAAU,KAAe,EAA8B,CAC5D;EACE,IAAI;EACJ,QAAQ;EACR,SACE;EACH,EACD;EACE,IAAI;EACJ,QAAQ;EACR,SACE;EACH,CACF,CAAC,EACI,CAAC,GAAY,KAAiB,EAAS,GAAG,EAC1C,CAAC,GAAU,KAAe,EAAS,GAAM;CAE/C,SAAS,IAAe;AACtB,MAAI,CAAC,EAAW,MAAM,CAAE;EACxB,IAAM,IAA6B;GACjC,IAAI,KAAK,KAAK,CAAC,UAAU;GACzB,QAAQ;GACR,SAAS;GACV;AAID,EAHA,GAAa,MAAS,CAAC,GAAG,GAAM,EAAQ,CAAC,EACzC,EAAc,GAAG,EACjB,EAAY,GAAK,EACjB,iBAAiB;AAEf,GADA,EAAY,GAAM,EAClB,GAAa,MAAS,CACpB,GAAG,GACH;IACE,KAAK,KAAK,KAAK,GAAG,GAAG,UAAU;IAC/B,QAAQ;IACR,SAAS;IACV,CACF,CAAC;KACD,IAAK;;AAGV,QACE,kBAAC,GAAD;EACE,KAAK;EACK;EACE;EACF;EACV,eAAe;EACf,UAAU;EACV,eAAe;EACf,oBAAoB;EACpB,iBAAiB,MAAW,QAAQ,IAAI,oBAAoB,EAAO;EACvD;EACW;EACH;EACpB,WAAU;EACV,CAAA"}
@@ -2,11 +2,7 @@ import { Tab as e, TabList as t, TabPanel as n, Tabs as r, TabsBody as i } from
2
2
  import { jsx as a, jsxs as o } from "react/jsx-runtime";
3
3
  //#region lib/examples/tabs-demo.tsx
4
4
  function s() {
5
- let s = [
6
- ,
7
- ,
8
- ,
9
- ].fill(void 0).map((e, t) => ({
5
+ let s = Array(24).fill(void 0).map((e, t) => ({
10
6
  label: `${t + 1}`,
11
7
  value: t + 1
12
8
  })), c = (e) => {
@@ -19,6 +15,7 @@ function s() {
19
15
  };
20
16
  return /* @__PURE__ */ o(r, {
21
17
  selected: 1,
18
+ className: "max-w-500",
22
19
  children: [/* @__PURE__ */ a(t, { children: s.map((t) => /* @__PURE__ */ a(e, {
23
20
  label: `Tab - ${t.label}`,
24
21
  value: t.value
@@ -1 +1 @@
1
- {"version":3,"file":"tabs-demo.js","names":[],"sources":["../../lib/examples/tabs-demo.tsx"],"sourcesContent":["import { Tab, TabList, TabPanel, Tabs, TabsBody } from '../ui/tabs/Tabs';\n\nexport default function TabsDemo() {\n const tabs = new Array(3).fill(undefined).map((_, n) => ({ label: `${n + 1}`, value: n + 1 }));\n\n const bgs = (count: number) => {\n switch (count % 3) {\n case 0:\n return 'bg-lightSand';\n case 1:\n return 'bg-saffron';\n case 2:\n return 'bg-lavender';\n default:\n return 'bg-sienna';\n }\n };\n return (\n <Tabs selected={1}>\n <TabList>\n {tabs.map((tab) => {\n return <Tab key={tab.label} label={`Tab - ${tab.label}`} value={tab.value} />;\n })}\n </TabList>\n <TabsBody>\n {tabs.map((tab) => {\n return (\n <TabPanel key={tab.label} value={tab.value}>\n <div className={`p-16 ${bgs(tab.value)}`}>Panel - {tab.label}</div>\n </TabPanel>\n );\n })}\n </TabsBody>\n </Tabs>\n );\n}\n"],"mappings":";;;AAEA,SAAwB,IAAW;CACjC,IAAM,IAAO;;;;EAAY,CAAC,KAAK,KAAA,EAAU,CAAC,KAAK,GAAG,OAAO;EAAE,OAAO,GAAG,IAAI;EAAK,OAAO,IAAI;EAAG,EAAE,EAExF,KAAO,MAAkB;AAC7B,UAAQ,IAAQ,GAAhB;GACE,KAAK,EACH,QAAO;GACT,KAAK,EACH,QAAO;GACT,KAAK,EACH,QAAO;GACT,QACE,QAAO;;;AAGb,QACE,kBAAC,GAAD;EAAM,UAAU;YAAhB,CACE,kBAAC,GAAD,EAAA,UACG,EAAK,KAAK,MACF,kBAAC,GAAD;GAAqB,OAAO,SAAS,EAAI;GAAS,OAAO,EAAI;GAAS,EAA5D,EAAI,MAAwD,CAC7E,EACM,CAAA,EACV,kBAAC,GAAD,EAAA,UACG,EAAK,KAAK,MAEP,kBAAC,GAAD;GAA0B,OAAO,EAAI;aACnC,kBAAC,OAAD;IAAK,WAAW,QAAQ,EAAI,EAAI,MAAM;cAAtC,CAA0C,YAAS,EAAI,MAAY;;GAC1D,EAFI,EAAI,MAER,CAEb,EACO,CAAA,CACN"}
1
+ {"version":3,"file":"tabs-demo.js","names":[],"sources":["../../lib/examples/tabs-demo.tsx"],"sourcesContent":["import { Tab, TabList, TabPanel, Tabs, TabsBody } from '../ui/tabs/Tabs';\n\nexport default function TabsDemo() {\n const tabs = new Array(24).fill(undefined).map((_, n) => ({ label: `${n + 1}`, value: n + 1 }));\n\n const bgs = (count: number) => {\n switch (count % 3) {\n case 0:\n return 'bg-lightSand';\n case 1:\n return 'bg-saffron';\n case 2:\n return 'bg-lavender';\n default:\n return 'bg-sienna';\n }\n };\n return (\n <Tabs selected={1} className=\"max-w-500\">\n <TabList>\n {tabs.map((tab) => {\n return <Tab key={tab.label} label={`Tab - ${tab.label}`} value={tab.value} />;\n })}\n </TabList>\n <TabsBody>\n {tabs.map((tab) => {\n return (\n <TabPanel key={tab.label} value={tab.value}>\n <div className={`p-16 ${bgs(tab.value)}`}>Panel - {tab.label}</div>\n </TabPanel>\n );\n })}\n </TabsBody>\n </Tabs>\n );\n}\n"],"mappings":";;;AAEA,SAAwB,IAAW;CACjC,IAAM,IAAW,MAAM,GAAG,CAAC,KAAK,KAAA,EAAU,CAAC,KAAK,GAAG,OAAO;EAAE,OAAO,GAAG,IAAI;EAAK,OAAO,IAAI;EAAG,EAAE,EAEzF,KAAO,MAAkB;AAC7B,UAAQ,IAAQ,GAAhB;GACE,KAAK,EACH,QAAO;GACT,KAAK,EACH,QAAO;GACT,KAAK,EACH,QAAO;GACT,QACE,QAAO;;;AAGb,QACE,kBAAC,GAAD;EAAM,UAAU;EAAG,WAAU;YAA7B,CACE,kBAAC,GAAD,EAAA,UACG,EAAK,KAAK,MACF,kBAAC,GAAD;GAAqB,OAAO,SAAS,EAAI;GAAS,OAAO,EAAI;GAAS,EAA5D,EAAI,MAAwD,CAC7E,EACM,CAAA,EACV,kBAAC,GAAD,EAAA,UACG,EAAK,KAAK,MAEP,kBAAC,GAAD;GAA0B,OAAO,EAAI;aACnC,kBAAC,OAAD;IAAK,WAAW,QAAQ,EAAI,EAAI,MAAM;cAAtC,CAA0C,YAAS,EAAI,MAAY;;GAC1D,EAFI,EAAI,MAER,CAEb,EACO,CAAA,CACN"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubmed/trident-ui",
3
- "version": "2.0.0-beta.55",
3
+ "version": "2.0.0-beta.57",
4
4
  "type": "module",
5
5
  "description": "Shared ClubMed React UI components",
6
6
  "keywords": [
@@ -1,5 +1,8 @@
1
1
  import { HTMLAttributes, ReactNode } from 'react';
2
2
  import { ChatMessageSender } from './ChatMessage';
3
+ import { ChatWindowCsatPromptLabels } from './ChatWindowCsatPrompt';
4
+ import { ChatWindowCsatThankYouLabels } from './ChatWindowCsatThankYou';
5
+ export type { ChatWindowCsatPromptLabels, ChatWindowCsatThankYouLabels };
3
6
  export interface ChatWindowMessage {
4
7
  id: string;
5
8
  sender: ChatMessageSender;
@@ -17,5 +20,12 @@ export interface ChatWindowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'c
17
20
  onClose?: () => void;
18
21
  onAudioClick?: () => void;
19
22
  logo?: ReactNode;
23
+ onFeedbackRate?: (rating: number) => void;
24
+ csatLabels?: ChatWindowCsatPromptLabels;
25
+ csatRenderRatingLabel?: (rating: number) => ReactNode;
26
+ csatThankYouLabels?: ChatWindowCsatThankYouLabels;
20
27
  }
21
- export declare function ChatWindow({ title, greeting, messages, inputValue, inputPlaceholder, isTyping, onInputChange, onSubmit, onClose, onAudioClick, logo, className, ...props }: ChatWindowProps): import("react/jsx-runtime").JSX.Element;
28
+ export interface ChatWindowHandle {
29
+ askForFeedback: () => void;
30
+ }
31
+ export declare const ChatWindow: import('react').ForwardRefExoticComponent<ChatWindowProps & import('react').RefAttributes<ChatWindowHandle>>;
package/ui/ChatWindow.js CHANGED
@@ -3,106 +3,129 @@ import { twMerge as e } from "./helpers/twMerge.js";
3
3
  import { ChatInput as t } from "./ChatInput.js";
4
4
  import { t as n } from "../chunks/ChatMessage.js";
5
5
  import { ChatTypingIndicator as r } from "./ChatTypingIndicator.js";
6
- import { useEffect as i, useRef as a } from "react";
7
- import { Icon as o } from "@clubmed/trident-icons";
8
- import { jsx as s, jsxs as c } from "react/jsx-runtime";
6
+ import { ChatWindowCsatPrompt as i } from "./ChatWindowCsatPrompt.js";
7
+ import { ChatWindowCsatThankYou as a } from "./ChatWindowCsatThankYou.js";
8
+ import { forwardRef as o, useEffect as s, useImperativeHandle as c, useRef as l, useState as u } from "react";
9
+ import { Icon as d } from "@clubmed/trident-icons";
10
+ import { jsx as f, jsxs as p } from "react/jsx-runtime";
9
11
  //#region lib/ui/ChatWindow.tsx
10
- var l = /* @__PURE__ */ s("svg", {
12
+ var m = /* @__PURE__ */ f("svg", {
11
13
  className: "absolute top-full inset-x-[50%] -translate-x-1/2",
12
14
  width: "118",
13
15
  height: "27",
14
16
  viewBox: "0 0 118 27",
15
- children: /* @__PURE__ */ s("path", {
17
+ children: /* @__PURE__ */ f("path", {
16
18
  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",
17
19
  fill: "#F6EFE7"
18
20
  })
19
- });
20
- function u({ title: u = "Ask me a question", greeting: d, messages: f, inputValue: p, inputPlaceholder: m, isTyping: h = !1, onInputChange: g, onSubmit: _, onClose: v, onAudioClick: y, logo: b, className: x, ...S }) {
21
- let C = a(null);
22
- return i(() => {
23
- let e = C.current;
21
+ }), h = o(function({ title: o = "Ask me a question", greeting: h, messages: g, inputValue: _, inputPlaceholder: v, isTyping: y = !1, onInputChange: b, onSubmit: x, onClose: S, onAudioClick: C, logo: w, onFeedbackRate: T, csatLabels: E, csatRenderRatingLabel: D, csatThankYouLabels: O, className: k, ...A }, j) {
22
+ let M = l(null), [N, P] = u(!1), [F, I] = u(null);
23
+ c(j, () => ({ askForFeedback() {
24
+ P(!0);
25
+ } })), s(() => {
26
+ let e = M.current;
24
27
  e && e.scrollTo({
25
28
  top: e.scrollHeight,
26
29
  behavior: "smooth"
27
30
  });
28
- }, [f, h]), /* @__PURE__ */ c("div", {
29
- ...S,
30
- className: e("flex flex-col gap-20 items-center", x),
31
- children: [/* @__PURE__ */ c("div", {
31
+ }, [
32
+ g,
33
+ y,
34
+ N,
35
+ F
36
+ ]);
37
+ function L(e) {
38
+ I(String(D ? D(e) : e)), T?.(e);
39
+ }
40
+ let R = N && E && D;
41
+ return /* @__PURE__ */ p("div", {
42
+ ...A,
43
+ className: e("flex flex-col gap-20 items-center", k),
44
+ children: [/* @__PURE__ */ p("div", {
32
45
  className: "chat-border bg-white flex flex-col h-full w-full rounded-32 overflow-hidden",
33
46
  children: [
34
- /* @__PURE__ */ c("div", {
47
+ /* @__PURE__ */ p("div", {
35
48
  className: "relative flex flex-col items-center shrink-0 pt-4 px-4 h-133 overflow-hidden",
36
- children: [/* @__PURE__ */ s("div", {
49
+ children: [/* @__PURE__ */ f("div", {
37
50
  className: "absolute inset-x-4 top-4 h-102 bg-lightSand rounded-[24]",
38
- children: l
39
- }), /* @__PURE__ */ c("div", {
51
+ children: m
52
+ }), /* @__PURE__ */ p("div", {
40
53
  className: "relative flex flex-col flex-1 items-center justify-end pb-8 w-full z-10",
41
- children: [/* @__PURE__ */ s("div", {
54
+ children: [/* @__PURE__ */ f("div", {
42
55
  className: "flex items-center justify-center px-12 py-8 w-full",
43
- children: /* @__PURE__ */ s("p", {
56
+ children: /* @__PURE__ */ f("p", {
44
57
  className: "font-serif font-bold italic text-h5 text-center text-black line-clamp-2",
45
- children: u
58
+ children: o
46
59
  })
47
- }), b ?? /* @__PURE__ */ s("button", {
60
+ }), w ?? /* @__PURE__ */ f("button", {
48
61
  type: "button",
49
62
  "aria-label": "AI assistant",
50
63
  className: "chat-border flex items-center justify-center size-48 rounded-full bg-white",
51
- children: /* @__PURE__ */ s(o, {
64
+ children: /* @__PURE__ */ f(d, {
52
65
  name: "Sparkles",
53
66
  width: "24px"
54
67
  })
55
68
  })]
56
69
  })]
57
70
  }),
58
- /* @__PURE__ */ c("div", {
59
- ref: C,
71
+ /* @__PURE__ */ p("div", {
72
+ ref: M,
60
73
  role: "log",
61
74
  "aria-label": "Chat messages",
62
75
  "aria-live": "polite",
63
76
  className: "chat-message-log flex flex-col flex-1 gap-20 overflow-y-auto py-20",
64
77
  children: [
65
- d ? /* @__PURE__ */ s("div", {
78
+ h ? /* @__PURE__ */ f("div", {
66
79
  className: "px-20",
67
- children: /* @__PURE__ */ s("p", {
80
+ children: /* @__PURE__ */ f("p", {
68
81
  className: "text-b3 text-black",
69
- children: d
82
+ children: h
70
83
  })
71
84
  }) : null,
72
- f.map((e) => /* @__PURE__ */ s(n, {
85
+ g.map((e) => /* @__PURE__ */ f(n, {
73
86
  sender: e.sender,
74
87
  children: e.content
75
88
  }, e.id)),
76
- h && /* @__PURE__ */ s("div", {
89
+ y && /* @__PURE__ */ f("div", {
77
90
  className: "px-12",
78
- children: /* @__PURE__ */ s(r, {})
79
- })
91
+ children: /* @__PURE__ */ f(r, {})
92
+ }),
93
+ R && /* @__PURE__ */ f(i, {
94
+ onRate: L,
95
+ labels: E,
96
+ renderRatingLabel: D
97
+ }),
98
+ F && /* @__PURE__ */ f(n, {
99
+ sender: "user",
100
+ children: F
101
+ }),
102
+ F && O && /* @__PURE__ */ f(a, { labels: O })
80
103
  ]
81
104
  }),
82
- /* @__PURE__ */ s("div", {
105
+ /* @__PURE__ */ f("div", {
83
106
  className: "shrink-0 px-8 pb-8 pt-12",
84
- children: /* @__PURE__ */ s(t, {
85
- value: p,
86
- placeholder: m,
87
- onValueChange: g,
88
- onSubmit: _,
89
- onAudioClick: y
107
+ children: /* @__PURE__ */ f(t, {
108
+ value: _,
109
+ placeholder: v,
110
+ onValueChange: b,
111
+ onSubmit: x,
112
+ onAudioClick: C
90
113
  })
91
114
  })
92
115
  ]
93
- }), v && /* @__PURE__ */ s("button", {
116
+ }), S && /* @__PURE__ */ f("button", {
94
117
  type: "button",
95
118
  "aria-label": "Close chat",
96
- onClick: v,
119
+ onClick: S,
97
120
  className: "flex items-center justify-center size-48 rounded-full bg-black border border-black text-white shrink-0 hover:opacity-80 transition-opacity",
98
- children: /* @__PURE__ */ s(o, {
121
+ children: /* @__PURE__ */ f(d, {
99
122
  name: "CrossDefault",
100
123
  width: "24px"
101
124
  })
102
125
  })]
103
126
  });
104
- }
127
+ });
105
128
  //#endregion
106
- export { u as ChatWindow };
129
+ export { h as ChatWindow };
107
130
 
108
131
  //# sourceMappingURL=ChatWindow.js.map
@@ -1 +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 { useEffect, useRef } from 'react';\nimport { twMerge } from './helpers/twMerge';\nimport { ChatInput } from './ChatInput';\nimport { ChatMessage } from './ChatMessage';\nimport type { ChatMessageSender } from './ChatMessage';\nimport { ChatTypingIndicator } from './ChatTypingIndicator';\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}\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 function ChatWindow({\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 className,\n ...props\n}: ChatWindowProps) {\n const logRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n // Scroll to bottom on new message or typing indicator change\n const el = logRef.current;\n if (el) el.scrollTo({ top: el.scrollHeight, behavior: 'smooth' });\n }, [messages, isTyping]);\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 </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":";;;;;;;;;AA+BA,IAAM,IACJ,kBAAC,OAAD;CACE,WAAU;CACV,OAAM;CACN,QAAO;CACP,SAAQ;WAER,kBAAC,QAAD;EACE,GAAE;EACF,MAAK;EACL,CAAA;CACE,CAAA;AAGR,SAAgB,EAAW,EACzB,WAAQ,qBACR,aACA,aACA,eACA,qBACA,cAAW,IACX,kBACA,aACA,YACA,iBACA,SACA,cACA,GAAG,KACe;CAClB,IAAM,IAAS,EAAuB,KAAK;AAQ3C,QANA,QAAgB;EAEd,IAAM,IAAK,EAAO;AAClB,EAAI,KAAI,EAAG,SAAS;GAAE,KAAK,EAAG;GAAc,UAAU;GAAU,CAAC;IAChE,CAAC,GAAU,EAAS,CAAC,EAGtB,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;;;IAIV,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"}
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';\n\nexport type { ChatWindowCsatPromptLabels, ChatWindowCsatThankYouLabels };\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}\n\nexport interface ChatWindowHandle {\n askForFeedback: () => 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 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\n useImperativeHandle(ref, () => ({\n askForFeedback() {\n setShowCsat(true);\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]);\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 </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":";;;;;;;;;;;AA6CA,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,cACA,GAAG,KAEL,GACA;CACA,IAAM,IAAS,EAAuB,KAAK,EACrC,CAAC,GAAU,KAAe,EAAS,GAAM,EACzC,CAAC,GAAmB,KAAwB,EAAwB,KAAK;AAQ/E,CANA,EAAoB,UAAY,EAC9B,iBAAiB;AACf,IAAY,GAAK;IAEpB,EAAE,EAEH,QAAgB;EACd,IAAM,IAAK,EAAO;AAClB,EAAI,KAAI,EAAG,SAAS;GAAE,KAAK,EAAG;GAAc,UAAU;GAAU,CAAC;IAChE;EAAC;EAAU;EAAU;EAAU;EAAkB,CAAC;CAErD,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;;;IAI1D,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"}
@@ -48,7 +48,7 @@ function a({ id: a, name: o, checked: s, className: c, color: l = "saffron", onC
48
48
  r: 4,
49
49
  fill: "var(--color-middleGrey)"
50
50
  }),
51
- /* @__PURE__ */ r("path", { d: "M18.73 6c.35 0 .68.14.9.37.5.5.5 1.35 0 1.85l-8.54 8.75q-.37.38-.9.38t-.9-.38l-4.91-5.02a1.33 1.33 0 0 1 0-1.85 1.26 1.26 0 0 1 1.8 0l4.01 4.09 7.64-7.82c.23-.24.56-.37.9-.37" })
51
+ /* @__PURE__ */ r("path", { d: "M18.1 7.5c.4.4.4 1.08 0 1.48l-6.83 7q-.3.3-.72.3a1 1 0 0 1-.72-.3L5.9 11.96c-.4-.41-.4-1.07 0-1.48a1 1 0 0 1 1.44 0l3.2 3.27 6.12-6.25a1 1 0 0 1 .72-.3 1 1 0 0 1 .72.3" })
52
52
  ] })
53
53
  })
54
54
  }),
@@ -1 +1 @@
1
- {"version":3,"file":"Switch.js","names":[],"sources":["../../../lib/ui/forms/Switch.tsx"],"sourcesContent":["'use client';\n\nimport clsx from 'clsx';\nimport { type ComponentPropsWithoutRef, useId } from 'react';\n\nimport { getBgColor } from '../helpers/colors/colors';\nimport type { Colors } from '../types/Colors';\n\nexport interface SwitchProps\n extends Omit<\n ComponentPropsWithoutRef<'input'>,\n 'size' | 'type' | 'defaultChecked' | 'onChange' | 'value'\n > {\n checked: boolean;\n color?: Colors;\n value?: string | number | boolean;\n onChange?: (event: Event, value: boolean) => void;\n}\n\nexport function Switch({\n id,\n name,\n checked,\n className,\n color = 'saffron',\n onChange,\n disabled = false,\n tabIndex = 0,\n children,\n role = 'switch',\n value,\n ...attrs\n}: SwitchProps) {\n const internalId = useId();\n const inputId = id ?? internalId;\n const inputName = name ?? inputId;\n const hasChildren = children !== undefined && children !== null;\n const rootClassName = clsx('inline-flex items-center relative', {\n 'gap-4': hasChildren,\n 'cursor-pointer': !disabled,\n 'cursor-not-allowed': disabled,\n });\n const Node = hasChildren ? 'label' : 'span';\n\n return (\n <Node htmlFor={inputId} className={rootClassName}>\n <input\n {...attrs}\n id={inputId}\n name={inputName}\n checked={checked}\n data-name=\"Switch\"\n disabled={disabled}\n role={role}\n tabIndex={tabIndex}\n type=\"checkbox\"\n value={typeof value === 'boolean' ? String(value) : value}\n className=\"absolute opacity-0 left-0 w-full top-0 m-0 p-0 z-1 h-full\"\n onChange={(e) => {\n onChange?.(e.nativeEvent, e.target.checked);\n }}\n />\n\n <span\n className={clsx(\n className,\n 'rounded-pill inline-flex min-w-56 items-center p-4 align-middle transition-colors',\n { 'bg-middleGrey': !checked },\n { [getBgColor(color)]: checked },\n )}\n >\n <svg className=\"size-24\" viewBox=\"0 0 24 24\">\n <g>\n <circle cx={12} cy={12} r={12} fill=\"var(--color-white)\" />\n <circle cx={12} cy={12} r={4} fill=\"var(--color-middleGrey)\" />\n <path d=\"M18.73 6c.35 0 .68.14.9.37.5.5.5 1.35 0 1.85l-8.54 8.75q-.37.38-.9.38t-.9-.38l-4.91-5.02a1.33 1.33 0 0 1 0-1.85 1.26 1.26 0 0 1 1.8 0l4.01 4.09 7.64-7.82c.23-.24.56-.37.9-.37\" />\n </g>\n </svg>\n </span>\n {children}\n </Node>\n );\n}\n"],"mappings":";;;;;;AAmBA,SAAgB,EAAO,EACrB,OACA,SACA,YACA,cACA,WAAQ,WACR,aACA,cAAW,IACX,cAAW,GACX,aACA,UAAO,UACP,UACA,GAAG,KACW;CACd,IAAM,IAAa,GAAO,EACpB,IAAU,KAAM,GAChB,IAAY,KAAQ,GACpB,IAAc,KAAuC,MACrD,IAAgB,EAAK,qCAAqC;EAC9D,SAAS;EACT,kBAAkB,CAAC;EACnB,sBAAsB;EACvB,CAAC;AAGF,QACE,kBAHW,IAAc,UAAU,QAGnC;EAAM,SAAS;EAAS,WAAW;YAAnC;GACE,kBAAC,SAAD;IACE,GAAI;IACJ,IAAI;IACJ,MAAM;IACG;IACT,aAAU;IACA;IACJ;IACI;IACV,MAAK;IACL,OAAO,OAAO,KAAU,YAAY,OAAO,EAAM,GAAG;IACpD,WAAU;IACV,WAAW,MAAM;AACf,SAAW,EAAE,aAAa,EAAE,OAAO,QAAQ;;IAE7C,CAAA;GAEF,kBAAC,QAAD;IACE,WAAW,EACT,GACA,qFACA,EAAE,iBAAiB,CAAC,GAAS,EAC7B,GAAG,EAAW,EAAM,GAAG,GAAS,CACjC;cAED,kBAAC,OAAD;KAAK,WAAU;KAAU,SAAQ;eAC/B,kBAAC,KAAD,EAAA,UAAA;MACE,kBAAC,UAAD;OAAQ,IAAI;OAAI,IAAI;OAAI,GAAG;OAAI,MAAK;OAAuB,CAAA;MAC3D,kBAAC,UAAD;OAAQ,IAAI;OAAI,IAAI;OAAI,GAAG;OAAG,MAAK;OAA4B,CAAA;MAC/D,kBAAC,QAAD,EAAM,GAAE,kLAAmL,CAAA;MACzL,EAAA,CAAA;KACA,CAAA;IACD,CAAA;GACN"}
1
+ {"version":3,"file":"Switch.js","names":[],"sources":["../../../lib/ui/forms/Switch.tsx"],"sourcesContent":["'use client';\n\nimport clsx from 'clsx';\nimport { type ComponentPropsWithoutRef, useId } from 'react';\n\nimport { getBgColor } from '../helpers/colors/colors';\nimport type { Colors } from '../types/Colors';\n\nexport interface SwitchProps\n extends Omit<\n ComponentPropsWithoutRef<'input'>,\n 'size' | 'type' | 'defaultChecked' | 'onChange' | 'value'\n > {\n checked: boolean;\n color?: Colors;\n value?: string | number | boolean;\n onChange?: (event: Event, value: boolean) => void;\n}\n\nexport function Switch({\n id,\n name,\n checked,\n className,\n color = 'saffron',\n onChange,\n disabled = false,\n tabIndex = 0,\n children,\n role = 'switch',\n value,\n ...attrs\n}: SwitchProps) {\n const internalId = useId();\n const inputId = id ?? internalId;\n const inputName = name ?? inputId;\n const hasChildren = children !== undefined && children !== null;\n const rootClassName = clsx('inline-flex items-center relative', {\n 'gap-4': hasChildren,\n 'cursor-pointer': !disabled,\n 'cursor-not-allowed': disabled,\n });\n const Node = hasChildren ? 'label' : 'span';\n\n return (\n <Node htmlFor={inputId} className={rootClassName}>\n <input\n {...attrs}\n id={inputId}\n name={inputName}\n checked={checked}\n data-name=\"Switch\"\n disabled={disabled}\n role={role}\n tabIndex={tabIndex}\n type=\"checkbox\"\n value={typeof value === 'boolean' ? String(value) : value}\n className=\"absolute opacity-0 left-0 w-full top-0 m-0 p-0 z-1 h-full\"\n onChange={(e) => {\n onChange?.(e.nativeEvent, e.target.checked);\n }}\n />\n\n <span\n className={clsx(\n className,\n 'rounded-pill inline-flex min-w-56 items-center p-4 align-middle transition-colors',\n { 'bg-middleGrey': !checked },\n { [getBgColor(color)]: checked },\n )}\n >\n <svg className=\"size-24\" viewBox=\"0 0 24 24\">\n <g>\n <circle cx={12} cy={12} r={12} fill=\"var(--color-white)\" />\n <circle cx={12} cy={12} r={4} fill=\"var(--color-middleGrey)\" />\n <path d=\"M18.1 7.5c.4.4.4 1.08 0 1.48l-6.83 7q-.3.3-.72.3a1 1 0 0 1-.72-.3L5.9 11.96c-.4-.41-.4-1.07 0-1.48a1 1 0 0 1 1.44 0l3.2 3.27 6.12-6.25a1 1 0 0 1 .72-.3 1 1 0 0 1 .72.3\" />\n </g>\n </svg>\n </span>\n {children}\n </Node>\n );\n}\n"],"mappings":";;;;;;AAmBA,SAAgB,EAAO,EACrB,OACA,SACA,YACA,cACA,WAAQ,WACR,aACA,cAAW,IACX,cAAW,GACX,aACA,UAAO,UACP,UACA,GAAG,KACW;CACd,IAAM,IAAa,GAAO,EACpB,IAAU,KAAM,GAChB,IAAY,KAAQ,GACpB,IAAc,KAAuC,MACrD,IAAgB,EAAK,qCAAqC;EAC9D,SAAS;EACT,kBAAkB,CAAC;EACnB,sBAAsB;EACvB,CAAC;AAGF,QACE,kBAHW,IAAc,UAAU,QAGnC;EAAM,SAAS;EAAS,WAAW;YAAnC;GACE,kBAAC,SAAD;IACE,GAAI;IACJ,IAAI;IACJ,MAAM;IACG;IACT,aAAU;IACA;IACJ;IACI;IACV,MAAK;IACL,OAAO,OAAO,KAAU,YAAY,OAAO,EAAM,GAAG;IACpD,WAAU;IACV,WAAW,MAAM;AACf,SAAW,EAAE,aAAa,EAAE,OAAO,QAAQ;;IAE7C,CAAA;GAEF,kBAAC,QAAD;IACE,WAAW,EACT,GACA,qFACA,EAAE,iBAAiB,CAAC,GAAS,EAC7B,GAAG,EAAW,EAAM,GAAG,GAAS,CACjC;cAED,kBAAC,OAAD;KAAK,WAAU;KAAU,SAAQ;eAC/B,kBAAC,KAAD,EAAA,UAAA;MACE,kBAAC,UAAD;OAAQ,IAAI;OAAI,IAAI;OAAI,GAAG;OAAI,MAAK;OAAuB,CAAA;MAC3D,kBAAC,UAAD;OAAQ,IAAI;OAAI,IAAI;OAAI,GAAG;OAAG,MAAK;OAA4B,CAAA;MAC/D,kBAAC,QAAD,EAAM,GAAE,2KAA4K,CAAA;MAClL,EAAA,CAAA;KACA,CAAA;IACD,CAAA;GACN"}
package/ui/tabs/Tabs.js CHANGED
@@ -1,80 +1,148 @@
1
1
  "use client";
2
- import { Select as e } from "../forms/Select.js";
3
- import { TabsProvider as t } from "../contexts/TabControl.js";
4
- import { useActiveTab as n, useActiveTabControl as r, useRegisterTabControl as i, useTabDispatch as a, useTabsUid as o } from "../hooks/tabControl.js";
5
- import { useKeyboardControls as s } from "../hooks/useKeyboardControls.js";
6
- import { cn as c } from "../helpers/cn.js";
7
- import { useCallback as l, useEffect as u, useRef as d } from "react";
8
- import { jsx as f, jsxs as p } from "react/jsx-runtime";
2
+ import { ArrowButton as e } from "../buttons/ArrowButton.js";
3
+ import { Select as t } from "../forms/Select.js";
4
+ import { TabsProvider as n } from "../contexts/TabControl.js";
5
+ import { useActiveTab as r, useActiveTabControl as i, useRegisterTabControl as a, useTabDispatch as o, useTabsUid as s } from "../hooks/tabControl.js";
6
+ import { useKeyboardControls as c } from "../hooks/useKeyboardControls.js";
7
+ import { cn as l } from "../helpers/cn.js";
8
+ import { useCallback as u, useEffect as d, useRef as f, useState as p } from "react";
9
+ import { jsx as m, jsxs as h } from "react/jsx-runtime";
9
10
  //#region lib/ui/tabs/Tabs.tsx
10
- var m = ({ className: e, compacted: n = !1, selected: r, children: i }) => /* @__PURE__ */ f("div", {
11
- className: c("flex flex-col", { "sm:gap-y-20": !n }, e),
11
+ var g = ({ className: e, compacted: t = !1, selected: r, children: i }) => /* @__PURE__ */ m("div", {
12
+ className: l("flex flex-col", { "sm:gap-y-20": !t }, e),
12
13
  "data-name": "Tabs",
13
- children: /* @__PURE__ */ f(t, {
14
+ children: /* @__PURE__ */ m(n, {
14
15
  selected: r,
15
16
  children: i
16
17
  })
17
- }), h = ({ className: e, children: t }) => /* @__PURE__ */ f("div", {
18
+ }), _ = ({ className: e, children: t }) => /* @__PURE__ */ m("div", {
18
19
  "data-name": "TabsBody",
19
- className: c("grid grid-rows-1 overflow-hidden", e),
20
+ className: l("grid grid-rows-1 overflow-hidden", e),
20
21
  children: t
21
- }), g = ({ value: e, className: t, onSelect: r, children: i, ...a }) => {
22
- let s = o(), l = d(!1), p = n() === e;
23
- return u(() => {
24
- if (!l.current) {
25
- l.current = !0;
22
+ }), v = ({ value: e, className: t, onSelect: n, children: i, ...a }) => {
23
+ let o = s(), c = f(!1), u = r() === e;
24
+ return d(() => {
25
+ if (!c.current) {
26
+ c.current = !0;
26
27
  return;
27
28
  }
28
- p && r?.({ value: e });
29
+ u && n?.({ value: e });
29
30
  }, [
30
- p,
31
+ u,
31
32
  e,
32
- r
33
- ]), /* @__PURE__ */ f("div", {
33
+ n
34
+ ]), /* @__PURE__ */ m("div", {
34
35
  ...a,
35
- "aria-hidden": !p,
36
- className: c("relative col-start-1 row-start-1 transition-opacity duration-500", {
37
- "pointer-events-none z-0 h-0 opacity-0": !p,
38
- "pointer-events-auto z-1 h-auto opacity-100": p
36
+ "aria-hidden": !u,
37
+ className: l("relative col-start-1 row-start-1 transition-opacity duration-500", {
38
+ "pointer-events-none z-0 h-0 opacity-0": !u,
39
+ "pointer-events-auto z-1 h-auto opacity-100": u
39
40
  }, t),
40
- inert: p ? void 0 : !0,
41
- tabIndex: p ? 0 : -1,
42
- id: `TabPanel_${e}_${s}`,
41
+ inert: u ? void 0 : !0,
42
+ tabIndex: u ? 0 : -1,
43
+ id: `TabPanel_${e}_${o}`,
43
44
  "data-name": `TabPanel_${e}`,
44
45
  role: "tabpanel",
45
- "aria-labelledby": `Tab_${e}_${s}`,
46
+ "aria-labelledby": `Tab_${e}_${o}`,
46
47
  children: i
47
48
  });
48
- }, _ = ({ className: e, constrained: t = !1, children: n }) => {
49
- let i = d(null), a = d(null), [o, s] = r();
50
- u(() => {
51
- if (!s?.current || !i.current) return;
52
- let e = a.current.offsetWidth ?? 0, t = s.current.getBoundingClientRect(), n = i.current.getBoundingClientRect(), r = i.current.scrollLeft;
53
- i.current.scrollLeft = r + (t.left - n.left) - e;
54
- }, [s]);
55
- let l = { "--active-tab": o };
56
- return /* @__PURE__ */ f("div", {
49
+ }, y = ({ className: t, constrained: n = !1, children: r }) => {
50
+ let a = f(null), o = f(null), [s, c] = i(), [g, _] = p(!1), [v, y] = p(!1), b = u(() => {
51
+ let e = a.current;
52
+ e && (_(e.scrollLeft > 0), y(Math.ceil(e.scrollLeft) < e.scrollWidth - e.clientWidth));
53
+ }, []);
54
+ d(() => {
55
+ let e = a.current;
56
+ if (!e) return;
57
+ b();
58
+ let t = new ResizeObserver(b);
59
+ return t.observe(e), e.addEventListener("scroll", b, { passive: !0 }), () => {
60
+ t.disconnect(), e.removeEventListener("scroll", b);
61
+ };
62
+ }, [b]), d(() => {
63
+ if (!c?.current || !a.current) return;
64
+ let e = o.current.offsetWidth ?? 0, t = c.current.getBoundingClientRect(), n = a.current.getBoundingClientRect();
65
+ a.current.scrollBy({
66
+ left: t.left - n.left - e,
67
+ behavior: "smooth"
68
+ });
69
+ }, [c]);
70
+ let x = f(null), S = u(() => {
71
+ x.current !== null && (cancelAnimationFrame(x.current), x.current = null);
72
+ }, []), C = u((e) => {
73
+ if (x.current !== null) return;
74
+ let t = () => {
75
+ a.current?.scrollBy({ left: e * 6 }), x.current = requestAnimationFrame(t);
76
+ };
77
+ x.current = requestAnimationFrame(t);
78
+ }, []), w = u((e) => {
79
+ (e.key === " " || e.key === "Enter") && S();
80
+ }, [S]), T = u((e) => {
81
+ (e.key === " " || e.key === "Enter") && (e.preventDefault(), C(-1));
82
+ }, [C]), E = u((e) => {
83
+ (e.key === " " || e.key === "Enter") && (e.preventDefault(), C(1));
84
+ }, [C]);
85
+ d(() => S, [S]);
86
+ let D = { "--active-tab": s }, O = g || v;
87
+ return /* @__PURE__ */ h("div", {
88
+ className: "relative",
57
89
  "data-name": "TabsHeader",
58
- className: c("scrollbar-hidden relative isolate flex max-w-full overflow-x-auto scroll-smooth py-20", e),
59
- ref: i,
60
- children: /* @__PURE__ */ p("div", {
61
- role: "tablist",
62
- className: "flex flex-row",
63
- style: l,
64
- children: [
65
- /* @__PURE__ */ f("div", {
66
- ref: a,
67
- className: c("shrink-0", { "w-16 lg:w-[max(calc((100vw-1212px)/2),116px)] xl:w-[max(calc((100vw-1212px)/2),156px)]": !t })
68
- }),
69
- n,
70
- /* @__PURE__ */ f("div", { className: c("shrink-0", { "w-16 lg:w-[max(calc((100vw-1212px)/2),116px)] xl:w-[max(calc((100vw-1212px)/2),156px)]": !t }) })
71
- ]
72
- })
90
+ children: [
91
+ O && /* @__PURE__ */ m("div", {
92
+ className: "pointer-events-none absolute inset-y-0 left-0 z-10 flex items-center",
93
+ children: /* @__PURE__ */ m(e, {
94
+ icon: "ArrowDefaultLeft",
95
+ size: "small",
96
+ className: l("pointer-events-auto transition-opacity duration-200 focus-visible:ring-8 focus-visible:ring-lavender/20", g ? "opacity-100" : "pointer-events-none opacity-0"),
97
+ onMouseDown: () => C(-1),
98
+ onMouseUp: S,
99
+ onMouseLeave: S,
100
+ onKeyDown: T,
101
+ onKeyUp: w,
102
+ inert: !g,
103
+ tabIndex: g ? 0 : -1,
104
+ children: "Scroll tabs left"
105
+ })
106
+ }),
107
+ /* @__PURE__ */ m("div", {
108
+ className: l("scrollbar-hidden relative isolate flex max-w-full overflow-x-auto py-20", t),
109
+ ref: a,
110
+ children: /* @__PURE__ */ h("div", {
111
+ role: "tablist",
112
+ className: "flex flex-row",
113
+ style: D,
114
+ children: [
115
+ /* @__PURE__ */ m("div", {
116
+ ref: o,
117
+ className: l("shrink-0", { "w-16 lg:w-[max(calc((100vw-1212px)/2),116px)] xl:w-[max(calc((100vw-1212px)/2),156px)]": !n })
118
+ }),
119
+ r,
120
+ /* @__PURE__ */ m("div", { className: l("shrink-0", { "w-16 lg:w-[max(calc((100vw-1212px)/2),116px)] xl:w-[max(calc((100vw-1212px)/2),156px)]": !n }) })
121
+ ]
122
+ })
123
+ }),
124
+ O && /* @__PURE__ */ m("div", {
125
+ className: "pointer-events-none absolute inset-y-0 right-0 z-10 flex items-center",
126
+ children: /* @__PURE__ */ m(e, {
127
+ icon: "ArrowDefaultRight",
128
+ size: "small",
129
+ className: l("pointer-events-auto transition-opacity duration-200 focus-visible:ring-8 focus-visible:ring-lavender/20", v ? "opacity-100" : "pointer-events-none opacity-0"),
130
+ onMouseDown: () => C(1),
131
+ onMouseUp: S,
132
+ onMouseLeave: S,
133
+ onKeyDown: E,
134
+ onKeyUp: w,
135
+ inert: !v,
136
+ tabIndex: v ? 0 : -1,
137
+ children: "Scroll tabs right"
138
+ })
139
+ })
140
+ ]
73
141
  });
74
- }, v = ({ items: t, className: r, "aria-label": i = "Select tab" }) => {
75
- let o = n(), s = a();
76
- return /* @__PURE__ */ f(e, {
77
- value: (o === -1 ? void 0 : o)?.toString(),
142
+ }, b = ({ items: e, className: n, "aria-label": i = "Select tab" }) => {
143
+ let a = r(), s = o();
144
+ return /* @__PURE__ */ m(t, {
145
+ value: (a === -1 ? void 0 : a)?.toString(),
78
146
  onChange: (e, t) => {
79
147
  s({
80
148
  type: "update",
@@ -82,50 +150,50 @@ var m = ({ className: e, compacted: n = !1, selected: r, children: i }) => /* @_
82
150
  });
83
151
  },
84
152
  name: i,
85
- className: c(r),
86
- children: t.map((e) => /* @__PURE__ */ f("option", {
153
+ className: l(n),
154
+ children: e.map((e) => /* @__PURE__ */ m("option", {
87
155
  value: e.value,
88
156
  children: e.label
89
157
  }, e.value))
90
158
  });
91
- }, y = ({ as: e = "h2", label: t, children: r, value: a, onSelect: m, className: h, ...g }) => {
92
- let _ = d(null), v = n(), y = o(), b = i({
93
- value: a,
159
+ }, x = ({ as: e = "h2", label: t, children: n, value: i, onSelect: o, className: p, ...g }) => {
160
+ let _ = f(null), v = r(), y = s(), b = a({
161
+ value: i,
94
162
  ref: _
95
- }), x = d(v), S = d(v === a);
96
- S.current = v === a;
97
- let C = { "--tab-index": a };
98
- u(() => {
99
- x.current !== v && a === v && (_.current?.focus(), m?.({
100
- value: a,
163
+ }), x = f(v), S = f(v === i);
164
+ S.current = v === i;
165
+ let C = { "--tab-index": i };
166
+ d(() => {
167
+ x.current !== v && i === v && (_.current?.focus(), o?.({
168
+ value: i,
101
169
  label: t
102
170
  })), x.current !== v && (x.current = v);
103
171
  }, [
104
172
  t,
105
- a,
106
- m,
173
+ i,
174
+ o,
107
175
  v
108
176
  ]);
109
- let w = s({
110
- start: l(() => {
177
+ let w = c({
178
+ start: u(() => {
111
179
  b({ type: "start" });
112
180
  }, [b]),
113
- end: l(() => {
181
+ end: u(() => {
114
182
  b({ type: "end" });
115
183
  }, [b]),
116
- up: l(() => {
184
+ up: u(() => {
117
185
  b({ type: "previous" });
118
186
  }, [b]),
119
- down: l(() => {
187
+ down: u(() => {
120
188
  b({ type: "next" });
121
189
  }, [b]),
122
- left: l(() => {
190
+ left: u(() => {
123
191
  b({ type: document.documentElement.dir === "rtl" ? "next" : "previous" });
124
192
  }, [b]),
125
- right: l(() => {
193
+ right: u(() => {
126
194
  b({ type: document.documentElement.dir === "rtl" ? "previous" : "next" });
127
195
  }, [b])
128
- }), T = l((e) => {
196
+ }), T = u((e) => {
129
197
  let t = e.key === " " || e.key === "Spacebar" || e.code === "Space", n = e.key === "Enter" || e.code === "Enter";
130
198
  if (S.current && (t || n)) {
131
199
  e.preventDefault();
@@ -135,36 +203,36 @@ var m = ({ className: e, compacted: n = !1, selected: r, children: i }) => /* @_
135
203
  }, [w]), E = () => {
136
204
  S.current || b({
137
205
  type: "update",
138
- payload: a
206
+ payload: i
139
207
  });
140
208
  };
141
- return /* @__PURE__ */ f("div", {
209
+ return /* @__PURE__ */ m("div", {
142
210
  ...g,
143
- id: g.id || `Tab_${a}_${y}`,
211
+ id: g.id || `Tab_${i}_${y}`,
144
212
  "data-name": "Tab",
145
213
  ref: _,
146
214
  role: "tab",
147
215
  "aria-selected": S.current,
148
- "aria-controls": `TabPanel_${a}_${y}`,
216
+ "aria-controls": `TabPanel_${i}_${y}`,
149
217
  tabIndex: S.current ? 0 : -1,
150
- className: c("group text-b3 inline-block cursor-pointer overflow-hidden bg-transparent pe-4 align-middle font-semibold whitespace-nowrap outline-none", {
218
+ className: l("group text-b3 inline-block cursor-pointer overflow-hidden bg-transparent pe-4 align-middle font-semibold whitespace-nowrap outline-none", {
151
219
  "text-white": S.current,
152
220
  "text-black": !S.current,
153
221
  "transition-transform active:scale-[1.03]": S.current
154
222
  }),
155
223
  onKeyDown: T,
156
224
  onClick: E,
157
- children: /* @__PURE__ */ p(e, {
158
- className: c("relative inline-block px-20 py-12", "before:rounded-pill before:transition-colors/opacity before:absolute before:inset-0 before:-z-1 before:duration-300 group-focus-within:before:bg-(--color-pearl) group-hover:before:bg-(--color-pearl) group-focus:before:bg-(--color-pearl)", h),
225
+ children: /* @__PURE__ */ h(e, {
226
+ className: l("relative inline-block px-20 py-12", "before:rounded-pill before:transition-colors/opacity before:absolute before:inset-0 before:-z-1 before:duration-300 group-focus-within:before:bg-(--color-pearl) group-hover:before:bg-(--color-pearl) group-focus:before:bg-(--color-pearl)", p),
159
227
  ...S ? {} : { role: "presentation" },
160
- children: [/* @__PURE__ */ f("span", {
161
- className: c("tab-focus-pill ease transition-transform/colors", "rounded-pill absolute inset-0 -z-1 duration-300", "bg-black"),
228
+ children: [/* @__PURE__ */ m("span", {
229
+ className: l("tab-focus-pill ease transition-transform/colors", "rounded-pill absolute inset-0 -z-1 duration-300", "bg-black"),
162
230
  style: C
163
- }), r ?? t]
231
+ }), n ?? t]
164
232
  })
165
233
  });
166
234
  };
167
235
  //#endregion
168
- export { y as Tab, _ as TabList, g as TabPanel, v as TabSelect, m as Tabs, h as TabsBody };
236
+ export { x as Tab, y as TabList, v as TabPanel, b as TabSelect, g as Tabs, _ as TabsBody };
169
237
 
170
238
  //# sourceMappingURL=Tabs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Tabs.js","names":[],"sources":["../../../lib/ui/tabs/Tabs.tsx"],"sourcesContent":["'use client';\n\nimport {\n type ComponentProps,\n type ComponentPropsWithoutRef,\n type CSSProperties,\n type FunctionComponent,\n type PropsWithChildren,\n type RefObject,\n type KeyboardEvent,\n useCallback,\n useEffect,\n useRef,\n} from 'react';\n\nimport { TabsProvider } from '../contexts/TabControl';\nimport {\n useActiveTab,\n useActiveTabControl,\n useRegisterTabControl,\n useTabDispatch,\n useTabsUid,\n} from '../hooks/tabControl';\nimport { useKeyboardControls } from '../hooks/useKeyboardControls';\nimport { Select } from '../forms/Select';\nimport { cn } from '../helpers/cn';\n\ntype ProviderProps = ComponentProps<typeof TabsProvider>;\n\ninterface Props extends ProviderProps {\n className?: string;\n /**\n * Shrink the spacing between tabs and panels\n */\n compacted?: boolean;\n}\n\nexport const Tabs: FunctionComponent<PropsWithChildren<Props>> = ({\n className,\n compacted = false,\n selected,\n children,\n}) => {\n return (\n <div\n className={cn(\n 'flex flex-col',\n {\n 'sm:gap-y-20': !compacted,\n },\n className,\n )}\n data-name=\"Tabs\"\n >\n <TabsProvider selected={selected}>{children}</TabsProvider>\n </div>\n );\n};\n\ninterface TabsBodyProps {\n className?: string;\n}\n\nexport const TabsBody: FunctionComponent<PropsWithChildren<TabsBodyProps>> = ({\n className,\n children,\n}) => {\n return (\n <div data-name=\"TabsBody\" className={cn('grid grid-rows-1 overflow-hidden', className)}>\n {children}\n </div>\n );\n};\n\ninterface TabPanelProps {\n className?: string;\n /**\n * Tab panel index<br/>\n * _Can be **0** or **1** indexed_\n */\n value: number;\n /**\n * on panel select handler\n */\n onSelect?: (context: { value: TabPanelProps['value'] }) => void;\n}\n\nexport const TabPanel: FunctionComponent<PropsWithChildren<TabPanelProps>> = ({\n value,\n className,\n onSelect,\n children,\n ...attrs\n}) => {\n const uid = useTabsUid();\n const mounted = useRef(false);\n const tabSelected = useActiveTab();\n const isActive = tabSelected === value;\n\n useEffect(() => {\n if (!mounted.current) {\n mounted.current = true;\n return;\n }\n\n if (isActive) {\n onSelect?.({ value });\n }\n }, [isActive, value, onSelect]);\n\n return (\n <div\n {...attrs}\n aria-hidden={!isActive}\n className={cn(\n 'relative col-start-1 row-start-1 transition-opacity duration-500',\n {\n 'pointer-events-none z-0 h-0 opacity-0': !isActive,\n 'pointer-events-auto z-1 h-auto opacity-100': isActive,\n },\n className,\n )}\n inert={isActive ? undefined : true}\n tabIndex={isActive ? 0 : -1}\n id={`TabPanel_${value}_${uid}`}\n data-name={`TabPanel_${value}`}\n role=\"tabpanel\"\n aria-labelledby={`Tab_${value}_${uid}`}\n >\n {children}\n </div>\n );\n};\n\ninterface TabListProps {\n className?: string;\n /**\n * Remove filler placeholders\n */\n constrained?: boolean;\n}\n\nexport const TabList: FunctionComponent<PropsWithChildren<TabListProps>> = ({\n className,\n constrained = false,\n children,\n}) => {\n const ref = useRef<HTMLDivElement>(null);\n const spacerRef = useRef<HTMLDivElement>(null);\n const [currentTab, currentControl] = useActiveTabControl();\n\n useEffect(() => {\n if (!currentControl?.current || !ref.current) {\n return;\n }\n\n const spacerWidth = spacerRef.current!.offsetWidth ?? 0;\n const currentRect = currentControl.current.getBoundingClientRect();\n const containerRect = ref.current!.getBoundingClientRect();\n const scroll = ref.current!.scrollLeft;\n\n ref.current.scrollLeft = scroll + (currentRect.left - containerRect.left) - spacerWidth;\n }, [currentControl]);\n\n const style = { '--active-tab': currentTab } as CSSProperties;\n\n return (\n <div\n data-name=\"TabsHeader\"\n className={cn(\n 'scrollbar-hidden relative isolate flex max-w-full overflow-x-auto scroll-smooth py-20',\n className,\n )}\n ref={ref}\n >\n <div role=\"tablist\" className=\"flex flex-row\" style={style}>\n <div\n ref={spacerRef}\n className={cn('shrink-0', {\n 'w-16 lg:w-[max(calc((100vw-1212px)/2),116px)] xl:w-[max(calc((100vw-1212px)/2),156px)]':\n !constrained,\n })}\n />\n {children}\n <div\n className={cn('shrink-0', {\n 'w-16 lg:w-[max(calc((100vw-1212px)/2),116px)] xl:w-[max(calc((100vw-1212px)/2),156px)]':\n !constrained,\n })}\n />\n </div>\n </div>\n );\n};\n\nexport interface TabSelectProps {\n items: { label: string; value: number }[];\n className?: string;\n 'aria-label'?: string;\n}\n\nexport const TabSelect: FunctionComponent<TabSelectProps> = ({\n items,\n className,\n 'aria-label': ariaLabel = 'Select tab',\n}) => {\n const activeTab = useActiveTab();\n const dispatch = useTabDispatch();\n\n const handleChange = (_event: Event, value: string) => {\n dispatch({ type: 'update', payload: Number(value) });\n };\n\n const selectValue = activeTab === -1 ? undefined : activeTab;\n\n return (\n <Select\n value={selectValue?.toString()}\n onChange={handleChange}\n name={ariaLabel}\n className={cn(className)}\n >\n {items.map((item) => (\n <option key={item.value} value={item.value}>\n {item.label}\n </option>\n ))}\n </Select>\n );\n};\n\ninterface TabProps extends Omit<ComponentPropsWithoutRef<'div'>, 'onSelect'> {\n /**\n * Tab Heading text\n */\n label?: string;\n /**\n * Tab heading index<br/>\n * _Can be **0** or **1** indexed_\n */\n value: number;\n /**\n * on tab select handler\n */\n onSelect?: (context: { value: TabProps['value']; label: TabProps['label'] }) => void;\n /**\n * Heading type\n * _default: **h2**_\n */\n as?: 'div' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5';\n}\n\nexport const Tab: FunctionComponent<PropsWithChildren<TabProps>> = ({\n as: TagName = 'h2',\n label,\n children,\n value,\n onSelect,\n className,\n ...attr\n}) => {\n const ref = useRef<HTMLDivElement>(null);\n const activeTab = useActiveTab();\n const uid = useTabsUid();\n const dispatch = useRegisterTabControl({ value, ref: ref as RefObject<HTMLDivElement> });\n const previousActiveTab = useRef(activeTab);\n\n const isActive = useRef(activeTab === value);\n isActive.current = activeTab === value;\n\n const pillStyle = { '--tab-index': value } as CSSProperties;\n\n useEffect(() => {\n if (previousActiveTab.current !== activeTab && value === activeTab) {\n ref.current?.focus();\n onSelect?.({ value, label });\n }\n if (previousActiveTab.current !== activeTab) {\n previousActiveTab.current = activeTab;\n }\n }, [label, value, onSelect, activeTab]);\n\n const start = useCallback(() => {\n dispatch({ type: 'start' });\n }, [dispatch]);\n\n const end = useCallback(() => {\n dispatch({ type: 'end' });\n }, [dispatch]);\n\n const up = useCallback(() => {\n dispatch({ type: 'previous' });\n }, [dispatch]);\n\n const down = useCallback(() => {\n dispatch({ type: 'next' });\n }, [dispatch]);\n\n const left = useCallback(() => {\n const isRTL = document.documentElement.dir === 'rtl';\n\n dispatch({ type: isRTL ? 'next' : 'previous' });\n }, [dispatch]);\n\n const right = useCallback(() => {\n const isRTL = document.documentElement.dir === 'rtl';\n\n dispatch({ type: isRTL ? 'previous' : 'next' });\n }, [dispatch]);\n\n const keyboardHandler = useKeyboardControls({\n start,\n end,\n up,\n down,\n left,\n right,\n });\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent<HTMLDivElement>) => {\n const isSpace = e.key === ' ' || e.key === 'Spacebar' || e.code === 'Space';\n const isEnter = e.key === 'Enter' || e.code === 'Enter';\n\n if (isActive.current && (isSpace || isEnter)) {\n e.preventDefault();\n return;\n }\n\n keyboardHandler(e);\n },\n [keyboardHandler],\n );\n\n const handleClick = () => {\n if (isActive.current) {\n return;\n }\n\n dispatch({ type: 'update', payload: value });\n };\n\n return (\n <div\n {...attr}\n id={attr.id || `Tab_${value}_${uid}`}\n data-name=\"Tab\"\n ref={ref}\n role=\"tab\"\n aria-selected={isActive.current}\n aria-controls={`TabPanel_${value}_${uid}`}\n tabIndex={isActive.current ? 0 : -1}\n className={cn(\n 'group text-b3 inline-block cursor-pointer overflow-hidden bg-transparent pe-4 align-middle font-semibold whitespace-nowrap outline-none',\n {\n 'text-white': isActive.current,\n 'text-black': !isActive.current,\n 'transition-transform active:scale-[1.03]': isActive.current,\n },\n )}\n onKeyDown={handleKeyDown}\n onClick={handleClick}\n >\n <TagName\n className={cn(\n 'relative inline-block px-20 py-12',\n 'before:rounded-pill before:transition-colors/opacity before:absolute before:inset-0 before:-z-1 before:duration-300 group-focus-within:before:bg-(--color-pearl) group-hover:before:bg-(--color-pearl) group-focus:before:bg-(--color-pearl)',\n className,\n )}\n {...(!isActive ? { role: 'presentation' } : {})}\n >\n <span\n className={cn(\n 'tab-focus-pill ease transition-transform/colors',\n 'rounded-pill absolute inset-0 -z-1 duration-300',\n 'bg-black',\n )}\n style={pillStyle}\n />\n {children ?? label}\n </TagName>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"Tabs.js","names":[],"sources":["../../../lib/ui/tabs/Tabs.tsx"],"sourcesContent":["'use client';\n\nimport {\n type ComponentProps,\n type ComponentPropsWithoutRef,\n type CSSProperties,\n type FunctionComponent,\n type PropsWithChildren,\n type RefObject,\n type KeyboardEvent,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react';\n\nimport { TabsProvider } from '../contexts/TabControl';\nimport {\n useActiveTab,\n useActiveTabControl,\n useRegisterTabControl,\n useTabDispatch,\n useTabsUid,\n} from '../hooks/tabControl';\nimport { useKeyboardControls } from '../hooks/useKeyboardControls';\nimport { Select } from '../forms/Select';\nimport { cn } from '../helpers/cn';\nimport { ArrowButton } from '../buttons/ArrowButton';\n\ntype ProviderProps = ComponentProps<typeof TabsProvider>;\n\ninterface Props extends ProviderProps {\n className?: string;\n /**\n * Shrink the spacing between tabs and panels\n */\n compacted?: boolean;\n}\n\nexport const Tabs: FunctionComponent<PropsWithChildren<Props>> = ({\n className,\n compacted = false,\n selected,\n children,\n}) => {\n return (\n <div\n className={cn(\n 'flex flex-col',\n {\n 'sm:gap-y-20': !compacted,\n },\n className,\n )}\n data-name=\"Tabs\"\n >\n <TabsProvider selected={selected}>{children}</TabsProvider>\n </div>\n );\n};\n\ninterface TabsBodyProps {\n className?: string;\n}\n\nexport const TabsBody: FunctionComponent<PropsWithChildren<TabsBodyProps>> = ({\n className,\n children,\n}) => {\n return (\n <div data-name=\"TabsBody\" className={cn('grid grid-rows-1 overflow-hidden', className)}>\n {children}\n </div>\n );\n};\n\ninterface TabPanelProps {\n className?: string;\n /**\n * Tab panel index<br/>\n * _Can be **0** or **1** indexed_\n */\n value: number;\n /**\n * on panel select handler\n */\n onSelect?: (context: { value: TabPanelProps['value'] }) => void;\n}\n\nexport const TabPanel: FunctionComponent<PropsWithChildren<TabPanelProps>> = ({\n value,\n className,\n onSelect,\n children,\n ...attrs\n}) => {\n const uid = useTabsUid();\n const mounted = useRef(false);\n const tabSelected = useActiveTab();\n const isActive = tabSelected === value;\n\n useEffect(() => {\n if (!mounted.current) {\n mounted.current = true;\n return;\n }\n\n if (isActive) {\n onSelect?.({ value });\n }\n }, [isActive, value, onSelect]);\n\n return (\n <div\n {...attrs}\n aria-hidden={!isActive}\n className={cn(\n 'relative col-start-1 row-start-1 transition-opacity duration-500',\n {\n 'pointer-events-none z-0 h-0 opacity-0': !isActive,\n 'pointer-events-auto z-1 h-auto opacity-100': isActive,\n },\n className,\n )}\n inert={isActive ? undefined : true}\n tabIndex={isActive ? 0 : -1}\n id={`TabPanel_${value}_${uid}`}\n data-name={`TabPanel_${value}`}\n role=\"tabpanel\"\n aria-labelledby={`Tab_${value}_${uid}`}\n >\n {children}\n </div>\n );\n};\n\ninterface TabListProps {\n className?: string;\n /**\n * Remove filler placeholders\n */\n constrained?: boolean;\n}\n\nexport const TabList: FunctionComponent<PropsWithChildren<TabListProps>> = ({\n className,\n constrained = false,\n children,\n}) => {\n const ref = useRef<HTMLDivElement>(null);\n const spacerRef = useRef<HTMLDivElement>(null);\n const [currentTab, currentControl] = useActiveTabControl();\n const [canScrollLeft, setCanScrollLeft] = useState(false);\n const [canScrollRight, setCanScrollRight] = useState(false);\n\n const updateScrollState = useCallback(() => {\n const el = ref.current;\n if (!el) return;\n setCanScrollLeft(el.scrollLeft > 0);\n setCanScrollRight(Math.ceil(el.scrollLeft) < el.scrollWidth - el.clientWidth);\n }, []);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n\n updateScrollState();\n\n const observer = new ResizeObserver(updateScrollState);\n observer.observe(el);\n el.addEventListener('scroll', updateScrollState, { passive: true });\n\n return () => {\n observer.disconnect();\n el.removeEventListener('scroll', updateScrollState);\n };\n }, [updateScrollState]);\n\n useEffect(() => {\n if (!currentControl?.current || !ref.current) {\n return;\n }\n\n const spacerWidth = spacerRef.current!.offsetWidth ?? 0;\n const currentRect = currentControl.current.getBoundingClientRect();\n const containerRect = ref.current!.getBoundingClientRect();\n\n ref.current.scrollBy({\n left: currentRect.left - containerRect.left - spacerWidth,\n behavior: 'smooth',\n });\n }, [currentControl]);\n\n const scrollRafRef = useRef<number | null>(null);\n\n const stopScroll = useCallback(() => {\n if (scrollRafRef.current !== null) {\n cancelAnimationFrame(scrollRafRef.current);\n scrollRafRef.current = null;\n }\n }, []);\n\n const startScroll = useCallback((direction: -1 | 1) => {\n if (scrollRafRef.current !== null) return;\n const step = () => {\n ref.current?.scrollBy({ left: direction * 6 });\n scrollRafRef.current = requestAnimationFrame(step);\n };\n scrollRafRef.current = requestAnimationFrame(step);\n }, []);\n\n const handleScrollKeyUp = useCallback(\n (e: KeyboardEvent<HTMLButtonElement>) => {\n if (e.key === ' ' || e.key === 'Enter') stopScroll();\n },\n [stopScroll],\n );\n\n const handleScrollLeftKeyDown = useCallback(\n (e: KeyboardEvent<HTMLButtonElement>) => {\n if (e.key === ' ' || e.key === 'Enter') {\n e.preventDefault();\n startScroll(-1);\n }\n },\n [startScroll],\n );\n\n const handleScrollRightKeyDown = useCallback(\n (e: KeyboardEvent<HTMLButtonElement>) => {\n if (e.key === ' ' || e.key === 'Enter') {\n e.preventDefault();\n startScroll(1);\n }\n },\n [startScroll],\n );\n\n useEffect(() => stopScroll, [stopScroll]);\n\n const style = { '--active-tab': currentTab } as CSSProperties;\n const isScrollable = canScrollLeft || canScrollRight;\n\n return (\n <div className=\"relative\" data-name=\"TabsHeader\">\n {isScrollable && (\n <div className=\"pointer-events-none absolute inset-y-0 left-0 z-10 flex items-center\">\n <ArrowButton\n icon=\"ArrowDefaultLeft\"\n size=\"small\"\n className={cn(\n 'pointer-events-auto transition-opacity duration-200 focus-visible:ring-8 focus-visible:ring-lavender/20',\n canScrollLeft ? 'opacity-100' : 'pointer-events-none opacity-0',\n )}\n onMouseDown={() => startScroll(-1)}\n onMouseUp={stopScroll}\n onMouseLeave={stopScroll}\n onKeyDown={handleScrollLeftKeyDown}\n onKeyUp={handleScrollKeyUp}\n inert={!canScrollLeft}\n tabIndex={canScrollLeft ? 0 : -1}\n >\n Scroll tabs left\n </ArrowButton>\n </div>\n )}\n <div\n className={cn(\n 'scrollbar-hidden relative isolate flex max-w-full overflow-x-auto py-20',\n className,\n )}\n ref={ref}\n >\n <div role=\"tablist\" className=\"flex flex-row\" style={style}>\n <div\n ref={spacerRef}\n className={cn('shrink-0', {\n 'w-16 lg:w-[max(calc((100vw-1212px)/2),116px)] xl:w-[max(calc((100vw-1212px)/2),156px)]':\n !constrained,\n })}\n />\n {children}\n <div\n className={cn('shrink-0', {\n 'w-16 lg:w-[max(calc((100vw-1212px)/2),116px)] xl:w-[max(calc((100vw-1212px)/2),156px)]':\n !constrained,\n })}\n />\n </div>\n </div>\n {isScrollable && (\n <div className=\"pointer-events-none absolute inset-y-0 right-0 z-10 flex items-center\">\n <ArrowButton\n icon=\"ArrowDefaultRight\"\n size=\"small\"\n className={cn(\n 'pointer-events-auto transition-opacity duration-200 focus-visible:ring-8 focus-visible:ring-lavender/20',\n canScrollRight ? 'opacity-100' : 'pointer-events-none opacity-0',\n )}\n onMouseDown={() => startScroll(1)}\n onMouseUp={stopScroll}\n onMouseLeave={stopScroll}\n onKeyDown={handleScrollRightKeyDown}\n onKeyUp={handleScrollKeyUp}\n inert={!canScrollRight}\n tabIndex={canScrollRight ? 0 : -1}\n >\n Scroll tabs right\n </ArrowButton>\n </div>\n )}\n </div>\n );\n};\n\nexport interface TabSelectProps {\n items: { label: string; value: number }[];\n className?: string;\n 'aria-label'?: string;\n}\n\nexport const TabSelect: FunctionComponent<TabSelectProps> = ({\n items,\n className,\n 'aria-label': ariaLabel = 'Select tab',\n}) => {\n const activeTab = useActiveTab();\n const dispatch = useTabDispatch();\n\n const handleChange = (_event: Event, value: string) => {\n dispatch({ type: 'update', payload: Number(value) });\n };\n\n const selectValue = activeTab === -1 ? undefined : activeTab;\n\n return (\n <Select\n value={selectValue?.toString()}\n onChange={handleChange}\n name={ariaLabel}\n className={cn(className)}\n >\n {items.map((item) => (\n <option key={item.value} value={item.value}>\n {item.label}\n </option>\n ))}\n </Select>\n );\n};\n\ninterface TabProps extends Omit<ComponentPropsWithoutRef<'div'>, 'onSelect'> {\n /**\n * Tab Heading text\n */\n label?: string;\n /**\n * Tab heading index<br/>\n * _Can be **0** or **1** indexed_\n */\n value: number;\n /**\n * on tab select handler\n */\n onSelect?: (context: { value: TabProps['value']; label: TabProps['label'] }) => void;\n /**\n * Heading type\n * _default: **h2**_\n */\n as?: 'div' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5';\n}\n\nexport const Tab: FunctionComponent<PropsWithChildren<TabProps>> = ({\n as: TagName = 'h2',\n label,\n children,\n value,\n onSelect,\n className,\n ...attr\n}) => {\n const ref = useRef<HTMLDivElement>(null);\n const activeTab = useActiveTab();\n const uid = useTabsUid();\n const dispatch = useRegisterTabControl({ value, ref: ref as RefObject<HTMLDivElement> });\n const previousActiveTab = useRef(activeTab);\n\n const isActive = useRef(activeTab === value);\n isActive.current = activeTab === value;\n\n const pillStyle = { '--tab-index': value } as CSSProperties;\n\n useEffect(() => {\n if (previousActiveTab.current !== activeTab && value === activeTab) {\n ref.current?.focus();\n onSelect?.({ value, label });\n }\n if (previousActiveTab.current !== activeTab) {\n previousActiveTab.current = activeTab;\n }\n }, [label, value, onSelect, activeTab]);\n\n const start = useCallback(() => {\n dispatch({ type: 'start' });\n }, [dispatch]);\n\n const end = useCallback(() => {\n dispatch({ type: 'end' });\n }, [dispatch]);\n\n const up = useCallback(() => {\n dispatch({ type: 'previous' });\n }, [dispatch]);\n\n const down = useCallback(() => {\n dispatch({ type: 'next' });\n }, [dispatch]);\n\n const left = useCallback(() => {\n const isRTL = document.documentElement.dir === 'rtl';\n\n dispatch({ type: isRTL ? 'next' : 'previous' });\n }, [dispatch]);\n\n const right = useCallback(() => {\n const isRTL = document.documentElement.dir === 'rtl';\n\n dispatch({ type: isRTL ? 'previous' : 'next' });\n }, [dispatch]);\n\n const keyboardHandler = useKeyboardControls({\n start,\n end,\n up,\n down,\n left,\n right,\n });\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent<HTMLDivElement>) => {\n const isSpace = e.key === ' ' || e.key === 'Spacebar' || e.code === 'Space';\n const isEnter = e.key === 'Enter' || e.code === 'Enter';\n\n if (isActive.current && (isSpace || isEnter)) {\n e.preventDefault();\n return;\n }\n\n keyboardHandler(e);\n },\n [keyboardHandler],\n );\n\n const handleClick = () => {\n if (isActive.current) {\n return;\n }\n\n dispatch({ type: 'update', payload: value });\n };\n\n return (\n <div\n {...attr}\n id={attr.id || `Tab_${value}_${uid}`}\n data-name=\"Tab\"\n ref={ref}\n role=\"tab\"\n aria-selected={isActive.current}\n aria-controls={`TabPanel_${value}_${uid}`}\n tabIndex={isActive.current ? 0 : -1}\n className={cn(\n 'group text-b3 inline-block cursor-pointer overflow-hidden bg-transparent pe-4 align-middle font-semibold whitespace-nowrap outline-none',\n {\n 'text-white': isActive.current,\n 'text-black': !isActive.current,\n 'transition-transform active:scale-[1.03]': isActive.current,\n },\n )}\n onKeyDown={handleKeyDown}\n onClick={handleClick}\n >\n <TagName\n className={cn(\n 'relative inline-block px-20 py-12',\n 'before:rounded-pill before:transition-colors/opacity before:absolute before:inset-0 before:-z-1 before:duration-300 group-focus-within:before:bg-(--color-pearl) group-hover:before:bg-(--color-pearl) group-focus:before:bg-(--color-pearl)',\n className,\n )}\n {...(!isActive ? { role: 'presentation' } : {})}\n >\n <span\n className={cn(\n 'tab-focus-pill ease transition-transform/colors',\n 'rounded-pill absolute inset-0 -z-1 duration-300',\n 'bg-black',\n )}\n style={pillStyle}\n />\n {children ?? label}\n </TagName>\n </div>\n );\n};\n"],"mappings":"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"}