mirror of
https://github.com/codex-team/codex.docs.git
synced 2025-07-19 21:29:41 +02:00
Compare commits
25 commits
Author | SHA1 | Date | |
---|---|---|---|
|
6c4d4310a9 | ||
|
453f706050 | ||
|
bb9d537fb9 | ||
|
481461de4f | ||
|
f8ba8abf19 | ||
|
a6391a3da0 | ||
|
7f51a72bcb | ||
|
f78b9ecff5 | ||
|
d3e0cb176c | ||
|
8a8db5c136 | ||
|
9218843636 | ||
|
174274eaf5 | ||
|
59829f54dc | ||
|
6ff8b7e40b | ||
|
36ba3a3cd8 | ||
|
185550dd08 | ||
|
3d172d3021 | ||
|
3ca4a175e9 | ||
|
1c3ada993e | ||
|
dc0c32a93f | ||
|
285b94779f | ||
|
91ba44a169 | ||
|
de30ddf243 | ||
|
b83d468ba0 | ||
|
f372a574f0 |
19 changed files with 296 additions and 159 deletions
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
|
@ -45,7 +45,7 @@ jobs:
|
||||||
type=raw,value=latest,enable={{is_default_branch}}
|
type=raw,value=latest,enable={{is_default_branch}}
|
||||||
type=raw,value={{branch}}-{{sha}}-{{date 'X'}},enable=${{ startsWith(github.ref, 'refs/heads') }}
|
type=raw,value={{branch}}-{{sha}}-{{date 'X'}},enable=${{ startsWith(github.ref, 'refs/heads') }}
|
||||||
type=semver,pattern={{version}},prefix=v
|
type=semver,pattern={{version}},prefix=v
|
||||||
type=semver,pattern=v{{major}}.{{minor}},prefix=v
|
type=semver,pattern={{major}}.{{minor}},prefix=v
|
||||||
|
|
||||||
- name: Build and push image
|
- name: Build and push image
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v3
|
||||||
|
|
|
@ -12,6 +12,7 @@ It's super easy to install and use.
|
||||||
|
|
||||||
- 🤩 [Editor.js](https://editorjs.io/) ecosystem powered
|
- 🤩 [Editor.js](https://editorjs.io/) ecosystem powered
|
||||||
- 📂 Docs nesting — create any structure you need
|
- 📂 Docs nesting — create any structure you need
|
||||||
|
- 💎 Static rendering
|
||||||
- 📱 Nice look on Desktop and Mobile
|
- 📱 Nice look on Desktop and Mobile
|
||||||
- 🔥 Beautiful page URLs. Human-readable and SEO-friendly.
|
- 🔥 Beautiful page URLs. Human-readable and SEO-friendly.
|
||||||
- 🦅 [Hawk](https://hawk.so/?from=docs-demo) is hunting. Errors tracking integrated
|
- 🦅 [Hawk](https://hawk.so/?from=docs-demo) is hunting. Errors tracking integrated
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "codex.docs",
|
"name": "codex.docs",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"version": "v2.0.0-rc.4",
|
"version": "2.2.3",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"bin": {
|
"bin": {
|
||||||
"codex.docs": "dist/backend/app.js"
|
"codex.docs": "dist/backend/app.js"
|
||||||
|
|
|
@ -13,6 +13,7 @@ import fse from 'fs-extra';
|
||||||
import appConfig from './utils/appConfig.js';
|
import appConfig from './utils/appConfig.js';
|
||||||
import Aliases from './controllers/aliases.js';
|
import Aliases from './controllers/aliases.js';
|
||||||
import Pages from './controllers/pages.js';
|
import Pages from './controllers/pages.js';
|
||||||
|
import { downloadFavicon } from './utils/downloadFavicon.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build static pages from database
|
* Build static pages from database
|
||||||
|
@ -45,14 +46,42 @@ export default async function buildStatic(): Promise<void> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.overwrite) {
|
||||||
console.log('Removing old static files');
|
console.log('Removing old static files');
|
||||||
await fse.remove(distPath);
|
await fse.remove(distPath);
|
||||||
|
}
|
||||||
|
|
||||||
console.log('Building static files');
|
console.log('Building static files');
|
||||||
const pagesOrder = await PagesOrder.getAll();
|
const pagesOrder = await PagesOrder.getAll();
|
||||||
const allPages = await Page.getAll();
|
const allPages = await Page.getAll();
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('Create dist folder');
|
||||||
await mkdirp(distPath);
|
await mkdirp(distPath);
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Error while creating dist folder', e);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Copy public directory');
|
||||||
|
const publicDir = path.resolve(dirname, '../../public');
|
||||||
|
|
||||||
|
console.log(`Copy from ${publicDir} to ${distPath}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fse.copy(publicDir, distPath);
|
||||||
|
console.log('Public directory copied');
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Error while copying public directory');
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
const favicon = appConfig.favicon ? await downloadFavicon(appConfig.favicon, distPath, '') : {
|
||||||
|
destination: '/favicon.png',
|
||||||
|
type: 'image/png',
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders single page
|
* Renders single page
|
||||||
|
@ -62,6 +91,11 @@ export default async function buildStatic(): Promise<void> {
|
||||||
*/
|
*/
|
||||||
async function renderPage(page: Page, isIndex?: boolean): Promise<void> {
|
async function renderPage(page: Page, isIndex?: boolean): Promise<void> {
|
||||||
console.log(`Rendering page ${page.uri}`);
|
console.log(`Rendering page ${page.uri}`);
|
||||||
|
const pageUri = page.uri;
|
||||||
|
|
||||||
|
if (!pageUri) {
|
||||||
|
throw new Error('Page uri is not defined');
|
||||||
|
}
|
||||||
const pageParent = await page.getParent();
|
const pageParent = await page.getParent();
|
||||||
const pageId = page._id;
|
const pageId = page._id;
|
||||||
|
|
||||||
|
@ -72,16 +106,30 @@ export default async function buildStatic(): Promise<void> {
|
||||||
const previousPage = await PagesFlatArray.getPageBefore(pageId);
|
const previousPage = await PagesFlatArray.getPageBefore(pageId);
|
||||||
const nextPage = await PagesFlatArray.getPageAfter(pageId);
|
const nextPage = await PagesFlatArray.getPageAfter(pageId);
|
||||||
const menu = createMenuTree(parentIdOfRootPages, allPages, pagesOrder, 2);
|
const menu = createMenuTree(parentIdOfRootPages, allPages, pagesOrder, 2);
|
||||||
|
|
||||||
const result = await renderTemplate('./views/pages/page.twig', {
|
const result = await renderTemplate('./views/pages/page.twig', {
|
||||||
page,
|
page,
|
||||||
pageParent,
|
pageParent,
|
||||||
previousPage,
|
previousPage,
|
||||||
nextPage,
|
nextPage,
|
||||||
menu,
|
menu,
|
||||||
|
favicon,
|
||||||
config: appConfig.frontend,
|
config: appConfig.frontend,
|
||||||
});
|
});
|
||||||
|
|
||||||
const filename = (isIndex || page.uri === '') ? 'index.html' : `${page.uri}.html`;
|
let filename: string;
|
||||||
|
|
||||||
|
if (isIndex) {
|
||||||
|
filename = 'index.html';
|
||||||
|
} else if (config?.pagesInsideFolders) { // create folder for each page if pagesInsideFolders is true
|
||||||
|
const pagePath = path.resolve(distPath, pageUri);
|
||||||
|
|
||||||
|
await mkdirp(pagePath);
|
||||||
|
|
||||||
|
filename = path.resolve(pagePath, 'index.html');
|
||||||
|
} else {
|
||||||
|
filename = `${page.uri}.html`;
|
||||||
|
}
|
||||||
|
|
||||||
await fs.writeFile(path.resolve(distPath, filename), result);
|
await fs.writeFile(path.resolve(distPath, filename), result);
|
||||||
console.log(`Page ${page.uri} rendered`);
|
console.log(`Page ${page.uri} rendered`);
|
||||||
|
@ -111,15 +159,16 @@ export default async function buildStatic(): Promise<void> {
|
||||||
await renderPage(page);
|
await renderPage(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
await renderIndexPage(config.indexPageUri);
|
// Check if index page is enabled
|
||||||
|
if (config.indexPage.enabled) {
|
||||||
|
await renderIndexPage(config.indexPage.uri);
|
||||||
|
}
|
||||||
console.log('Static files built');
|
console.log('Static files built');
|
||||||
|
|
||||||
console.log('Copy public directory');
|
|
||||||
await fse.copy(path.resolve(dirname, '../../public'), distPath);
|
|
||||||
|
|
||||||
if (appConfig.uploads.driver === 'local') {
|
if (appConfig.uploads.driver === 'local') {
|
||||||
console.log('Copy uploads directory');
|
console.log('Copy uploads directory');
|
||||||
await fse.copy(path.resolve(cwd, appConfig.uploads.local.path), path.resolve(distPath, 'uploads'));
|
await fse.copy(path.resolve(cwd, appConfig.uploads.local.path), path.resolve(distPath, 'uploads'));
|
||||||
|
console.log('Uploads directory copied');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,7 +90,14 @@ const FrontendConfig = z.object({
|
||||||
*/
|
*/
|
||||||
const StaticBuildConfig = z.object({
|
const StaticBuildConfig = z.object({
|
||||||
outputDir: z.string(), // Output directory for static build
|
outputDir: z.string(), // Output directory for static build
|
||||||
indexPageUri: z.string(), // URI for index page to render
|
overwrite: z.boolean().optional() // Overwrite output directory
|
||||||
|
.default(true),
|
||||||
|
pagesInsideFolders: z.boolean().optional() // Create separate folder for each page
|
||||||
|
.default(true),
|
||||||
|
indexPage: z.object({
|
||||||
|
enabled: z.boolean(), // Is index page enabled
|
||||||
|
uri: z.string(), // Index page uri
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type StaticBuildConfig = z.infer<typeof StaticBuildConfig>;
|
export type StaticBuildConfig = z.infer<typeof StaticBuildConfig>;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs/promises';
|
||||||
import fetch, { RequestInit } from 'node-fetch';
|
import fetch, { RequestInit } from 'node-fetch';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -32,9 +32,10 @@ function checkIsUrl(str: string): boolean {
|
||||||
*
|
*
|
||||||
* @param destination - url or path of favicon
|
* @param destination - url or path of favicon
|
||||||
* @param faviconFolder - folder to save favicon
|
* @param faviconFolder - folder to save favicon
|
||||||
|
* @param subRoute - subroute from which the favicon will be served
|
||||||
* @returns { Promise<FaviconData> } - Promise with data about favicon
|
* @returns { Promise<FaviconData> } - Promise with data about favicon
|
||||||
*/
|
*/
|
||||||
export async function downloadFavicon(destination: string, faviconFolder: string): Promise<FaviconData> {
|
export async function downloadFavicon(destination: string, faviconFolder: string, subRoute = '/favicon'): Promise<FaviconData> {
|
||||||
// Check of destination is empty
|
// Check of destination is empty
|
||||||
if (!destination) {
|
if (!destination) {
|
||||||
throw Error('Favicon destination is empty');
|
throw Error('Favicon destination is empty');
|
||||||
|
@ -48,8 +49,10 @@ export async function downloadFavicon(destination: string, faviconFolder: string
|
||||||
|
|
||||||
// Check if string is url
|
// Check if string is url
|
||||||
if (!checkIsUrl(destination)) {
|
if (!checkIsUrl(destination)) {
|
||||||
|
await fs.copyFile(destination, path.join(faviconFolder, filename));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
destination: `/${filename}`,
|
destination: `${subRoute}/${filename}`,
|
||||||
type: `image/${format}`,
|
type: `image/${format}`,
|
||||||
} as FaviconData;
|
} as FaviconData;
|
||||||
}
|
}
|
||||||
|
@ -72,14 +75,10 @@ export async function downloadFavicon(destination: string, faviconFolder: string
|
||||||
const filePath = path.join(faviconFolder, `favicon.${format}`);
|
const filePath = path.join(faviconFolder, `favicon.${format}`);
|
||||||
|
|
||||||
// Save file
|
// Save file
|
||||||
await fs.writeFile(filePath, fileData, (err) => {
|
await fs.writeFile(filePath, fileData);
|
||||||
if (err) {
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
destination: `/favicon/favicon.${format}`,
|
destination: `${subRoute}/favicon.${format}`,
|
||||||
type: `image/${format}`,
|
type: `image/${format}`,
|
||||||
} as FaviconData;
|
} as FaviconData;
|
||||||
}
|
}
|
||||||
|
|
25
src/backend/views/components/copy-button.twig
Normal file
25
src/backend/views/components/copy-button.twig
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
{#
|
||||||
|
Reusable copy button component.
|
||||||
|
Available props:
|
||||||
|
- ariaLabel: label for better accessibility
|
||||||
|
- class: additional class for the button
|
||||||
|
- textToCopy: text to be copied to the clipboard (use '#' for anchor links)
|
||||||
|
|
||||||
|
Usage examples:
|
||||||
|
{% include 'components/copy-button.twig' with { textToCopy: 'Lorem ipsum dolor' } %}
|
||||||
|
{% include 'components/copy-button.twig' with { textToCopy: '#anchor-link-dolor' } %}
|
||||||
|
#}
|
||||||
|
|
||||||
|
{% set attrNameForTextToCopy = 'data-text-to-copy' %}
|
||||||
|
|
||||||
|
{% set ariaLabel = ariaLabel ?? 'Copy to the Clipboard' %}
|
||||||
|
|
||||||
|
{% set mainTag = 'button' %}
|
||||||
|
{% set mainClass = 'copy-button' %}
|
||||||
|
|
||||||
|
<{{ mainTag }} class="{{ mainClass }} {{ class ?? '' }}" aria-label="{{ ariaLabel }}" {{ attrNameForTextToCopy }}="{{ textToCopy }}">
|
||||||
|
<div class="{{ mainClass }}__inner">
|
||||||
|
<div class="{{ mainClass }}__icon--initial">{{ svg('copy') }}</div>
|
||||||
|
<div class="{{ mainClass }}__icon--success">{{ svg('check') }}</div>
|
||||||
|
</div>
|
||||||
|
</{{ mainTag }}>
|
|
@ -1,4 +1,12 @@
|
||||||
<div class="block-code">
|
<div class="block-code">
|
||||||
|
<div class="block-code__wrapper">
|
||||||
<div class="block-code__content">{{ code | escape }}</div>
|
<div class="block-code__content">{{ code | escape }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
{%
|
||||||
|
include '../../components/copy-button.twig' with {
|
||||||
|
ariaLabel: 'Copy Code to Clipboard',
|
||||||
|
class: 'block-code__copy-button',
|
||||||
|
textToCopy: code | escape,
|
||||||
|
}
|
||||||
|
%}
|
||||||
|
</div>
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
<h{{ level }} id="{{ text | urlify }}" class="block-header block-header--{{ level }} block-header--anchor">
|
<h{{ level }} id="{{ text | urlify }}" class="block-header block-header--{{ level }}">
|
||||||
<div class="block-header__copy-link-splash"></div>
|
{%
|
||||||
<div class="block-header__copy-link">
|
include '../../components/copy-button.twig' with {
|
||||||
<div class="block-header__copy-link-icon--initial">{{ svg('copy') }}</div>
|
ariaLabel: 'Copy Link to the ' ~ text,
|
||||||
<div class="block-header__copy-link-icon--success">{{ svg('check') }}</div>
|
class: 'block-header__copy-button',
|
||||||
</div>
|
textToCopy: '#' ~ text | urlify,
|
||||||
|
}
|
||||||
|
%}
|
||||||
<a href="#{{ text | urlify }}">
|
<a href="#{{ text | urlify }}">
|
||||||
{{ text }}
|
{{ text }}
|
||||||
</a>
|
</a>
|
||||||
</h{{ level }}>
|
</h{{ level }}>
|
||||||
|
|
||||||
|
|
|
@ -193,7 +193,7 @@ export default class TableOfContent {
|
||||||
|
|
||||||
const linkWrapper = $.make('li', this.CSS.tocElementItem);
|
const linkWrapper = $.make('li', this.CSS.tocElementItem);
|
||||||
const linkBlock = $.make('a', null, {
|
const linkBlock = $.make('a', null, {
|
||||||
innerText: tag.innerText,
|
innerText: tag.innerText.trim(),
|
||||||
href: `${linkTarget}`,
|
href: `${linkTarget}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -20,10 +20,9 @@ export default class Page {
|
||||||
*/
|
*/
|
||||||
static get CSS() {
|
static get CSS() {
|
||||||
return {
|
return {
|
||||||
|
copyButton: 'copy-button',
|
||||||
|
copyButtonCopied: 'copy-button__copied',
|
||||||
page: 'page',
|
page: 'page',
|
||||||
copyLinkBtn: 'block-header__copy-link',
|
|
||||||
header: 'block-header--anchor',
|
|
||||||
headerLinkCopied: 'block-header--link-copied',
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,11 +34,15 @@ export default class Page {
|
||||||
this.tableOfContent = this.createTableOfContent();
|
this.tableOfContent = this.createTableOfContent();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add click event listener to capture copy link button clicks
|
* Add click event listener
|
||||||
*/
|
*/
|
||||||
const page = document.querySelector(`.${Page.CSS.page}`);
|
const page = document.querySelector(`.${Page.CSS.page}`);
|
||||||
|
|
||||||
page.addEventListener('click', this.copyAnchorLinkIfNeeded);
|
page.addEventListener('click', (event) => {
|
||||||
|
if (event.target.classList.contains(Page.CSS.copyButton)) {
|
||||||
|
this.handleCopyButtonClickEvent(event);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -69,10 +72,7 @@ export default class Page {
|
||||||
try {
|
try {
|
||||||
// eslint-disable-next-line no-new
|
// eslint-disable-next-line no-new
|
||||||
new TableOfContent({
|
new TableOfContent({
|
||||||
tagSelector:
|
tagSelector: '.block-header',
|
||||||
'h2.block-header--anchor,' +
|
|
||||||
'h3.block-header--anchor,' +
|
|
||||||
'h4.block-header--anchor',
|
|
||||||
appendTo: document.getElementById('layout-sidebar-right'),
|
appendTo: document.getElementById('layout-sidebar-right'),
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -81,27 +81,31 @@ export default class Page {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if 'copy link' button was clicked and copies the link to clipboard
|
* Handles copy button click events
|
||||||
*
|
*
|
||||||
* @param e - click event
|
* @param {Event} e - Event Object.
|
||||||
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
copyAnchorLinkIfNeeded = async (e) => {
|
async handleCopyButtonClickEvent({ target }) {
|
||||||
const copyLinkButtonClicked = e.target.closest(`.${Page.CSS.copyLinkBtn}`);
|
if (target.classList.contains(Page.CSS.copyButtonCopied)) return;
|
||||||
|
|
||||||
if (!copyLinkButtonClicked) {
|
let textToCopy = target.getAttribute('data-text-to-copy');
|
||||||
return;
|
if (!textToCopy) return;
|
||||||
}
|
|
||||||
|
|
||||||
const header = e.target.closest(`.${Page.CSS.header}`);
|
// Check if text to copy is an anchor link
|
||||||
const link = header.querySelector('a').href;
|
if (/^#\S*$/.test(textToCopy))
|
||||||
|
textToCopy = window.location.origin + window.location.pathname + textToCopy;
|
||||||
|
|
||||||
await copyToClipboard(link);
|
try {
|
||||||
header.classList.add(Page.CSS.headerLinkCopied);
|
await copyToClipboard(textToCopy);
|
||||||
|
|
||||||
header.addEventListener('mouseleave', () => {
|
target.classList.add(Page.CSS.copyButtonCopied);
|
||||||
setTimeout(() => {
|
target.addEventListener('mouseleave', () => {
|
||||||
header.classList.remove(Page.CSS.headerLinkCopied);
|
setTimeout(() => target.classList.remove(Page.CSS.copyButtonCopied), 5e2);
|
||||||
}, 500);
|
|
||||||
}, { once: true });
|
}, { once: true });
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error); // @todo send to Hawk
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
103
src/frontend/styles/components/copy-button.pcss
Normal file
103
src/frontend/styles/components/copy-button.pcss
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
.copy-button {
|
||||||
|
position: relative;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: opacity 200ms;
|
||||||
|
|
||||||
|
@media (--can-hover) {
|
||||||
|
&:hover .copy-button__inner {
|
||||||
|
background: var(--color-link-hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
border-radius: 100%;
|
||||||
|
background-color: var(--color-success);
|
||||||
|
visibility: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
transform: scale(1);
|
||||||
|
transition: transform 400ms ease-out, opacity 400ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__inner {
|
||||||
|
@apply --squircle;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: white;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon--initial {
|
||||||
|
display: flex;
|
||||||
|
transform: translateZ(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon--success {
|
||||||
|
display: none;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__copied {
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
opacity: 0;
|
||||||
|
visibility: visible;
|
||||||
|
transform: scale(3.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-button__inner,
|
||||||
|
.copy-button__inner:hover {
|
||||||
|
background: var(--color-success) !important;
|
||||||
|
animation: check-square-in 250ms ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-button__icon--initial {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-button__icon--success {
|
||||||
|
display: flex;
|
||||||
|
animation: check-sign-in 350ms ease-in forwards;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes check-sign-in {
|
||||||
|
from {
|
||||||
|
transform: scale(.7);
|
||||||
|
}
|
||||||
|
80% {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes check-square-in {
|
||||||
|
from {
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
80% {
|
||||||
|
transform: scale(.96);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,8 +40,11 @@ html {
|
||||||
|
|
||||||
@media (--not-mobile) {
|
@media (--not-mobile) {
|
||||||
padding: 4px 10px;
|
padding: 4px 10px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
@apply --squircle;
|
@apply --squircle;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--color-link-hover);
|
background-color: var(--color-link-hover);
|
||||||
|
|
|
@ -125,79 +125,15 @@
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&--link-copied {
|
&__copy-button {
|
||||||
.block-header__copy-link,
|
margin-right: 8px;
|
||||||
.block-header__copy-link:hover {
|
|
||||||
background: var(--color-success);
|
|
||||||
opacity: 1;
|
|
||||||
animation: check-square-in 250ms ease-in;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.block-header__copy-link-icon--initial {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.block-header__copy-link-icon--success {
|
|
||||||
display: flex;
|
|
||||||
animation: check-sign-in 350ms ease-in forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
.block-header__copy-link-splash {
|
|
||||||
opacity: 0;
|
|
||||||
visibility: visible;
|
|
||||||
transform: scale(3.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__copy-link-splash {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
width: 28px;
|
|
||||||
height: 28px;
|
|
||||||
background-color: var(--color-success);
|
|
||||||
transform: scale(1);
|
|
||||||
border-radius: 100%;
|
|
||||||
transition: transform 400ms ease-out, opacity 400ms;
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__copy-link-icon--success {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
display: none;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__copy-link-icon--initial {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__copy-link {
|
|
||||||
width: 28px;
|
|
||||||
height: 28px;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
border-radius: 8px;
|
|
||||||
@apply --squircle;
|
|
||||||
color: var(--color-text-second);
|
color: var(--color-text-second);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
margin-right: 8px;
|
|
||||||
|
|
||||||
@media (--can-hover) {
|
|
||||||
&:hover {
|
|
||||||
background: var(--color-link-hover);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (--can-hover) {
|
@media (--can-hover) {
|
||||||
&:hover {
|
&:hover {
|
||||||
.block-header__copy-link {
|
.block-header__copy-button {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -206,30 +142,6 @@
|
||||||
.inline-code {
|
.inline-code {
|
||||||
line-height: inherit;
|
line-height: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes check-sign-in {
|
|
||||||
from {
|
|
||||||
transform: scale(.7);
|
|
||||||
}
|
|
||||||
80% {
|
|
||||||
transform: scale(1.1);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes check-square-in {
|
|
||||||
from {
|
|
||||||
transform: scale(1.05);
|
|
||||||
}
|
|
||||||
80% {
|
|
||||||
transform: scale(.96);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -237,8 +149,18 @@
|
||||||
* ==================
|
* ==================
|
||||||
*/
|
*/
|
||||||
.block-code {
|
.block-code {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.block-code__copy-button {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__wrapper {
|
||||||
@apply --text-code-block;
|
@apply --text-code-block;
|
||||||
@apply --squircle;
|
@apply --squircle;
|
||||||
|
}
|
||||||
|
|
||||||
&__content {
|
&__content {
|
||||||
display: inline-block !important;
|
display: inline-block !important;
|
||||||
|
@ -257,6 +179,13 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__copy-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.hljs-params {
|
.hljs-params {
|
||||||
color: var(--color-code-params);
|
color: var(--color-code-params);
|
||||||
}
|
}
|
||||||
|
@ -580,4 +509,3 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
.docs-sidebar {
|
.docs-sidebar {
|
||||||
width: 100vw;
|
|
||||||
|
|
||||||
/* Bottom and Left coord of the "Hide Sidebar" toggler */
|
/* Bottom and Left coord of the "Hide Sidebar" toggler */
|
||||||
--hide-sidebar-toggler-offset: 11px;
|
--hide-sidebar-toggler-offset: 11px;
|
||||||
|
@ -183,13 +182,16 @@
|
||||||
transition-property: background-color;
|
transition-property: background-color;
|
||||||
transition-duration: 0.1s;
|
transition-duration: 0.1s;
|
||||||
|
|
||||||
@apply --squircle;
|
|
||||||
|
|
||||||
&--selected {
|
&--selected {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
/* border using box-shadow which doesn't increase the height */
|
/* border using box-shadow which doesn't increase the height */
|
||||||
box-shadow: 0 0 0 2px rgba(147, 166, 233, 0.5) inset;
|
box-shadow: 0 0 0 2px rgba(147, 166, 233, 0.5) inset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&--active,
|
||||||
|
&:hover {
|
||||||
|
@apply --squircle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__section-title > span,
|
&__section-title > span,
|
||||||
|
|
|
@ -31,7 +31,10 @@
|
||||||
gap: 2px;
|
gap: 2px;
|
||||||
|
|
||||||
&-item {
|
&-item {
|
||||||
|
&:hover,
|
||||||
|
&--active {
|
||||||
@apply --squircle;
|
@apply --squircle;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--color-link-hover);
|
background-color: var(--color-link-hover);
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
@media (--desktop) {
|
@media (--desktop) {
|
||||||
max-width: min(
|
max-width: min(
|
||||||
calc(var(--layout-width-main-col) + var(--max-space-between-cols) + var(--layout-sidebar-width)),
|
calc(var(--layout-width-main-col) + var(--max-space-between-cols) + var(--layout-sidebar-width)),
|
||||||
calc(100vw - var(--layout-sidebar-width))
|
calc(100% - var(--layout-sidebar-width))
|
||||||
);
|
);
|
||||||
margin-left: max(var(--main-col-min-margin-left), calc(50vw - var(--layout-sidebar-width) - var(--layout-width-main-col) / 2) - var(--layout-padding-horizontal));
|
margin-left: max(var(--main-col-min-margin-left), calc(50vw - var(--layout-sidebar-width) - var(--layout-width-main-col) / 2) - var(--layout-padding-horizontal));
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
@import './carbon.pcss';
|
@import './carbon.pcss';
|
||||||
@import './components/header.pcss';
|
@import './components/header.pcss';
|
||||||
@import './components/writing.pcss';
|
@import './components/writing.pcss';
|
||||||
|
@import './components/copy-button.pcss';
|
||||||
@import './components/page.pcss';
|
@import './components/page.pcss';
|
||||||
@import './components/greeting.pcss';
|
@import './components/greeting.pcss';
|
||||||
@import './components/auth.pcss';
|
@import './components/auth.pcss';
|
||||||
|
|
|
@ -2887,6 +2887,7 @@ basic-auth@~2.0.1:
|
||||||
big.js@^5.2.2:
|
big.js@^5.2.2:
|
||||||
version "5.2.2"
|
version "5.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
|
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
|
||||||
|
integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
|
||||||
|
|
||||||
binary-extensions@^2.0.0:
|
binary-extensions@^2.0.0:
|
||||||
version "2.2.0"
|
version "2.2.0"
|
||||||
|
@ -3733,6 +3734,7 @@ emoji-regex@^8.0.0:
|
||||||
emojis-list@^3.0.0:
|
emojis-list@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
|
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
|
||||||
|
integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
|
||||||
|
|
||||||
encodeurl@~1.0.2:
|
encodeurl@~1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
|
@ -5062,8 +5064,9 @@ loader-runner@^4.2.0:
|
||||||
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1"
|
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1"
|
||||||
|
|
||||||
loader-utils@^2.0.0:
|
loader-utils@^2.0.0:
|
||||||
version "2.0.2"
|
version "2.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.2.tgz#d6e3b4fb81870721ae4e0868ab11dd638368c129"
|
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c"
|
||||||
|
integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==
|
||||||
dependencies:
|
dependencies:
|
||||||
big.js "^5.2.2"
|
big.js "^5.2.2"
|
||||||
emojis-list "^3.0.0"
|
emojis-list "^3.0.0"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue