@clubmed/trident-ui 2.0.0-beta.65 → 2.0.0-beta.66

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubmed/trident-ui",
3
- "version": "2.0.0-beta.65",
3
+ "version": "2.0.0-beta.66",
4
4
  "type": "module",
5
5
  "description": "Shared ClubMed React UI components",
6
6
  "keywords": [
package/styles/index.css CHANGED
@@ -15,5 +15,4 @@
15
15
  @import './ui/controls.css';
16
16
  @import './ui/number-field.css';
17
17
  @import './ui/range.css';
18
- @import './ui/tabs.css';
19
18
  @import './ui/chat-button.css';
@@ -1,19 +0,0 @@
1
- @layer components {
2
- .tab-focus-pill {
3
- transform: translateX(
4
- calc(
5
- var(--tab-index, 0) * -100% + var(--active-tab, 0) * 100% -
6
- (var(--spacing) * 4 * (var(--tab-index) - var(--active-tab, 0)))
7
- )
8
- );
9
- }
10
-
11
- [dir='rtl'] .tab-focus-pill {
12
- transform: translateX(
13
- calc(
14
- var(--tab-index) * 100% + var(--active-tab) * -100% +
15
- (var(--spacing) * 4 * (var(--tab-index) - var(--active-tab)))
16
- )
17
- );
18
- }
19
- }
package/ui/tabs/Tabs.js CHANGED
@@ -5,22 +5,22 @@ import { TabsProvider as n } from "../contexts/TabControl.js";
5
5
  import { useActiveTab as r, useActiveTabControl as i, useRegisterTabControl as a, useTabDispatch as o, useTabsUid as s } from "../hooks/tabControl.js";
6
6
  import { useKeyboardControls as c } from "../hooks/useKeyboardControls.js";
7
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";
8
+ import { useCallback as u, useEffect as d, useMemo as f, useRef as p, useState as m } from "react";
9
+ import { jsx as h, jsxs as g } from "react/jsx-runtime";
10
10
  //#region lib/ui/tabs/Tabs.tsx
11
- var g = ({ className: e, compacted: t = !1, selected: r, children: i }) => /* @__PURE__ */ m("div", {
11
+ var _ = ({ className: e, compacted: t = !1, selected: r, children: i }) => /* @__PURE__ */ h("div", {
12
12
  className: l("flex flex-col", { "sm:gap-y-20": !t }, e),
13
13
  "data-name": "Tabs",
14
- children: /* @__PURE__ */ m(n, {
14
+ children: /* @__PURE__ */ h(n, {
15
15
  selected: r,
16
16
  children: i
17
17
  })
18
- }), _ = ({ className: e, children: t }) => /* @__PURE__ */ m("div", {
18
+ }), v = ({ className: e, children: t }) => /* @__PURE__ */ h("div", {
19
19
  "data-name": "TabsBody",
20
20
  className: l("grid grid-rows-1 overflow-hidden", e),
21
21
  children: t
22
- }), v = ({ value: e, className: t, onSelect: n, children: i, ...a }) => {
23
- let o = s(), c = f(!1), u = r() === e;
22
+ }), y = ({ value: e, className: t, onSelect: n, children: i, ...a }) => {
23
+ let o = s(), c = p(!1), u = r() === e;
24
24
  return d(() => {
25
25
  if (!c.current) {
26
26
  c.current = !0;
@@ -31,7 +31,7 @@ var g = ({ className: e, compacted: t = !1, selected: r, children: i }) => /* @_
31
31
  u,
32
32
  e,
33
33
  n
34
- ]), /* @__PURE__ */ m("div", {
34
+ ]), /* @__PURE__ */ h("div", {
35
35
  ...a,
36
36
  "aria-hidden": !u,
37
37
  className: l("relative col-start-1 row-start-1 transition-opacity duration-500", {
@@ -46,20 +46,27 @@ var g = ({ className: e, compacted: t = !1, selected: r, children: i }) => /* @_
46
46
  "aria-labelledby": `Tab_${e}_${o}`,
47
47
  children: i
48
48
  });
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(() => {
49
+ }, b = ({ className: t, constrained: n = !1, children: r }) => {
50
+ let a = p(null), o = p(null), [, c] = i(), _ = s(), [v, y] = m(!1), [b, x] = m(!1), S = `--tab-pill-${_}`, C = f(() => ({
51
+ positionAnchor: S,
52
+ top: "anchor(top)",
53
+ bottom: "anchor(bottom)",
54
+ left: "anchor(left)",
55
+ right: "anchor(right)",
56
+ position: "absolute"
57
+ }), [S]), w = u(() => {
51
58
  let e = a.current;
52
- e && (_(e.scrollLeft > 0), y(Math.ceil(e.scrollLeft) < e.scrollWidth - e.clientWidth));
59
+ e && (y(e.scrollLeft > 0), x(Math.ceil(e.scrollLeft) < e.scrollWidth - e.clientWidth));
53
60
  }, []);
54
61
  d(() => {
55
62
  let e = a.current;
56
63
  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);
64
+ w();
65
+ let t = new ResizeObserver(w);
66
+ return t.observe(e), e.addEventListener("scroll", w, { passive: !0 }), () => {
67
+ t.disconnect(), e.removeEventListener("scroll", w);
61
68
  };
62
- }, [b]), d(() => {
69
+ }, [w]), d(() => {
63
70
  if (!c?.current || !a.current) return;
64
71
  let e = o.current.offsetWidth ?? 0, t = c.current.getBoundingClientRect(), n = a.current.getBoundingClientRect();
65
72
  a.current.scrollBy({
@@ -67,81 +74,85 @@ var g = ({ className: e, compacted: t = !1, selected: r, children: i }) => /* @_
67
74
  behavior: "smooth"
68
75
  });
69
76
  }, [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;
77
+ let T = p(null), E = u(() => {
78
+ T.current !== null && (cancelAnimationFrame(T.current), T.current = null);
79
+ }, []), D = u((e) => {
80
+ if (T.current !== null) return;
74
81
  let t = () => {
75
- a.current?.scrollBy({ left: e * 6 }), x.current = requestAnimationFrame(t);
82
+ a.current?.scrollBy({ left: e * 6 }), T.current = requestAnimationFrame(t);
76
83
  };
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", {
84
+ T.current = requestAnimationFrame(t);
85
+ }, []), O = u((e) => {
86
+ (e.key === " " || e.key === "Enter") && E();
87
+ }, [E]), k = u((e) => {
88
+ (e.key === " " || e.key === "Enter") && (e.preventDefault(), D(-1));
89
+ }, [D]), A = u((e) => {
90
+ (e.key === " " || e.key === "Enter") && (e.preventDefault(), D(1));
91
+ }, [D]);
92
+ d(() => E, [E]);
93
+ let j = v || b;
94
+ return /* @__PURE__ */ g("div", {
88
95
  className: "relative",
89
96
  "data-name": "TabsHeader",
90
97
  children: [
91
- O && /* @__PURE__ */ m("div", {
98
+ j && /* @__PURE__ */ h("div", {
92
99
  className: "pointer-events-none absolute inset-y-0 left-0 z-10 flex items-center",
93
- children: /* @__PURE__ */ m(e, {
100
+ children: /* @__PURE__ */ h(e, {
94
101
  icon: "ArrowDefaultLeft",
95
102
  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,
103
+ className: l("pointer-events-auto transition-opacity duration-200 focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2", v ? "opacity-100" : "pointer-events-none opacity-0"),
104
+ onMouseDown: () => D(-1),
105
+ onMouseUp: E,
106
+ onMouseLeave: E,
107
+ onKeyDown: k,
108
+ onKeyUp: O,
109
+ inert: !v,
110
+ tabIndex: v ? 0 : -1,
104
111
  children: "Scroll tabs left"
105
112
  })
106
113
  }),
107
- /* @__PURE__ */ m("div", {
114
+ /* @__PURE__ */ h("div", {
108
115
  className: l("scrollbar-hidden relative isolate flex max-w-full overflow-x-auto py-20", t),
109
116
  ref: a,
110
- children: /* @__PURE__ */ h("div", {
117
+ children: /* @__PURE__ */ g("div", {
111
118
  role: "tablist",
112
- className: "flex flex-row",
113
- style: D,
119
+ className: "relative flex flex-row",
114
120
  children: [
115
- /* @__PURE__ */ m("div", {
121
+ /* @__PURE__ */ h("span", {
122
+ "aria-hidden": "true",
123
+ className: "pointer-events-none absolute rounded-pill bg-black transition-all duration-300",
124
+ style: C
125
+ }),
126
+ /* @__PURE__ */ h("div", {
116
127
  ref: o,
117
128
  className: l("shrink-0", { "w-16 lg:w-[max(calc((100vw-1212px)/2),116px)] xl:w-[max(calc((100vw-1212px)/2),156px)]": !n })
118
129
  }),
119
130
  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 }) })
131
+ /* @__PURE__ */ h("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
132
  ]
122
133
  })
123
134
  }),
124
- O && /* @__PURE__ */ m("div", {
135
+ j && /* @__PURE__ */ h("div", {
125
136
  className: "pointer-events-none absolute inset-y-0 right-0 z-10 flex items-center",
126
- children: /* @__PURE__ */ m(e, {
137
+ children: /* @__PURE__ */ h(e, {
127
138
  icon: "ArrowDefaultRight",
128
139
  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,
140
+ className: l("pointer-events-auto transition-opacity duration-200 focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2", b ? "opacity-100" : "pointer-events-none opacity-0"),
141
+ onMouseDown: () => D(1),
142
+ onMouseUp: E,
143
+ onMouseLeave: E,
144
+ onKeyDown: A,
145
+ onKeyUp: O,
146
+ inert: !b,
147
+ tabIndex: b ? 0 : -1,
137
148
  children: "Scroll tabs right"
138
149
  })
139
150
  })
140
151
  ]
141
152
  });
142
- }, b = ({ items: e, className: n, "aria-label": i = "Select tab" }) => {
153
+ }, x = ({ items: e, className: n, "aria-label": i = "Select tab" }) => {
143
154
  let a = r(), s = o();
144
- return /* @__PURE__ */ m(t, {
155
+ return /* @__PURE__ */ h(t, {
145
156
  value: (a === -1 ? void 0 : a)?.toString(),
146
157
  onChange: (e, t) => {
147
158
  s({
@@ -151,18 +162,18 @@ var g = ({ className: e, compacted: t = !1, selected: r, children: i }) => /* @_
151
162
  },
152
163
  name: i,
153
164
  className: l(n),
154
- children: e.map((e) => /* @__PURE__ */ m("option", {
165
+ children: e.map((e) => /* @__PURE__ */ h("option", {
155
166
  value: e.value,
156
167
  children: e.label
157
168
  }, e.value))
158
169
  });
159
- }, x = ({ as: e = "h2", label: t, children: n, value: i, onSelect: o, className: p, notification: g, ..._ }) => {
160
- let v = f(null), y = r(), b = s(), x = a({
170
+ }, S = ({ as: e = "h2", label: t, children: n, value: i, onSelect: o, className: f, notification: m, ..._ }) => {
171
+ let v = p(null), y = r(), b = s(), x = a({
161
172
  value: i,
162
173
  ref: v
163
- }), S = f(y), C = f(y === i);
174
+ }), S = p(y), C = p(y === i);
164
175
  C.current = y === i;
165
- let w = { "--tab-index": i };
176
+ let w = `--tab-pill-${b}`;
166
177
  d(() => {
167
178
  S.current !== y && i === y && (v.current?.focus(), o?.({
168
179
  value: i,
@@ -206,7 +217,7 @@ var g = ({ className: e, compacted: t = !1, selected: r, children: i }) => /* @_
206
217
  payload: i
207
218
  });
208
219
  };
209
- return /* @__PURE__ */ m("div", {
220
+ return /* @__PURE__ */ h("div", {
210
221
  ..._,
211
222
  id: _.id || `Tab_${i}_${b}`,
212
223
  "data-name": "Tab",
@@ -215,30 +226,29 @@ var g = ({ className: e, compacted: t = !1, selected: r, children: i }) => /* @_
215
226
  "aria-selected": C.current,
216
227
  "aria-controls": `TabPanel_${i}_${b}`,
217
228
  tabIndex: C.current ? 0 : -1,
218
- className: l("group/tab text-b3 inline-block cursor-pointer overflow-hidden bg-transparent pe-4 align-middle font-semibold whitespace-nowrap outline-none", {
229
+ className: l("group/tab text-b3 inline-block cursor-pointer overflow-hidden bg-transparent align-middle font-semibold whitespace-nowrap outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2 rounded-pill", {
219
230
  "text-white": C.current,
220
231
  "text-black": !C.current,
221
- "transition-transform active:scale-[1.03]": C.current
232
+ "transition-transform active:scale-[1.03]": C.current,
233
+ "[@supports(not(anchor-name:--x))]:bg-black": C.current
222
234
  }),
223
235
  onKeyDown: E,
224
236
  onClick: D,
225
237
  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/tab:before:bg-(--color-pearl) group-hover/tab:before:bg-(--color-pearl) group/tab-focus:before:bg-(--color-pearl)", p),
227
- ...C ? {} : { role: "presentation" },
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"),
230
- style: w
231
- }), /* @__PURE__ */ h("span", {
238
+ className: l("relative inline-block px-20 py-12", !C.current && "before:rounded-pill before:transition-colors/opacity before:absolute before:inset-0 before:-z-1 before:duration-300 group-focus-within/tab:before:bg-(--color-pearl) group-hover/tab:before:bg-(--color-pearl) group/tab-focus:before:bg-(--color-pearl)", f),
239
+ style: C.current ? { anchorName: w } : void 0,
240
+ ...C.current ? {} : { role: "presentation" },
241
+ children: /* @__PURE__ */ g("span", {
232
242
  className: "flex items-center gap-8",
233
- children: [n ?? t, g && /* @__PURE__ */ m("div", {
243
+ children: [n ?? t, m && /* @__PURE__ */ h("div", {
234
244
  className: l("size-18 flex items-center justify-center text-b5 rounded-full bg-saffron text-black transition-colors duration-300", { "group-focus-within/tab:bg-darkGrey group-hover/tab:bg-darkGrey group-focus-within/tab:text-pearl group-hover/tab:text-pearl": !C.current }),
235
- children: g
245
+ children: m
236
246
  })]
237
- })]
247
+ })
238
248
  })
239
249
  });
240
250
  };
241
251
  //#endregion
242
- export { x as Tab, y as TabList, v as TabPanel, b as TabSelect, g as Tabs, _ as TabsBody };
252
+ export { S as Tab, b as TabList, y as TabPanel, x as TabSelect, _ as Tabs, v as TabsBody };
243
253
 
244
254
  //# sourceMappingURL=Tabs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Tabs.js","names":[],"sources":["../../../lib/ui/tabs/Tabs.tsx"],"sourcesContent":["'use client';\n\nimport {\n type ComponentProps,\n type ComponentPropsWithoutRef,\n type CSSProperties,\n type FunctionComponent,\n type PropsWithChildren,\n type RefObject,\n type KeyboardEvent,\n type ReactNode,\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 * Notification content\n */\n notification?: ReactNode;\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 notification,\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/tab 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/tab:before:bg-(--color-pearl) group-hover/tab:before:bg-(--color-pearl) group/tab-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 <span className=\"flex items-center gap-8\">\n {children ?? label}\n {notification && (\n <div\n className={cn(\n 'size-18 flex items-center justify-center text-b5 rounded-full bg-saffron text-black transition-colors duration-300',\n {\n 'group-focus-within/tab:bg-darkGrey group-hover/tab:bg-darkGrey group-focus-within/tab:text-pearl group-hover/tab:text-pearl':\n !isActive.current,\n },\n )}\n >\n {notification}\n </div>\n )}\n </span>\n </TagName>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"Tabs.js","names":[],"sources":["../../../lib/ui/tabs/Tabs.tsx"],"sourcesContent":["'use client';\n\nimport {\n type ComponentProps,\n type ComponentPropsWithoutRef,\n type CSSProperties,\n type FunctionComponent,\n type PropsWithChildren,\n type RefObject,\n type KeyboardEvent,\n type ReactNode,\n useCallback,\n useEffect,\n useRef,\n useState,\n useMemo,\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 [, currentControl] = useActiveTabControl();\n const uid = useTabsUid();\n const [canScrollLeft, setCanScrollLeft] = useState(false);\n const [canScrollRight, setCanScrollRight] = useState(false);\n\n const anchorName = `--tab-pill-${uid}` as CSSProperties['anchorName'];\n\n const pillStyle = useMemo<CSSProperties>(\n () => ({\n positionAnchor: anchorName,\n top: 'anchor(top)',\n bottom: 'anchor(bottom)',\n left: 'anchor(left)',\n right: 'anchor(right)',\n position: 'absolute',\n }),\n [anchorName],\n );\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 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-4 focus-visible:ring-black focus-visible:ring-offset-2',\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=\"relative flex flex-row\">\n <span\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute rounded-pill bg-black transition-all duration-300\"\n style={pillStyle}\n />\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-4 focus-visible:ring-black focus-visible:ring-offset-2',\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 * Notification content\n */\n notification?: ReactNode;\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 notification,\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 anchorName = `--tab-pill-${uid}` as CSSProperties['anchorName'];\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/tab text-b3 inline-block cursor-pointer overflow-hidden bg-transparent align-middle font-semibold whitespace-nowrap outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2 rounded-pill',\n {\n 'text-white': isActive.current,\n 'text-black': !isActive.current,\n 'transition-transform active:scale-[1.03]': isActive.current,\n '[@supports(not(anchor-name:--x))]:bg-black': 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 !isActive.current &&\n 'before:rounded-pill before:transition-colors/opacity before:absolute before:inset-0 before:-z-1 before:duration-300 group-focus-within/tab:before:bg-(--color-pearl) group-hover/tab:before:bg-(--color-pearl) group/tab-focus:before:bg-(--color-pearl)',\n className,\n )}\n style={isActive.current ? { anchorName } : undefined}\n {...(!isActive.current ? { role: 'presentation' } : {})}\n >\n <span className=\"flex items-center gap-8\">\n {children ?? label}\n {notification && (\n <div\n className={cn(\n 'size-18 flex items-center justify-center text-b5 rounded-full bg-saffron text-black transition-colors duration-300',\n {\n 'group-focus-within/tab:bg-darkGrey group-hover/tab:bg-darkGrey group-focus-within/tab:text-pearl group-hover/tab:text-pearl':\n !isActive.current,\n },\n )}\n >\n {notification}\n </div>\n )}\n </span>\n </TagName>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;AAyCA,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,GAAG,KAAkB,GAAqB,EAC1C,IAAM,GAAY,EAClB,CAAC,GAAe,KAAoB,EAAS,GAAM,EACnD,CAAC,GAAgB,KAAqB,EAAS,GAAM,EAErD,IAAa,cAAc,KAE3B,IAAY,SACT;EACL,gBAAgB;EAChB,KAAK;EACL,QAAQ;EACR,MAAM;EACN,OAAO;EACP,UAAU;EACX,GACD,CAAC,EAAW,CACb,EAEK,IAAoB,QAAkB;EAC1C,IAAM,IAAK,EAAI;AACV,QACL,EAAiB,EAAG,aAAa,EAAE,EACnC,EAAkB,KAAK,KAAK,EAAG,WAAW,GAAG,EAAG,cAAc,EAAG,YAAY;IAC5E,EAAE,CAAC;AAkBN,CAhBA,QAAgB;EACd,IAAM,IAAK,EAAI;AACf,MAAI,CAAC,EAAI;AAET,KAAmB;EAEnB,IAAM,IAAW,IAAI,eAAe,EAAkB;AAItD,SAHA,EAAS,QAAQ,EAAG,EACpB,EAAG,iBAAiB,UAAU,GAAmB,EAAE,SAAS,IAAM,CAAC,QAEtD;AAEX,GADA,EAAS,YAAY,EACrB,EAAG,oBAAoB,UAAU,EAAkB;;IAEpD,CAAC,EAAkB,CAAC,EAEvB,QAAgB;AACd,MAAI,CAAC,GAAgB,WAAW,CAAC,EAAI,QACnC;EAGF,IAAM,IAAc,EAAU,QAAS,eAAe,GAChD,IAAc,EAAe,QAAQ,uBAAuB,EAC5D,IAAgB,EAAI,QAAS,uBAAuB;AAE1D,IAAI,QAAQ,SAAS;GACnB,MAAM,EAAY,OAAO,EAAc,OAAO;GAC9C,UAAU;GACX,CAAC;IACD,CAAC,EAAe,CAAC;CAEpB,IAAM,IAAe,EAAsB,KAAK,EAE1C,IAAa,QAAkB;AACnC,EAAI,EAAa,YAAY,SAC3B,qBAAqB,EAAa,QAAQ,EAC1C,EAAa,UAAU;IAExB,EAAE,CAAC,EAEA,IAAc,GAAa,MAAsB;AACrD,MAAI,EAAa,YAAY,KAAM;EACnC,IAAM,UAAa;AAEjB,GADA,EAAI,SAAS,SAAS,EAAE,MAAM,IAAY,GAAG,CAAC,EAC9C,EAAa,UAAU,sBAAsB,EAAK;;AAEpD,IAAa,UAAU,sBAAsB,EAAK;IACjD,EAAE,CAAC,EAEA,IAAoB,GACvB,MAAwC;AACvC,GAAI,EAAE,QAAQ,OAAO,EAAE,QAAQ,YAAS,GAAY;IAEtD,CAAC,EAAW,CACb,EAEK,IAA0B,GAC7B,MAAwC;AACvC,GAAI,EAAE,QAAQ,OAAO,EAAE,QAAQ,aAC7B,EAAE,gBAAgB,EAClB,EAAY,GAAG;IAGnB,CAAC,EAAY,CACd,EAEK,IAA2B,GAC9B,MAAwC;AACvC,GAAI,EAAE,QAAQ,OAAO,EAAE,QAAQ,aAC7B,EAAE,gBAAgB,EAClB,EAAY,EAAE;IAGlB,CAAC,EAAY,CACd;AAED,SAAgB,GAAY,CAAC,EAAW,CAAC;CAEzC,IAAM,IAAe,KAAiB;AAEtC,QACE,kBAAC,OAAD;EAAK,WAAU;EAAW,aAAU;YAApC;GACG,KACC,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KACE,MAAK;KACL,MAAK;KACL,WAAW,EACT,iIACA,IAAgB,gBAAgB,gCACjC;KACD,mBAAmB,EAAY,GAAG;KAClC,WAAW;KACX,cAAc;KACd,WAAW;KACX,SAAS;KACT,OAAO,CAAC;KACR,UAAU,IAAgB,IAAI;eAC/B;KAEa,CAAA;IACV,CAAA;GAER,kBAAC,OAAD;IACE,WAAW,EACT,2EACA,EACD;IACI;cAEL,kBAAC,OAAD;KAAK,MAAK;KAAU,WAAU;eAA9B;MACE,kBAAC,QAAD;OACE,eAAY;OACZ,WAAU;OACV,OAAO;OACP,CAAA;MACF,kBAAC,OAAD;OACE,KAAK;OACL,WAAW,EAAG,YAAY,EACxB,0FACE,CAAC,GACJ,CAAA;OACD,CAAA;MACD;MACD,kBAAC,OAAD,EACE,WAAW,EAAG,YAAY,EACxB,0FACE,CAAC,GACJ,CAAC,EACF,CAAA;;;IAEA,CAAA;GACL,KACC,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KACE,MAAK;KACL,MAAK;KACL,WAAW,EACT,iIACA,IAAiB,gBAAgB,gCAClC;KACD,mBAAmB,EAAY,EAAE;KACjC,WAAW;KACX,cAAc;KACd,WAAW;KACX,SAAS;KACT,OAAO,CAAC;KACR,UAAU,IAAiB,IAAI;eAChC;KAEa,CAAA;IACV,CAAA;;;GAYD,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;GA6BA,KAAuD,EAClE,IAAI,IAAU,MACd,UACA,aACA,UACA,aACA,cACA,iBACA,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,IAAa,cAAc;AAEjC,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,iOACA;GACE,cAAc,EAAS;GACvB,cAAc,CAAC,EAAS;GACxB,4CAA4C,EAAS;GACrD,8CAA8C,EAAS;GACxD,CACF;EACD,WAAW;EACX,SAAS;YAET,kBAAC,GAAD;GACE,WAAW,EACT,qCACA,CAAC,EAAS,WACR,4PACF,EACD;GACD,OAAO,EAAS,UAAU,EAAE,eAAY,GAAG,KAAA;GAC3C,GAAM,EAAS,UAAqC,EAAE,GAA7B,EAAE,MAAM,gBAAgB;aAEjD,kBAAC,QAAD;IAAM,WAAU;cAAhB,CACG,KAAY,GACZ,KACC,kBAAC,OAAD;KACE,WAAW,EACT,sHACA,EACE,+HACE,CAAC,EAAS,SACb,CACF;eAEA;KACG,CAAA,CAAA;;GAGF,CAAA;EACN,CAAA"}