@mshafiqyajid/react-tabs 0.2.0 → 0.3.0

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/dist/styled.cjs CHANGED
@@ -183,10 +183,23 @@ var TabsStyled = react.forwardRef(
183
183
  orientation = "horizontal",
184
184
  lazyMount = false,
185
185
  forceMount = false,
186
+ onTabClose,
187
+ scrollable = false,
188
+ sortable = false,
189
+ onReorder,
190
+ scrollActiveIntoView,
186
191
  renderTab,
187
192
  renderPanel,
188
193
  className
189
194
  }, ref) {
195
+ const autoScrollActive = scrollActiveIntoView ?? scrollable;
196
+ const [scrollState, setScrollState] = react.useState({
197
+ left: false,
198
+ right: false
199
+ });
200
+ const typeaheadBufferRef = react.useRef("");
201
+ const typeaheadTimerRef = react.useRef(null);
202
+ const dragValueRef = react.useRef(null);
190
203
  const tabDefs = tabs.map((t) => ({ value: t.value, disabled: t.disabled }));
191
204
  const resolvedDefault = defaultValue ?? tabs.find((t) => !t.disabled)?.value;
192
205
  const { activeValue, getTabProps, getPanelProps } = useTabs({
@@ -202,43 +215,169 @@ var TabsStyled = react.forwardRef(
202
215
  const activatedRef = react.useRef(/* @__PURE__ */ new Set());
203
216
  if (activeValue !== void 0) activatedRef.current.add(activeValue);
204
217
  const rootClass = ["rtab-root", className].filter(Boolean).join(" ");
218
+ react.useEffect(() => {
219
+ if (!scrollable) return;
220
+ const list = tabListRef.current;
221
+ if (!list) return;
222
+ const update = () => {
223
+ setScrollState({
224
+ left: list.scrollLeft > 4,
225
+ right: list.scrollLeft + list.clientWidth < list.scrollWidth - 4
226
+ });
227
+ };
228
+ update();
229
+ list.addEventListener("scroll", update, { passive: true });
230
+ const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(update) : null;
231
+ ro?.observe(list);
232
+ return () => {
233
+ list.removeEventListener("scroll", update);
234
+ ro?.disconnect();
235
+ };
236
+ }, [scrollable, tabs.length]);
237
+ react.useEffect(() => {
238
+ if (!autoScrollActive || !activeValue) return;
239
+ const list = tabListRef.current;
240
+ if (!list) return;
241
+ const activeEl = list.querySelector(`[aria-selected="true"]`);
242
+ if (!activeEl) return;
243
+ activeEl.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "nearest" });
244
+ }, [autoScrollActive, activeValue]);
245
+ const scrollByAmount = (delta) => {
246
+ tabListRef.current?.scrollBy({ left: delta, behavior: "smooth" });
247
+ };
248
+ const handleTypeahead = (e) => {
249
+ if (e.key.length !== 1 || e.altKey || e.ctrlKey || e.metaKey) return;
250
+ typeaheadBufferRef.current = (typeaheadBufferRef.current + e.key).toLowerCase();
251
+ if (typeaheadTimerRef.current) clearTimeout(typeaheadTimerRef.current);
252
+ typeaheadTimerRef.current = setTimeout(() => {
253
+ typeaheadBufferRef.current = "";
254
+ }, 600);
255
+ const buffer = typeaheadBufferRef.current;
256
+ const startIdx = Math.max(0, tabs.findIndex((t) => t.value === activeValue));
257
+ for (let i = 1; i <= tabs.length; i++) {
258
+ const idx = (startIdx + i) % tabs.length;
259
+ const tab = tabs[idx];
260
+ if (!tab || tab.disabled) continue;
261
+ const labelStr = typeof tab.label === "string" ? tab.label : "";
262
+ if (labelStr.toLowerCase().startsWith(buffer)) {
263
+ const list = tabListRef.current;
264
+ const btn = list?.querySelector(`[data-value="${tab.value}"]`);
265
+ btn?.click();
266
+ btn?.focus();
267
+ break;
268
+ }
269
+ }
270
+ };
271
+ const handleDragStart = (val) => (e) => {
272
+ if (!sortable) return;
273
+ dragValueRef.current = val;
274
+ e.dataTransfer.effectAllowed = "move";
275
+ };
276
+ const handleDragOver = (val) => (e) => {
277
+ if (!sortable || !dragValueRef.current || dragValueRef.current === val) return;
278
+ e.preventDefault();
279
+ e.dataTransfer.dropEffect = "move";
280
+ };
281
+ const handleDrop = (val) => (e) => {
282
+ if (!sortable) return;
283
+ e.preventDefault();
284
+ const from = dragValueRef.current;
285
+ dragValueRef.current = null;
286
+ if (!from || from === val || !onReorder) return;
287
+ const order = tabs.map((t) => t.value);
288
+ const fromIdx = order.indexOf(from);
289
+ const toIdx = order.indexOf(val);
290
+ if (fromIdx === -1 || toIdx === -1) return;
291
+ const next = [...order];
292
+ next.splice(fromIdx, 1);
293
+ next.splice(toIdx, 0, from);
294
+ onReorder(next);
295
+ };
205
296
  return /* @__PURE__ */ jsxRuntime.jsxs(
206
297
  "div",
207
298
  {
208
299
  ref,
209
300
  className: rootClass,
210
301
  "data-orientation": orientation,
302
+ "data-scrollable": scrollable || void 0,
303
+ onKeyDown: handleTypeahead,
211
304
  children: [
212
- /* @__PURE__ */ jsxRuntime.jsxs(
213
- "div",
214
- {
215
- ref: tabListRef,
216
- role: "tablist",
217
- "aria-orientation": orientation,
218
- className: "rtab-list",
219
- "data-variant": variant,
220
- "data-size": size,
221
- "data-tone": tone,
222
- "data-orientation": orientation,
223
- style: indicatorStyle,
224
- children: [
225
- tabs.map((tab, index) => {
226
- const isActive = activeValue === tab.value;
227
- const isDisabled = !!tab.disabled;
228
- return /* @__PURE__ */ jsxRuntime.jsx(
229
- "button",
230
- {
231
- className: "rtab-tab",
232
- ...getTabProps(tab.value, { disabled: tab.disabled }),
233
- children: renderTab ? renderTab({ tab, index, isActive, isDisabled }) : tab.label
234
- },
235
- tab.value
236
- );
237
- }),
238
- (variant === "line" || variant === "solid" || variant === "pill") && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rtab-indicator", "aria-hidden": "true" })
239
- ]
240
- }
241
- ),
305
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rtab-list-wrap", children: [
306
+ scrollable && scrollState.left && /* @__PURE__ */ jsxRuntime.jsx(
307
+ "button",
308
+ {
309
+ type: "button",
310
+ className: "rtab-scroll-btn rtab-scroll-btn--left",
311
+ "aria-label": "Scroll left",
312
+ onClick: () => scrollByAmount(-200),
313
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 12 12", width: "12", height: "12", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7.5 3l-3 3 3 3" }) })
314
+ }
315
+ ),
316
+ /* @__PURE__ */ jsxRuntime.jsxs(
317
+ "div",
318
+ {
319
+ ref: tabListRef,
320
+ role: "tablist",
321
+ "aria-orientation": orientation,
322
+ className: "rtab-list",
323
+ "data-variant": variant,
324
+ "data-size": size,
325
+ "data-tone": tone,
326
+ "data-orientation": orientation,
327
+ "data-scrollable": scrollable || void 0,
328
+ style: indicatorStyle,
329
+ children: [
330
+ tabs.map((tab, index) => {
331
+ const isActive = activeValue === tab.value;
332
+ const isDisabled = !!tab.disabled;
333
+ const tabProps = getTabProps(tab.value, { disabled: tab.disabled });
334
+ return /* @__PURE__ */ jsxRuntime.jsxs(
335
+ "button",
336
+ {
337
+ "data-value": tab.value,
338
+ "data-closable": tab.closable || void 0,
339
+ className: "rtab-tab",
340
+ ...tabProps,
341
+ draggable: sortable && !isDisabled ? true : void 0,
342
+ onDragStart: sortable ? handleDragStart(tab.value) : void 0,
343
+ onDragOver: sortable ? handleDragOver(tab.value) : void 0,
344
+ onDrop: sortable ? handleDrop(tab.value) : void 0,
345
+ children: [
346
+ renderTab ? renderTab({ tab, index, isActive, isDisabled }) : tab.label,
347
+ tab.closable && /* @__PURE__ */ jsxRuntime.jsx(
348
+ "span",
349
+ {
350
+ className: "rtab-close",
351
+ role: "button",
352
+ tabIndex: -1,
353
+ "aria-label": `Close ${typeof tab.label === "string" ? tab.label : "tab"}`,
354
+ onClick: (e) => {
355
+ e.stopPropagation();
356
+ onTabClose?.(tab.value);
357
+ },
358
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 12 12", width: "10", height: "10", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 3l6 6M9 3l-6 6" }) })
359
+ }
360
+ )
361
+ ]
362
+ },
363
+ tab.value
364
+ );
365
+ }),
366
+ (variant === "line" || variant === "solid" || variant === "pill") && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rtab-indicator", "aria-hidden": "true" })
367
+ ]
368
+ }
369
+ ),
370
+ scrollable && scrollState.right && /* @__PURE__ */ jsxRuntime.jsx(
371
+ "button",
372
+ {
373
+ type: "button",
374
+ className: "rtab-scroll-btn rtab-scroll-btn--right",
375
+ "aria-label": "Scroll right",
376
+ onClick: () => scrollByAmount(200),
377
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 12 12", width: "12", height: "12", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4.5 3l3 3-3 3" }) })
378
+ }
379
+ )
380
+ ] }),
242
381
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rtab-panels", children: tabs.map((tab) => {
243
382
  const isActive = activeValue === tab.value;
