Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,20 @@ window.$docsify = {
};
```

## sidebarPosition

- Type: `String`
- Default: `'left'`

Controls which side of the page displays the sidebar. Set this to `'right'` to
place the sidebar and its toggle on the right.

```js
window.$docsify = {
sidebarPosition: 'right',
};
```

## homepage

- Type: `String`
Expand Down
1 change: 1 addition & 0 deletions src/core/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ const defaultDocsifyConfig = () => ({
skipLink: /** @type {false | string | Record<string, string>} */ (
'Skip to main content'
),
sidebarPosition: /** @type {'left' | 'right'} */ ('left'),
subMaxLevel: 0,
vueComponents: /** @type {Record<string, TODO>} */ ({}),
vueGlobalOptions: /** @type {Record<string, TODO>} */ ({}),
Expand Down
8 changes: 5 additions & 3 deletions src/core/render/tpl.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,20 @@ export function corner(data, cornerExternalLinkTarget) {
* @returns {String} HTML of the main content
*/
export function main(config) {
const { hideSidebar, name } = config;
const { hideSidebar, name, sidebarPosition } = config;
const sidebarPositionClass =
sidebarPosition === 'right' ? ' sidebar-right' : '';
// const name = config.name ? config.name : '';

const aside = /* html */ hideSidebar
? ''
: `
<button class="sidebar-toggle" tabindex="-1" title="Press \\ to toggle">
<button class="sidebar-toggle${sidebarPositionClass}" tabindex="-1" title="Press \\ to toggle">
<div class="sidebar-toggle-button" tabindex="0" aria-label="Hide primary navigation" aria-keyshortcuts="Use shortcut key \\" aria-controls="__sidebar" role="button">
<span></span><span></span><span></span>
</div>
</button>
<aside id="__sidebar" class="sidebar${!isMobile() ? ' show' : ''}" tabindex="-1" role="none">
<aside id="__sidebar" class="sidebar${sidebarPositionClass}${!isMobile() ? ' show' : ''}" tabindex="-1" role="none">
${
config.name
? /* html */ `
Expand Down
9 changes: 8 additions & 1 deletion src/themes/shared/_app.css
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,19 @@ main {
> .content {
position: absolute;
inset: 0;
transition: left var(--duration-medium) ease;
transition:
left var(--duration-medium) ease,
right var(--duration-medium) ease;

body:has(.sidebar.show) & {
left: var(--sidebar-width);
}

body:has(.sidebar.sidebar-right.show) & {
right: var(--sidebar-width);
left: 0;
}

/* hideSidebar: true */
body:not:has(.sidebar) & {
position: static;
Expand Down
10 changes: 10 additions & 0 deletions src/themes/shared/_mq.css
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,16 @@
}
}

body:has(.sidebar.sidebar-right.show) {
.app-nav {
right: 0;
}

main > .content {
right: 0;
}
}

body:has(.app-nav-merged) {
.app-nav {
display: none;
Expand Down
5 changes: 5 additions & 0 deletions src/themes/shared/_navbar.css
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
left: var(--sidebar-width);
}

body:where(:has(.sidebar.sidebar-right.show)) & {
right: var(--sidebar-width);
left: 0;
}

a {
color: var(--navbar-link-color);
text-decoration-color: transparent;
Expand Down
22 changes: 22 additions & 0 deletions src/themes/shared/_sidebar.css
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,14 @@
}
}

.sidebar.sidebar-right {
right: 0;
left: auto;
translate: var(--sidebar-width);
border-right: 0;
border-left: 1px solid var(--sidebar-border-color);
}

.sidebar-nav {
li {
a.page-link {
Expand Down Expand Up @@ -283,3 +291,17 @@
}
}
}

.sidebar-toggle.sidebar-right {
right: 0;
left: auto;
justify-content: end;

body:where(:has(.sidebar.sidebar-right.show)) & {
translate: calc(0px - var(--sidebar-width));
}

.sidebar-toggle-button {
border-radius: var(--border-radius) 0 0 var(--border-radius);
}
}
64 changes: 64 additions & 0 deletions test/e2e/configuration.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,70 @@ test.describe('Configuration options', () => {
await expect(page.locator('#main')).toContainText(expectText);
});
});

test.describe('sidebarPosition', () => {
test('right: renders the sidebar and toggle on the right', async ({
page,
}) => {
await page.emulateMedia({ reducedMotion: 'reduce' });
await docsifyInit({
config: {
sidebarPosition: 'right',
},
markdown: {
homepage: '# Hello World',
},
styleURLs: ['/dist/themes/core.css'],
});

const sidebar = page.locator('.sidebar');
const toggle = page.locator('.sidebar-toggle');

await expect(sidebar).toHaveClass(/sidebar-right/);
await expect(toggle).toHaveClass(/sidebar-right/);
await expect(sidebar).toHaveClass(/show/);

const sidebarBox = await sidebar.boundingBox();
const toggleBox = await toggle.boundingBox();
const layoutWidth = await page.evaluate(
() => document.documentElement.clientWidth,
);

expect(sidebarBox?.x + sidebarBox?.width).toBe(layoutWidth);
expect(toggleBox?.x + toggleBox?.width).toBe(sidebarBox?.x);
});

test('right: opens from the right on mobile', async ({ page }) => {
await page.setViewportSize({ width: 600, height: 800 });
await page.emulateMedia({ reducedMotion: 'reduce' });
await docsifyInit({
config: {
sidebarPosition: 'right',
},
markdown: {
homepage: '# Hello World',
},
styleURLs: ['/dist/themes/core.css'],
});

const sidebar = page.locator('.sidebar');
const toggle = page.locator('.sidebar-toggle');

await expect(sidebar).not.toHaveClass(/show/);
await page.locator('.sidebar-toggle-button').click();
await expect(sidebar).toHaveClass(/show/);

const sidebarBox = await sidebar.boundingBox();
const toggleBox = await toggle.boundingBox();
const layoutWidth = await page.evaluate(
() => document.documentElement.clientWidth,
);

expect(sidebarBox?.x + sidebarBox?.width).toBe(layoutWidth);
expect(toggleBox?.x).toBe(0);
expect(toggleBox?.width).toBe(layoutWidth - (sidebarBox?.width ?? 0));
});
});
});

test.describe('keyBindings', () => {
Expand Down