Commit workspace changes
This commit is contained in:
43
resources/js/components/docs/DocsCallout.jsx
Normal file
43
resources/js/components/docs/DocsCallout.jsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import React from 'react'
|
||||
|
||||
const TONES = {
|
||||
tip: {
|
||||
shell: 'border-sky-300/25 bg-sky-400/10 text-sky-50',
|
||||
icon: 'fa-solid fa-lightbulb text-sky-200',
|
||||
label: 'Tip',
|
||||
},
|
||||
note: {
|
||||
shell: 'border-white/15 bg-white/[0.05] text-white',
|
||||
icon: 'fa-solid fa-circle-info text-slate-200',
|
||||
label: 'Note',
|
||||
},
|
||||
warning: {
|
||||
shell: 'border-amber-300/25 bg-amber-400/10 text-amber-50',
|
||||
icon: 'fa-solid fa-triangle-exclamation text-amber-200',
|
||||
label: 'Warning',
|
||||
},
|
||||
practice: {
|
||||
shell: 'border-emerald-300/25 bg-emerald-400/10 text-emerald-50',
|
||||
icon: 'fa-solid fa-badge-check text-emerald-200',
|
||||
label: 'Best Practice',
|
||||
},
|
||||
}
|
||||
|
||||
export default function DocsCallout({ tone = 'note', title, children }) {
|
||||
const styles = TONES[tone] || TONES.note
|
||||
|
||||
return (
|
||||
<aside className={`rounded-[24px] border px-4 py-4 md:px-5 ${styles.shell}`}>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="mt-0.5 flex h-10 w-10 shrink-0 items-center justify-center rounded-2xl border border-white/10 bg-black/20">
|
||||
<i className={styles.icon} />
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<p className="text-[11px] font-semibold uppercase tracking-[0.18em] opacity-80">{styles.label}</p>
|
||||
{title ? <h3 className="mt-1 text-base font-semibold">{title}</h3> : null}
|
||||
<div className="mt-2 text-sm leading-6 opacity-90">{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
)
|
||||
}
|
||||
33
resources/js/components/docs/DocsComparisonTable.jsx
Normal file
33
resources/js/components/docs/DocsComparisonTable.jsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import React from 'react'
|
||||
|
||||
export default function DocsComparisonTable({ columns, rows, caption }) {
|
||||
return (
|
||||
<div className="overflow-hidden rounded-[28px] border border-white/10 bg-black/20">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full border-collapse text-left">
|
||||
{caption ? <caption className="sr-only">{caption}</caption> : null}
|
||||
<thead>
|
||||
<tr className="border-b border-white/10 bg-white/[0.04]">
|
||||
{columns.map((column) => (
|
||||
<th key={column.key} scope="col" className="px-4 py-3 text-xs font-semibold uppercase tracking-[0.16em] text-slate-300 first:min-w-[180px]">
|
||||
{column.label}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rows.map((row) => (
|
||||
<tr key={row.id} className="border-b border-white/5 last:border-b-0">
|
||||
{columns.map((column, index) => (
|
||||
<td key={`${row.id}-${column.key}`} className={`px-4 py-4 align-top text-sm leading-6 ${index === 0 ? 'font-semibold text-white' : 'text-slate-300'}`}>
|
||||
{row[column.key]}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
53
resources/js/components/docs/DocsFaqAccordion.jsx
Normal file
53
resources/js/components/docs/DocsFaqAccordion.jsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import React, { useEffect, useId, useState } from 'react'
|
||||
|
||||
export default function DocsFaqAccordion({ items, initialOpenIndex = 0, renderAnswer }) {
|
||||
const [openIndex, setOpenIndex] = useState(items.length > 0 ? initialOpenIndex : -1)
|
||||
const baseId = useId()
|
||||
|
||||
useEffect(() => {
|
||||
setOpenIndex(items.length > 0 ? Math.min(initialOpenIndex, items.length - 1) : -1)
|
||||
}, [items.length, initialOpenIndex])
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
{items.map((item, index) => {
|
||||
const buttonId = `${baseId}-button-${index}`
|
||||
const panelId = `${baseId}-panel-${index}`
|
||||
const isOpen = openIndex === index
|
||||
const answerContent = renderAnswer ? renderAnswer(item) : item.answer
|
||||
|
||||
return (
|
||||
<div key={item.question} className="overflow-hidden rounded-[24px] border border-white/10 bg-black/20">
|
||||
<h3>
|
||||
<button
|
||||
id={buttonId}
|
||||
type="button"
|
||||
className="flex w-full items-center justify-between gap-4 px-4 py-4 text-left md:px-5"
|
||||
aria-expanded={isOpen}
|
||||
aria-controls={panelId}
|
||||
onClick={() => setOpenIndex(isOpen ? -1 : index)}
|
||||
>
|
||||
<span className="text-base font-semibold text-white">{item.question}</span>
|
||||
<span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full border border-white/10 bg-white/[0.04] text-slate-300">
|
||||
<i className={`fa-solid ${isOpen ? 'fa-minus' : 'fa-plus'} text-xs`} />
|
||||
</span>
|
||||
</button>
|
||||
</h3>
|
||||
<div
|
||||
id={panelId}
|
||||
role="region"
|
||||
aria-labelledby={buttonId}
|
||||
className={isOpen ? 'block border-t border-white/10 px-4 py-4 md:px-5' : 'hidden'}
|
||||
>
|
||||
{typeof answerContent === 'string' ? (
|
||||
<p className="text-sm leading-7 text-slate-300">{answerContent}</p>
|
||||
) : (
|
||||
<div className="space-y-4 text-sm leading-7 text-slate-300">{answerContent}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
14
resources/js/components/docs/DocsSection.jsx
Normal file
14
resources/js/components/docs/DocsSection.jsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import React from 'react'
|
||||
|
||||
export default function DocsSection({ id, eyebrow, title, summary, children, className = '' }) {
|
||||
return (
|
||||
<section id={id} aria-labelledby={`${id}-title`} className={`scroll-mt-24 rounded-[32px] border border-white/10 bg-white/[0.03] p-6 shadow-[0_22px_70px_rgba(2,6,23,0.22)] md:p-7 ${className}`.trim()}>
|
||||
<div className="max-w-3xl">
|
||||
{eyebrow ? <p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-sky-200/80">{eyebrow}</p> : null}
|
||||
<h2 id={`${id}-title`} className="mt-2 text-3xl font-semibold tracking-[-0.03em] text-white md:text-[2rem]">{title}</h2>
|
||||
{summary ? <p className="mt-4 text-sm leading-7 text-slate-300 md:text-[15px]">{summary}</p> : null}
|
||||
</div>
|
||||
<div className="mt-6">{children}</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
50
resources/js/components/docs/DocsSidebarNav.jsx
Normal file
50
resources/js/components/docs/DocsSidebarNav.jsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import React from 'react'
|
||||
|
||||
function jumpToSection(targetId) {
|
||||
if (!targetId || typeof window === 'undefined') return
|
||||
|
||||
const element = document.getElementById(targetId)
|
||||
if (!element) return
|
||||
|
||||
element.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||
window.history.replaceState(null, '', `#${targetId}`)
|
||||
}
|
||||
|
||||
export default function DocsSidebarNav({ sections, ariaLabel = 'Sections on this page', selectLabel = 'Jump to section', navTitle = 'On this page' }) {
|
||||
return (
|
||||
<>
|
||||
<div className="lg:hidden">
|
||||
<label htmlFor="groups-help-nav" className="sr-only">{selectLabel}</label>
|
||||
<select
|
||||
id="groups-help-nav"
|
||||
className="w-full rounded-[20px] border border-white/10 bg-white/[0.04] px-4 py-3 text-sm text-white outline-none"
|
||||
defaultValue=""
|
||||
onChange={(event) => {
|
||||
jumpToSection(event.target.value)
|
||||
event.target.value = ''
|
||||
}}
|
||||
>
|
||||
<option value="">Jump to a section</option>
|
||||
{sections.map((section) => (
|
||||
<option key={section.id} value={section.id}>{section.label}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<nav aria-label={ariaLabel} className="hidden lg:block lg:sticky lg:top-24">
|
||||
<div className="rounded-[28px] border border-white/10 bg-white/[0.03] p-4 shadow-[0_18px_50px_rgba(2,6,23,0.22)]">
|
||||
<p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-sky-200/80">{navTitle}</p>
|
||||
<ul className="mt-4 space-y-1.5">
|
||||
{sections.map((section) => (
|
||||
<li key={section.id}>
|
||||
<a href={`#${section.id}`} className="block rounded-2xl px-3 py-2 text-sm text-slate-300 transition hover:bg-white/[0.05] hover:text-white">
|
||||
{section.label}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</>
|
||||
)
|
||||
}
|
||||
19
resources/js/components/docs/DocsStepList.jsx
Normal file
19
resources/js/components/docs/DocsStepList.jsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from 'react'
|
||||
|
||||
export default function DocsStepList({ items }) {
|
||||
return (
|
||||
<ol className="space-y-3">
|
||||
{items.map((item, index) => (
|
||||
<li key={item.title} className="flex gap-4 rounded-[24px] border border-white/10 bg-black/20 p-4">
|
||||
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-2xl border border-sky-300/20 bg-sky-300/10 text-sm font-semibold text-sky-100">
|
||||
{index + 1}
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<h3 className="text-base font-semibold text-white">{item.title}</h3>
|
||||
<p className="mt-1 text-sm leading-6 text-slate-300">{item.description}</p>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
)
|
||||
}
|
||||
35
resources/js/components/docs/FaqSearchInput.jsx
Normal file
35
resources/js/components/docs/FaqSearchInput.jsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import React from 'react'
|
||||
|
||||
export default function FaqSearchInput({ value, onChange, onClear, resultCount }) {
|
||||
return (
|
||||
<div className="rounded-[26px] border border-white/10 bg-black/20 p-4 md:p-5">
|
||||
<label htmlFor="groups-faq-search" className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500">
|
||||
Search questions
|
||||
</label>
|
||||
<div className="mt-3 flex gap-3">
|
||||
<div className="relative flex-1">
|
||||
<span className="pointer-events-none absolute inset-y-0 left-4 flex items-center text-slate-500">
|
||||
<i className="fa-solid fa-magnifying-glass" />
|
||||
</span>
|
||||
<input
|
||||
id="groups-faq-search"
|
||||
value={value}
|
||||
onChange={(event) => onChange(event.target.value)}
|
||||
placeholder="Search roles, invites, contributor credit, review, troubleshooting..."
|
||||
className="w-full rounded-[20px] border border-white/10 bg-white/[0.04] py-3 pl-11 pr-4 text-sm text-white outline-none placeholder:text-slate-500"
|
||||
/>
|
||||
</div>
|
||||
{value ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClear}
|
||||
className="rounded-[20px] border border-white/10 bg-white/[0.04] px-4 py-3 text-sm font-semibold text-white transition hover:border-white/20 hover:bg-white/[0.06]"
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
<p className="mt-3 text-sm text-slate-400">{resultCount} question{resultCount === 1 ? '' : 's'} visible</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
21
resources/js/components/docs/QuickstartChecklist.jsx
Normal file
21
resources/js/components/docs/QuickstartChecklist.jsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import React from 'react'
|
||||
|
||||
export default function QuickstartChecklist({ title, summary, items }) {
|
||||
return (
|
||||
<section className="rounded-[30px] border border-emerald-300/20 bg-emerald-400/10 p-5 shadow-[0_22px_70px_rgba(2,6,23,0.2)] md:p-6">
|
||||
<p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-emerald-100/80">Checklist</p>
|
||||
<h3 className="mt-2 text-2xl font-semibold text-white">{title}</h3>
|
||||
{summary ? <p className="mt-3 text-sm leading-7 text-emerald-50/90">{summary}</p> : null}
|
||||
<ul className="mt-5 grid gap-3 md:grid-cols-2">
|
||||
{items.map((item) => (
|
||||
<li key={item} className="flex gap-3 rounded-[22px] border border-white/10 bg-black/20 px-4 py-4 text-sm leading-6 text-white">
|
||||
<span className="mt-1 flex h-6 w-6 shrink-0 items-center justify-center rounded-full border border-emerald-300/20 bg-emerald-300/10 text-emerald-100">
|
||||
<i className="fa-solid fa-check text-[10px]" />
|
||||
</span>
|
||||
<span>{item}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
25
resources/js/components/docs/QuickstartNextSteps.jsx
Normal file
25
resources/js/components/docs/QuickstartNextSteps.jsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import React from 'react'
|
||||
|
||||
const TONES = {
|
||||
sky: 'border-sky-300/20 bg-sky-300/10 text-sky-100',
|
||||
amber: 'border-amber-300/20 bg-amber-400/10 text-amber-100',
|
||||
white: 'border-white/10 bg-black/20 text-white',
|
||||
}
|
||||
|
||||
export default function QuickstartNextSteps({ items }) {
|
||||
return (
|
||||
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
|
||||
{items.map((item) => (
|
||||
<a
|
||||
key={item.title}
|
||||
href={item.href}
|
||||
className={`rounded-[28px] border p-5 transition hover:-translate-y-0.5 hover:border-white/20 ${TONES[item.tone] || TONES.white}`}
|
||||
>
|
||||
<div className="text-[11px] font-semibold uppercase tracking-[0.18em] opacity-80">{item.eyebrow}</div>
|
||||
<div className="mt-2 text-lg font-semibold text-white">{item.title}</div>
|
||||
<p className="mt-3 text-sm leading-6 opacity-90">{item.body}</p>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user