244
383
  const shouldRender = forceMount || !lazyMount || activatedRef.current.has(tab.value);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/useTabs.ts","../src/styled/TabsStyled.tsx"],"names":["useRef","useState","useCallback","useMemo","useLayoutEffect","useEffect","forwardRef","TabsStyled","jsxs","jsx"],"mappings":"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","file":"styled.cjs","sourcesContent":["import {\n type HTMLAttributes,\n type KeyboardEvent,\n useCallback,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nexport type TabsActivation = \"automatic\" | \"manual\";\nexport type TabsOrientation = \"horizontal\" | \"vertical\";\nexport type TabsChangeReason = \"click\" | \"keyboard\" | \"programmatic\";\n\nexport interface UseTabsTab {\n value: string;\n disabled?: boolean;\n}\n\nexport interface UseTabsOptions {\n /** Tab definitions — needed for keyboard navigation between tabs. */\n tabs?: UseTabsTab[];\n /** Controlled active value. */\n value?: string;\n /** Initial active value when uncontrolled. */\n defaultValue?: string;\n /** Called when the active tab changes. Optional second arg reports the trigger reason. */\n onChange?: (value: string, reason: TabsChangeReason) => void;\n /** \"automatic\" (default) — arrow keys move focus AND activate. \"manual\" — arrows move focus only; Enter/Space activates. */\n activation?: TabsActivation;\n /** Affects keyboard nav. Default \"horizontal\". */\n orientation?: TabsOrientation;\n}\n\nexport interface TabProps extends HTMLAttributes<HTMLButtonElement> {\n id: string;\n role: \"tab\";\n \"aria-selected\": boolean;\n \"aria-controls\": string;\n \"aria-disabled\"?: boolean;\n \"data-state\": \"active\" | \"inactive\";\n tabIndex: number;\n}\n\nexport interface PanelProps extends HTMLAttributes<HTMLDivElement> {\n id: string;\n role: \"tabpanel\";\n \"aria-labelledby\": string;\n \"data-state\": \"active\" | \"inactive\";\n hidden: boolean;\n}\n\nexport interface UseTabsResult {\n /** Currently active tab value. */\n activeValue: string | undefined;\n /** Returns props to spread onto a tab trigger `<button>`. */\n getTabProps: (value: string, options?: { disabled?: boolean }) => TabProps;\n /** Returns props to spread onto a tab panel. */\n getPanelProps: (value: string) => PanelProps;\n /** Programmatically set the active tab. */\n setActiveValue: (value: string) => void;\n}\n\nconst tabId = (listId: string, value: string) => `${listId}-tab-${value}`;\nconst panelId = (listId: string, value: string) => `${listId}-panel-${value}`;\n\nlet counter = 0;\nfunction useStableId() {\n const ref = useRef<string | null>(null);\n if (ref.current === null) {\n ref.current = `rtabs-${++counter}`;\n }\n return ref.current;\n}\n\nexport function useTabs(opts: UseTabsOptions = {}): UseTabsResult {\n const {\n tabs = [],\n value: controlledValue,\n defaultValue,\n onChange,\n activation = \"automatic\",\n orientation = \"horizontal\",\n } = opts;\n\n const listId = useStableId();\n const isControlled = controlledValue !== undefined;\n\n const [internalValue, setInternalValue] = useState<string | undefined>(\n defaultValue,\n );\n\n const activeValue = isControlled ? controlledValue : internalValue;\n\n const tabRefs = useRef<Map<string, HTMLButtonElement>>(new Map());\n const onChangeRef = useRef(onChange);\n onChangeRef.current = onChange;\n\n const setActiveValue = useCallback(\n (next: string, reason: TabsChangeReason = \"programmatic\") => {\n const tab = tabs.find((t) => t.value === next);\n if (tab?.disabled) return;\n if (isControlled) {\n if (next !== controlledValue) onChangeRef.current?.(next, reason);\n } else {\n if (next === internalValue) return;\n setInternalValue(next);\n onChangeRef.current?.(next, reason);\n }\n },\n [tabs, isControlled, controlledValue, internalValue],\n );\n\n const focusTab = useCallback((value: string) => {\n tabRefs.current.get(value)?.focus();\n }, []);\n\n const handleKeyDown = useCallback(\n (currentValue: string) =>\n (event: KeyboardEvent<HTMLButtonElement>) => {\n const enabled = tabs.filter((t) => !t.disabled);\n if (enabled.length === 0) return;\n\n const currentIndex = enabled.findIndex((t) => t.value === currentValue);\n\n const isForward =\n orientation === \"vertical\"\n ? event.key === \"ArrowDown\"\n : event.key === \"ArrowRight\";\n const isBackward =\n orientation === \"vertical\"\n ? event.key === \"ArrowUp\"\n : event.key === \"ArrowLeft\";\n\n let nextValue: string | undefined;\n\n if (isForward) {\n const nextIndex = (currentIndex + 1) % enabled.length;\n nextValue = enabled[nextIndex]?.value;\n } else if (isBackward) {\n const prevIndex = (currentIndex - 1 + enabled.length) % enabled.length;\n nextValue = enabled[prevIndex]?.value;\n } else if (event.key === \"Home\") {\n nextValue = enabled[0]?.value;\n } else if (event.key === \"End\") {\n nextValue = enabled[enabled.length - 1]?.value;\n } else if (\n activation === \"manual\" &&\n (event.key === \"Enter\" || event.key === \" \")\n ) {\n event.preventDefault();\n setActiveValue(currentValue, \"keyboard\");\n return;\n } else {\n return;\n }\n\n if (nextValue === undefined || nextValue === currentValue) return;\n event.preventDefault();\n focusTab(nextValue);\n if (activation === \"automatic\") {\n setActiveValue(nextValue, \"keyboard\");\n }\n },\n [tabs, orientation, activation, setActiveValue, focusTab],\n );\n\n const getTabProps = useCallback(\n (value: string, options?: { disabled?: boolean }): TabProps => {\n const isDisabled =\n options?.disabled ?? tabs.find((t) => t.value === value)?.disabled;\n const isSelected = activeValue === value;\n return {\n id: tabId(listId, value),\n role: \"tab\",\n \"aria-selected\": isSelected,\n \"aria-controls\": panelId(listId, value),\n \"aria-disabled\": isDisabled || undefined,\n \"data-state\": isSelected ? \"active\" : \"inactive\",\n tabIndex: isSelected ? 0 : -1,\n disabled: isDisabled,\n ref: (node: HTMLButtonElement | null) => {\n if (node) {\n tabRefs.current.set(value, node);\n } else {\n tabRefs.current.delete(value);\n }\n },\n onClick: () => {\n if (!isDisabled) setActiveValue(value, \"click\");\n },\n onKeyDown: handleKeyDown(value),\n } as TabProps & {\n disabled: boolean | undefined;\n ref: (node: HTMLButtonElement | null) => void;\n };\n },\n [activeValue, listId, tabs, setActiveValue, handleKeyDown],\n );\n\n const getPanelProps = useCallback(\n (value: string): PanelProps => {\n const isActive = activeValue === value;\n return {\n id: panelId(listId, value),\n role: \"tabpanel\",\n \"aria-labelledby\": tabId(listId, value),\n \"data-state\": isActive ? \"active\" : \"inactive\",\n hidden: !isActive,\n tabIndex: 0,\n };\n },\n [activeValue, listId],\n );\n\n return useMemo(\n () => ({ activeValue, getTabProps, getPanelProps, setActiveValue }),\n [activeValue, getTabProps, getPanelProps, setActiveValue],\n );\n}\n","import {\n type CSSProperties,\n type ReactNode,\n forwardRef,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from \"react\";\nimport {\n useTabs,\n type TabsActivation,\n type TabsChangeReason,\n type TabsOrientation,\n} from \"../useTabs\";\n\nexport type TabsVariant = \"line\" | \"solid\" | \"pill\";\nexport type TabsSize = \"sm\" | \"md\" | \"lg\";\nexport type TabsTone = \"neutral\" | \"primary\" | \"success\" | \"danger\";\n\nexport interface TabItem {\n value: string;\n label: ReactNode;\n content: ReactNode;\n disabled?: boolean;\n}\n\nexport interface TabsRenderTabContext {\n tab: TabItem;\n index: number;\n isActive: boolean;\n isDisabled: boolean;\n}\n\nexport interface TabsRenderPanelContext {\n tab: TabItem;\n isActive: boolean;\n}\n\nexport interface TabsStyledProps {\n tabs: TabItem[];\n variant?: TabsVariant;\n size?: TabsSize;\n tone?: TabsTone;\n defaultValue?: string;\n value?: string;\n /** Optional second arg reports the trigger reason (\"click\" | \"keyboard\" | \"programmatic\"). */\n onChange?: (value: string, reason: TabsChangeReason) => void;\n /** \"automatic\" (default) — arrow keys move focus AND activate. \"manual\" — arrows move focus only. */\n activation?: TabsActivation;\n /** Affects keyboard nav direction. Default \"horizontal\". */\n orientation?: TabsOrientation;\n /** Only mount panels after they've been activated at least once. Default: false (all panels mount eagerly). */\n lazyMount?: boolean;\n /** Keep all panels mounted regardless of activation (useful with `lazyMount` overrides). Default: false. */\n forceMount?: boolean;\n /** Replace the default tab button content. The button shell (a11y, ref, key) stays owned by the component. */\n renderTab?: (ctx: TabsRenderTabContext) => ReactNode;\n /** Replace the default panel rendering. */\n renderPanel?: (ctx: TabsRenderPanelContext) => ReactNode;\n className?: string;\n}\n\n// Run layout effects on the client; fall back to a no-op effect on the server.\nconst useIsoLayoutEffect =\n typeof window !== \"undefined\" ? useLayoutEffect : useEffect;\n\nfunction useIndicator(\n tabListRef: React.RefObject<HTMLDivElement | null>,\n activeValue: string | undefined,\n orientation: TabsOrientation,\n) {\n const [style, setStyle] = useState<CSSProperties>({});\n\n useIsoLayoutEffect(() => {\n if (!tabListRef.current || activeValue === undefined) return;\n\n const measure = () => {\n const list = tabListRef.current;\n if (!list) return;\n const activeTab = list.querySelector<HTMLElement>(\n `[aria-selected=\"true\"]`,\n );\n if (!activeTab) return;\n\n const listRect = list.getBoundingClientRect();\n const tabRect = activeTab.getBoundingClientRect();\n\n if (orientation === \"vertical\") {\n const y = tabRect.top - listRect.top;\n const h = tabRect.height;\n setStyle({\n [\"--rtab-indicator-y\" as string]: `${y}px`,\n [\"--rtab-indicator-height\" as string]: `${h}px`,\n [\"--rtab-indicator-ready\" as string]: \"1\",\n });\n } else {\n const x = tabRect.left - listRect.left;\n const w = tabRect.width;\n setStyle({\n [\"--rtab-indicator-x\" as string]: `${x}px`,\n [\"--rtab-indicator-width\" as string]: `${w}px`,\n [\"--rtab-indicator-ready\" as string]: \"1\",\n });\n }\n };\n\n measure();\n\n if (typeof ResizeObserver === \"undefined\") return;\n const ro = new ResizeObserver(measure);\n ro.observe(tabListRef.current);\n return () => ro.disconnect();\n }, [activeValue, tabListRef, orientation]);\n\n return style;\n}\n\nexport const TabsStyled = forwardRef<HTMLDivElement, TabsStyledProps>(\n function TabsStyled(\n {\n tabs,\n variant = \"line\",\n size = \"md\",\n tone = \"neutral\",\n defaultValue,\n value,\n onChange,\n activation = \"automatic\",\n orientation = \"horizontal\",\n lazyMount = false,\n forceMount = false,\n renderTab,\n renderPanel,\n className,\n },\n ref,\n ) {\n const tabDefs = tabs.map((t) => ({ value: t.value, disabled: t.disabled }));\n\n const resolvedDefault =\n defaultValue ?? tabs.find((t) => !t.disabled)?.value;\n\n const { activeValue, getTabProps, getPanelProps } = useTabs({\n tabs: tabDefs,\n defaultValue: value === undefined ? resolvedDefault : undefined,\n value,\n onChange,\n activation,\n orientation,\n });\n\n const tabListRef = useRef<HTMLDivElement>(null);\n const indicatorStyle = useIndicator(tabListRef, activeValue, orientation);\n\n // Track which tabs have been activated for lazyMount.\n const activatedRef = useRef<Set<string>>(new Set());\n if (activeValue !== undefined) activatedRef.current.add(activeValue);\n\n const rootClass = [\"rtab-root\", className].filter(Boolean).join(\" \");\n\n return (\n <div\n ref={ref}\n className={rootClass}\n data-orientation={orientation}\n >\n <div\n ref={tabListRef}\n role=\"tablist\"\n aria-orientation={orientation}\n className=\"rtab-list\"\n data-variant={variant}\n data-size={size}\n data-tone={tone}\n data-orientation={orientation}\n style={indicatorStyle}\n >\n {tabs.map((tab, index) => {\n const isActive = activeValue === tab.value;\n const isDisabled = !!tab.disabled;\n return (\n <button\n key={tab.value}\n className=\"rtab-tab\"\n {...getTabProps(tab.value, { disabled: tab.disabled })}\n >\n {renderTab\n ? renderTab({ tab, index, isActive, isDisabled })\n : tab.label}\n </button>\n );\n })}\n {(variant === \"line\" || variant === \"solid\" || variant === \"pill\") && (\n <span className=\"rtab-indicator\" aria-hidden=\"true\" />\n )}\n </div>\n <div className=\"rtab-panels\">\n {tabs.map((tab) => {\n const isActive = activeValue === tab.value;\n const shouldRender =\n forceMount || !lazyMount || activatedRef.current.has(tab.value);\n return (\n <div\n key={tab.value}\n className=\"rtab-panel\"\n {...getPanelProps(tab.value)}\n >\n {shouldRender\n ? renderPanel\n ? renderPanel({ tab, isActive })\n : tab.content\n : null}\n </div>\n );\n })}\n </div>\n </div>\n );\n },\n);\n"]}
