@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.
- package/examples/chat-window-csat-demo.d.ts +1 -0
- package/examples/chat-window-csat-demo.js +68 -0
- package/examples/chat-window-csat-demo.js.map +1 -0
- package/examples/chat-window-csat-prompt-demo.d.ts +1 -0
- package/examples/chat-window-csat-prompt-demo.js +40 -0
- package/examples/chat-window-csat-prompt-demo.js.map +1 -0
- package/examples/chat-window-demo.js +41 -17
- package/examples/chat-window-demo.js.map +1 -1
- package/examples/tabs-demo.js +2 -5
- package/examples/tabs-demo.js.map +1 -1
- package/package.json +1 -1
- package/ui/ChatWindow.d.ts +11 -1
- package/ui/ChatWindow.js +67 -44
- package/ui/ChatWindow.js.map +1 -1
- package/ui/ChatWindowCsatPrompt.d.ts +15 -0
- package/ui/ChatWindowCsatPrompt.js +71 -0
- package/ui/ChatWindowCsatPrompt.js.map +1 -0
- package/ui/ChatWindowCsatThankYou.d.ts +9 -0
- package/ui/ChatWindowCsatThankYou.js +29 -0
- package/ui/ChatWindowCsatThankYou.js.map +1 -0
- package/ui/forms/Switch.js +1 -1
- package/ui/forms/Switch.js.map +1 -1
- package/ui/tabs/Tabs.js +157 -89
- package/ui/tabs/Tabs.js.map +1 -1
|
@@ -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 {
|
|
4
|
-
import { jsx as
|
|
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
|
-
|
|
7
|
-
|
|
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
|
-
}]), [
|
|
16
|
-
function
|
|
17
|
-
if (!
|
|
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:
|
|
40
|
+
content: u
|
|
22
41
|
};
|
|
23
|
-
|
|
24
|
-
|
|
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__ */
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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 {
|
|
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 {
|
|
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"}
|
package/examples/tabs-demo.js
CHANGED
|
@@ -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(
|
|
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
package/ui/ChatWindow.d.ts
CHANGED
|
@@ -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
|
|
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 {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
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
|
|
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__ */
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
}, [
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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__ */
|
|
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__ */
|
|
49
|
+
children: [/* @__PURE__ */ f("div", {
|
|
37
50
|
className: "absolute inset-x-4 top-4 h-102 bg-lightSand rounded-[24]",
|
|
38
|
-
children:
|
|
39
|
-
}), /* @__PURE__ */
|
|
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__ */
|
|
54
|
+
children: [/* @__PURE__ */ f("div", {
|
|
42
55
|
className: "flex items-center justify-center px-12 py-8 w-full",
|
|
43
|
-
children: /* @__PURE__ */
|
|
56
|
+
children: /* @__PURE__ */ f("p", {
|
|
44
57
|
className: "font-serif font-bold italic text-h5 text-center text-black line-clamp-2",
|
|
45
|
-
children:
|
|
58
|
+
children: o
|
|
46
59
|
})
|
|
47
|
-
}),
|
|
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__ */
|
|
64
|
+
children: /* @__PURE__ */ f(d, {
|
|
52
65
|
name: "Sparkles",
|
|
53
66
|
width: "24px"
|
|
54
67
|
})
|
|
55
68
|
})]
|
|
56
69
|
})]
|
|
57
70
|
}),
|
|
58
|
-
/* @__PURE__ */
|
|
59
|
-
ref:
|
|
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
|
-
|
|
78
|
+
h ? /* @__PURE__ */ f("div", {
|
|
66
79
|
className: "px-20",
|
|
67
|
-
children: /* @__PURE__ */
|
|
80
|
+
children: /* @__PURE__ */ f("p", {
|
|
68
81
|
className: "text-b3 text-black",
|
|
69
|
-
children:
|
|
82
|
+
children: h
|
|
70
83
|
})
|
|
71
84
|
}) : null,
|
|
72
|
-
|
|
85
|
+
g.map((e) => /* @__PURE__ */ f(n, {
|
|
73
86
|
sender: e.sender,
|
|
74
87
|
children: e.content
|
|
75
88
|
}, e.id)),
|
|
76
|
-
|
|
89
|
+
y && /* @__PURE__ */ f("div", {
|
|
77
90
|
className: "px-12",
|
|
78
|
-
children: /* @__PURE__ */
|
|
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__ */
|
|
105
|
+
/* @__PURE__ */ f("div", {
|
|
83
106
|
className: "shrink-0 px-8 pb-8 pt-12",
|
|
84
|
-
children: /* @__PURE__ */
|
|
85
|
-
value:
|
|
86
|
-
placeholder:
|
|
87
|
-
onValueChange:
|
|
88
|
-
onSubmit:
|
|
89
|
-
onAudioClick:
|
|
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
|
-
}),
|
|
116
|
+
}), S && /* @__PURE__ */ f("button", {
|
|
94
117
|
type: "button",
|
|
95
118
|
"aria-label": "Close chat",
|
|
96
|
-
onClick:
|
|
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__ */
|
|
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 {
|
|
129
|
+
export { h as ChatWindow };
|
|
107
130
|
|
|
108
131
|
//# sourceMappingURL=ChatWindow.js.map
|
package/ui/ChatWindow.js.map
CHANGED
|
@@ -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
|
|
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"}
|
package/ui/forms/Switch.js
CHANGED
|
@@ -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.
|
|
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
|
}),
|
package/ui/forms/Switch.js.map
CHANGED
|
@@ -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.
|
|
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 {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
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
|
|
11
|
-
className:
|
|
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__ */
|
|
14
|
+
children: /* @__PURE__ */ m(n, {
|
|
14
15
|
selected: r,
|
|
15
16
|
children: i
|
|
16
17
|
})
|
|
17
|
-
}),
|
|
18
|
+
}), _ = ({ className: e, children: t }) => /* @__PURE__ */ m("div", {
|
|
18
19
|
"data-name": "TabsBody",
|
|
19
|
-
className:
|
|
20
|
+
className: l("grid grid-rows-1 overflow-hidden", e),
|
|
20
21
|
children: t
|
|
21
|
-
}),
|
|
22
|
-
let
|
|
23
|
-
return
|
|
24
|
-
if (!
|
|
25
|
-
|
|
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
|
-
|
|
29
|
+
u && n?.({ value: e });
|
|
29
30
|
}, [
|
|
30
|
-
|
|
31
|
+
u,
|
|
31
32
|
e,
|
|
32
|
-
|
|
33
|
-
]), /* @__PURE__ */
|
|
33
|
+
n
|
|
34
|
+
]), /* @__PURE__ */ m("div", {
|
|
34
35
|
...a,
|
|
35
|
-
"aria-hidden": !
|
|
36
|
-
className:
|
|
37
|
-
"pointer-events-none z-0 h-0 opacity-0": !
|
|
38
|
-
"pointer-events-auto z-1 h-auto opacity-100":
|
|
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:
|
|
41
|
-
tabIndex:
|
|
42
|
-
id: `TabPanel_${e}_${
|
|
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}_${
|
|
46
|
+
"aria-labelledby": `Tab_${e}_${o}`,
|
|
46
47
|
children: i
|
|
47
48
|
});
|
|
48
|
-
},
|
|
49
|
-
let
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
},
|
|
75
|
-
let
|
|
76
|
-
return /* @__PURE__ */
|
|
77
|
-
value: (
|
|
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:
|
|
86
|
-
children:
|
|
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
|
-
},
|
|
92
|
-
let _ =
|
|
93
|
-
value:
|
|
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 =
|
|
96
|
-
S.current = v ===
|
|
97
|
-
let C = { "--tab-index":
|
|
98
|
-
|
|
99
|
-
x.current !== v &&
|
|
100
|
-
value:
|
|
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
|
-
|
|
106
|
-
|
|
173
|
+
i,
|
|
174
|
+
o,
|
|
107
175
|
v
|
|
108
176
|
]);
|
|
109
|
-
let w =
|
|
110
|
-
start:
|
|
177
|
+
let w = c({
|
|
178
|
+
start: u(() => {
|
|
111
179
|
b({ type: "start" });
|
|
112
180
|
}, [b]),
|
|
113
|
-
end:
|
|
181
|
+
end: u(() => {
|
|
114
182
|
b({ type: "end" });
|
|
115
183
|
}, [b]),
|
|
116
|
-
up:
|
|
184
|
+
up: u(() => {
|
|
117
185
|
b({ type: "previous" });
|
|
118
186
|
}, [b]),
|
|
119
|
-
down:
|
|
187
|
+
down: u(() => {
|
|
120
188
|
b({ type: "next" });
|
|
121
189
|
}, [b]),
|
|
122
|
-
left:
|
|
190
|
+
left: u(() => {
|
|
123
191
|
b({ type: document.documentElement.dir === "rtl" ? "next" : "previous" });
|
|
124
192
|
}, [b]),
|
|
125
|
-
right:
|
|
193
|
+
right: u(() => {
|
|
126
194
|
b({ type: document.documentElement.dir === "rtl" ? "previous" : "next" });
|
|
127
195
|
}, [b])
|
|
128
|
-
}), T =
|
|
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:
|
|
206
|
+
payload: i
|
|
139
207
|
});
|
|
140
208
|
};
|
|
141
|
-
return /* @__PURE__ */
|
|
209
|
+
return /* @__PURE__ */ m("div", {
|
|
142
210
|
...g,
|
|
143
|
-
id: g.id || `Tab_${
|
|
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_${
|
|
216
|
+
"aria-controls": `TabPanel_${i}_${y}`,
|
|
149
217
|
tabIndex: S.current ? 0 : -1,
|
|
150
|
-
className:
|
|
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__ */
|
|
158
|
-
className:
|
|
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__ */
|
|
161
|
-
className:
|
|
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
|
-
}),
|
|
231
|
+
}), n ?? t]
|
|
164
232
|
})
|
|
165
233
|
});
|
|
166
234
|
};
|
|
167
235
|
//#endregion
|
|
168
|
-
export {
|
|
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
|
package/ui/tabs/Tabs.js.map
CHANGED
|
@@ -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":";;;;;;;;;AAqCA,IAAa,KAAqD,EAChE,cACA,eAAY,IACZ,aACA,kBAGE,kBAAC,OAAD;CACE,WAAW,EACT,iBACA,EACE,eAAe,CAAC,GACjB,EACD,EACD;CACD,aAAU;WAEV,kBAAC,GAAD;EAAwB;EAAW;EAAwB,CAAA;CACvD,CAAA,EAQG,KAAiE,EAC5E,cACA,kBAGE,kBAAC,OAAD;CAAK,aAAU;CAAW,WAAW,EAAG,oCAAoC,EAAU;CACnF;CACG,CAAA,EAiBG,KAAiE,EAC5E,UACA,cACA,aACA,aACA,GAAG,QACC;CACJ,IAAM,IAAM,GAAY,EAClB,IAAU,EAAO,GAAM,EAEvB,IADc,GAAc,KACD;AAajC,QAXA,QAAgB;AACd,MAAI,CAAC,EAAQ,SAAS;AACpB,KAAQ,UAAU;AAClB;;AAGF,EAAI,KACF,IAAW,EAAE,UAAO,CAAC;IAEtB;EAAC;EAAU;EAAO;EAAS,CAAC,EAG7B,kBAAC,OAAD;EACE,GAAI;EACJ,eAAa,CAAC;EACd,WAAW,EACT,oEACA;GACE,yCAAyC,CAAC;GAC1C,8CAA8C;GAC/C,EACD,EACD;EACD,OAAO,IAAW,KAAA,IAAY;EAC9B,UAAU,IAAW,IAAI;EACzB,IAAI,YAAY,EAAM,GAAG;EACzB,aAAW,YAAY;EACvB,MAAK;EACL,mBAAiB,OAAO,EAAM,GAAG;EAEhC;EACG,CAAA;GAYG,KAA+D,EAC1E,cACA,iBAAc,IACd,kBACI;CACJ,IAAM,IAAM,EAAuB,KAAK,EAClC,IAAY,EAAuB,KAAK,EACxC,CAAC,GAAY,KAAkB,GAAqB;AAE1D,SAAgB;AACd,MAAI,CAAC,GAAgB,WAAW,CAAC,EAAI,QACnC;EAGF,IAAM,IAAc,EAAU,QAAS,eAAe,GAChD,IAAc,EAAe,QAAQ,uBAAuB,EAC5D,IAAgB,EAAI,QAAS,uBAAuB,EACpD,IAAS,EAAI,QAAS;AAE5B,IAAI,QAAQ,aAAa,KAAU,EAAY,OAAO,EAAc,QAAQ;IAC3E,CAAC,EAAe,CAAC;CAEpB,IAAM,IAAQ,EAAE,gBAAgB,GAAY;AAE5C,QACE,kBAAC,OAAD;EACE,aAAU;EACV,WAAW,EACT,yFACA,EACD;EACI;YAEL,kBAAC,OAAD;GAAK,MAAK;GAAU,WAAU;GAAuB;aAArD;IACE,kBAAC,OAAD;KACE,KAAK;KACL,WAAW,EAAG,YAAY,EACxB,0FACE,CAAC,GACJ,CAAA;KACD,CAAA;IACD;IACD,kBAAC,OAAD,EACE,WAAW,EAAG,YAAY,EACxB,0FACE,CAAC,GACJ,CAAC,EACF,CAAA;;;EAEA,CAAA;GAUG,KAAgD,EAC3D,UACA,cACA,cAAc,IAAY,mBACtB;CACJ,IAAM,IAAY,GAAc,EAC1B,IAAW,GAAgB;AAQjC,QACE,kBAAC,GAAD;EACE,QAJgB,MAAc,KAAK,KAAA,IAAY,IAI3B,UAAU;EAC9B,WATkB,GAAe,MAAkB;AACrD,KAAS;IAAE,MAAM;IAAU,SAAS,OAAO,EAAA;IAAQ,CAAC;;EASlD,MAAM;EACN,WAAW,EAAG,EAAU;YAEvB,EAAM,KAAK,MACV,kBAAC,UAAD;GAAyB,OAAO,EAAK;aAClC,EAAK;GACC,EAFI,EAAK,MAET,CAAA;EAEJ,CAAA;GAyBA,KAAuD,EAClE,IAAI,IAAU,MACd,UACA,aACA,UACA,aACA,cACA,GAAG,QACC;CACJ,IAAM,IAAM,EAAuB,KAAK,EAClC,IAAY,GAAc,EAC1B,IAAM,GAAY,EAClB,IAAW,EAAsB;EAAE;EAAY;EAAkC,CAAC,EAClF,IAAoB,EAAO,EAAU,EAErC,IAAW,EAAO,MAAc,EAAM;AAC5C,GAAS,UAAU,MAAc;CAEjC,IAAM,IAAY,EAAE,eAAe,GAAO;AAE1C,SAAgB;AAKd,EAJI,EAAkB,YAAY,KAAa,MAAU,MACvD,EAAI,SAAS,OAAO,EACpB,IAAW;GAAE;GAAO;GAAO,CAAC,GAE1B,EAAkB,YAAY,MAChC,EAAkB,UAAU;IAE7B;EAAC;EAAO;EAAO;EAAU;EAAU,CAAC;CA8BvC,IAAM,IAAkB,EAAoB;EAC1C,OA7BY,QAAkB;AAC9B,KAAS,EAAE,MAAM,SAAS,CAAC;KAC1B,CAAC,EAAS,CAAC;EA4BZ,KA1BU,QAAkB;AAC5B,KAAS,EAAE,MAAM,OAAO,CAAC;KACxB,CAAC,EAAS,CAAC;EAyBZ,IAvBS,QAAkB;AAC3B,KAAS,EAAE,MAAM,YAAY,CAAC;KAC7B,CAAC,EAAS,CAAC;EAsBZ,MApBW,QAAkB;AAC7B,KAAS,EAAE,MAAM,QAAQ,CAAC;KACzB,CAAC,EAAS,CAAC;EAmBZ,MAjBW,QAAkB;AAG7B,KAAS,EAAE,MAFG,SAAS,gBAAgB,QAAQ,QAEtB,SAAS,YAAY,CAAC;KAC9C,CAAC,EAAS,CAAC;EAcZ,OAZY,QAAkB;AAG9B,KAAS,EAAE,MAFG,SAAS,gBAAgB,QAAQ,QAEtB,aAAa,QAAQ,CAAC;KAC9C,CAAC,EAAS,CAAA;EASZ,CAAC,EAEI,IAAgB,GACnB,MAAqC;EACpC,IAAM,IAAU,EAAE,QAAQ,OAAO,EAAE,QAAQ,cAAc,EAAE,SAAS,SAC9D,IAAU,EAAE,QAAQ,WAAW,EAAE,SAAS;AAEhD,MAAI,EAAS,YAAY,KAAW,IAAU;AAC5C,KAAE,gBAAgB;AAClB;;AAGF,IAAgB,EAAE;IAEpB,CAAC,EAAgB,CAClB,EAEK,UAAoB;AACpB,IAAS,WAIb,EAAS;GAAE,MAAM;GAAU,SAAS;GAAO,CAAC;;AAG9C,QACE,kBAAC,OAAD;EACE,GAAI;EACJ,IAAI,EAAK,MAAM,OAAO,EAAM,GAAG;EAC/B,aAAU;EACL;EACL,MAAK;EACL,iBAAe,EAAS;EACxB,iBAAe,YAAY,EAAM,GAAG;EACpC,UAAU,EAAS,UAAU,IAAI;EACjC,WAAW,EACT,2IACA;GACE,cAAc,EAAS;GACvB,cAAc,CAAC,EAAS;GACxB,4CAA4C,EAAS;GACtD,CACF;EACD,WAAW;EACX,SAAS;YAET,kBAAC,GAAD;GACE,WAAW,EACT,qCACA,gPACA,EACD;GACD,GAAM,IAAsC,EAAE,GAA7B,EAAE,MAAM,gBAAgB;aAN3C,CAQE,kBAAC,QAAD;IACE,WAAW,EACT,mDACA,mDACA,WACD;IACD,OAAO;IACP,CAAA,EACD,KAAY,EAAA;;EAEX,CAAA"}
|
|
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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"}
|