@theguild/components 9.11.2-alpha-20251124160801-d2ae58aaa5d903a684e7bb9879e57719af6deb1e → 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.
- package/dist/components/legacy-package-cmd.js +2 -1
- package/dist/components/marketplace-search.js +1 -1
- package/dist/components/tabs/index.client.d.mts +29 -0
- package/dist/components/tabs/index.client.js +214 -0
- package/dist/components/tabs/index.d.mts +54 -0
- package/dist/components/tabs/index.js +9 -0
- package/dist/components/use-hash.d.mts +3 -0
- package/dist/components/use-hash.js +16 -0
- package/package.json +3 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useMemo } from "react";
|
|
3
|
-
import { Pre
|
|
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 "
|
|
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,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.
|
|
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",
|