1
+ {"version":3,"sources":["../src/useTabs.ts","../src/styled/TabsStyled.tsx"],"names":["useRef","useState","useCallback","useMemo","useLayoutEffect","useEffect","forwardRef","TabsStyled","jsxs","jsx"],"mappings":"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","file":"styled.cjs","sourcesContent":["import {\n type HTMLAttributes,\n type KeyboardEvent,\n useCallback,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nexport type TabsActivation = \"automatic\" | \"manual\";\nexport type TabsOrientation = \"horizontal\" | \"vertical\";\nexport type TabsChangeReason = \"click\" | \"keyboard\" | \"programmatic\";\n\nexport interface UseTabsTab {\n value: string;\n disabled?: boolean;\n}\n\nexport interface UseTabsOptions {\n /** Tab definitions — needed for keyboard navigation between tabs. */\n tabs?: UseTabsTab[];\n /** Controlled active value. */\n value?: string;\n /** Initial active value when uncontrolled. */\n defaultValue?: string;\n /** Called when the active tab changes. Optional second arg reports the trigger reason. */\n onChange?: (value: string, reason: TabsChangeReason) => void;\n /** \"automatic\" (default) — arrow keys move focus AND activate. \"manual\" — arrows move focus only; Enter/Space activates. */\n activation?: TabsActivation;\n /** Affects keyboard nav. Default \"horizontal\". */\n orientation?: TabsOrientation;\n}\n\nexport interface TabProps extends HTMLAttributes<HTMLButtonElement> {\n id: string;\n role: \"tab\";\n \"aria-selected\": boolean;\n \"aria-controls\": string;\n \"aria-disabled\"?: boolean;\n \"data-state\": \"active\" | \"inactive\";\n tabIndex: number;\n}\n\nexport interface PanelProps extends HTMLAttributes<HTMLDivElement> {\n id: string;\n role: \"tabpanel\";\n \"aria-labelledby\": string;\n \"data-state\": \"active\" | \"inactive\";\n hidden: boolean;\n}\n\nexport interface UseTabsResult {\n /** Currently active tab value. */\n activeValue: string | undefined;\n /** Returns props to spread onto a tab trigger `<button>`. */\n getTabProps: (value: string, options?: { disabled?: boolean }) => TabProps;\n /** Returns props to spread onto a tab panel. */\n getPanelProps: (value: string) => PanelProps;\n /** Programmatically set the active tab. */\n setActiveValue: (value: string) => void;\n}\n\nconst tabId = (listId: string, value: string) => `${listId}-tab-${value}`;\nconst panelId = (listId: string, value: string) => `${listId}-panel-${value}`;\n\nlet counter = 0;\nfunction useStableId() {\n const ref = useRef<string | null>(null);\n if (ref.current === null) {\n ref.current = `rtabs-${++counter}`;\n }\n return ref.current;\n}\n\nexport function useTabs(opts: UseTabsOptions = {}): UseTabsResult {\n const {\n tabs = [],\n value: controlledValue,\n defaultValue,\n onChange,\n activation = \"automatic\",\n orientation = \"horizontal\",\n } = opts;\n\n const listId = useStableId();\n const isControlled = controlledValue !== undefined;\n\n const [internalValue, setInternalValue] = useState<string | undefined>(\n defaultValue,\n );\n\n const activeValue = isControlled ? controlledValue : internalValue;\n\n const tabRefs = useRef<Map<string, HTMLButtonElement>>(new Map());\n const onChangeRef = useRef(onChange);\n onChangeRef.current = onChange;\n\n const setActiveValue = useCallback(\n (next: string, reason: TabsChangeReason = \"programmatic\") => {\n const tab = tabs.find((t) => t.value === next);\n if (tab?.disabled) return;\n if (isControlled) {\n if (next !== controlledValue) onChangeRef.current?.(next, reason);\n } else {\n if (next === internalValue) return;\n setInternalValue(next);\n onChangeRef.current?.(next, reason);\n }\n },\n [tabs, isControlled, controlledValue, internalValue],\n );\n\n const focusTab = useCallback((value: string) => {\n tabRefs.current.get(value)?.focus();\n }, []);\n\n const handleKeyDown = useCallback(\n (currentValue: string) =>\n (event: KeyboardEvent<HTMLButtonElement>) => {\n const enabled = tabs.filter((t) => !t.disabled);\n if (enabled.length === 0) return;\n\n const currentIndex = enabled.findIndex((t) => t.value === currentValue);\n\n const isForward =\n orientation === \"vertical\"\n ? event.key === \"ArrowDown\"\n : event.key === \"ArrowRight\";\n const isBackward =\n orientation === \"vertical\"\n ? event.key === \"ArrowUp\"\n : event.key === \"ArrowLeft\";\n\n let nextValue: string | undefined;\n\n if (isForward) {\n const nextIndex = (currentIndex + 1) % enabled.length;\n nextValue = enabled[nextIndex]?.value;\n } else if (isBackward) {\n const prevIndex = (currentIndex - 1 + enabled.length) % enabled.length;\n nextValue = enabled[prevIndex]?.value;\n } else if (event.key === \"Home\") {\n nextValue = enabled[0]?.value;\n } else if (event.key === \"End\") {\n nextValue = enabled[enabled.length - 1]?.value;\n } else if (\n activation === \"manual\" &&\n (event.key === \"Enter\" || event.key === \" \")\n ) {\n event.preventDefault();\n setActiveValue(currentValue, \"keyboard\");\n return;\n } else {\n return;\n }\n\n if (nextValue === undefined || nextValue === currentValue) return;\n event.preventDefault();\n focusTab(nextValue);\n if (activation === \"automatic\") {\n setActiveValue(nextValue, \"keyboard\");\n }\n },\n [tabs, orientation, activation, setActiveValue, focusTab],\n );\n\n const getTabProps = useCallback(\n (value: string, options?: { disabled?: boolean }): TabProps => {\n const isDisabled =\n options?.disabled ?? tabs.find((t) => t.value === value)?.disabled;\n const isSelected = activeValue === value;\n return {\n id: tabId(listId, value),\n role: \"tab\",\n \"aria-selected\": isSelected,\n \"aria-controls\": panelId(listId, value),\n \"aria-disabled\": isDisabled || undefined,\n \"data-state\": isSelected ? \"active\" : \"inactive\",\n tabIndex: isSelected ? 0 : -1,\n disabled: isDisabled,\n ref: (node: HTMLButtonElement | null) => {\n if (node) {\n tabRefs.current.set(value, node);\n } else {\n tabRefs.current.delete(value);\n }\n },\n onClick: () => {\n if (!isDisabled) setActiveValue(value, \"click\");\n },\n onKeyDown: handleKeyDown(value),\n } as TabProps & {\n disabled: boolean | undefined;\n ref: (node: HTMLButtonElement | null) => void;\n };\n },\n [activeValue, listId, tabs, setActiveValue, handleKeyDown],\n );\n\n const getPanelProps = useCallback(\n (value: string): PanelProps => {\n const isActive = activeValue === value;\n return {\n id: panelId(listId, value),\n role: \"tabpanel\",\n \"aria-labelledby\": tabId(listId, value),\n \"data-state\": isActive ? \"active\" : \"inactive\",\n hidden: !isActive,\n tabIndex: 0,\n };\n },\n [activeValue, listId],\n );\n\n return useMemo(\n () => ({ activeValue, getTabProps, getPanelProps, setActiveValue }),\n [activeValue, getTabProps, getPanelProps, setActiveValue],\n );\n}\n","import {\n type CSSProperties,\n type ReactNode,\n forwardRef,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from \"react\";\nimport {\n useTabs,\n type TabsActivation,\n type TabsChangeReason,\n type TabsOrientation,\n} from \"../useTabs\";\n\nexport type TabsVariant = \"line\" | \"solid\" | \"pill\";\nexport type TabsSize = \"sm\" | \"md\" | \"lg\";\nexport type TabsTone = \"neutral\" | \"primary\" | \"success\" | \"danger\";\n\nexport interface TabItem {\n value: string;\n label: ReactNode;\n content: ReactNode;\n disabled?: boolean;\n /** When true, renders a × button on the tab. Pair with `onTabClose`. */\n closable?: boolean;\n}\n\nexport interface TabsRenderTabContext {\n tab: TabItem;\n index: number;\n isActive: boolean;\n isDisabled: boolean;\n}\n\nexport interface TabsRenderPanelContext {\n tab: TabItem;\n isActive: boolean;\n}\n\nexport interface TabsStyledProps {\n tabs: TabItem[];\n variant?: TabsVariant;\n size?: TabsSize;\n tone?: TabsTone;\n defaultValue?: string;\n value?: string;\n /** Optional second arg reports the trigger reason (\"click\" | \"keyboard\" | \"programmatic\"). */\n onChange?: (value: string, reason: TabsChangeReason) => void;\n /** \"automatic\" (default) — arrow keys move focus AND activate. \"manual\" — arrows move focus only. */\n activation?: TabsActivation;\n /** Affects keyboard nav direction. Default \"horizontal\". */\n orientation?: TabsOrientation;\n /** Only mount panels after they've been activated at least once. Default: false (all panels mount eagerly). */\n lazyMount?: boolean;\n /** Keep all panels mounted regardless of activation (useful with `lazyMount` overrides). Default: false. */\n forceMount?: boolean;\n /** Fires when the × on a closable tab is clicked. */\n onTabClose?: (value: string) => void;\n /** When true, the tab list scrolls horizontally with chevron buttons at the edges instead of wrapping. */\n scrollable?: boolean;\n /** When true, tabs can be reordered by dragging. Fires `onReorder` with the new value order. */\n sortable?: boolean;\n onReorder?: (values: string[]) => void;\n /** When true, the active tab is scrolled into view on activation. Default: true when `scrollable`. */\n scrollActiveIntoView?: boolean;\n /** Replace the default tab button content. The button shell (a11y, ref, key) stays owned by the component. */\n renderTab?: (ctx: TabsRenderTabContext) => ReactNode;\n /** Replace the default panel rendering. */\n renderPanel?: (ctx: TabsRenderPanelContext) => ReactNode;\n className?: string;\n}\n\n// Run layout effects on the client; fall back to a no-op effect on the server.\nconst useIsoLayoutEffect =\n typeof window !== \"undefined\" ? useLayoutEffect : useEffect;\n\nfunction useIndicator(\n tabListRef: React.RefObject<HTMLDivElement | null>,\n activeValue: string | undefined,\n orientation: TabsOrientation,\n) {\n const [style, setStyle] = useState<CSSProperties>({});\n\n useIsoLayoutEffect(() => {\n if (!tabListRef.current || activeValue === undefined) return;\n\n const measure = () => {\n const list = tabListRef.current;\n if (!list) return;\n const activeTab = list.querySelector<HTMLElement>(\n `[aria-selected=\"true\"]`,\n );\n if (!activeTab) return;\n\n const listRect = list.getBoundingClientRect();\n const tabRect = activeTab.getBoundingClientRect();\n\n if (orientation === \"vertical\") {\n const y = tabRect.top - listRect.top;\n const h = tabRect.height;\n setStyle({\n [\"--rtab-indicator-y\" as string]: `${y}px`,\n [\"--rtab-indicator-height\" as string]: `${h}px`,\n [\"--rtab-indicator-ready\" as string]: \"1\",\n });\n } else {\n const x = tabRect.left - listRect.left;\n const w = tabRect.width;\n setStyle({\n [\"--rtab-indicator-x\" as string]: `${x}px`,\n [\"--rtab-indicator-width\" as string]: `${w}px`,\n [\"--rtab-indicator-ready\" as string]: \"1\",\n });\n }\n };\n\n measure();\n\n if (typeof ResizeObserver === \"undefined\") return;\n const ro = new ResizeObserver(measure);\n ro.observe(tabListRef.current);\n return () => ro.disconnect();\n }, [activeValue, tabListRef, orientation]);\n\n return style;\n}\n\nexport const TabsStyled = forwardRef<HTMLDivElement, TabsStyledProps>(\n function TabsStyled(\n {\n tabs,\n variant = \"line\",\n size = \"md\",\n tone = \"neutral\",\n defaultValue,\n value,\n onChange,\n activation = \"automatic\",\n orientation = \"horizontal\",\n lazyMount = false,\n forceMount = false,\n onTabClose,\n scrollable = false,\n sortable = false,\n onReorder,\n scrollActiveIntoView,\n renderTab,\n renderPanel,\n className,\n },\n ref,\n ) {\n const autoScrollActive = scrollActiveIntoView ?? scrollable;\n const [scrollState, setScrollState] = useState<{ left: boolean; right: boolean }>({\n left: false,\n right: false,\n });\n const typeaheadBufferRef = useRef(\"\");\n const typeaheadTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const dragValueRef = useRef<string | null>(null);\n const tabDefs = tabs.map((t) => ({ value: t.value, disabled: t.disabled }));\n\n const resolvedDefault =\n defaultValue ?? tabs.find((t) => !t.disabled)?.value;\n\n const { activeValue, getTabProps, getPanelProps } = useTabs({\n tabs: tabDefs,\n defaultValue: value === undefined ? resolvedDefault : undefined,\n value,\n onChange,\n activation,\n orientation,\n });\n\n const tabListRef = useRef<HTMLDivElement>(null);\n const indicatorStyle = useIndicator(tabListRef, activeValue, orientation);\n\n // Track which tabs have been activated for lazyMount.\n const activatedRef = useRef<Set<string>>(new Set());\n if (activeValue !== undefined) activatedRef.current.add(activeValue);\n\n const rootClass = [\"rtab-root\", className].filter(Boolean).join(\" \");\n\n // Scroll-state: detect when there's content off the left/right edges so we can\n // toggle the chevron buttons. Only relevant when scrollable.\n useEffect(() => {\n if (!scrollable) return;\n const list = tabListRef.current;\n if (!list) return;\n const update = () => {\n setScrollState({\n left: list.scrollLeft > 4,\n right: list.scrollLeft + list.clientWidth < list.scrollWidth - 4,\n });\n };\n update();\n list.addEventListener(\"scroll\", update, { passive: true });\n const ro = typeof ResizeObserver !== \"undefined\" ? new ResizeObserver(update) : null;\n ro?.observe(list);\n return () => {\n list.removeEventListener(\"scroll\", update);\n ro?.disconnect();\n };\n }, [scrollable, tabs.length]);\n\n // Auto-scroll active tab into view\n useEffect(() => {\n if (!autoScrollActive || !activeValue) return;\n const list = tabListRef.current;\n if (!list) return;\n const activeEl = list.querySelector<HTMLElement>(`[aria-selected=\"true\"]`);\n if (!activeEl) return;\n activeEl.scrollIntoView({ behavior: \"smooth\", block: \"nearest\", inline: \"nearest\" });\n }, [autoScrollActive, activeValue]);\n\n const scrollByAmount = (delta: number) => {\n tabListRef.current?.scrollBy({ left: delta, behavior: \"smooth\" });\n };\n\n // Typeahead: letters jump to the next tab whose label starts with the buffer.\n const handleTypeahead = (e: React.KeyboardEvent<HTMLDivElement>) => {\n if (e.key.length !== 1 || e.altKey || e.ctrlKey || e.metaKey) return;\n typeaheadBufferRef.current = (typeaheadBufferRef.current + e.key).toLowerCase();\n if (typeaheadTimerRef.current) clearTimeout(typeaheadTimerRef.current);\n typeaheadTimerRef.current = setTimeout(() => { typeaheadBufferRef.current = \"\"; }, 600);\n\n const buffer = typeaheadBufferRef.current;\n const startIdx = Math.max(0, tabs.findIndex((t) => t.value === activeValue));\n // Search starting just after the active tab, wrapping around.\n for (let i = 1; i <= tabs.length; i++) {\n const idx = (startIdx + i) % tabs.length;\n const tab = tabs[idx];\n if (!tab || tab.disabled) continue;\n const labelStr = typeof tab.label === \"string\" ? tab.label : \"\";\n if (labelStr.toLowerCase().startsWith(buffer)) {\n // Use the existing tab button's click via aria-selected target.\n const list = tabListRef.current;\n const btn = list?.querySelector<HTMLButtonElement>(`[data-value=\"${tab.value}\"]`);\n btn?.click();\n btn?.focus();\n break;\n }\n }\n };\n\n // Drag-to-reorder: HTML5 DnD is fine here — no external lib.\n const handleDragStart = (val: string) => (e: React.DragEvent<HTMLButtonElement>) => {\n if (!sortable) return;\n dragValueRef.current = val;\n e.dataTransfer.effectAllowed = \"move\";\n };\n const handleDragOver = (val: string) => (e: React.DragEvent<HTMLButtonElement>) => {\n if (!sortable || !dragValueRef.current || dragValueRef.current === val) return;\n e.preventDefault();\n e.dataTransfer.dropEffect = \"move\";\n };\n const handleDrop = (val: string) => (e: React.DragEvent<HTMLButtonElement>) => {\n if (!sortable) return;\n e.preventDefault();\n const from = dragValueRef.current;\n dragValueRef.current = null;\n if (!from || from === val || !onReorder) return;\n const order = tabs.map((t) => t.value);\n const fromIdx = order.indexOf(from);\n const toIdx = order.indexOf(val);\n if (fromIdx === -1 || toIdx === -1) return;\n const next = [...order];\n next.splice(fromIdx, 1);\n next.splice(toIdx, 0, from);\n onReorder(next);\n };\n\n return (\n <div\n ref={ref}\n className={rootClass}\n data-orientation={orientation}\n data-scrollable={scrollable || undefined}\n onKeyDown={handleTypeahead}\n >\n <div className=\"rtab-list-wrap\">\n {scrollable && scrollState.left && (\n <button\n type=\"button\"\n className=\"rtab-scroll-btn rtab-scroll-btn--left\"\n aria-label=\"Scroll left\"\n onClick={() => scrollByAmount(-200)}\n >\n <svg viewBox=\"0 0 12 12\" width=\"12\" height=\"12\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\"><path d=\"M7.5 3l-3 3 3 3\"/></svg>\n </button>\n )}\n <div\n ref={tabListRef}\n role=\"tablist\"\n aria-orientation={orientation}\n className=\"rtab-list\"\n data-variant={variant}\n data-size={size}\n data-tone={tone}\n data-orientation={orientation}\n data-scrollable={scrollable || undefined}\n style={indicatorStyle}\n >\n {tabs.map((tab, index) => {\n const isActive = activeValue === tab.value;\n const isDisabled = !!tab.disabled;\n const tabProps = getTabProps(tab.value, { disabled: tab.disabled });\n return (\n <button\n key={tab.value}\n data-value={tab.value}\n data-closable={tab.closable || undefined}\n className=\"rtab-tab\"\n {...tabProps}\n draggable={sortable && !isDisabled ? true : undefined}\n onDragStart={sortable ? handleDragStart(tab.value) : undefined}\n onDragOver={sortable ? handleDragOver(tab.value) : undefined}\n onDrop={sortable ? handleDrop(tab.value) : undefined}\n >\n {renderTab\n ? renderTab({ tab, index, isActive, isDisabled })\n : tab.label}\n {tab.closable && (\n <span\n className=\"rtab-close\"\n role=\"button\"\n tabIndex={-1}\n aria-label={`Close ${typeof tab.label === \"string\" ? tab.label : \"tab\"}`}\n onClick={(e) => {\n e.stopPropagation();\n onTabClose?.(tab.value);\n }}\n >\n <svg viewBox=\"0 0 12 12\" width=\"10\" height=\"10\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" aria-hidden=\"true\">\n <path d=\"M3 3l6 6M9 3l-6 6\"/>\n </svg>\n </span>\n )}\n </button>\n );\n })}\n {(variant === \"line\" || variant === \"solid\" || variant === \"pill\") && (\n <span className=\"rtab-indicator\" aria-hidden=\"true\" />\n )}\n </div>\n {scrollable && scrollState.right && (\n <button\n type=\"button\"\n className=\"rtab-scroll-btn rtab-scroll-btn--right\"\n aria-label=\"Scroll right\"\n onClick={() => scrollByAmount(200)}\n >\n <svg viewBox=\"0 0 12 12\" width=\"12\" height=\"12\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\"><path d=\"M4.5 3l3 3-3 3\"/></svg>\n </button>\n )}\n </div>\n <div className=\"rtab-panels\">\n {tabs.map((tab) => {\n const isActive = activeValue === tab.value;\n const shouldRender =\n forceMount || !lazyMount || activatedRef.current.has(tab.value);\n return (\n <div\n key={tab.value}\n className=\"rtab-panel\"\n {...getPanelProps(tab.value)}\n >\n {shouldRender\n ? renderPanel\n ? renderPanel({ tab, isActive })\n : tab.content\n : null}\n </div>\n );\n })}\n </div>\n </div>\n );\n },\n);\n"]}
package/dist/styled.d.cts CHANGED
@@ -10,6 +10,8 @@ interface TabItem {
10
10
  label: ReactNode;
11
11
  content: ReactNode;
12
12
  disabled?: boolean;
13
+ /** When true, renders a × button on the tab. Pair with `onTabClose`. */
14
+ closable?: boolean;
13
15
  }
