@chassis-ui/docs 0.6.2 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/README.md +5 -2
- package/package.json +1 -1
- package/src/js/search.js +66 -13
- package/src/layouts/head/Head.astro +9 -2
- package/src/libs/docs-path.ts +13 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @chassis-ui/docs
|
|
2
2
|
|
|
3
|
+
## 0.6.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- The home page of a site under a path gets the home form of the title, `title · subtitle`. The layouts took the page at `/` as the home page, so a site with `baseURL` `https://chassis-ui.com/css/` never got it: its home page is at `/css/`. The home page is now the page at the path of `baseURL`. The title of such a home page changes with this release, and the `title` prop of its layout is used for the social tags only. Keep `title · subtitle` within 70 characters where the site validates its HTML.
|
|
8
|
+
- The color mode script is inline in the head and runs before the first paint. It was a module, which runs after the document is parsed, so a page in dark mode showed light first.
|
|
9
|
+
- Five fixes of the search dialog. The page scrolls again after a search result on the current page is opened: the dialog closed without removing the scroll lock of the body. The input has the focus when the dialog is opened with the button, so typing works at once. The keyboard shortcuts open the dialog as the button does: the page behind it no longer scrolls, and a click outside closes it. A click or Enter on a loading placeholder no longer closes the dialog and saves an empty visit. Escape closes the dialog in Safari when the focus is in the empty input.
|
|
10
|
+
|
|
3
11
|
## 0.6.2
|
|
4
12
|
|
|
5
13
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -109,7 +109,7 @@ currentVersion: '0.5.2'
|
|
|
109
109
|
| Key | Required | Meaning |
|
|
110
110
|
| --------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
111
111
|
| `title`, `subtitle`, `description`, `authors` | yes | Used in the page title and the meta tags |
|
|
112
|
-
| `baseURL` | yes | Canonical URL of the site
|
|
112
|
+
| `baseURL` | yes | Canonical URL of the site. The page at its path is the home page: `/css/` for `https://chassis-ui.com/css/` |
|
|
113
113
|
| `docsPath` | yes | URL path of the docs pages. Starts with `/` |
|
|
114
114
|
| `staticPath` | no | URL path that the pages load the static files from. Default `/static`. See [A site under a prefix](#a-site-under-a-prefix) |
|
|
115
115
|
| `repo` | yes | URL of the repository |
|
|
@@ -207,7 +207,7 @@ staticPath: '/css/static'
|
|
|
207
207
|
|
|
208
208
|
The layouts, the `Icon` shortcode and the scripts of the package then link `/css/static/css/chassis.min.css` and so on. The files do not move: they stay in `static/` of the `public` directory. Two rewrites make the path answer:
|
|
209
209
|
|
|
210
|
-
- on the host, from `/css/static/*` to `/static/*` of the site's deployment.
|
|
210
|
+
- on the host, from `/css/static/*` to `/static/*` of the site's deployment.
|
|
211
211
|
- in the site's own `vercel.json`, from `/css/static/(.*)` to `/static/$1`, so that the deployment also works when it is opened directly.
|
|
212
212
|
|
|
213
213
|
Two kinds of URL are the site's own to move:
|
|
@@ -229,6 +229,8 @@ import SingleLayout from '@chassis-ui/docs/layouts/SingleLayout.astro'
|
|
|
229
229
|
</SingleLayout>
|
|
230
230
|
```
|
|
231
231
|
|
|
232
|
+
The `<title>` of a page is its `title` and the `title` of `config.yml`: `About · Chassis UI`. The home page, which is the page at the path of `baseURL`, gets `title` and `subtitle` of `config.yml` instead: `Chassis UI · Design Systems that Scale & Sync`. Its own `title` is used for the social tags only. html-validate reports a `<title>` of more than 70 characters, so keep the two within that.
|
|
233
|
+
|
|
232
234
|
## Shortcodes
|
|
233
235
|
|
|
234
236
|
Every component in `@chassis-ui/docs/shortcodes/` is available in MDX files without an import. So is every component in the site's `src/components/shortcodes/`. A component of the site replaces the package's component of the same name.
|
|
@@ -374,6 +376,7 @@ The integration provides five modules: `virtual:chassis-docs/config`, `virtual:c
|
|
|
374
376
|
|
|
375
377
|
| `@chassis-ui/docs` | Requires Astro | Requires `@chassis-ui/css` | Requires Node | Built with |
|
|
376
378
|
| ------------------ | -------------------------- | -------------------------- | ------------- | ------------------------------------ |
|
|
379
|
+
| 0.6.3 | `^7.0.0` | `^0.5.0-0` | `>=22.12.0` | Astro 7.3.5, css 0.5.2, tokens 0.6.0 |
|
|
377
380
|
| 0.6.2 | `^7.0.0` | `^0.5.0-0` | `>=22.12.0` | Astro 7.3.5, css 0.5.2, tokens 0.6.0 |
|
|
378
381
|
| 0.6.1 | `^7.0.0` | `^0.5.0-0` | `>=22.12.0` | Astro 7.3.5, css 0.5.2, tokens 0.6.0 |
|
|
379
382
|
| 0.6.0 | `^7.0.0` | `^0.5.0-0` | `>=22.12.0` | Astro 7.3.5, css 0.5.2, tokens 0.5.3 |
|
package/package.json
CHANGED
package/src/js/search.js
CHANGED
|
@@ -16,6 +16,9 @@ import { getInstanceManager } from '@pagefind/component-ui'
|
|
|
16
16
|
import { staticPath } from 'virtual:chassis-docs/static'
|
|
17
17
|
|
|
18
18
|
const DIALOG_SELECTOR = '#cxdSearchDialog'
|
|
19
|
+
// The rows that are links. The loading skeleton reuses the class on a `<div>`,
|
|
20
|
+
// which must not be followed, focused or saved as a visit.
|
|
21
|
+
const ITEM_SELECTOR = 'a.cxd-search-item'
|
|
19
22
|
const SUB_RESULTS_LIMIT = 3
|
|
20
23
|
// How long a search may take before we show the loading skeleton. Most queries
|
|
21
24
|
// resolve faster, so the previous results stay on-screen instead of flashing.
|
|
@@ -294,12 +297,21 @@ class CxdSearchInput extends HTMLElement {
|
|
|
294
297
|
return
|
|
295
298
|
}
|
|
296
299
|
|
|
300
|
+
// Safari keeps Escape for the search field, even an empty one, and the
|
|
301
|
+
// dialog never gets its cancel event. With text in the field the key clears
|
|
302
|
+
// it, as in every browser; without text it closes the dialog here.
|
|
303
|
+
if (event.key === 'Escape' && !this.input.value) {
|
|
304
|
+
event.preventDefault()
|
|
305
|
+
closeSearchDialog(this.closest('dialog'))
|
|
306
|
+
return
|
|
307
|
+
}
|
|
308
|
+
|
|
297
309
|
// Enter follows the first result link.
|
|
298
310
|
// Always preventDefault so the form never implicit-submits.
|
|
299
311
|
if (event.key === 'Enter') {
|
|
300
312
|
event.preventDefault()
|
|
301
313
|
const dialogEl = this.closest('dialog')
|
|
302
|
-
dialogEl?.querySelector(
|
|
314
|
+
dialogEl?.querySelector(ITEM_SELECTOR)?.click()
|
|
303
315
|
}
|
|
304
316
|
}
|
|
305
317
|
|
|
@@ -339,11 +351,11 @@ class CxdSearchResults extends HTMLElement {
|
|
|
339
351
|
}
|
|
340
352
|
|
|
341
353
|
_getLinks() {
|
|
342
|
-
return [...this.querySelectorAll(
|
|
354
|
+
return [...this.querySelectorAll(ITEM_SELECTOR)]
|
|
343
355
|
}
|
|
344
356
|
|
|
345
357
|
_onKeydown(event) {
|
|
346
|
-
const link = event.target.closest(
|
|
358
|
+
const link = event.target.closest(ITEM_SELECTOR)
|
|
347
359
|
if (!link) return
|
|
348
360
|
|
|
349
361
|
const links = this._getLinks()
|
|
@@ -617,6 +629,29 @@ const isEditableTarget = (target) => {
|
|
|
617
629
|
return tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT'
|
|
618
630
|
}
|
|
619
631
|
|
|
632
|
+
const TRIGGER_SELECTOR = `[data-cx-toggle="dialog"][data-cx-target="${DIALOG_SELECTOR}"]`
|
|
633
|
+
|
|
634
|
+
// Puts the caret in the input once the dialog is open. The chassis Dialog
|
|
635
|
+
// focuses the dialog element itself when it opens, so a key typed right after a
|
|
636
|
+
// click on the trigger went nowhere.
|
|
637
|
+
const focusSearchInput = (dialogEl) => {
|
|
638
|
+
requestAnimationFrame(() => {
|
|
639
|
+
if (!dialogEl.open) return
|
|
640
|
+
dialogEl.querySelector('cxd-search-input')?.focus()
|
|
641
|
+
if (!instance.searchTerm) {
|
|
642
|
+
dialogEl.querySelector('cxd-search-results')?._renderEmpty?.()
|
|
643
|
+
}
|
|
644
|
+
})
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
const registerTriggerHandler = () => {
|
|
648
|
+
document.addEventListener('click', (event) => {
|
|
649
|
+
if (!event.target.closest(TRIGGER_SELECTOR)) return
|
|
650
|
+
const dialogEl = document.querySelector(DIALOG_SELECTOR)
|
|
651
|
+
if (dialogEl) focusSearchInput(dialogEl)
|
|
652
|
+
})
|
|
653
|
+
}
|
|
654
|
+
|
|
620
655
|
const openSearchDialog = () => {
|
|
621
656
|
const dialogEl = document.querySelector(DIALOG_SELECTOR)
|
|
622
657
|
if (!dialogEl) return
|
|
@@ -626,15 +661,24 @@ const openSearchDialog = () => {
|
|
|
626
661
|
return
|
|
627
662
|
}
|
|
628
663
|
|
|
629
|
-
//
|
|
630
|
-
//
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
}
|
|
664
|
+
// Open through the trigger, so that a shortcut does what the button does: the
|
|
665
|
+
// chassis Dialog locks the scroll of the body, closes on a click on the
|
|
666
|
+
// backdrop and gives the focus back. Without the chassis bundle the click does
|
|
667
|
+
// nothing, and the native showModal() opens the dialog.
|
|
668
|
+
document.querySelector(TRIGGER_SELECTOR)?.click()
|
|
669
|
+
if (!dialogEl.open) {
|
|
670
|
+
dialogEl.showModal()
|
|
671
|
+
focusSearchInput(dialogEl)
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// Closes through the dismiss button, so that the chassis Dialog animates the
|
|
676
|
+
// dialog out, unlocks the scroll of the body and gives the focus back. Without
|
|
677
|
+
// the chassis bundle the click does nothing, and the native close() closes it.
|
|
678
|
+
const closeSearchDialog = (dialogEl) => {
|
|
679
|
+
if (!dialogEl?.open) return
|
|
680
|
+
dialogEl.querySelector('[data-cx-dismiss="dialog"]')?.click()
|
|
681
|
+
if (dialogEl.open && !dialogEl.classList.contains('hiding')) dialogEl.close()
|
|
638
682
|
}
|
|
639
683
|
|
|
640
684
|
const registerGlobalShortcuts = () => {
|
|
@@ -683,7 +727,7 @@ const setupDialogResetOnClose = () => {
|
|
|
683
727
|
|
|
684
728
|
const registerResultLinkHandler = () => {
|
|
685
729
|
document.addEventListener('click', (event) => {
|
|
686
|
-
const link = event.target.closest(
|
|
730
|
+
const link = event.target.closest(ITEM_SELECTOR)
|
|
687
731
|
if (!link) return
|
|
688
732
|
const dialogEl = link.closest('dialog')
|
|
689
733
|
if (!dialogEl?.matches(DIALOG_SELECTOR)) return
|
|
@@ -697,6 +741,14 @@ const registerResultLinkHandler = () => {
|
|
|
697
741
|
})
|
|
698
742
|
|
|
699
743
|
dialogEl.close()
|
|
744
|
+
|
|
745
|
+
// The trigger opens the dialog through the chassis Dialog, which locks the
|
|
746
|
+
// scroll of the body with this class and unlocks it in its own hide(). The
|
|
747
|
+
// native close() above skips that, so the class stays: when the result is on
|
|
748
|
+
// the current page, nothing reloads and the page can no longer scroll.
|
|
749
|
+
if (!document.querySelector('dialog[open]:modal')) {
|
|
750
|
+
document.body.classList.remove('dialog-open')
|
|
751
|
+
}
|
|
700
752
|
})
|
|
701
753
|
}
|
|
702
754
|
|
|
@@ -704,6 +756,7 @@ const registerResultLinkHandler = () => {
|
|
|
704
756
|
|
|
705
757
|
defineSearchCustomElements()
|
|
706
758
|
registerGlobalShortcuts()
|
|
759
|
+
registerTriggerHandler()
|
|
707
760
|
registerResultLinkHandler()
|
|
708
761
|
|
|
709
762
|
if (document.readyState === 'loading') {
|
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { getConfig } from '../../site'
|
|
9
|
+
import { isHomePath } from '../../libs/docs-path'
|
|
9
10
|
import type { Layout } from '../../libs/layout'
|
|
10
11
|
import Stylesheet from './Stylesheet.astro'
|
|
11
12
|
import Favicons from './Favicons.astro'
|
|
12
13
|
import Social from './Social.astro'
|
|
14
|
+
import colorModes from '../../js/color-modes.js?raw'
|
|
13
15
|
import 'virtual:chassis-docs/styles'
|
|
14
16
|
|
|
15
17
|
interface Props {
|
|
@@ -29,7 +31,8 @@ const { description, layout, robots, thumbnail, title } = Astro.props
|
|
|
29
31
|
|
|
30
32
|
const canonicalUrl = new URL(Astro.url.pathname, Astro.site)
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
// The home page of a site under a path, such as `/css/`, is not at `/`
|
|
35
|
+
const isHome = isHomePath(getConfig().baseURL, Astro.url.pathname)
|
|
33
36
|
const pageTitle = isHome
|
|
34
37
|
? `${getConfig().title} · ${getConfig().subtitle}`
|
|
35
38
|
: `${title} · ${getConfig().title}`
|
|
@@ -60,7 +63,11 @@ const pageTitle = isHome
|
|
|
60
63
|
|
|
61
64
|
{robots && <meta name="robots" content={robots} />}
|
|
62
65
|
|
|
63
|
-
|
|
66
|
+
<!--
|
|
67
|
+
Inline and not a module, so that it sets the theme before the first paint. A module runs
|
|
68
|
+
after the document is parsed, and a page in dark mode would show light first
|
|
69
|
+
-->
|
|
70
|
+
<script is:inline set:html={colorModes} />
|
|
64
71
|
|
|
65
72
|
<Stylesheet />
|
|
66
73
|
<slot name="styles" />
|
package/src/libs/docs-path.ts
CHANGED
|
@@ -17,3 +17,16 @@ export function joinStaticPath(staticPath: string, file = ''): string {
|
|
|
17
17
|
|
|
18
18
|
return name ? `${staticPath}/${name}` : staticPath
|
|
19
19
|
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Whether a page is the home page of the site: the page at the path of `baseURL`. That is
|
|
23
|
+
* `/` for `https://chassis-ui.com` and `/css/` for `https://chassis-ui.com/css/`.
|
|
24
|
+
*
|
|
25
|
+
* @param baseURL The `baseURL` of the site's `config.yml`.
|
|
26
|
+
* @param pathname URL path of the page, with or without a trailing slash.
|
|
27
|
+
*/
|
|
28
|
+
export function isHomePath(baseURL: string, pathname: string): boolean {
|
|
29
|
+
const trim = (value: string) => value.replace(/\/+$/, '')
|
|
30
|
+
|
|
31
|
+
return trim(pathname) === trim(new URL(baseURL).pathname)
|
|
32
|
+
}
|