@uxfront/layer-docs 0.6.0 → 0.7.0

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @uxfront/layer-docs
2
2
 
3
- The Nuxt layer for UXFront's documentation sites. It extends [Docus](https://docus.dev), which renders the markdown in `content/docs/` with a header, sidebar, search and table of contents, and adds a framework switcher: every reader sees the examples for their framework, on every page. It can also set the header's site name the way the UXFront homepages do, signed "by UXFront".
3
+ The Nuxt layer for UXFront's documentation sites. It extends [Docus](https://docus.dev), which renders the markdown in `content/docs/` with a header, sidebar, search and table of contents, and adds a framework switcher: every reader picks their framework once, and sees its examples on every page. It can also set the header's site name the way the UXFront homepages do, signed "by UXFront".
4
4
 
5
5
  ## Install
6
6
 
@@ -70,12 +70,29 @@ export const Button = () => <button>Save</button>;
70
70
  ::
71
71
  ````
72
72
 
73
+ It shows the code for the reader's framework. Docus only highlights a few languages (Vue, TypeScript, HTML, CSS, …), so the layer adds `tsx`, `svelte`, `angular-html`, `angular-ts` and `astro`. Add any other your examples need in the app's `nuxt.config.ts`:
74
+
75
+ ```ts
76
+ // nuxt.config.ts
77
+ export default defineNuxtConfig({
78
+ content: {
79
+ build: {
80
+ markdown: {
81
+ highlight: { langs: ["jsx"] },
82
+ },
83
+ },
84
+ },
85
+ });
86
+ ```
87
+
73
88
  ## What it adds
74
89
 
75
- - **`FrameworkSwitcher`.** One tab per framework, on Nuxt UI's tabs, so it follows the WAI-ARIA tabs pattern (arrow keys between tabs, panels labelled by their tab). A page doesn't have to cover every framework: a missing one shows the first one the page has, with a note saying so. When the tabs outgrow the width, the list scrolls.
76
- - **A Framework select above the sidebar.** It replaces Docus's `DocsAsideLeftTop` and renders Docus's below it.
90
+ - **A Framework select above the sidebar.** It picks the framework for the whole site, and lines up with the navigation's pages below it. It replaces Docus's `DocsAsideLeftTop` and renders Docus's below it.
91
+ - **The same select in the header's menu.** On smaller screens, where Docus hides the sidebar, the menu that stands in for it starts with the select. It replaces Docus's `AppHeaderBody` and renders Docus's below it.
92
+ - **`FrameworkSwitcher`.** It shows the slot for the reader's framework, with no tabs of its own, since the select already picks one for every page. A page doesn't have to cover every framework: a missing one shows the first one the page has, with a note saying so.
77
93
  - **A header wordmark and byline.** It replaces Docus's `AppHeaderLeft`, and renders Docus's unless the app sets a wordmark.
78
94
  - **`useFramework()`.** The reader's pick, shared by every switcher and the select, and kept in `localStorage` across visits. It's read once the page is mounted, so the prerendered HTML shows the default framework and hydrates cleanly.
95
+ - **Highlighting for the frameworks' languages.** `tsx`, `svelte`, `angular-html`, `angular-ts` and `astro`, on top of Docus's.
79
96
  - **Bundled icons.** Nuxt Icon bundles the icons named in app config too, so the select's icons don't come from the Iconify API.
80
97
 
81
98
  Everything else is Docus's: configure it as its [docs](https://docus.dev) describe.
