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
1,938 changes: 1,267 additions & 671 deletions custom-elements.json

Large diffs are not rendered by default.

46 changes: 45 additions & 1 deletion doc-layout/src/template.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,26 @@ import { styles } from '@divriots/dockit-core/layout';
import '@api-viewer/docs';
import { html } from 'lit';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import { until } from 'lit/directives/until.js';
import logoSvg from './logo.svg?raw';
import { search } from '@divriots/dockit-core/search';
import '~/search/dockit-search.define.js';
import '@divriots/dockit-core/search/dockit-search.define.js';
import '@divriots/dockit-core/version-selector/dockit-version-selector.define.js';

export const docLayoutTemplate = (content, context) => {
// TODO: remove when Backlight is released with new functionality
const isMultiverseSupported = context.isLive !== undefined;

let versionsPromise;
if (isMultiverseSupported) {
if (context.isLive) {
versionsPromise = Promise.resolve([context.version]);
} else {
versionsPromise = import(new URL('/docs-shared.mjs', location.href)).then(
(m) => m.versions
);
}
}
return html`
<style>
${unsafeHTML(styles)} .logo {
Expand Down Expand Up @@ -35,6 +50,35 @@ export const docLayoutTemplate = (content, context) => {
</div>
<dockit-search slot="topbar" .search=${(query) => search(query, context)}>
</dockit-search>
${isMultiverseSupported
? until(
versionsPromise.then((versions) => {
const isLatestVersion =
context.version === versions[versions.length - 1];
return html`
<dockit-version-selector
slot="topbar"
.versions=${versions}
selected=${context.version}
@select=${(event) => {
if (!context.isLive) {
const baseUrl = new URL(
isLatestVersion ? './' : '../',
new URL(context.base, location.href)
);
location.href = new URL(
event.detail.isLatest
? './'
: `./${event.detail.version}/`,
baseUrl
).href;
}
}}
></dockit-version-selector>
`;
})
)
: null}
<div class="prose dark:prose-invert">${unsafeHTML(content)}</div>
</dockit-layout>
`;
Expand Down
7 changes: 7 additions & 0 deletions layout/src/Layout.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,13 @@ export const LayoutStyles = css`
margin-right: var(--private--dockit-layout-spacer);
padding: var(--private--dockit-layout-spacer) 0;
}

:host([data-has-color-schema-toggle]) .topbar-container {
padding-right: calc(
var(--private--dockit-layout-spacer) +
var(--private--dockit-layout-header-content-height)
);
}
}

.buttons-container {
Expand Down
5 changes: 5 additions & 0 deletions layout/src/Layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,11 @@ export class Layout extends LitElement {
} else {
this.removeAttribute('data-is-navigation-shown');
}
if (this.disableColorSchemeChange) {
this.removeAttribute('data-has-color-schema-toggle');
} else {
this.setAttribute('data-has-color-schema-toggle', '');
}
this.setAttribute('data-color-scheme', this.colorScheme);
}

Expand Down
6 changes: 6 additions & 0 deletions layout/src/misc.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ dockit-layout [slot='logo'] > :first-child {
height: var(--private--dockit-layout-logo-height);
}

dockit-layout
dockit-search[slot='topbar']
+ dockit-version-selector[slot='topbar'] {
margin-left: 1em;
}

.prose :where(h1, h2, h3, h4, h5, h6) {
scroll-margin-top: calc(var(--private--dockit-layout-header-height) + 1rem);
}
Expand Down
7 changes: 0 additions & 7 deletions search/src/Search.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,6 @@ export const SearchStyles = css`
:host {
position: relative;
flex-grow: 1;
margin: 0 4rem 0 1rem;
}

@media only screen and (max-width: ${unsafeCSS(breakpoints.lg)}) {
:host {
margin: 0px;
}
}

form {
Expand Down
2 changes: 1 addition & 1 deletion studio.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"introduction",
["Design Token Showcases", ["tailwind-showcases", "css-showcases"]],
["Components", ["do-dont", "note", "columns"]],
["Layout", ["layout", "speedy-links", "search"]],
["Layout", ["layout", "speedy-links", "search", "version-selector"]],
["Atoms", ["box", "clipboard", "text", "showcases", "space"]],
["Infra", ["doc-layout"]]
]
Expand Down
109 changes: 109 additions & 0 deletions version-selector/doc/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
```js script
import { html } from 'lit';
```

# dockit-version-selector

Reusable version selector to implement multiple versions support on the exported static doc site.
Designed and styled to fit the topbar slot of [`<dockit-layout>`](../../layout/doc/index.md).

## Use in Backlight within dockit-layout topbar

You need to add a few things to the [layout template](../../layout/doc/index.md):

- load a list of versions which you plan to expose on the exported static site (build your own shared module for that)
- hardcode versions array to contain the current version if `context.isLive` flag is set
- use `until` directive to render `dockit-version-selector` when versions are loaded
- place it in `slot="topbar"`
- set `dockit-layout.versions` and `dockit-layout.selected`
- provide select handler

```js
// mdjs.config.js
import '@divriots/dockit-core/layout/dockit-layout.define.js';
import '@divriots/dockit-core/layout/dockit-version-selector.define.js';
import { styles } from '@divriots/dockit-core/layout';
import { html } from 'lit';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import { until } from 'lit/directives/until.js';

export default {
layout: (content, context) => {
let versionsPromise;
if (context.isLive) {
versionsPromise = Promise.resolve([context.version]);
} else {
versionsPromise = import(new URL('/docs-shared.mjs', location.href)).then(
(m) => m.versions
);
}
return html`
<style>
/* ... */ ${unsafeHTML(styles)}; /* ... */
</style>
...
<dockit-layout>
<!- ... ->
${until(
versionsPromise.then((versions) => {
return html`
<dockit-version-selector
slot="topbar"
.versions=${versions}
selected=${context.version}
@select=${(event) => {
...
}}
></dockit-version-selector>
`;
})
)}
<!- ... ->
</dockit-layout>
`;
},
};
```

You can find full documentation of [how to setup multiple versions for the exported static doc site in Backlight](https://backlight.dev/docs/doc-multiple-versions).

## Latest version

By convention the last item in `versions` array is the latest version.
This is used inside the component to provide a different label for it and you can use it outside of it to determine if the version is latest if necessary.

## Latest label

By default the latest version will be displayed with the ` (latest)` at the end.
You can adjust this behavior by using one of the following:

- set a dynamic function via `.latestLabel` property, e.g. `` .latestLabel=${(version) => `${version} (current)`} ``
- set a static value via `latest-label` attribute, e.g. `latest-label="latest"`

## Select event

Select event is dispatched when new version is selected and contains `event.detail` with 2 values: `isLatest` flag and `version` value which let you decide where to redirect the page to.
Simple example of the select event handler might look like this:

```js
@select=${(event) => {
if (!context.isLive) {
const version = event.detail.isLatest ? 'latest' : event.detail.version;
location.href = new URL(
`../${version}/`,
new URL(context.base, location.href)
).href;
}
}}
```

## API

```js story
import manifest from '../../custom-elements.json';
export const api = () => html`<api-docs
.manifest="${manifest}"
only="dockit-version-selector"
selected="dockit-version-selector"
></api-docs>`;
```
3 changes: 3 additions & 0 deletions version-selector/dockit-version-selector.define.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { VersionSelector } from './src/VersionSelector.ts';

customElements.define('dockit-version-selector', VersionSelector);
15 changes: 15 additions & 0 deletions version-selector/src/VersionSelector.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { css } from 'lit';

export const VersionSelectorStyles = css`
select {
border: 1px solid var(--dockit-layout-header-border-color);
border-radius: 0.5rem;
padding: 0.5rem;
width: 100%;
z-index: 11;
position: relative;
background: var(--dockit-layout-bg);
font-size: large;
color: inherit;
}
`;
78 changes: 78 additions & 0 deletions version-selector/src/VersionSelector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { html, LitElement, PropertyValues } from 'lit';
import { property } from 'lit/decorators.js';
import { VersionSelectorStyles } from './VersionSelector.styles';

/**
* Version selector for documentation site layout.
*/
export class VersionSelector extends LitElement {
static styles = VersionSelectorStyles;

@property({ type: Array })
versions: string[] = [];

@property()
selected?: string;

@property({ attribute: 'latest-label' })
latestLabel: string | ((version: string) => string) = (version) =>
`${version} (latest)`;

private sortedVersions: string[] = [];

private latest: string;

connectedCallback(): void {
super.connectedCallback();
}

disconnectedCallback(): void {
super.disconnectedCallback();
}

update(changedProperties: PropertyValues): void {
if (changedProperties.has('versions')) {
this.sortedVersions = this.versions.sort().reverse();
this.latest = this.sortedVersions[0];
this.selected ??= this.sortedVersions[0];
}
super.update(changedProperties);
}

render() {
return html`<select
@change=${(event) => this.onVersionSelect(event)}
aria-label="Select version"
>
${this.sortedVersions.map(
(version) =>
html`<option
value="${version}"
?selected=${this.selected === version}
>
${version === this.latest ? this.getLatestLabel() : version}
</option>`
)}
</select>`;
}

protected getLatestLabel(): string {
return typeof this.latestLabel === 'string'
? this.latestLabel
: this.latestLabel(this.latest);
}

protected onVersionSelect(event: Event): void {
if (event.target instanceof HTMLSelectElement) {
const $selectedOption = event.target.selectedOptions[0];
this.dispatchEvent(
new CustomEvent('select', {
detail: {
version: $selectedOption.value,
isLatest: $selectedOption.value === this.latest,
},
})
);
}
}
}