14
16
  interface TabsRenderTabContext {
15
17
  tab: TabItem;
@@ -38,6 +40,15 @@ interface TabsStyledProps {
38
40
  lazyMount?: boolean;
39
41
  /** Keep all panels mounted regardless of activation (useful with `lazyMount` overrides). Default: false. */
40
42
  forceMount?: boolean;
43
+ /** Fires when the × on a closable tab is clicked. */
44
+ onTabClose?: (value: string) => void;
45
+ /** When true, the tab list scrolls horizontally with chevron buttons at the edges instead of wrapping. */
46
+ scrollable?: boolean;
47
+ /** When true, tabs can be reordered by dragging. Fires `onReorder` with the new value order. */
48
+ sortable?: boolean;
49
+ onReorder?: (values: string[]) => void;
50
+ /** When true, the active tab is scrolled into view on activation. Default: true when `scrollable`. */
51
+ scrollActiveIntoView?: boolean;
41
52
  /** Replace the default tab button content. The button shell (a11y, ref, key) stays owned by the component. */
42
53
  renderTab?: (ctx: TabsRenderTabContext) => ReactNode;
43
54
  /** Replace the default panel rendering. */
package/dist/styled.d.ts CHANGED
@@ -10,6 +10,8 @@ interface TabItem {
10
10
  label: ReactNode;
11
11
  content: ReactNode;
12
12
  disabled?: boolean;
13
+ /** When true, renders a × button on the tab. Pair with `onTabClose`. */
14
+ closable?: boolean;
13
15
  }
14
16
  interface TabsRenderTabContext {
15
17
  tab: TabItem;
@@ -38,6 +40,15 @@ interface TabsStyledProps {
38
40
  lazyMount?: boolean;
39
41
  /** Keep all panels mounted regardless of activation (useful with `lazyMount` overrides). Default: false. */
40
42
  forceMount?: boolean;
43
+ /** Fires when the × on a closable tab is clicked. */
44
+ onTabClose?: (value: string) => void;
45
+ /** When true, the tab list scrolls horizontally with chevron buttons at the edges instead of wrapping. */
46
+ scrollable?: boolean;
47
+ /** When true, tabs can be reordered by dragging. Fires `onReorder` with the new value order. */
48
+ sortable?: boolean;
49
+ onReorder?: (values: string[]) => void;
50
+ /** When true, the active tab is scrolled into view on activation. Default: true when `scrollable`. */
51
+ scrollActiveIntoView?: boolean;
41
52
  /** Replace the default tab button content. The button shell (a11y, ref, key) stays owned by the component. */
42
53
  renderTab?: (ctx: TabsRenderTabContext) => ReactNode;
43
54
  /** Replace the default panel rendering. */
package/dist/styled.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { useTabs } from './chunk-NNDW3W6L.js';
2
- import { forwardRef, useRef, useState, useLayoutEffect, useEffect } from 'react';
2
+ import { forwardRef, useState, useRef, useEffect, useLayoutEffect } from 'react';
3
3
  import { jsxs, jsx } from 'react/jsx-runtime';
4
4
 
5
5
  var useIsoLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
@@ -55,10 +55,23 @@ var TabsStyled = forwardRef(
55
55
  orientation = "horizontal",
56
56
  lazyMount = false,
57
57
  forceMount = false,
58
+ onTabClose,
59
+ scrollable = false,
60
+ sortable = false,
61
+ onReorder,
62
+ scrollActiveIntoView,
58
63
  renderTab,
59
64
  renderPanel,
60
65
  className
61
66
  }, ref) {
67
+ const autoScrollActive = scrollActiveIntoView ?? scrollable;
68
+ const [scrollState, setScrollState] = useState({
69
+ left: false,
70
+ right: false
71
+ });
72
+ const typeaheadBufferRef = useRef("");
73
+ const typeaheadTimerRef = useRef(null);
74
+ const dragValueRef = useRef(null);
62
75
  const tabDefs = tabs.map((t) => ({ value: t.value, disabled: t.disabled }));
63
76
  const resolvedDefault = defaultValue ?? tabs.find((t) => !t.disabled)?.value;
64
77
  const { activeValue, getTabProps, getPanelProps } = useTabs({
@@ -74,43 +87,169 @@ var TabsStyled = forwardRef(
74
87
  const activatedRef = useRef(/* @__PURE__ */ new Set());
75
88
  if (activeValue !== void 0) activatedRef.current.add(activeValue);
76
89
  const rootClass = ["rtab-root", className].filter(Boolean).join(" ");
90
+ useEffect(() => {
91
+ if (!scrollable) return;
92
+ const list = tabListRef.current;
93
+ if (!list) return;
94
+ const update = () => {
95
+ setScrollState({
96
+ left: list.scrollLeft > 4,
97
+ right: list.scrollLeft + list.clientWidth < list.scrollWidth - 4
98
+ });
99
+ };
100
+ update();
101
+ list.addEventListener("scroll", update, { passive: true });
102
+ const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(update) : null;
103
+ ro?.observe(list);
104
+ return () => {
105
+ list.removeEventListener("scroll", update);
106
+ ro?.disconnect();
107
+ };
108
+ }, [scrollable, tabs.length]);
109
+ useEffect(() => {
110
+ if (!autoScrollActive || !activeValue) return;
111
+ const list = tabListRef.current;
112
+ if (!list) return;
113
+ const activeEl = list.querySelector(`[aria-selected="true"]`);
114
+ if (!activeEl) return;
115
+ activeEl.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "nearest" });
116
+ }, [autoScrollActive, activeValue]);
117
+ const scrollByAmount = (delta) => {
118
+ tabListRef.current?.scrollBy({ left: delta, behavior: "smooth" });
119
+ };
120
+ const handleTypeahead = (e) => {
121
+ if (e.key.length !== 1 || e.altKey || e.ctrlKey || e.metaKey) return;
122
+ typeaheadBufferRef.current = (typeaheadBufferRef.current + e.key).toLowerCase();
123
+ if (typeaheadTimerRef.current) clearTimeout(typeaheadTimerRef.current);
124
+ typeaheadTimerRef.current = setTimeout(() => {
125
+ typeaheadBufferRef.current = "";
126
+ }, 600);
127
+ const buffer = typeaheadBufferRef.current;
128
+ const startIdx = Math.max(0, tabs.findIndex((t) => t.value === activeValue));
129
+ for (let i = 1; i <= tabs.length; i++) {
130
+ const idx = (startIdx + i) % tabs.length;
131
+ const tab = tabs[idx];
132
+ if (!tab || tab.disabled) continue;
133
+ const labelStr = typeof tab.label === "string" ? tab.label : "";
134
+ if (labelStr.toLowerCase().startsWith(buffer)) {
135
+ const list = tabListRef.current;
136
+ const btn = list?.querySelector(`[data-value="${tab.value}"]`);
137
+ btn?.click();
138
+ btn?.focus();
139
+ break;
140
+ }
141
+ }
142
+ };
143
+ const handleDragStart = (val) => (e) => {
144
+ if (!sortable) return;
145
+ dragValueRef.current = val;
146
+ e.dataTransfer.effectAllowed = "move";
147
+ };
148
+ const handleDragOver = (val) => (e) => {
149
+ if (!sortable || !dragValueRef.current || dragValueRef.current === val) return;
150
+ e.preventDefault();
151
+ e.dataTransfer.dropEffect = "move";
152
+ };
153
+ const handleDrop = (val) => (e) => {
154
+ if (!sortable) return;
155
+ e.preventDefault();
156
+ const from = dragValueRef.current;
157
+ dragValueRef.current = null;
158
+ if (!from || from === val || !onReorder) return;
159
+ const order = tabs.map((t) => t.value);
160
+ const fromIdx = order.indexOf(from);
161
+ const toIdx = order.indexOf(val);
162
+ if (fromIdx === -1 || toIdx === -1) return;
163
+ const next = [...order];
164
+ next.splice(fromIdx, 1);
165
+ next.splice(toIdx, 0, from);
166
+ onReorder(next);
167
+ };
77
168
  return /* @__PURE__ */ jsxs(
78
169
  "div",
79
170
  {
80
171
  ref,
81
172
  className: rootClass,
82
173
  "data-orientation": orientation,
174
+ "data-scrollable": scrollable || void 0,
175
+ onKeyDown: handleTypeahead,
83
176
  children: [
84
- /* @__PURE__ */ jsxs(
85
- "div",
86
- {
87
- ref: tabListRef,
88
- role: "tablist",
89
- "aria-orientation": orientation,
90
- className: "rtab-list",
91
- "data-variant": variant,
92
- "data-size": size,
93
- "data-tone": tone,
94
- "data-orientation": orientation,
95
- style: indicatorStyle,
96
- children: [
97
- tabs.map((tab, index) => {
98
- const isActive = activeValue === tab.value;
99
- const isDisabled = !!tab.disabled;
100
- return /* @__PURE__ */ jsx(
101
- "button",
102
- {
103
- className: "rtab-tab",
104
- ...getTabProps(tab.value, { disabled: tab.disabled }),
105
- children: renderTab ? renderTab({ tab, index, isActive, isDisabled }) : tab.label
106
- },
107
- tab.value
108
- );
109
- }),
110
- (variant === "line" || variant === "solid" || variant === "pill") && /* @__PURE__ */ jsx("span", { className: "rtab-indicator", "aria-hidden": "true" })
111
- ]
112
- }
113
- ),
177
+ /* @__PURE__ */ jsxs("div", { className: "rtab-list-wrap", children: [
178
+ scrollable && scrollState.left && /* @__PURE__ */ jsx(
179
+ "button",
180
+ {
181
+ type: "button",
182
+ className: "rtab-scroll-btn rtab-scroll-btn--left",
183
+ "aria-label": "Scroll left",
184
+ onClick: () => scrollByAmount(-200),
185
+ children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 12 12", width: "12", height: "12", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M7.5 3l-3 3 3 3" }) })
186
+ }
187
+ ),
188
+ /* @__PURE__ */ jsxs(
189
+ "div",
190
+ {
191
+ ref: tabListRef,
192
+ role: "tablist",
193
+ "aria-orientation": orientation,
194
+ className: "rtab-list",
195
+ "data-variant": variant,
196
+ "data-size": size,
197
+ "data-tone": tone,
198
+ "data-orientation": orientation,
199
+ "data-scrollable": scrollable || void 0,
200
+ style: indicatorStyle,
201
+ children: [
202
+ tabs.map((tab, index) => {
203
+ const isActive = activeValue === tab.value;
204
+ const isDisabled = !!tab.disabled;
205
+ const tabProps = getTabProps(tab.value, { disabled: tab.disabled });
206
+ return /* @__PURE__ */ jsxs(
207
+ "button",
208
+ {
209
+ "data-value": tab.value,
210
+ "data-closable": tab.closable || void 0,
211
+ className: "rtab-tab",
212
+ ...tabProps,
213
+ draggable: sortable && !isDisabled ? true : void 0,
214
+ onDragStart: sortable ? handleDragStart(tab.value) : void 0,
215
+ onDragOver: sortable ? handleDragOver(tab.value) : void 0,
216
+ onDrop: sortable ? handleDrop(tab.value) : void 0,
217
+ children: [
218
+ renderTab ? renderTab({ tab, index, isActive, isDisabled }) : tab.label,
219
+ tab.closable && /* @__PURE__ */ jsx(
220
+ "span",
221
+ {
222
+ className: "rtab-close",
223
+ role: "button",
224
+ tabIndex: -1,
225
+ "aria-label": `Close ${typeof tab.label === "string" ? tab.label : "tab"}`,
226
+ onClick: (e) => {
227
+ e.stopPropagation();
228
+ onTabClose?.(tab.value);
229
+ },
230
+ children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 12 12", width: "10", height: "10", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M3 3l6 6M9 3l-6 6" }) })
231
+ }
232
+ )
233
+ ]
234
+ },
235
+ tab.value
236
+ );
237
+ }),
238
+ (variant === "line" || variant === "solid" || variant === "pill") && /* @__PURE__ */ jsx("span", { className: "rtab-indicator", "aria-hidden": "true" })
239
+ ]
240
+ }
241
+ ),
242
+ scrollable && scrollState.right && /* @__PURE__ */ jsx(
243
+ "button",
244
+ {
245
+ type: "button",
246
+ className: "rtab-scroll-btn rtab-scroll-btn--right",
247
+ "aria-label": "Scroll right",
248
+ onClick: () => scrollByAmount(200),
249
+ children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 12 12", width: "12", height: "12", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M4.5 3l3 3-3 3" }) })
250
+ }
251
+ )
252
+ ] }),
114
253
  /* @__PURE__ */ jsx("div", { className: "rtab-panels", children: tabs.map((tab) => {
115
254
  const isActive = activeValue === tab.value;
116
255
  const shouldRender = forceMount || !lazyMount || activatedRef.current.has(tab.value);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/styled/TabsStyled.tsx"],"names":["TabsStyled"],"mappings":"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","file":"styled.js","sourcesContent":["import {\n type CSSProperties,\n type ReactNode,\n forwardRef,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from \"react\";\nimport {\n useTabs,\n type TabsActivation,\n type TabsChangeReason,\n type TabsOrientation,\n} from \"../useTabs\";\n\nexport type TabsVariant = \"line\" | \"solid\" | \"pill\";\nexport type TabsSize = \"sm\" | \"md\" | \"lg\";\nexport type TabsTone = \"neutral\" | \"primary\" | \"success\" | \"danger\";\n\nexport interface TabItem {\n value: string;\n label: ReactNode;\n content: ReactNode;\n disabled?: boolean;\n}\n\nexport interface TabsRenderTabContext {\n tab: TabItem;\n index: number;\n isActive: boolean;\n isDisabled: boolean;\n}\n\nexport interface TabsRenderPanelContext {\n tab: TabItem;\n isActive: boolean;\n}\n\nexport interface TabsStyledProps {\n tabs: TabItem[];\n variant?: TabsVariant;\n size?: TabsSize;\n tone?: TabsTone;\n defaultValue?: string;\n value?: string;\n /** Optional second arg reports the trigger reason (\"click\" | \"keyboard\" | \"programmatic\"). */\n onChange?: (value: string, reason: TabsChangeReason) => void;\n /** \"automatic\" (default) — arrow keys move focus AND activate. \"manual\" — arrows move focus only. */\n activation?: TabsActivation;\n /** Affects keyboard nav direction. Default \"horizontal\". */\n orientation?: TabsOrientation;\n /** Only mount panels after they've been activated at least once. Default: false (all panels mount eagerly). */\n lazyMount?: boolean;\n /** Keep all panels mounted regardless of activation (useful with `lazyMount` overrides). Default: false. */\n forceMount?: boolean;\n /** Replace the default tab button content. The button shell (a11y, ref, key) stays owned by the component. */\n renderTab?: (ctx: TabsRenderTabContext) => ReactNode;\n /** Replace the default panel rendering. */\n renderPanel?: (ctx: TabsRenderPanelContext) => ReactNode;\n className?: string;\n}\n\n// Run layout effects on the client; fall back to a no-op effect on the server.\nconst useIsoLayoutEffect =\n typeof window !== \"undefined\" ? useLayoutEffect : useEffect;\n\nfunction useIndicator(\n tabListRef: React.RefObject<HTMLDivElement | null>,\n activeValue: string | undefined,\n orientation: TabsOrientation,\n) {\n const [style, setStyle] = useState<CSSProperties>({});\n\n useIsoLayoutEffect(() => {\n if (!tabListRef.current || activeValue === undefined) return;\n\n const measure = () => {\n const list = tabListRef.current;\n if (!list) return;\n const activeTab = list.querySelector<HTMLElement>(\n `[aria-selected=\"true\"]`,\n );\n if (!activeTab) return;\n\n const listRect = list.getBoundingClientRect();\n const tabRect = activeTab.getBoundingClientRect();\n\n if (orientation === \"vertical\") {\n const y = tabRect.top - listRect.top;\n const h = tabRect.height;\n setStyle({\n [\"--rtab-indicator-y\" as string]: `${y}px`,\n [\"--rtab-indicator-height\" as string]: `${h}px`,\n [\"--rtab-indicator-ready\" as string]: \"1\",\n });\n } else {\n const x = tabRect.left - listRect.left;\n const w = tabRect.width;\n setStyle({\n [\"--rtab-indicator-x\" as string]: `${x}px`,\n [\"--rtab-indicator-width\" as string]: `${w}px`,\n [\"--rtab-indicator-ready\" as string]: \"1\",\n });\n }\n };\n\n measure();\n\n if (typeof ResizeObserver === \"undefined\") return;\n const ro = new ResizeObserver(measure);\n ro.observe(tabListRef.current);\n return () => ro.disconnect();\n }, [activeValue, tabListRef, orientation]);\n\n return style;\n}\n\nexport const TabsStyled = forwardRef<HTMLDivElement, TabsStyledProps>(\n function TabsStyled(\n {\n tabs,\n variant = \"line\",\n size = \"md\",\n tone = \"neutral\",\n defaultValue,\n value,\n onChange,\n activation = \"automatic\",\n orientation = \"horizontal\",\n lazyMount = false,\n forceMount = false,\n renderTab,\n renderPanel,\n className,\n },\n ref,\n ) {\n const tabDefs = tabs.map((t) => ({ value: t.value, disabled: t.disabled }));\n\n const resolvedDefault =\n defaultValue ?? tabs.find((t) => !t.disabled)?.value;\n\n const { activeValue, getTabProps, getPanelProps } = useTabs({\n tabs: tabDefs,\n defaultValue: value === undefined ? resolvedDefault : undefined,\n value,\n onChange,\n activation,\n orientation,\n });\n\n const tabListRef = useRef<HTMLDivElement>(null);\n const indicatorStyle = useIndicator(tabListRef, activeValue, orientation);\n\n // Track which tabs have been activated for lazyMount.\n const activatedRef = useRef<Set<string>>(new Set());\n if (activeValue !== undefined) activatedRef.current.add(activeValue);\n\n const rootClass = [\"rtab-root\", className].filter(Boolean).join(\" \");\n\n return (\n <div\n ref={ref}\n className={rootClass}\n data-orientation={orientation}\n >\n <div\n ref={tabListRef}\n role=\"tablist\"\n aria-orientation={orientation}\n className=\"rtab-list\"\n data-variant={variant}\n data-size={size}\n data-tone={tone}\n data-orientation={orientation}\n style={indicatorStyle}\n >\n {tabs.map((tab, index) => {\n const isActive = activeValue === tab.value;\n const isDisabled = !!tab.disabled;\n return (\n <button\n key={tab.value}\n className=\"rtab-tab\"\n {...getTabProps(tab.value, { disabled: tab.disabled })}\n >\n {renderTab\n ? renderTab({ tab, index, isActive, isDisabled })\n : tab.label}\n </button>\n );\n })}\n {(variant === \"line\" || variant === \"solid\" || variant === \"pill\") && (\n <span className=\"rtab-indicator\" aria-hidden=\"true\" />\n )}\n </div>\n <div className=\"rtab-panels\">\n {tabs.map((tab) => {\n const isActive = activeValue === tab.value;\n const shouldRender =\n forceMount || !lazyMount || activatedRef.current.has(tab.value);\n return (\n <div\n key={tab.value}\n className=\"rtab-panel\"\n {...getPanelProps(tab.value)}\n >\n {shouldRender\n ? renderPanel\n ? renderPanel({ tab, isActive })\n : tab.content\n : null}\n </div>\n );\n })}\n </div>\n </div>\n );\n },\n);\n"]}
1
+ {"version":3,"sources":["../src/styled/TabsStyled.tsx"],"names":["TabsStyled"],"mappings":";;;;AA2EA,IAAM,kBAAA,GACJ,OAAO,MAAA,KAAW,WAAA,GAAc,eAAA,GAAkB,SAAA;AAEpD,SAAS,YAAA,CACP,UAAA,EACA,WAAA,EACA,WAAA,EACA;AACA,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,QAAA,CAAwB,EAAE,CAAA;AAEpD,EAAA,kBAAA,CAAmB,MAAM;AACvB,IAAA,IAAI,CAAC,UAAA,CAAW,OAAA,IAAW,WAAA,KAAgB,MAAA,EAAW;AAEtD,IAAA,MAAM,UAAU,MAAM;AACpB,MAAA,MAAM,OAAO,UAAA,CAAW,OAAA;AACxB,MAAA,IAAI,CAAC,IAAA,EAAM;AACX,MAAA,MAAM,YAAY,IAAA,CAAK,aAAA;AAAA,QACrB,CAAA,sBAAA;AAAA,OACF;AACA,MAAA,IAAI,CAAC,SAAA,EAAW;AAEhB,MAAA,MAAM,QAAA,GAAW,KAAK,qBAAA,EAAsB;AAC5C,MAAA,MAAM,OAAA,GAAU,UAAU,qBAAA,EAAsB;AAEhD,MAAA,IAAI,gBAAgB,UAAA,EAAY;AAC9B,QAAA,MAAM,CAAA,GAAI,OAAA,CAAQ,GAAA,GAAM,QAAA,CAAS,GAAA;AACjC,QAAA,MAAM,IAAI,OAAA,CAAQ,MAAA;AAClB,QAAA,QAAA,CAAS;AAAA,UACP,CAAC,oBAA8B,GAAG,CAAA,EAAG,CAAC,CAAA,EAAA,CAAA;AAAA,UACtC,CAAC,yBAAmC,GAAG,CAAA,EAAG,CAAC,CAAA,EAAA,CAAA;AAAA,UAC3C,CAAC,wBAAkC,GAAG;AAAA,SACvC,CAAA;AAAA,MACH,CAAA,MAAO;AACL,QAAA,MAAM,CAAA,GAAI,OAAA,CAAQ,IAAA,GAAO,QAAA,CAAS,IAAA;AAClC,QAAA,MAAM,IAAI,OAAA,CAAQ,KAAA;AAClB,QAAA,QAAA,CAAS;AAAA,UACP,CAAC,oBAA8B,GAAG,CAAA,EAAG,CAAC,CAAA,EAAA,CAAA;AAAA,UACtC,CAAC,wBAAkC,GAAG,CAAA,EAAG,CAAC,CAAA,EAAA,CAAA;AAAA,UAC1C,CAAC,wBAAkC,GAAG;AAAA,SACvC,CAAA;AAAA,MACH;AAAA,IACF,CAAA;AAEA,IAAA,OAAA,EAAQ;AAER,IAAA,IAAI,OAAO,mBAAmB,WAAA,EAAa;AAC3C,IAAA,MAAM,EAAA,GAAK,IAAI,cAAA,CAAe,OAAO,CAAA;AACrC,IAAA,EAAA,CAAG,OAAA,CAAQ,WAAW,OAAO,CAAA;AAC7B,IAAA,OAAO,MAAM,GAAG,UAAA,EAAW;AAAA,EAC7B,CAAA,EAAG,CAAC,WAAA,EAAa,UAAA,EAAY,WAAW,CAAC,CAAA;AAEzC,EAAA,OAAO,KAAA;AACT;AAEO,IAAM,UAAA,GAAa,UAAA;AAAA,EACxB,SAASA,WAAAA,CACP;AAAA,IACE,IAAA;AAAA,IACA,OAAA,GAAU,MAAA;AAAA,IACV,IAAA,GAAO,IAAA;AAAA,IACP,IAAA,GAAO,SAAA;AAAA,IACP,YAAA;AAAA,IACA,KAAA;AAAA,IACA,QAAA;AAAA,IACA,UAAA,GAAa,WAAA;AAAA,IACb,WAAA,GAAc,YAAA;AAAA,IACd,SAAA,GAAY,KAAA;AAAA,IACZ,UAAA,GAAa,KAAA;AAAA,IACb,UAAA;AAAA,IACA,UAAA,GAAa,KAAA;AAAA,IACb,QAAA,GAAW,KAAA;AAAA,IACX,SAAA;AAAA,IACA,oBAAA;AAAA,IACA,SAAA;AAAA,IACA,WAAA;AAAA,IACA;AAAA,KAEF,GAAA,EACA;AACA,IAAA,MAAM,mBAAmB,oBAAA,IAAwB,UAAA;AACjD,IAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,QAAA,CAA4C;AAAA,MAChF,IAAA,EAAM,KAAA;AAAA,MACN,KAAA,EAAO;AAAA,KACR,CAAA;AACD,IAAA,MAAM,kBAAA,GAAqB,OAAO,EAAE,CAAA;AACpC,IAAA,MAAM,iBAAA,GAAoB,OAA6C,IAAI,CAAA;AAC3E,IAAA,MAAM,YAAA,GAAe,OAAsB,IAAI,CAAA;AAC/C,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,MAAO,EAAE,KAAA,EAAO,CAAA,CAAE,KAAA,EAAO,QAAA,EAAU,CAAA,CAAE,QAAA,EAAS,CAAE,CAAA;AAE1E,IAAA,MAAM,eAAA,GACJ,gBAAgB,IAAA,CAAK,IAAA,CAAK,CAAC,CAAA,KAAM,CAAC,CAAA,CAAE,QAAQ,CAAA,EAAG,KAAA;AAEjD,IAAA,MAAM,EAAE,WAAA,EAAa,WAAA,EAAa,aAAA,KAAkB,OAAA,CAAQ;AAAA,MAC1D,IAAA,EAAM,OAAA;AAAA,MACN,YAAA,EAAc,KAAA,KAAU,MAAA,GAAY,eAAA,GAAkB,MAAA;AAAA,MACtD,KAAA;AAAA,MACA,QAAA;AAAA,MACA,UAAA;AAAA,MACA;AAAA,KACD,CAAA;AAED,IAAA,MAAM,UAAA,GAAa,OAAuB,IAAI,CAAA;AAC9C,IAAA,MAAM,cAAA,GAAiB,YAAA,CAAa,UAAA,EAAY,WAAA,EAAa,WAAW,CAAA;AAGxE,IAAA,MAAM,YAAA,GAAe,MAAA,iBAAoB,IAAI,GAAA,EAAK,CAAA;AAClD,IAAA,IAAI,WAAA,KAAgB,MAAA,EAAW,YAAA,CAAa,OAAA,CAAQ,IAAI,WAAW,CAAA;AAEnE,IAAA,MAAM,SAAA,GAAY,CAAC,WAAA,EAAa,SAAS,EAAE,MAAA,CAAO,OAAO,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AAInE,IAAA,SAAA,CAAU,MAAM;AACd,MAAA,IAAI,CAAC,UAAA,EAAY;AACjB,MAAA,MAAM,OAAO,UAAA,CAAW,OAAA;AACxB,MAAA,IAAI,CAAC,IAAA,EAAM;AACX,MAAA,MAAM,SAAS,MAAM;AACnB,QAAA,cAAA,CAAe;AAAA,UACb,IAAA,EAAM,KAAK,UAAA,GAAa,CAAA;AAAA,UACxB,OAAO,IAAA,CAAK,UAAA,GAAa,IAAA,CAAK,WAAA,GAAc,KAAK,WAAA,GAAc;AAAA,SAChE,CAAA;AAAA,MACH,CAAA;AACA,MAAA,MAAA,EAAO;AACP,MAAA,IAAA,CAAK,iBAAiB,QAAA,EAAU,MAAA,EAAQ,EAAE,OAAA,EAAS,MAAM,CAAA;AACzD,MAAA,MAAM,KAAK,OAAO,cAAA,KAAmB,cAAc,IAAI,cAAA,CAAe,MAAM,CAAA,GAAI,IAAA;AAChF,MAAA,EAAA,EAAI,QAAQ,IAAI,CAAA;AAChB,MAAA,OAAO,MAAM;AACX,QAAA,IAAA,CAAK,mBAAA,CAAoB,UAAU,MAAM,CAAA;AACzC,QAAA,EAAA,EAAI,UAAA,EAAW;AAAA,MACjB,CAAA;AAAA,IACF,CAAA,EAAG,CAAC,UAAA,EAAY,IAAA,CAAK,MAAM,CAAC,CAAA;AAG5B,IAAA,SAAA,CAAU,MAAM;AACd,MAAA,IAAI,CAAC,gBAAA,IAAoB,CAAC,WAAA,EAAa;AACvC,MAAA,MAAM,OAAO,UAAA,CAAW,OAAA;AACxB,MAAA,IAAI,CAAC,IAAA,EAAM;AACX,MAAA,MAAM,QAAA,GAAW,IAAA,CAAK,aAAA,CAA2B,CAAA,sBAAA,CAAwB,CAAA;AACzE,MAAA,IAAI,CAAC,QAAA,EAAU;AACf,MAAA,QAAA,CAAS,cAAA,CAAe,EAAE,QAAA,EAAU,QAAA,EAAU,OAAO,SAAA,EAAW,MAAA,EAAQ,WAAW,CAAA;AAAA,IACrF,CAAA,EAAG,CAAC,gBAAA,EAAkB,WAAW,CAAC,CAAA;AAElC,IAAA,MAAM,cAAA,GAAiB,CAAC,KAAA,KAAkB;AACxC,MAAA,UAAA,CAAW,SAAS,QAAA,CAAS,EAAE,MAAM,KAAA,EAAO,QAAA,EAAU,UAAU,CAAA;AAAA,IAClE,CAAA;AAGA,IAAA,MAAM,eAAA,GAAkB,CAAC,CAAA,KAA2C;AAClE,MAAA,IAAI,CAAA,CAAE,IAAI,MAAA,KAAW,CAAA,IAAK,EAAE,MAAA,IAAU,CAAA,CAAE,OAAA,IAAW,CAAA,CAAE,OAAA,EAAS;AAC9D,MAAA,kBAAA,CAAmB,OAAA,GAAA,CAAW,kBAAA,CAAmB,OAAA,GAAU,CAAA,CAAE,KAAK,WAAA,EAAY;AAC9E,MAAA,IAAI,iBAAA,CAAkB,OAAA,EAAS,YAAA,CAAa,iBAAA,CAAkB,OAAO,CAAA;AACrE,MAAA,iBAAA,CAAkB,OAAA,GAAU,WAAW,MAAM;AAAE,QAAA,kBAAA,CAAmB,OAAA,GAAU,EAAA;AAAA,MAAI,GAAG,GAAG,CAAA;AAEtF,MAAA,MAAM,SAAS,kBAAA,CAAmB,OAAA;AAClC,MAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,SAAA,CAAU,CAAC,CAAA,KAAM,CAAA,CAAE,KAAA,KAAU,WAAW,CAAC,CAAA;AAE3E,MAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,IAAK,IAAA,CAAK,QAAQ,CAAA,EAAA,EAAK;AACrC,QAAA,MAAM,GAAA,GAAA,CAAO,QAAA,GAAW,CAAA,IAAK,IAAA,CAAK,MAAA;AAClC,QAAA,MAAM,GAAA,GAAM,KAAK,GAAG,CAAA;AACpB,QAAA,IAAI,CAAC,GAAA,IAAO,GAAA,CAAI,QAAA,EAAU;AAC1B,QAAA,MAAM,WAAW,OAAO,GAAA,CAAI,KAAA,KAAU,QAAA,GAAW,IAAI,KAAA,GAAQ,EAAA;AAC7D,QAAA,IAAI,QAAA,CAAS,WAAA,EAAY,CAAE,UAAA,CAAW,MAAM,CAAA,EAAG;AAE7C,UAAA,MAAM,OAAO,UAAA,CAAW,OAAA;AACxB,UAAA,MAAM,MAAM,IAAA,EAAM,aAAA,CAAiC,CAAA,aAAA,EAAgB,GAAA,CAAI,KAAK,CAAA,EAAA,CAAI,CAAA;AAChF,UAAA,GAAA,EAAK,KAAA,EAAM;AACX,UAAA,GAAA,EAAK,KAAA,EAAM;AACX,UAAA;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAA;AAGA,IAAA,MAAM,eAAA,GAAkB,CAAC,GAAA,KAAgB,CAAC,CAAA,KAA0C;AAClF,MAAA,IAAI,CAAC,QAAA,EAAU;AACf,MAAA,YAAA,CAAa,OAAA,GAAU,GAAA;AACvB,MAAA,CAAA,CAAE,aAAa,aAAA,GAAgB,MAAA;AAAA,IACjC,CAAA;AACA,IAAA,MAAM,cAAA,GAAiB,CAAC,GAAA,KAAgB,CAAC,CAAA,KAA0C;AACjF,MAAA,IAAI,CAAC,QAAA,IAAY,CAAC,aAAa,OAAA,IAAW,YAAA,CAAa,YAAY,GAAA,EAAK;AACxE,MAAA,CAAA,CAAE,cAAA,EAAe;AACjB,MAAA,CAAA,CAAE,aAAa,UAAA,GAAa,MAAA;AAAA,IAC9B,CAAA;AACA,IAAA,MAAM,UAAA,GAAa,CAAC,GAAA,KAAgB,CAAC,CAAA,KAA0C;AAC7E,MAAA,IAAI,CAAC,QAAA,EAAU;AACf,MAAA,CAAA,CAAE,cAAA,EAAe;AACjB,MAAA,MAAM,OAAO,YAAA,CAAa,OAAA;AAC1B,MAAA,YAAA,CAAa,OAAA,GAAU,IAAA;AACvB,MAAA,IAAI,CAAC,IAAA,IAAQ,IAAA,KAAS,GAAA,IAAO,CAAC,SAAA,EAAW;AACzC,MAAA,MAAM,QAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,KAAK,CAAA;AACrC,MAAA,MAAM,OAAA,GAAU,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA;AAClC,MAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA;AAC/B,MAAA,IAAI,OAAA,KAAY,EAAA,IAAM,KAAA,KAAU,EAAA,EAAI;AACpC,MAAA,MAAM,IAAA,GAAO,CAAC,GAAG,KAAK,CAAA;AACtB,MAAA,IAAA,CAAK,MAAA,CAAO,SAAS,CAAC,CAAA;AACtB,MAAA,IAAA,CAAK,MAAA,CAAO,KAAA,EAAO,CAAA,EAAG,IAAI,CAAA;AAC1B,MAAA,SAAA,CAAU,IAAI,CAAA;AAAA,IAChB,CAAA;AAEA,IAAA,uBACE,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,SAAA,EAAW,SAAA;AAAA,QACX,kBAAA,EAAkB,WAAA;AAAA,QAClB,mBAAiB,UAAA,IAAc,MAAA;AAAA,QAC/B,SAAA,EAAW,eAAA;AAAA,QAEX,QAAA,EAAA;AAAA,0BAAA,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,gBAAA,EACZ,QAAA,EAAA;AAAA,YAAA,UAAA,IAAc,YAAY,IAAA,oBACzB,GAAA;AAAA,cAAC,QAAA;AAAA,cAAA;AAAA,gBACC,IAAA,EAAK,QAAA;AAAA,gBACL,SAAA,EAAU,uCAAA;AAAA,gBACV,YAAA,EAAW,aAAA;AAAA,gBACX,OAAA,EAAS,MAAM,cAAA,CAAe,IAAI,CAAA;AAAA,gBAElC,QAAA,kBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,OAAA,EAAQ,WAAA,EAAY,KAAA,EAAM,MAAK,MAAA,EAAO,IAAA,EAAK,IAAA,EAAK,MAAA,EAAO,MAAA,EAAO,cAAA,EAAe,aAAY,KAAA,EAAM,aAAA,EAAc,OAAA,EAAQ,cAAA,EAAe,OAAA,EAAQ,aAAA,EAAY,QAAO,QAAA,kBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAE,iBAAA,EAAiB,CAAA,EAAE;AAAA;AAAA,aACjM;AAAA,4BAEF,IAAA;AAAA,cAAC,KAAA;AAAA,cAAA;AAAA,gBACC,GAAA,EAAK,UAAA;AAAA,gBACL,IAAA,EAAK,SAAA;AAAA,gBACL,kBAAA,EAAkB,WAAA;AAAA,gBAClB,SAAA,EAAU,WAAA;AAAA,gBACV,cAAA,EAAc,OAAA;AAAA,gBACd,WAAA,EAAW,IAAA;AAAA,gBACX,WAAA,EAAW,IAAA;AAAA,gBACX,kBAAA,EAAkB,WAAA;AAAA,gBAClB,mBAAiB,UAAA,IAAc,MAAA;AAAA,gBAC/B,KAAA,EAAO,cAAA;AAAA,gBAEN,QAAA,EAAA;AAAA,kBAAA,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,EAAK,KAAA,KAAU;AACxB,oBAAA,MAAM,QAAA,GAAW,gBAAgB,GAAA,CAAI,KAAA;AACrC,oBAAA,MAAM,UAAA,GAAa,CAAC,CAAC,GAAA,CAAI,QAAA;AACzB,oBAAA,MAAM,QAAA,GAAW,YAAY,GAAA,CAAI,KAAA,EAAO,EAAE,QAAA,EAAU,GAAA,CAAI,UAAU,CAAA;AAClE,oBAAA,uBACE,IAAA;AAAA,sBAAC,QAAA;AAAA,sBAAA;AAAA,wBAEC,cAAY,GAAA,CAAI,KAAA;AAAA,wBAChB,eAAA,EAAe,IAAI,QAAA,IAAY,MAAA;AAAA,wBAC/B,SAAA,EAAU,UAAA;AAAA,wBACT,GAAG,QAAA;AAAA,wBACJ,SAAA,EAAW,QAAA,IAAY,CAAC,UAAA,GAAa,IAAA,GAAO,MAAA;AAAA,wBAC5C,WAAA,EAAa,QAAA,GAAW,eAAA,CAAgB,GAAA,CAAI,KAAK,CAAA,GAAI,MAAA;AAAA,wBACrD,UAAA,EAAY,QAAA,GAAW,cAAA,CAAe,GAAA,CAAI,KAAK,CAAA,GAAI,MAAA;AAAA,wBACnD,MAAA,EAAQ,QAAA,GAAW,UAAA,CAAW,GAAA,CAAI,KAAK,CAAA,GAAI,MAAA;AAAA,wBAE1C,QAAA,EAAA;AAAA,0BAAA,SAAA,GACG,SAAA,CAAU,EAAE,GAAA,EAAK,KAAA,EAAO,UAAU,UAAA,EAAY,IAC9C,GAAA,CAAI,KAAA;AAAA,0BACP,IAAI,QAAA,oBACH,GAAA;AAAA,4BAAC,MAAA;AAAA,4BAAA;AAAA,8BACC,SAAA,EAAU,YAAA;AAAA,8BACV,IAAA,EAAK,QAAA;AAAA,8BACL,QAAA,EAAU,EAAA;AAAA,8BACV,YAAA,EAAY,SAAS,OAAO,GAAA,CAAI,UAAU,QAAA,GAAW,GAAA,CAAI,QAAQ,KAAK,CAAA,CAAA;AAAA,8BACtE,OAAA,EAAS,CAAC,CAAA,KAAM;AACd,gCAAA,CAAA,CAAE,eAAA,EAAgB;AAClB,gCAAA,UAAA,GAAa,IAAI,KAAK,CAAA;AAAA,8BACxB,CAAA;AAAA,8BAEA,QAAA,kBAAA,GAAA,CAAC,SAAI,OAAA,EAAQ,WAAA,EAAY,OAAM,IAAA,EAAK,MAAA,EAAO,IAAA,EAAK,IAAA,EAAK,MAAA,EAAO,MAAA,EAAO,gBAAe,WAAA,EAAY,KAAA,EAAM,eAAc,OAAA,EAAQ,aAAA,EAAY,QACpI,QAAA,kBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAE,mBAAA,EAAmB,CAAA,EAC7B;AAAA;AAAA;AACF;AAAA,uBAAA;AAAA,sBA3BG,GAAA,CAAI;AAAA,qBA6BX;AAAA,kBAEJ,CAAC,CAAA;AAAA,kBAAA,CACC,OAAA,KAAY,MAAA,IAAU,OAAA,KAAY,OAAA,IAAW,OAAA,KAAY,MAAA,qBACzD,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,gBAAA,EAAiB,aAAA,EAAY,MAAA,EAAO;AAAA;AAAA;AAAA,aAExD;AAAA,YACC,UAAA,IAAc,YAAY,KAAA,oBACzB,GAAA;AAAA,cAAC,QAAA;AAAA,cAAA;AAAA,gBACC,IAAA,EAAK,QAAA;AAAA,gBACL,SAAA,EAAU,wCAAA;AAAA,gBACV,YAAA,EAAW,cAAA;AAAA,gBACX,OAAA,EAAS,MAAM,cAAA,CAAe,GAAG,CAAA;AAAA,gBAEjC,QAAA,kBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,OAAA,EAAQ,WAAA,EAAY,KAAA,EAAM,MAAK,MAAA,EAAO,IAAA,EAAK,IAAA,EAAK,MAAA,EAAO,MAAA,EAAO,cAAA,EAAe,aAAY,KAAA,EAAM,aAAA,EAAc,OAAA,EAAQ,cAAA,EAAe,OAAA,EAAQ,aAAA,EAAY,QAAO,QAAA,kBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAE,gBAAA,EAAgB,CAAA,EAAE;AAAA;AAAA;AAChM,WAAA,EAEJ,CAAA;AAAA,8BACC,KAAA,EAAA,EAAI,SAAA,EAAU,eACZ,QAAA,EAAA,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,KAAQ;AACjB,YAAA,MAAM,QAAA,GAAW,gBAAgB,GAAA,CAAI,KAAA;AACrC,YAAA,MAAM,YAAA,GACJ,cAAc,CAAC,SAAA,IAAa,aAAa,OAAA,CAAQ,GAAA,CAAI,IAAI,KAAK,CAAA;AAChE,YAAA,uBACE,GAAA;AAAA,cAAC,KAAA;AAAA,cAAA;AAAA,gBAEC,SAAA,EAAU,YAAA;AAAA,gBACT,GAAG,aAAA,CAAc,GAAA,CAAI,KAAK,CAAA;AAAA,gBAE1B,QAAA,EAAA,YAAA,GACG,cACE,WAAA,CAAY,EAAE,KAAK,QAAA,EAAU,CAAA,GAC7B,GAAA,CAAI,OAAA,GACN;AAAA,eAAA;AAAA,cARC,GAAA,CAAI;AAAA,aASX;AAAA,UAEJ,CAAC,CAAA,EACH;AAAA;AAAA;AAAA,KACF;AAAA,EAEJ;AACF","file":"styled.js","sourcesContent":["import {\n type CSSProperties,\n type ReactNode,\n forwardRef,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from \"react\";\nimport {\n useTabs,\n type TabsActivation,\n type TabsChangeReason,\n type TabsOrientation,\n} from \"../useTabs\";\n\nexport type TabsVariant = \"line\" | \"solid\" | \"pill\";\nexport type TabsSize = \"sm\" | \"md\" | \"lg\";\nexport type TabsTone = \"neutral\" | \"primary\" | \"success\" | \"danger\";\n\nexport interface TabItem {\n value: string;\n label: ReactNode;\n content: ReactNode;\n disabled?: boolean;\n /** When true, renders a × button on the tab. Pair with `onTabClose`. */\n closable?: boolean;\n}\n\nexport interface TabsRenderTabContext {\n tab: TabItem;\n index: number;\n isActive: boolean;\n isDisabled: boolean;\n}\n\nexport interface TabsRenderPanelContext {\n tab: TabItem;\n isActive: boolean;\n}\n\nexport interface TabsStyledProps {\n tabs: TabItem[];\n variant?: TabsVariant;\n size?: TabsSize;\n tone?: TabsTone;\n defaultValue?: string;\n value?: string;\n /** Optional second arg reports the trigger reason (\"click\" | \"keyboard\" | \"programmatic\"). */\n onChange?: (value: string, reason: TabsChangeReason) => void;\n /** \"automatic\" (default) — arrow keys move focus AND activate. \"manual\" — arrows move focus only. */\n activation?: TabsActivation;\n /** Affects keyboard nav direction. Default \"horizontal\". */\n orientation?: TabsOrientation;\n /** Only mount panels after they've been activated at least once. Default: false (all panels mount eagerly). */\n lazyMount?: boolean;\n /** Keep all panels mounted regardless of activation (useful with `lazyMount` overrides). Default: false. */\n forceMount?: boolean;\n /** Fires when the × on a closable tab is clicked. */\n onTabClose?: (value: string) => void;\n /** When true, the tab list scrolls horizontally with chevron buttons at the edges instead of wrapping. */\n scrollable?: boolean;\n /** When true, tabs can be reordered by dragging. Fires `onReorder` with the new value order. */\n sortable?: boolean;\n onReorder?: (values: string[]) => void;\n /** When true, the active tab is scrolled into view on activation. Default: true when `scrollable`. */\n scrollActiveIntoView?: boolean;\n /** Replace the default tab button content. The button shell (a11y, ref, key) stays owned by the component. */\n renderTab?: (ctx: TabsRenderTabContext) => ReactNode;\n /** Replace the default panel rendering. */\n renderPanel?: (ctx: TabsRenderPanelContext) => ReactNode;\n className?: string;\n}\n\n// Run layout effects on the client; fall back to a no-op effect on the server.\nconst useIsoLayoutEffect =\n typeof window !== \"undefined\" ? useLayoutEffect : useEffect;\n\nfunction useIndicator(\n tabListRef: React.RefObject<HTMLDivElement | null>,\n activeValue: string | undefined,\n orientation: TabsOrientation,\n) {\n const [style, setStyle] = useState<CSSProperties>({});\n\n useIsoLayoutEffect(() => {\n if (!tabListRef.current || activeValue === undefined) return;\n\n const measure = () => {\n const list = tabListRef.current;\n if (!list) return;\n const activeTab = list.querySelector<HTMLElement>(\n `[aria-selected=\"true\"]`,\n );\n if (!activeTab) return;\n\n const listRect = list.getBoundingClientRect();\n const tabRect = activeTab.getBoundingClientRect();\n\n if (orientation === \"vertical\") {\n const y = tabRect.top - listRect.top;\n const h = tabRect.height;\n setStyle({\n [\"--rtab-indicator-y\" as string]: `${y}px`,\n [\"--rtab-indicator-height\" as string]: `${h}px`,\n [\"--rtab-indicator-ready\" as string]: \"1\",\n });\n } else {\n const x = tabRect.left - listRect.left;\n const w = tabRect.width;\n setStyle({\n [\"--rtab-indicator-x\" as string]: `${x}px`,\n [\"--rtab-indicator-width\" as string]: `${w}px`,\n [\"--rtab-indicator-ready\" as string]: \"1\",\n });\n }\n };\n\n measure();\n\n if (typeof ResizeObserver === \"undefined\") return;\n const ro = new ResizeObserver(measure);\n ro.observe(tabListRef.current);\n return () => ro.disconnect();\n }, [activeValue, tabListRef, orientation]);\n\n return style;\n}\n\nexport const TabsStyled = forwardRef<HTMLDivElement, TabsStyledProps>(\n function TabsStyled(\n {\n tabs,\n variant = \"line\",\n size = \"md\",\n tone = \"neutral\",\n defaultValue,\n value,\n onChange,\n activation = \"automatic\",\n orientation = \"horizontal\",\n lazyMount = false,\n forceMount = false,\n onTabClose,\n scrollable = false,\n sortable = false,\n onReorder,\n scrollActiveIntoView,\n renderTab,\n renderPanel,\n className,\n },\n ref,\n ) {\n const autoScrollActive = scrollActiveIntoView ?? scrollable;\n const [scrollState, setScrollState] = useState<{ left: boolean; right: boolean }>({\n left: false,\n right: false,\n });\n const typeaheadBufferRef = useRef(\"\");\n const typeaheadTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const dragValueRef = useRef<string | null>(null);\n const tabDefs = tabs.map((t) => ({ value: t.value, disabled: t.disabled }));\n\n const resolvedDefault =\n defaultValue ?? tabs.find((t) => !t.disabled)?.value;\n\n const { activeValue, getTabProps, getPanelProps } = useTabs({\n tabs: tabDefs,\n defaultValue: value === undefined ? resolvedDefault : undefined,\n value,\n onChange,\n activation,\n orientation,\n });\n\n const tabListRef = useRef<HTMLDivElement>(null);\n const indicatorStyle = useIndicator(tabListRef, activeValue, orientation);\n\n // Track which tabs have been activated for lazyMount.\n const activatedRef = useRef<Set<string>>(new Set());\n if (activeValue !== undefined) activatedRef.current.add(activeValue);\n\n const rootClass = [\"rtab-root\", className].filter(Boolean).join(\" \");\n\n // Scroll-state: detect when there's content off the left/right edges so we can\n // toggle the chevron buttons. Only relevant when scrollable.\n useEffect(() => {\n if (!scrollable) return;\n const list = tabListRef.current;\n if (!list) return;\n const update = () => {\n setScrollState({\n left: list.scrollLeft > 4,\n right: list.scrollLeft + list.clientWidth < list.scrollWidth - 4,\n });\n };\n update();\n list.addEventListener(\"scroll\", update, { passive: true });\n const ro = typeof ResizeObserver !== \"undefined\" ? new ResizeObserver(update) : null;\n ro?.observe(list);\n return () => {\n list.removeEventListener(\"scroll\", update);\n ro?.disconnect();\n };\n }, [scrollable, tabs.length]);\n\n // Auto-scroll active tab into view\n useEffect(() => {\n if (!autoScrollActive || !activeValue) return;\n const list = tabListRef.current;\n if (!list) return;\n const activeEl = list.querySelector<HTMLElement>(`[aria-selected=\"true\"]`);\n if (!activeEl) return;\n activeEl.scrollIntoView({ behavior: \"smooth\", block: \"nearest\", inline: \"nearest\" });\n }, [autoScrollActive, activeValue]);\n\n const scrollByAmount = (delta: number) => {\n tabListRef.current?.scrollBy({ left: delta, behavior: \"smooth\" });\n };\n\n // Typeahead: letters jump to the next tab whose label starts with the buffer.\n const handleTypeahead = (e: React.KeyboardEvent<HTMLDivElement>) => {\n if (e.key.length !== 1 || e.altKey || e.ctrlKey || e.metaKey) return;\n typeaheadBufferRef.current = (typeaheadBufferRef.current + e.key).toLowerCase();\n if (typeaheadTimerRef.current) clearTimeout(typeaheadTimerRef.current);\n typeaheadTimerRef.current = setTimeout(() => { typeaheadBufferRef.current = \"\"; }, 600);\n\n const buffer = typeaheadBufferRef.current;\n const startIdx = Math.max(0, tabs.findIndex((t) => t.value === activeValue));\n // Search starting just after the active tab, wrapping around.\n for (let i = 1; i <= tabs.length; i++) {\n const idx = (startIdx + i) % tabs.length;\n const tab = tabs[idx];\n if (!tab || tab.disabled) continue;\n const labelStr = typeof tab.label === \"string\" ? tab.label : \"\";\n if (labelStr.toLowerCase().startsWith(buffer)) {\n // Use the existing tab button's click via aria-selected target.\n const list = tabListRef.current;\n const btn = list?.querySelector<HTMLButtonElement>(`[data-value=\"${tab.value}\"]`);\n btn?.click();\n btn?.focus();\n break;\n }\n }\n };\n\n // Drag-to-reorder: HTML5 DnD is fine here — no external lib.\n const handleDragStart = (val: string) => (e: React.DragEvent<HTMLButtonElement>) => {\n if (!sortable) return;\n dragValueRef.current = val;\n e.dataTransfer.effectAllowed = \"move\";\n };\n const handleDragOver = (val: string) => (e: React.DragEvent<HTMLButtonElement>) => {\n if (!sortable || !dragValueRef.current || dragValueRef.current === val) return;\n e.preventDefault();\n e.dataTransfer.dropEffect = \"move\";\n };\n const handleDrop = (val: string) => (e: React.DragEvent<HTMLButtonElement>) => {\n if (!sortable) return;\n e.preventDefault();\n const from = dragValueRef.current;\n dragValueRef.current = null;\n if (!from || from === val || !onReorder) return;\n const order = tabs.map((t) => t.value);\n const fromIdx = order.indexOf(from);\n const toIdx = order.indexOf(val);\n if (fromIdx === -1 || toIdx === -1) return;\n const next = [...order];\n next.splice(fromIdx, 1);\n next.splice(toIdx, 0, from);\n onReorder(next);\n };\n\n return (\n <div\n ref={ref}\n className={rootClass}\n data-orientation={orientation}\n data-scrollable={scrollable || undefined}\n onKeyDown={handleTypeahead}\n >\n <div className=\"rtab-list-wrap\">\n {scrollable && scrollState.left && (\n <button\n type=\"button\"\n className=\"rtab-scroll-btn rtab-scroll-btn--left\"\n aria-label=\"Scroll left\"\n onClick={() => scrollByAmount(-200)}\n >\n <svg viewBox=\"0 0 12 12\" width=\"12\" height=\"12\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\"><path d=\"M7.5 3l-3 3 3 3\"/></svg>\n </button>\n )}\n <div\n ref={tabListRef}\n role=\"tablist\"\n aria-orientation={orientation}\n className=\"rtab-list\"\n data-variant={variant}\n data-size={size}\n data-tone={tone}\n data-orientation={orientation}\n data-scrollable={scrollable || undefined}\n style={indicatorStyle}\n >\n {tabs.map((tab, index) => {\n const isActive = activeValue === tab.value;\n const isDisabled = !!tab.disabled;\n const tabProps = getTabProps(tab.value, { disabled: tab.disabled });\n return (\n <button\n key={tab.value}\n data-value={tab.value}\n data-closable={tab.closable || undefined}\n className=\"rtab-tab\"\n {...tabProps}\n draggable={sortable && !isDisabled ? true : undefined}\n onDragStart={sortable ? handleDragStart(tab.value) : undefined}\n onDragOver={sortable ? handleDragOver(tab.value) : undefined}\n onDrop={sortable ? handleDrop(tab.value) : undefined}\n >\n {renderTab\n ? renderTab({ tab, index, isActive, isDisabled })\n : tab.label}\n {tab.closable && (\n <span\n className=\"rtab-close\"\n role=\"button\"\n tabIndex={-1}\n aria-label={`Close ${typeof tab.label === \"string\" ? tab.label : \"tab\"}`}\n onClick={(e) => {\n e.stopPropagation();\n onTabClose?.(tab.value);\n }}\n >\n <svg viewBox=\"0 0 12 12\" width=\"10\" height=\"10\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" aria-hidden=\"true\">\n <path d=\"M3 3l6 6M9 3l-6 6\"/>\n </svg>\n </span>\n )}\n </button>\n );\n })}\n {(variant === \"line\" || variant === \"solid\" || variant === \"pill\") && (\n <span className=\"rtab-indicator\" aria-hidden=\"true\" />\n )}\n </div>\n {scrollable && scrollState.right && (\n <button\n type=\"button\"\n className=\"rtab-scroll-btn rtab-scroll-btn--right\"\n aria-label=\"Scroll right\"\n onClick={() => scrollByAmount(200)}\n >\n <svg viewBox=\"0 0 12 12\" width=\"12\" height=\"12\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\"><path d=\"M4.5 3l3 3-3 3\"/></svg>\n </button>\n )}\n </div>\n <div className=\"rtab-panels\">\n {tabs.map((tab) => {\n const isActive = activeValue === tab.value;\n const shouldRender =\n forceMount || !lazyMount || activatedRef.current.has(tab.value);\n return (\n <div\n key={tab.value}\n className=\"rtab-panel\"\n {...getPanelProps(tab.value)}\n >\n {shouldRender\n ? renderPanel\n ? renderPanel({ tab, isActive })\n : tab.content\n : null}\n </div>\n );\n })}\n </div>\n </div>\n );\n },\n);\n"]}
package/dist/styles.css CHANGED
@@ -328,3 +328,91 @@
328
328
 
329
329
  [data-theme="dark"] .rtab-list[data-tone="success"] { --rtab-fg-active: #bbf7d0; --rtab-bg-tab-active: #14532d; --rtab-bg-indicator: #4ade80; }
330
330
  [data-theme="dark"] .rtab-list[data-tone="danger"] { --rtab-fg-active: #fecaca; --rtab-bg-tab-active: #7f1d1d; --rtab-bg-indicator: #f87171; }
331
+
332
+ /* ============================================================================
333
+ * Closeable / scrollable / sortable tabs (1.x additions)
334
+ * ============================================================================ */
335
+
336
+ /* Wrap that holds the list + the chevron scroll buttons */
337
+ .rtab-list-wrap {
338
+ position: relative;
339
+ display: flex;
340
+ align-items: stretch;
341
+ }
342
+
343
+ /* Scrollable mode — list itself becomes a horizontal scroll container */
344
+ .rtab-list[data-scrollable] {
345
+ overflow-x: auto;
346
+ scrollbar-width: none; /* Firefox */
347
+ scroll-behavior: smooth;
348
+ flex-wrap: nowrap;
349
+ }
350
+ .rtab-list[data-scrollable]::-webkit-scrollbar { display: none; }
351
+ .rtab-list[data-scrollable] .rtab-tab {
352
+ flex-shrink: 0;
353
+ }
354
+
355
+ /* Chevron scroll buttons */
356
+ .rtab-scroll-btn {
357
+ appearance: none;
358
+ background: var(--rtab-bg, transparent);
359
+ border: none;
360
+ width: 28px;
361
+ flex-shrink: 0;
362
+ display: inline-flex;
363
+ align-items: center;
364
+ justify-content: center;
365
+ cursor: pointer;
366
+ color: var(--rtab-fg-muted, currentColor);
367
+ opacity: 0.85;
368
+ transition: opacity 120ms ease, background 120ms ease;
369
+ }
370
+ .rtab-scroll-btn:hover { opacity: 1; }
371
+ .rtab-scroll-btn:focus-visible {
372
+ outline: 2px solid var(--rtab-color-active, #6366f1);
373
+ outline-offset: 1px;
374
+ }
375
+ .rtab-scroll-btn--left {
376
+ background: linear-gradient(to right, var(--rtab-bg, var(--bg, #fff)) 50%, transparent);
377
+ }
378
+ .rtab-scroll-btn--right {
379
+ background: linear-gradient(to left, var(--rtab-bg, var(--bg, #fff)) 50%, transparent);
380
+ }
381
+
382
+ /* Close (×) button on closable tabs */
383
+ .rtab-tab[data-closable] {
384
+ padding-right: 0.4rem;
385
+ }
386
+ .rtab-close {
387
+ display: inline-flex;
388
+ align-items: center;
389
+ justify-content: center;
390
+ width: 16px;
391
+ height: 16px;
392
+ margin-left: 0.4rem;
393
+ border-radius: 3px;
394
+ cursor: pointer;
395
+ color: var(--rtab-fg-muted, currentColor);
396
+ opacity: 0.55;
397
+ transition: background 120ms ease, opacity 120ms ease;
398
+ }
399
+ .rtab-close:hover {
400
+ opacity: 1;
401
+ background: var(--rtab-close-hover-bg, rgba(0, 0, 0, 0.08));
402
+ }
403
+ [data-theme="dark"] .rtab-close:hover {
404
+ background: var(--rtab-close-hover-bg, rgba(255, 255, 255, 0.12));
405
+ }
406
+
407
+ /* Drag-to-reorder visuals */
408
+ .rtab-tab[draggable="true"] { cursor: grab; }
409
+ .rtab-tab[draggable="true"]:active { cursor: grabbing; }
410
+ .rtab-tab[draggable="true"]:hover {
411
+ background: var(--rtab-tab-hover-bg, rgba(0, 0, 0, 0.04));
412
+ }
413
+
414
+ @media (prefers-reduced-motion: reduce) {
415
+ .rtab-list { scroll-behavior: auto; }
416
+ .rtab-scroll-btn { transition: none; }
417
+ .rtab-close { transition: none; }
418
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mshafiqyajid/react-tabs",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Headless tabs hook and styled component for React. Accessible, keyboard-friendly, animated, SSR-safe, fully typed.",
5
5
  "keywords": [
6
6
  "react",