docs-please 0.2.0-beta.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 +63 -0
- package/app/app.config.ts +13 -0
- package/app/app.vue +17 -0
- package/app/assets/css/main.css +367 -0
- package/app/components/Icons.ts +163 -0
- package/app/components/app/AppFooter.vue +24 -0
- package/app/components/app/AppHeader.vue +58 -0
- package/app/components/content/BrowserFrame.vue +21 -0
- package/app/components/content/Callout.vue +80 -0
- package/app/components/content/Caution.vue +25 -0
- package/app/components/content/CodeBlockCommand.vue +92 -0
- package/app/components/content/CodeCollapsibleWrapper.vue +50 -0
- package/app/components/content/CodeTabs.vue +14 -0
- package/app/components/content/ComponentPreview.vue +71 -0
- package/app/components/content/ComponentsList.vue +24 -0
- package/app/components/content/CopyButton.vue +39 -0
- package/app/components/content/FeatureCard.vue +25 -0
- package/app/components/content/LinkedCard.vue +19 -0
- package/app/components/content/Note.vue +25 -0
- package/app/components/content/ProseA.vue +18 -0
- package/app/components/content/ProseBlockQuote.vue +8 -0
- package/app/components/content/ProseCode.vue +8 -0
- package/app/components/content/ProseH1.vue +7 -0
- package/app/components/content/ProseH2.vue +8 -0
- package/app/components/content/ProseH3.vue +9 -0
- package/app/components/content/ProseH4.vue +9 -0
- package/app/components/content/ProseH5.vue +7 -0
- package/app/components/content/ProseH6.vue +7 -0
- package/app/components/content/ProseHr.vue +6 -0
- package/app/components/content/ProseIcon.vue +32 -0
- package/app/components/content/ProseImg.vue +6 -0
- package/app/components/content/ProseLi.vue +8 -0
- package/app/components/content/ProseOl.vue +8 -0
- package/app/components/content/ProseP.vue +14 -0
- package/app/components/content/ProsePre.vue +80 -0
- package/app/components/content/ProseStrong.vue +8 -0
- package/app/components/content/ProseTable.vue +26 -0
- package/app/components/content/ProseTd.vue +8 -0
- package/app/components/content/ProseTh.vue +8 -0
- package/app/components/content/ProseTr.vue +8 -0
- package/app/components/content/ProseUl.vue +8 -0
- package/app/components/content/Step.vue +18 -0
- package/app/components/content/Steps.vue +18 -0
- package/app/components/content/Tabs.vue +129 -0
- package/app/components/content/TabsItem.vue +26 -0
- package/app/components/content/Tip.vue +25 -0
- package/app/components/content/UButton.vue +34 -0
- package/app/components/content/UColorModeImage.vue +48 -0
- package/app/components/content/UPageCard.vue +83 -0
- package/app/components/content/UPageGrid.vue +18 -0
- package/app/components/content/UPageHero.vue +92 -0
- package/app/components/content/UPageSection.vue +90 -0
- package/app/components/content/Warning.vue +25 -0
- package/app/components/docs/DocsPageHeader.vue +20 -0
- package/app/components/docs/DocsPageNav.vue +31 -0
- package/app/components/docs/DocsSidebar.vue +97 -0
- package/app/components/docs/DocsTableOfContents.vue +64 -0
- package/app/components/ui/accordion/Accordion.vue +22 -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 +37 -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 +17 -0
- package/app/components/ui/alert/AlertTitle.vue +17 -0
- package/app/components/ui/alert/index.ts +28 -0
- package/app/components/ui/button/Button.vue +29 -0
- package/app/components/ui/button/index.ts +38 -0
- package/app/components/ui/card/Card.vue +22 -0
- package/app/components/ui/card/CardAction.vue +17 -0
- package/app/components/ui/card/CardContent.vue +17 -0
- package/app/components/ui/card/CardDescription.vue +17 -0
- package/app/components/ui/card/CardFooter.vue +17 -0
- package/app/components/ui/card/CardHeader.vue +17 -0
- package/app/components/ui/card/CardTitle.vue +17 -0
- package/app/components/ui/card/index.ts +7 -0
- package/app/components/ui/collapsible/Collapsible.vue +19 -0
- package/app/components/ui/collapsible/CollapsibleContent.vue +15 -0
- package/app/components/ui/collapsible/CollapsibleTrigger.vue +15 -0
- package/app/components/ui/collapsible/index.ts +3 -0
- package/app/components/ui/separator/Separator.vue +29 -0
- package/app/components/ui/separator/index.ts +1 -0
- package/app/components/ui/switch/Switch.vue +35 -0
- package/app/components/ui/switch/index.ts +1 -0
- package/app/components/ui/table/Table.vue +16 -0
- package/app/components/ui/table/TableBody.vue +14 -0
- package/app/components/ui/table/TableCaption.vue +14 -0
- package/app/components/ui/table/TableCell.vue +21 -0
- package/app/components/ui/table/TableEmpty.vue +34 -0
- package/app/components/ui/table/TableFooter.vue +14 -0
- package/app/components/ui/table/TableHead.vue +14 -0
- package/app/components/ui/table/TableHeader.vue +14 -0
- package/app/components/ui/table/TableRow.vue +14 -0
- package/app/components/ui/table/index.ts +9 -0
- package/app/components/ui/tabs/Tabs.vue +15 -0
- package/app/components/ui/tabs/TabsContent.vue +20 -0
- package/app/components/ui/tabs/TabsList.vue +23 -0
- package/app/components/ui/tabs/TabsTrigger.vue +27 -0
- package/app/components/ui/tabs/index.ts +4 -0
- package/app/components/ui/tooltip/Tooltip.vue +19 -0
- package/app/components/ui/tooltip/TooltipContent.vue +34 -0
- package/app/components/ui/tooltip/TooltipProvider.vue +14 -0
- package/app/components/ui/tooltip/TooltipTrigger.vue +15 -0
- package/app/components/ui/tooltip/index.ts +4 -0
- package/app/composables/useConfig.ts +24 -0
- package/app/composables/useNavigation.ts +43 -0
- package/app/layouts/default.vue +12 -0
- package/app/layouts/docs.vue +27 -0
- package/app/lib/utils.ts +7 -0
- package/app/pages/[...slug].vue +97 -0
- package/app/pages/index.vue +29 -0
- package/app/plugins/ssr-width.ts +5 -0
- package/content.config.ts +36 -0
- package/i18n/locales/en.json +14 -0
- package/modules/config.ts +38 -0
- package/modules/css.ts +38 -0
- package/modules/shadcn.ts +116 -0
- package/nuxt.config.ts +125 -0
- package/nuxt.schema.ts +68 -0
- package/package.json +81 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Component, HTMLAttributes } from 'vue'
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<script setup lang="ts">
|
|
6
|
+
import { useForwardProps } from 'reka-ui'
|
|
7
|
+
import { reactivePick } from '@vueuse/core'
|
|
8
|
+
|
|
9
|
+
export interface ProseIconProps {
|
|
10
|
+
name: string | Component
|
|
11
|
+
mode?: 'svg' | 'css'
|
|
12
|
+
size?: string | number
|
|
13
|
+
class?: HTMLAttributes['class']
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const props = defineProps<ProseIconProps>()
|
|
17
|
+
|
|
18
|
+
const iconProps = useForwardProps(reactivePick(props, 'name', 'mode', 'size'))
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<template>
|
|
22
|
+
<Icon
|
|
23
|
+
v-if="typeof name === 'string'"
|
|
24
|
+
v-bind="iconProps"
|
|
25
|
+
:class="props.class"
|
|
26
|
+
/>
|
|
27
|
+
<component
|
|
28
|
+
:is="name"
|
|
29
|
+
v-else
|
|
30
|
+
:class="props.class"
|
|
31
|
+
/>
|
|
32
|
+
</template>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'vue'
|
|
3
|
+
import { cn } from '~/lib/utils'
|
|
4
|
+
|
|
5
|
+
const props = defineProps<{
|
|
6
|
+
class?: HTMLAttributes['class']
|
|
7
|
+
}>()
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<p :class="cn('leading-relaxed [&:not(:first-child)]:mt-6', props.class)">
|
|
12
|
+
<slot />
|
|
13
|
+
</p>
|
|
14
|
+
</template>
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'vue'
|
|
3
|
+
import { getIconForLanguageExtension } from '~/components/Icons'
|
|
4
|
+
import { cn } from '~/lib/utils'
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{
|
|
7
|
+
code?: string
|
|
8
|
+
language?: string
|
|
9
|
+
filename?: string
|
|
10
|
+
highlights?: number[]
|
|
11
|
+
meta?: string
|
|
12
|
+
class?: HTMLAttributes['class']
|
|
13
|
+
unwrap?: boolean
|
|
14
|
+
}>()
|
|
15
|
+
|
|
16
|
+
const npmBlock = ['npm install', 'npm create', 'npm run', 'npx']
|
|
17
|
+
const isNpmCommand = computed(() => props.code && npmBlock.some(s => props.code!.startsWith(s)))
|
|
18
|
+
const isShowingLineNumber = computed(() => props.meta?.includes('showLineNumbers'))
|
|
19
|
+
|
|
20
|
+
const title = computed(() => props.filename || props.meta?.match(/title="([^"]+)"/)?.[1])
|
|
21
|
+
|
|
22
|
+
const lang = computed(() => props.language?.replace('language-', '') || '')
|
|
23
|
+
const IconExtension = computed(() => {
|
|
24
|
+
return getIconForLanguageExtension(lang.value)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
const codeAttributes = computed(() => isShowingLineNumber.value
|
|
28
|
+
? ({
|
|
29
|
+
'data-line-numbers': '',
|
|
30
|
+
'data-line-numbers-max-digits': 2,
|
|
31
|
+
})
|
|
32
|
+
: undefined)
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<template>
|
|
36
|
+
<pre
|
|
37
|
+
v-if="unwrap"
|
|
38
|
+
:class="cn('no-scrollbar min-w-0 overflow-x-auto px-4 py-3.5 outline-none has-[[data-highlighted-line]]:px-0 has-[[data-line-numbers]]:px-0 has-[[data-slot=tabs]]:p-0 !bg-transparent', props.class)"
|
|
39
|
+
:data-language="lang"
|
|
40
|
+
><code v-bind="codeAttributes"><slot /></code></pre>
|
|
41
|
+
<figure
|
|
42
|
+
v-else
|
|
43
|
+
data-pretty-code-figure
|
|
44
|
+
>
|
|
45
|
+
<pre
|
|
46
|
+
v-if="isNpmCommand"
|
|
47
|
+
:class="cn('no-scrollbar min-w-0 overflow-x-auto px-4 py-3.5 outline-none has-[[data-highlighted-line]]:px-0 has-[[data-line-numbers]]:px-0 has-[[data-slot=tabs]]:p-0', props.class)"
|
|
48
|
+
><CodeBlockCommand :code="code!" /></pre>
|
|
49
|
+
|
|
50
|
+
<template v-else-if="title">
|
|
51
|
+
<figcaption
|
|
52
|
+
data-pretty-code-title
|
|
53
|
+
:data-language="lang"
|
|
54
|
+
class="text-code-foreground [&_svg]:text-code-foreground flex items-center gap-2 [&_svg]:size-4 [&_svg]:opacity-70"
|
|
55
|
+
>
|
|
56
|
+
<component
|
|
57
|
+
:is="IconExtension"
|
|
58
|
+
v-if="IconExtension"
|
|
59
|
+
/>
|
|
60
|
+
{{ title }}
|
|
61
|
+
</figcaption>
|
|
62
|
+
<pre
|
|
63
|
+
:data-language="lang"
|
|
64
|
+
:class="cn('relative no-scrollbar min-w-0 overflow-x-auto px-4 py-3.5 outline-none has-[[data-highlighted-line]]:px-0 has-[[data-line-numbers]]:px-0 has-[[data-slot=tabs]]:p-0', props.class)"
|
|
65
|
+
><CopyButton
|
|
66
|
+
v-if="code"
|
|
67
|
+
:value="code"
|
|
68
|
+
/><code v-bind="codeAttributes"><slot /></code></pre>
|
|
69
|
+
</template>
|
|
70
|
+
|
|
71
|
+
<pre
|
|
72
|
+
v-else
|
|
73
|
+
:data-language="lang"
|
|
74
|
+
:class="cn('relative no-scrollbar min-w-0 overflow-x-auto px-4 py-3.5 outline-none has-[[data-highlighted-line]]:px-0 has-[[data-line-numbers]]:px-0 has-[[data-slot=tabs]]:p-0', props.class)"
|
|
75
|
+
><CopyButton
|
|
76
|
+
v-if="code"
|
|
77
|
+
:value="code"
|
|
78
|
+
/><code v-bind="codeAttributes"><slot /></code></pre>
|
|
79
|
+
</figure>
|
|
80
|
+
</template>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'vue'
|
|
3
|
+
import { cn } from '~/lib/utils'
|
|
4
|
+
|
|
5
|
+
defineOptions({
|
|
6
|
+
inheritAttrs: false,
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
const props = defineProps<{
|
|
10
|
+
class?: HTMLAttributes['class']
|
|
11
|
+
}>()
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<template>
|
|
15
|
+
<div class="no-scrollbar my-6 w-full overflow-y-auto rounded-lg border">
|
|
16
|
+
<table
|
|
17
|
+
:class="cn(
|
|
18
|
+
'relative w-full overflow-hidden border-none text-sm [&_tbody_tr:last-child]:border-b-0',
|
|
19
|
+
props.class,
|
|
20
|
+
)"
|
|
21
|
+
v-bind="$attrs"
|
|
22
|
+
>
|
|
23
|
+
<slot />
|
|
24
|
+
</table>
|
|
25
|
+
</div>
|
|
26
|
+
</template>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'vue'
|
|
3
|
+
import { cn } from '~/lib/utils'
|
|
4
|
+
|
|
5
|
+
const props = defineProps<{
|
|
6
|
+
class?: HTMLAttributes['class']
|
|
7
|
+
}>()
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<h3
|
|
12
|
+
:class="cn(
|
|
13
|
+
'font-heading mt-8 scroll-m-32 text-xl font-medium tracking-tight', props.class,
|
|
14
|
+
)"
|
|
15
|
+
>
|
|
16
|
+
<slot mdc-unwrap="p" />
|
|
17
|
+
</h3>
|
|
18
|
+
</template>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'vue'
|
|
3
|
+
import { cn } from '~/lib/utils'
|
|
4
|
+
|
|
5
|
+
const props = defineProps<{
|
|
6
|
+
class?: HTMLAttributes['class']
|
|
7
|
+
}>()
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<div
|
|
12
|
+
:class="cn(
|
|
13
|
+
'[&>h3]:step steps mb-12 [counter-reset:step] *:[h3]:first:!mt-0', props.class,
|
|
14
|
+
)"
|
|
15
|
+
>
|
|
16
|
+
<slot />
|
|
17
|
+
</div>
|
|
18
|
+
</template>
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes, VNode } from 'vue'
|
|
3
|
+
import { computed, onBeforeUpdate, onMounted, ref, watch } from 'vue'
|
|
4
|
+
import { cn } from '~/lib/utils'
|
|
5
|
+
import {
|
|
6
|
+
Tabs as UITabs,
|
|
7
|
+
TabsContent as UITabsContent,
|
|
8
|
+
TabsList as UITabsList,
|
|
9
|
+
TabsTrigger as UITabsTrigger,
|
|
10
|
+
} from '~/components/ui/tabs'
|
|
11
|
+
|
|
12
|
+
export interface TabsProps {
|
|
13
|
+
/**
|
|
14
|
+
* Default selected tab index (0-based).
|
|
15
|
+
* @default 0
|
|
16
|
+
*/
|
|
17
|
+
defaultIndex?: number
|
|
18
|
+
/**
|
|
19
|
+
* Sync selected tab across all tabs with this key via localStorage.
|
|
20
|
+
* @example 'package-manager'
|
|
21
|
+
*/
|
|
22
|
+
sync?: string
|
|
23
|
+
class?: HTMLAttributes['class']
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const props = withDefaults(defineProps<TabsProps>(), {
|
|
27
|
+
defaultIndex: 0,
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const slots = defineSlots<{
|
|
31
|
+
default(): VNode[]
|
|
32
|
+
}>()
|
|
33
|
+
|
|
34
|
+
const defaultValue = computed(() => String(props.defaultIndex))
|
|
35
|
+
const model = ref<string>(defaultValue.value)
|
|
36
|
+
const rerenderCount = ref(1)
|
|
37
|
+
|
|
38
|
+
interface TabItem {
|
|
39
|
+
value: string
|
|
40
|
+
label: string
|
|
41
|
+
icon?: string
|
|
42
|
+
component: VNode
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const items = computed<TabItem[]>(() => {
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
47
|
+
rerenderCount.value
|
|
48
|
+
|
|
49
|
+
let counter = 0
|
|
50
|
+
function transformSlot(slot: VNode): TabItem | null {
|
|
51
|
+
if (typeof slot.type === 'symbol') {
|
|
52
|
+
return null // Fragments are handled by flatMap in parent call
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const slotProps = slot.props as { label?: string, icon?: string } | null
|
|
56
|
+
if (!slotProps?.label) {
|
|
57
|
+
return null
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
value: String(counter++),
|
|
62
|
+
label: slotProps.label,
|
|
63
|
+
icon: slotProps.icon,
|
|
64
|
+
component: slot,
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function flattenSlots(slot: VNode): VNode[] {
|
|
69
|
+
if (typeof slot.type === 'symbol') {
|
|
70
|
+
const children = slot.children as VNode[] | null
|
|
71
|
+
return children?.flatMap(flattenSlots) ?? []
|
|
72
|
+
}
|
|
73
|
+
return [slot]
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return slots.default?.()?.flatMap(flattenSlots).map(transformSlot).filter((item): item is TabItem => item !== null) || []
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
onMounted(() => {
|
|
80
|
+
if (props.sync) {
|
|
81
|
+
const syncKey = `tabs-${props.sync}`
|
|
82
|
+
const stored = localStorage.getItem(syncKey)
|
|
83
|
+
|
|
84
|
+
if (stored && items.value.some(item => item.value === stored)) {
|
|
85
|
+
model.value = stored
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
watch(model, (value) => {
|
|
89
|
+
if (value) {
|
|
90
|
+
localStorage.setItem(syncKey, value)
|
|
91
|
+
}
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
onBeforeUpdate(() => rerenderCount.value++)
|
|
97
|
+
</script>
|
|
98
|
+
|
|
99
|
+
<template>
|
|
100
|
+
<UITabs
|
|
101
|
+
v-model="model"
|
|
102
|
+
:default-value="defaultValue"
|
|
103
|
+
:class="cn('relative mt-6', props.class)"
|
|
104
|
+
>
|
|
105
|
+
<UITabsList>
|
|
106
|
+
<UITabsTrigger
|
|
107
|
+
v-for="item in items"
|
|
108
|
+
:key="item.value"
|
|
109
|
+
:value="item.value"
|
|
110
|
+
>
|
|
111
|
+
<Icon
|
|
112
|
+
v-if="item.icon"
|
|
113
|
+
:name="item.icon"
|
|
114
|
+
class="size-4 mr-1.5"
|
|
115
|
+
aria-hidden="true"
|
|
116
|
+
/>
|
|
117
|
+
{{ item.label }}
|
|
118
|
+
</UITabsTrigger>
|
|
119
|
+
</UITabsList>
|
|
120
|
+
<UITabsContent
|
|
121
|
+
v-for="item in items"
|
|
122
|
+
:key="item.value"
|
|
123
|
+
:value="item.value"
|
|
124
|
+
class="mt-2"
|
|
125
|
+
>
|
|
126
|
+
<component :is="item.component" />
|
|
127
|
+
</UITabsContent>
|
|
128
|
+
</UITabs>
|
|
129
|
+
</template>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes, VNode } from 'vue'
|
|
3
|
+
|
|
4
|
+
export interface TabsItemProps {
|
|
5
|
+
/**
|
|
6
|
+
* The label displayed in the tab trigger.
|
|
7
|
+
*/
|
|
8
|
+
label: string
|
|
9
|
+
/**
|
|
10
|
+
* Optional icon name for the tab.
|
|
11
|
+
*/
|
|
12
|
+
icon?: string
|
|
13
|
+
class?: HTMLAttributes['class']
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const props = defineProps<TabsItemProps>()
|
|
17
|
+
defineSlots<{
|
|
18
|
+
default(): VNode[]
|
|
19
|
+
}>()
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<template>
|
|
23
|
+
<div :class="props.class">
|
|
24
|
+
<slot />
|
|
25
|
+
</div>
|
|
26
|
+
</template>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'vue'
|
|
3
|
+
import Callout from './Callout.vue'
|
|
4
|
+
|
|
5
|
+
defineProps<{
|
|
6
|
+
title?: string
|
|
7
|
+
class?: HTMLAttributes['class']
|
|
8
|
+
}>()
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<template>
|
|
12
|
+
<Callout
|
|
13
|
+
type="tip"
|
|
14
|
+
:title
|
|
15
|
+
:class
|
|
16
|
+
>
|
|
17
|
+
<template
|
|
18
|
+
v-if="$slots.title"
|
|
19
|
+
#title
|
|
20
|
+
>
|
|
21
|
+
<slot name="title" />
|
|
22
|
+
</template>
|
|
23
|
+
<slot />
|
|
24
|
+
</Callout>
|
|
25
|
+
</template>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { NuxtLinkProps } from '#app'
|
|
3
|
+
import type { ButtonVariants } from '~/components/ui/button'
|
|
4
|
+
import Button from '~/components/ui/button/Button.vue'
|
|
5
|
+
|
|
6
|
+
interface Props extends NuxtLinkProps {
|
|
7
|
+
variant?: ButtonVariants['variant']
|
|
8
|
+
size?: ButtonVariants['size']
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
12
|
+
size: 'default',
|
|
13
|
+
variant: 'default',
|
|
14
|
+
})
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<template>
|
|
18
|
+
<Button
|
|
19
|
+
:size="size"
|
|
20
|
+
:variant="variant"
|
|
21
|
+
as-child
|
|
22
|
+
class="no-underline"
|
|
23
|
+
>
|
|
24
|
+
<NuxtLink
|
|
25
|
+
:to="to"
|
|
26
|
+
:href="href"
|
|
27
|
+
:target="target"
|
|
28
|
+
:rel="rel"
|
|
29
|
+
:external="external"
|
|
30
|
+
>
|
|
31
|
+
<slot />
|
|
32
|
+
</NuxtLink>
|
|
33
|
+
</Button>
|
|
34
|
+
</template>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
light: string
|
|
4
|
+
dark: string
|
|
5
|
+
alt?: string
|
|
6
|
+
width?: number | string
|
|
7
|
+
height?: number | string
|
|
8
|
+
class?: string
|
|
9
|
+
format?: string
|
|
10
|
+
loading?: 'lazy' | 'eager'
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
14
|
+
alt: '',
|
|
15
|
+
loading: 'lazy',
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const colorMode = useColorMode()
|
|
19
|
+
|
|
20
|
+
const src = computed(() => {
|
|
21
|
+
return colorMode.value === 'dark' ? props.dark : props.light
|
|
22
|
+
})
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<template>
|
|
26
|
+
<ClientOnly>
|
|
27
|
+
<NuxtImg
|
|
28
|
+
:src="src"
|
|
29
|
+
:alt="alt"
|
|
30
|
+
:width="width"
|
|
31
|
+
:height="height"
|
|
32
|
+
:class="props.class"
|
|
33
|
+
:loading="loading"
|
|
34
|
+
:format="format"
|
|
35
|
+
/>
|
|
36
|
+
<template #fallback>
|
|
37
|
+
<NuxtImg
|
|
38
|
+
:src="light"
|
|
39
|
+
:alt="alt"
|
|
40
|
+
:width="width"
|
|
41
|
+
:height="height"
|
|
42
|
+
:class="props.class"
|
|
43
|
+
:loading="loading"
|
|
44
|
+
:format="format"
|
|
45
|
+
/>
|
|
46
|
+
</template>
|
|
47
|
+
</ClientOnly>
|
|
48
|
+
</template>
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'vue'
|
|
3
|
+
import type { NuxtLinkProps } from '#app'
|
|
4
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '~/components/ui/card'
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{
|
|
7
|
+
title?: string
|
|
8
|
+
description?: string
|
|
9
|
+
icon?: string
|
|
10
|
+
to?: NuxtLinkProps['to']
|
|
11
|
+
target?: string
|
|
12
|
+
orientation?: 'vertical' | 'horizontal'
|
|
13
|
+
spotlight?: boolean
|
|
14
|
+
class?: HTMLAttributes['class']
|
|
15
|
+
}>()
|
|
16
|
+
|
|
17
|
+
const isLink = computed(() => !!props.to)
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<template>
|
|
21
|
+
<Card
|
|
22
|
+
:class="[
|
|
23
|
+
'transition-colors',
|
|
24
|
+
isLink && 'hover:border-primary/50 cursor-pointer',
|
|
25
|
+
props.spotlight && 'group',
|
|
26
|
+
props.class,
|
|
27
|
+
]"
|
|
28
|
+
>
|
|
29
|
+
<component
|
|
30
|
+
:is="isLink ? 'NuxtLink' : 'div'"
|
|
31
|
+
:to="props.to"
|
|
32
|
+
:target="props.target"
|
|
33
|
+
:class="[
|
|
34
|
+
'block h-full',
|
|
35
|
+
props.orientation === 'horizontal' && 'flex flex-col sm:flex-row',
|
|
36
|
+
]"
|
|
37
|
+
>
|
|
38
|
+
<div :class="props.orientation === 'horizontal' && 'flex-1'">
|
|
39
|
+
<CardHeader v-if="$slots.title || $slots.description || props.title || props.description || props.icon">
|
|
40
|
+
<!-- Icon (decorative - title/description provide meaning) -->
|
|
41
|
+
<div
|
|
42
|
+
v-if="props.icon"
|
|
43
|
+
class="mb-2"
|
|
44
|
+
>
|
|
45
|
+
<Icon
|
|
46
|
+
:name="props.icon"
|
|
47
|
+
class="size-8 text-primary"
|
|
48
|
+
aria-hidden="true"
|
|
49
|
+
/>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<!-- Title -->
|
|
53
|
+
<CardTitle v-if="$slots.title || props.title">
|
|
54
|
+
<slot name="title">
|
|
55
|
+
{{ props.title }}
|
|
56
|
+
</slot>
|
|
57
|
+
</CardTitle>
|
|
58
|
+
|
|
59
|
+
<!-- Description -->
|
|
60
|
+
<CardDescription v-if="$slots.description || props.description">
|
|
61
|
+
<slot name="description">
|
|
62
|
+
{{ props.description }}
|
|
63
|
+
</slot>
|
|
64
|
+
</CardDescription>
|
|
65
|
+
</CardHeader>
|
|
66
|
+
|
|
67
|
+
<CardContent v-if="$slots.body">
|
|
68
|
+
<slot name="body" />
|
|
69
|
+
</CardContent>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<!-- Default slot for media/illustration (below content) -->
|
|
73
|
+
<CardContent
|
|
74
|
+
v-if="$slots.default"
|
|
75
|
+
:class="[
|
|
76
|
+
props.orientation === 'horizontal' ? 'sm:w-1/3 shrink-0' : '',
|
|
77
|
+
]"
|
|
78
|
+
>
|
|
79
|
+
<slot />
|
|
80
|
+
</CardContent>
|
|
81
|
+
</component>
|
|
82
|
+
</Card>
|
|
83
|
+
</template>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'vue'
|
|
3
|
+
|
|
4
|
+
const props = defineProps<{
|
|
5
|
+
class?: HTMLAttributes['class']
|
|
6
|
+
}>()
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<template>
|
|
10
|
+
<div
|
|
11
|
+
:class="[
|
|
12
|
+
'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6',
|
|
13
|
+
props.class,
|
|
14
|
+
]"
|
|
15
|
+
>
|
|
16
|
+
<slot />
|
|
17
|
+
</div>
|
|
18
|
+
</template>
|