@@ -0,0 +1,19 @@
1
+ <script setup lang="ts">
2
+ // Replaces Docus's to put the framework select above the navigation in the
3
+ // header's menu, which stands in for the sidebar on smaller screens, as
4
+ // DocsAsideLeftTop does above the sidebar. Docus's renders below it.
5
+ import DocusAppHeaderBody from "docus/app/components/app/AppHeaderBody.vue";
6
+
7
+ const { frameworks } = useFramework();
8
+ const labelId = useId();
9
+ </script>
10
+
11
+ <template>
12
+ <div v-if="frameworks.length">
13
+ <span :id="labelId" class="w-full pr-2.5 py-1.5 flex items-center text-sm text-muted">
14
+ Framework
15
+ </span>
16
+ <DocsFrameworkSelect :aria-labelledby="labelId" class="mb-2" />
17
+ </div>
18
+ <DocusAppHeaderBody />
19
+ </template>
@@ -1,6 +1,4 @@
1
1
  <script setup lang="ts">
2
- import type { FrameworkOption } from "../../composables/useFramework";
3
-
4
2
  /**
5
3
  * Shows the reader's framework out of a page's per-framework examples:
6
4
  *
@@ -13,62 +11,24 @@ import type { FrameworkOption } from "../../composables/useFramework";
13
11
  * ::
14
12
  * ```
15
13
  *
16
- * One tab per framework in `docsTheme.frameworks`. Picking one here picks it
17
- * everywhere, the sidebar's select included. Nuxt UI's tabs bring the WAI-ARIA
18
- * tabs pattern: arrow keys move between tabs, and each panel is labelled by its tab.
14
+ * The reader picks it in the Framework select, above the sidebar or, on
15
+ * smaller screens, in the header's menu, for the whole site at once, so the
16
+ * switcher has no tabs of its own. A page can cover only some frameworks. The
17
+ * others show the first one it covers, with a note saying so.
19
18
  */
20
- const { framework, frameworks, current } = useFramework();
19
+ const { frameworks, current } = useFramework();
21
20
  const slots = useSlots();
22
- const tabs = useTemplateRef("tabs");
23
-
24
- // A page can cover only some frameworks. The others show the first one it covers.
25
- function shown(option: FrameworkOption) {
26
- return slots[option.value] ? option : frameworks.value.find((f) => slots[f.value]);
27
- }
28
21
 
29
- // When the tabs outgrow the width, the list scrolls. A pick made elsewhere (in
30
- // another switcher, in the sidebar, or restored once mounted) scrolls the list
31
- // to its tab. scrollIntoView() would scroll the page to the switcher too.
32
- watch(
33
- current,
34
- (option) => {
35
- const index = option ? frameworks.value.indexOf(option) : -1;
36
- const tab: HTMLElement | undefined = tabs.value?.triggersRef[index]?.$el;
37
- const list = tab?.parentElement;
38
- if (!tab || !list) return;
39
- const end = tab.offsetLeft + tab.offsetWidth - list.clientWidth;
40
- list.scrollLeft = Math.min(tab.offsetLeft, Math.max(list.scrollLeft, end));
41
- },
42
- { flush: "post" },
22
+ const shown = computed(() =>
23
+ current.value && slots[current.value.value]
24
+ ? current.value
25
+ : frameworks.value.find((option) => slots[option.value]),
43
26
  );
44
27
  </script>
45
28
 
46
29
  <template>
47
- <UTabs
48
- v-if="current"
49
- ref="tabs"
50
- :model-value="current.value"
51
- :items="frameworks"
52
- color="primary"
53
- variant="link"
54
- size="sm"
55
- class="framework-switcher"
56
- :ui="{
57
- root: 'my-5 gap-4',
58
- // Scrolls when the tabs outgrow the width. The baseline and the selected
59
- // tab's underline are drawn inside the list, where scrolling doesn't clip them.
60
- list: 'overflow-x-auto border-b-0 mb-0 shadow-[inset_0_-1px_0_var(--ui-border)]',
61
- indicator: 'bottom-0',
62
- trigger: 'shrink-0',
63
- content: '*:first:mt-0 *:last:mb-0',
64
- }"
65
- @update:model-value="framework = String($event)"
66
- >
67
- <template #content="{ item }">
68
- <p v-if="!slots[item.value] && shown(item)" class="mb-2 text-sm text-muted">
69
- Not available for {{ item.label }}, showing {{ shown(item)!.label }}.
70
- </p>
71
- <slot v-if="shown(item)" :name="shown(item)!.value" />
72
- </template>
73
- </UTabs>
30
+ <p v-if="current && shown && shown !== current" class="my-5 text-sm text-muted">
31
+ Not available for {{ current.label }}, showing {{ shown.label }}.
32
+ </p>
33
+ <slot v-if="shown" :name="shown.value" />
74
34
  </template>
