@kirchdev/duxt 0.3.4 → 0.3.5-dev-main.43-3310f4b
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 +24 -9
- package/app/app.vue +17 -11
- package/app/assets/css/duxt.css +47 -6
- package/app/components/DuxtAnnouncements.vue +117 -0
- package/app/components/DuxtBrunoEntries.vue +62 -0
- package/app/components/DuxtCodeBlock.vue +18 -34
- package/app/components/DuxtCodeToolbar.vue +14 -0
- package/app/components/DuxtCopyButton.vue +33 -0
- package/app/components/DuxtCopyPage.vue +32 -30
- package/app/components/DuxtFooter.vue +39 -8
- package/app/components/DuxtHeader.vue +107 -48
- package/app/components/DuxtLandingProse.vue +1 -1
- package/app/components/DuxtLinkLabel.vue +36 -0
- package/app/components/DuxtLiveWindow.vue +8 -8
- package/app/components/DuxtLocale.vue +10 -1
- package/app/components/DuxtNavigation.vue +2 -2
- package/app/components/DuxtOpenApiClient.vue +285 -90
- package/app/components/DuxtOpenApiMedia.vue +30 -18
- package/app/components/DuxtOpenApiSchema.vue +10 -7
- package/app/components/DuxtPageFeedback.vue +7 -0
- package/app/components/DuxtPageInfo.vue +15 -12
- package/app/components/DuxtPageNav.vue +41 -27
- package/app/components/DuxtPreviewSkeleton.vue +4 -4
- package/app/components/DuxtProgress.vue +2 -14
- package/app/components/DuxtSearch.vue +230 -90
- package/app/components/DuxtSearchInput.vue +70 -0
- package/app/components/DuxtSections.vue +11 -1
- package/app/components/DuxtSegmented.vue +64 -0
- package/app/components/DuxtShortcuts.vue +16 -11
- package/app/components/DuxtShortcutsTrigger.vue +49 -0
- package/app/components/DuxtSkipLink.vue +1 -1
- package/app/components/DuxtToc.vue +16 -10
- package/app/components/DuxtTranslationBanner.vue +1 -1
- package/app/components/DuxtVersion.vue +53 -12
- package/app/components/DuxtVersionBanner.vue +5 -5
- package/app/components/content/AccordionItem.vue +2 -2
- package/app/components/content/BrunoOverview.vue +260 -0
- package/app/components/content/BrunoRequest.vue +141 -0
- package/app/components/content/BrunoRequests.vue +104 -0
- package/app/components/content/Callout.vue +5 -5
- package/app/components/content/ChangelogReleases.vue +161 -40
- package/app/components/content/CodeGroup.vue +41 -37
- package/app/components/content/Field.vue +1 -1
- package/app/components/content/FileTree.vue +1 -1
- package/app/components/content/OpenApiOperation.vue +4 -4
- package/app/components/content/OpenApiOperations.vue +1 -1
- package/app/components/content/OpenApiOverview.vue +1 -1
- package/app/components/content/PackageManagers.vue +42 -58
- package/app/components/content/PageCards.vue +7 -4
- package/app/components/content/Preview.vue +15 -39
- package/app/components/content/ProseImg.vue +596 -49
- package/app/components/content/Since.vue +1 -1
- package/app/components/content/Steps.vue +6 -4
- package/app/components/content/TabGroup.vue +13 -12
- package/app/components/ui/breadcrumb/BreadcrumbSeparator.vue +1 -1
- package/app/components/ui/command/CommandShortcut.vue +1 -1
- package/app/components/ui/dialog/DialogContent.vue +2 -2
- package/app/components/ui/dialog/DialogHeader.vue +1 -1
- package/app/components/ui/dialog/DialogScrollContent.vue +1 -1
- package/app/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +2 -2
- package/app/components/ui/dropdown-menu/DropdownMenuItem.vue +1 -1
- package/app/components/ui/dropdown-menu/DropdownMenuLabel.vue +1 -1
- package/app/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +2 -2
- package/app/components/ui/dropdown-menu/DropdownMenuShortcut.vue +1 -1
- package/app/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +2 -2
- package/app/components/ui/navigation-menu/NavigationMenuContent.vue +1 -1
- package/app/components/ui/navigation-menu/NavigationMenuTrigger.vue +1 -1
- package/app/components/ui/navigation-menu/NavigationMenuViewport.vue +1 -1
- package/app/components/ui/scroll-area/ScrollBar.vue +1 -1
- package/app/components/ui/select/SelectItem.vue +2 -2
- package/app/components/ui/sheet/SheetContent.vue +29 -3
- package/app/components/ui/sidebar/Sidebar.vue +28 -4
- package/app/components/ui/sidebar/SidebarGroupAction.vue +1 -1
- package/app/components/ui/sidebar/SidebarInset.vue +1 -1
- package/app/components/ui/sidebar/SidebarMenuAction.vue +1 -1
- package/app/components/ui/sidebar/SidebarMenuBadge.vue +1 -1
- package/app/components/ui/sidebar/SidebarMenuSub.vue +1 -1
- package/app/components/ui/sidebar/SidebarMenuSubButton.vue +1 -1
- package/app/components/ui/sidebar/SidebarProvider.vue +7 -16
- package/app/components/ui/sidebar/index.ts +10 -2
- package/app/components/ui/sidebar/utils.ts +0 -1
- package/app/components/ui/sonner/Sonner.vue +18 -0
- package/app/components/ui/tabs/TabsList.vue +13 -3
- package/app/components/ui/tabs/TabsTrigger.vue +24 -3
- package/app/components/ui/tooltip/TooltipContent.vue +2 -2
- package/app/composables/useActiveHeading.ts +3 -25
- package/app/composables/useDuxtAnalytics.ts +50 -0
- package/app/composables/useDuxtAnimatedHeight.ts +8 -38
- package/app/composables/useDuxtAnnouncements.ts +153 -0
- package/app/composables/useDuxtCopy.ts +51 -0
- package/app/composables/useDuxtDirection.ts +23 -0
- package/app/composables/useDuxtNavigation.ts +28 -8
- package/app/composables/useDuxtPath.ts +3 -5
- package/app/composables/useDuxtSearch.ts +87 -35
- package/app/composables/useDuxtSection.ts +15 -1
- package/app/composables/useDuxtSectionRow.ts +6 -5
- package/app/composables/useDuxtShortcuts.ts +319 -20
- package/app/composables/useDuxtVersion.ts +57 -6
- package/app/composables/useDuxtViewportMeasure.ts +37 -0
- package/app/composables/useFuzzySearch.ts +9 -4
- package/app/composables/useRecentPages.ts +14 -17
- package/app/error.vue +112 -74
- package/app/layouts/changelog.vue +9 -1
- package/app/layouts/default.vue +9 -0
- package/app/layouts/docs.vue +9 -1
- package/app/layouts/reference.vue +9 -1
- package/app/middleware/duxt-section-layout.global.ts +1 -3
- package/app/pages/[...slug].vue +146 -35
- package/app/pages/index.vue +15 -23
- package/app/types/duxt.d.ts +312 -4
- package/app/utils/analytics.ts +91 -0
- package/app/utils/announcements.ts +205 -0
- package/app/utils/changelog.ts +0 -28
- package/app/utils/code-highlight.ts +3 -1
- package/app/utils/contributor-avatar.ts +27 -0
- package/app/utils/direction.ts +43 -0
- package/app/utils/duxt-config.ts +43 -0
- package/app/utils/duxt-text.ts +10 -0
- package/app/utils/format-date.ts +28 -0
- package/app/utils/generated-sections.ts +68 -12
- package/app/utils/generated-toc.ts +14 -7
- package/app/utils/locale-path.ts +43 -0
- package/app/utils/navigation-tree.ts +51 -1
- package/app/utils/nearest-page.ts +8 -0
- package/app/utils/openapi.ts +16 -11
- package/app/utils/page-controls.ts +215 -0
- package/app/utils/pill.ts +61 -0
- package/app/utils/prose-image.ts +128 -0
- package/app/utils/search-context.ts +110 -0
- package/app/utils/search-display.ts +248 -0
- package/app/utils/section-scope.ts +65 -4
- package/app/utils/stored-json.ts +31 -0
- package/app/utils/version-choices.ts +6 -13
- package/app/utils/version-paths.ts +21 -0
- package/bin/duxt-cache-key.mjs +61 -0
- package/bin/duxt-og-cache.mjs +58 -0
- package/bin/{duxt-report.mjs → duxt.mjs} +13 -7
- package/bruno-model.ts +169 -0
- package/bruno-parse.ts +656 -0
- package/bruno-zip.ts +195 -0
- package/cli.ts +240 -0
- package/duxt-app-config.ts +18 -1
- package/frontmatter.ts +49 -0
- package/git-contributors.ts +322 -0
- package/i18n/locales/de/duxt/announcement.json +7 -0
- package/i18n/locales/de/duxt/bruno.json +25 -0
- package/i18n/locales/de/duxt/changelog.json +1 -1
- package/i18n/locales/de/duxt/code.json +2 -1
- package/i18n/locales/de/duxt/page.json +9 -0
- package/i18n/locales/de/duxt/shortcuts.json +1 -0
- package/i18n/locales/de/duxt/theme.json +2 -1
- package/i18n/locales/en/duxt/announcement.json +7 -0
- package/i18n/locales/en/duxt/bruno.json +25 -0
- package/i18n/locales/en/duxt/changelog.json +1 -1
- package/i18n/locales/en/duxt/code.json +2 -1
- package/i18n/locales/en/duxt/page.json +9 -0
- package/i18n/locales/en/duxt/shortcuts.json +1 -0
- package/i18n/locales/en/duxt/theme.json +2 -1
- package/i18n/locales/es/duxt/announcement.json +7 -0
- package/i18n/locales/es/duxt/bruno.json +25 -0
- package/i18n/locales/es/duxt/changelog.json +1 -1
- package/i18n/locales/es/duxt/code.json +2 -1
- package/i18n/locales/es/duxt/page.json +9 -0
- package/i18n/locales/es/duxt/shortcuts.json +1 -0
- package/i18n/locales/es/duxt/theme.json +2 -1
- package/i18n/locales/fr/duxt/announcement.json +7 -0
- package/i18n/locales/fr/duxt/bruno.json +25 -0
- package/i18n/locales/fr/duxt/changelog.json +1 -1
- package/i18n/locales/fr/duxt/code.json +2 -1
- package/i18n/locales/fr/duxt/page.json +9 -0
- package/i18n/locales/fr/duxt/shortcuts.json +1 -0
- package/i18n/locales/fr/duxt/theme.json +2 -1
- package/i18n/locales/pt/duxt/announcement.json +7 -0
- package/i18n/locales/pt/duxt/bruno.json +25 -0
- package/i18n/locales/pt/duxt/changelog.json +1 -1
- package/i18n/locales/pt/duxt/code.json +2 -1
- package/i18n/locales/pt/duxt/page.json +9 -0
- package/i18n/locales/pt/duxt/shortcuts.json +1 -0
- package/i18n/locales/pt/duxt/theme.json +2 -1
- package/modules/bruno.ts +118 -0
- package/modules/config.ts +26 -0
- package/modules/devtools.ts +2 -3
- package/modules/git-meta.ts +16 -47
- package/modules/search-records.ts +132 -0
- package/nuxt.config.ts +81 -7
- package/og-image-cache.ts +742 -0
- package/package.json +41 -9
- package/public/devtools/checks.html +2 -2
- package/public/devtools/config.html +59 -4
- package/public/devtools/pages.html +8 -1
- package/public/devtools/paths.html +1 -1
- package/public/devtools/sources.html +2 -2
- package/public/devtools/versions.html +3 -0
- package/report.ts +28 -19
- package/search-records.ts +380 -0
- package/section-input.ts +142 -0
- package/section-markdown.ts +67 -0
- package/section-reports.ts +10 -4
- package/sections-bruno.ts +571 -0
- package/sections-changelog.ts +76 -43
- package/sections-openapi.ts +12 -83
- package/sections-resolve.ts +174 -8
- package/sections.ts +33 -10
- package/server/devtools/shell.ts +1 -1
- package/server/mcp/tools/list-pages.ts +77 -36
- package/server/mcp/tools/list-versions.ts +84 -0
- package/server/mcp/tools/read-page.ts +80 -26
- package/server/mcp/tools/search-docs.ts +87 -53
- package/server/middleware/raw-markdown.ts +1 -1
- package/server/routes/llms-full.txt.get.ts +6 -8
- package/server/routes/llms.txt.get.ts +1 -1
- package/server/utils/duxt-server-text.ts +0 -13
- package/server/utils/llms-pages.ts +24 -9
- package/server/utils/mcp-docs.ts +577 -0
- package/sources-cache.ts +376 -0
- package/sources-git.ts +186 -31
- package/sources-resolve.ts +59 -1
- package/sources.ts +122 -13
- package/tfplugindocs.ts +158 -0
|
@@ -8,12 +8,14 @@
|
|
|
8
8
|
get no marker at all. And the list PADDING: typeset gives every `ol` a
|
|
9
9
|
`padding-inline-start` and every `li` one of its own, which pushed the
|
|
10
10
|
items — and with them the markers, positioned relative to each item —
|
|
11
|
-
clear of the rail they are supposed to sit on. `
|
|
11
|
+
clear of the rail they are supposed to sit on. `ps-0` on both puts the
|
|
12
12
|
item's edge back at the container's own padding, which is what
|
|
13
|
-
`-
|
|
14
|
-
marker wide — centred on the line.
|
|
13
|
+
`-start-9` is measured against: 1.5rem in, 2.25rem back out, half a
|
|
14
|
+
marker wide — centred on the line. The logical spellings matter twice
|
|
15
|
+
over here: typeset's own indent is `padding-inline-start`, so a `pl-0`
|
|
16
|
+
reset would miss it entirely in a right-to-left page. -->
|
|
15
17
|
<div
|
|
16
|
-
class="my-6 border-
|
|
18
|
+
class="my-6 border-s ps-6 [counter-reset:step] [&>ol]:ms-0 [&>ol]:list-none [&>ol]:ps-0 [&>ol>li]:relative [&>ol>li]:my-6 [&>ol>li]:ps-0 [&>ol>li]:[counter-increment:step] [&>ol>li]:before:absolute [&>ol>li]:before:-start-9 [&>ol>li]:before:flex [&>ol>li]:before:size-6 [&>ol>li]:before:items-center [&>ol>li]:before:justify-center [&>ol>li]:before:rounded-full [&>ol>li]:before:bg-muted [&>ol>li]:before:text-xs [&>ol>li]:before:font-medium [&>ol>li]:before:text-foreground [&>ol>li]:before:content-[counter(step)]"
|
|
17
19
|
>
|
|
18
20
|
<slot />
|
|
19
21
|
</div>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { TabsRoot } from 'reka-ui';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* `::tab-group` with `:::tab{label="…"}` children.
|
|
@@ -11,11 +11,11 @@ import { TabsList, TabsRoot, TabsTrigger } from 'reka-ui';
|
|
|
11
11
|
* A content component may not take a name the UI kit owns. `::code-group` next
|
|
12
12
|
* to it reads the same way, which is the consolation.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
14
|
+
* The root is reka-ui's own, because the labels are not known until the
|
|
15
|
+
* children have registered themselves: MDC hands them in as slot content, not as
|
|
16
|
+
* a prop. A provide/inject pair collects them, which is also what makes a
|
|
17
|
+
* `:::tab` work at any depth inside the block. The strip is the UI kit's, in its
|
|
18
|
+
* `underline` look.
|
|
19
19
|
*
|
|
20
20
|
* Keyboard navigation, roving focus and the ARIA roles come from reka-ui — the
|
|
21
21
|
* reason not to hand-roll this out of divs.
|
|
@@ -39,19 +39,20 @@ provide<DuxtTabGroupContext>('duxt-tabs', {
|
|
|
39
39
|
|
|
40
40
|
<template>
|
|
41
41
|
<TabsRoot v-model="active" class="my-6">
|
|
42
|
-
<
|
|
43
|
-
|
|
42
|
+
<UiTabsList
|
|
43
|
+
variant="bare"
|
|
44
|
+
class="border-b"
|
|
44
45
|
:aria-label="$t('duxt.page.tabs') as string"
|
|
45
46
|
>
|
|
46
|
-
<
|
|
47
|
+
<UiTabsTrigger
|
|
47
48
|
v-for="tab in tabs"
|
|
48
49
|
:key="tab.value"
|
|
49
50
|
:value="tab.value"
|
|
50
|
-
|
|
51
|
+
variant="underline"
|
|
51
52
|
>
|
|
52
53
|
{{ tab.label }}
|
|
53
|
-
</
|
|
54
|
-
</
|
|
54
|
+
</UiTabsTrigger>
|
|
55
|
+
</UiTabsList>
|
|
55
56
|
|
|
56
57
|
<slot />
|
|
57
58
|
</TabsRoot>
|
|
@@ -28,7 +28,7 @@ const props = withDefaults(
|
|
|
28
28
|
);
|
|
29
29
|
const emits = defineEmits<DialogContentEmits>();
|
|
30
30
|
|
|
31
|
-
const delegatedProps = reactiveOmit(props, 'class');
|
|
31
|
+
const delegatedProps = reactiveOmit(props, 'class', 'showCloseButton');
|
|
32
32
|
|
|
33
33
|
const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|
34
34
|
</script>
|
|
@@ -51,7 +51,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|
|
51
51
|
<DialogClose
|
|
52
52
|
v-if="showCloseButton"
|
|
53
53
|
data-slot="dialog-close"
|
|
54
|
-
class="ring-offset-background focus:ring-ring
|
|
54
|
+
class="ring-offset-background focus-visible:ring-ring absolute top-3 end-3 inline-flex size-7 cursor-pointer items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground focus-visible:ring-2 focus-visible:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
|
|
55
55
|
>
|
|
56
56
|
<Icon name="lucide:x" />
|
|
57
57
|
<span class="sr-only">Close</span>
|
|
@@ -54,7 +54,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|
|
54
54
|
<slot />
|
|
55
55
|
|
|
56
56
|
<DialogClose
|
|
57
|
-
class="absolute top-4
|
|
57
|
+
class="absolute top-4 end-4 p-0.5 transition-colors rounded-md hover:bg-secondary"
|
|
58
58
|
>
|
|
59
59
|
<Icon name="lucide:x" class="w-4 h-4" />
|
|
60
60
|
<span class="sr-only">Close</span>
|
|
@@ -28,13 +28,13 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|
|
28
28
|
v-bind="forwarded"
|
|
29
29
|
:class="
|
|
30
30
|
cn(
|
|
31
|
-
`focus:bg-accent focus:text-accent-foreground relative flex cursor-pointer items-center gap-2 rounded-sm py-1.5
|
|
31
|
+
`focus:bg-accent focus:text-accent-foreground relative flex cursor-pointer items-center gap-2 rounded-sm py-1.5 pe-2 ps-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4`,
|
|
32
32
|
props.class
|
|
33
33
|
)
|
|
34
34
|
"
|
|
35
35
|
>
|
|
36
36
|
<span
|
|
37
|
-
class="pointer-events-none absolute
|
|
37
|
+
class="pointer-events-none absolute start-2 flex size-3.5 items-center justify-center"
|
|
38
38
|
>
|
|
39
39
|
<DropdownMenuItemIndicator>
|
|
40
40
|
<slot name="indicator-icon">
|
|
@@ -31,7 +31,7 @@ const forwardedProps = useForwardProps(delegatedProps);
|
|
|
31
31
|
v-bind="forwardedProps"
|
|
32
32
|
:class="
|
|
33
33
|
cn(
|
|
34
|
-
`relative flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground hover:bg-accent hover:text-accent-foreground cursor-pointer data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:
|
|
34
|
+
`relative flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground hover:bg-accent hover:text-accent-foreground cursor-pointer data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:ps-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:*:[svg]:text-destructive!`,
|
|
35
35
|
props.class
|
|
36
36
|
)
|
|
37
37
|
"
|
|
@@ -19,7 +19,7 @@ const forwardedProps = useForwardProps(delegatedProps);
|
|
|
19
19
|
:data-inset="inset ? '' : undefined"
|
|
20
20
|
v-bind="forwardedProps"
|
|
21
21
|
:class="
|
|
22
|
-
cn('px-2 py-1.5 text-sm font-medium data-[inset]:
|
|
22
|
+
cn('px-2 py-1.5 text-sm font-medium data-[inset]:ps-8', props.class)
|
|
23
23
|
"
|
|
24
24
|
>
|
|
25
25
|
<slot />
|
|
@@ -29,13 +29,13 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|
|
29
29
|
v-bind="forwarded"
|
|
30
30
|
:class="
|
|
31
31
|
cn(
|
|
32
|
-
`focus:bg-accent focus:text-accent-foreground relative flex cursor-pointer items-center gap-2 rounded-sm py-1.5
|
|
32
|
+
`focus:bg-accent focus:text-accent-foreground relative flex cursor-pointer items-center gap-2 rounded-sm py-1.5 pe-2 ps-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4`,
|
|
33
33
|
props.class
|
|
34
34
|
)
|
|
35
35
|
"
|
|
36
36
|
>
|
|
37
37
|
<span
|
|
38
|
-
class="pointer-events-none absolute
|
|
38
|
+
class="pointer-events-none absolute start-2 flex size-3.5 items-center justify-center"
|
|
39
39
|
>
|
|
40
40
|
<DropdownMenuItemIndicator>
|
|
41
41
|
<slot name="indicator-icon">
|
|
@@ -11,7 +11,7 @@ const props = defineProps<{
|
|
|
11
11
|
<span
|
|
12
12
|
data-slot="dropdown-menu-shortcut"
|
|
13
13
|
:class="
|
|
14
|
-
cn('text-muted-foreground
|
|
14
|
+
cn('text-muted-foreground ms-auto text-xs tracking-widest', props.class)
|
|
15
15
|
"
|
|
16
16
|
>
|
|
17
17
|
<slot />
|
|
@@ -23,12 +23,12 @@ const forwardedProps = useForwardProps(delegatedProps);
|
|
|
23
23
|
:data-inset="inset ? '' : undefined"
|
|
24
24
|
:class="
|
|
25
25
|
cn(
|
|
26
|
-
`relative flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:
|
|
26
|
+
`relative flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:ps-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:*:[svg]:text-destructive!`,
|
|
27
27
|
props.class
|
|
28
28
|
)
|
|
29
29
|
"
|
|
30
30
|
>
|
|
31
31
|
<slot />
|
|
32
|
-
<Icon name="lucide:chevron-right" class="
|
|
32
|
+
<Icon name="lucide:chevron-right" class="ms-auto size-4 rtl:-scale-x-100" />
|
|
33
33
|
</DropdownMenuSubTrigger>
|
|
34
34
|
</template>
|
|
@@ -24,7 +24,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|
|
24
24
|
v-bind="forwarded"
|
|
25
25
|
:class="
|
|
26
26
|
cn(
|
|
27
|
-
'data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 top-0
|
|
27
|
+
'data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out ltr:data-[motion=from-end]:slide-in-from-right-52 ltr:data-[motion=from-start]:slide-in-from-left-52 ltr:data-[motion=to-end]:slide-out-to-right-52 ltr:data-[motion=to-start]:slide-out-to-left-52 rtl:data-[motion=from-end]:slide-in-from-left-52 rtl:data-[motion=from-start]:slide-in-from-right-52 rtl:data-[motion=to-end]:slide-out-to-left-52 rtl:data-[motion=to-start]:slide-out-to-right-52 top-0 start-0 w-full p-2 pe-2.5 md:absolute md:w-auto',
|
|
28
28
|
'group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:data-[state=open]:animate-in group-data-[viewport=false]/navigation-menu:data-[state=closed]:animate-out group-data-[viewport=false]/navigation-menu:data-[state=closed]:zoom-out-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:fade-in-0 group-data-[viewport=false]/navigation-menu:data-[state=closed]:fade-out-0 group-data-[viewport=false]/navigation-menu:top-full group-data-[viewport=false]/navigation-menu:mt-1.5 group-data-[viewport=false]/navigation-menu:overflow-hidden group-data-[viewport=false]/navigation-menu:rounded-md group-data-[viewport=false]/navigation-menu:border group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:duration-200 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none',
|
|
29
29
|
props.class
|
|
30
30
|
)
|
|
@@ -24,7 +24,7 @@ const forwardedProps = useForwardProps(delegatedProps);
|
|
|
24
24
|
<slot />
|
|
25
25
|
<Icon
|
|
26
26
|
name="lucide:chevron-down"
|
|
27
|
-
class="relative top-px
|
|
27
|
+
class="relative top-px ms-1 size-3 transition duration-300 group-data-[state=open]:rotate-180"
|
|
28
28
|
aria-hidden="true"
|
|
29
29
|
/>
|
|
30
30
|
</NavigationMenuTrigger>
|
|
@@ -15,7 +15,7 @@ const forwardedProps = useForwardProps(delegatedProps);
|
|
|
15
15
|
</script>
|
|
16
16
|
|
|
17
17
|
<template>
|
|
18
|
-
<div class="absolute top-full
|
|
18
|
+
<div class="absolute top-full start-0 isolate z-50 flex justify-center">
|
|
19
19
|
<NavigationMenuViewport
|
|
20
20
|
data-slot="navigation-menu-viewport"
|
|
21
21
|
v-bind="forwardedProps"
|
|
@@ -23,7 +23,7 @@ const delegatedProps = reactiveOmit(props, 'class');
|
|
|
23
23
|
cn(
|
|
24
24
|
'flex touch-none p-px transition-colors select-none',
|
|
25
25
|
orientation === 'vertical' &&
|
|
26
|
-
'h-full w-2.5 border-
|
|
26
|
+
'h-full w-2.5 border-s border-s-transparent',
|
|
27
27
|
orientation === 'horizontal' &&
|
|
28
28
|
'h-2.5 flex-col border-t border-t-transparent',
|
|
29
29
|
props.class
|
|
@@ -24,12 +24,12 @@ const forwardedProps = useForwardProps(delegatedProps);
|
|
|
24
24
|
v-bind="forwardedProps"
|
|
25
25
|
:class="
|
|
26
26
|
cn(
|
|
27
|
-
'relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5
|
|
27
|
+
'relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 ps-2 pe-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
|
28
28
|
props.class
|
|
29
29
|
)
|
|
30
30
|
"
|
|
31
31
|
>
|
|
32
|
-
<span class="absolute
|
|
32
|
+
<span class="absolute end-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
33
33
|
<SelectItemIndicator>
|
|
34
34
|
<Icon name="lucide:check" class="size-4" />
|
|
35
35
|
</SelectItemIndicator>
|
|
@@ -11,9 +11,28 @@ import {
|
|
|
11
11
|
import { cn } from '@duxt/lib/utils';
|
|
12
12
|
import SheetOverlay from './SheetOverlay.vue';
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* `start` and `end` are ADDED beside `left` and `right`, never instead of them.
|
|
16
|
+
*
|
|
17
|
+
* A consumer asking for `side="left"` means the left of the screen and should
|
|
18
|
+
* keep getting it — a drawer pinned to a fixed edge is a legitimate thing to
|
|
19
|
+
* want, and silently redefining the word would move somebody's sheet the first
|
|
20
|
+
* time they shipped a right-to-left locale. `start` and `end` are the ones that
|
|
21
|
+
* follow the reader, and they are what this layer's own navigation uses.
|
|
22
|
+
*
|
|
23
|
+
* THE DEFAULT IS LOGICAL, the same answer `SidebarProps.side` gives. The two
|
|
24
|
+
* props ask one question and it would be strange to answer it twice: a sheet
|
|
25
|
+
* opened with no side named is not a sheet somebody pinned to an edge, it is
|
|
26
|
+
* one nobody thought about, and the theme's default for a thing nobody thought
|
|
27
|
+
* about is to follow the reader. `end` resolves to `right` in every locale this
|
|
28
|
+
* layer ships, so nothing moves today; what changes is the consumer who writes
|
|
29
|
+
* a bare `<UiSheetContent>` and later declares `dir: 'rtl'`, for whom the panel
|
|
30
|
+
* arrives on the side the page starts from rather than needing a second edit.
|
|
31
|
+
* Naming `right` explicitly is still how you pin it to the screen's right.
|
|
32
|
+
*/
|
|
14
33
|
interface SheetContentProps extends DialogContentProps {
|
|
15
34
|
class?: HTMLAttributes['class'];
|
|
16
|
-
side?: 'top' | 'right' | 'bottom' | 'left';
|
|
35
|
+
side?: 'top' | 'right' | 'bottom' | 'left' | 'start' | 'end';
|
|
17
36
|
}
|
|
18
37
|
|
|
19
38
|
defineOptions({
|
|
@@ -21,7 +40,7 @@ defineOptions({
|
|
|
21
40
|
});
|
|
22
41
|
|
|
23
42
|
const props = withDefaults(defineProps<SheetContentProps>(), {
|
|
24
|
-
side: '
|
|
43
|
+
side: 'end'
|
|
25
44
|
});
|
|
26
45
|
const emits = defineEmits<DialogContentEmits>();
|
|
27
46
|
|
|
@@ -46,6 +65,13 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|
|
46
65
|
'data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b',
|
|
47
66
|
side === 'bottom' &&
|
|
48
67
|
'data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t',
|
|
68
|
+
// The logical pair. Everything but the slide is expressible
|
|
69
|
+
// logically; `slide-in-from-*` names a physical edge and has no
|
|
70
|
+
// logical spelling at all, so each direction states its own.
|
|
71
|
+
side === 'start' &&
|
|
72
|
+
'ltr:data-[state=closed]:slide-out-to-left ltr:data-[state=open]:slide-in-from-left rtl:data-[state=closed]:slide-out-to-right rtl:data-[state=open]:slide-in-from-right inset-y-0 start-0 h-full w-3/4 border-e sm:max-w-sm',
|
|
73
|
+
side === 'end' &&
|
|
74
|
+
'ltr:data-[state=closed]:slide-out-to-right ltr:data-[state=open]:slide-in-from-right rtl:data-[state=closed]:slide-out-to-left rtl:data-[state=open]:slide-in-from-left inset-y-0 end-0 h-full w-3/4 border-s sm:max-w-sm',
|
|
49
75
|
props.class
|
|
50
76
|
)
|
|
51
77
|
"
|
|
@@ -58,7 +84,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|
|
58
84
|
the 24px target size axe asks for. Same ghost treatment as every
|
|
59
85
|
other icon button in the header, so it reads as one. -->
|
|
60
86
|
<DialogClose
|
|
61
|
-
class="ring-offset-background focus:ring-ring absolute top-3
|
|
87
|
+
class="ring-offset-background focus:ring-ring absolute top-3 end-3 flex size-8 cursor-pointer items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none"
|
|
62
88
|
>
|
|
63
89
|
<Icon name="lucide:x" class="size-5" />
|
|
64
90
|
<span class="sr-only">Close</span>
|
|
@@ -5,6 +5,7 @@ import { Sheet, SheetContent } from '@duxt/components/ui/sheet';
|
|
|
5
5
|
import SheetDescription from '@duxt/components/ui/sheet/SheetDescription.vue';
|
|
6
6
|
import SheetHeader from '@duxt/components/ui/sheet/SheetHeader.vue';
|
|
7
7
|
import SheetTitle from '@duxt/components/ui/sheet/SheetTitle.vue';
|
|
8
|
+
import { useDirection } from 'reka-ui';
|
|
8
9
|
import { SIDEBAR_WIDTH_MOBILE, useSidebar } from './utils';
|
|
9
10
|
|
|
10
11
|
defineOptions({
|
|
@@ -12,12 +13,35 @@ defineOptions({
|
|
|
12
13
|
});
|
|
13
14
|
|
|
14
15
|
const props = withDefaults(defineProps<SidebarProps>(), {
|
|
15
|
-
side: '
|
|
16
|
+
side: 'start',
|
|
16
17
|
variant: 'sidebar',
|
|
17
18
|
collapsible: 'offcanvas'
|
|
18
19
|
});
|
|
19
20
|
|
|
20
21
|
const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The logical side, resolved to a physical one ONCE.
|
|
25
|
+
*
|
|
26
|
+
* Everything below this line — `data-side`, the rail's `[data-side=left]`
|
|
27
|
+
* selectors, the border and offset classes — is physical and stays physical,
|
|
28
|
+
* because it describes where the panel actually is on the screen. Turning
|
|
29
|
+
* `start` into `left` here is what lets all of it keep working while the prop
|
|
30
|
+
* a consumer writes follows the reader.
|
|
31
|
+
*
|
|
32
|
+
* reka's `useDirection` rather than the i18n locale: this is a ui primitive,
|
|
33
|
+
* and the `ConfigProvider` the app shell mounts is the one place the direction
|
|
34
|
+
* is already published to components that must not know about i18n.
|
|
35
|
+
*/
|
|
36
|
+
const direction = useDirection();
|
|
37
|
+
|
|
38
|
+
const resolvedSide = computed(() => {
|
|
39
|
+
if (props.side === 'start')
|
|
40
|
+
return direction.value === 'rtl' ? 'right' : 'left';
|
|
41
|
+
if (props.side === 'end') return direction.value === 'rtl' ? 'left' : 'right';
|
|
42
|
+
|
|
43
|
+
return props.side;
|
|
44
|
+
});
|
|
21
45
|
</script>
|
|
22
46
|
|
|
23
47
|
<template>
|
|
@@ -45,7 +69,7 @@ const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
|
|
|
45
69
|
data-sidebar="sidebar"
|
|
46
70
|
data-slot="sidebar"
|
|
47
71
|
data-mobile="true"
|
|
48
|
-
:side="
|
|
72
|
+
:side="resolvedSide"
|
|
49
73
|
class="bg-sidebar text-sidebar-foreground w-(--sidebar-width) p-0 [&>button]:hidden"
|
|
50
74
|
:style="{
|
|
51
75
|
'--sidebar-width': SIDEBAR_WIDTH_MOBILE
|
|
@@ -68,7 +92,7 @@ const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
|
|
|
68
92
|
:data-state="state"
|
|
69
93
|
:data-collapsible="state === 'collapsed' ? collapsible : ''"
|
|
70
94
|
:data-variant="variant"
|
|
71
|
-
:data-side="
|
|
95
|
+
:data-side="resolvedSide"
|
|
72
96
|
>
|
|
73
97
|
<!-- This is what handles the sidebar gap on desktop -->
|
|
74
98
|
<div
|
|
@@ -87,7 +111,7 @@ const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
|
|
|
87
111
|
:class="
|
|
88
112
|
cn(
|
|
89
113
|
'fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex',
|
|
90
|
-
|
|
114
|
+
resolvedSide === 'left'
|
|
91
115
|
? 'left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]'
|
|
92
116
|
: 'right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]',
|
|
93
117
|
// Adjust the padding for floating and inset variants.
|
|
@@ -19,7 +19,7 @@ const props = defineProps<
|
|
|
19
19
|
:as-child="asChild"
|
|
20
20
|
:class="
|
|
21
21
|
cn(
|
|
22
|
-
'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-3.5
|
|
22
|
+
'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-3.5 end-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
|
|
23
23
|
'after:absolute after:-inset-2 md:after:hidden',
|
|
24
24
|
'group-data-[collapsible=icon]:hidden',
|
|
25
25
|
props.class
|
|
@@ -13,7 +13,7 @@ const props = defineProps<{
|
|
|
13
13
|
:class="
|
|
14
14
|
cn(
|
|
15
15
|
'bg-background relative flex w-full flex-1 flex-col',
|
|
16
|
-
'md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:
|
|
16
|
+
'md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ms-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ms-2',
|
|
17
17
|
props.class
|
|
18
18
|
)
|
|
19
19
|
"
|
|
@@ -23,7 +23,7 @@ const props = withDefaults(
|
|
|
23
23
|
data-sidebar="menu-action"
|
|
24
24
|
:class="
|
|
25
25
|
cn(
|
|
26
|
-
'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute top-1.5
|
|
26
|
+
'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute top-1.5 end-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
|
|
27
27
|
'after:absolute after:-inset-2 md:after:hidden',
|
|
28
28
|
'peer-data-[size=sm]/menu-button:top-1',
|
|
29
29
|
'peer-data-[size=default]/menu-button:top-1.5',
|
|
@@ -13,7 +13,7 @@ const props = defineProps<{
|
|
|
13
13
|
data-sidebar="menu-badge"
|
|
14
14
|
:class="
|
|
15
15
|
cn(
|
|
16
|
-
'text-sidebar-foreground pointer-events-none absolute
|
|
16
|
+
'text-sidebar-foreground pointer-events-none absolute end-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums select-none',
|
|
17
17
|
'peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground',
|
|
18
18
|
'peer-data-[size=sm]/menu-button:top-1',
|
|
19
19
|
'peer-data-[size=default]/menu-button:top-1.5',
|
|
@@ -13,7 +13,7 @@ const props = defineProps<{
|
|
|
13
13
|
data-sidebar="menu-badge"
|
|
14
14
|
:class="
|
|
15
15
|
cn(
|
|
16
|
-
'border-sidebar-border mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-
|
|
16
|
+
'border-sidebar-border mx-3.5 flex min-w-0 ltr:translate-x-px rtl:-translate-x-px flex-col gap-1 border-s px-2.5 py-0.5',
|
|
17
17
|
'group-data-[collapsible=icon]:hidden',
|
|
18
18
|
props.class
|
|
19
19
|
)
|
|
@@ -29,7 +29,7 @@ const props = withDefaults(
|
|
|
29
29
|
:data-active="isActive"
|
|
30
30
|
:class="
|
|
31
31
|
cn(
|
|
32
|
-
'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',
|
|
32
|
+
'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground flex h-7 min-w-0 ltr:-translate-x-px rtl:translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',
|
|
33
33
|
'data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground',
|
|
34
34
|
size === 'sm' && 'text-xs',
|
|
35
35
|
size === 'md' && 'text-sm',
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { HTMLAttributes, Ref } from 'vue';
|
|
3
|
-
import {
|
|
4
|
-
defaultDocument,
|
|
5
|
-
useEventListener,
|
|
6
|
-
useMediaQuery,
|
|
7
|
-
useVModel
|
|
8
|
-
} from '@vueuse/core';
|
|
3
|
+
import { defaultDocument, useMediaQuery, useVModel } from '@vueuse/core';
|
|
9
4
|
import { TooltipProvider } from 'reka-ui';
|
|
10
5
|
import { computed, ref } from 'vue';
|
|
11
6
|
import { cn } from '@duxt/lib/utils';
|
|
@@ -13,7 +8,6 @@ import {
|
|
|
13
8
|
provideSidebarContext,
|
|
14
9
|
SIDEBAR_COOKIE_MAX_AGE,
|
|
15
10
|
SIDEBAR_COOKIE_NAME,
|
|
16
|
-
SIDEBAR_KEYBOARD_SHORTCUT,
|
|
17
11
|
SIDEBAR_WIDTH,
|
|
18
12
|
SIDEBAR_WIDTH_ICON
|
|
19
13
|
} from './utils';
|
|
@@ -62,15 +56,12 @@ function toggleSidebar() {
|
|
|
62
56
|
: setOpen(!open.value);
|
|
63
57
|
}
|
|
64
58
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
toggleSidebar();
|
|
72
|
-
}
|
|
73
|
-
});
|
|
59
|
+
// Upstream binds `⌘/Ctrl+B` here. It is gone deliberately: a global key
|
|
60
|
+
// registered by a primitive is one `useDuxtShortcuts()` does not know about, so
|
|
61
|
+
// the sheet cannot list it, the documentation cannot describe it and
|
|
62
|
+
// `shortcuts.singleCharacter` cannot switch it off — a shortcut that works and
|
|
63
|
+
// that nobody can find. `SidebarTrigger` is how the sidebar is toggled; a site
|
|
64
|
+
// that wants a key for it declares one the same way the layer does.
|
|
74
65
|
|
|
75
66
|
// We add a state so that we can do data-state="expanded" or "collapsed".
|
|
76
67
|
// This makes it easier to style the sidebar with Tailwind classes.
|
|
@@ -3,7 +3,15 @@ import type { HTMLAttributes } from 'vue';
|
|
|
3
3
|
import { cva } from 'class-variance-authority';
|
|
4
4
|
|
|
5
5
|
export interface SidebarProps {
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* `start`/`end` follow the reader; `left`/`right` pin to the screen.
|
|
8
|
+
*
|
|
9
|
+
* The default moved from `left` to `start`, which is the same edge in a
|
|
10
|
+
* left-to-right page and the mirrored one in a right-to-left page — so an
|
|
11
|
+
* existing site sees no change and a translated one stops being wrong. The
|
|
12
|
+
* physical pair stays for a drawer that genuinely means a fixed edge.
|
|
13
|
+
*/
|
|
14
|
+
side?: 'left' | 'right' | 'start' | 'end';
|
|
7
15
|
variant?: 'sidebar' | 'floating' | 'inset';
|
|
8
16
|
collapsible?: 'offcanvas' | 'icon' | 'none';
|
|
9
17
|
class?: HTMLAttributes['class'];
|
|
@@ -36,7 +44,7 @@ export { default as SidebarTrigger } from './SidebarTrigger.vue';
|
|
|
36
44
|
export { useSidebar } from './utils';
|
|
37
45
|
|
|
38
46
|
export const sidebarMenuButtonVariants = cva(
|
|
39
|
-
'peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-
|
|
47
|
+
'peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-start text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pe-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',
|
|
40
48
|
{
|
|
41
49
|
variants: {
|
|
42
50
|
variant: {
|
|
@@ -6,7 +6,6 @@ export const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
|
|
|
6
6
|
export const SIDEBAR_WIDTH = '16rem';
|
|
7
7
|
export const SIDEBAR_WIDTH_MOBILE = '18rem';
|
|
8
8
|
export const SIDEBAR_WIDTH_ICON = '3rem';
|
|
9
|
-
export const SIDEBAR_KEYBOARD_SHORTCUT = 'b';
|
|
10
9
|
|
|
11
10
|
export const [useSidebar, provideSidebarContext] = createContext<{
|
|
12
11
|
state: ComputedRef<'expanded' | 'collapsed'>;
|
|
@@ -11,16 +11,34 @@ const {
|
|
|
11
11
|
closeButton = true,
|
|
12
12
|
duration = 4000,
|
|
13
13
|
position = 'bottom-right',
|
|
14
|
+
dir,
|
|
14
15
|
...forwarded
|
|
15
16
|
} = defineProps<ToasterProps>();
|
|
16
17
|
|
|
18
|
+
// 4000 ms is part of this layer-owned component's notification behaviour.
|
|
19
|
+
// A site needing different notification UX overrides the component instead of
|
|
20
|
+
// taking on a one-purpose configuration key.
|
|
21
|
+
|
|
17
22
|
const theme = computed(() => (colorMode.value === 'dark' ? 'dark' : 'light'));
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Sonner reads neither the document nor the reka provider — same as the theme
|
|
26
|
+
* above, it has to be told. Left out, a toast in a right-to-left page keeps its
|
|
27
|
+
* close button and its icon on the western side of the card.
|
|
28
|
+
*
|
|
29
|
+
* Pulled out of `forwarded` rather than bound beside it, so a caller passing
|
|
30
|
+
* `dir` explicitly still wins; the locale is the default, not an override.
|
|
31
|
+
*/
|
|
32
|
+
const locale = useDuxtDirection();
|
|
33
|
+
|
|
34
|
+
const direction = computed(() => dir ?? locale.value);
|
|
18
35
|
</script>
|
|
19
36
|
|
|
20
37
|
<template>
|
|
21
38
|
<Sonner
|
|
22
39
|
class="toaster group"
|
|
23
40
|
:theme="theme"
|
|
41
|
+
:dir="direction"
|
|
24
42
|
:close-button="closeButton"
|
|
25
43
|
:duration="duration"
|
|
26
44
|
:position="position"
|
|
@@ -5,11 +5,19 @@ import { reactiveOmit } from '@vueuse/core';
|
|
|
5
5
|
import { TabsList } from 'reka-ui';
|
|
6
6
|
import { cn } from '@duxt/lib/utils';
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* `variant="bare"` is the strip inside a code card's toolbar: no track of its
|
|
10
|
+
* own, because the toolbar already is one. `default` is shadcn's segmented
|
|
11
|
+
* track, kept for a consumer who wants it.
|
|
12
|
+
*/
|
|
8
13
|
const props = defineProps<
|
|
9
|
-
TabsListProps & {
|
|
14
|
+
TabsListProps & {
|
|
15
|
+
class?: HTMLAttributes['class'];
|
|
16
|
+
variant?: 'default' | 'bare';
|
|
17
|
+
}
|
|
10
18
|
>();
|
|
11
19
|
|
|
12
|
-
const delegatedProps = reactiveOmit(props, 'class');
|
|
20
|
+
const delegatedProps = reactiveOmit(props, 'class', 'variant');
|
|
13
21
|
</script>
|
|
14
22
|
|
|
15
23
|
<template>
|
|
@@ -18,7 +26,9 @@ const delegatedProps = reactiveOmit(props, 'class');
|
|
|
18
26
|
v-bind="delegatedProps"
|
|
19
27
|
:class="
|
|
20
28
|
cn(
|
|
21
|
-
|
|
29
|
+
props.variant === 'bare'
|
|
30
|
+
? 'flex items-center gap-1'
|
|
31
|
+
: 'bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-0.75',
|
|
22
32
|
props.class
|
|
23
33
|
)
|
|
24
34
|
"
|