@clubmed/trident-ui 2.0.0-beta.75 → 2.0.0-beta.77
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/chapters-demo.d.ts +1 -0
- package/examples/chapters-demo.js +46 -0
- package/examples/chapters-demo.js.map +1 -0
- package/examples/promises-demo.d.ts +1 -0
- package/examples/promises-demo.js +50 -0
- package/examples/promises-demo.js.map +1 -0
- package/examples/sparkle-wrapper-demo.d.ts +1 -0
- package/examples/sparkle-wrapper-demo.js +34 -0
- package/examples/sparkle-wrapper-demo.js.map +1 -0
- package/package.json +1 -1
- package/styles/animate.css +19 -0
- package/styles/theme.css +1 -0
- package/ui/Chapters/Chapter.d.ts +7 -0
- package/ui/Chapters/Chapter.js +36 -0
- package/ui/Chapters/Chapter.js.map +1 -0
- package/ui/Chapters/ChapterTitle.d.ts +14 -0
- package/ui/Chapters/ChapterTitle.js +35 -0
- package/ui/Chapters/ChapterTitle.js.map +1 -0
- package/ui/Chapters/Chapters.d.ts +15 -0
- package/ui/Chapters/Chapters.js +42 -0
- package/ui/Chapters/Chapters.js.map +1 -0
- package/ui/Chapters/Chapters.themes.d.ts +9 -0
- package/ui/Chapters/Chapters.themes.js +65 -0
- package/ui/Chapters/Chapters.themes.js.map +1 -0
- package/ui/Chapters/ChaptersSummary.d.ts +17 -0
- package/ui/Chapters/ChaptersSummary.js +29 -0
- package/ui/Chapters/ChaptersSummary.js.map +1 -0
- package/ui/Promises/Promises.d.ts +26 -0
- package/ui/Promises/Promises.js +42 -0
- package/ui/Promises/Promises.js.map +1 -0
- package/ui/Promises/PromisesMedia.d.ts +12 -0
- package/ui/Promises/PromisesMedia.js +64 -0
- package/ui/Promises/PromisesMedia.js.map +1 -0
- package/ui/Promises/PromisesMenu.d.ts +11 -0
- package/ui/Promises/PromisesMenu.js +154 -0
- package/ui/Promises/PromisesMenu.js.map +1 -0
- package/ui/SidebarLayout.js +1 -1
- package/ui/SidebarLayout.js.map +1 -1
- package/ui/SparkleWrapper.d.ts +5 -0
- package/ui/SparkleWrapper.js +56 -0
- package/ui/SparkleWrapper.js.map +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function ChaptersDemo(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Chapters as e } from "../ui/Chapters/Chapters.js";
|
|
3
|
+
import { jsx as t } from "react/jsx-runtime";
|
|
4
|
+
//#region lib/examples/chapters-demo.tsx
|
|
5
|
+
function n() {
|
|
6
|
+
return /* @__PURE__ */ t(e, {
|
|
7
|
+
chapters: [
|
|
8
|
+
{
|
|
9
|
+
title: "All-inclusive experience",
|
|
10
|
+
content: /* @__PURE__ */ t("div", {
|
|
11
|
+
className: "min-h-screen p-40",
|
|
12
|
+
children: /* @__PURE__ */ t("p", {
|
|
13
|
+
className: "text-body max-w-prose",
|
|
14
|
+
children: "From sunrise yoga to late-night entertainment, every moment is included. Fine dining, premium beverages, world-class facilities — yours without a second thought."
|
|
15
|
+
})
|
|
16
|
+
})
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
title: "Sports & activities",
|
|
20
|
+
content: /* @__PURE__ */ t("div", {
|
|
21
|
+
className: "min-h-screen p-40",
|
|
22
|
+
children: /* @__PURE__ */ t("p", {
|
|
23
|
+
className: "text-body max-w-prose",
|
|
24
|
+
children: "Surf, dive, sail, climb — over 60 land and water sports await you. Our certified instructors guide guests of all levels, from first-timers to seasoned athletes."
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
title: "Kids clubs",
|
|
30
|
+
content: /* @__PURE__ */ t("div", {
|
|
31
|
+
className: "min-h-screen p-40",
|
|
32
|
+
children: /* @__PURE__ */ t("p", {
|
|
33
|
+
className: "text-body max-w-prose",
|
|
34
|
+
children: "Dedicated clubs for every age from 4 months to 17 years. Supervised activities, creative workshops, and adventure programmes tailored to each stage of childhood."
|
|
35
|
+
})
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
theme: "coreRange",
|
|
40
|
+
className: "w-full"
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
export { n as default };
|
|
45
|
+
|
|
46
|
+
//# sourceMappingURL=chapters-demo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chapters-demo.js","names":[],"sources":["../../lib/examples/chapters-demo.tsx"],"sourcesContent":["'use client';\n\nimport { Chapters } from '@/ui/Chapters/Chapters';\n\nexport default function ChaptersDemo() {\n return (\n <Chapters\n chapters={[\n {\n title: 'All-inclusive experience',\n content: (\n <div className=\"min-h-screen p-40\">\n <p className=\"text-body max-w-prose\">\n From sunrise yoga to late-night entertainment, every moment is included. Fine\n dining, premium beverages, world-class facilities — yours without a second thought.\n </p>\n </div>\n ),\n },\n {\n title: 'Sports & activities',\n content: (\n <div className=\"min-h-screen p-40\">\n <p className=\"text-body max-w-prose\">\n Surf, dive, sail, climb — over 60 land and water sports await you. Our certified\n instructors guide guests of all levels, from first-timers to seasoned athletes.\n </p>\n </div>\n ),\n },\n {\n title: 'Kids clubs',\n content: (\n <div className=\"min-h-screen p-40\">\n <p className=\"text-body max-w-prose\">\n Dedicated clubs for every age from 4 months to 17 years. Supervised activities,\n creative workshops, and adventure programmes tailored to each stage of childhood.\n </p>\n </div>\n ),\n },\n ]}\n theme=\"coreRange\"\n className=\"w-full\"\n />\n );\n}\n"],"mappings":";;;;AAIA,SAAwB,IAAe;AACrC,QACE,kBAAC,GAAD;EACE,UAAU;GACR;IACE,OAAO;IACP,SACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,KAAD;MAAG,WAAU;gBAAwB;MAGjC,CAAA;KACA,CAAA;IAET;GACD;IACE,OAAO;IACP,SACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,KAAD;MAAG,WAAU;gBAAwB;MAGjC,CAAA;KACA,CAAA;IAET;GACD;IACE,OAAO;IACP,SACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,KAAD;MAAG,WAAU;gBAAwB;MAGjC,CAAA;KACA,CAAA;;GAGX;EACD,OAAM;EACN,WAAU;EACV,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function PromisesDemo(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Promises as e } from "../ui/Promises/Promises.js";
|
|
3
|
+
import { jsx as t } from "react/jsx-runtime";
|
|
4
|
+
//#region lib/examples/promises-demo.tsx
|
|
5
|
+
function n() {
|
|
6
|
+
return /* @__PURE__ */ t(e, {
|
|
7
|
+
className: "min-h-500 w-full",
|
|
8
|
+
items: [
|
|
9
|
+
{
|
|
10
|
+
title: "The most beautiful destinations",
|
|
11
|
+
href: "#",
|
|
12
|
+
image: {
|
|
13
|
+
src: "https://images.unsplash.com/photo-1507525428034-b723cf961d3e?w=400",
|
|
14
|
+
alt: "Beautiful beach destination"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
title: "Kids club starting from 4 months",
|
|
19
|
+
href: "#",
|
|
20
|
+
image: {
|
|
21
|
+
src: "https://images.unsplash.com/photo-1529156069898-49953e39b3ac?w=400",
|
|
22
|
+
alt: "Kids playing at the club"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
title: "The best restaurants on the coast",
|
|
27
|
+
href: "#",
|
|
28
|
+
image: {
|
|
29
|
+
src: "https://images.unsplash.com/photo-1414235077428-338989a2e8c0?w=400",
|
|
30
|
+
alt: "Fine dining restaurant"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
labels: {
|
|
35
|
+
anchor: { continue: "Discover" },
|
|
36
|
+
layer: {
|
|
37
|
+
close: "Close menu",
|
|
38
|
+
open: "Open menu"
|
|
39
|
+
},
|
|
40
|
+
scroller: {
|
|
41
|
+
previous: "Previous",
|
|
42
|
+
next: "Next"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
export { n as default };
|
|
49
|
+
|
|
50
|
+
//# sourceMappingURL=promises-demo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promises-demo.js","names":[],"sources":["../../lib/examples/promises-demo.tsx"],"sourcesContent":["'use client';\n\nimport { Promises } from '@/ui/Promises/Promises';\n\nexport default function PromisesDemo() {\n return (\n <Promises\n className=\"min-h-500 w-full\"\n items={[\n {\n title: 'The most beautiful destinations',\n href: '#',\n image: {\n src: 'https://images.unsplash.com/photo-1507525428034-b723cf961d3e?w=400',\n alt: 'Beautiful beach destination',\n },\n },\n {\n title: 'Kids club starting from 4 months',\n href: '#',\n image: {\n src: 'https://images.unsplash.com/photo-1529156069898-49953e39b3ac?w=400',\n alt: 'Kids playing at the club',\n },\n },\n {\n title: 'The best restaurants on the coast',\n href: '#',\n image: {\n src: 'https://images.unsplash.com/photo-1414235077428-338989a2e8c0?w=400',\n alt: 'Fine dining restaurant',\n },\n },\n ]}\n labels={{\n anchor: { continue: 'Discover' },\n layer: { close: 'Close menu', open: 'Open menu' },\n scroller: { previous: 'Previous', next: 'Next' },\n }}\n />\n );\n}\n"],"mappings":";;;;AAIA,SAAwB,IAAe;AACrC,QACE,kBAAC,GAAD;EACE,WAAU;EACV,OAAO;GACL;IACE,OAAO;IACP,MAAM;IACN,OAAO;KACL,KAAK;KACL,KAAK;;IAER;GACD;IACE,OAAO;IACP,MAAM;IACN,OAAO;KACL,KAAK;KACL,KAAK;;IAER;GACD;IACE,OAAO;IACP,MAAM;IACN,OAAO;KACL,KAAK;KACL,KAAK;;;GAGV;EACD,QAAQ;GACN,QAAQ,EAAE,UAAU,YAAY;GAChC,OAAO;IAAE,OAAO;IAAc,MAAM;IAAa;GACjD,UAAU;IAAE,UAAU;IAAY,MAAM;;;EAE1C,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function SparkleWrapperDemo(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { SparkleWrapper as e } from "../ui/SparkleWrapper.js";
|
|
3
|
+
import { useState as t } from "react";
|
|
4
|
+
import { jsx as n, jsxs as r } from "react/jsx-runtime";
|
|
5
|
+
//#region lib/examples/sparkle-wrapper-demo.tsx
|
|
6
|
+
function i() {
|
|
7
|
+
let [i, a] = t(0);
|
|
8
|
+
return /* @__PURE__ */ r("div", {
|
|
9
|
+
className: "flex flex-col items-center gap-48 p-48",
|
|
10
|
+
children: [
|
|
11
|
+
/* @__PURE__ */ n(e, {
|
|
12
|
+
count: 3,
|
|
13
|
+
children: /* @__PURE__ */ n("div", {
|
|
14
|
+
className: "bg-saffron text-black font-bold px-20 py-12 rounded-pill text-b3",
|
|
15
|
+
children: "✨ New feature"
|
|
16
|
+
})
|
|
17
|
+
}, i),
|
|
18
|
+
/* @__PURE__ */ n(e, { children: /* @__PURE__ */ n("span", {
|
|
19
|
+
className: "text-h4 font-bold",
|
|
20
|
+
children: "🎉 Congratulations!"
|
|
21
|
+
}) }, `b-${i}`),
|
|
22
|
+
/* @__PURE__ */ n("button", {
|
|
23
|
+
type: "button",
|
|
24
|
+
onClick: () => a((e) => e + 1),
|
|
25
|
+
className: "px-20 py-10 rounded-pill border-2 border-black font-semibold text-b3 hover:bg-black hover:text-white transition-colors cursor-pointer",
|
|
26
|
+
children: "Replay animation"
|
|
27
|
+
})
|
|
28
|
+
]
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { i as default };
|
|
33
|
+
|
|
34
|
+
//# sourceMappingURL=sparkle-wrapper-demo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sparkle-wrapper-demo.js","names":[],"sources":["../../lib/examples/sparkle-wrapper-demo.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { SparkleWrapper } from '@/ui/SparkleWrapper';\n\nexport default function SparkleWrapperDemo() {\n const [key, setKey] = useState(0);\n\n return (\n <div className=\"flex flex-col items-center gap-48 p-48\">\n <SparkleWrapper count={3} key={key}>\n <div className=\"bg-saffron text-black font-bold px-20 py-12 rounded-pill text-b3\">\n ✨ New feature\n </div>\n </SparkleWrapper>\n\n <SparkleWrapper key={`b-${key}`}>\n <span className=\"text-h4 font-bold\">🎉 Congratulations!</span>\n </SparkleWrapper>\n\n <button\n type=\"button\"\n onClick={() => setKey((k) => k + 1)}\n className=\"px-20 py-10 rounded-pill border-2 border-black font-semibold text-b3 hover:bg-black hover:text-white transition-colors cursor-pointer\"\n >\n Replay animation\n </button>\n </div>\n );\n}\n"],"mappings":";;;;;AAKA,SAAwB,IAAqB;CAC3C,IAAM,CAAC,GAAK,KAAU,EAAS,EAAE;AAEjC,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,GAAD;IAAgB,OAAO;cACrB,kBAAC,OAAD;KAAK,WAAU;eAAmE;KAE5E,CAAA;IACS,EAJc,EAId;GAEjB,kBAAC,GAAD,EAAA,UACE,kBAAC,QAAD;IAAM,WAAU;cAAoB;IAA0B,CAAA,EAC/C,EAFI,KAAK,IAET;GAEjB,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,GAAQ,MAAM,IAAI,EAAE;IACnC,WAAU;cACX;IAEQ,CAAA"}
|
package/package.json
CHANGED
package/styles/animate.css
CHANGED
|
@@ -299,3 +299,22 @@
|
|
|
299
299
|
transform: translateX(0px);
|
|
300
300
|
}
|
|
301
301
|
}
|
|
302
|
+
|
|
303
|
+
@keyframes sparkle-fly {
|
|
304
|
+
0% {
|
|
305
|
+
opacity: 1;
|
|
306
|
+
transform: translate(
|
|
307
|
+
calc(-50% + var(--sparkle-start-x, 0px)),
|
|
308
|
+
calc(-50% + var(--sparkle-start-y, 0px))
|
|
309
|
+
)
|
|
310
|
+
scale(1);
|
|
311
|
+
}
|
|
312
|
+
100% {
|
|
313
|
+
opacity: 0;
|
|
314
|
+
transform: translate(
|
|
315
|
+
calc(-50% + var(--sparkle-end-x, 0px)),
|
|
316
|
+
calc(-50% + var(--sparkle-end-y, 0px))
|
|
317
|
+
)
|
|
318
|
+
scale(0);
|
|
319
|
+
}
|
|
320
|
+
}
|
package/styles/theme.css
CHANGED
|
@@ -174,4 +174,5 @@
|
|
|
174
174
|
--animate-zoom-in: zoom-in 220ms cubic-bezier(0.2, 0.8, 0.2, 1) forwards; /* NOT IN DCX */
|
|
175
175
|
--animate-zoom-out: zoom-out 180ms cubic-bezier(0.4, 0, 1, 1) forwards; /* NOT IN DCX */
|
|
176
176
|
--animate-wiggle: wiggle 0.75s ease-in-out;
|
|
177
|
+
--animate-sparkle-fly: sparkle-fly 900ms ease-out both;
|
|
177
178
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect as e, useRef as t } from "react";
|
|
3
|
+
import { jsx as n } from "react/jsx-runtime";
|
|
4
|
+
var r = (() => {
|
|
5
|
+
let e = [];
|
|
6
|
+
for (let t = 0; t <= 1; t += .03) e.push(parseFloat(t.toFixed(2)));
|
|
7
|
+
return e;
|
|
8
|
+
})(), i = (e) => {
|
|
9
|
+
let t = e.boundingClientRect.height <= window.innerHeight / 2 && e.intersectionRect.height > window.innerHeight / 3, n = e.boundingClientRect.height <= window.innerHeight / 2 && e.boundingClientRect.height === e.intersectionRect.height, r = e.boundingClientRect.height > window.innerHeight / 2 && e.intersectionRect.height > window.innerHeight / 2;
|
|
10
|
+
return t || n || r;
|
|
11
|
+
}, a = ({ children: a, index: o, setActiveChapter: s }) => {
|
|
12
|
+
let c = t(null);
|
|
13
|
+
return e(() => {
|
|
14
|
+
let e = c.current;
|
|
15
|
+
if (e) {
|
|
16
|
+
let t = new IntersectionObserver(([e]) => {
|
|
17
|
+
i(e) && s(o);
|
|
18
|
+
}, {
|
|
19
|
+
threshold: r,
|
|
20
|
+
rootMargin: "0px 0px 0px 0px"
|
|
21
|
+
});
|
|
22
|
+
return t.observe(e), () => t.unobserve(e);
|
|
23
|
+
}
|
|
24
|
+
}, [
|
|
25
|
+
c,
|
|
26
|
+
o,
|
|
27
|
+
s
|
|
28
|
+
]), /* @__PURE__ */ n("section", {
|
|
29
|
+
ref: c,
|
|
30
|
+
children: a
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
//#endregion
|
|
34
|
+
export { a as Chapter };
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=Chapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Chapter.js","names":[],"sources":["../../../lib/ui/Chapters/Chapter.tsx"],"sourcesContent":["'use client';\nimport { useEffect, useRef, type FunctionComponent, type PropsWithChildren } from 'react';\n\ninterface Props {\n index: number;\n setActiveChapter: (index: number) => void;\n}\n\nconst generateThreshold = () => {\n const threshold: number[] = [];\n\n for (let i = 0; i <= 1; i += 0.03) {\n threshold.push(parseFloat(i.toFixed(2)));\n }\n\n return threshold;\n};\n\nconst THRESHOLD = generateThreshold();\n\nconst isChapterActive = (entry: IntersectionObserverEntry) => {\n const isHalfHeightAndOneThirdVisible =\n entry.boundingClientRect.height <= window.innerHeight / 2 &&\n entry.intersectionRect.height > window.innerHeight / 3;\n\n const isHalfHeightAndFullyVisible =\n entry.boundingClientRect.height <= window.innerHeight / 2 &&\n entry.boundingClientRect.height === entry.intersectionRect.height;\n\n const isMoreThanHalfHeightAndHalfVisible =\n entry.boundingClientRect.height > window.innerHeight / 2 &&\n entry.intersectionRect.height > window.innerHeight / 2;\n\n return (\n isHalfHeightAndOneThirdVisible ||\n isHalfHeightAndFullyVisible ||\n isMoreThanHalfHeightAndHalfVisible\n );\n};\n\nexport const Chapter: FunctionComponent<PropsWithChildren<Props>> = ({\n children,\n index,\n setActiveChapter,\n}) => {\n const element = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const current = element.current;\n\n if (current) {\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (isChapterActive(entry)) {\n setActiveChapter(index);\n }\n },\n {\n threshold: THRESHOLD,\n rootMargin: '0px 0px 0px 0px',\n },\n );\n\n observer.observe(current);\n\n return () => observer.unobserve(current);\n }\n }, [element, index, setActiveChapter]);\n\n return <section ref={element}>{children}</section>;\n};\n"],"mappings":";;;AAkBA,IAAM,WAV0B;CAC9B,IAAM,IAAsB,EAAE;AAE9B,MAAK,IAAI,IAAI,GAAG,KAAK,GAAG,KAAK,IAC3B,GAAU,KAAK,WAAW,EAAE,QAAQ,EAAE,CAAC,CAAC;AAG1C,QAAO;IAG4B,EAE/B,KAAmB,MAAqC;CAC5D,IAAM,IACJ,EAAM,mBAAmB,UAAU,OAAO,cAAc,KACxD,EAAM,iBAAiB,SAAS,OAAO,cAAc,GAEjD,IACJ,EAAM,mBAAmB,UAAU,OAAO,cAAc,KACxD,EAAM,mBAAmB,WAAW,EAAM,iBAAiB,QAEvD,IACJ,EAAM,mBAAmB,SAAS,OAAO,cAAc,KACvD,EAAM,iBAAiB,SAAS,OAAO,cAAc;AAEvD,QACE,KACA,KACA;GAIS,KAAwD,EACnE,aACA,UACA,0BACI;CACJ,IAAM,IAAU,EAAuB,KAAK;AAwB5C,QAtBA,QAAgB;EACd,IAAM,IAAU,EAAQ;AAExB,MAAI,GAAS;GACX,IAAM,IAAW,IAAI,sBAClB,CAAC,OAAW;AACX,IAAI,EAAgB,EAAM,IACxB,EAAiB,EAAM;MAG3B;IACE,WAAW;IACX,YAAY;IACb,CACF;AAID,UAFA,EAAS,QAAQ,EAAQ,QAEZ,EAAS,UAAU,EAAQ;;IAEzC;EAAC;EAAS;EAAO;EAAiB,CAAC,EAE/B,kBAAC,WAAD;EAAS,KAAK;EAAU;EAAmB,CAAA"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ComponentProps, FunctionComponent, PropsWithChildren } from 'react';
|
|
2
|
+
import { Heading } from '../heading/Heading';
|
|
3
|
+
import { Theme } from '../types/Theme';
|
|
4
|
+
interface Props {
|
|
5
|
+
className?: string;
|
|
6
|
+
hasNext?: boolean;
|
|
7
|
+
hLevel?: ComponentProps<typeof Heading>['level'];
|
|
8
|
+
index: number;
|
|
9
|
+
isActive?: boolean;
|
|
10
|
+
onClick?: () => void;
|
|
11
|
+
theme?: Theme;
|
|
12
|
+
}
|
|
13
|
+
export declare const ChapterTitle: FunctionComponent<PropsWithChildren<Props>>;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { t as e } from "../../chunks/clsx.js";
|
|
3
|
+
import { Heading as t } from "../heading/Heading.js";
|
|
4
|
+
import { getTheme as n } from "./Chapters.themes.js";
|
|
5
|
+
import { jsx as r, jsxs as i } from "react/jsx-runtime";
|
|
6
|
+
import { Icon as a } from "@clubmed/trident-icons";
|
|
7
|
+
//#region lib/ui/Chapters/ChapterTitle.tsx
|
|
8
|
+
var o = ({ children: o, className: s, hasNext: c = !1, hLevel: l = 2, index: u, isActive: d = !1, onClick: f, theme: p = "coreRange" }) => {
|
|
9
|
+
let { thTitle: m, thTitleUnActive: h } = n(p);
|
|
10
|
+
return /* @__PURE__ */ i(t, {
|
|
11
|
+
className: e("text-h5 @md:text-h2 sticky top-0 z-1 flex justify-between gap-x-8 p-20 font-serif transition-colors duration-300 @md:static @md:bg-transparent @md:p-0", m, s),
|
|
12
|
+
id: `chapter${u}`,
|
|
13
|
+
level: l,
|
|
14
|
+
children: [/* @__PURE__ */ i("a", {
|
|
15
|
+
className: e("flex-1", { [h]: !d }),
|
|
16
|
+
href: `#chapter${u}`,
|
|
17
|
+
onClick: f,
|
|
18
|
+
children: [/* @__PURE__ */ i("span", {
|
|
19
|
+
className: "@md:hidden",
|
|
20
|
+
children: [u + 1, " - "]
|
|
21
|
+
}), o]
|
|
22
|
+
}), c && /* @__PURE__ */ r("a", {
|
|
23
|
+
className: "@md:hidden",
|
|
24
|
+
href: `#chapter${u + 1}`,
|
|
25
|
+
children: /* @__PURE__ */ r(a, {
|
|
26
|
+
name: "ArrowTailDown",
|
|
27
|
+
width: "24px"
|
|
28
|
+
})
|
|
29
|
+
})]
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
//#endregion
|
|
33
|
+
export { o as ChapterTitle };
|
|
34
|
+
|
|
35
|
+
//# sourceMappingURL=ChapterTitle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChapterTitle.js","names":[],"sources":["../../../lib/ui/Chapters/ChapterTitle.tsx"],"sourcesContent":["'use client';\nimport clsx from 'clsx';\nimport type { ComponentProps, FunctionComponent, PropsWithChildren } from 'react';\n\nimport { Icon } from '@clubmed/trident-icons';\nimport { Heading } from '../heading/Heading';\nimport type { Theme } from '../types/Theme';\n\nimport { getTheme } from './Chapters.themes';\n\ninterface Props {\n className?: string;\n hasNext?: boolean;\n hLevel?: ComponentProps<typeof Heading>['level'];\n index: number;\n isActive?: boolean;\n onClick?: () => void;\n theme?: Theme;\n}\n\nexport const ChapterTitle: FunctionComponent<PropsWithChildren<Props>> = ({\n children,\n className,\n hasNext = false,\n hLevel = 2,\n index,\n isActive = false,\n onClick,\n theme = 'coreRange',\n}) => {\n const { thTitle, thTitleUnActive } = getTheme(theme);\n\n return (\n <Heading\n className={clsx(\n 'text-h5 @md:text-h2 sticky top-0 z-1 flex justify-between gap-x-8 p-20 font-serif transition-colors duration-300 @md:static @md:bg-transparent @md:p-0',\n thTitle,\n className,\n )}\n id={`chapter${index}`}\n level={hLevel}\n >\n <a\n className={clsx('flex-1', {\n [thTitleUnActive]: !isActive,\n })}\n href={`#chapter${index}`}\n onClick={onClick}\n >\n <span className=\"@md:hidden\">{index + 1} - </span>\n {children}\n </a>\n {hasNext && (\n <a className=\"@md:hidden\" href={`#chapter${index + 1}`}>\n <Icon name=\"ArrowTailDown\" width=\"24px\" />\n </a>\n )}\n </Heading>\n );\n};\n"],"mappings":";;;;;;;AAoBA,IAAa,KAA6D,EACxE,aACA,cACA,aAAU,IACV,YAAS,GACT,UACA,cAAW,IACX,YACA,WAAQ,kBACJ;CACJ,IAAM,EAAE,YAAS,uBAAoB,EAAS,EAAM;AAEpD,QACE,kBAAC,GAAD;EACE,WAAW,EACT,0JACA,GACA,EACD;EACD,IAAI,UAAU;EACd,OAAO;YAPT,CASE,kBAAC,KAAD;GACE,WAAW,EAAK,UAAU,GACvB,IAAkB,CAAC,GACrB,CAAC;GACF,MAAM,WAAW;GACR;aALX,CAOE,kBAAC,QAAD;IAAM,WAAU;cAAhB,CAA8B,IAAQ,GAAE,MAAA;OACvC,EAAA;MAEF,KACC,kBAAC,KAAD;GAAG,WAAU;GAAa,MAAM,WAAW,IAAQ;aACjD,kBAAC,GAAD;IAAM,MAAK;IAAgB,OAAM;IAAS,CAAA;GACxC,CAAA,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ComponentPropsWithoutRef, FunctionComponent, ReactNode } from 'react';
|
|
2
|
+
import { IconicNames } from '@clubmed/trident-icons';
|
|
3
|
+
import { Theme } from '../types/Theme';
|
|
4
|
+
export interface ChapterItem {
|
|
5
|
+
content: ReactNode;
|
|
6
|
+
title: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ChaptersProps extends Omit<ComponentPropsWithoutRef<'section'>, 'onClick' | 'children'> {
|
|
9
|
+
chapters: ChapterItem[];
|
|
10
|
+
hLevel?: 2 | 3 | 4;
|
|
11
|
+
icon?: IconicNames;
|
|
12
|
+
onClick?: (chapter: ChapterItem) => void;
|
|
13
|
+
theme?: Theme;
|
|
14
|
+
}
|
|
15
|
+
export declare const Chapters: FunctionComponent<ChaptersProps>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { t as e } from "../../chunks/clsx.js";
|
|
3
|
+
import { Chapter as t } from "./Chapter.js";
|
|
4
|
+
import { ChapterTitle as n } from "./ChapterTitle.js";
|
|
5
|
+
import { ChaptersSummary as r } from "./ChaptersSummary.js";
|
|
6
|
+
import { useState as i } from "react";
|
|
7
|
+
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
8
|
+
//#region lib/ui/Chapters/Chapters.tsx
|
|
9
|
+
var s = ({ chapters: s, className: c, hLevel: l = 2, icon: u, onClick: d, theme: f = "coreRange", ...p }) => {
|
|
10
|
+
let [m, h] = i(0);
|
|
11
|
+
return /* @__PURE__ */ o("section", {
|
|
12
|
+
...p,
|
|
13
|
+
className: e(c, "flex @container/chapters"),
|
|
14
|
+
children: [/* @__PURE__ */ a(r, {
|
|
15
|
+
activeChapter: m,
|
|
16
|
+
chapters: s,
|
|
17
|
+
className: "hidden w-3/10 @md:block @xl:w-[calc(50%-380px)]",
|
|
18
|
+
icon: u,
|
|
19
|
+
onClick: d,
|
|
20
|
+
theme: f
|
|
21
|
+
}), /* @__PURE__ */ a("div", {
|
|
22
|
+
className: e("relative isolate w-full flex-1 @md:max-w-7/10 @xl:max-w-[calc(50%+380px)]", "@md:ps-20 @lg:ps-40 @xl:ps-120"),
|
|
23
|
+
children: s.map((e, r) => /* @__PURE__ */ o(t, {
|
|
24
|
+
index: r,
|
|
25
|
+
setActiveChapter: h,
|
|
26
|
+
children: [/* @__PURE__ */ a(n, {
|
|
27
|
+
hasNext: r < s.length - 1,
|
|
28
|
+
hLevel: l,
|
|
29
|
+
index: r,
|
|
30
|
+
isActive: r === m,
|
|
31
|
+
onClick: () => d?.(e),
|
|
32
|
+
theme: f,
|
|
33
|
+
children: e.title
|
|
34
|
+
}), e.content]
|
|
35
|
+
}, e.title))
|
|
36
|
+
})]
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
//#endregion
|
|
40
|
+
export { s as Chapters };
|
|
41
|
+
|
|
42
|
+
//# sourceMappingURL=Chapters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Chapters.js","names":[],"sources":["../../../lib/ui/Chapters/Chapters.tsx"],"sourcesContent":["'use client';\nimport clsx from 'clsx';\nimport {\n useState,\n type ComponentPropsWithoutRef,\n type FunctionComponent,\n type ReactNode,\n} from 'react';\n\nimport type { IconicNames } from '@clubmed/trident-icons';\nimport type { Theme } from '../types/Theme';\n\nimport { Chapter } from './Chapter';\nimport { ChapterTitle } from './ChapterTitle';\nimport { ChaptersSummary } from './ChaptersSummary';\n\nexport interface ChapterItem {\n content: ReactNode;\n title: string;\n}\n\nexport interface ChaptersProps\n extends Omit<ComponentPropsWithoutRef<'section'>, 'onClick' | 'children'> {\n chapters: ChapterItem[];\n hLevel?: 2 | 3 | 4;\n icon?: IconicNames;\n onClick?: (chapter: ChapterItem) => void;\n theme?: Theme;\n}\n\nexport const Chapters: FunctionComponent<ChaptersProps> = ({\n chapters,\n className,\n hLevel = 2,\n icon,\n onClick,\n theme = 'coreRange',\n ...attrs\n}) => {\n const [activeChapter, setActiveChapter] = useState(0);\n\n return (\n <section {...attrs} className={clsx(className, 'flex @container/chapters')}>\n <ChaptersSummary\n activeChapter={activeChapter}\n chapters={chapters}\n className=\"hidden w-3/10 @md:block @xl:w-[calc(50%-380px)]\"\n icon={icon}\n onClick={onClick}\n theme={theme}\n />\n\n <div\n className={clsx(\n 'relative isolate w-full flex-1 @md:max-w-7/10 @xl:max-w-[calc(50%+380px)]',\n '@md:ps-20 @lg:ps-40 @xl:ps-120',\n )}\n >\n {chapters.map((chapter, index) => (\n <Chapter index={index} key={chapter.title} setActiveChapter={setActiveChapter}>\n <ChapterTitle\n hasNext={index < chapters.length - 1}\n hLevel={hLevel}\n index={index}\n isActive={index === activeChapter}\n onClick={() => onClick?.(chapter)}\n theme={theme}\n >\n {chapter.title}\n </ChapterTitle>\n {chapter.content}\n </Chapter>\n ))}\n </div>\n </section>\n );\n};\n"],"mappings":";;;;;;;;AA8BA,IAAa,KAA8C,EACzD,aACA,cACA,YAAS,GACT,SACA,YACA,WAAQ,aACR,GAAG,QACC;CACJ,IAAM,CAAC,GAAe,KAAoB,EAAS,EAAE;AAErD,QACE,kBAAC,WAAD;EAAS,GAAI;EAAO,WAAW,EAAK,GAAW,2BAA2B;YAA1E,CACE,kBAAC,GAAD;GACiB;GACL;GACV,WAAU;GACJ;GACG;GACF;GACP,CAAA,EAEF,kBAAC,OAAD;GACE,WAAW,EACT,6EACA,iCACD;aAEA,EAAS,KAAK,GAAS,MACtB,kBAAC,GAAD;IAAgB;IAA6C;cAA7D,CACE,kBAAC,GAAD;KACE,SAAS,IAAQ,EAAS,SAAS;KAC3B;KACD;KACP,UAAU,MAAU;KACpB,eAAe,IAAU,EAAQ;KAC1B;eAEN,EAAQ;KACI,CAAA,EACd,EAAQ,QAAA;MAXiB,EAAQ,MAY1B,CAAA;GAER,CAAA,CAAA"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
//#region lib/ui/Chapters/Chapters.themes.ts
|
|
2
|
+
var e = {
|
|
3
|
+
BLACK: {
|
|
4
|
+
thSummary: "bg-black text-white",
|
|
5
|
+
thSummaryIcon: "text-white max-w-140",
|
|
6
|
+
thSummaryIconName: "Trident",
|
|
7
|
+
thSummaryIconWidth: "100%",
|
|
8
|
+
thTitle: "bg-black text-white",
|
|
9
|
+
thTitleUnActive: "max-md:text-white/50"
|
|
10
|
+
},
|
|
11
|
+
CORE_RANGE: {
|
|
12
|
+
thSummary: "bg-lightSand text-black",
|
|
13
|
+
thSummaryIcon: "text-sand max-w-140",
|
|
14
|
+
thSummaryIconName: "Trident",
|
|
15
|
+
thSummaryIconWidth: "100%",
|
|
16
|
+
thTitle: "bg-lightSand text-black",
|
|
17
|
+
thTitleUnActive: "max-md:text-black/50"
|
|
18
|
+
},
|
|
19
|
+
EXCLUSIVE_COLLECTION: {
|
|
20
|
+
thSummary: "bg-ultramarine text-white",
|
|
21
|
+
thSummaryIcon: "text-white max-w-220",
|
|
22
|
+
thSummaryIconName: "ExclusiveCollection",
|
|
23
|
+
thSummaryIconWidth: "100%",
|
|
24
|
+
thTitle: "bg-ultramarine text-white md:text-black",
|
|
25
|
+
thTitleUnActive: "max-md:text-white/50"
|
|
26
|
+
},
|
|
27
|
+
HAPPY_TO_CARE: {
|
|
28
|
+
thSummary: "bg-verdigris text-black",
|
|
29
|
+
thSummaryIcon: "text-black max-w-220",
|
|
30
|
+
thSummaryIconName: "HappyToCare",
|
|
31
|
+
thSummaryIconWidth: "100%",
|
|
32
|
+
thTitle: "bg-verdigris text-black",
|
|
33
|
+
thTitleUnActive: "max-md:text-black/50"
|
|
34
|
+
},
|
|
35
|
+
SIENNA: {
|
|
36
|
+
thSummary: "bg-sienna text-white",
|
|
37
|
+
thSummaryIcon: "text-white max-w-140",
|
|
38
|
+
thSummaryIconName: "Trident",
|
|
39
|
+
thSummaryIconWidth: "100%",
|
|
40
|
+
thTitle: "bg-sienna text-white md:text-black",
|
|
41
|
+
thTitleUnActive: "max-md:text-white/50"
|
|
42
|
+
},
|
|
43
|
+
WHITE: {
|
|
44
|
+
thSummary: "bg-white text-black",
|
|
45
|
+
thSummaryIcon: "text-sand max-w-140",
|
|
46
|
+
thSummaryIconName: "Trident",
|
|
47
|
+
thSummaryIconWidth: "100%",
|
|
48
|
+
thTitle: "bg-white text-black",
|
|
49
|
+
thTitleUnActive: "max-md:text-black/50"
|
|
50
|
+
}
|
|
51
|
+
}, t = (t) => {
|
|
52
|
+
switch (t) {
|
|
53
|
+
case "black": return e.BLACK;
|
|
54
|
+
case "dark":
|
|
55
|
+
case "exclusiveCollection": return e.EXCLUSIVE_COLLECTION;
|
|
56
|
+
case "happyToCare": return e.HAPPY_TO_CARE;
|
|
57
|
+
case "sienna": return e.SIENNA;
|
|
58
|
+
case "white": return e.WHITE;
|
|
59
|
+
default: return e.CORE_RANGE;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
//#endregion
|
|
63
|
+
export { t as getTheme };
|
|
64
|
+
|
|
65
|
+
//# sourceMappingURL=Chapters.themes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Chapters.themes.js","names":[],"sources":["../../../lib/ui/Chapters/Chapters.themes.ts"],"sourcesContent":["import type { Theme } from '../types/Theme';\n\nconst THEMES = {\n BLACK: {\n thSummary: 'bg-black text-white',\n thSummaryIcon: 'text-white max-w-140',\n thSummaryIconName: 'Trident',\n thSummaryIconWidth: '100%',\n thTitle: 'bg-black text-white',\n thTitleUnActive: 'max-md:text-white/50',\n },\n CORE_RANGE: {\n thSummary: 'bg-lightSand text-black',\n thSummaryIcon: 'text-sand max-w-140',\n thSummaryIconName: 'Trident',\n thSummaryIconWidth: '100%',\n thTitle: 'bg-lightSand text-black',\n thTitleUnActive: 'max-md:text-black/50',\n },\n EXCLUSIVE_COLLECTION: {\n thSummary: 'bg-ultramarine text-white',\n thSummaryIcon: 'text-white max-w-220',\n thSummaryIconName: 'ExclusiveCollection',\n thSummaryIconWidth: '100%',\n thTitle: 'bg-ultramarine text-white md:text-black',\n thTitleUnActive: 'max-md:text-white/50',\n },\n HAPPY_TO_CARE: {\n thSummary: 'bg-verdigris text-black',\n thSummaryIcon: 'text-black max-w-220',\n thSummaryIconName: 'HappyToCare',\n thSummaryIconWidth: '100%',\n thTitle: 'bg-verdigris text-black',\n thTitleUnActive: 'max-md:text-black/50',\n },\n SIENNA: {\n thSummary: 'bg-sienna text-white',\n thSummaryIcon: 'text-white max-w-140',\n thSummaryIconName: 'Trident',\n thSummaryIconWidth: '100%',\n thTitle: 'bg-sienna text-white md:text-black',\n thTitleUnActive: 'max-md:text-white/50',\n },\n WHITE: {\n thSummary: 'bg-white text-black',\n thSummaryIcon: 'text-sand max-w-140',\n thSummaryIconName: 'Trident',\n thSummaryIconWidth: '100%',\n thTitle: 'bg-white text-black',\n thTitleUnActive: 'max-md:text-black/50',\n },\n};\n\nexport const getTheme = (theme: Theme) => {\n switch (theme) {\n case 'black':\n return THEMES.BLACK;\n case 'dark':\n case 'exclusiveCollection':\n return THEMES.EXCLUSIVE_COLLECTION;\n case 'happyToCare':\n return THEMES.HAPPY_TO_CARE;\n case 'sienna':\n return THEMES.SIENNA;\n case 'white':\n return THEMES.WHITE;\n case 'light':\n case 'coreRange':\n default:\n return THEMES.CORE_RANGE;\n }\n};\n"],"mappings":";AAEA,IAAM,IAAS;CACb,OAAO;EACL,WAAW;EACX,eAAe;EACf,mBAAmB;EACnB,oBAAoB;EACpB,SAAS;EACT,iBAAiB;EAClB;CACD,YAAY;EACV,WAAW;EACX,eAAe;EACf,mBAAmB;EACnB,oBAAoB;EACpB,SAAS;EACT,iBAAiB;EAClB;CACD,sBAAsB;EACpB,WAAW;EACX,eAAe;EACf,mBAAmB;EACnB,oBAAoB;EACpB,SAAS;EACT,iBAAiB;EAClB;CACD,eAAe;EACb,WAAW;EACX,eAAe;EACf,mBAAmB;EACnB,oBAAoB;EACpB,SAAS;EACT,iBAAiB;EAClB;CACD,QAAQ;EACN,WAAW;EACX,eAAe;EACf,mBAAmB;EACnB,oBAAoB;EACpB,SAAS;EACT,iBAAiB;EAClB;CACD,OAAO;EACL,WAAW;EACX,eAAe;EACf,mBAAmB;EACnB,oBAAoB;EACpB,SAAS;EACT,iBAAiB;EAClB;CACF,EAEY,KAAY,MAAiB;AACxC,SAAQ,GAAR;EACE,KAAK,QACH,QAAO,EAAO;EAChB,KAAK;EACL,KAAK,sBACH,QAAO,EAAO;EAChB,KAAK,cACH,QAAO,EAAO;EAChB,KAAK,SACH,QAAO,EAAO;EAChB,KAAK,QACH,QAAO,EAAO;EAGhB,QACE,QAAO,EAAO"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { FunctionComponent, ReactNode } from 'react';
|
|
2
|
+
import { IconicNames } from '@clubmed/trident-icons';
|
|
3
|
+
import { Theme } from '../types/Theme';
|
|
4
|
+
interface ChapterSummaryItem {
|
|
5
|
+
content: ReactNode;
|
|
6
|
+
title: string;
|
|
7
|
+
}
|
|
8
|
+
interface Props {
|
|
9
|
+
activeChapter: number;
|
|
10
|
+
chapters: ChapterSummaryItem[];
|
|
11
|
+
className?: string;
|
|
12
|
+
icon?: IconicNames;
|
|
13
|
+
onClick?: (chapter: ChapterSummaryItem) => void;
|
|
14
|
+
theme?: Theme;
|
|
15
|
+
}
|
|
16
|
+
export declare const ChaptersSummary: FunctionComponent<Props>;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { t as e } from "../../chunks/clsx.js";
|
|
3
|
+
import { getTheme as t } from "./Chapters.themes.js";
|
|
4
|
+
import { jsx as n, jsxs as r } from "react/jsx-runtime";
|
|
5
|
+
import { Icon as i } from "@clubmed/trident-icons";
|
|
6
|
+
//#region lib/ui/Chapters/ChaptersSummary.tsx
|
|
7
|
+
var a = { HappyToCareFRCA: "translate-y-[28px]" }, o = ({ activeChapter: o, chapters: s, className: c, icon: l, onClick: u, theme: d = "coreRange" }) => {
|
|
8
|
+
let { thSummary: f, thSummaryIcon: p, thSummaryIconName: m, thSummaryIconWidth: h } = t(d), g = l ?? m, _ = a[g];
|
|
9
|
+
return /* @__PURE__ */ n("div", {
|
|
10
|
+
className: e("rounded-e-16", f, c),
|
|
11
|
+
children: /* @__PURE__ */ r("nav", {
|
|
12
|
+
className: "sticky top-0 ms-auto flex w-full max-w-380 flex-col gap-y-20 p-40",
|
|
13
|
+
children: [s.map((t, r) => /* @__PURE__ */ n("a", {
|
|
14
|
+
className: e("text-h5 block font-serif", { "opacity-50": r !== o }),
|
|
15
|
+
href: `#chapter${r}`,
|
|
16
|
+
onClick: () => u?.(t),
|
|
17
|
+
children: t.title
|
|
18
|
+
}, t.title)), /* @__PURE__ */ n(i, {
|
|
19
|
+
className: e("mt-90 self-center", p, _),
|
|
20
|
+
name: g,
|
|
21
|
+
width: h
|
|
22
|
+
})]
|
|
23
|
+
})
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
//#endregion
|
|
27
|
+
export { o as ChaptersSummary };
|
|
28
|
+
|
|
29
|
+
//# sourceMappingURL=ChaptersSummary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChaptersSummary.js","names":[],"sources":["../../../lib/ui/Chapters/ChaptersSummary.tsx"],"sourcesContent":["'use client';\nimport clsx from 'clsx';\nimport type { FunctionComponent, ReactNode } from 'react';\n\nimport { Icon, type IconicNames } from '@clubmed/trident-icons';\nimport type { Theme } from '../types/Theme';\n\nimport { getTheme } from './Chapters.themes';\n\nconst ICON_VERTICAL_OFFSET_CLASS: Partial<Record<IconicNames, string>> = {\n // FR-CA asset has an intrinsic Y offset; compensate to match EN-CA visual alignment.\n HappyToCareFRCA: 'translate-y-[28px]',\n};\n\ninterface ChapterSummaryItem {\n content: ReactNode;\n title: string;\n}\n\ninterface Props {\n activeChapter: number;\n chapters: ChapterSummaryItem[];\n className?: string;\n icon?: IconicNames;\n onClick?: (chapter: ChapterSummaryItem) => void;\n theme?: Theme;\n}\n\nexport const ChaptersSummary: FunctionComponent<Props> = ({\n activeChapter,\n chapters,\n className,\n icon,\n onClick,\n theme = 'coreRange',\n}) => {\n const { thSummary, thSummaryIcon, thSummaryIconName, thSummaryIconWidth } = getTheme(theme);\n const summaryIconName = (icon ?? thSummaryIconName) as IconicNames;\n const iconVerticalOffsetClass = ICON_VERTICAL_OFFSET_CLASS[summaryIconName];\n\n return (\n <div className={clsx('rounded-e-16', thSummary, className)}>\n <nav className=\"sticky top-0 ms-auto flex w-full max-w-380 flex-col gap-y-20 p-40\">\n {chapters.map((chapter, index) => (\n <a\n className={clsx('text-h5 block font-serif', {\n 'opacity-50': index !== activeChapter,\n })}\n href={`#chapter${index}`}\n key={chapter.title}\n onClick={() => onClick?.(chapter)}\n >\n {chapter.title}\n </a>\n ))}\n <Icon\n className={clsx('mt-90 self-center', thSummaryIcon, iconVerticalOffsetClass)}\n name={summaryIconName}\n width={thSummaryIconWidth}\n />\n </nav>\n </div>\n );\n};\n"],"mappings":";;;;;;AASA,IAAM,IAAmE,EAEvE,iBAAiB,sBAClB,EAgBY,KAA6C,EACxD,kBACA,aACA,cACA,SACA,YACA,WAAQ,kBACJ;CACJ,IAAM,EAAE,cAAW,kBAAe,sBAAmB,0BAAuB,EAAS,EAAM,EACrF,IAAmB,KAAQ,GAC3B,IAA0B,EAA2B;AAE3D,QACE,kBAAC,OAAD;EAAK,WAAW,EAAK,gBAAgB,GAAW,EAAU;YACxD,kBAAC,OAAD;GAAK,WAAU;aAAf,CACG,EAAS,KAAK,GAAS,MACtB,kBAAC,KAAD;IACE,WAAW,EAAK,4BAA4B,EAC1C,cAAc,MAAU,GACzB,CAAC;IACF,MAAM,WAAW;IAEjB,eAAe,IAAU,EAAQ;cAEhC,EAAQ;IACP,EAJG,EAAQ,MAIX,CACJ,EACF,kBAAC,GAAD;IACE,WAAW,EAAK,qBAAqB,GAAe,EAAwB;IAC5E,MAAM;IACN,OAAO;IACP,CAAA,CAAA;;EAEA,CAAA"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ComponentPropsWithoutRef, FunctionComponent } from 'react';
|
|
2
|
+
import { ImageProps } from '../Image';
|
|
3
|
+
export interface PromisesItem {
|
|
4
|
+
href: string;
|
|
5
|
+
id?: string;
|
|
6
|
+
image: ImageProps;
|
|
7
|
+
title: string;
|
|
8
|
+
}
|
|
9
|
+
export interface PromisesLabels {
|
|
10
|
+
anchor: {
|
|
11
|
+
continue: string;
|
|
12
|
+
};
|
|
13
|
+
layer: {
|
|
14
|
+
close: string;
|
|
15
|
+
open: string;
|
|
16
|
+
};
|
|
17
|
+
scroller: {
|
|
18
|
+
previous: string;
|
|
19
|
+
next: string;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export interface PromisesProps extends ComponentPropsWithoutRef<'div'> {
|
|
23
|
+
items: PromisesItem[];
|
|
24
|
+
labels: PromisesLabels;
|
|
25
|
+
}
|
|
26
|
+
export declare const Promises: FunctionComponent<PromisesProps>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { t as e } from "../../chunks/clsx.js";
|
|
3
|
+
import { PromisesMedia as t } from "./PromisesMedia.js";
|
|
4
|
+
import { PromisesMenu as n } from "./PromisesMenu.js";
|
|
5
|
+
import { useCallback as r, useEffect as i, useState as a } from "react";
|
|
6
|
+
import { jsx as o, jsxs as s } from "react/jsx-runtime";
|
|
7
|
+
//#region lib/ui/Promises/Promises.tsx
|
|
8
|
+
var c = 1e4, l = ({ className: l, items: u, labels: d, ...f }) => {
|
|
9
|
+
let [p, m] = a(0), [h, g] = a(0), _ = r(() => {
|
|
10
|
+
m((e) => (e - 1 + u.length) % u.length);
|
|
11
|
+
}, [u.length]), v = r(() => {
|
|
12
|
+
m((e) => (e + 1) % u.length);
|
|
13
|
+
}, [u.length]), y = r((e) => {
|
|
14
|
+
m(e), g((e) => e + 1);
|
|
15
|
+
}, []);
|
|
16
|
+
return i(() => {
|
|
17
|
+
let e = setInterval(() => {
|
|
18
|
+
v();
|
|
19
|
+
}, c);
|
|
20
|
+
return () => clearInterval(e);
|
|
21
|
+
}, [v, h]), /* @__PURE__ */ s("div", {
|
|
22
|
+
...f,
|
|
23
|
+
className: e("relative isolate flex gap-x-20 @container/promises", l),
|
|
24
|
+
children: [/* @__PURE__ */ o(t, {
|
|
25
|
+
activeIndex: p,
|
|
26
|
+
items: u,
|
|
27
|
+
labels: d,
|
|
28
|
+
onClickMenu: (e) => m(e),
|
|
29
|
+
onNext: v,
|
|
30
|
+
onPrevious: _
|
|
31
|
+
}), /* @__PURE__ */ o(n, {
|
|
32
|
+
activeIndex: p,
|
|
33
|
+
items: u,
|
|
34
|
+
labels: d,
|
|
35
|
+
onClickMenu: (e) => y(e)
|
|
36
|
+
})]
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
//#endregion
|
|
40
|
+
export { l as Promises };
|
|
41
|
+
|
|
42
|
+
//# sourceMappingURL=Promises.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Promises.js","names":[],"sources":["../../../lib/ui/Promises/Promises.tsx"],"sourcesContent":["'use client';\nimport clsx from 'clsx';\nimport type { ComponentPropsWithoutRef, FunctionComponent } from 'react';\nimport { useCallback, useEffect, useState } from 'react';\n\nimport type { ImageProps } from '../Image';\n\nimport { PromisesMedia } from './PromisesMedia';\nimport { PromisesMenu } from './PromisesMenu';\n\nconst INTERVAL_IN_MS = 10000;\n\nexport interface PromisesItem {\n href: string;\n id?: string;\n image: ImageProps;\n title: string;\n}\n\nexport interface PromisesLabels {\n anchor: { continue: string };\n layer: { close: string; open: string };\n scroller: { previous: string; next: string };\n}\n\nexport interface PromisesProps extends ComponentPropsWithoutRef<'div'> {\n items: PromisesItem[];\n labels: PromisesLabels;\n}\n\nexport const Promises: FunctionComponent<PromisesProps> = ({\n className,\n items,\n labels,\n ...attrs\n}) => {\n const [activeIndex, setActiveIndex] = useState(0);\n const [intervalKey, setIntervalKey] = useState(0);\n\n const previousActiveIndex = useCallback(() => {\n setActiveIndex((state) => (state - 1 + items.length) % items.length);\n }, [items.length]);\n\n const nextActiveIndex = useCallback(() => {\n setActiveIndex((state) => (state + 1) % items.length);\n }, [items.length]);\n\n const goToIndex = useCallback((index: number) => {\n setActiveIndex(index);\n setIntervalKey((k) => k + 1);\n }, []);\n\n useEffect(() => {\n const interval = setInterval(() => {\n nextActiveIndex();\n }, INTERVAL_IN_MS);\n\n return () => clearInterval(interval);\n }, [nextActiveIndex, intervalKey]);\n\n return (\n <div\n {...attrs}\n className={clsx('relative isolate flex gap-x-20 @container/promises', className)}\n >\n <PromisesMedia\n activeIndex={activeIndex}\n items={items}\n labels={labels}\n onClickMenu={(i) => setActiveIndex(i)}\n onNext={nextActiveIndex}\n onPrevious={previousActiveIndex}\n />\n <PromisesMenu\n activeIndex={activeIndex}\n items={items}\n labels={labels}\n onClickMenu={(i) => goToIndex(i)}\n />\n </div>\n );\n};\n"],"mappings":";;;;;;;AAUA,IAAM,IAAiB,KAoBV,KAA8C,EACzD,cACA,UACA,WACA,GAAG,QACC;CACJ,IAAM,CAAC,GAAa,KAAkB,EAAS,EAAE,EAC3C,CAAC,GAAa,KAAkB,EAAS,EAAE,EAE3C,IAAsB,QAAkB;AAC5C,KAAgB,OAAW,IAAQ,IAAI,EAAM,UAAU,EAAM,OAAO;IACnE,CAAC,EAAM,OAAO,CAAC,EAEZ,IAAkB,QAAkB;AACxC,KAAgB,OAAW,IAAQ,KAAK,EAAM,OAAO;IACpD,CAAC,EAAM,OAAO,CAAC,EAEZ,IAAY,GAAa,MAAkB;AAE/C,EADA,EAAe,EAAM,EACrB,GAAgB,MAAM,IAAI,EAAE;IAC3B,EAAE,CAAC;AAUN,QARA,QAAgB;EACd,IAAM,IAAW,kBAAkB;AACjC,MAAiB;KAChB,EAAe;AAElB,eAAa,cAAc,EAAS;IACnC,CAAC,GAAiB,EAAY,CAAC,EAGhC,kBAAC,OAAD;EACE,GAAI;EACJ,WAAW,EAAK,sDAAsD,EAAU;YAFlF,CAIE,kBAAC,GAAD;GACe;GACN;GACC;GACR,cAAc,MAAM,EAAe,EAAE;GACrC,QAAQ;GACR,YAAY;GACZ,CAAA,EACF,kBAAC,GAAD;GACe;GACN;GACC;GACR,cAAc,MAAM,EAAU,EAAA;GAC9B,CAAA,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ComponentPropsWithoutRef, FunctionComponent } from 'react';
|
|
2
|
+
import { PromisesItem, PromisesLabels } from './Promises';
|
|
3
|
+
interface Props extends ComponentPropsWithoutRef<'div'> {
|
|
4
|
+
activeIndex: number;
|
|
5
|
+
items: PromisesItem[];
|
|
6
|
+
labels: PromisesLabels;
|
|
7
|
+
onClickMenu: (index: number) => void;
|
|
8
|
+
onNext: () => void;
|
|
9
|
+
onPrevious: () => void;
|
|
10
|
+
}
|
|
11
|
+
export declare const PromisesMedia: FunctionComponent<Props>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { t as e } from "../../chunks/clsx.js";
|
|
3
|
+
import { Arrows as t } from "../arrows/Arrows.js";
|
|
4
|
+
import { Image as n } from "../Image.js";
|
|
5
|
+
import { jsx as r, jsxs as i } from "react/jsx-runtime";
|
|
6
|
+
//#region lib/ui/Promises/PromisesMedia.tsx
|
|
7
|
+
var a = ({ activeIndex: a, items: o, labels: s, onClickMenu: c, onNext: l, onPrevious: u, ...d }) => /* @__PURE__ */ i("div", {
|
|
8
|
+
...d,
|
|
9
|
+
className: "rounded-16 relative isolate w-full shrink-0 overflow-hidden @3xl/promises:max-w-380",
|
|
10
|
+
children: [
|
|
11
|
+
/* @__PURE__ */ r("div", {
|
|
12
|
+
className: "relative isolate grid",
|
|
13
|
+
children: o.map(({ id: t, image: i, title: o }, s) => {
|
|
14
|
+
let c = s === a;
|
|
15
|
+
return /* @__PURE__ */ r("article", {
|
|
16
|
+
"aria-hidden": !c,
|
|
17
|
+
className: e("aspect-vertical-video rounded-16 relative isolate col-start-1 row-start-1 w-full origin-top overflow-hidden opacity-0 transition-opacity duration-500", { "opacity-100": c }),
|
|
18
|
+
inert: !c,
|
|
19
|
+
children: /* @__PURE__ */ r(n, {
|
|
20
|
+
...i,
|
|
21
|
+
className: "size-full object-cover",
|
|
22
|
+
lazyLoad: !0,
|
|
23
|
+
sizes: "(min-width: 768px) 380px, 100vw"
|
|
24
|
+
})
|
|
25
|
+
}, t ?? `${o}-${s}`);
|
|
26
|
+
})
|
|
27
|
+
}),
|
|
28
|
+
/* @__PURE__ */ r(t, {
|
|
29
|
+
iconType: "ArrowDefault",
|
|
30
|
+
isDisabledHidden: !0,
|
|
31
|
+
isNextDisabled: a === o.length - 1,
|
|
32
|
+
isPrevDisabled: a === 0,
|
|
33
|
+
labels: {
|
|
34
|
+
next: s.scroller.next,
|
|
35
|
+
previous: s.scroller.previous
|
|
36
|
+
},
|
|
37
|
+
size: "small",
|
|
38
|
+
className: "absolute inset-0 -mx-12 !w-[calc(100%_+_24px)] @3xl/promises:hidden",
|
|
39
|
+
onNext: l,
|
|
40
|
+
onPrev: u
|
|
41
|
+
}),
|
|
42
|
+
/* @__PURE__ */ r("ul", {
|
|
43
|
+
className: "absolute inset-x-0 top-0 flex list-none gap-2 p-20",
|
|
44
|
+
children: o.map(({ id: t, title: n }, i) => /* @__PURE__ */ r("li", {
|
|
45
|
+
className: "w-full flex items-start",
|
|
46
|
+
children: /* @__PURE__ */ r("button", {
|
|
47
|
+
"aria-current": i === a,
|
|
48
|
+
"aria-label": n,
|
|
49
|
+
className: e("rounded-pill h-4 w-full overflow-hidden", {
|
|
50
|
+
"bg-white": i < a,
|
|
51
|
+
"bg-white/40": i >= a
|
|
52
|
+
}),
|
|
53
|
+
onClick: () => c(i),
|
|
54
|
+
type: "button",
|
|
55
|
+
children: i === a && /* @__PURE__ */ r("span", { className: "animate-filling block h-full w-full origin-left bg-white rtl:origin-right" })
|
|
56
|
+
})
|
|
57
|
+
}, t ?? i))
|
|
58
|
+
})
|
|
59
|
+
]
|
|
60
|
+
});
|
|
61
|
+
//#endregion
|
|
62
|
+
export { a as PromisesMedia };
|
|
63
|
+
|
|
64
|
+
//# sourceMappingURL=PromisesMedia.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PromisesMedia.js","names":[],"sources":["../../../lib/ui/Promises/PromisesMedia.tsx"],"sourcesContent":["'use client';\nimport clsx from 'clsx';\nimport type { ComponentPropsWithoutRef, FunctionComponent } from 'react';\n\nimport { Image } from '../Image';\nimport { Arrows } from '../arrows/Arrows';\nimport type { PromisesItem, PromisesLabels } from './Promises';\n\ninterface Props extends ComponentPropsWithoutRef<'div'> {\n activeIndex: number;\n items: PromisesItem[];\n labels: PromisesLabels;\n onClickMenu: (index: number) => void;\n onNext: () => void;\n onPrevious: () => void;\n}\n\nexport const PromisesMedia: FunctionComponent<Props> = ({\n activeIndex,\n items,\n labels,\n onClickMenu,\n onNext,\n onPrevious,\n ...attrs\n}) => {\n return (\n <div\n {...attrs}\n className=\"rounded-16 relative isolate w-full shrink-0 overflow-hidden @3xl/promises:max-w-380\"\n >\n <div className=\"relative isolate grid\">\n {items.map(({ id, image, title }, index) => {\n const isActive = index === activeIndex;\n\n return (\n <article\n aria-hidden={!isActive}\n key={id ?? `${title}-${index}`}\n className={clsx(\n 'aspect-vertical-video rounded-16 relative isolate col-start-1 row-start-1 w-full origin-top overflow-hidden opacity-0 transition-opacity duration-500',\n { 'opacity-100': isActive },\n )}\n inert={!isActive}\n >\n <Image\n {...image}\n className=\"size-full object-cover\"\n lazyLoad\n sizes=\"(min-width: 768px) 380px, 100vw\"\n />\n </article>\n );\n })}\n </div>\n\n <Arrows\n iconType=\"ArrowDefault\"\n isDisabledHidden={true}\n isNextDisabled={activeIndex === items.length - 1}\n isPrevDisabled={activeIndex === 0}\n labels={{\n next: labels.scroller.next,\n previous: labels.scroller.previous,\n }}\n size=\"small\"\n className=\"absolute inset-0 -mx-12 !w-[calc(100%_+_24px)] @3xl/promises:hidden\"\n onNext={onNext}\n onPrev={onPrevious}\n />\n\n <ul className=\"absolute inset-x-0 top-0 flex list-none gap-2 p-20\">\n {items.map(({ id, title }, index) => (\n <li key={id ?? index} className=\"w-full flex items-start\">\n <button\n aria-current={index === activeIndex}\n aria-label={title}\n className={clsx('rounded-pill h-4 w-full overflow-hidden', {\n 'bg-white': index < activeIndex,\n 'bg-white/40': index >= activeIndex,\n })}\n onClick={() => onClickMenu(index)}\n type=\"button\"\n >\n {index === activeIndex && (\n <span className=\"animate-filling block h-full w-full origin-left bg-white rtl:origin-right\" />\n )}\n </button>\n </li>\n ))}\n </ul>\n </div>\n );\n};\n"],"mappings":";;;;;;AAiBA,IAAa,KAA2C,EACtD,gBACA,UACA,WACA,gBACA,WACA,eACA,GAAG,QAGD,kBAAC,OAAD;CACE,GAAI;CACJ,WAAU;WAFZ;EAIE,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAM,KAAK,EAAE,OAAI,UAAO,YAAS,MAAU;IAC1C,IAAM,IAAW,MAAU;AAE3B,WACE,kBAAC,WAAD;KACE,eAAa,CAAC;KAEd,WAAW,EACT,yJACA,EAAE,eAAe,GAAU,CAC5B;KACD,OAAO,CAAC;eAER,kBAAC,GAAD;MACE,GAAI;MACJ,WAAU;MACV,UAAA;MACA,OAAM;MACN,CAAA;KACM,EAbH,KAAM,GAAG,EAAM,GAAG,IAaf;;GAGV,CAAA;EAEN,kBAAC,GAAD;GACE,UAAS;GACT,kBAAkB;GAClB,gBAAgB,MAAgB,EAAM,SAAS;GAC/C,gBAAgB,MAAgB;GAChC,QAAQ;IACN,MAAM,EAAO,SAAS;IACtB,UAAU,EAAO,SAAS;IAC3B;GACD,MAAK;GACL,WAAU;GACF;GACR,QAAQ;GACR,CAAA;EAEF,kBAAC,MAAD;GAAI,WAAU;aACX,EAAM,KAAK,EAAE,OAAI,YAAS,MACzB,kBAAC,MAAD;IAAsB,WAAU;cAC9B,kBAAC,UAAD;KACE,gBAAc,MAAU;KACxB,cAAY;KACZ,WAAW,EAAK,2CAA2C;MACzD,YAAY,IAAQ;MACpB,eAAe,KAAS;MACzB,CAAC;KACF,eAAe,EAAY,EAAM;KACjC,MAAK;eAEJ,MAAU,KACT,kBAAC,QAAD,EAAM,WAAU,6EAA8E,CAAA;KAEzF,CAAA;IACN,EAfI,KAAM,EAeV,CAAA;GAEJ,CAAA"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { FunctionComponent } from 'react';
|
|
2
|
+
import { PromisesItem, PromisesLabels } from './Promises';
|
|
3
|
+
interface Props {
|
|
4
|
+
activeIndex: number;
|
|
5
|
+
className?: string;
|
|
6
|
+
items: PromisesItem[];
|
|
7
|
+
labels: PromisesLabels;
|
|
8
|
+
onClickMenu: (index: number) => void;
|
|
9
|
+
}
|
|
10
|
+
export declare const PromisesMenu: FunctionComponent<Props>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { t as e } from "../../chunks/clsx.js";
|
|
3
|
+
import { Button as t } from "../buttons/Button.js";
|
|
4
|
+
import { n } from "../../chunks/Carousel.context.js";
|
|
5
|
+
import { Carousel as r } from "../carousel/Carousel.js";
|
|
6
|
+
import { CarouselBody as i } from "../carousel/CarouselBody.js";
|
|
7
|
+
import { CarouselItem as a } from "../carousel/CarouselItem.js";
|
|
8
|
+
import { useEffect as o, useId as s, useLayoutEffect as c, useMemo as l, useRef as u } from "react";
|
|
9
|
+
import { Fragment as d, jsx as f, jsxs as p } from "react/jsx-runtime";
|
|
10
|
+
//#region lib/ui/Promises/PromisesMenu.tsx
|
|
11
|
+
var m = ({ anchorLabel: n, anchorName: r, isActive: i, labelNode: a }) => {
|
|
12
|
+
let o = u(null), s = u(null);
|
|
13
|
+
return c(() => {
|
|
14
|
+
if (!o.current || !s.current) return;
|
|
15
|
+
let e = new ResizeObserver(() => {
|
|
16
|
+
s.current && o.current && (s.current.style.height = `${o.current.offsetHeight}px`);
|
|
17
|
+
});
|
|
18
|
+
return e.observe(o.current), () => e.disconnect();
|
|
19
|
+
}, []), /* @__PURE__ */ f("li", {
|
|
20
|
+
className: e("relative isolate transition-[opacity,height,margin-inline-start] ms-0 duration-300 overflow-hidden rounded-32 bg-white p-20", { "@3xl/promises:ms-52": !i }),
|
|
21
|
+
children: /* @__PURE__ */ p("div", {
|
|
22
|
+
ref: s,
|
|
23
|
+
className: "flex justify-between items-start overflow-hidden",
|
|
24
|
+
style: { anchorName: r },
|
|
25
|
+
children: [/* @__PURE__ */ f("div", {
|
|
26
|
+
ref: o,
|
|
27
|
+
className: "pe-40",
|
|
28
|
+
children: a
|
|
29
|
+
}), i && /* @__PURE__ */ f(t, {
|
|
30
|
+
"aria-hidden": !0,
|
|
31
|
+
className: "opacity-0 pointer-events-none",
|
|
32
|
+
icon: "ArrowDefaultRight",
|
|
33
|
+
inert: !0,
|
|
34
|
+
tabIndex: -1,
|
|
35
|
+
variant: "pill",
|
|
36
|
+
children: n
|
|
37
|
+
})]
|
|
38
|
+
})
|
|
39
|
+
});
|
|
40
|
+
}, h = ({ activeIndex: e, onSelect: t }) => {
|
|
41
|
+
let { api: r, snapped: i } = n(), a = u(t);
|
|
42
|
+
a.current = t;
|
|
43
|
+
let s = i[0];
|
|
44
|
+
return o(() => {
|
|
45
|
+
s !== void 0 && a.current(s);
|
|
46
|
+
}, [s]), o(() => {
|
|
47
|
+
r?.scrollTo(e);
|
|
48
|
+
}, [r, e]), null;
|
|
49
|
+
}, g = ({ activeIndex: t, className: n, onSelect: o, sliderItems: s }) => {
|
|
50
|
+
let c = u(t), m = l(() => ({
|
|
51
|
+
active: s.length > 1,
|
|
52
|
+
align: "center",
|
|
53
|
+
startIndex: c.current
|
|
54
|
+
}), [s.length]);
|
|
55
|
+
return /* @__PURE__ */ f(d, { children: /* @__PURE__ */ p(r, {
|
|
56
|
+
className: e("relative isolate flex h-full items-center", n),
|
|
57
|
+
options: m,
|
|
58
|
+
children: [/* @__PURE__ */ f(h, {
|
|
59
|
+
activeIndex: t,
|
|
60
|
+
onSelect: o
|
|
61
|
+
}), /* @__PURE__ */ f(i, {
|
|
62
|
+
className: "w-full px-20",
|
|
63
|
+
scrollClassName: "gap-12 md:gap-40 auto-cols-full",
|
|
64
|
+
children: s.map((t, n) => /* @__PURE__ */ f(a, {
|
|
65
|
+
index: n,
|
|
66
|
+
children: /* @__PURE__ */ f("div", {
|
|
67
|
+
"aria-label": `${n + 1}/${s.length}`,
|
|
68
|
+
className: e("rounded-16 overflow-hidden bg-white"),
|
|
69
|
+
children: t
|
|
70
|
+
})
|
|
71
|
+
}, n))
|
|
72
|
+
})]
|
|
73
|
+
}) });
|
|
74
|
+
}, _ = ({ activeIndex: n, className: r, items: i, labels: a, onClickMenu: o }) => {
|
|
75
|
+
let c = s(), u = c.replace(/[^a-zA-Z0-9_-]/g, ""), { menuItems: h, sliderItems: _ } = l(() => {
|
|
76
|
+
let r = i.map(({ href: t, id: r, title: i }, a) => {
|
|
77
|
+
let s = a === n, l = r ?? `${c}_${a}`;
|
|
78
|
+
return {
|
|
79
|
+
anchorName: `--promises-${u}-${a}`,
|
|
80
|
+
href: t,
|
|
81
|
+
id: r,
|
|
82
|
+
isActive: s,
|
|
83
|
+
itemId: l,
|
|
84
|
+
labelNode: /* @__PURE__ */ f("button", {
|
|
85
|
+
id: l,
|
|
86
|
+
"aria-label": i,
|
|
87
|
+
className: e("text-start hover:underline text-h4 text-center font-serif @3xl/promises:text-h4 @5xl/promises:text-h2 @6xl/promises:text-h1 @3xl/promises:text-start"),
|
|
88
|
+
onClick: () => o(a),
|
|
89
|
+
type: "button",
|
|
90
|
+
children: i
|
|
91
|
+
}),
|
|
92
|
+
title: i
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
return {
|
|
96
|
+
menuItems: r,
|
|
97
|
+
sliderItems: r.map(({ labelNode: e, href: n, itemId: r }) => /* @__PURE__ */ p("div", {
|
|
98
|
+
className: "rounded-32 p-16 flex flex-col items-start gap-8",
|
|
99
|
+
children: [e, /* @__PURE__ */ f(t, {
|
|
100
|
+
"aria-describedby": r,
|
|
101
|
+
"aria-label": a.anchor.continue,
|
|
102
|
+
href: n,
|
|
103
|
+
icon: "ArrowDefaultRight",
|
|
104
|
+
variant: "pill",
|
|
105
|
+
children: a.anchor.continue
|
|
106
|
+
})]
|
|
107
|
+
}))
|
|
108
|
+
};
|
|
109
|
+
}, [
|
|
110
|
+
n,
|
|
111
|
+
i,
|
|
112
|
+
a,
|
|
113
|
+
c,
|
|
114
|
+
o,
|
|
115
|
+
u
|
|
116
|
+
]), v = h[n];
|
|
117
|
+
return /* @__PURE__ */ p(d, { children: [
|
|
118
|
+
/* @__PURE__ */ f("ul", {
|
|
119
|
+
className: e("hidden @3xl/promises:block", "absolute bottom-0 inset-x-0 overflow-hidden @3xl/promises:-ms-52 text-black w-full", "@3xl/promises:pointer-events-auto @3xl/promises:static @3xl/promises:p-0 @3xl/promises:pt-40", r),
|
|
120
|
+
children: h.map(({ anchorName: e, id: t, isActive: n, labelNode: r, title: i }, o) => /* @__PURE__ */ f(m, {
|
|
121
|
+
anchorLabel: a.anchor.continue,
|
|
122
|
+
anchorName: e,
|
|
123
|
+
isActive: n,
|
|
124
|
+
labelNode: r
|
|
125
|
+
}, t ?? `${i}-${o}`))
|
|
126
|
+
}),
|
|
127
|
+
v && /* @__PURE__ */ f(t, {
|
|
128
|
+
"aria-describedby": v.itemId,
|
|
129
|
+
"aria-label": a.anchor.continue,
|
|
130
|
+
className: "hidden @3xl/promises:inline-flex transition-[top] duration-200 ease-out-quad absolute z-1",
|
|
131
|
+
href: v.href,
|
|
132
|
+
icon: "ArrowDefaultRight",
|
|
133
|
+
style: {
|
|
134
|
+
positionAnchor: v.anchorName,
|
|
135
|
+
top: "anchor(top)",
|
|
136
|
+
right: "anchor(right)"
|
|
137
|
+
},
|
|
138
|
+
variant: "pill",
|
|
139
|
+
children: a.anchor.continue
|
|
140
|
+
}),
|
|
141
|
+
/* @__PURE__ */ f("div", {
|
|
142
|
+
className: "@3xl/promises:hidden absolute inset-x-0 bottom-20",
|
|
143
|
+
children: /* @__PURE__ */ f(g, {
|
|
144
|
+
activeIndex: n,
|
|
145
|
+
onSelect: o,
|
|
146
|
+
sliderItems: _
|
|
147
|
+
})
|
|
148
|
+
})
|
|
149
|
+
] });
|
|
150
|
+
};
|
|
151
|
+
//#endregion
|
|
152
|
+
export { _ as PromisesMenu };
|
|
153
|
+
|
|
154
|
+
//# sourceMappingURL=PromisesMenu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PromisesMenu.js","names":[],"sources":["../../../lib/ui/Promises/PromisesMenu.tsx"],"sourcesContent":["'use client';\nimport clsx from 'clsx';\nimport type { FunctionComponent, ReactNode } from 'react';\nimport { useEffect, useId, useMemo, useRef, useLayoutEffect } from 'react';\nimport { useCarousel } from '../carousel/Carousel.context';\n\nimport { Carousel } from '../carousel/Carousel';\nimport { CarouselBody } from '../carousel/CarouselBody';\nimport { CarouselItem } from '../carousel/CarouselItem';\nimport { Button } from '../buttons/Button';\nimport type { PromisesItem, PromisesLabels } from './Promises';\n\ninterface MenuItemProps {\n anchorLabel: string;\n anchorName: string;\n isActive: boolean;\n labelNode: ReactNode;\n}\n\nconst PromisesMenuItem: FunctionComponent<MenuItemProps> = ({\n anchorLabel,\n anchorName,\n isActive,\n labelNode,\n}) => {\n const labelRef = useRef<HTMLDivElement>(null);\n\n const containerRef = useRef<HTMLDivElement>(null);\n\n useLayoutEffect(() => {\n if (!labelRef.current || !containerRef.current) return;\n const resizeObserver = new ResizeObserver(() => {\n if (containerRef.current && labelRef.current) {\n containerRef.current.style.height = `${labelRef.current.offsetHeight}px`;\n }\n });\n resizeObserver.observe(labelRef.current);\n return () => resizeObserver.disconnect();\n }, []);\n\n return (\n <li\n className={clsx(\n 'relative isolate transition-[opacity,height,margin-inline-start] ms-0 duration-300 overflow-hidden rounded-32 bg-white p-20',\n { '@3xl/promises:ms-52': !isActive },\n )}\n >\n <div\n ref={containerRef}\n className=\"flex justify-between items-start overflow-hidden\"\n style={{ anchorName } as unknown as React.CSSProperties}\n >\n <div ref={labelRef} className=\"pe-40\">\n {labelNode}\n </div>\n {isActive && (\n <Button\n aria-hidden\n className=\"opacity-0 pointer-events-none\"\n icon=\"ArrowDefaultRight\"\n inert\n tabIndex={-1}\n variant=\"pill\"\n >\n {anchorLabel}\n </Button>\n )}\n </div>\n </li>\n );\n};\n\ninterface Props {\n activeIndex: number;\n className?: string;\n items: PromisesItem[];\n labels: PromisesLabels;\n onClickMenu: (index: number) => void;\n}\n\ninterface PromisesItemSliderProps {\n activeIndex: number;\n className?: string;\n onSelect: (index: number) => void;\n sliderItems: ReactNode[];\n}\n\nconst CarouselSync: FunctionComponent<{\n activeIndex: number;\n onSelect: (index: number) => void;\n}> = ({ activeIndex, onSelect }) => {\n const { api, snapped } = useCarousel();\n const onSelectRef = useRef(onSelect);\n onSelectRef.current = onSelect;\n\n const snappedIndex = snapped[0];\n useEffect(() => {\n if (snappedIndex !== undefined) onSelectRef.current(snappedIndex);\n }, [snappedIndex]);\n\n useEffect(() => {\n api?.scrollTo(activeIndex);\n }, [api, activeIndex]);\n\n return null;\n};\n\nconst PromisesItemSlider: FunctionComponent<PromisesItemSliderProps> = ({\n activeIndex,\n className,\n onSelect,\n sliderItems,\n}) => {\n const initialIndex = useRef(activeIndex);\n const carouselOptions = useMemo(\n () => ({\n active: sliderItems.length > 1,\n align: 'center' as const,\n startIndex: initialIndex.current,\n }),\n [sliderItems.length],\n );\n\n return (\n <>\n <Carousel\n className={clsx('relative isolate flex h-full items-center', className)}\n options={carouselOptions}\n >\n <CarouselSync activeIndex={activeIndex} onSelect={onSelect} />\n <CarouselBody className=\"w-full px-20\" scrollClassName=\"gap-12 md:gap-40 auto-cols-full\">\n {sliderItems.map((item, i) => (\n <CarouselItem index={i} key={i}>\n <div\n aria-label={`${i + 1}/${sliderItems.length}`}\n className={clsx('rounded-16 overflow-hidden bg-white')}\n >\n {item}\n </div>\n </CarouselItem>\n ))}\n </CarouselBody>\n </Carousel>\n </>\n );\n};\n\nexport const PromisesMenu: FunctionComponent<Props> = ({\n activeIndex,\n className,\n items,\n labels,\n onClickMenu,\n}) => {\n const menuId = useId();\n const safeMenuId = menuId.replace(/[^a-zA-Z0-9_-]/g, '');\n\n const { menuItems, sliderItems } = useMemo(() => {\n const mItems = items.map(({ href, id, title }, index) => {\n const isActive = index === activeIndex;\n const itemId = id ?? `${menuId}_${index}`;\n const anchorName = `--promises-${safeMenuId}-${index}`;\n\n const labelNode = (\n <button\n id={itemId}\n aria-label={title}\n className={clsx(\n 'text-start hover:underline text-h4 text-center font-serif @3xl/promises:text-h4 @5xl/promises:text-h2 @6xl/promises:text-h1 @3xl/promises:text-start',\n )}\n onClick={() => onClickMenu(index)}\n type=\"button\"\n >\n {title}\n </button>\n );\n\n return { anchorName, href, id, isActive, itemId, labelNode, title };\n });\n\n const sItems = mItems.map(({ labelNode, href, itemId }) => (\n <div className=\"rounded-32 p-16 flex flex-col items-start gap-8\">\n {labelNode}\n <Button\n aria-describedby={itemId}\n aria-label={labels.anchor.continue}\n href={href}\n icon=\"ArrowDefaultRight\"\n variant=\"pill\"\n >\n {labels.anchor.continue}\n </Button>\n </div>\n ));\n\n return { menuItems: mItems, sliderItems: sItems };\n }, [activeIndex, items, labels, menuId, onClickMenu, safeMenuId]);\n\n const activeItem = menuItems[activeIndex];\n\n return (\n <>\n <ul\n className={clsx(\n 'hidden @3xl/promises:block',\n 'absolute bottom-0 inset-x-0 overflow-hidden @3xl/promises:-ms-52 text-black w-full',\n '@3xl/promises:pointer-events-auto @3xl/promises:static @3xl/promises:p-0 @3xl/promises:pt-40',\n className,\n )}\n >\n {menuItems.map(({ anchorName, id, isActive, labelNode, title }, index) => (\n <PromisesMenuItem\n anchorLabel={labels.anchor.continue}\n anchorName={anchorName}\n isActive={isActive}\n key={id ?? `${title}-${index}`}\n labelNode={labelNode}\n />\n ))}\n </ul>\n {activeItem && (\n <Button\n aria-describedby={activeItem.itemId}\n aria-label={labels.anchor.continue}\n className=\"hidden @3xl/promises:inline-flex transition-[top] duration-200 ease-out-quad absolute z-1\"\n href={activeItem.href}\n icon=\"ArrowDefaultRight\"\n style={\n {\n positionAnchor: activeItem.anchorName,\n top: 'anchor(top)',\n right: 'anchor(right)',\n } as React.CSSProperties\n }\n variant=\"pill\"\n >\n {labels.anchor.continue}\n </Button>\n )}\n <div className=\"@3xl/promises:hidden absolute inset-x-0 bottom-20\">\n <PromisesItemSlider\n activeIndex={activeIndex}\n onSelect={onClickMenu}\n sliderItems={sliderItems}\n />\n </div>\n </>\n );\n};\n"],"mappings":"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"}
|
package/ui/SidebarLayout.js
CHANGED
|
@@ -141,7 +141,7 @@ var m = ({ items: i, children: a, className: o, activeIndex: l = 0, isCollapsed:
|
|
|
141
141
|
style: { positionAnchor: `--sidebar-item-${l}` },
|
|
142
142
|
children: /* @__PURE__ */ s(f, { isCollapsed: g })
|
|
143
143
|
}), /* @__PURE__ */ s("nav", {
|
|
144
|
-
className: t("max-md:divide-y-1 p-8 max-md:h-full max-md:overflow-auto md:gap-y-20 grid overflow-hidden text-b3 font-semibold relative transition-grid-template-columns duration-300 ease-in-out", g ? "grid-cols-[48px_0fr]" : "grid-cols-[48px_1fr]"),
|
|
144
|
+
className: t("max-md:divide-y-1 p-8 max-md:h-full max-md:overflow-auto md:flex-1 md:min-h-0 md:overflow-y-auto md:overflow-x-hidden md:gap-y-20 grid overflow-hidden text-b3 font-semibold relative transition-grid-template-columns duration-300 ease-in-out", g ? "grid-cols-[48px_0fr]" : "grid-cols-[48px_1fr]"),
|
|
145
145
|
children: i.map((e, t) => /* @__PURE__ */ s(d, {
|
|
146
146
|
item: e,
|
|
147
147
|
isActive: t === l,
|
package/ui/SidebarLayout.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SidebarLayout.js","names":[],"sources":["../../lib/ui/SidebarLayout.tsx"],"sourcesContent":["import { Icon, type IconicNames } from '@clubmed/trident-icons';\nimport clsx from 'clsx';\nimport {\n type ComponentPropsWithoutRef,\n type CSSProperties,\n type Dispatch,\n type PropsWithChildren,\n type ReactNode,\n type SetStateAction,\n type MouseEvent,\n useCallback,\n useRef,\n useState,\n} from 'react';\nimport { twMerge } from 'tailwind-merge';\nimport { HamburgerIcon } from './HamburgerIcon';\nimport { useSlots } from '@/ui/hooks/useSlots';\n\ntype SidebarSubItem = {\n label: string;\n href: string;\n} & ComponentPropsWithoutRef<'a'>;\n\ntype SidebarItem = {\n label: string;\n icon: IconicNames;\n items?: SidebarSubItem[];\n} & ((ComponentPropsWithoutRef<'a'> & { href: string }) | ComponentPropsWithoutRef<'button'>);\n\ninterface SidebarItemProps {\n item: SidebarItem;\n isActive?: boolean;\n isCollapsed?: boolean;\n anchorName: string;\n onItemClick?: () => void;\n}\n\nfunction Cta(item: PropsWithChildren<SidebarItem>) {\n if ('href' in item) {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { children, icon, label, ...attrs } = item;\n\n return (\n <a\n {...attrs}\n className=\"flex whitespace-nowrap items-center text-white hover:text-saffron transition-colors outline-none focus-visible:bg-white/20 rounded-pill relative group/sidebar-item\"\n >\n {children}\n </a>\n );\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { children, icon, label, ...attrs } = item;\n\n return (\n <button\n {...attrs}\n className=\"flex whitespace-nowrap items-center text-white outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2 rounded-pill relative group/sidebar-item\"\n >\n {children}\n </button>\n );\n}\n\nconst SidebarItem = ({\n item,\n isActive = false,\n isCollapsed = false,\n anchorName,\n onItemClick,\n}: SidebarItemProps) => {\n const hasSubItems = item.items && item.items.length > 0;\n\n return (\n <div className=\"col-start-1 col-end-3\">\n <Cta\n {...item}\n onClick={(e: MouseEvent<HTMLButtonElement | HTMLAnchorElement>) => {\n if (hasSubItems && onItemClick) {\n e.preventDefault();\n onItemClick();\n }\n }}\n style={{ anchorName: `--${anchorName}` } as CSSProperties}\n >\n <div className=\"relative\">\n <div\n className={twMerge('aspect-square w-48 rounded-full flex items-center justify-center')}\n >\n <Icon name={item.icon} className=\"w-24 text-white\" />\n </div>\n <div className=\"absolute inset-0 pointer-events-none\" aria-hidden=\"true\" inert>\n <div\n className={twMerge(\n 'max-md:hidden aspect-square w-48 rounded-full flex items-center justify-center bg-lightSand text-black',\n '[clip-path:circle(var(--sidebarItem_clipCircle))] transition-all duration-200 delay-100 ease-in-out group-focus-visible/sidebar-item:ring-4 group-focus-visible/sidebar-item:ring-black group-focus-visible/sidebar-item:ring-offset-2',\n isActive ? '[--sidebarItem_clipCircle:100%]' : '[--sidebarItem_clipCircle:0%]',\n 'group-hover/sidebar-item:[--sidebarItem_clipCircle:100%] group-focus-within/sidebar-item:[--sidebarItem_clipCircle:100%]',\n !isActive &&\n 'group-hover/sidebar-item:bg-lightSand/80 group-focus-within/sidebar-item:bg-lightSand/80',\n )}\n >\n <Icon name={item.icon} className=\"w-24\" />\n </div>\n </div>\n </div>\n <span\n className={twMerge(\n 'transition-all duration-300 ease-in-out whitespace-nowrap ps-8 pe-12 truncate md:max-w-240',\n isCollapsed ? 'opacity-0' : 'opacity-100',\n )}\n >\n {item.label}\n </span>\n </Cta>\n {hasSubItems && (\n <div\n className={twMerge(\n 'grid transition-[grid-template-rows,opacity] duration-300 ease-in-out overflow-hidden max-md:grid-rows-[1fr] max-md:opacity-100',\n isCollapsed ? 'grid-rows-[0fr] opacity-0' : 'grid-rows-[1fr] opacity-100 pb-12',\n )}\n inert={isCollapsed}\n >\n <div className=\"outline-none relative overflow-hidden flex flex-col gap-8 ps-48 font-normal\">\n {item.items!.map((subItem, subIndex) => (\n <a\n key={subIndex}\n href={subItem.href}\n className=\"text-white hover:text-saffron transition-colors text-b4 whitespace-nowrap outline-none focus-visible:bg-white/20 ps-8 pe-16 rounded-pill\"\n >\n {subItem.label}\n </a>\n ))}\n </div>\n </div>\n )}\n </div>\n );\n};\n\nexport const CollapseIndicatorSVG = ({ isCollapsed }: { isCollapsed: boolean }) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"33\" height=\"48\">\n <g fill=\"none\" fillRule=\"nonzero\">\n <path\n fill=\"#FFF\"\n d=\"M10.008 48v-1.829c0-4.116-1.722-8.045-4.75-10.835-6.331-5.837-6.331-15.835 0-21.672a14.736 14.736 0 0 0 4.75-10.835V0h27.417v48H10.008Z\"\n />\n <g\n className={twMerge(\n 'transition-transform duration-500 ease-in-out origin-center [transform-box:fill-box]',\n isCollapsed ? 'rotate-180' : 'rotate-0',\n )}\n >\n <path\n fill=\"#1E2643\"\n d=\"M19.686 12.547c6.617 0 12 5.383 12 12s-5.383 12-12 12-12-5.383-12-12 5.383-12 12-12Zm0 1.986c-5.522 0-10.015 4.492-10.015 10.014 0 5.523 4.493 10.015 10.016 10.015 5.52 0 10.012-4.492 10.012-10.015 0-5.522-4.492-10.014-10.012-10.014Zm-2.097 4.382c.376-.366 1.032-.366 1.409 0l5.037 4.895a.948.948 0 0 1 0 1.37l-5.037 4.894a1.002 1.002 0 0 1-.705.285 1 1 0 0 1-.704-.285.947.947 0 0 1 0-1.369l4.332-4.21-4.332-4.21a.95.95 0 0 1 0-1.37Z\"\n />\n </g>\n </g>\n </svg>\n);\n\nexport interface SidebarLayoutProps extends ComponentPropsWithoutRef<'div'> {\n items: SidebarItem[];\n children: ReactNode;\n activeIndex?: number;\n isCollapsed?: boolean;\n onCollapsedChange?: (isCollapsed: boolean) => void;\n}\n\nfunction useSidebarLayout({\n isCollapsed: initialCollapsedValue,\n onCollapsedChange,\n}: {\n isCollapsed?: boolean;\n onCollapsedChange?: (isCollapsed: boolean) => void;\n}) {\n const [isCollapsed, setIsCollapsedState] = useState(() => initialCollapsedValue ?? true);\n const overlayRef = useRef<HTMLDivElement>(null);\n\n const setIsCollapsed = useCallback<Dispatch<SetStateAction<boolean>>>(\n (value) => {\n setIsCollapsedState((previousState) => {\n const nextState = typeof value === 'function' ? value(previousState) : value;\n if (nextState !== previousState) {\n onCollapsedChange?.(nextState);\n }\n return nextState;\n });\n },\n [onCollapsedChange],\n );\n\n const handleToggleCollapse = useCallback(() => {\n setIsCollapsed((prev) => !prev);\n }, [setIsCollapsed]);\n\n return {\n isCollapsed,\n setIsCollapsed,\n overlayRef,\n handleToggleCollapse,\n };\n}\n\nexport const SidebarLayout = ({\n items,\n children: initialChildren,\n className,\n activeIndex = 0,\n isCollapsed: initialCollapsedValue,\n onCollapsedChange,\n ...attrs\n}: SidebarLayoutProps) => {\n const { isCollapsed, setIsCollapsed, handleToggleCollapse } = useSidebarLayout({\n isCollapsed: initialCollapsedValue,\n onCollapsedChange,\n });\n const {\n 'header-logo': headerLogo,\n header,\n ['header-actions']: headerActions,\n children,\n } = useSlots(initialChildren, ['header-logo', 'header', 'header-actions']);\n\n return (\n <div\n data-name=\"SidebarLayout\"\n className={clsx('h-screen overflow-clip flex flex-col bg-ultramarine', className)}\n {...attrs}\n >\n {/* Header */}\n <header className=\"bg-ultramarine h-64 flex items-center justify-between gap-8 p-8 ps-20 md:px-24 text-white shrink-0\">\n {headerLogo}\n {header}\n {headerActions}\n <button\n className=\"md:hidden w-48 h-48 focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2 rounded-full flex items-center justify-center text-white transition-opacity outline-none\"\n aria-label={isCollapsed ? 'Open the navigation menu' : 'Close the navigation menu'}\n onClick={handleToggleCollapse}\n >\n <HamburgerIcon isActive={!isCollapsed} />\n </button>\n </header>\n\n {/* Main container with sidebar and content */}\n <div className=\"flex h-full overflow-x-clip overflow-y-hidden\">\n {/* Sidebar */}\n <aside\n className={twMerge(\n 'z-10 max-md:absolute max-md:top-64 max-md:h-[calc(100dvh-64px)] max-md:w-screen flex bg-ultramarine px-8 md:py-16 md:ps-28 md:pe-16 text-white flex-col gap-24 transition-all duration-300 ease-in-out relative h-full shrink-0',\n isCollapsed ? 'max-md:-translate-x-full' : 'md:pe-24',\n )}\n >\n {/* Indicator */}\n <button\n className=\"hidden md:block absolute transition-top duration-300 ease-in-out rounded-pill outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2 [--indicatorYPos:anchor(top)] top-[max(var(--indicatorYPos,24px),24px)] left-[max(calc(100%_-_10px),86px)]\"\n onClick={handleToggleCollapse}\n aria-label={isCollapsed ? 'Expand sidebar' : 'Collapse sidebar'}\n style={\n {\n positionAnchor: `--sidebar-item-${activeIndex}`,\n } as CSSProperties\n }\n >\n <CollapseIndicatorSVG isCollapsed={isCollapsed} />\n </button>\n\n <nav\n className={twMerge(\n 'max-md:divide-y-1 p-8 max-md:h-full max-md:overflow-auto md:gap-y-20 grid overflow-hidden text-b3 font-semibold relative transition-grid-template-columns duration-300 ease-in-out',\n isCollapsed ? 'grid-cols-[48px_0fr]' : 'grid-cols-[48px_1fr]',\n )}\n >\n {items.map((item, index) => (\n <SidebarItem\n key={index}\n item={item}\n isActive={index === activeIndex}\n isCollapsed={isCollapsed}\n anchorName={`sidebar-item-${index}`}\n onItemClick={() => {\n if (item.items && item.items.length > 0) {\n setIsCollapsed((prev) => (prev ? false : prev));\n }\n }}\n />\n ))}\n </nav>\n </aside>\n\n <main className=\"w-full md:min-w-0 md:w-[calc(100%-108px)] bg-white p-24 overflow-auto md:rounded-ss-16 h-full shrink-0\">\n {children}\n </main>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SidebarLayout.js","names":[],"sources":["../../lib/ui/SidebarLayout.tsx"],"sourcesContent":["import { Icon, type IconicNames } from '@clubmed/trident-icons';\nimport clsx from 'clsx';\nimport {\n type ComponentPropsWithoutRef,\n type CSSProperties,\n type Dispatch,\n type PropsWithChildren,\n type ReactNode,\n type SetStateAction,\n type MouseEvent,\n useCallback,\n useRef,\n useState,\n} from 'react';\nimport { twMerge } from 'tailwind-merge';\nimport { HamburgerIcon } from './HamburgerIcon';\nimport { useSlots } from '@/ui/hooks/useSlots';\n\ntype SidebarSubItem = {\n label: string;\n href: string;\n} & ComponentPropsWithoutRef<'a'>;\n\ntype SidebarItem = {\n label: string;\n icon: IconicNames;\n items?: SidebarSubItem[];\n} & ((ComponentPropsWithoutRef<'a'> & { href: string }) | ComponentPropsWithoutRef<'button'>);\n\ninterface SidebarItemProps {\n item: SidebarItem;\n isActive?: boolean;\n isCollapsed?: boolean;\n anchorName: string;\n onItemClick?: () => void;\n}\n\nfunction Cta(item: PropsWithChildren<SidebarItem>) {\n if ('href' in item) {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { children, icon, label, ...attrs } = item;\n\n return (\n <a\n {...attrs}\n className=\"flex whitespace-nowrap items-center text-white hover:text-saffron transition-colors outline-none focus-visible:bg-white/20 rounded-pill relative group/sidebar-item\"\n >\n {children}\n </a>\n );\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { children, icon, label, ...attrs } = item;\n\n return (\n <button\n {...attrs}\n className=\"flex whitespace-nowrap items-center text-white outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2 rounded-pill relative group/sidebar-item\"\n >\n {children}\n </button>\n );\n}\n\nconst SidebarItem = ({\n item,\n isActive = false,\n isCollapsed = false,\n anchorName,\n onItemClick,\n}: SidebarItemProps) => {\n const hasSubItems = item.items && item.items.length > 0;\n\n return (\n <div className=\"col-start-1 col-end-3\">\n <Cta\n {...item}\n onClick={(e: MouseEvent<HTMLButtonElement | HTMLAnchorElement>) => {\n if (hasSubItems && onItemClick) {\n e.preventDefault();\n onItemClick();\n }\n }}\n style={{ anchorName: `--${anchorName}` } as CSSProperties}\n >\n <div className=\"relative\">\n <div\n className={twMerge('aspect-square w-48 rounded-full flex items-center justify-center')}\n >\n <Icon name={item.icon} className=\"w-24 text-white\" />\n </div>\n <div className=\"absolute inset-0 pointer-events-none\" aria-hidden=\"true\" inert>\n <div\n className={twMerge(\n 'max-md:hidden aspect-square w-48 rounded-full flex items-center justify-center bg-lightSand text-black',\n '[clip-path:circle(var(--sidebarItem_clipCircle))] transition-all duration-200 delay-100 ease-in-out group-focus-visible/sidebar-item:ring-4 group-focus-visible/sidebar-item:ring-black group-focus-visible/sidebar-item:ring-offset-2',\n isActive ? '[--sidebarItem_clipCircle:100%]' : '[--sidebarItem_clipCircle:0%]',\n 'group-hover/sidebar-item:[--sidebarItem_clipCircle:100%] group-focus-within/sidebar-item:[--sidebarItem_clipCircle:100%]',\n !isActive &&\n 'group-hover/sidebar-item:bg-lightSand/80 group-focus-within/sidebar-item:bg-lightSand/80',\n )}\n >\n <Icon name={item.icon} className=\"w-24\" />\n </div>\n </div>\n </div>\n <span\n className={twMerge(\n 'transition-all duration-300 ease-in-out whitespace-nowrap ps-8 pe-12 truncate md:max-w-240',\n isCollapsed ? 'opacity-0' : 'opacity-100',\n )}\n >\n {item.label}\n </span>\n </Cta>\n {hasSubItems && (\n <div\n className={twMerge(\n 'grid transition-[grid-template-rows,opacity] duration-300 ease-in-out overflow-hidden max-md:grid-rows-[1fr] max-md:opacity-100',\n isCollapsed ? 'grid-rows-[0fr] opacity-0' : 'grid-rows-[1fr] opacity-100 pb-12',\n )}\n inert={isCollapsed}\n >\n <div className=\"outline-none relative overflow-hidden flex flex-col gap-8 ps-48 font-normal\">\n {item.items!.map((subItem, subIndex) => (\n <a\n key={subIndex}\n href={subItem.href}\n className=\"text-white hover:text-saffron transition-colors text-b4 whitespace-nowrap outline-none focus-visible:bg-white/20 ps-8 pe-16 rounded-pill\"\n >\n {subItem.label}\n </a>\n ))}\n </div>\n </div>\n )}\n </div>\n );\n};\n\nexport const CollapseIndicatorSVG = ({ isCollapsed }: { isCollapsed: boolean }) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"33\" height=\"48\">\n <g fill=\"none\" fillRule=\"nonzero\">\n <path\n fill=\"#FFF\"\n d=\"M10.008 48v-1.829c0-4.116-1.722-8.045-4.75-10.835-6.331-5.837-6.331-15.835 0-21.672a14.736 14.736 0 0 0 4.75-10.835V0h27.417v48H10.008Z\"\n />\n <g\n className={twMerge(\n 'transition-transform duration-500 ease-in-out origin-center [transform-box:fill-box]',\n isCollapsed ? 'rotate-180' : 'rotate-0',\n )}\n >\n <path\n fill=\"#1E2643\"\n d=\"M19.686 12.547c6.617 0 12 5.383 12 12s-5.383 12-12 12-12-5.383-12-12 5.383-12 12-12Zm0 1.986c-5.522 0-10.015 4.492-10.015 10.014 0 5.523 4.493 10.015 10.016 10.015 5.52 0 10.012-4.492 10.012-10.015 0-5.522-4.492-10.014-10.012-10.014Zm-2.097 4.382c.376-.366 1.032-.366 1.409 0l5.037 4.895a.948.948 0 0 1 0 1.37l-5.037 4.894a1.002 1.002 0 0 1-.705.285 1 1 0 0 1-.704-.285.947.947 0 0 1 0-1.369l4.332-4.21-4.332-4.21a.95.95 0 0 1 0-1.37Z\"\n />\n </g>\n </g>\n </svg>\n);\n\nexport interface SidebarLayoutProps extends ComponentPropsWithoutRef<'div'> {\n items: SidebarItem[];\n children: ReactNode;\n activeIndex?: number;\n isCollapsed?: boolean;\n onCollapsedChange?: (isCollapsed: boolean) => void;\n}\n\nfunction useSidebarLayout({\n isCollapsed: initialCollapsedValue,\n onCollapsedChange,\n}: {\n isCollapsed?: boolean;\n onCollapsedChange?: (isCollapsed: boolean) => void;\n}) {\n const [isCollapsed, setIsCollapsedState] = useState(() => initialCollapsedValue ?? true);\n const overlayRef = useRef<HTMLDivElement>(null);\n\n const setIsCollapsed = useCallback<Dispatch<SetStateAction<boolean>>>(\n (value) => {\n setIsCollapsedState((previousState) => {\n const nextState = typeof value === 'function' ? value(previousState) : value;\n if (nextState !== previousState) {\n onCollapsedChange?.(nextState);\n }\n return nextState;\n });\n },\n [onCollapsedChange],\n );\n\n const handleToggleCollapse = useCallback(() => {\n setIsCollapsed((prev) => !prev);\n }, [setIsCollapsed]);\n\n return {\n isCollapsed,\n setIsCollapsed,\n overlayRef,\n handleToggleCollapse,\n };\n}\n\nexport const SidebarLayout = ({\n items,\n children: initialChildren,\n className,\n activeIndex = 0,\n isCollapsed: initialCollapsedValue,\n onCollapsedChange,\n ...attrs\n}: SidebarLayoutProps) => {\n const { isCollapsed, setIsCollapsed, handleToggleCollapse } = useSidebarLayout({\n isCollapsed: initialCollapsedValue,\n onCollapsedChange,\n });\n const {\n 'header-logo': headerLogo,\n header,\n ['header-actions']: headerActions,\n children,\n } = useSlots(initialChildren, ['header-logo', 'header', 'header-actions']);\n\n return (\n <div\n data-name=\"SidebarLayout\"\n className={clsx('h-screen overflow-clip flex flex-col bg-ultramarine', className)}\n {...attrs}\n >\n {/* Header */}\n <header className=\"bg-ultramarine h-64 flex items-center justify-between gap-8 p-8 ps-20 md:px-24 text-white shrink-0\">\n {headerLogo}\n {header}\n {headerActions}\n <button\n className=\"md:hidden w-48 h-48 focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2 rounded-full flex items-center justify-center text-white transition-opacity outline-none\"\n aria-label={isCollapsed ? 'Open the navigation menu' : 'Close the navigation menu'}\n onClick={handleToggleCollapse}\n >\n <HamburgerIcon isActive={!isCollapsed} />\n </button>\n </header>\n\n {/* Main container with sidebar and content */}\n <div className=\"flex h-full overflow-x-clip overflow-y-hidden\">\n {/* Sidebar */}\n <aside\n className={twMerge(\n 'z-10 max-md:absolute max-md:top-64 max-md:h-[calc(100dvh-64px)] max-md:w-screen flex bg-ultramarine px-8 md:py-16 md:ps-28 md:pe-16 text-white flex-col gap-24 transition-all duration-300 ease-in-out relative h-full shrink-0',\n isCollapsed ? 'max-md:-translate-x-full' : 'md:pe-24',\n )}\n >\n {/* Indicator */}\n <button\n className=\"hidden md:block absolute transition-top duration-300 ease-in-out rounded-pill outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2 [--indicatorYPos:anchor(top)] top-[max(var(--indicatorYPos,24px),24px)] left-[max(calc(100%_-_10px),86px)]\"\n onClick={handleToggleCollapse}\n aria-label={isCollapsed ? 'Expand sidebar' : 'Collapse sidebar'}\n style={\n {\n positionAnchor: `--sidebar-item-${activeIndex}`,\n } as CSSProperties\n }\n >\n <CollapseIndicatorSVG isCollapsed={isCollapsed} />\n </button>\n\n <nav\n className={twMerge(\n 'max-md:divide-y-1 p-8 max-md:h-full max-md:overflow-auto md:flex-1 md:min-h-0 md:overflow-y-auto md:overflow-x-hidden md:gap-y-20 grid overflow-hidden text-b3 font-semibold relative transition-grid-template-columns duration-300 ease-in-out',\n isCollapsed ? 'grid-cols-[48px_0fr]' : 'grid-cols-[48px_1fr]',\n )}\n >\n {items.map((item, index) => (\n <SidebarItem\n key={index}\n item={item}\n isActive={index === activeIndex}\n isCollapsed={isCollapsed}\n anchorName={`sidebar-item-${index}`}\n onItemClick={() => {\n if (item.items && item.items.length > 0) {\n setIsCollapsed((prev) => (prev ? false : prev));\n }\n }}\n />\n ))}\n </nav>\n </aside>\n\n <main className=\"w-full md:min-w-0 md:w-[calc(100%-108px)] bg-white p-24 overflow-auto md:rounded-ss-16 h-full shrink-0\">\n {children}\n </main>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { HTMLAttributes, PropsWithChildren } from 'react';
|
|
2
|
+
export interface SparkleWrapperProps extends HTMLAttributes<HTMLDivElement> {
|
|
3
|
+
count?: number;
|
|
4
|
+
}
|
|
5
|
+
export declare function SparkleWrapper({ children, className, count, ...props }: PropsWithChildren<SparkleWrapperProps>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { t as e } from "../chunks/clsx.js";
|
|
3
|
+
import { useLayoutEffect as t, useRef as n, useState as r } from "react";
|
|
4
|
+
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
5
|
+
import { Icon as o } from "@clubmed/trident-icons";
|
|
6
|
+
//#region lib/ui/SparkleWrapper.tsx
|
|
7
|
+
function s(e, t) {
|
|
8
|
+
return e + Math.random() * (t - e);
|
|
9
|
+
}
|
|
10
|
+
function c(e) {
|
|
11
|
+
return Array.from({ length: e }, () => ({
|
|
12
|
+
angle: Math.random() * 360,
|
|
13
|
+
distance: s(60, 110),
|
|
14
|
+
delay: Math.round(s(0, 150)),
|
|
15
|
+
size: Math.round(s(8, 20))
|
|
16
|
+
}));
|
|
17
|
+
}
|
|
18
|
+
function l(e, t, n, r) {
|
|
19
|
+
return Math.abs(e) < 1e-6 ? r / Math.abs(t) : Math.abs(t) < 1e-6 ? n / Math.abs(e) : Math.min(n / Math.abs(e), r / Math.abs(t));
|
|
20
|
+
}
|
|
21
|
+
function u({ children: u, className: d, count: f = 7, ...p }) {
|
|
22
|
+
let m = n(null), [h, g] = r([0, 0]), [_] = r(() => c(Math.round(s(f, f + 3))));
|
|
23
|
+
return t(() => {
|
|
24
|
+
if (!m.current) return;
|
|
25
|
+
let { width: e, height: t } = m.current.getBoundingClientRect();
|
|
26
|
+
g([e / 2, t / 2]);
|
|
27
|
+
}, []), /* @__PURE__ */ a("div", {
|
|
28
|
+
...p,
|
|
29
|
+
className: e("relative inline-flex items-center justify-center", d),
|
|
30
|
+
children: [_.map(({ angle: e, distance: t, delay: n, size: r }, a) => {
|
|
31
|
+
let s = e * Math.PI / 180, c = Math.cos(s), u = Math.sin(s), [d, f] = h, p = d > 0 || f > 0 ? l(c, u, d, f) : 0;
|
|
32
|
+
return /* @__PURE__ */ i(o, {
|
|
33
|
+
name: "Diamond",
|
|
34
|
+
"aria-hidden": "true",
|
|
35
|
+
className: "absolute top-1/2 left-1/2 animate-sparkle-fly pointer-events-none",
|
|
36
|
+
style: {
|
|
37
|
+
"--sparkle-start-x": `${Math.round(c * p)}px`,
|
|
38
|
+
"--sparkle-start-y": `${Math.round(u * p)}px`,
|
|
39
|
+
"--sparkle-end-x": `${Math.round(c * (p + t))}px`,
|
|
40
|
+
"--sparkle-end-y": `${Math.round(u * (p + t))}px`,
|
|
41
|
+
animationDelay: `${n}ms`,
|
|
42
|
+
width: `${r}px`,
|
|
43
|
+
height: `${r}px`
|
|
44
|
+
}
|
|
45
|
+
}, a);
|
|
46
|
+
}), /* @__PURE__ */ i("div", {
|
|
47
|
+
ref: m,
|
|
48
|
+
className: "relative z-10 animate-bounce-enter",
|
|
49
|
+
children: u
|
|
50
|
+
})]
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
export { u as SparkleWrapper };
|
|
55
|
+
|
|
56
|
+
//# sourceMappingURL=SparkleWrapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SparkleWrapper.js","names":[],"sources":["../../lib/ui/SparkleWrapper.tsx"],"sourcesContent":["'use client';\n\nimport { Icon } from '@clubmed/trident-icons';\nimport type { HTMLAttributes, PropsWithChildren } from 'react';\nimport { useLayoutEffect, useRef, useState } from 'react';\nimport clsx from 'clsx';\n\nfunction randomBetween(min: number, max: number) {\n return min + Math.random() * (max - min);\n}\n\nfunction generateSparkles(count: number) {\n return Array.from({ length: count }, () => ({\n angle: Math.random() * 360,\n distance: randomBetween(60, 110),\n delay: Math.round(randomBetween(0, 150)),\n size: Math.round(randomBetween(8, 20)),\n }));\n}\n\nfunction edgeT(cos: number, sin: number, hw: number, hh: number): number {\n if (Math.abs(cos) < 1e-6) return hh / Math.abs(sin);\n if (Math.abs(sin) < 1e-6) return hw / Math.abs(cos);\n return Math.min(hw / Math.abs(cos), hh / Math.abs(sin));\n}\n\nexport interface SparkleWrapperProps extends HTMLAttributes<HTMLDivElement> {\n count?: number;\n}\n\nexport function SparkleWrapper({\n children,\n className,\n count = 7,\n ...props\n}: PropsWithChildren<SparkleWrapperProps>) {\n const childRef = useRef<HTMLDivElement>(null);\n const [halfSize, setHalfSize] = useState<[number, number]>([0, 0]);\n const [sparkles] = useState(() => generateSparkles(Math.round(randomBetween(count, count + 3))));\n\n useLayoutEffect(() => {\n if (!childRef.current) return;\n const { width, height } = childRef.current.getBoundingClientRect();\n setHalfSize([width / 2, height / 2]);\n }, []);\n\n return (\n <div {...props} className={clsx('relative inline-flex items-center justify-center', className)}>\n {sparkles.map(({ angle, distance, delay, size }, i) => {\n const rad = (angle * Math.PI) / 180;\n const cos = Math.cos(rad);\n const sin = Math.sin(rad);\n const [hw, hh] = halfSize;\n const t = hw > 0 || hh > 0 ? edgeT(cos, sin, hw, hh) : 0;\n\n return (\n <Icon\n key={i}\n name=\"Diamond\"\n aria-hidden=\"true\"\n className=\"absolute top-1/2 left-1/2 animate-sparkle-fly pointer-events-none\"\n style={\n {\n '--sparkle-start-x': `${Math.round(cos * t)}px`,\n '--sparkle-start-y': `${Math.round(sin * t)}px`,\n '--sparkle-end-x': `${Math.round(cos * (t + distance))}px`,\n '--sparkle-end-y': `${Math.round(sin * (t + distance))}px`,\n animationDelay: `${delay}ms`,\n width: `${size}px`,\n height: `${size}px`,\n } as React.CSSProperties\n }\n />\n );\n })}\n <div ref={childRef} className=\"relative z-10 animate-bounce-enter\">\n {children}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;AAOA,SAAS,EAAc,GAAa,GAAa;AAC/C,QAAO,IAAM,KAAK,QAAQ,IAAI,IAAM;;AAGtC,SAAS,EAAiB,GAAe;AACvC,QAAO,MAAM,KAAK,EAAE,QAAQ,GAAO,SAAS;EAC1C,OAAO,KAAK,QAAQ,GAAG;EACvB,UAAU,EAAc,IAAI,IAAI;EAChC,OAAO,KAAK,MAAM,EAAc,GAAG,IAAI,CAAC;EACxC,MAAM,KAAK,MAAM,EAAc,GAAG,GAAG,CAAA;EACtC,EAAE;;AAGL,SAAS,EAAM,GAAa,GAAa,GAAY,GAAoB;AAGvE,QAFI,KAAK,IAAI,EAAI,GAAG,OAAa,IAAK,KAAK,IAAI,EAAI,GAC/C,KAAK,IAAI,EAAI,GAAG,OAAa,IAAK,KAAK,IAAI,EAAI,GAC5C,KAAK,IAAI,IAAK,KAAK,IAAI,EAAI,EAAE,IAAK,KAAK,IAAI,EAAI,CAAC;;AAOzD,SAAgB,EAAe,EAC7B,aACA,cACA,WAAQ,GACR,GAAG,KACsC;CACzC,IAAM,IAAW,EAAuB,KAAK,EACvC,CAAC,GAAU,KAAe,EAA2B,CAAC,GAAG,EAAE,CAAC,EAC5D,CAAC,KAAY,QAAe,EAAiB,KAAK,MAAM,EAAc,GAAO,IAAQ,EAAE,CAAC,CAAC,CAAC;AAQhG,QANA,QAAsB;AACpB,MAAI,CAAC,EAAS,QAAS;EACvB,IAAM,EAAE,UAAO,cAAW,EAAS,QAAQ,uBAAuB;AAClE,IAAY,CAAC,IAAQ,GAAG,IAAS,EAAE,CAAC;IACnC,EAAE,CAAC,EAGJ,kBAAC,OAAD;EAAK,GAAI;EAAO,WAAW,EAAK,oDAAoD,EAAU;YAA9F,CACG,EAAS,KAAK,EAAE,UAAO,aAAU,UAAO,WAAQ,MAAM;GACrD,IAAM,IAAO,IAAQ,KAAK,KAAM,KAC1B,IAAM,KAAK,IAAI,EAAI,EACnB,IAAM,KAAK,IAAI,EAAI,EACnB,CAAC,GAAI,KAAM,GACX,IAAI,IAAK,KAAK,IAAK,IAAI,EAAM,GAAK,GAAK,GAAI,EAAG,GAAG;AAEvD,UACE,kBAAC,GAAD;IAEE,MAAK;IACL,eAAY;IACZ,WAAU;IACV,OACE;KACE,qBAAqB,GAAG,KAAK,MAAM,IAAM,EAAE,CAAC;KAC5C,qBAAqB,GAAG,KAAK,MAAM,IAAM,EAAE,CAAC;KAC5C,mBAAmB,GAAG,KAAK,MAAM,KAAO,IAAI,GAAU,CAAC;KACvD,mBAAmB,GAAG,KAAK,MAAM,KAAO,IAAI,GAAU,CAAC;KACvD,gBAAgB,GAAG,EAAM;KACzB,OAAO,GAAG,EAAK;KACf,QAAQ,GAAG,EAAK;;IAGpB,EAfK,EAeL;IAEJ,EACF,kBAAC,OAAD;GAAK,KAAK;GAAU,WAAU;GAC3B;GACG,CAAA,CAAA"}
|