-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
<ComparisonTable>
, <FAQ>
, <Search>
tweaks
#1927
base: main
Are you sure you want to change the base?
Changes from all commits
7fe38a4
3b27662
4c9daac
5db65e3
f9df32f
7023a32
7c43302
0db29d5
945c465
e103cd6
88d29fe
5820425
b63a862
09bf8a8
f566890
08e100b
040af40
1060194
d8069a7
d54bd22
b4f6927
eb89005
cf90315
d0090d9
d6e8022
fdac057
ac86875
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
"@theguild/components": patch | ||
--- | ||
dependencies updates: | ||
- Added dependency [`@radix-ui/react-accordion@^1.2.2` ↗︎](https://www.npmjs.com/package/@radix-ui/react-accordion/v/1.2.2) (to `dependencies`) | ||
- Added dependency [`@radix-ui/react-icons@^1.3.2` ↗︎](https://www.npmjs.com/package/@radix-ui/react-icons/v/1.3.2) (to `dependencies`) | ||
- Added dependency [`[email protected]` ↗︎](https://www.npmjs.com/package/unist-util-visit/v/5.0.0) (to `dependencies`) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { ComponentProps, FC } from 'react'; | ||
import { cn } from '@theguild/components'; | ||
|
||
const Table: FC<ComponentProps<'table'>> = ({ className, ...props }) => { | ||
return ( | ||
<table | ||
className={cn( | ||
'x:block x:overflow-x-auto nextra-scrollbar overflow-x-auto rounded-2xl border border-green-200', | ||
className, | ||
)} | ||
{...props} | ||
/> | ||
); | ||
}; | ||
|
||
const TableRow: FC<ComponentProps<'tr'> & { highlight?: boolean }> = ({ | ||
highlight, | ||
className, | ||
...props | ||
}) => { | ||
return <tr className={cn(highlight && 'bg-green-100', className)} {...props} />; | ||
}; | ||
|
||
const cellStyle = cn( | ||
'border border-green-200 p-4 first:border-l-0 last:border-r-0', | ||
'[tbody_&]:border-b-0 [thead_&]:border-t-0', | ||
'first:sticky', | ||
'first:left-0', | ||
'max-sm:first:drop-shadow-2xl', | ||
'first:bg-[rgb(var(--nextra-bg))]', | ||
); | ||
|
||
const TableHeader: FC<ComponentProps<'th'>> = ({ className, ...props }) => { | ||
return <th className={cn(cellStyle, 'font-medium', className)} {...props} />; | ||
}; | ||
|
||
const TableCell: FC<ComponentProps<'td'>> = ({ className, ...props }) => { | ||
return <td className={cn(cellStyle, className)} {...props} />; | ||
}; | ||
|
||
export const ComparisonTable = Object.assign(Table, { | ||
Row: TableRow, | ||
Header: TableHeader, | ||
Cell: TableCell, | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
'use client'; | ||
|
||
import { FC, useEffect } from 'react'; | ||
import { usePathname } from 'next/navigation'; | ||
|
||
export const AttachPageFAQSchema: FC<{ faqPages: string[] }> = ({ faqPages }) => { | ||
const pathname = usePathname(); | ||
|
||
useEffect(() => { | ||
const html = document.querySelector('html')!; | ||
const path = pathname.replace('/graphql/hive', '/'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this always be |
||
|
||
if (faqPages.includes(path) && !html.hasAttribute('itemscope')) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Like I mentioned on Slack, I doubt this check is needed. |
||
html.setAttribute('itemscope', ''); | ||
html.setAttribute('itemtype', 'https://schema.org/FAQPage'); | ||
|
||
return () => { | ||
html.removeAttribute('itemscope'); | ||
html.removeAttribute('itemtype'); | ||
}; | ||
} | ||
}, [pathname]); | ||
|
||
return null; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import { | ||
Children, | ||
cloneElement, | ||
ComponentProps, | ||
ComponentPropsWithoutRef, | ||
FC, | ||
ReactElement, | ||
ReactNode, | ||
} from 'react'; | ||
import * as RadixAccordion from '@radix-ui/react-accordion'; | ||
import { ChevronDownIcon } from '@radix-ui/react-icons'; | ||
import { cn } from '../../cn'; | ||
import { Anchor } from '../anchor'; | ||
import { Heading } from '../heading'; | ||
import { AttachPageFAQSchema } from './attach-page-faq-schema'; | ||
|
||
const UnwrapChild: FC<{ children?: ReactNode }> = props => props.children; | ||
|
||
const a: FC<ComponentPropsWithoutRef<'a'>> = props => ( | ||
<Anchor | ||
className="hive-focus rounded underline hover:text-blue-700" | ||
{...props} | ||
href={props.href!} | ||
> | ||
{props.children} | ||
</Anchor> | ||
); | ||
|
||
const h2: FC<ComponentPropsWithoutRef<'h2'>> = props => ( | ||
<Heading as="h2" size="md" className="basis-1/2" {...props} /> | ||
); | ||
|
||
export const FrequentlyAskedQuestions: FC< | ||
ComponentProps<typeof AttachPageFAQSchema> & { | ||
className?: string; | ||
children: ReactElement; | ||
} | ||
> = ({ className, faqPages, children }) => { | ||
return ( | ||
<section | ||
className={cn( | ||
className, | ||
'flex flex-col gap-x-6 gap-y-2 px-4 py-6 text-green-1000 md:flex-row md:px-10 lg:gap-x-24 lg:px-[120px] lg:py-24', | ||
)} | ||
> | ||
<AttachPageFAQSchema faqPages={faqPages} /> | ||
{cloneElement(children, { | ||
components: { | ||
a, | ||
h2, | ||
Comment on lines
+47
to
+50
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can't be done with a provider in Server Components world, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
p: UnwrapChild, | ||
ul: Accordion, | ||
li: AccordionItem, | ||
}, | ||
})} | ||
</section> | ||
); | ||
}; | ||
|
||
const Accordion: FC<ComponentPropsWithoutRef<'ul'>> = props => ( | ||
<RadixAccordion.Root asChild type="single" collapsible> | ||
<ul className="basis-1/2 divide-y divide-beige-400 max-xl:grow" {...props} /> | ||
</RadixAccordion.Root> | ||
); | ||
|
||
const AccordionItem: FC<ComponentPropsWithoutRef<'li'>> = props => { | ||
const texts = Children.toArray(props.children).filter(child => child !== '\n'); | ||
|
||
if (texts.length === 0) { | ||
return null; | ||
} | ||
|
||
if (texts.length < 2) { | ||
// eslint-disable-next-line no-console | ||
console.error(texts); | ||
throw new Error(`Expected a question and an answer, got ${texts.length} items`); | ||
} | ||
|
||
const [first, ...answers] = texts; | ||
|
||
const question = | ||
typeof first === 'string' | ||
? first | ||
: typeof first === 'object' && 'type' in first | ||
? first.props.children | ||
: null; | ||
|
||
if (!question) return null; | ||
|
||
return ( | ||
<RadixAccordion.Item | ||
asChild | ||
value={question} | ||
className="relative pb-0 focus-within:z-10 data-[state=open]:pb-4" | ||
itemScope | ||
itemProp="mainEntity" | ||
itemType="https://schema.org/Question" | ||
> | ||
<li> | ||
<RadixAccordion.Header> | ||
<RadixAccordion.Trigger className="hive-focus duration-[.8s] -mx-2 my-1 flex w-[calc(100%+1rem)] items-center justify-between rounded-xl bg-white px-2 py-3 text-left font-medium transition-colors hover:bg-beige-100/80 md:my-2 md:py-4"> | ||
<span itemProp="name">{question}</span> | ||
<ChevronDownIcon className="size-5 [[data-state='open']_&]:[transform:rotateX(180deg)]" /> | ||
</RadixAccordion.Trigger> | ||
</RadixAccordion.Header> | ||
<RadixAccordion.Content | ||
forceMount | ||
className="overflow-hidden bg-white text-green-800 data-[state=closed]:hidden" | ||
itemScope | ||
itemProp="acceptedAnswer" | ||
itemType="https://schema.org/Answer" | ||
> | ||
<div itemProp="text" className="space-y-2"> | ||
{answers.map((answer, i) => ( | ||
<p key={i}>{answer}</p> | ||
))} | ||
</div> | ||
</RadixAccordion.Content> | ||
</li> | ||
</RadixAccordion.Item> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ import React, { | |
FC, | ||
forwardRef, | ||
Fragment, | ||
ReactElement, | ||
ReactNode, | ||
useEffect, | ||
useRef, | ||
|
@@ -55,6 +56,7 @@ export type HiveNavigationProps = { | |
logo?: ReactNode; | ||
navLinks?: { href: string; children: ReactNode }[]; | ||
developerMenu: DeveloperMenuProps['developerMenu']; | ||
search?: ReactElement; | ||
searchProps?: ComponentProps<typeof Search>; | ||
}; | ||
|
||
|
@@ -83,7 +85,7 @@ export function HiveNavigation({ | |
}, | ||
], | ||
developerMenu, | ||
searchProps, | ||
search = <Search />, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You moved the styles here to a .css file, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, because in Yoga I use So I will avoid copy-pasting x100 characters |
||
}: HiveNavigationProps) { | ||
const containerRef = useRef<HTMLDivElement>(null!); | ||
|
||
|
@@ -152,10 +154,7 @@ export function HiveNavigation({ | |
|
||
{children} | ||
|
||
<Search | ||
className="relative ml-4 basis-64 [&_:is(input,kbd)]:text-green-700 dark:[&_:is(input,kbd)]:text-neutral-300 [&_input]:h-12 [&_input]:w-full [&_input]:rounded-lg [&_input]:border [&_input]:border-green-200 [&_input]:bg-white [&_input]:pl-4 [&_input]:pr-8 [&_input]:ring-[hsl(var(--nextra-primary-hue)_var(--nextra-primary-saturation)_32%/var(--tw-ring-opacity))] [&_input]:ring-offset-[rgb(var(--nextra-bg))] dark:[&_input]:border-neutral-800 [&_input]:dark:bg-inherit [&_kbd]:absolute [&_kbd]:right-4 [&_kbd]:top-1/2 [&_kbd]:my-0 [&_kbd]:-translate-y-1/2 [&_kbd]:border-none [&_kbd]:bg-green-200 dark:[&_kbd]:bg-neutral-700" | ||
{...searchProps} | ||
/> | ||
{search} | ||
|
||
<CallToAction | ||
className="ml-4 max-lg:hidden" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
'use client'; | ||
|
||
import { FC, ReactNode } from 'react'; | ||
import { usePathname } from 'next/navigation'; | ||
|
||
export const Body: FC<{ | ||
lightOnlyPages: string[]; | ||
children: ReactNode; | ||
}> = ({ lightOnlyPages, children }) => { | ||
const pathname = usePathname(); | ||
|
||
const isLightOnlyPage = lightOnlyPages.includes(pathname); | ||
|
||
return <body className={isLightOnlyPage ? 'light text-green-1000' : undefined}>{children}</body>; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { Root } from 'mdast'; | ||
import { Plugin } from 'unified'; | ||
import { visit } from 'unist-util-visit'; | ||
|
||
const EXTERNAL_URL_RE = /^https?:\/\//; | ||
|
||
export type RemarkLinkRewriteOptions = { | ||
pattern: RegExp; | ||
replace: string; | ||
}; | ||
|
||
export const remarkLinkRewrite: Plugin<[RemarkLinkRewriteOptions], Root> = | ||
({ pattern, replace }) => | ||
ast => { | ||
visit(ast, 'link', node => { | ||
if (EXTERNAL_URL_RE.test(node.url)) { | ||
return; | ||
} | ||
node.url = node.url.replace(pattern, replace); | ||
}); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we could
It doesn't really have to be a comparison table. It is one in Mesh, maybe in Yoga too, but I think the styles work for every table.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in components
<Table>
component already reexported from Nextradocs/packages/components/src/index.ts
Line 10 in e60c4c6