@@ -1,15 +1,29 @@
1
1
  <script setup lang="ts">
2
+ // The Framework label above it lines up with the navigation's categories, so the
3
+ // select sits like one of their pages: indented behind the same guide line, at
4
+ // the same width, with the same icon size, so its icon and label line up with
5
+ // theirs. The wrappers mirror the navigation's child list and its items. The
6
+ // classes go to the outer one, so a margin stays outside the guide line, and
7
+ // the other attributes, like DocsAsideLeftTop's aria-labelledby, to the select.
8
+ defineOptions({ inheritAttrs: false });
9
+
2
10
  const { framework, frameworks, current } = useFramework();
3
11
  </script>
4
12
 
5
13
  <template>
6
- <USelect
7
- :model-value="current?.value"
8
- :items="frameworks"
9
- variant="ghost"
10
- color="neutral"
11
- :icon="current?.icon"
12
- class="w-full"
13
- @update:model-value="framework = $event"
14
- />
14
+ <div :class="$attrs.class" class="ms-2.5 -me-2.5 border-s border-default">
15
+ <div class="ps-1.5 -ms-px">
16
+ <USelect
17
+ v-bind="{ ...$attrs, class: undefined }"
18
+ :model-value="current?.value"
19
+ :items="frameworks"
20
+ variant="ghost"
21
+ color="neutral"
22
+ :icon="current?.icon"
23
+ :ui="{ leadingIcon: 'size-4 mx-0.5' }"
24
+ class="w-full"
25
+ @update:model-value="framework = $event"
26
+ />
27
+ </div>
28
+ </div>
15
29
  </template>
package/nuxt.config.ts CHANGED
@@ -3,8 +3,9 @@
3
3
  *
4
4
  * It extends Docus, which renders `content/docs/` with its header, sidebar,
5
5
  * search and table of contents, and adds a framework switcher on top: a
6
- * `::framework-switcher` content component with one slot per framework, and a
7
- * Framework select above the sidebar. The app lists its frameworks in
6
+ * Framework select above the sidebar (in the header's menu on smaller screens),
7
+ * and a `::framework-switcher` content component with one slot per framework,
8
+ * which shows the reader's pick. The app lists its frameworks in
8
9
  * `docsTheme.frameworks` in its `app.config.ts`. It can also set the header's
9
10
  * site name as a wordmark, signed "by UXFront" (`docsTheme.wordmark` and
10
11
  * `docsTheme.byline`).
@@ -14,6 +15,18 @@
14
15
  export default defineNuxtConfig({
15
16
  extends: ["docus"],
16
17
 
18
+ // The languages of the per-framework examples, on top of the ones Docus
19
+ // highlights (Vue, TypeScript, HTML, CSS, …). Nuxt merges these with the app's.
20
+ content: {
21
+ build: {
22
+ markdown: {
23
+ highlight: {
24
+ langs: ["tsx", "svelte", "angular-html", "angular-ts", "astro"],
25
+ },
26
+ },
27
+ },
28
+ },
29
+
17
30
  icon: {
18
31
  clientBundle: {
19
32
  scan: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uxfront/layer-docs",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Nuxt layer for UXFront documentation sites: Docus, plus a framework switcher that shows every reader the examples for their framework, and the UXFront header wordmark.",
5
5
  "keywords": [
6
6
  "docs",