Skip to content

Commit

Permalink
Merge pull request #114 from scroll-tech/feat/add-tab-component
Browse files Browse the repository at this point in the history
Add TabContent unstyled component
  • Loading branch information
dghelm authored Apr 17, 2024
2 parents 285d32e + 8efa24c commit 9f10e0c
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 8 deletions.
103 changes: 103 additions & 0 deletions src/components/Tabs/TabsContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/** @jsxImportSource preact */
import type { ComponentChild } from "preact"
import { useRef } from "preact/hooks"
import { useTabState } from "./useTabState"
import styles from "./Tabs.module.css"
import { clsx } from "~/lib"
const tabSlotKey = "tab." as const
const panelSlotKey = "panel." as const

type TabSlot = `${typeof tabSlotKey}${string}`
type PanelSlot = `${typeof panelSlotKey}${string}`

function isTabSlotEntry(entry: [string, ComponentChild]): entry is [TabSlot, ComponentChild] {
const [key] = entry
return key.startsWith(tabSlotKey)
}

function isPanelSlotEntry(entry: [string, ComponentChild]): entry is [PanelSlot, ComponentChild] {
const [key] = entry
return key.startsWith(panelSlotKey)
}

function getBaseKeyFromTab(slot: TabSlot) {
return slot.replace(new RegExp(`^${tabSlotKey}`), "")
}

function getBaseKeyFromPanel(slot: PanelSlot) {
return slot.replace(new RegExp(`^${panelSlotKey}`), "")
}

type Props = {
[key: TabSlot | PanelSlot]: ComponentChild
sharedStore?: string
}

export function TabsContent({ sharedStore, ...slots }: Props) {
const tabs = Object.entries(slots).filter(isTabSlotEntry)
const panels = Object.entries(slots).filter(isPanelSlotEntry)

/** Used to focus next and previous tab on arrow key press */
const tabButtonRefs = useRef<Record<TabSlot, HTMLButtonElement | null>>({})

const firstPanelKey = panels[0] ? getBaseKeyFromPanel(panels[0][0]) : ""
const [curr, setCurrStore] = useTabState(firstPanelKey, sharedStore)

function moveFocus(event: KeyboardEvent) {
if (event.key === "ArrowLeft") {
const currIdx = tabs.findIndex(([key]) => getBaseKeyFromTab(key) === curr)
if (currIdx > 0) {
const [prevTabKey] = tabs[currIdx - 1]
setCurrStore(getBaseKeyFromTab(prevTabKey))
tabButtonRefs.current[prevTabKey]?.focus()
}
}
if (event.key === "ArrowRight") {
const currIdx = tabs.findIndex(([key]) => getBaseKeyFromTab(key) === curr)
if (currIdx < tabs.length - 1) {
const [nextTabKey] = tabs[currIdx + 1]
setCurrStore(getBaseKeyFromTab(nextTabKey))
tabButtonRefs.current[nextTabKey]?.focus()
}
}
}

return (
<div className={styles.contentContainer}>
<div role="tablist" onKeyDown={moveFocus}>
{tabs.map(([key, content]) => (
<button
ref={(el) => (tabButtonRefs.current[key] = el)}
onClick={() => {
setCurrStore(getBaseKeyFromTab(key))
}}
aria-selected={curr === getBaseKeyFromTab(key)}
tabIndex={curr === getBaseKeyFromTab(key) ? 0 : -1}
role="tab"
type="button"
data-astro-tab
id={key}
key={key}
class={clsx(
curr === getBaseKeyFromTab(key) ? styles.contentTabPrimary : styles.contentTabSecondary,
styles.contentTab
)}
>
{content}
</button>
))}
</div>
{panels.map(([key, content]) => (
<div
hidden={curr !== getBaseKeyFromPanel(key)}
role="tabpanel"
aria-labelledby={`${tabSlotKey}${getBaseKeyFromPanel(key)}`}
key={key}
class={styles.panel}
>
{content}
</div>
))}
</div>
)
}
2 changes: 2 additions & 0 deletions src/components/Tabs/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export { Tabs } from "./Tabs"

export { TabsContent } from "./TabsContent"
54 changes: 54 additions & 0 deletions src/content/docs/en/article-components.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import ToggleElement from "../../../components/ToggleElement.astro"
import Aside from "../../../components/Aside.astro"
import MarkmapView from "../../../components/MarkmapView/index.astro"
import RPCTable from "../../../components/RPCTable/RPCTable.astro"
import { Tabs, TabsContent } from "../../../components/Tabs"

