Compare commits

..

1 Commits

Author SHA1 Message Date
mDuo13
c5bf019608 Add example JS code for deleting an account 2026-02-19 17:35:19 -08:00
16 changed files with 243 additions and 569 deletions

View File

@@ -577,16 +577,6 @@ Stay ahead of the curve with the latest developments in RWA tokenization on the
Join the Developer Discord: 開発者ディスコードに参加
Sign up for the Newsletter: ニュースレターに登録
# docs/tutorials/index.page.tsx
Crypto Wallet and Blockchain Development Tutorials: 暗号資産ウォレットやブロックチェーン開発のチュートリアル
These tutorials walk you through the basics of building a very simple XRP Ledger-connected application using your favorite programming language.: これらのチュートリアルでは、お好きなプログラミング言語を使って、XRP Ledgerに接続するアプリケーションを構築するための基礎について説明します。
Get Started with SDKs: SDKを使って始める
Using the xrpl.js client library.: クライアントライブラリxrpl.jsを使用
Using xrpl.py, a pure Python library.: Pythonライブラリxrpl.pyを使用
Using xrpl4j, a pure Java library.: Javaライブラリを使用
Using the XRPL_PHP client library.: PHPライブラリXRPL_PHPを使用
Access the XRP Ledger directly through the APIs of its core server.: コアサーバのAPIを通じてXRP Ledgerに直接アクセス
# community/index.page.tsx
XRPL Community: XRPLコミュニティ
community.index.h1part1: 開発者とイノベーターによる

View File

