@indielayer/ui 1.17.0 → 1.18.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 +2 -2
- package/docs/assets/css/tailwind.css +6 -0
- package/docs/components/common/CodePreview.vue +14 -9
- package/docs/components/common/DocsFeatures.vue +41 -0
- package/docs/components/common/DocsHero.vue +216 -0
- package/docs/components/common/DocumentPage.vue +99 -112
- package/docs/components/common/ExampleBlocks.vue +157 -0
- package/docs/components/toolbar/Toolbar.vue +11 -2
- package/docs/components/toolbar/ToolbarColorToggle.vue +4 -4
- package/docs/components/toolbar/ToolbarSearch.vue +59 -62
- package/docs/composables/useDocMeta.ts +47 -0
- package/docs/icons.ts +28 -0
- package/docs/layouts/default.vue +1 -3
- package/docs/layouts/simple.vue +3 -1
- package/docs/main.ts +5 -0
- package/docs/pages/colors.vue +56 -47
- package/docs/pages/component/select/size.vue +1 -1
- package/docs/pages/component/select/usage.vue +14 -7
- package/docs/pages/error.vue +5 -3
- package/docs/pages/icons.vue +64 -54
- package/docs/pages/index.vue +93 -82
- package/docs/pages/typography.vue +38 -28
- package/docs/router/index.ts +31 -3
- package/docs/search/components.json +1 -1
- package/docs/search/index.json +1 -0
- package/lib/components/container/theme/Container.base.theme.js +1 -1
- package/lib/components/divider/theme/Divider.base.theme.js +1 -1
- package/lib/components/input/Input.vue.js +23 -24
- package/lib/components/select/Select.vue.d.ts +16 -27
- package/lib/components/select/Select.vue.js +451 -344
- package/lib/index.js +1 -1
- package/lib/index.umd.js +4 -4
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/lib/virtual/components/virtualList/VirtualList.vue.js +33 -31
- package/lib/virtual/components/virtualList/useDynamicRowHeight.js +18 -19
- package/package.json +8 -3
- package/src/components/container/theme/Container.base.theme.ts +1 -1
- package/src/components/divider/theme/Divider.base.theme.ts +1 -1
- package/src/components/input/Input.vue +1 -2
- package/src/components/select/Select.vue +94 -18
- package/src/version.ts +1 -1
- package/src/virtual/components/virtualList/VirtualList.test.ts +143 -26
- package/src/virtual/components/virtualList/VirtualList.vue +12 -18
- package/src/virtual/components/virtualList/useDynamicRowHeight.test.ts +22 -8
- package/src/virtual/components/virtualList/useDynamicRowHeight.ts +4 -2
- package/src/virtual/utils/parseNumericStyleValue.ts +2 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { onBeforeUnmount, ref } from 'vue'
|
|
3
|
+
|
|
4
|
+
const selected = ref('A')
|
|
5
|
+
const options = [{
|
|
6
|
+
value: 'A',
|
|
7
|
+
label: 'Option A',
|
|
8
|
+
}, {
|
|
9
|
+
value: 'B',
|
|
10
|
+
label: 'Option B',
|
|
11
|
+
}]
|
|
12
|
+
const toggle = ref(false)
|
|
13
|
+
const tab = ref('A')
|
|
14
|
+
const radio = ref('a')
|
|
15
|
+
const password = ref('')
|
|
16
|
+
const slider = ref(80)
|
|
17
|
+
const isPlaying = ref(false)
|
|
18
|
+
let interval: ReturnType<typeof setInterval> | null = null
|
|
19
|
+
|
|
20
|
+
function togglePlayer() {
|
|
21
|
+
if (isPlaying.value) {
|
|
22
|
+
if (interval) clearInterval(interval)
|
|
23
|
+
interval = null
|
|
24
|
+
} else {
|
|
25
|
+
interval = setInterval(() => {
|
|
26
|
+
slider.value += 1
|
|
27
|
+
}, 1000)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
isPlaying.value = !isPlaying.value
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
onBeforeUnmount(() => {
|
|
34
|
+
if (interval) clearInterval(interval)
|
|
35
|
+
})
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<template>
|
|
39
|
+
<div class="grid gap-6 grid-cols-1 xl:grid-cols-2 max-w-4xl">
|
|
40
|
+
<div>
|
|
41
|
+
<div class="flex items-center">
|
|
42
|
+
<x-button-group>
|
|
43
|
+
<x-button icon="menu-alt-1"/>
|
|
44
|
+
<x-button icon="menu"/>
|
|
45
|
+
<x-button icon="menu-alt-3"/>
|
|
46
|
+
</x-button-group>
|
|
47
|
+
|
|
48
|
+
<x-spacer/>
|
|
49
|
+
|
|
50
|
+
<x-select
|
|
51
|
+
v-model="selected"
|
|
52
|
+
:options="options"
|
|
53
|
+
placeholder="Placeholder"
|
|
54
|
+
class="w-64 ml-2"
|
|
55
|
+
hide-footer
|
|
56
|
+
/>
|
|
57
|
+
</div>
|
|
58
|
+
<x-card class="p-4 flex items-center mt-4">
|
|
59
|
+
<x-badge outlined position="bottom">
|
|
60
|
+
<x-avatar image="https://avatars.githubusercontent.com/u/3942799?v=4" rounded/>
|
|
61
|
+
</x-badge>
|
|
62
|
+
<div class="ml-4">
|
|
63
|
+
<div class="font-bold">John Smith</div>
|
|
64
|
+
<div class="text-gray-400 text-sm">jsmith@indielayer.com</div>
|
|
65
|
+
</div>
|
|
66
|
+
</x-card>
|
|
67
|
+
<div class="flex space-x-2 mt-4">
|
|
68
|
+
<x-toggle v-model="toggle"/>
|
|
69
|
+
<x-spacer/>
|
|
70
|
+
<x-tag rounded size="sm" :color="toggle ? 'primary' : undefined">{{ toggle ? 'Active' : 'Inactive' }}</x-tag>
|
|
71
|
+
</div>
|
|
72
|
+
<div class="mt-4">
|
|
73
|
+
<div class="grid grid-cols-3 gap-2">
|
|
74
|
+
<x-button color="primary">Filled</x-button>
|
|
75
|
+
<x-button color="primary" outlined>Outlined</x-button>
|
|
76
|
+
<x-button color="primary" light>Light</x-button>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
<x-card flat class="border mt-4">
|
|
80
|
+
<div class="p-2 flex items-center">
|
|
81
|
+
<x-avatar image="https://avatars.githubusercontent.com/u/3942799?v=4" size="lg"/>
|
|
82
|
+
<div class="ml-4">
|
|
83
|
+
<div class="font-bold">Holding back</div>
|
|
84
|
+
<div class="text-gray-400 text-sm">BANKS</div>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
<x-divider/>
|
|
88
|
+
<div class="p-6">
|
|
89
|
+
<div class="flex items-center justify-center mb-6">
|
|
90
|
+
<x-button icon="rewind" ghost rounded/>
|
|
91
|
+
<x-button
|
|
92
|
+
:icon="isPlaying ? 'pause' : 'play'"
|
|
93
|
+
rounded
|
|
94
|
+
size="lg"
|
|
95
|
+
class="mx-2"
|
|
96
|
+
@click="togglePlayer"
|
|
97
|
+
/>
|
|
98
|
+
<x-button icon="forward" ghost rounded/>
|
|
99
|
+
</div>
|
|
100
|
+
<x-slider v-model="slider" class="w-full">
|
|
101
|
+
<template #prefix>
|
|
102
|
+
<div class="w-12 text-sm text-left">3:26</div>
|
|
103
|
+
</template>
|
|
104
|
+
<template #suffix>
|
|
105
|
+
<div class="w-12 text-sm text-right">4:12</div>
|
|
106
|
+
</template>
|
|
107
|
+
</x-slider>
|
|
108
|
+
</div>
|
|
109
|
+
</x-card>
|
|
110
|
+
</div>
|
|
111
|
+
<div>
|
|
112
|
+
<x-tab-group v-model="tab" grow variant="block">
|
|
113
|
+
<x-tab label="Profile" value="A"/>
|
|
114
|
+
<x-tab label="Settings" value="B"/>
|
|
115
|
+
<x-tab label="Content" value="C"/>
|
|
116
|
+
</x-tab-group>
|
|
117
|
+
<div class="mt-3 mb-1">
|
|
118
|
+
<x-radio
|
|
119
|
+
v-model="radio"
|
|
120
|
+
value="a"
|
|
121
|
+
label="Credit card"
|
|
122
|
+
class="border p-3 rounded w-full"
|
|
123
|
+
:class="{ 'bg-emerald-50 dark:bg-gray-800 border-emerald-400 dark:border-emerald-800': radio === 'a'}"
|
|
124
|
+
>
|
|
125
|
+
<div class="text-gray-400 font-light text-sm">VISA · · · · 2592</div>
|
|
126
|
+
</x-radio>
|
|
127
|
+
</div>
|
|
128
|
+
<div>
|
|
129
|
+
<x-radio
|
|
130
|
+
v-model="radio"
|
|
131
|
+
value="b"
|
|
132
|
+
label="Bank account"
|
|
133
|
+
class="border p-3 rounded w-full"
|
|
134
|
+
:class="{ 'bg-emerald-50 dark:bg-gray-800 border-emerald-400 dark:border-emerald-800': radio === 'b'}"
|
|
135
|
+
>
|
|
136
|
+
<div class="text-gray-400 font-light text-sm">Santander · · · · 1580</div>
|
|
137
|
+
</x-radio>
|
|
138
|
+
</div>
|
|
139
|
+
<div class="my-3">
|
|
140
|
+
<x-alert type="success" color="success" outlined>User details updated!</x-alert>
|
|
141
|
+
</div>
|
|
142
|
+
<div>
|
|
143
|
+
<x-form :auto-focus="false">
|
|
144
|
+
<x-input label="Email" type="email" block/>
|
|
145
|
+
<x-input
|
|
146
|
+
v-model="password"
|
|
147
|
+
label="Password"
|
|
148
|
+
type="password"
|
|
149
|
+
show-password-toggle
|
|
150
|
+
block
|
|
151
|
+
/>
|
|
152
|
+
<x-checkbox label="Keep me logged in"/>
|
|
153
|
+
</x-form>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
</template>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { inject, ref, unref, watch } from 'vue'
|
|
3
3
|
import { version, type UITheme } from '@indielayer/ui'
|
|
4
4
|
import ToolbarSearch from './ToolbarSearch.vue'
|
|
5
|
+
import ToolbarColorToggle from './ToolbarColorToggle.vue'
|
|
5
6
|
|
|
6
7
|
const selectTheme = inject('selectTheme', {
|
|
7
8
|
theme: {} as UITheme,
|
|
@@ -25,7 +26,7 @@ const isDev = import.meta.env.DEV
|
|
|
25
26
|
</script>
|
|
26
27
|
|
|
27
28
|
<template>
|
|
28
|
-
<x-container fluid class="bg-white dark:bg-
|
|
29
|
+
<x-container fluid class="bg-white dark:bg-slate-800/60">
|
|
29
30
|
<div class="flex justify-items-center items-center py-2 min-h-[54px]">
|
|
30
31
|
<a href="/" class="flex items-center">
|
|
31
32
|
<img src="@/assets/images/logo_mini.svg" width="26" alt="Indielayer"/>
|
|
@@ -40,7 +41,15 @@ const isDev = import.meta.env.DEV
|
|
|
40
41
|
|
|
41
42
|
<x-spacer/>
|
|
42
43
|
|
|
43
|
-
<div class="flex items-center font-semibold text-sm">
|
|
44
|
+
<div class="flex items-center font-semibold text-sm gap-1">
|
|
45
|
+
<x-select
|
|
46
|
+
v-if="isDev"
|
|
47
|
+
v-model="selected"
|
|
48
|
+
:options="options"
|
|
49
|
+
size="xs"
|
|
50
|
+
class="hidden md:block w-28"
|
|
51
|
+
/>
|
|
52
|
+
<toolbar-color-toggle class="hidden sm:flex" />
|
|
44
53
|
<toolbar-search />
|
|
45
54
|
<x-divider vertical class="!h-2 px-2"/>
|
|
46
55
|
<div class="tracking-wide text-xs">v{{ version }}</div>
|
|
@@ -8,10 +8,10 @@ try {
|
|
|
8
8
|
|
|
9
9
|
if (storedMode) {
|
|
10
10
|
colorMode.value = storedMode
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
} else {
|
|
12
|
+
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
|
13
|
+
colorMode.value = 'dark'
|
|
14
|
+
}
|
|
15
15
|
}
|
|
16
16
|
} catch (e) {
|
|
17
17
|
colorMode.value = 'light'
|
|
@@ -2,10 +2,17 @@
|
|
|
2
2
|
import { useEventListener, useMouse } from '@vueuse/core'
|
|
3
3
|
import { computed, ref, watch, nextTick, onMounted } from 'vue'
|
|
4
4
|
import Fuse from 'fuse.js'
|
|
5
|
-
import
|
|
5
|
+
import SearchIndex from '../../search/index.json'
|
|
6
6
|
import type { XInput } from 'src'
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
type SearchItem = {
|
|
9
|
+
name: string;
|
|
10
|
+
description: string;
|
|
11
|
+
url: string;
|
|
12
|
+
category: 'guide' | 'component';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const fuse = new Fuse(SearchIndex as SearchItem[], {
|
|
9
16
|
keys: [{
|
|
10
17
|
name: 'name',
|
|
11
18
|
weight: 2,
|
|
@@ -17,65 +24,63 @@ const fuse = new Fuse(ComponentsSearchIndex, {
|
|
|
17
24
|
})
|
|
18
25
|
|
|
19
26
|
type FuseResult = {
|
|
20
|
-
item:
|
|
21
|
-
name: string;
|
|
22
|
-
description: string;
|
|
23
|
-
url: string;
|
|
24
|
-
};
|
|
27
|
+
item: SearchItem;
|
|
25
28
|
refIndex: number;
|
|
26
29
|
score: number;
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
const isModalOpen = ref(false)
|
|
30
33
|
const searchInput = ref('')
|
|
31
|
-
const
|
|
34
|
+
const results = ref<FuseResult[]>([])
|
|
32
35
|
const searchList = ref<HTMLDivElement>()
|
|
33
|
-
const
|
|
36
|
+
const resultElements = ref<HTMLLIElement[]>([])
|
|
34
37
|
const selectedIndex = ref<number>(-1)
|
|
35
38
|
|
|
36
39
|
const inputEl = ref<InstanceType<typeof XInput> | null>(null)
|
|
37
40
|
|
|
38
|
-
|
|
41
|
+
const searchSections = computed(() => {
|
|
42
|
+
const guides = results.value.filter((r) => r.item.category === 'guide')
|
|
43
|
+
const components = results.value.filter((r) => r.item.category === 'component')
|
|
44
|
+
|
|
45
|
+
return [
|
|
46
|
+
{ key: 'component', label: 'Components', items: components },
|
|
47
|
+
{ key: 'guide', label: 'Guides', items: guides },
|
|
48
|
+
].filter((s) => s.items.length > 0)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
watch(isModalOpen, (newValue) => {
|
|
39
52
|
setTimeout(() => {
|
|
40
53
|
if (newValue) inputEl.value?.focus()
|
|
41
54
|
}, 100)
|
|
42
55
|
})
|
|
43
56
|
|
|
44
|
-
const searchSections = computed(() => [
|
|
45
|
-
{ key:'ui', label: 'UI', items: ui.value },
|
|
46
|
-
])
|
|
47
|
-
|
|
48
57
|
function clearSearch() {
|
|
49
58
|
selectedIndex.value = -1
|
|
50
|
-
|
|
59
|
+
results.value = []
|
|
51
60
|
}
|
|
52
61
|
|
|
53
62
|
function openSearch() {
|
|
54
63
|
clearSearch()
|
|
55
|
-
|
|
56
64
|
searchInput.value = ''
|
|
57
65
|
isModalOpen.value = true
|
|
58
66
|
}
|
|
59
67
|
|
|
60
68
|
function selectItem(item: HTMLLIElement, index?: number) {
|
|
61
|
-
if (!item)
|
|
62
|
-
return
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
results.value?.[selectedIndex.value]?.setAttribute('aria-selected', 'false')
|
|
69
|
+
if (!item) return
|
|
66
70
|
|
|
71
|
+
resultElements.value?.[selectedIndex.value]?.setAttribute('aria-selected', 'false')
|
|
67
72
|
item.setAttribute('aria-selected', 'true')
|
|
68
|
-
item.scrollIntoView({
|
|
73
|
+
item.scrollIntoView({ block: 'nearest' })
|
|
69
74
|
|
|
70
|
-
if (
|
|
71
|
-
index =
|
|
75
|
+
if (index === undefined) {
|
|
76
|
+
index = resultElements.value.findIndex(({ id }) => id === item.id)
|
|
72
77
|
}
|
|
73
78
|
|
|
74
79
|
selectedIndex.value = index
|
|
75
80
|
}
|
|
76
81
|
|
|
77
82
|
function selectItemByIndex(index: number) {
|
|
78
|
-
selectItem(
|
|
83
|
+
selectItem(resultElements.value?.[index], index)
|
|
79
84
|
}
|
|
80
85
|
|
|
81
86
|
function selectFirstItem() {
|
|
@@ -83,39 +88,29 @@ function selectFirstItem() {
|
|
|
83
88
|
}
|
|
84
89
|
|
|
85
90
|
function selectLastItem() {
|
|
86
|
-
selectItemByIndex(
|
|
91
|
+
selectItemByIndex(resultElements.value.length - 1)
|
|
87
92
|
}
|
|
88
93
|
|
|
89
94
|
function selectNextItem() {
|
|
90
|
-
const
|
|
95
|
+
const total = resultElements.value.length
|
|
91
96
|
|
|
92
|
-
if (
|
|
93
|
-
return
|
|
94
|
-
}
|
|
97
|
+
if (total <= 0) return
|
|
95
98
|
|
|
96
99
|
const nextIndex = selectedIndex.value + 1
|
|
97
100
|
|
|
98
|
-
if (nextIndex >=
|
|
99
|
-
|
|
100
|
-
} else {
|
|
101
|
-
selectItemByIndex(nextIndex)
|
|
102
|
-
}
|
|
101
|
+
if (nextIndex >= total) selectFirstItem()
|
|
102
|
+
else selectItemByIndex(nextIndex)
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
function selectPreviousItem() {
|
|
106
|
-
const
|
|
106
|
+
const total = resultElements.value.length
|
|
107
107
|
|
|
108
|
-
if (
|
|
109
|
-
return
|
|
110
|
-
}
|
|
108
|
+
if (total <= 0) return
|
|
111
109
|
|
|
112
110
|
const previousIndex = selectedIndex.value - 1
|
|
113
111
|
|
|
114
|
-
if (previousIndex >= 0)
|
|
115
|
-
|
|
116
|
-
} else {
|
|
117
|
-
selectLastItem()
|
|
118
|
-
}
|
|
112
|
+
if (previousIndex >= 0) selectItemByIndex(previousIndex)
|
|
113
|
+
else selectLastItem()
|
|
119
114
|
}
|
|
120
115
|
|
|
121
116
|
function keydownInput(e: KeyboardEvent) {
|
|
@@ -130,7 +125,7 @@ function keydownInput(e: KeyboardEvent) {
|
|
|
130
125
|
}
|
|
131
126
|
|
|
132
127
|
if (e.key === 'Enter') {
|
|
133
|
-
const item =
|
|
128
|
+
const item = resultElements.value?.[selectedIndex.value]
|
|
134
129
|
|
|
135
130
|
if (item) {
|
|
136
131
|
(item.firstElementChild as HTMLLinkElement)?.click()
|
|
@@ -142,37 +137,38 @@ const { x: mouseX, y: mouseY } = useMouse({ type: 'page' })
|
|
|
142
137
|
|
|
143
138
|
function hoverResult(e: MouseEvent) {
|
|
144
139
|
if (mouseX.value !== e.x || mouseY.value !== e.y) {
|
|
145
|
-
selectItem(e.
|
|
140
|
+
selectItem(e.currentTarget as HTMLLIElement)
|
|
146
141
|
}
|
|
147
142
|
}
|
|
148
143
|
|
|
149
144
|
function searchIndexes() {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
145
|
+
if (!searchInput.value.trim()) {
|
|
146
|
+
results.value = []
|
|
147
|
+
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
results.value = fuse.search(searchInput.value, { limit: 12 }) as FuseResult[]
|
|
153
152
|
}
|
|
154
153
|
|
|
155
154
|
watch(searchSections, async () => {
|
|
156
|
-
|
|
157
|
-
|
|
155
|
+
resultElements.value = []
|
|
158
156
|
await nextTick()
|
|
159
157
|
|
|
160
158
|
const items = searchList.value?.querySelectorAll('[data-name="list-item"]')
|
|
161
159
|
|
|
162
160
|
items?.forEach((el) => {
|
|
163
161
|
el.setAttribute('aria-selected', 'false')
|
|
164
|
-
|
|
162
|
+
resultElements.value.push(el as HTMLLIElement)
|
|
165
163
|
})
|
|
166
164
|
|
|
167
|
-
setTimeout(() => selectFirstItem())
|
|
165
|
+
if (resultElements.value.length) setTimeout(() => selectFirstItem())
|
|
168
166
|
})
|
|
169
167
|
|
|
170
168
|
const metaKey = ref('')
|
|
171
169
|
|
|
172
170
|
onMounted(() => {
|
|
173
|
-
metaKey.value = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform)
|
|
174
|
-
? '⌘'
|
|
175
|
-
: 'Ctrl'
|
|
171
|
+
metaKey.value = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform) ? '⌘' : 'Ctrl'
|
|
176
172
|
})
|
|
177
173
|
|
|
178
174
|
if (document) {
|
|
@@ -206,7 +202,7 @@ if (document) {
|
|
|
206
202
|
v-model="searchInput"
|
|
207
203
|
type="search"
|
|
208
204
|
aria-controls="search-list"
|
|
209
|
-
placeholder="Search components"
|
|
205
|
+
placeholder="Search docs and components"
|
|
210
206
|
hide-footer
|
|
211
207
|
icon-left="search"
|
|
212
208
|
@input="searchIndexes"
|
|
@@ -214,10 +210,10 @@ if (document) {
|
|
|
214
210
|
/>
|
|
215
211
|
</div>
|
|
216
212
|
</template>
|
|
217
|
-
<div id="search-list" ref="searchList">
|
|
213
|
+
<div id="search-list" ref="searchList" class="max-h-96 overflow-y-auto px-2">
|
|
218
214
|
<template v-for="section in searchSections" :key="section.key">
|
|
219
|
-
<section v-if="section.items.length > 0">
|
|
220
|
-
|
|
215
|
+
<section v-if="section.items.length > 0" class="mb-2">
|
|
216
|
+
<x-divider :label="section.label" class="my-2" />
|
|
221
217
|
<ul role="listbox" :aria-labelledby="`${section.key}-label`">
|
|
222
218
|
<li
|
|
223
219
|
v-for="(result) in section.items"
|
|
@@ -225,7 +221,7 @@ if (document) {
|
|
|
225
221
|
:key="result.item.url"
|
|
226
222
|
data-name="list-item"
|
|
227
223
|
role="option"
|
|
228
|
-
class="aria-selected:bg-secondary-100 dark:aria-selected:bg-secondary-800 rounded p-2 mb-
|
|
224
|
+
class="aria-selected:bg-secondary-100 dark:aria-selected:bg-secondary-800 rounded p-2 mb-1"
|
|
229
225
|
@mouseenter="hoverResult"
|
|
230
226
|
>
|
|
231
227
|
<x-link
|
|
@@ -234,12 +230,13 @@ if (document) {
|
|
|
234
230
|
@click="isModalOpen = false"
|
|
235
231
|
>
|
|
236
232
|
<p class="text-base w-full mb-1 mt-0 font-bold">{{ result.item.name }}</p>
|
|
237
|
-
<p class="text-sm m-0">{{ result.item.description }}</p>
|
|
233
|
+
<p class="text-sm m-0 text-gray-500 dark:text-gray-400 line-clamp-2">{{ result.item.description }}</p>
|
|
238
234
|
</x-link>
|
|
239
235
|
</li>
|
|
240
236
|
</ul>
|
|
241
237
|
</section>
|
|
242
238
|
</template>
|
|
239
|
+
<p v-if="searchInput && !results.length" class="text-center text-gray-500 py-8 text-sm">No results found.</p>
|
|
243
240
|
</div>
|
|
244
241
|
|
|
245
242
|
<template #actions>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { useHead } from '@unhead/vue'
|
|
2
|
+
|
|
3
|
+
const SITE_URL = 'https://indielayer.com'
|
|
4
|
+
const SITE_NAME = 'Indielayer'
|
|
5
|
+
const DEFAULT_OG_IMAGE = `${SITE_URL}/card.jpg`
|
|
6
|
+
const DEFAULT_KEYWORDS = 'indielayer, vue, vue 3, nuxt, nuxt 3, tailwind css, ui kit, ui library, ui components, component library'
|
|
7
|
+
|
|
8
|
+
export function useDocMeta(options: {
|
|
9
|
+
title: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
path?: string;
|
|
12
|
+
}) {
|
|
13
|
+
const pageTitle = options.title === 'Indielayer UI'
|
|
14
|
+
? 'Indielayer UI — Vue 3 component library'
|
|
15
|
+
: `${options.title} — Indielayer UI`
|
|
16
|
+
|
|
17
|
+
const description = options.description
|
|
18
|
+
|| 'Vue 3 and Tailwind CSS UI components for fast web applications.'
|
|
19
|
+
const url = options.path ? `${SITE_URL}${options.path}` : SITE_URL
|
|
20
|
+
|
|
21
|
+
useHead({
|
|
22
|
+
title: pageTitle,
|
|
23
|
+
meta: [
|
|
24
|
+
{ name: 'description', content: description },
|
|
25
|
+
{ name: 'keywords', content: DEFAULT_KEYWORDS },
|
|
26
|
+
{ property: 'og:site_name', content: SITE_NAME },
|
|
27
|
+
{ property: 'og:title', content: pageTitle },
|
|
28
|
+
{ property: 'og:description', content: description },
|
|
29
|
+
{ property: 'og:url', content: url },
|
|
30
|
+
{ property: 'og:type', content: 'website' },
|
|
31
|
+
{ property: 'og:image', content: DEFAULT_OG_IMAGE },
|
|
32
|
+
{ property: 'og:image:width', content: '1440' },
|
|
33
|
+
{ property: 'og:image:height', content: '731' },
|
|
34
|
+
{ property: 'og:image:type', content: 'image/jpeg' },
|
|
35
|
+
{ property: 'og:image:alt', content: SITE_NAME },
|
|
36
|
+
{ name: 'twitter:card', content: 'summary_large_image' },
|
|
37
|
+
{ name: 'twitter:site', content: '@indielayer' },
|
|
38
|
+
{ name: 'twitter:image', content: DEFAULT_OG_IMAGE },
|
|
39
|
+
{ name: 'twitter:image:alt', content: SITE_NAME },
|
|
40
|
+
{ name: 'twitter:title', content: pageTitle },
|
|
41
|
+
{ name: 'twitter:description', content: description },
|
|
42
|
+
],
|
|
43
|
+
link: [
|
|
44
|
+
{ rel: 'canonical', href: url },
|
|
45
|
+
],
|
|
46
|
+
})
|
|
47
|
+
}
|
package/docs/icons.ts
CHANGED
|
@@ -28,6 +28,34 @@ export default {
|
|
|
28
28
|
moon: '<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>',
|
|
29
29
|
upload: '<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="17 8 12 3 7 8"></polyline><line x1="12" y1="3" x2="12" y2="15"></line>',
|
|
30
30
|
menu: '<line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line>',
|
|
31
|
+
'menu-alt-1': '<path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16M4 18h7"/>',
|
|
32
|
+
'menu-alt-3': '<path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16m-7 6h7" />',
|
|
33
|
+
rewind: {
|
|
34
|
+
icon: '<polygon points="11 19 2 12 11 5 11 19"></polygon><polygon points="22 19 13 12 22 5 22 19"></polygon>',
|
|
35
|
+
filled: true,
|
|
36
|
+
},
|
|
37
|
+
play: {
|
|
38
|
+
icon: '<polygon points="5 3 19 12 5 21 5 3"></polygon>',
|
|
39
|
+
filled: true,
|
|
40
|
+
},
|
|
41
|
+
pause: {
|
|
42
|
+
icon: '<rect x="6" y="4" width="4" height="16"></rect><rect x="14" y="4" width="4" height="16"></rect>',
|
|
43
|
+
filled: true,
|
|
44
|
+
},
|
|
45
|
+
forward: {
|
|
46
|
+
icon: '<polygon points="13 19 22 12 13 5 13 19"></polygon><polygon points="2 19 11 12 2 5 2 19"></polygon>',
|
|
47
|
+
filled: true,
|
|
48
|
+
},
|
|
49
|
+
vue: {
|
|
50
|
+
icon: '<path d="M17.2322 0.000135422L13.9992 5.77351L10.7661 0.000135422H-0.000488281L13.9992 24.9999L27.9988 0.000135422H17.2322Z" fill="#41B883"/><path d="M17.2322 0.000144958L13.9992 5.77352L10.7661 0.000144958H5.59937L13.9992 14.9997L22.3989 0.000144958H17.2322Z" fill="#34495E"/>',
|
|
51
|
+
filled: true,
|
|
52
|
+
viewBox: '0 0 28 25',
|
|
53
|
+
},
|
|
54
|
+
nuxt: {
|
|
55
|
+
icon: '<path fill-rule="evenodd" clip-rule="evenodd" d="M15.6348 2.10272C14.6734 0.449622 12.2697 0.449592 11.3083 2.10272L0.830082 20.1192C-0.131361 21.7723 1.07044 23.8387 2.99332 23.8387H11.1732C10.3515 23.1206 10.0473 21.8784 10.6691 20.8126L18.6048 7.2094L15.6348 2.10272Z" fill="#80EEC0"/><path d="M22.2744 6.05476C23.0701 4.7022 25.0593 4.7022 25.855 6.05476L34.5264 20.7955C35.3222 22.148 34.3277 23.8387 32.7364 23.8387H15.3931C13.8017 23.8387 12.8071 22.148 13.6028 20.7955L22.2744 6.05476Z" fill="#00DC82"/>',
|
|
56
|
+
filled: true,
|
|
57
|
+
viewBox: '0 0 35 24',
|
|
58
|
+
},
|
|
31
59
|
bold: '<path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M7 5h6a3.5 3.5 0 0 1 0 7h-6z" /><path d="M13 12h1a3.5 3.5 0 0 1 0 7h-7v-7" />',
|
|
32
60
|
italic: '<path stroke="none" d="M0 0h24v24H0z" fill="none"></path><line x1="11" y1="5" x2="17" y2="5"></line><line x1="7" y1="19" x2="13" y2="19"></line><line x1="14" y1="5" x2="10" y2="19"></line>',
|
|
33
61
|
'edit': '<path d="M12 20h9"></path><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"></path>',
|
package/docs/layouts/default.vue
CHANGED
|
@@ -24,9 +24,7 @@ const drawer = ref(false)
|
|
|
24
24
|
</x-drawer>
|
|
25
25
|
<x-notifications>
|
|
26
26
|
<div id="main" class="flex-1 overflow-y-scroll bg-secondary-50 dark:bg-gray-900">
|
|
27
|
-
<
|
|
28
|
-
<router-view />
|
|
29
|
-
</x-container>
|
|
27
|
+
<router-view />
|
|
30
28
|
</div>
|
|
31
29
|
</x-notifications>
|
|
32
30
|
</div>
|
package/docs/layouts/simple.vue
CHANGED
package/docs/main.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createApp } from 'vue'
|
|
2
|
+
import { createHead } from '@unhead/vue/client'
|
|
2
3
|
import UI, { BaseTheme } from '@indielayer/ui'
|
|
3
4
|
import App from './App.vue'
|
|
4
5
|
import router from './router'
|
|
@@ -10,12 +11,15 @@ import CodeSnippet from './components/common/CodeSnippet.vue'
|
|
|
10
11
|
import MultiSnippet from './components/common/MultiSnippet.vue'
|
|
11
12
|
import CodePreview from './components/common/CodePreview.vue'
|
|
12
13
|
import DocumentPage from './components/common/DocumentPage.vue'
|
|
14
|
+
import DocsHero from './components/common/DocsHero.vue'
|
|
13
15
|
|
|
14
16
|
// css
|
|
15
17
|
import './assets/css/tailwind.css'
|
|
16
18
|
|
|
17
19
|
const app = createApp(App)
|
|
20
|
+
const head = createHead()
|
|
18
21
|
|
|
22
|
+
app.use(head)
|
|
19
23
|
app.use(UI, {
|
|
20
24
|
prefix: 'X',
|
|
21
25
|
icons,
|
|
@@ -29,5 +33,6 @@ app.component('CodeSnippet', CodeSnippet)
|
|
|
29
33
|
app.component('MultiSnippet', MultiSnippet)
|
|
30
34
|
app.component('CodePreview', CodePreview)
|
|
31
35
|
app.component('DocumentPage', DocumentPage)
|
|
36
|
+
app.component('DocsHero', DocsHero)
|
|
32
37
|
|
|
33
38
|
app.mount('#app')
|