This is body text right under the article title. It typically is just paragraph text that's pretty straightforward. Then there's **bold text**, and _italic text_, and **_bold-italic text_**, and `inline-code` and **`bold inline code`** and even _`italic inline code`_ and **_`bold italic inline code`_**. And of course don't forget [links](#), and [**bold links**](#), and [_italic links_](#), and [**_bold-italic links_**](#).

Expand Down Expand Up @@ -164,6 +165,59 @@ stateDiagram
step_context --> ConstraintBuilder
```

### Tabs

<Tabs client:visible>
<Fragment slot="tab.1">npm</Fragment>
<Fragment slot="tab.2">yarn</Fragment>
<Fragment slot="panel.1">```console npm install @chainlink/hardhat-chainlink ```</Fragment>
<Fragment slot="panel.2">```console yarn add @chainlink/hardhat-chainlink ```</Fragment>
</Tabs>

### TabsContent

<TabsContent sharedStore="vrfMethod" client:visible>
<Fragment slot="tab.1">Subscription</Fragment>
<Fragment slot="tab.2">Direct funding</Fragment>
<Fragment slot="panel.1">

For Chainlink VRF v2 to fulfill your requests, you must maintain a sufficient amount of LINK in your subscription balance. Gas cost calculation includes the following variables:

- **Gas price:** The current gas price, which fluctuates depending on network conditions.

- **Callback gas:** The amount of gas used for the callback request that returns your requested random values.

- **Verification gas:** The amount of gas used to verify randomness on-chain.

The gas price depends on current network conditions. The callback gas depends on your callback function, and the number of random values in your request. The cost of each request is final only after the transaction is complete, but you define the limits you are willing to spend for the request with the following variables:

- **Gas lane:** The maximum gas price you are willing to pay for a request in wei. Define this limit by specifying the appropriate `keyHash` in your request. The limits of each gas lane are important for handling gas price spikes when Chainlink VRF bumps the gas price to fulfill your request quickly.

- **Callback gas limit:** Specifies the maximum amount of gas you are willing to spend on the callback request. Define this limit by specifying the `callbackGasLimit` value in your request.

</Fragment>
<Fragment slot="panel.2">

For Chainlink VRF v2 to fulfill your requests, you must have a sufficient amount of LINK in your consuming contract. Gas cost calculation includes the following variables:

- **Gas price:** The current gas price, which fluctuates depending on network conditions.

- **Callback gas:** The amount of gas used for the callback request that returns your requested random values. The callback gas depends on your callback function and the number of random values in your request. Set the **callback gas limit** to specify the maximum amount of gas you are willing to spend on the callback request.

- **Verification gas:** The amount of gas used to verify randomness on-chain.

- **Wrapper overhead gas:** The amount of gas used by the VRF Wrapper contract. See the [Request and Receive Data](/vrf/v2/direct-funding#request-and-receive-data) section for details about the VRF v2 Wrapper contract design.

Because the consuming contract directly pays the LINK for the request, the cost is calculated during the request and not during the callback when the randomness is fulfilled. Test your callback function to learn how to correctly estimate the callback gas limit.

- If the gas limit is underestimated, the callback fails and the consuming contract is still charged for the work done to generate the requested random values.
- If the gas limit is overestimated, the callback function will be executed but your contract is not refunded for the excess gas amount that you paid.

Make sure that your consuming contracts are funded with enough LINK tokens to cover the transaction costs. If the consuming contract doesn't have enough LINK tokens, your request will revert.

</Fragment>
</TabsContent>

### RPC Table

<RPCTable />
5 changes: 1 addition & 4 deletions src/pages/zh/home/ConnectUs.astro
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,7 @@ const mediaList = [
---

<div class="connect-us">
<SectionHeader
title="联系我们"
content="随时了解来自 Scroll 社区的最新新闻和发展信息。"
/>
<SectionHeader title="联系我们" content="随时了解来自 Scroll 社区的最新新闻和发展信息。" />
<div class="medias">
{
mediaList.map(({ icon, name, content, link }) => (
Expand Down
5 changes: 1 addition & 4 deletions src/pages/zh/home/QuickStart.astro
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ const toolList = [
---

<div>
<SectionHeader
title="开发者快速入门"
content="开始使用您喜爱的工具在 Scroll 上进行智能合约的构建和测试"
/>
<SectionHeader title="开发者快速入门" content="开始使用您喜爱的工具在 Scroll 上进行智能合约的构建和测试" />
<div class="tools">
{
toolList.map(({ icon, name, quickstartAnchor, color }) => (
Expand Down

0 comments on commit 9f10e0c

Please sign in to comment.