@theguild/components 9.11.2 → 9.12.0-alpha-20251204002326-2a985828907ff904a08269604e8ed510d0fd16c4

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.
@@ -1,6 +1,7 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { useMemo } from "react";
3
- import { Pre, Tabs } from "nextra/components";
3
+ import { Pre } from "nextra/components";
4
+ import { Tabs } from "./tabs";
4
5
  const PACKAGE_MANAGERS = ["yarn", "npm", "pnpm"];
5
6
  const Add = {
6
7
  yarn: "yarn add",
@@ -2,7 +2,7 @@
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { isValidElement, useMemo, useState } from "react";
4
4
  import fuzzy from "fuzzy";
5
- import { Tabs } from "nextra/components";
5
+ import { Tabs } from "./tabs";
6
6
  import { cn } from "../cn";
7
7
  import { Heading } from "./heading";
8
8
  import { CloseIcon, SearchIcon } from "./icons";
@@ -0,0 +1,29 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactElement, ReactNode, FC } from 'react';
3
+ import { TabGroupProps, TabListProps, TabProps, TabPanelProps } from '@headlessui/react';
4
+
5
+ type TabItem = string | ReactElement;
6
+ type TabObjectItem = {
7
+ key?: string;
8
+ label: TabItem;
9
+ disabled: boolean;
10
+ };
11
+ interface TabsProps extends Pick<TabGroupProps, 'defaultIndex' | 'selectedIndex' | 'onChange'> {
12
+ items: (TabItem | TabObjectItem)[];
13
+ children: ReactNode;
14
+ /**
15
+ * URLSearchParams key for persisting the selected tab.
16
+ * @default "tab"
17
+ */
18
+ searchParamKey?: string;
19
+ /** LocalStorage key for persisting the selected tab. */
20
+ storageKey?: string;
21
+ /** Tabs CSS class name. */
22
+ className?: TabListProps['className'];
23
+ /** Tab CSS class name. */
24
+ tabClassName?: TabProps['className'];
25
+ }
26
+ declare const Tabs: ({ items, children, searchParamKey, storageKey, defaultIndex, selectedIndex: _selectedIndex, onChange, className, tabClassName, }: TabsProps) => react_jsx_runtime.JSX.Element;
27
+ declare const Tab: FC<TabPanelProps>;
28
+
29
+ export { Tab, Tabs, type TabsProps };
@@ -0,0 +1,214 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import {
4
+ Fragment,
5
+ useEffect,
6
+ useId,
7
+ useLayoutEffect,
8
+ useRef,
9
+ useState
10
+ } from "react";
11
+ import { useSearchParams } from "next/navigation";
12
+ import cn from "clsx";
13
+ import {
14
+ Tab as HeadlessTab,
15
+ TabGroup,
16
+ TabList,
17
+ TabPanel,
18
+ TabPanels
19
+ } from "@headlessui/react";
20
+ import { useHash } from "../use-hash";
21
+ function isTabObjectItem(item) {
22
+ return !!item && typeof item === "object" && "label" in item;
23
+ }
24
+ const Tabs = ({
25
+ items,
26
+ children,
27
+ searchParamKey = "tab",
28
+ storageKey,
29
+ defaultIndex = 0,
30
+ selectedIndex: _selectedIndex,
31
+ onChange,
32
+ className,
33
+ tabClassName
34
+ }) => {
35
+ const id = useId();
36
+ storageKey ??= `tabs-${id}`;
37
+ let [selectedIndex, setSelectedIndex] = useState(defaultIndex);
38
+ if (_selectedIndex !== void 0) {
39
+ selectedIndex = _selectedIndex;
40
+ }
41
+ const tabPanelsRef = useRef(null);
42
+ const tabIndexFromSearchParams = useActiveTabFromURL(
43
+ tabPanelsRef,
44
+ items,
45
+ searchParamKey,
46
+ setSelectedIndex,
47
+ id
48
+ );
49
+ useActiveTabFromStorage(storageKey, items, setSelectedIndex, tabIndexFromSearchParams !== -1, id);
50
+ const handleChange = (index) => {
51
+ onChange?.(index);
52
+ if (storageKey) {
53
+ const newValue = getTabKey(items, index, id);
54
+ localStorage.setItem(storageKey, newValue);
55
+ window.dispatchEvent(new StorageEvent("storage", { key: storageKey, newValue }));
56
+ } else {
57
+ setSelectedIndex(index);
58
+ }
59
+ if (searchParamKey) {
60
+ const searchParams = new URLSearchParams(window.location.search);
61
+ searchParams.set(searchParamKey, getTabKey(items, index, id));
62
+ window.history.replaceState(
63
+ null,
64
+ "",
65
+ `${window.location.pathname}?${searchParams.toString()}`
66
+ );
67
+ }
68
+ };
69
+ return /* @__PURE__ */ jsxs(
70
+ TabGroup,
71
+ {
72
+ selectedIndex,
73
+ defaultIndex,
74
+ onChange: handleChange,
75
+ as: Fragment,
76
+ children: [
77
+ /* @__PURE__ */ jsx(
78
+ TabList,
79
+ {
80
+ className: (args) => cn(
81
+ "nextra-scrollbar overflow-x-auto overflow-y-hidden overscroll-x-contain",
82
+ "mt-4 flex w-full gap-2 border-b border-beige-200 pb-px dark:border-neutral-800",
83
+ "focus-visible:hive-focus",
84
+ typeof className === "function" ? className(args) : className
85
+ ),
86
+ children: items.map((item, index) => /* @__PURE__ */ jsx(
87
+ HeadlessTab,
88
+ {
89
+ disabled: isTabObjectItem(item) && item.disabled,
90
+ className: (args) => {
91
+ const { selected, disabled, hover, focus } = args;
92
+ return cn(
93
+ focus && "hive-focus ring-inset",
94
+ "cursor-pointer whitespace-nowrap",
95
+ "rounded-t p-2 font-medium leading-5 transition-colors",
96
+ "-mb-0.5 select-none border-b-2",
97
+ selected ? "border-current outline-none" : hover ? "border-beige-200 dark:border-neutral-800" : "border-transparent",
98
+ selected ? "text-green-900 dark:text-primary" : disabled ? "pointer-events-none text-beige-400 dark:text-neutral-600" : hover ? "text-black dark:text-white" : "text-beige-600 dark:text-beige-200",
99
+ typeof tabClassName === "function" ? tabClassName(args) : tabClassName
100
+ );
101
+ },
102
+ children: isTabObjectItem(item) ? item.label : item
103
+ },
104
+ index
105
+ ))
106
+ }
107
+ ),
108
+ /* @__PURE__ */ jsx(TabPanels, { ref: tabPanelsRef, children })
109
+ ]
110
+ }
111
+ );
112
+ };
113
+ const Tab = ({
114
+ children,
115
+ // For SEO display all the Panel in the DOM and set `display: none;` for those that are not selected
116
+ unmount = false,
117
+ className,
118
+ ...props
119
+ }) => {
120
+ return /* @__PURE__ */ jsx(
121
+ TabPanel,
122
+ {
123
+ ...props,
124
+ unmount,
125
+ className: (args) => cn(
126
+ "mt-[1.25em] rounded",
127
+ args.focus && "hive-focus",
128
+ typeof className === "function" ? className(args) : className
129
+ ),
130
+ children
131
+ }
132
+ );
133
+ };
134
+ function useActiveTabFromURL(tabPanelsRef, items, searchParamKey, setSelectedIndex, id) {
135
+ const hash = useHash();
136
+ const searchParams = useSearchParams();
137
+ const tabsInSearchParams = searchParams.getAll(searchParamKey).sort();
138
+ const tabIndexFromSearchParams = items.findIndex(
139
+ (_, index) => tabsInSearchParams.includes(getTabKey(items, index, id))
140
+ );
141
+ useIsomorphicLayoutEffect(() => {
142
+ const tabPanel = hash ? tabPanelsRef.current?.querySelector(`[role=tabpanel]:has([id="${hash}"])`) : null;
143
+ if (tabPanel) {
144
+ let index = 0;
145
+ for (const el of tabPanelsRef.current.children) {
146
+ if (el === tabPanel) {
147
+ setSelectedIndex(Number(index));
148
+ location.hash = "";
149
+ requestAnimationFrame(() => location.hash = `#${hash}`);
150
+ }
151
+ index++;
152
+ }
153
+ } else if (tabIndexFromSearchParams !== -1) {
154
+ setSelectedIndex(tabIndexFromSearchParams);
155
+ }
156
+ return function cleanUpTabFromSearchParams() {
157
+ const newSearchParams = new URLSearchParams(window.location.search);
158
+ newSearchParams.delete(searchParamKey);
159
+ window.history.replaceState(
160
+ null,
161
+ "",
162
+ `${window.location.pathname}?${newSearchParams.toString()}`
163
+ );
164
+ };
165
+ }, [hash, tabsInSearchParams.join(",")]);
166
+ return tabIndexFromSearchParams;
167
+ }
168
+ function useActiveTabFromStorage(storageKey, items, setSelectedIndex, ignoreLocalStorage, id) {
169
+ useIsomorphicLayoutEffect(() => {
170
+ if (!storageKey || ignoreLocalStorage) {
171
+ return;
172
+ }
173
+ const setSelectedTab = (key) => {
174
+ const index = items.findIndex((_, i) => getTabKey(items, i, id) === key);
175
+ if (index !== -1) {
176
+ setSelectedIndex(index);
177
+ }
178
+ };
179
+ function onStorageChange(event) {
180
+ if (event.key === storageKey) {
181
+ const value2 = event.newValue;
182
+ if (value2) {
183
+ setSelectedTab(value2);
184
+ }
185
+ }
186
+ }
187
+ const value = localStorage.getItem(storageKey);
188
+ if (value) {
189
+ setSelectedTab(value);
190
+ }
191
+ window.addEventListener("storage", onStorageChange);
192
+ return () => {
193
+ window.removeEventListener("storage", onStorageChange);
194
+ };
195
+ }, [storageKey]);
196
+ }
197
+ function getTabKey(items, index, prefix) {
198
+ const item = items[index];
199
+ const isObject = isTabObjectItem(item);
200
+ if (isObject && item.key) {
201
+ return item.key;
202
+ }
203
+ const label = isObject ? item.label : item;
204
+ const key = typeof label === "string" ? slugify(label) : `${prefix}-${index.toString()}`;
205
+ return key;
206
+ }
207
+ function slugify(label) {
208
+ return label.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
209
+ }
210
+ const useIsomorphicLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
211
+ export {
212
+ Tab,
213
+ Tabs
214
+ };
@@ -0,0 +1,54 @@
1
+ import * as react from 'react';
2
+ import { ComponentProps } from 'react';
3
+ import * as _headlessui_react from '@headlessui/react';
4
+ import * as react_jsx_runtime from 'react/jsx-runtime';
5
+ import { Tabs as Tabs$1 } from './index.client.mjs';
6
+ export { TabsProps } from './index.client.mjs';
7
+
8
+ /**
9
+ * A built-in component for creating tabbed content, helping organize related information in a
10
+ * compact, interactive layout.
11
+ *
12
+ * @example
13
+ * <Tabs items={['pnpm', 'npm', 'yarn']}>
14
+ * <Tabs.Tab>**pnpm**: Fast, disk space efficient package manager.</Tabs.Tab>
15
+ * <Tabs.Tab>**npm** is a package manager for the JavaScript programming language.</Tabs.Tab>
16
+ * <Tabs.Tab>**Yarn** is a software packaging system.</Tabs.Tab>
17
+ * </Tabs>
18
+ *
19
+ * @usage
20
+ * ```mdx
21
+ * import { Tabs } from '@theguild/components'
22
+ *
23
+ * <Tabs items={['pnpm', 'npm', 'yarn']}>
24
+ * <Tabs.Tab>**pnpm**: Fast, disk space efficient package manager.</Tabs.Tab>
25
+ * <Tabs.Tab>**npm** is a package manager for the JavaScript programming language.</Tabs.Tab>
26
+ * <Tabs.Tab>**Yarn** is a software packaging system.</Tabs.Tab>
27
+ * </Tabs>
28
+ * ```
29
+ *
30
+ * ### Default Selected Index
31
+ *
32
+ * You can use the `defaultIndex` prop to set the default tab index:
33
+ *
34
+ * ```mdx /defaultIndex="1"/
35
+ * import { Tabs } from '@theguild/components'
36
+ *
37
+ * <Tabs items={['pnpm', 'npm', 'yarn']} defaultIndex="1">
38
+ * ...
39
+ * </Tabs>
40
+ * ```
41
+ *
42
+ * And you will have `npm` as the default tab:
43
+ *
44
+ * <Tabs items={['pnpm', 'npm', 'yarn']} defaultIndex="1">
45
+ * <Tabs.Tab>**pnpm**: Fast, disk space efficient package manager.</Tabs.Tab>
46
+ * <Tabs.Tab>**npm** is a package manager for the JavaScript programming language.</Tabs.Tab>
47
+ * <Tabs.Tab>**Yarn** is a software packaging system.</Tabs.Tab>
48
+ * </Tabs>
49
+ */
50
+ declare const Tabs: ((props: ComponentProps<typeof Tabs$1>) => react_jsx_runtime.JSX.Element) & {
51
+ Tab: react.FC<_headlessui_react.TabPanelProps>;
52
+ };
53
+
54
+ export { Tabs };
@@ -0,0 +1,9 @@
1
+ "use no memo";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { Tabs as _Tabs, Tab } from "./index.client";
4
+ const Tabs = Object.assign((props) => /* @__PURE__ */ jsx(_Tabs, { ...props }), {
5
+ Tab
6
+ });
7
+ export {
8
+ Tabs
9
+ };
@@ -0,0 +1,3 @@
1
+ declare function useHash(): string;
2
+
3
+ export { useHash };
@@ -0,0 +1,16 @@
1
+ "use client";
2
+ "use no memo";
3
+ import { useEffect, useState } from "react";
4
+ function useHash() {
5
+ const [hash, setHash] = useState("");
6
+ useEffect(() => {
7
+ const handleHashChange = () => setHash(location.hash.replace("#", ""));
8
+ handleHashChange();
9
+ window.addEventListener("hashchange", handleHashChange);
10
+ return () => window.removeEventListener("hashchange", handleHashChange);
11
+ }, []);
12
+ return hash;
13
+ }
14
+ export {
15
+ useHash
16
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theguild/components",
3
- "version": "9.11.2",
3
+ "version": "9.12.0-alpha-20251204002326-2a985828907ff904a08269604e8ed510d0fd16c4",
4
4
  "repository": {
5
5
  "url": "https://github.com/the-guild-org/docs",
6
6
  "directory": "packages/components"
@@ -41,7 +41,8 @@
41
41
  "@theguild/tailwind-config": "^0.6.3",
42
42
  "next": "^13 || ^14 || ^15.0.0",
43
43
  "react": "^18.2.0",
44
- "react-dom": "^18.2.0"
44
+ "react-dom": "^18.2.0",
45
+ "@headlessui/react": "2.2.0"
45
46
  },
46
47
  "dependencies": {
47
48
  "@giscus/react": "3.1.0",