@lupinum/ginko-docs 0.2.2
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/LICENSE +21 -0
- package/README.md +112 -0
- package/app/app.config.ts +49 -0
- package/app/app.vue +71 -0
- package/app/assets/css/prose.css +1154 -0
- package/app/assets/css/tailwind.css +365 -0
- package/app/components/OgImage/GinkoDocs.satori.vue +117 -0
- package/app/components/content/DocumentPageShell.vue +55 -0
- package/app/components/content/Feedback.vue +97 -0
- package/app/components/content/PageMarkdownCopy.vue +102 -0
- package/app/components/mdc/MdcAccordion.vue +64 -0
- package/app/components/mdc/MdcAccordionItem.vue +51 -0
- package/app/components/mdc/MdcAlert.vue +34 -0
- package/app/components/mdc/MdcAside.vue +27 -0
- package/app/components/mdc/MdcCallout.vue +62 -0
- package/app/components/mdc/MdcCard.vue +186 -0
- package/app/components/mdc/MdcCardGroup.vue +47 -0
- package/app/components/mdc/MdcCenter.vue +71 -0
- package/app/components/mdc/MdcCodeGroup.vue +193 -0
- package/app/components/mdc/MdcColumn.vue +70 -0
- package/app/components/mdc/MdcDocImg.vue +25 -0
- package/app/components/mdc/MdcDropcap.vue +84 -0
- package/app/components/mdc/MdcError.vue +16 -0
- package/app/components/mdc/MdcField.vue +62 -0
- package/app/components/mdc/MdcFieldGroup.vue +18 -0
- package/app/components/mdc/MdcFigure.vue +51 -0
- package/app/components/mdc/MdcIdea.vue +16 -0
- package/app/components/mdc/MdcInfo.vue +16 -0
- package/app/components/mdc/MdcLayout.vue +57 -0
- package/app/components/mdc/MdcNote.vue +16 -0
- package/app/components/mdc/MdcPassage.vue +32 -0
- package/app/components/mdc/MdcQuiz.vue +281 -0
- package/app/components/mdc/MdcQuizOption.vue +91 -0
- package/app/components/mdc/MdcQuizQuestion.vue +226 -0
- package/app/components/mdc/MdcReadMore.vue +90 -0
- package/app/components/mdc/MdcReadMoreGroup.vue +18 -0
- package/app/components/mdc/MdcShortcut.vue +46 -0
- package/app/components/mdc/MdcStep.vue +42 -0
- package/app/components/mdc/MdcSteps.vue +25 -0
- package/app/components/mdc/MdcSuccess.vue +16 -0
- package/app/components/mdc/MdcTab.vue +17 -0
- package/app/components/mdc/MdcTabs.vue +132 -0
- package/app/components/mdc/MdcTimeline.vue +14 -0
- package/app/components/mdc/MdcTimelineItem.vue +49 -0
- package/app/components/mdc/MdcWarning.vue +16 -0
- package/app/components/mdc/icons.ts +10 -0
- package/app/components/prose/ProseA.vue +27 -0
- package/app/components/prose/ProseBlockquote.vue +14 -0
- package/app/components/prose/ProseCode.vue +14 -0
- package/app/components/prose/ProseEm.vue +14 -0
- package/app/components/prose/ProseH1.vue +18 -0
- package/app/components/prose/ProseH2.vue +24 -0
- package/app/components/prose/ProseH3.vue +24 -0
- package/app/components/prose/ProseH4.vue +14 -0
- package/app/components/prose/ProseHr.vue +12 -0
- package/app/components/prose/ProseImg.vue +120 -0
- package/app/components/prose/ProseLi.vue +14 -0
- package/app/components/prose/ProseOl.vue +14 -0
- package/app/components/prose/ProseP.vue +14 -0
- package/app/components/prose/ProsePre.vue +217 -0
- package/app/components/prose/ProseStrong.vue +14 -0
- package/app/components/prose/ProseTable.vue +16 -0
- package/app/components/prose/ProseTbody.vue +14 -0
- package/app/components/prose/ProseTd.vue +14 -0
- package/app/components/prose/ProseTh.vue +14 -0
- package/app/components/prose/ProseThead.vue +14 -0
- package/app/components/prose/ProseTr.vue +14 -0
- package/app/components/prose/ProseUl.vue +14 -0
- package/app/components/site/ModeToggle.vue +123 -0
- package/app/components/site/SiteBanner.vue +56 -0
- package/app/components/site/SiteFooter.vue +80 -0
- package/app/components/site/SiteHeader.vue +250 -0
- package/app/components/site/SiteInteractionLayer.vue +35 -0
- package/app/components/site/SiteLocaleSwitcher.vue +268 -0
- package/app/components/site/SiteLogoMark.vue +12 -0
- package/app/components/site/SiteSkipLink.vue +14 -0
- package/app/components/ui/accordion/Accordion.vue +15 -0
- package/app/components/ui/accordion/AccordionContent.vue +23 -0
- package/app/components/ui/accordion/AccordionItem.vue +24 -0
- package/app/components/ui/accordion/AccordionTrigger.vue +35 -0
- package/app/components/ui/accordion/index.ts +4 -0
- package/app/components/ui/alert/Alert.vue +21 -0
- package/app/components/ui/alert/AlertDescription.vue +22 -0
- package/app/components/ui/alert/AlertTitle.vue +22 -0
- package/app/components/ui/alert/index.ts +34 -0
- package/app/components/ui/badge/Badge.vue +28 -0
- package/app/components/ui/badge/index.ts +24 -0
- package/app/components/ui/button/Button.vue +31 -0
- package/app/components/ui/button/index.ts +35 -0
- package/app/components/ui/card/Card.vue +16 -0
- package/app/components/ui/card/CardAction.vue +17 -0
- package/app/components/ui/card/CardContent.vue +14 -0
- package/app/components/ui/card/CardDescription.vue +14 -0
- package/app/components/ui/card/CardFooter.vue +14 -0
- package/app/components/ui/card/CardHeader.vue +22 -0
- package/app/components/ui/card/CardTitle.vue +14 -0
- package/app/components/ui/card/card-variants.ts +15 -0
- package/app/components/ui/card/index.ts +8 -0
- package/app/components/ui/checkbox/Checkbox.vue +35 -0
- package/app/components/ui/checkbox/index.ts +1 -0
- package/app/components/ui/collapsible/Collapsible.vue +15 -0
- package/app/components/ui/collapsible/CollapsibleContent.vue +25 -0
- package/app/components/ui/collapsible/CollapsibleTrigger.vue +12 -0
- package/app/components/ui/collapsible/index.ts +3 -0
- package/app/components/ui/dialog/Dialog.vue +15 -0
- package/app/components/ui/dialog/DialogClose.vue +12 -0
- package/app/components/ui/dialog/DialogContent.vue +69 -0
- package/app/components/ui/dialog/DialogDescription.vue +21 -0
- package/app/components/ui/dialog/DialogFooter.vue +15 -0
- package/app/components/ui/dialog/DialogHeader.vue +12 -0
- package/app/components/ui/dialog/DialogOverlay.vue +26 -0
- package/app/components/ui/dialog/DialogPortal.vue +12 -0
- package/app/components/ui/dialog/DialogTitle.vue +21 -0
- package/app/components/ui/dialog/DialogTrigger.vue +12 -0
- package/app/components/ui/dialog/index.ts +10 -0
- package/app/components/ui/dropdown-menu/DropdownMenu.vue +15 -0
- package/app/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +36 -0
- package/app/components/ui/dropdown-menu/DropdownMenuContent.vue +40 -0
- package/app/components/ui/dropdown-menu/DropdownMenuGroup.vue +12 -0
- package/app/components/ui/dropdown-menu/DropdownMenuItem.vue +41 -0
- package/app/components/ui/dropdown-menu/DropdownMenuLabel.vue +25 -0
- package/app/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +15 -0
- package/app/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +37 -0
- package/app/components/ui/dropdown-menu/DropdownMenuSeparator.vue +23 -0
- package/app/components/ui/dropdown-menu/DropdownMenuShortcut.vue +17 -0
- package/app/components/ui/dropdown-menu/DropdownMenuSub.vue +15 -0
- package/app/components/ui/dropdown-menu/DropdownMenuSubContent.vue +29 -0
- package/app/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +31 -0
- package/app/components/ui/dropdown-menu/DropdownMenuTrigger.vue +14 -0
- package/app/components/ui/dropdown-menu/index.ts +16 -0
- package/app/components/ui/input/Input.vue +23 -0
- package/app/components/ui/input/index.ts +1 -0
- package/app/components/ui/kbd/Kbd.vue +23 -0
- package/app/components/ui/kbd/KbdGroup.vue +14 -0
- package/app/components/ui/kbd/index.ts +22 -0
- package/app/components/ui/label/Label.vue +14 -0
- package/app/components/ui/label/index.ts +1 -0
- package/app/components/ui/scroll-area/ScrollArea.vue +39 -0
- package/app/components/ui/scroll-area/ScrollBar.vue +33 -0
- package/app/components/ui/scroll-area/index.ts +2 -0
- package/app/components/ui/separator/Separator.vue +27 -0
- package/app/components/ui/separator/index.ts +1 -0
- package/app/components/ui/sheet/Sheet.vue +15 -0
- package/app/components/ui/sheet/SheetClose.vue +12 -0
- package/app/components/ui/sheet/SheetContent.vue +59 -0
- package/app/components/ui/sheet/SheetDescription.vue +21 -0
- package/app/components/ui/sheet/SheetFooter.vue +12 -0
- package/app/components/ui/sheet/SheetHeader.vue +12 -0
- package/app/components/ui/sheet/SheetOverlay.vue +26 -0
- package/app/components/ui/sheet/SheetTitle.vue +21 -0
- package/app/components/ui/sheet/SheetTrigger.vue +12 -0
- package/app/components/ui/sheet/index.ts +8 -0
- package/app/components/ui/switch/Switch.vue +39 -0
- package/app/components/ui/switch/index.ts +1 -0
- package/app/components/ui/tabs/Tabs.vue +24 -0
- package/app/components/ui/tabs/TabsContent.vue +21 -0
- package/app/components/ui/tabs/TabsList.vue +26 -0
- package/app/components/ui/tabs/TabsTrigger.vue +28 -0
- package/app/components/ui/tabs/index.ts +4 -0
- package/app/components/ui/textarea/Textarea.vue +31 -0
- package/app/components/ui/textarea/index.ts +1 -0
- package/app/composables/site-navigation.utils.ts +80 -0
- package/app/composables/useCanonicalUrl.ts +9 -0
- package/app/composables/useContentRouteAlternates.ts +43 -0
- package/app/composables/useGinkoAnalytics.ts +37 -0
- package/app/composables/useGinkoDocsConfig.ts +6 -0
- package/app/composables/useGinkoOgImage.ts +20 -0
- package/app/composables/useLocalizedPath.ts +12 -0
- package/app/composables/useLocalizedRouteSwitch.ts +31 -0
- package/app/composables/usePageMarkdownActions.ts +79 -0
- package/app/composables/useSchemaJsonLd.ts +25 -0
- package/app/composables/useSiteNavigation.ts +86 -0
- package/app/config/site.utils.ts +36 -0
- package/app/error.vue +125 -0
- package/app/features/docs/components/DocsContributeLinks.vue +65 -0
- package/app/features/docs/components/DocsMobileToc.vue +104 -0
- package/app/features/docs/components/DocsPageContent.vue +188 -0
- package/app/features/docs/components/DocsPageNav.vue +57 -0
- package/app/features/docs/components/DocsSidebar.vue +129 -0
- package/app/features/docs/components/DocsSidebarDropdown.vue +70 -0
- package/app/features/docs/components/DocsSidebarItem.vue +169 -0
- package/app/features/docs/components/DocsSidebarList.vue +35 -0
- package/app/features/docs/components/DocsSidebarTabs.vue +35 -0
- package/app/features/docs/components/DocsToc.vue +60 -0
- package/app/features/docs/composables/useDocsEntryPath.ts +13 -0
- package/app/features/docs/composables/useDocsNavigation.ts +29 -0
- package/app/features/docs/composables/useDocsNavigationData.ts +15 -0
- package/app/features/docs/composables/useScrollspy.ts +67 -0
- package/app/features/docs/docs-navigation.ts +170 -0
- package/app/features/search/command-center.ts +156 -0
- package/app/features/search/components/SiteCommandCenter.vue +323 -0
- package/app/features/search/components/SiteSearchPageHighlight.vue +124 -0
- package/app/features/search/useCommandCenter.ts +300 -0
- package/app/layouts/blog.vue +15 -0
- package/app/layouts/default.vue +12 -0
- package/app/layouts/docs.vue +20 -0
- package/app/lib/errors.ts +84 -0
- package/app/lib/schema-org.ts +38 -0
- package/app/lib/utils.ts +7 -0
- package/app/pages/blog/[slug].vue +211 -0
- package/app/pages/blog/index.vue +106 -0
- package/app/pages/docs/[...slug].vue +10 -0
- package/app/pages/docs/index.vue +23 -0
- package/app/pages/index.vue +140 -0
- package/app/plugins/ssr-width.ts +6 -0
- package/app/utils/content.ts +56 -0
- package/app/utils/index.ts +1 -0
- package/components.ts +9 -0
- package/content.js +175 -0
- package/content.ts +148 -0
- package/i18n/i18n.config.ts +10 -0
- package/i18n/locales.ts +81 -0
- package/i18n/messages/global/banner.ts +8 -0
- package/i18n/messages/global/blog.ts +14 -0
- package/i18n/messages/global/command.ts +44 -0
- package/i18n/messages/global/docs.ts +32 -0
- package/i18n/messages/global/errors.ts +36 -0
- package/i18n/messages/global/feedback.ts +11 -0
- package/i18n/messages/global/index.ts +21 -0
- package/i18n/messages/global/nav.ts +18 -0
- package/i18n/messages/global/site.ts +4 -0
- package/i18n/messages/global/theme.ts +7 -0
- package/i18n/messages/index.ts +20 -0
- package/i18n/messages/localize.ts +35 -0
- package/i18n/messages/source.ts +5 -0
- package/i18n/routes.ts +33 -0
- package/icon-bundle.ts +141 -0
- package/index.d.ts +13 -0
- package/modules/feature-routing.ts +32 -0
- package/nuxt.config.ts +194 -0
- package/package.json +91 -0
- package/runtime/markdown/inline-code-highlight.ts +155 -0
- package/runtime/server/agent-markdown.ts +21 -0
- package/runtime/utils.ts +7 -0
- package/server/mcp/tools/get-page.ts +14 -0
- package/server/mcp/tools/list-pages.ts +10 -0
- package/server/plugins/agent-markdown.ts +6 -0
- package/shared/route-slugs.ts +5 -0
- package/shared/types/app-config.ts +100 -0
- package/tags.ts +204 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { SeparatorProps } from "reka-ui";
|
|
3
|
+
import type { HTMLAttributes } from "vue";
|
|
4
|
+
import { reactiveOmit } from "@vueuse/core";
|
|
5
|
+
import { Separator } from "reka-ui";
|
|
6
|
+
import { cn } from "#ginko-docs/lib/utils";
|
|
7
|
+
|
|
8
|
+
const props = withDefaults(defineProps<SeparatorProps & { class?: HTMLAttributes["class"] }>(), {
|
|
9
|
+
orientation: "horizontal",
|
|
10
|
+
decorative: true,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const delegatedProps = reactiveOmit(props, "class");
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<Separator
|
|
18
|
+
data-slot="separator"
|
|
19
|
+
v-bind="delegatedProps"
|
|
20
|
+
:class="
|
|
21
|
+
cn(
|
|
22
|
+
'shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px',
|
|
23
|
+
props.class,
|
|
24
|
+
)
|
|
25
|
+
"
|
|
26
|
+
/>
|
|
27
|
+
</template>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Separator } from "./Separator.vue";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { DialogRootEmits, DialogRootProps } from "reka-ui";
|
|
3
|
+
import { DialogRoot, useForwardPropsEmits } from "reka-ui";
|
|
4
|
+
|
|
5
|
+
const props = defineProps<DialogRootProps>();
|
|
6
|
+
const emits = defineEmits<DialogRootEmits>();
|
|
7
|
+
|
|
8
|
+
const forwarded = useForwardPropsEmits(props, emits);
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<template>
|
|
12
|
+
<DialogRoot v-slot="slotProps" data-slot="sheet" v-bind="forwarded">
|
|
13
|
+
<slot v-bind="slotProps" />
|
|
14
|
+
</DialogRoot>
|
|
15
|
+
</template>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { DialogCloseProps } from "reka-ui";
|
|
3
|
+
import { DialogClose } from "reka-ui";
|
|
4
|
+
|
|
5
|
+
const props = defineProps<DialogCloseProps>();
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<template>
|
|
9
|
+
<DialogClose data-slot="sheet-close" v-bind="props">
|
|
10
|
+
<slot />
|
|
11
|
+
</DialogClose>
|
|
12
|
+
</template>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { DialogContentEmits, DialogContentProps } from "reka-ui";
|
|
3
|
+
import type { HTMLAttributes } from "vue";
|
|
4
|
+
import { reactiveOmit } from "@vueuse/core";
|
|
5
|
+
import { DialogClose, DialogContent, DialogPortal, useForwardPropsEmits } from "reka-ui";
|
|
6
|
+
import { cn } from "#ginko-docs/lib/utils";
|
|
7
|
+
import SheetOverlay from "./SheetOverlay.vue";
|
|
8
|
+
|
|
9
|
+
interface SheetContentProps extends DialogContentProps {
|
|
10
|
+
class?: HTMLAttributes["class"];
|
|
11
|
+
side?: "top" | "right" | "bottom" | "left";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
defineOptions({
|
|
15
|
+
inheritAttrs: false,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const props = withDefaults(defineProps<SheetContentProps>(), {
|
|
19
|
+
side: "right",
|
|
20
|
+
});
|
|
21
|
+
const emits = defineEmits<DialogContentEmits>();
|
|
22
|
+
|
|
23
|
+
const delegatedProps = reactiveOmit(props, "class", "side");
|
|
24
|
+
|
|
25
|
+
const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<template>
|
|
29
|
+
<DialogPortal>
|
|
30
|
+
<SheetOverlay />
|
|
31
|
+
<DialogContent
|
|
32
|
+
data-slot="sheet-content"
|
|
33
|
+
:class="
|
|
34
|
+
cn(
|
|
35
|
+
'fixed z-[70] flex flex-col gap-4 bg-background shadow-lg transition ease-in-out data-[state=closed]:animate-out data-[state=closed]:duration-200 data-[state=open]:animate-in data-[state=open]:duration-200',
|
|
36
|
+
side === 'right' &&
|
|
37
|
+
'inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm',
|
|
38
|
+
side === 'left' &&
|
|
39
|
+
'inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm',
|
|
40
|
+
side === 'top' &&
|
|
41
|
+
'inset-x-0 top-0 h-auto border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top',
|
|
42
|
+
side === 'bottom' &&
|
|
43
|
+
'inset-x-0 bottom-0 h-auto border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom',
|
|
44
|
+
props.class,
|
|
45
|
+
)
|
|
46
|
+
"
|
|
47
|
+
v-bind="{ ...$attrs, ...forwarded }"
|
|
48
|
+
>
|
|
49
|
+
<slot />
|
|
50
|
+
|
|
51
|
+
<DialogClose
|
|
52
|
+
class="absolute top-4 right-4 inline-flex size-8 items-center justify-center rounded-md text-muted-foreground transition-colors outline-none hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none"
|
|
53
|
+
>
|
|
54
|
+
<Icon name="lucide:x" class="size-4" aria-hidden="true" />
|
|
55
|
+
<span class="sr-only">Close</span>
|
|
56
|
+
</DialogClose>
|
|
57
|
+
</DialogContent>
|
|
58
|
+
</DialogPortal>
|
|
59
|
+
</template>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { DialogDescriptionProps } from "reka-ui";
|
|
3
|
+
import type { HTMLAttributes } from "vue";
|
|
4
|
+
import { reactiveOmit } from "@vueuse/core";
|
|
5
|
+
import { DialogDescription } from "reka-ui";
|
|
6
|
+
import { cn } from "#ginko-docs/lib/utils";
|
|
7
|
+
|
|
8
|
+
const props = defineProps<DialogDescriptionProps & { class?: HTMLAttributes["class"] }>();
|
|
9
|
+
|
|
10
|
+
const delegatedProps = reactiveOmit(props, "class");
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<DialogDescription
|
|
15
|
+
data-slot="sheet-description"
|
|
16
|
+
:class="cn('text-sm text-muted-foreground', props.class)"
|
|
17
|
+
v-bind="delegatedProps"
|
|
18
|
+
>
|
|
19
|
+
<slot />
|
|
20
|
+
</DialogDescription>
|
|
21
|
+
</template>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from "vue";
|
|
3
|
+
import { cn } from "#ginko-docs/lib/utils";
|
|
4
|
+
|
|
5
|
+
const props = defineProps<{ class?: HTMLAttributes["class"] }>();
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<template>
|
|
9
|
+
<div data-slot="sheet-footer" :class="cn('mt-auto flex flex-col gap-2 p-4', props.class)">
|
|
10
|
+
<slot />
|
|
11
|
+
</div>
|
|
12
|
+
</template>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from "vue";
|
|
3
|
+
import { cn } from "#ginko-docs/lib/utils";
|
|
4
|
+
|
|
5
|
+
const props = defineProps<{ class?: HTMLAttributes["class"] }>();
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<template>
|
|
9
|
+
<div data-slot="sheet-header" :class="cn('flex flex-col gap-1.5 p-4', props.class)">
|
|
10
|
+
<slot />
|
|
11
|
+
</div>
|
|
12
|
+
</template>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { DialogOverlayProps } from "reka-ui";
|
|
3
|
+
import type { HTMLAttributes } from "vue";
|
|
4
|
+
import { reactiveOmit } from "@vueuse/core";
|
|
5
|
+
import { DialogOverlay } from "reka-ui";
|
|
6
|
+
import { cn } from "#ginko-docs/lib/utils";
|
|
7
|
+
|
|
8
|
+
const props = defineProps<DialogOverlayProps & { class?: HTMLAttributes["class"] }>();
|
|
9
|
+
|
|
10
|
+
const delegatedProps = reactiveOmit(props, "class");
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<DialogOverlay
|
|
15
|
+
data-slot="sheet-overlay"
|
|
16
|
+
:class="
|
|
17
|
+
cn(
|
|
18
|
+
'fixed inset-0 z-[60] bg-overlay data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0',
|
|
19
|
+
props.class,
|
|
20
|
+
)
|
|
21
|
+
"
|
|
22
|
+
v-bind="delegatedProps"
|
|
23
|
+
>
|
|
24
|
+
<slot />
|
|
25
|
+
</DialogOverlay>
|
|
26
|
+
</template>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { DialogTitleProps } from "reka-ui";
|
|
3
|
+
import type { HTMLAttributes } from "vue";
|
|
4
|
+
import { reactiveOmit } from "@vueuse/core";
|
|
5
|
+
import { DialogTitle } from "reka-ui";
|
|
6
|
+
import { cn } from "#ginko-docs/lib/utils";
|
|
7
|
+
|
|
8
|
+
const props = defineProps<DialogTitleProps & { class?: HTMLAttributes["class"] }>();
|
|
9
|
+
|
|
10
|
+
const delegatedProps = reactiveOmit(props, "class");
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<DialogTitle
|
|
15
|
+
data-slot="sheet-title"
|
|
16
|
+
:class="cn('font-semibold text-foreground', props.class)"
|
|
17
|
+
v-bind="delegatedProps"
|
|
18
|
+
>
|
|
19
|
+
<slot />
|
|
20
|
+
</DialogTitle>
|
|
21
|
+
</template>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { DialogTriggerProps } from "reka-ui";
|
|
3
|
+
import { DialogTrigger } from "reka-ui";
|
|
4
|
+
|
|
5
|
+
const props = defineProps<DialogTriggerProps>();
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<template>
|
|
9
|
+
<DialogTrigger data-slot="sheet-trigger" v-bind="props">
|
|
10
|
+
<slot />
|
|
11
|
+
</DialogTrigger>
|
|
12
|
+
</template>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { default as Sheet } from "./Sheet.vue";
|
|
2
|
+
export { default as SheetClose } from "./SheetClose.vue";
|
|
3
|
+
export { default as SheetContent } from "./SheetContent.vue";
|
|
4
|
+
export { default as SheetDescription } from "./SheetDescription.vue";
|
|
5
|
+
export { default as SheetFooter } from "./SheetFooter.vue";
|
|
6
|
+
export { default as SheetHeader } from "./SheetHeader.vue";
|
|
7
|
+
export { default as SheetTitle } from "./SheetTitle.vue";
|
|
8
|
+
export { default as SheetTrigger } from "./SheetTrigger.vue";
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { SwitchRootEmits, SwitchRootProps } from "reka-ui";
|
|
3
|
+
import type { HTMLAttributes } from "vue";
|
|
4
|
+
import { reactiveOmit } from "@vueuse/core";
|
|
5
|
+
import { SwitchRoot, SwitchThumb, useForwardPropsEmits } from "reka-ui";
|
|
6
|
+
import { cn } from "#ginko-docs/lib/utils";
|
|
7
|
+
|
|
8
|
+
interface Props extends SwitchRootProps {
|
|
9
|
+
class?: HTMLAttributes["class"];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const props = defineProps<Props>();
|
|
13
|
+
const emits = defineEmits<SwitchRootEmits>();
|
|
14
|
+
|
|
15
|
+
const delegatedProps = reactiveOmit(props, "class");
|
|
16
|
+
const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<template>
|
|
20
|
+
<SwitchRoot
|
|
21
|
+
data-slot="switch"
|
|
22
|
+
v-bind="forwarded"
|
|
23
|
+
:class="
|
|
24
|
+
cn(
|
|
25
|
+
'peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-xs transition-colors focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input',
|
|
26
|
+
props.class,
|
|
27
|
+
)
|
|
28
|
+
"
|
|
29
|
+
>
|
|
30
|
+
<SwitchThumb
|
|
31
|
+
data-slot="switch-thumb"
|
|
32
|
+
:class="
|
|
33
|
+
cn(
|
|
34
|
+
'pointer-events-none block size-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0',
|
|
35
|
+
)
|
|
36
|
+
"
|
|
37
|
+
/>
|
|
38
|
+
</SwitchRoot>
|
|
39
|
+
</template>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Switch } from "./Switch.vue";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { TabsRootEmits, TabsRootProps } from "reka-ui";
|
|
3
|
+
import type { HTMLAttributes } from "vue";
|
|
4
|
+
import { reactiveOmit } from "@vueuse/core";
|
|
5
|
+
import { TabsRoot, useForwardPropsEmits } from "reka-ui";
|
|
6
|
+
import { cn } from "#ginko-docs/lib/utils";
|
|
7
|
+
|
|
8
|
+
const props = defineProps<TabsRootProps & { class?: HTMLAttributes["class"] }>();
|
|
9
|
+
const emits = defineEmits<TabsRootEmits>();
|
|
10
|
+
|
|
11
|
+
const delegatedProps = reactiveOmit(props, "class");
|
|
12
|
+
const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<TabsRoot
|
|
17
|
+
v-slot="slotProps"
|
|
18
|
+
data-slot="tabs"
|
|
19
|
+
v-bind="forwarded"
|
|
20
|
+
:class="cn('flex flex-col gap-2', props.class)"
|
|
21
|
+
>
|
|
22
|
+
<slot v-bind="slotProps" />
|
|
23
|
+
</TabsRoot>
|
|
24
|
+
</template>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { TabsContentProps } from "reka-ui";
|
|
3
|
+
import type { HTMLAttributes } from "vue";
|
|
4
|
+
import { reactiveOmit } from "@vueuse/core";
|
|
5
|
+
import { TabsContent } from "reka-ui";
|
|
6
|
+
import { cn } from "#ginko-docs/lib/utils";
|
|
7
|
+
|
|
8
|
+
const props = defineProps<TabsContentProps & { class?: HTMLAttributes["class"] }>();
|
|
9
|
+
|
|
10
|
+
const delegatedProps = reactiveOmit(props, "class");
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<TabsContent
|
|
15
|
+
data-slot="tabs-content"
|
|
16
|
+
:class="cn('flex-1 outline-none', props.class)"
|
|
17
|
+
v-bind="delegatedProps"
|
|
18
|
+
>
|
|
19
|
+
<slot />
|
|
20
|
+
</TabsContent>
|
|
21
|
+
</template>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { TabsListProps } from "reka-ui";
|
|
3
|
+
import type { HTMLAttributes } from "vue";
|
|
4
|
+
import { reactiveOmit } from "@vueuse/core";
|
|
5
|
+
import { TabsList } from "reka-ui";
|
|
6
|
+
import { cn } from "#ginko-docs/lib/utils";
|
|
7
|
+
|
|
8
|
+
const props = defineProps<TabsListProps & { class?: HTMLAttributes["class"] }>();
|
|
9
|
+
|
|
10
|
+
const delegatedProps = reactiveOmit(props, "class");
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<TabsList
|
|
15
|
+
data-slot="tabs-list"
|
|
16
|
+
v-bind="delegatedProps"
|
|
17
|
+
:class="
|
|
18
|
+
cn(
|
|
19
|
+
'inline-flex h-9 w-fit items-center justify-center rounded-lg bg-muted p-[3px] text-muted-foreground',
|
|
20
|
+
props.class,
|
|
21
|
+
)
|
|
22
|
+
"
|
|
23
|
+
>
|
|
24
|
+
<slot />
|
|
25
|
+
</TabsList>
|
|
26
|
+
</template>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { TabsTriggerProps } from "reka-ui";
|
|
3
|
+
import type { HTMLAttributes } from "vue";
|
|
4
|
+
import { reactiveOmit } from "@vueuse/core";
|
|
5
|
+
import { TabsTrigger, useForwardProps } from "reka-ui";
|
|
6
|
+
import { cn } from "#ginko-docs/lib/utils";
|
|
7
|
+
|
|
8
|
+
const props = defineProps<TabsTriggerProps & { class?: HTMLAttributes["class"] }>();
|
|
9
|
+
|
|
10
|
+
const delegatedProps = reactiveOmit(props, "class");
|
|
11
|
+
|
|
12
|
+
const forwardedProps = useForwardProps(delegatedProps);
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<TabsTrigger
|
|
17
|
+
data-slot="tabs-trigger"
|
|
18
|
+
:class="
|
|
19
|
+
cn(
|
|
20
|
+
'inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap text-foreground transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:shadow-sm dark:text-muted-foreground dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 dark:data-[state=active]:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4',
|
|
21
|
+
props.class,
|
|
22
|
+
)
|
|
23
|
+
"
|
|
24
|
+
v-bind="forwardedProps"
|
|
25
|
+
>
|
|
26
|
+
<slot />
|
|
27
|
+
</TabsTrigger>
|
|
28
|
+
</template>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from "vue";
|
|
3
|
+
import { ref } from "vue";
|
|
4
|
+
import { cn } from "#ginko-docs/lib/utils";
|
|
5
|
+
import { h } from "vue";
|
|
6
|
+
|
|
7
|
+
const model = defineModel<string>();
|
|
8
|
+
const textarea = ref<HTMLTextAreaElement | null>(null);
|
|
9
|
+
|
|
10
|
+
const props = defineProps<{
|
|
11
|
+
class?: HTMLAttributes["class"];
|
|
12
|
+
}>();
|
|
13
|
+
|
|
14
|
+
defineExpose({
|
|
15
|
+
focus: () => textarea.value?.focus(),
|
|
16
|
+
});
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<template>
|
|
20
|
+
<textarea
|
|
21
|
+
ref="textarea"
|
|
22
|
+
v-model="model"
|
|
23
|
+
data-slot="textarea"
|
|
24
|
+
:class="
|
|
25
|
+
cn(
|
|
26
|
+
'min-h-36 w-full resize-y rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground shadow-xs transition-colors outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40',
|
|
27
|
+
props.class,
|
|
28
|
+
)
|
|
29
|
+
"
|
|
30
|
+
/>
|
|
31
|
+
</template>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Textarea } from "./Textarea.vue";
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { GinkoDocsAppConfig, GinkoDocsLink } from "../../shared/types/app-config";
|
|
2
|
+
import { getLocalizedSiteText } from "../config/site.utils";
|
|
3
|
+
|
|
4
|
+
export interface NavItem {
|
|
5
|
+
label: string;
|
|
6
|
+
href: string;
|
|
7
|
+
external?: boolean;
|
|
8
|
+
icon?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface MainNavContext {
|
|
12
|
+
locale: string;
|
|
13
|
+
docsPath: string;
|
|
14
|
+
blogPath: string;
|
|
15
|
+
blogExists: boolean;
|
|
16
|
+
docsLabel: string;
|
|
17
|
+
blogLabel: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const isExternalHref = (href: string) => /^https?:\/\//.test(href);
|
|
21
|
+
|
|
22
|
+
export function resolveMainNav(
|
|
23
|
+
links: GinkoDocsAppConfig["nav"]["links"],
|
|
24
|
+
ctx: MainNavContext,
|
|
25
|
+
): NavItem[] {
|
|
26
|
+
if (Array.isArray(links)) {
|
|
27
|
+
return links.map((link: GinkoDocsLink) => {
|
|
28
|
+
const href = getLocalizedSiteText(link.to, ctx.locale);
|
|
29
|
+
return {
|
|
30
|
+
label: getLocalizedSiteText(link.label, ctx.locale),
|
|
31
|
+
href,
|
|
32
|
+
external: isExternalHref(href),
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const items: NavItem[] = [{ label: ctx.docsLabel, href: ctx.docsPath }];
|
|
38
|
+
if (ctx.blogExists) {
|
|
39
|
+
items.push({ label: ctx.blogLabel, href: ctx.blogPath });
|
|
40
|
+
}
|
|
41
|
+
return items;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface BannerContext {
|
|
45
|
+
locale: string;
|
|
46
|
+
blogPath: string;
|
|
47
|
+
blogExists: boolean;
|
|
48
|
+
defaultText: string;
|
|
49
|
+
defaultLinkLabel: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface ResolvedBanner {
|
|
53
|
+
show: boolean;
|
|
54
|
+
text: string;
|
|
55
|
+
linkLabel: string;
|
|
56
|
+
linkHref?: string;
|
|
57
|
+
storageKey: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function bannerStorageKey(id: string) {
|
|
61
|
+
return `ginko-docs:banner:${id}`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function resolveBanner(
|
|
65
|
+
config: GinkoDocsAppConfig["banner"],
|
|
66
|
+
ctx: BannerContext,
|
|
67
|
+
): ResolvedBanner {
|
|
68
|
+
const show = config.enabled === "auto" ? ctx.blogExists : config.enabled;
|
|
69
|
+
const text = config.text ? getLocalizedSiteText(config.text, ctx.locale) : ctx.defaultText;
|
|
70
|
+
const linkHref = config.link?.to
|
|
71
|
+
? getLocalizedSiteText(config.link.to, ctx.locale)
|
|
72
|
+
: ctx.blogExists
|
|
73
|
+
? ctx.blogPath
|
|
74
|
+
: undefined;
|
|
75
|
+
const linkLabel = config.link?.label
|
|
76
|
+
? getLocalizedSiteText(config.link.label, ctx.locale)
|
|
77
|
+
: ctx.defaultLinkLabel;
|
|
78
|
+
|
|
79
|
+
return { show, text, linkLabel, linkHref, storageKey: bannerStorageKey(config.id) };
|
|
80
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { computed } from "vue";
|
|
2
|
+
import { useAppConfig, useRoute } from "#imports";
|
|
3
|
+
|
|
4
|
+
export function useCanonicalUrl() {
|
|
5
|
+
const route = useRoute();
|
|
6
|
+
const config = useAppConfig().ginkoDocs;
|
|
7
|
+
|
|
8
|
+
return computed(() => new URL(route.path, config.site.url).toString());
|
|
9
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ContentAlternate } from "@lupinum/ginko-content/client";
|
|
2
|
+
import { computed, watch, type Ref } from "vue";
|
|
3
|
+
import { useRoute, useState } from "#imports";
|
|
4
|
+
|
|
5
|
+
type ContentRouteAlternatesState = {
|
|
6
|
+
sourcePath: string;
|
|
7
|
+
alternates: ContentAlternate[];
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export function useContentRouteAlternates() {
|
|
11
|
+
const route = useRoute();
|
|
12
|
+
const state = useState<ContentRouteAlternatesState>("content-route-alternates", () => ({
|
|
13
|
+
sourcePath: "",
|
|
14
|
+
alternates: [],
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
const current = computed(() =>
|
|
18
|
+
state.value.sourcePath === route.path ? state.value.alternates : [],
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
return current;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function syncContentRouteAlternates(
|
|
25
|
+
page: Ref<{ route?: { alternates?: ContentAlternate[] } } | null | undefined>,
|
|
26
|
+
) {
|
|
27
|
+
const route = useRoute();
|
|
28
|
+
const state = useState<ContentRouteAlternatesState>("content-route-alternates", () => ({
|
|
29
|
+
sourcePath: "",
|
|
30
|
+
alternates: [],
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
watch(
|
|
34
|
+
page,
|
|
35
|
+
(document) => {
|
|
36
|
+
state.value = {
|
|
37
|
+
sourcePath: route.path,
|
|
38
|
+
alternates: document?.route?.alternates ?? [],
|
|
39
|
+
};
|
|
40
|
+
},
|
|
41
|
+
{ immediate: true },
|
|
42
|
+
);
|
|
43
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useScriptPlausibleAnalytics } from "#imports";
|
|
2
|
+
import { useGinkoDocsConfig } from "./useGinkoDocsConfig";
|
|
3
|
+
|
|
4
|
+
export type AnalyticsProps = Record<string, string>;
|
|
5
|
+
|
|
6
|
+
export interface GinkoAnalytics {
|
|
7
|
+
enabled: boolean;
|
|
8
|
+
track: (event: string, props?: AnalyticsProps) => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Plausible analytics via the Nuxt Scripts registry script. Fully disabled
|
|
13
|
+
* unless `ginkoDocs.analytics.plausible.domain` is configured — without a
|
|
14
|
+
* domain nothing loads and `track()` is a no-op, so callers never guard.
|
|
15
|
+
*/
|
|
16
|
+
export function useGinkoAnalytics(): GinkoAnalytics {
|
|
17
|
+
const config = useGinkoDocsConfig();
|
|
18
|
+
const plausible = config.analytics?.plausible;
|
|
19
|
+
|
|
20
|
+
if (!plausible?.domain) {
|
|
21
|
+
return { enabled: false, track: () => {} };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const script = useScriptPlausibleAnalytics({
|
|
25
|
+
domain: plausible.domain,
|
|
26
|
+
extension: plausible.extensions ?? ["outbound-links"],
|
|
27
|
+
...(plausible.scriptSrc ? { scriptInput: { src: plausible.scriptSrc } } : {}),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
enabled: true,
|
|
32
|
+
track: (event, props) => {
|
|
33
|
+
if (!import.meta.client) return;
|
|
34
|
+
script.proxy.plausible(event, props ? { props } : undefined);
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { defineOgImage } from "#imports";
|
|
2
|
+
import { useGinkoDocsConfig } from "./useGinkoDocsConfig";
|
|
3
|
+
|
|
4
|
+
export interface GinkoOgImageProps {
|
|
5
|
+
title?: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
locale?: string;
|
|
8
|
+
[key: string]: unknown;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Declares the page's social image using the configured OgImage template.
|
|
13
|
+
* No-op when the consumer sets `ginkoDocs.ogImage.enabled: false`.
|
|
14
|
+
*/
|
|
15
|
+
export function useGinkoOgImage(props?: GinkoOgImageProps) {
|
|
16
|
+
const config = useGinkoDocsConfig();
|
|
17
|
+
if (config.ogImage?.enabled === false) return;
|
|
18
|
+
const component = config.ogImage?.component ?? "GinkoDocs";
|
|
19
|
+
defineOgImage(component as never, props as never);
|
|
20
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useI18n } from "#imports";
|
|
2
|
+
import { isLocaleCode, localizedPath } from "../../i18n/locales";
|
|
3
|
+
import type { LocalizedRouteKey } from "../../i18n/routes";
|
|
4
|
+
|
|
5
|
+
export function useLocalizedPath() {
|
|
6
|
+
const { locale, t } = useI18n();
|
|
7
|
+
|
|
8
|
+
return (key: LocalizedRouteKey) => {
|
|
9
|
+
const path = t(`routes.${key}`);
|
|
10
|
+
return isLocaleCode(locale.value) ? localizedPath(locale.value, path) : path;
|
|
11
|
+
};
|
|
12
|
+
}
|