@dbosoft/nextjs-uicore 1.5.1 → 1.6.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.
@@ -1,46 +1,46 @@
1
- import clsx from 'clsx'
2
- import LinkButton from '@dbosoft/react-uicore/linkbutton'
3
- import Link from 'next/link'
4
- import type { ICtaItem } from '../..'
5
- import GithubStarsLink from './github-stars-link'
6
-
7
- function CtaLinks(props: {
8
- links: ICtaItem[],
9
- hideGithubStars: boolean,
10
- isInDropdown: boolean
11
- }) {
12
- const { links, hideGithubStars, isInDropdown } = props
13
- return (
14
- <div className={clsx("flex gap-2", isInDropdown ? "pt-4 flex-col" : "items-center ml-4 space-x-1 ")}>
15
- {links.map((link, stableIdx) => {
16
-
17
- const textKey = link.text.toLowerCase()
18
- const isGithub = textKey === 'github'
19
-
20
- if (isGithub)
21
- return (
22
- <GithubStarsLink
23
-
24
- hideGithubStars={hideGithubStars}
25
- key={stableIdx}
26
- url={link.url}
27
- />
28
- )
29
- return (
30
- <LinkButton
31
-
32
- Link={Link}
33
- className={link.className}
34
- key={stableIdx}
35
- linktype={link.linktype}
36
- text={link.text}
37
- url={link.url}
38
- variant={link.variant}
39
- />
40
- )
41
- })}
42
- </div>
43
- )
44
- }
45
-
46
- export default CtaLinks
1
+ import clsx from 'clsx'
2
+ import LinkButton from '@dbosoft/react-uicore/linkbutton'
3
+ import Link from 'next/link'
4
+ import type { ICtaItem } from '../..'
5
+ import GithubStarsLink from './github-stars-link'
6
+
7
+ function CtaLinks(props: {
8
+ links: ICtaItem[],
9
+ hideGithubStars: boolean,
10
+ isInDropdown: boolean
11
+ }) {
12
+ const { links, hideGithubStars, isInDropdown } = props
13
+ return (
14
+ <div className={clsx("flex gap-2", isInDropdown ? "pt-4 flex-col" : "items-center ml-4 space-x-1 ")}>
15
+ {links.map((link, stableIdx) => {
16
+
17
+ const textKey = link.text.toLowerCase()
18
+ const isGithub = textKey === 'github'
19
+
20
+ if (isGithub)
21
+ return (
22
+ <GithubStarsLink
23
+
24
+ hideGithubStars={hideGithubStars}
25
+ key={stableIdx}
26
+ url={link.url}
27
+ />
28
+ )
29
+ return (
30
+ <LinkButton
31
+
32
+ Link={Link}
33
+ className={link.className}
34
+ key={stableIdx}
35
+ linktype={link.linktype}
36
+ text={link.text}
37
+ url={link.url}
38
+ variant={link.variant}
39
+ />
40
+ )
41
+ })}
42
+ </div>
43
+ )
44
+ }
45
+
46
+ export default CtaLinks
@@ -1,52 +1,52 @@
1
- import type { ReactElement } from 'react';
2
- import React from 'react'
3
- import LinkWrap from '@dbosoft/react-uicore/link-wrap'
4
- import NavItemText from '../nav-item-text'
5
- import type { MenuItem } from '../..'
6
- import Link from 'next/link';
7
-
8
- function MenuItemsDefault(props: {
9
- menuItems: MenuItem[]
10
- }) {
11
- const { menuItems } = props
12
-
13
- return (
14
- <ul
15
- className="inline-flex items-center">
16
- {menuItems.map((menuItem, stableIdx) => {
17
- if (menuItem === 'divider') {
18
- return <VerticalDivider key={stableIdx} />
19
- }
20
- const { text, url } = menuItem
21
- return <NavLink
22
- isActive={menuItem.active || false}
23
- key={stableIdx}
24
- text={text}
25
- url={url}
26
- />
27
- })}
28
- </ul>
29
- )
30
- }
31
- function VerticalDivider() {
32
- return (
33
- <li>
34
- <span className="bg-gray-400 dark:bg-gray-200 w-0.5 h-6 mx-1 block" />
35
- </li>
36
- )
37
- }
38
-
39
-
40
- function NavLink(props: { text: string; url: string; isActive: boolean }) {
41
- const { text, url, isActive } = props
42
- return (
43
- <li>
44
- <LinkWrap Link={Link} className="px-2" href={url}>
45
- <NavItemText isActive={isActive} text={text} />
46
- </LinkWrap>
47
- </li>
48
- )
49
- }
50
-
51
-
52
- export default MenuItemsDefault
1
+ import type { ReactElement } from 'react';
2
+ import React from 'react'
3
+ import LinkWrap from '@dbosoft/react-uicore/link-wrap'
4
+ import NavItemText from '../nav-item-text'
5
+ import type { MenuItem } from '../..'
6
+ import Link from 'next/link';
7
+
8
+ function MenuItemsDefault(props: {
9
+ menuItems: MenuItem[]
10
+ }) {
11
+ const { menuItems } = props
12
+
13
+ return (
14
+ <ul
15
+ className="inline-flex items-center">
16
+ {menuItems.map((menuItem, stableIdx) => {
17
+ if (menuItem === 'divider') {
18
+ return <VerticalDivider key={stableIdx} />
19
+ }
20
+ const { text, url } = menuItem
21
+ return <NavLink
22
+ isActive={menuItem.active || false}
23
+ key={stableIdx}
24
+ text={text}
25
+ url={url}
26
+ />
27
+ })}
28
+ </ul>
29
+ )
30
+ }
31
+ function VerticalDivider() {
32
+ return (
33
+ <li>
34
+ <span className="bg-gray-400 dark:bg-gray-200 w-0.5 h-6 mx-1 block" />
35
+ </li>
36
+ )
37
+ }
38
+
39
+
40
+ function NavLink(props: { text: string; url: string; isActive: boolean }) {
41
+ const { text, url, isActive } = props
42
+ return (
43
+ <li>
44
+ <LinkWrap Link={Link} className="px-2" href={url}>
45
+ <NavItemText isActive={isActive} text={text} />
46
+ </LinkWrap>
47
+ </li>
48
+ )
49
+ }
50
+
51
+
52
+ export default MenuItemsDefault
@@ -1,51 +1,51 @@
1
- import type { ReactElement } from 'react';
2
- import React from 'react'
3
- import LinkWrap from '@dbosoft/react-uicore/link-wrap'
4
- import CtaLinks from '../CtaLinks'
5
- import type { ICtaItem, MenuItem } from '../..'
6
- import s from './style.module.scss'
7
- import Link from 'next/link';
8
- import clsx from 'clsx'
9
-
10
- interface MenuItemsOverflowProps {
11
- menuItems: MenuItem[],
12
- ctaLinks: ICtaItem[],
13
- hideGithubStars: boolean,
14
- }
15
-
16
-
17
- export default function MenuItemsOverflow({ menuItems, ctaLinks, hideGithubStars }: MenuItemsOverflowProps) {
18
- return (<><ul className={s.ulElem}>
19
- {menuItems.map((menuItem, stableIdx) => {
20
- if (menuItem === 'divider') return null
21
-
22
- const { text, url } = menuItem
23
- return (
24
- <SubmenuItem
25
- active={menuItem.active || false}
26
- key={stableIdx}
27
- text={text}
28
- url={url}
29
- />
30
- )
31
- })}
32
- </ul>
33
- <CtaLinks
34
- hideGithubStars={hideGithubStars}
35
- isInDropdown
36
- links={ctaLinks}
37
- /></>)
38
-
39
- }
40
-
41
- function SubmenuItem({ url, text, active }: { url: string; text: string; active: boolean }) {
42
-
43
- return (
44
- <li>
45
- <LinkWrap Link={Link} className={clsx(s.submenuItem, "text-black dark:text-white" )} href={url} title={text}>
46
- {text}
47
- </LinkWrap>
48
- </li>
49
- )
50
- }
51
-
1
+ import type { ReactElement } from 'react';
2
+ import React from 'react'
3
+ import LinkWrap from '@dbosoft/react-uicore/link-wrap'
4
+ import CtaLinks from '../CtaLinks'
5
+ import type { ICtaItem, MenuItem } from '../..'
6
+ import s from './style.module.scss'
7
+ import Link from 'next/link';
8
+ import clsx from 'clsx'
9
+
10
+ interface MenuItemsOverflowProps {
11
+ menuItems: MenuItem[],
12
+ ctaLinks: ICtaItem[],
13
+ hideGithubStars: boolean,
14
+ }
15
+
16
+
17
+ export default function MenuItemsOverflow({ menuItems, ctaLinks, hideGithubStars }: MenuItemsOverflowProps) {
18
+ return (<><ul className={s.ulElem}>
19
+ {menuItems.map((menuItem, stableIdx) => {
20
+ if (menuItem === 'divider') return null
21
+
22
+ const { text, url } = menuItem
23
+ return (
24
+ <SubmenuItem
25
+ active={menuItem.active || false}
26
+ key={stableIdx}
27
+ text={text}
28
+ url={url}
29
+ />
30
+ )
31
+ })}
32
+ </ul>
33
+ <CtaLinks
34
+ hideGithubStars={hideGithubStars}
35
+ isInDropdown
36
+ links={ctaLinks}
37
+ /></>)
38
+
39
+ }
40
+
41
+ function SubmenuItem({ url, text, active }: { url: string; text: string; active: boolean }) {
42
+
43
+ return (
44
+ <li>
45
+ <LinkWrap Link={Link} className={clsx(s.submenuItem, "text-black dark:text-white" )} href={url} title={text}>
46
+ {text}
47
+ </LinkWrap>
48
+ </li>
49
+ )
50
+ }
51
+
@@ -13,7 +13,7 @@ const TitleLink: FC<{
13
13
  return (
14
14
  <LinkWrap
15
15
  Link={Link}
16
- className="font-bold text-gray-900 tracking-widest text-lg inline"
16
+ className="font-semibold text-gray-900 tracking-widest text-lg inline"
17
17
  href={url}
18
18
  title={text}
19
19
  >{text}
@@ -1,29 +1,29 @@
1
- import clsx from 'clsx'
2
- import React from 'react'
3
-
4
- /**
5
- *
6
- * A span of styled text with
7
- * an active state that adds a thick
8
- * bottom border.
9
- *
10
- */
11
- function NavItemText({
12
- isActive,
13
- text,
14
- }: {
15
- /** If true, item will be rendered with a thick bottom border below the text. */
16
- isActive: boolean
17
- /** Plain text to render within the styled <span /> */
18
- text: string
19
- }): React.ReactElement {
20
- return (
21
- <span
22
- className={clsx("text-black dark:text-white text-sm font-semibold", isActive ? "border-b-2 pb-2 border-black" : "")}
23
- >
24
- {text}
25
- </span>
26
- )
27
- }
28
-
29
- export default NavItemText
1
+ import clsx from 'clsx'
2
+ import React from 'react'
3
+
4
+ /**
5
+ *
6
+ * A span of styled text with
7
+ * an active state that adds a thick
8
+ * bottom border.
9
+ *
10
+ */
11
+ function NavItemText({
12
+ isActive,
13
+ text,
14
+ }: {
15
+ /** If true, item will be rendered with a thick bottom border below the text. */
16
+ isActive: boolean
17
+ /** Plain text to render within the styled <span /> */
18
+ text: string
19
+ }): React.ReactElement {
20
+ return (
21
+ <span
22
+ className={clsx("text-black dark:text-white text-sm font-semibold", isActive ? "border-b-2 pb-2 border-black" : "")}
23
+ >
24
+ {text}
25
+ </span>
26
+ )
27
+ }
28
+
29
+ export default NavItemText
package/src/tabs/Tabs.tsx CHANGED
@@ -1,50 +1,50 @@
1
- import { TabList, Tab, TabPanels, TabPanel, TabGroup } from "@headlessui/react"
2
- import { FC, Suspense } from "react"
3
-
4
- import TabsClient, { TabConfig, TabLink } from "./TabsClient"
5
-
6
-
7
- export const TabsContent: FC<{
8
- tabs: TabConfig[],
9
- tabNames: string[]
10
- }> = ({
11
- tabs,
12
- tabNames
13
- }) => {
14
-
15
- return <>
16
- <TabList className={` pt-2 flex justify-center`}>
17
- {tabs.map((tab, index) => <Suspense key={tab.name}><Tab as={TabLink} className={tab.className} index={index} tabs={tabNames}>{tab.displayName}</Tab></Suspense>)}
18
- </TabList>
19
- <TabPanels className={`mt-4 pt-0 border-t-2 border-brand-light dark:border-brand-dark`}>
20
- {tabs.map((tab, index) => <TabPanel className={`py-4`} key={index}>{tab.content}</TabPanel>)}
21
- </TabPanels>
22
- </>
23
- }
24
-
25
-
26
- const Tabs: FC<{
27
- tabs: TabConfig[],
28
- currentTab: string | undefined
29
- }> = ({
30
- tabs,
31
- currentTab
32
- }) => {
33
- let selectedTab = currentTab ? tabs.findIndex(tab => tab.name === currentTab) : 0;
34
- if (selectedTab == -1)
35
- selectedTab = 0;
36
-
37
- const tabNames = tabs.map(tab => tab.name);
38
-
39
-
40
- return <div className="pb-8"><Suspense fallback={<TabGroup selectedIndex={selectedTab}>
41
- <TabsContent tabs={tabs} tabNames={tabNames} /></TabGroup>}>
42
- <TabsClient initialTab={selectedTab} tabs={tabNames}>
43
- <TabsContent tabs={tabs} tabNames={tabNames} />
44
- </TabsClient>
45
- </Suspense>
46
- </div>
47
-
48
- }
49
-
50
- export default Tabs;
1
+ import { TabList, Tab, TabPanels, TabPanel, TabGroup } from "@headlessui/react"
2
+ import { FC, Suspense } from "react"
3
+
4
+ import TabsClient, { TabConfig, TabLink } from "./TabsClient"
5
+
6
+
7
+ export const TabsContent: FC<{
8
+ tabs: TabConfig[],
9
+ tabNames: string[]
10
+ }> = ({
11
+ tabs,
12
+ tabNames
13
+ }) => {
14
+
15
+ return <>
16
+ <TabList className={` pt-2 flex justify-center`}>
17
+ {tabs.map((tab, index) => <Suspense key={tab.name}><Tab as={TabLink} className={tab.className} index={index} tabs={tabNames}>{tab.displayName}</Tab></Suspense>)}
18
+ </TabList>
19
+ <TabPanels className={`mt-4 pt-0 border-t-2 border-brand-light dark:border-brand-dark`}>
20
+ {tabs.map((tab, index) => <TabPanel className={`py-4`} key={index}>{tab.content}</TabPanel>)}
21
+ </TabPanels>
22
+ </>
23
+ }
24
+
25
+
26
+ const Tabs: FC<{
27
+ tabs: TabConfig[],
28
+ currentTab: string | undefined
29
+ }> = ({
30
+ tabs,
31
+ currentTab
32
+ }) => {
33
+ let selectedTab = currentTab ? tabs.findIndex(tab => tab.name === currentTab) : 0;
34
+ if (selectedTab == -1)
35
+ selectedTab = 0;
36
+
37
+ const tabNames = tabs.map(tab => tab.name);
38
+
39
+
40
+ return <div className="pb-8"><Suspense fallback={<TabGroup selectedIndex={selectedTab}>
41
+ <TabsContent tabs={tabs} tabNames={tabNames} /></TabGroup>}>
42
+ <TabsClient initialTab={selectedTab} tabs={tabNames}>
43
+ <TabsContent tabs={tabs} tabNames={tabNames} />
44
+ </TabsClient>
45
+ </Suspense>
46
+ </div>
47
+
48
+ }
49
+
50
+ export default Tabs;
@@ -1,75 +1,75 @@
1
- "use client"
2
-
3
- import { usePathname, useRouter, useSearchParams } from "next/navigation";
4
- import { FC, ReactNode, forwardRef, useState } from "react";
5
- import { TabGroup } from "@headlessui/react"
6
- import Link from "next/link";
7
- import clsx from "clsx";
8
-
9
- export type TabConfig = {
10
- name: string,
11
- displayName: string
12
- className?: string
13
- content: ReactNode
14
- }
15
-
16
- export const createNavUrl = (index: number, tabs: string[], pathname: string, searchParams: URLSearchParams) => {
17
- const lastSegment = pathname.split(`/`).pop();
18
- let newPath = pathname;
19
-
20
- if (lastSegment && tabs.includes(lastSegment)) {
21
- newPath = pathname.substring(0, pathname.lastIndexOf(`/`));
22
- }
23
-
24
- const newTab = index > 0 ? tabs[index] : ``;
25
- newPath = newPath + `/` + newTab;
26
-
27
- const query = searchParams.toString();
28
-
29
- if (query)
30
- return (newPath + `?` + query);
31
- else
32
- return newPath;
33
- }
34
-
35
- export const TabLink = forwardRef<HTMLAnchorElement, React.PropsWithChildren<{
36
- index: number, tabs: string[], className: string
37
- }>>((props, ref) => {
38
-
39
- const pathname = usePathname()
40
- const searchParams = useSearchParams()
41
-
42
- return <Link prefetch={false} role="tab" {...props} className={clsx(`data-[selected]:border-secondary data-[selected]:text-link-light dark:data-[selected]:text-sky-300`,
43
- `border-transparent text-content-secondary hover:border-secondary hover:text-sky-600 dark:hover:text-sky-200 `,
44
- `whitespace-nowrap border-b-2 mx-3 px-2 py-2 text-base font-semibold`)} ref={ref} href={createNavUrl(props.index, props.tabs, pathname, searchParams)}
45
- >
46
- {props.children}
47
- </Link >
48
- });
49
-
50
- TabLink.displayName = `TabLink`;
51
-
52
- const TabsClient: FC<{
53
- children: ReactNode,
54
- initialTab: number,
55
- tabs: string[]
56
- }> = ({
57
- children,
58
- tabs,
59
- initialTab
60
- }) => {
61
- const router = useRouter();
62
- const pathname = usePathname();
63
- const searchParams = useSearchParams();
64
- const [selectedTab, setSelectedTab] = useState(initialTab);
65
-
66
- return <TabGroup className="text-content-primary" manual selectedIndex={selectedTab}
67
- defaultIndex={initialTab} onChange={(index) => {
68
- router.push(createNavUrl(index, tabs, pathname, searchParams));
69
- setSelectedTab(index);
70
- }}>
71
- {children}
72
- </TabGroup>
73
- }
74
-
75
- export default TabsClient
1
+ "use client"
2
+
3
+ import { usePathname, useRouter, useSearchParams } from "next/navigation";
4
+ import { FC, ReactNode, forwardRef, useState } from "react";
5
+ import { TabGroup } from "@headlessui/react"
6
+ import Link from "next/link";
7
+ import clsx from "clsx";
8
+
9
+ export type TabConfig = {
10
+ name: string,
11
+ displayName: string
12
+ className?: string
13
+ content: ReactNode
14
+ }
15
+
16
+ export const createNavUrl = (index: number, tabs: string[], pathname: string, searchParams: URLSearchParams) => {
17
+ const lastSegment = pathname.split(`/`).pop();
18
+ let newPath = pathname;
19
+
20
+ if (lastSegment && tabs.includes(lastSegment)) {
21
+ newPath = pathname.substring(0, pathname.lastIndexOf(`/`));
22
+ }
23
+
24
+ const newTab = index > 0 ? tabs[index] : ``;
25
+ newPath = newPath + `/` + newTab;
26
+
27
+ const query = searchParams.toString();
28
+
29
+ if (query)
30
+ return (newPath + `?` + query);
31
+ else
32
+ return newPath;
33
+ }
34
+
35
+ export const TabLink = forwardRef<HTMLAnchorElement, React.PropsWithChildren<{
36
+ index: number, tabs: string[], className: string
37
+ }>>((props, ref) => {
38
+
39
+ const pathname = usePathname()
40
+ const searchParams = useSearchParams()
41
+
42
+ return <Link prefetch={false} role="tab" {...props} className={clsx(`data-[selected]:border-secondary data-[selected]:text-link-light dark:data-[selected]:text-sky-300`,
43
+ `border-transparent text-content-secondary hover:border-secondary hover:text-sky-600 dark:hover:text-sky-200 `,
44
+ `whitespace-nowrap border-b-2 mx-3 px-2 py-2 text-base font-semibold`)} ref={ref} href={createNavUrl(props.index, props.tabs, pathname, searchParams)}
45
+ >
46
+ {props.children}
47
+ </Link >
48
+ });
49
+
50
+ TabLink.displayName = `TabLink`;
51
+
52
+ const TabsClient: FC<{
53
+ children: ReactNode,
54
+ initialTab: number,
55
+ tabs: string[]
56
+ }> = ({
57
+ children,
58
+ tabs,
59
+ initialTab
60
+ }) => {
61
+ const router = useRouter();
62
+ const pathname = usePathname();
63
+ const searchParams = useSearchParams();
64
+ const [selectedTab, setSelectedTab] = useState(initialTab);
65
+
66
+ return <TabGroup className="text-content-primary" manual selectedIndex={selectedTab}
67
+ defaultIndex={initialTab} onChange={(index) => {
68
+ router.push(createNavUrl(index, tabs, pathname, searchParams));
69
+ setSelectedTab(index);
70
+ }}>
71
+ {children}
72
+ </TabGroup>
73
+ }
74
+
75
+ export default TabsClient
package/src/tabs/index.ts CHANGED
@@ -1,3 +1,3 @@
1
- // Client-safe exports — can be imported from 'use client' components
2
- export type { TabConfig } from './TabsClient'
3
- export { default as TabsClient, createNavUrl, TabLink } from './TabsClient'
1
+ // Client-safe exports — can be imported from 'use client' components
2
+ export type { TabConfig } from './TabsClient'
3
+ export { default as TabsClient, createNavUrl, TabLink } from './TabsClient'
@@ -1,2 +1,2 @@
1
- // Server-only exports — must NOT be imported from 'use client' components
2
- export { default as Tabs, TabsContent } from './Tabs'
1
+ // Server-only exports — must NOT be imported from 'use client' components
2
+ export { default as Tabs, TabsContent } from './Tabs'
@@ -83,7 +83,7 @@ export function ThemeSelector({
83
83
  <Listbox as="div" value={theme} onChange={setTheme} {...props}>
84
84
  <ListboxLabel className="sr-only">{labels.theme}</ListboxLabel>
85
85
  <ListboxButton
86
- className="flex h-9 w-12 mt-3 items-center justify-center rounded-lg shadow-md shadow-black/5 ring-1 ring-black/5 dark:bg-slate-700 dark:ring-inset dark:ring-white/5"
86
+ className="flex h-9 w-12 mt-3 items-center justify-center rounded-container-sm shadow-popover shadow-black/5 ring-1 ring-black/5 dark:bg-slate-700 dark:ring-inset dark:ring-white/5"
87
87
  aria-label={labels.themeAriaLabel}
88
88
  >
89
89
  <LightIcon
@@ -99,7 +99,7 @@ export function ThemeSelector({
99
99
  )}
100
100
  />
101
101
  </ListboxButton>
102
- <ListboxOptions className="absolute left-1/2 top-full mt-3 w-36 -translate-x-1/2 space-y-1 rounded-xl bg-white p-3 text-sm font-medium shadow-md shadow-black/5 ring-1 ring-black/5 dark:bg-slate-800 dark:ring-white/5">
102
+ <ListboxOptions className="absolute left-1/2 top-full mt-3 w-36 -translate-x-1/2 space-y-1 rounded-container bg-white p-3 text-sm font-medium shadow-popover shadow-black/5 ring-1 ring-black/5 dark:bg-slate-800 dark:ring-white/5">
103
103
  {themes.map((theme) => (
104
104
  <ListboxOption
105
105
  key={theme.value}
@@ -118,7 +118,7 @@ export function ThemeSelector({
118
118
  >
119
119
  {({ selected }) => (
120
120
  <>
121
- <div className="rounded-md bg-white p-1 shadow ring-1 ring-slate-900/5 dark:bg-slate-700 dark:ring-inset dark:ring-white/5">
121
+ <div className="rounded-control bg-white p-1 shadow-control ring-1 ring-slate-900/5 dark:bg-slate-700 dark:ring-inset dark:ring-white/5">
122
122
  <theme.icon
123
123
  className={clsx(
124
124
  'h-4 w-4',