@@ -1,13 +1,13 @@
import { indexPages } from './plugins/index-pages.js';
import { codeSamples } from './plugins/code-samples.js';
import { blogPosts } from './plugins/blog-posts.js';
import { tutorialLanguages } from './plugins/tutorial-languages.js';
export default function customPlugin() {
const indexPagesInst = indexPages();
const codeSamplesInst = codeSamples();
const blogPostsInst = blogPosts();
const tutorialLanguagesInst = tutorialLanguages();
/** @type {import("@redocly/realm/dist/server/plugins/types").PluginInstance } */
const pluginInstance = {
@@ -16,7 +16,6 @@ export default function customPlugin() {
await indexPagesInst.processContent?.(content, actions);
await codeSamplesInst.processContent?.(content, actions);
await blogPostsInst.processContent?.(content, actions);
await tutorialLanguagesInst.processContent?.(content, actions);
},
afterRoutesCreated: async (content, actions) => {
await indexPagesInst.afterRoutesCreated?.(content, actions);

View File

@@ -1,117 +0,0 @@
// @ts-check
/**
* Plugin to detect languages supported in tutorial pages by scanning for tab labels.
* This creates shared data that maps tutorial paths to their supported languages.
*/
export function tutorialLanguages() {
/** @type {import("@redocly/realm/dist/server/plugins/types").PluginInstance } */
const instance = {
processContent: async (actions, { fs, cache }) => {
try {
/** @type {Record<string, string[]>} */
const tutorialLanguagesMap = {};
const allFiles = await fs.scan();
// Find all markdown files in tutorials directory
const tutorialFiles = allFiles.filter((file) =>
file.relativePath.match(/^docs[\/\\]tutorials[\/\\].*\.md$/)
);
for (const { relativePath } of tutorialFiles) {
try {
const { data } = await cache.load(relativePath, 'markdown-ast');
const languages = extractLanguagesFromAst(data.ast);
if (languages.length > 0) {
// Convert file path to URL path
const urlPath = '/' + relativePath
.replace(/\.md$/, '/')
.replace(/\\/g, '/');
tutorialLanguagesMap[urlPath] = languages;
}
} catch {
continue; // Skip files that can't be parsed.
}
}
actions.createSharedData('tutorial-languages', tutorialLanguagesMap);
actions.addRouteSharedData('/docs/tutorials/', 'tutorial-languages', 'tutorial-languages');
actions.addRouteSharedData('/ja/docs/tutorials/', 'tutorial-languages', 'tutorial-languages');
actions.addRouteSharedData('/es-es/docs/tutorials/', 'tutorial-languages', 'tutorial-languages');
} catch (e) {
console.log(e);
}
},
};
return instance;
}
/**
* Extract language names from tab labels in the markdown AST
*/
function extractLanguagesFromAst(ast) {
const languages = new Set();
visit(ast, (node) => {
// Look for tab nodes with a label attribute
if (isNode(node) && node.type === 'tag' && node.tag === 'tab') {
const label = node.attributes?.label;
if (label) {
const normalizedLang = normalizeLanguage(label);
if (normalizedLang) {
languages.add(normalizedLang);
}
}
}
});
return Array.from(languages);
}
/**
* Normalize language label to match our icon keys
*/
function normalizeLanguage(label) {
const labelLower = label.toLowerCase();
if (labelLower.includes('javascript') || labelLower === 'js') {
return 'javascript';
}
if (labelLower.includes('python') || labelLower === 'py') {
return 'python';
}
if (labelLower.includes('java') && !labelLower.includes('javascript')) {
return 'java';
}
if (labelLower.includes('php')) {
return 'php';
}
if (labelLower.includes('go') || labelLower === 'golang') {
return 'go';
}
if (labelLower.includes('http') || labelLower.includes('websocket')) {
return 'http';
}
return null;
}
function isNode(value) {
return !!(value?.$$mdtype === 'Node');
}
function visit(node, visitor) {
if (!node) return;
visitor(node);
if (node.children) {
for (const child of node.children) {
if (!child || typeof child === 'string') {
continue;
}
visit(child, visitor);
}
}
}

View File

@@ -0,0 +1,3 @@
# Delete Account
Delete an account from the XRP Ledger, removing its data and sending its XRP to another account.

View File

@@ -0,0 +1,3 @@
# Delete Account (JavaScript)
JavaScript sample code showing how to delete an account from the XRP Ledger.

View File

@@ -0,0 +1,187 @@
import { Client, getBalanceChanges, validate } from "xrpl"
const client = new Client('wss://s.altnet.rippletest.net:51233')
await client.connect()
// Where to send the deleted account's remaining XRP:
const DESTINATION_ACCOUNT = 'rJjHYTCPpNA3qAM8ZpCDtip3a8xg7B8PFo' // Testnet faucet
/* Alternative to load an existing account from a seed in a .env file ----------
// To use: do `npm install dotenv` and uncomment this section, but comment out
// the "Get an account from the faucet..." section below.
// Also edit example.env with your account seed and rename it to .env
// Change the algorithm to secp256k1 if the seed is that type.
// (If the generated address isn't what you expect, this is probably the case.)
import 'dotenv/config'
import { Wallet } from "xrpl"
const wallet = Wallet.fromSeed(process.env.ACCOUNT_SEED, { algorithm: 'ed25519' })
console.log(`Using existing wallet:
Address: ${wallet.address}
Seed: ${wallet.seed}
`)
*/
// Get an account from the faucet and wait for it to be old enough to delete ---
const { wallet } = await client.fundWallet()
console.log(`Got new account from faucet:
Address: ${wallet.address}
Seed: ${wallet.seed}
`)
// Wait ~20 minutes for the current ledger index to get 256+ larger than the
// account's sequence number.
const delay = 60 * 20
console.log(`Waiting ${delay} seconds for the account to be old enough to delete.`)
await sleep(delay)
// Sleep function that can be used with await
function sleep (delayInSeconds) {
const delayInMs = delayInSeconds * 1000
return new Promise((resolve) => setTimeout(resolve, delayInMs))
}
// Check account info to see if account can be deleted -------------------------
let acctInfoResp
try {
acctInfoResp = await client.request({
command: 'account_info',
account: wallet.address,
ledger_index: 'validated'
})
} catch (err) {
console.error('account_info failed with error:', err)
client.disconnect()
process.exit(1)
}
let numProblems = 0
// Check if sequence number is too high
const acctSeq = acctInfoResp.result.account_data.Sequence
const lastValidatedLedgerIndex = acctInfoResp.result.ledger_index
if (acctSeq + 256 >= lastValidatedLedgerIndex) {
console.error(`Account is too new to be deleted.
Account sequence: ${acctSeq}
Validated ledger index: ${lastValidatedLedgerIndex}
(Sequence + 256 must be less than ledger index)`)
numProblems += 1
} else {
console.log(`OK: Account sequence number (${acctSeq}) is low enough.`)
}
// Check if owner count is too high
const ownerCount = acctInfoResp.result.account_data.OwnerCount
if (ownerCount > 1000) {
console.error(`Account owns too many objects in the ledger.
Owner count: ${ownerCount}
(Must be less than 1000)`)
numProblems += 1
} else {
console.log(`OK: Account owner count (${ownerCount}) is low enough.`)
}
// Check if XRP balance is high enough
// Look up current incremental owner reserve to compare vs account's XRP balance
// using server_state so that both are in drops
let serverStateResp
try {
serverStateResp = await client.request({
command: 'server_state',
})
} catch (err) {
console.error('server_state failed with error:', err)
client.disconnect()
process.exit(1)
}
const deletionCost = serverStateResp.result.state.validated_ledger?.reserve_inc
if (!deletionCost) {
console.error("Couldn't get reserve values from server. "+
"Maybe it's not synced to the network?")
console.log(JSON.stringify(serverStateResp.result, null, 2))//TODO:remove
client.disconnect()
process.exit(1)
}
const acctBalance = acctInfoResp.result.account_data.Balance
if (acctBalance < deletionCost) {
console.error(`Account does not have enough XRP to pay the cost of deletion.
Balance: ${acctBalance}
Cost of account deletion: ${deletionCost}`)
numProblems += 1
} else {
console.log(`OK: Account balance (${acctBalance} drops) is high enough.`)
}
if (numProblems) {
console.error(`A total of ${numProblems} problem(s) prevent the account from being deleted.`)
client.disconnect()
process.exit(1)
}
// Check for deletion blockers -------------------------------------------------
let blockers = []
let marker
let ledger_index = 'validated'
while (true) {
let accountObjResp
try {
accountObjResp = await client.request({
command: 'account_objects',
account: wallet.address,
deletion_blockers_only: true,
ledger_index,
marker
})
} catch (err) {
console.error('account_objects failed with error:', err)
client.disconnect()
process.exit(1)
}
for (const obj of accountObjResp.result.account_objects) {
blockers.push(obj)
}
if (accountObjResp.result.marker) {
marker = accountObjResp.result.marker
} else {
break
}
}
if (!blockers.length) {
console.log('OK: Account has no deletion blockers.')
} else {
console.log(`Account cannot be deleted until ${blockers.length} blocker(s) are removed:`)
for (const blocker of blockers) {
console.log(JSON.stringify(blocker, null, 2))
}
client.disconnect()
process.exit(1)
}
// Delete the account ----------------------------------------------------------
const accountDeleteTx = {
TransactionType: 'AccountDelete',
Account: wallet.address,
Destination: DESTINATION_ACCOUNT
}
validate(accountDeleteTx)
console.log('Signing and submitting the AccountDelete transaction:',
JSON.stringify(accountDeleteTx, null, 2))
const deleteTxResponse = await client.submitAndWait(accountDeleteTx, { wallet, autofill: true, failHard: true })
// Check result of the AccountDelete transaction -------------------------------
console.log(JSON.stringify(deleteTxResponse.result, null, 2))
const resultCode = deleteTxResponse.result.meta.TransactionResult
if (resultCode !== 'tesSUCCESS') {
console.error(`AccountDelete failed with code ${resultCode}.`)
client.disconnect()
process.exit(1)
}
console.log('Account deleted successfully.')
const balanceChanges = getBalanceChanges(deleteTxResponse.result.meta)
console.log('Balance changes:', JSON.stringify(balanceChanges, null, 2))
client.disconnect()

View File

@@ -0,0 +1,4 @@
# Example env file used when loading an existing account. Ignore if getting
# a new account to delete from the faucet.
# Replace the seed with an appropriate value, then rename to .env
ACCOUNT_SEED=s████████████████████████████

View File

@@ -0,0 +1,10 @@
{
"name": "delete-account",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"dotenv": "^17.3.1",
"xrpl": "^4.6.0"
},
"type": "module"
}

29
docs/tutorials/index.md Normal file
View File

@@ -0,0 +1,29 @@
---
seo:
description: Learn how to get started building on the XRP Ledger with these helpful crypto wallet and blockchain tutorials for developers.
---
# Crypto Wallet and Blockchain Development Tutorials
The XRP Ledger tutorials walk you through the steps to learn and get started with the XRP Ledger and to use the ledger for advanced use cases.
## Get Started with SDKs
These tutorials walk you through the basics of building a very simple XRP Ledger-connected application using your favorite programming language.
{% card-grid %}
{% xrpl-card title="Javascript" body="Using the xrpl.js client library." href="/docs/tutorials/get-started/get-started-javascript/" image="/img/logos/javascript.svg" imageAlt="Javascript logo" /%}
{% xrpl-card title="Python" body="Using xrpl.py, a pure Python library." href="/docs/tutorials/get-started/get-started-python/" image="/img/logos/python.svg" imageAlt="Python logo" /%}
{% xrpl-card title="Go" body="Using xrpl-go, a pure Go library." href="/docs/tutorials/get-started/get-started-go/" image="/img/logos/golang.svg" imageAlt="Go logo" /%}
{% xrpl-card title="Java" body="Using xrpl4j, a pure Java library." href="/docs/tutorials/get-started/get-started-java/" image="/img/logos/java.svg" imageAlt="Java logo" /%}
{% xrpl-card title="PHP" body="Using the XRPL_PHP client library." href="/docs/tutorials/get-started/get-started-php/" image="/img/logos/php.svg" imageAlt="PHP logo" /%}
{% xrpl-card title="HTTP & WebSocket APIs" body="Access the XRP Ledger directly through the APIs of its core server." href="/docs/tutorials/get-started/get-started-http-websocket-apis/" image="/img/logos/globe.svg" imageAlt="globe icon" /%}
{% /card-grid %}
<!-- TODO: Add new sections for the new tutorial categories -->

View File

@@ -1,353 +0,0 @@
import { useThemeHooks } from "@redocly/theme/core/hooks";
export const frontmatter = {
seo: {
title: "Tutorials",
description:
"Learn how to get started building on the XRP Ledger with these helpful crypto wallet and blockchain tutorials for developers.",
},
};
const langIcons: Record<string, { src: string; alt: string }> = {
javascript: { src: "/img/logos/javascript.svg", alt: "JavaScript" },
python: { src: "/img/logos/python.svg", alt: "Python" },
java: { src: "/img/logos/java.svg", alt: "Java" },
go: { src: "/img/logos/golang.svg", alt: "Go" },
php: { src: "/img/logos/php.svg", alt: "PHP" },
http: { src: "/img/logos/globe.svg", alt: "HTTP / WebSocket" },
xrpl: { src: "/img/logos/xrp-mark.svg", alt: "XRP Ledger" },
};
// Type for the tutorial languages map from the plugin
type TutorialLanguagesMap = Record<string, string[]>;
interface Tutorial {
title: string;
body?: string;
path: string;
icon?: string; // Single language icon (for single-language tutorials)
}
interface TutorialSection {
id: string;
title: string;
description: string;
tutorials: Tutorial[];
}
// Get Started tutorials -----------------
const getStartedTutorials: Tutorial[] = [
{
title: "JavaScript",
body: "Using the xrpl.js client library.",
path: "/docs/tutorials/get-started/get-started-javascript/",
icon: "javascript",
},
{
title: "Python",
body: "Using xrpl.py, a pure Python library.",
path: "/docs/tutorials/get-started/get-started-python/",
icon: "python",
},
{
title: "Go",
body: "Using xrpl-go, a pure Go library.",
path: "/docs/tutorials/get-started/get-started-go/",
icon: "go",
},
{
title: "Java",
body: "Using xrpl4j, a pure Java library.",
path: "/docs/tutorials/get-started/get-started-java/",
icon: "java",
},
{
title: "PHP",
body: "Using the XRPL_PHP client library.",
path: "/docs/tutorials/get-started/get-started-php/",
icon: "php",
},
{
title: "HTTP & WebSocket APIs",
body: "Access the XRP Ledger directly through the APIs of its core server.",
path: "/docs/tutorials/get-started/get-started-http-websocket-apis/",
icon: "http",
},
];
// Other tutorial sections -----------------
// Languages are auto-detected from the markdown files by the tutorial-languages plugin.
// Only specify `icon` for single-language tutorials without tabs.
const sections: TutorialSection[] = [
{
id: "tokens",
title: "Tokens",
description: "Create and manage tokens on the XRP Ledger.",
tutorials: [
{
title: "Issue a Multi-Purpose Token",
body: "Issue new tokens using the v2 fungible token standard.",
path: "/docs/tutorials/tokens/mpts/issue-a-multi-purpose-token/",
},
{
title: "Issue a Fungible Token",
body: "Issue new tokens using the v1 fungible token standard.",
path: "/docs/tutorials/tokens/fungible-tokens/issue-a-fungible-token/",
},
{
title: "Mint and Burn NFTs Using JavaScript",
body: "Create new NFTs, retrieve existing tokens, and burn the ones you no longer need.",
path: "/docs/tutorials/tokens/nfts/mint-and-burn-nfts-js/",
icon: "javascript",
},
],
},
{
id: "payments",
title: "Payments",
description: "Transfer XRP and issued currencies using various payment types.",
tutorials: [
{
title: "Send XRP",
body: "Send a direct XRP payment to another account.",
path: "/docs/tutorials/payments/send-xrp/",
},
{
title: "Sending MPTs in JavaScript",
body: "Send a Multi-Purpose Token (MPT) to another account with the JavaScript SDK.",
path: "/docs/tutorials/tokens/mpts/sending-mpts-in-javascript/",
icon: "javascript",
},
{
title: "Create Trust Line and Send Currency in JavaScript",
body: "Set up trust lines and send issued currencies with the JavaScript SDK.",
path: "/docs/tutorials/payments/create-trust-line-send-currency-in-javascript/",
icon: "javascript",
},
{
title: "Create Trust Line and Send Currency in Python",
body: "Set up trust lines and send issued currencies with the Python SDK.",
path: "/docs/tutorials/payments/create-trust-line-send-currency-in-python/",
icon: "python",
},
{
title: "Send a Conditional Escrow",
body: "Send an escrow that can be released when a specific crypto-condition is fulfilled.",
path: "/docs/tutorials/payments/send-a-conditional-escrow/",
},
{
title: "Send a Timed Escrow",
body: "Send an escrow whose only condition for release is that a specific time has passed.",
path: "/docs/tutorials/payments/send-a-timed-escrow/",
},
],
},
{
id: "defi",
title: "DeFi",
description: "Trade, provide liquidity, and lend using native XRP Ledger DeFi features.",
tutorials: [
{
title: "Create an Automated Market Maker",
body: "Set up an AMM for a token pair and provide liquidity.",
path: "/docs/tutorials/defi/dex/create-an-automated-market-maker/",
},
{
title: "Trade in the Decentralized Exchange",
body: "Buy and sell tokens in the Decentralized Exchange (DEX).",
path: "/docs/tutorials/defi/dex/trade-in-the-decentralized-exchange/",
},
{
title: "Create a Loan Broker",
body: "Set up a loan broker to create and manage loans.",
path: "/docs/tutorials/defi/lending/use-the-lending-protocol/create-a-loan-broker/",
},
{
title: "Create a Loan",
body: "Create a loan on the XRP Ledger.",
path: "/docs/tutorials/defi/lending/use-the-lending-protocol/create-a-loan/",
},
{
title: "Create a Single Asset Vault",
body: "Create a single asset vault on the XRP Ledger.",
path: "/docs/tutorials/defi/lending/use-single-asset-vaults/create-a-single-asset-vault/",
},
{
title: "Deposit into a Vault",
body: "Deposit assets into a vault and receive shares.",
path: "/docs/tutorials/defi/lending/use-single-asset-vaults/deposit-into-a-vault/",
},
],
},
{
id: "best-practices",
title: "Best Practices",
description: "Learn recommended patterns for building reliable, secure applications on the XRP Ledger.",
tutorials: [
{
title: "API Usage",
body: "Best practices for using XRP Ledger APIs.",
path: "/docs/tutorials/best-practices/api-usage/",
},
{
title: "Use Tickets",
body: "Use tickets to send transactions out of the normal order.",
path: "/docs/tutorials/best-practices/transaction-sending/use-tickets/",
},
{
title: "Send a Single Account Batch Transaction",
body: "Group multiple transactions together and execute them as a single atomic operation.",
path: "/docs/tutorials/best-practices/transaction-sending/send-a-single-account-batch-transaction/",
},
{
title: "Assign a Regular Key Pair",
body: "Assign a regular key pair for signing transactions.",
path: "/docs/tutorials/best-practices/key-management/assign-a-regular-key-pair/",
},
{
title: "Set Up Multi-Signing",
body: "Configure multi-signing for enhanced security.",
path: "/docs/tutorials/best-practices/key-management/set-up-multi-signing/",
},
{
title: "Send a Multi-Signed Transaction",
body: "Send a transaction with multiple signatures.",
path: "/docs/tutorials/best-practices/key-management/send-a-multi-signed-transaction/",
},
],
},
{
id: "sample-apps",
title: "Sample Apps",
description: "Build complete, end-to-end applications like wallets and credential services.",
tutorials: [
{
title: "Build a Browser Wallet in JavaScript",
body: "Build a browser wallet for the XRP Ledger using JavaScript and various libraries.",
path: "/docs/tutorials/sample-apps/build-a-browser-wallet-in-javascript/",
icon: "javascript",
},
{
title: "Build a Desktop Wallet in JavaScript",
body: "Build a desktop wallet for the XRP Ledger using JavaScript, the Electron Framework, and various libraries.",
path: "/docs/tutorials/sample-apps/build-a-desktop-wallet-in-javascript/",
icon: "javascript",
},
{
title: "Build a Desktop Wallet in Python",
body: "Build a desktop wallet for the XRP Ledger using Python and various libraries.",
path: "/docs/tutorials/sample-apps/build-a-desktop-wallet-in-python/",
icon: "python",
},
{
title: "Credential Issuing Service in JavaScript",
body: "Build a credential issuing service using the JavaScript SDK.",
path: "/docs/tutorials/sample-apps/credential-issuing-service-in-javascript/",
icon: "javascript",
},
{
title: "Credential Issuing Service in Python",
body: "Build a credential issuing service using the Python SDK.",
path: "/docs/tutorials/sample-apps/credential-issuing-service-in-python/",
icon: "python",
},
],
},
];
function TutorialCard({
tutorial,
detectedLanguages,
showFooter = false,
translate,
}: {
tutorial: Tutorial;
detectedLanguages?: string[];
showFooter?: boolean;
translate: (text: string) => string;
}) {
// Get icons: auto-detected languages take priority, then manual icon, then XRPL fallback
const icons = detectedLanguages && detectedLanguages.length > 0
? detectedLanguages.map((lang) => langIcons[lang]).filter(Boolean)
: tutorial.icon && langIcons[tutorial.icon]
? [langIcons[tutorial.icon]]
: [langIcons.xrpl];
return (
<a href={tutorial.path} className="card">
<div className="card-header d-flex align-items-center flex-wrap">
{icons.map((icon, idx) => (
<span className="circled-logo" key={idx}>
<img src={icon.src} alt={icon.alt} />
</span>
))}
</div>
<div className="card-body">
<h4 className="card-title h5">{translate(tutorial.title)}</h4>
{tutorial.body && <p className="card-text">{translate(tutorial.body)}</p>}
</div>
{showFooter && <div className="card-footer"></div>}
</a>
);
}
export default function TutorialsIndex() {
const { useTranslate, usePageSharedData } = useThemeHooks();
const { translate } = useTranslate();
// Get auto-detected languages from the plugin (maps tutorial paths to language arrays).
const tutorialLanguages = usePageSharedData<TutorialLanguagesMap>("tutorial-languages") || {};
return (
<main className="landing page-tutorials landing-builtin-bg">
<section className="container-new py-26">
<div className="col-lg-8 mx-auto text-lg-center">
<div className="d-flex flex-column-reverse">
<h1 className="mb-0">
{translate("Crypto Wallet and Blockchain Development Tutorials")}
</h1>
<h6 className="eyebrow mb-3">{translate("Tutorials")}</h6>
</div>
</div>
</section>
{/* Get Started */}
<section className="container-new pt-10 pb-20">
<div className="col-12 col-xl-8 p-0">
<h3 className="h4 mb-3">{translate("Get Started with SDKs")}</h3>
<p className="mb-4">
{translate("These tutorials walk you through the basics of building a very simple XRP Ledger-connected application using your favorite programming language.")}
</p>
</div>
<div className="row tutorial-cards">
{getStartedTutorials.map((tutorial, idx) => (
<div key={idx} className="col-lg-4 col-md-6 mb-5">
<TutorialCard tutorial={tutorial} showFooter translate={translate} />
</div>
))}
</div>
</section>
{/* Other Tutorials */}
{sections.map((section) => (
<section className="container-new pt-10 pb-10" key={section.id}>
<div className="col-12 col-xl-8 p-0">
<h3 className="h4 mb-3">{translate(section.title)}</h3>
<p className="mb-4">{translate(section.description)}</p>
</div>
<div className="row tutorial-cards">
{section.tutorials.slice(0, 6).map((tutorial, idx) => (
<div key={idx} className="col-lg-4 col-md-6 mb-5">
<TutorialCard
tutorial={tutorial}
detectedLanguages={tutorialLanguages[tutorial.path]}
translate={translate}
/>
</div>
))}
</div>
</section>
))}
</main>
);
}

View File

@@ -127,7 +127,7 @@ footer:
- page: docs/concepts/index.md
label: Concepts
labelTranslationKey: footer.docs.concepts
- page: docs/tutorials/index.page.tsx
- page: docs/tutorials/index.md
label: Tutorials
labelTranslationKey: footer.docs.tutorials
- page: docs/references/index.md

View File

@@ -184,7 +184,7 @@
- page: docs/concepts/decentralized-storage/decentralized-identifiers.md
- page: docs/concepts/decentralized-storage/price-oracles.md
- page: docs/tutorials/index.page.tsx
- page: docs/tutorials/index.md
label: Tutorials
labelTranslationKey: sidebar.docs.tutorials
expanded: false

File diff suppressed because one or more lines are too long

View File

@@ -138,9 +138,6 @@ a.card:hover h3 {
padding: .65rem;
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
img {
width: 26px;
height: 26px;
@@ -323,81 +320,3 @@ main article .card-grid {
margin-bottom: 0.25rem;
margin-top: 0.5rem;
}
/* Tutorial cards */
.page-tutorials .tutorial-cards {
> div {
display: flex;
}
.card {
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
min-height: 280px;
transition: all 0.35s ease-out;
cursor: pointer;
&:hover {
transform: translateY(-16px);
}
.card-header {
border-bottom: none;
background: transparent;
padding: 1.5rem 1.5rem 0 2rem;
}
.circled-logo {
margin-left: -10px;
}
.card-body {
padding: 1rem 1.5rem;
flex: 1;
}
.card-title {
font-weight: 700;
margin-bottom: 1rem;
}
.card-text {
font-size: 1rem;
margin-bottom: 1.5rem;
}
.card-footer {
background-color: transparent;
border-top: none;
padding: 0;
height: 40px;
background-size: cover;
background-position: bottom;
background-repeat: no-repeat;
margin-top: auto;
}
}
// Apply colored footers to each card
> div:nth-child(1) .card .card-footer {
background-image: url("../img/cards/3-col-pink.svg");
}
> div:nth-child(2) .card .card-footer {
background-image: url("../img/cards/3col-blue-light-blue.svg");
}
> div:nth-child(3) .card .card-footer {
background-image: url("../img/cards/3-col-light-blue.svg");
}
> div:nth-child(4) .card .card-footer {
background-image: url("../img/cards/3col-blue-green.svg");
}
> div:nth-child(5) .card .card-footer {
background-image: url("../img/cards/3col-magenta.svg");
}
> div:nth-child(6) .card .card-footer {
background-image: url("../img/cards/3-col-orange.svg");
}
}

View File

@@ -424,7 +424,7 @@ aside .card {
.circled-logo {
background-color: $gray-200;
img[src="/img/logos/globe.svg"] {
img[src="assets/img/logos/globe.svg"] {
filter: invert(100%);
}
}

View File

@@ -60,7 +60,7 @@
- page: ./docs/concepts/index.md
label: Concepts
labelTranslationKey: topnav.docs.concepts
- page: ./docs/tutorials/index.page.tsx
- page: ./docs/tutorials/index.md
label: Tutorials
labelTranslationKey: topnav.docs.tutorials
- page: ./docs/references/index.md