@dolanske/vui 0.3.3 → 0.4.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 +9 -0
- package/dist/components/Avatar/Avatar.vue.d.ts +1 -0
- package/dist/components/Dropdown/DropdownItem.vue.d.ts +1 -0
- package/dist/style.css +1 -1
- package/dist/vui.js +1338 -1325
- package/package.json +1 -1
- package/src/App.vue +135 -102
- package/src/components/Avatar/Avatar.vue +8 -1
- package/src/components/Dropdown/Dropdown.vue +5 -4
- package/src/components/Dropdown/DropdownItem.vue +6 -3
- package/src/components/Dropdown/dropdown.scss +3 -1
- package/src/components/Flex/Flex.vue +5 -1
- package/src/components/Grid/Grid.vue +5 -2
- package/src/components/Popout/Popout.vue +4 -1
- package/src/components/Sidebar/Sidebar.vue +85 -0
- package/src/components/Sidebar/sidebar.scss +123 -0
- package/src/components/Tabs/Tabs.vue +2 -2
- package/src/components/Tooltip/Tooltip.vue +3 -1
- package/src/style/core.scss +5 -3
package/package.json
CHANGED
package/src/App.vue
CHANGED
|
@@ -1,124 +1,156 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { ref } from 'vue'
|
|
3
3
|
import Avatar from './components/Avatar/Avatar.vue'
|
|
4
|
+
import Button from './components/Button/Button.vue'
|
|
5
|
+
|
|
4
6
|
import Divider from './components/Divider/Divider.vue'
|
|
7
|
+
import Dropdown from './components/Dropdown/Dropdown.vue'
|
|
8
|
+
import DropdownItem from './components/Dropdown/DropdownItem.vue'
|
|
5
9
|
import Flex from './components/Flex/Flex.vue'
|
|
10
|
+
import Sidebar from './components/Sidebar/Sidebar.vue'
|
|
6
11
|
import Tab from './components/Tabs/Tab.vue'
|
|
7
|
-
|
|
8
12
|
import Tabs from './components/Tabs/Tabs.vue'
|
|
13
|
+
|
|
9
14
|
import Tooltip from './components/Tooltip/Tooltip.vue'
|
|
10
15
|
|
|
11
16
|
const tab = ref('components')
|
|
17
|
+
const sidebarOpen = ref(false)
|
|
12
18
|
</script>
|
|
13
19
|
|
|
14
20
|
<template>
|
|
15
|
-
<
|
|
16
|
-
<
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
<div class="vui-sidebar-layout">
|
|
22
|
+
<Sidebar v-model="sidebarOpen" appear floaty>
|
|
23
|
+
<template #header>
|
|
24
|
+
<img class="p-xs" style="background:white;border-radius: 16px;width:40px" src="https://dolansky.dev/backgrounds/star.svg" alt="">
|
|
25
|
+
</template>
|
|
26
|
+
<DropdownItem icon="ph:house">
|
|
27
|
+
Home
|
|
28
|
+
</DropdownItem>
|
|
29
|
+
<DropdownItem icon="ph:signpost">
|
|
30
|
+
Routing
|
|
31
|
+
</DropdownItem>
|
|
32
|
+
<template #footer>
|
|
33
|
+
<Button size="l" expand>
|
|
34
|
+
Sign out
|
|
35
|
+
</Button>
|
|
36
|
+
</template>
|
|
37
|
+
</Sidebar>
|
|
38
|
+
|
|
39
|
+
<main vaul-drawer-wrapper>
|
|
40
|
+
<Button icon="ph:sidebar-simple-bold" square @click="sidebarOpen = !sidebarOpen" />
|
|
41
|
+
<Tabs v-model="tab" expand>
|
|
42
|
+
<Tab id="home" label="Home" icon="ph:house" />
|
|
43
|
+
<Tab id="components" label="Components" />
|
|
44
|
+
<Tab id="typography" label="Typography" />
|
|
45
|
+
</Tabs>
|
|
46
|
+
<div style="margin-bottom: 64px;" />
|
|
47
|
+
<div v-if="tab === 'home'">
|
|
48
|
+
home
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<div v-if="tab === 'components'">
|
|
52
|
+
<h1 class="mb-xl">
|
|
53
|
+
Hii
|
|
54
|
+
<span class="counter text-color-accent">129</span>
|
|
55
|
+
</h1>
|
|
56
|
+
<p>I am down</p>
|
|
25
57
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
58
|
+
<Flex>
|
|
59
|
+
<Avatar size="s" />
|
|
60
|
+
<Avatar size="m" />
|
|
61
|
+
<Avatar size="l" />
|
|
62
|
+
<Avatar :size="80" />
|
|
63
|
+
</Flex>
|
|
32
64
|
|
|
33
|
-
|
|
34
|
-
<Avatar size="s" />
|
|
35
|
-
<Avatar size="m" />
|
|
36
|
-
<Avatar size="l" />
|
|
37
|
-
<Avatar :size="128" />
|
|
38
|
-
</Flex>
|
|
65
|
+
<Divider />
|
|
39
66
|
|
|
40
|
-
|
|
67
|
+
<Tooltip>
|
|
68
|
+
<span class="block mb-xl">Hello world</span>
|
|
69
|
+
<template #tooltip>
|
|
70
|
+
<p>
|
|
71
|
+
Lorem ipsum dolor sit amet consectetur adipisicing elit. Porro, animi! Nobis maxime neque cumque, in a amet voluptatibus tenetur dicta eos delectus illo soluta aliquam voluptatum nulla? In, incidunt asperiores?
|
|
72
|
+
</p>
|
|
73
|
+
</template>
|
|
74
|
+
</Tooltip>
|
|
41
75
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
</
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
<
|
|
77
|
-
</ul>
|
|
78
|
-
<p>As a result, people stopped telling jokes, and the kingdom fell into a gloom. But there was one person who refused to let the king's foolishness get him down: a court jester named Jokester.</p>
|
|
76
|
+
<Dropdown>
|
|
77
|
+
<template #trigger="{ toggle }">
|
|
78
|
+
<Button aria-label="Dropdown menu trigger" square icon="ph:x" @click="toggle" />
|
|
79
|
+
</template>
|
|
80
|
+
<DropdownItem>Hiiii</DropdownItem>
|
|
81
|
+
</Dropdown>
|
|
82
|
+
</div>
|
|
83
|
+
<div v-else-if="tab === 'typography'" class="typeset" :style="{ maxWidth: '688px', margin: 'auto' }">
|
|
84
|
+
<h1>The Joke Tax Chronicles</h1>
|
|
85
|
+
<p>Once upon a time, in a far-off land, there <code>was a very lazy</code> king who spent all day lounging on his throne. One day, his advisors came to him with a problem: the kingdom was running out of money</p>
|
|
86
|
+
<h2>The King's Plan</h2>
|
|
87
|
+
<p>The king thought long and hard, and finally came up with <a href="#">a brilliant plan</a>: he would tax the jokes in the kingdom.</p>
|
|
88
|
+
<blockquote>"After all," he said, "everyone enjoys a good joke, so it's only fair that they should pay for the privilege."</blockquote>
|
|
89
|
+
<h3>The Joke Tax</h3>
|
|
90
|
+
<p>The king's subjects were not amused. They grumbled and complained, but the king was firm:</p>
|
|
91
|
+
<ul class="ul">
|
|
92
|
+
<li>
|
|
93
|
+
I am an item
|
|
94
|
+
<ul>
|
|
95
|
+
<li>1st level of puns: 5 gold coins</li>
|
|
96
|
+
<li>2nd level of jokes: 10 gold coins</li>
|
|
97
|
+
<li>
|
|
98
|
+
3rd level of one-liners : 20 gold coins
|
|
99
|
+
<ul>
|
|
100
|
+
<li>1st level of puns: 5 gold coins</li>
|
|
101
|
+
<li>2nd level of jokes: 10 gold coins</li>
|
|
102
|
+
<li>3rd level of one-liners : 20 gold coins</li>
|
|
103
|
+
</ul>
|
|
104
|
+
</li>
|
|
105
|
+
</ul>
|
|
106
|
+
</li>
|
|
107
|
+
<li>2nd level of jokes: 10 gold coins</li>
|
|
108
|
+
<li>3rd level of one-liners : 20 gold coins</li>
|
|
109
|
+
</ul>
|
|
110
|
+
<p>As a result, people stopped telling jokes, and the kingdom fell into a gloom. But there was one person who refused to let the king's foolishness get him down: a court jester named Jokester.</p>
|
|
79
111
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
112
|
+
<figure>
|
|
113
|
+
<img src="https://i.imgur.com/6WQYrfN.jpeg" alt="">
|
|
114
|
+
<figcaption>Woah look at these mountains</figcaption>
|
|
115
|
+
</figure>
|
|
84
116
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
117
|
+
<h3>Jokester's Revolt</h3>
|
|
118
|
+
<p>Jokester began sneaking into the castle in the middle of the night and leaving jokes all over the place: under the king's pillow, in his soup, even in the royal toilet. The king was furious, but he couldn't seem to stop Jokester.</p>
|
|
119
|
+
<hr>
|
|
120
|
+
<p>And then, one day, the people of the kingdom discovered that the jokes left by Jokester were so funny that they couldn't help but laugh. And once they started laughing, they couldn't stop.</p>
|
|
121
|
+
<ol>
|
|
122
|
+
<li>But the kind was stupid</li>
|
|
123
|
+
<li>He was ratioed 5 times</li>
|
|
124
|
+
</ol>
|
|
125
|
+
<h3>The People's Rebellion</h3>
|
|
126
|
+
<p>The people of the kingdom, feeling uplifted by the laughter, started to tell jokes and puns again, and soon the entire kingdom was in on the joke.</p>
|
|
95
127
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
128
|
+
<table>
|
|
129
|
+
<thead>
|
|
130
|
+
<tr>
|
|
131
|
+
<th>King's Treasury</th>
|
|
132
|
+
<th>People's happiness</th>
|
|
133
|
+
</tr>
|
|
134
|
+
</thead>
|
|
135
|
+
<tbody>
|
|
136
|
+
<tr>
|
|
137
|
+
<td>Empty</td>
|
|
138
|
+
<td>And then, one day, the people of the kingdom discovered that the jokes left by Jokester were so funny that they couldn't help but laugh. And once they started laughing, they couldn't stop.</td>
|
|
139
|
+
</tr>
|
|
140
|
+
<tr>
|
|
141
|
+
<td>Modest</td>
|
|
142
|
+
<td>Satisfied</td>
|
|
143
|
+
</tr>
|
|
144
|
+
<tr>
|
|
145
|
+
<td>Full</td>
|
|
146
|
+
<td>Ecstatic</td>
|
|
147
|
+
</tr>
|
|
148
|
+
</tbody>
|
|
149
|
+
</table>
|
|
150
|
+
<p>The king, seeing how much happier his subjects were, realized the error of his ways and repealed the joke tax. Jokester was declared a hero, and the kingdom lived happily ever after.</p>
|
|
151
|
+
<p>The moral of the story is: never <code>underestimate</code> the power of a good laugh and always be careful of bad ideas.</p>
|
|
120
152
|
|
|
121
|
-
|
|
153
|
+
<pre data-lang="JSON">
|
|
122
154
|
{
|
|
123
155
|
"type": "Feature",
|
|
124
156
|
"properties": {
|
|
@@ -160,6 +192,7 @@ const tab = ref('components')
|
|
|
160
192
|
"id": "ak022eit4nup"
|
|
161
193
|
},
|
|
162
194
|
</pre>
|
|
163
|
-
|
|
164
|
-
|
|
195
|
+
</div>
|
|
196
|
+
</main>
|
|
197
|
+
</div>
|
|
165
198
|
</template>
|
|
@@ -11,12 +11,14 @@ interface Props {
|
|
|
11
11
|
url?: string
|
|
12
12
|
fallback?: string
|
|
13
13
|
icon?: string
|
|
14
|
+
alt?: string
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
const {
|
|
17
18
|
size = Size.m,
|
|
18
19
|
url,
|
|
19
20
|
fallback,
|
|
21
|
+
alt = 'avatar',
|
|
20
22
|
} = defineProps<Props>()
|
|
21
23
|
|
|
22
24
|
const showFallback = ref(false)
|
|
@@ -32,7 +34,12 @@ const showFallback = ref(false)
|
|
|
32
34
|
}),
|
|
33
35
|
}"
|
|
34
36
|
>
|
|
35
|
-
<img
|
|
37
|
+
<img
|
|
38
|
+
v-if="url && !showFallback"
|
|
39
|
+
:alt
|
|
40
|
+
:src="url"
|
|
41
|
+
@error="showFallback = true"
|
|
42
|
+
>
|
|
36
43
|
<strong v-else>
|
|
37
44
|
<template v-if="showFallback">
|
|
38
45
|
{{ fallback }}
|
|
@@ -73,13 +73,10 @@ const w = computed(() => expand ? `${anchorWidth.value}px` : 'initial')
|
|
|
73
73
|
<div
|
|
74
74
|
ref="anchor"
|
|
75
75
|
class="vui-dropdown-trigger-wrap"
|
|
76
|
-
:style="{
|
|
77
|
-
minWidth: mW,
|
|
78
|
-
width: w,
|
|
79
|
-
}"
|
|
80
76
|
role="button"
|
|
81
77
|
:aria-expanded="showMenu"
|
|
82
78
|
:aria-haspopup="true"
|
|
79
|
+
name="Dropdown menu"
|
|
83
80
|
>
|
|
84
81
|
<slot name="trigger" :open :is-open="showMenu" :close :toggle />
|
|
85
82
|
</div>
|
|
@@ -91,6 +88,10 @@ const w = computed(() => expand ? `${anchorWidth.value}px` : 'initial')
|
|
|
91
88
|
:anchor="anchorRef"
|
|
92
89
|
class="vui-dropdown"
|
|
93
90
|
:placement
|
|
91
|
+
:style="{
|
|
92
|
+
minWidth: mW,
|
|
93
|
+
width: w,
|
|
94
|
+
}"
|
|
94
95
|
>
|
|
95
96
|
<slot :open :close :toggle :is-open="showMenu" />
|
|
96
97
|
</Popout>
|
|
@@ -4,6 +4,7 @@ import { Icon } from '@iconify/vue'
|
|
|
4
4
|
interface Props {
|
|
5
5
|
disabled?: boolean
|
|
6
6
|
icon?: string
|
|
7
|
+
iconEnd?: string
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
const props = defineProps<Props>()
|
|
@@ -13,9 +14,7 @@ const props = defineProps<Props>()
|
|
|
13
14
|
<button class="vui-dropdown-item" :class="{ disabled }">
|
|
14
15
|
<!-- This should always be here to offset items which dont have icons -->
|
|
15
16
|
<div class="vui-dropdown-item-icon">
|
|
16
|
-
<
|
|
17
|
-
<Icon v-if="props.icon" :icon="props.icon" />
|
|
18
|
-
</Transition>
|
|
17
|
+
<Icon v-if="props.icon" :icon="props.icon" />
|
|
19
18
|
</div>
|
|
20
19
|
|
|
21
20
|
<div class="vui-dropdown-item-slot">
|
|
@@ -25,5 +24,9 @@ const props = defineProps<Props>()
|
|
|
25
24
|
<div class="vui-dropdown-item-hint">
|
|
26
25
|
<slot name="hint" />
|
|
27
26
|
</div>
|
|
27
|
+
|
|
28
|
+
<div v-if="props.iconEnd" class="vui-dropdown-item-icon">
|
|
29
|
+
<Icon :icon="props.iconEnd" />
|
|
30
|
+
</div>
|
|
28
31
|
</button>
|
|
29
32
|
</template>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
.vui-dropdown-trigger-wrap {
|
|
2
|
-
display: inline-block;
|
|
2
|
+
// display: inline-block;
|
|
3
|
+
width: fit-content;
|
|
3
4
|
}
|
|
4
5
|
|
|
5
6
|
.vui-dropdown {
|
|
@@ -16,6 +17,7 @@
|
|
|
16
17
|
width: fit-content;
|
|
17
18
|
gap: var(--space-xs);
|
|
18
19
|
width: 100%;
|
|
20
|
+
// TODO: make button-height variable global and use it here and everywhere else
|
|
19
21
|
height: 34px;
|
|
20
22
|
border-radius: var(--border-radius-m);
|
|
21
23
|
font-size: var(--font-size-s);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { Space } from '../../shared/types'
|
|
3
3
|
import { computed } from 'vue'
|
|
4
|
+
import { formatUnitValue } from '../../shared/helpers'
|
|
4
5
|
|
|
5
6
|
export interface FlexProps {
|
|
6
7
|
inline?: boolean
|
|
@@ -34,7 +35,10 @@ export interface FlexProps {
|
|
|
34
35
|
const props = defineProps<FlexProps>()
|
|
35
36
|
|
|
36
37
|
// Flex gap
|
|
37
|
-
const ag = computed(() =>
|
|
38
|
+
const ag = computed(() => typeof props.gap === 'number'
|
|
39
|
+
? formatUnitValue(props.gap)
|
|
40
|
+
: `var(--space-${props.gap})`,
|
|
41
|
+
)
|
|
38
42
|
|
|
39
43
|
// Flex direction
|
|
40
44
|
const ad = computed(() => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { Space } from '../../shared/types'
|
|
3
3
|
import { computed } from 'vue'
|
|
4
|
-
import { createArray } from '../../shared/helpers'
|
|
4
|
+
import { createArray, formatUnitValue } from '../../shared/helpers'
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* This component is not meant for complex grids
|
|
@@ -17,7 +17,10 @@ interface Props {
|
|
|
17
17
|
|
|
18
18
|
const props = defineProps<Props>()
|
|
19
19
|
|
|
20
|
-
const ag = computed(() =>
|
|
20
|
+
const ag = computed(() => typeof props.gap === 'number'
|
|
21
|
+
? formatUnitValue(props.gap)
|
|
22
|
+
: `var(--space-${props.gap})`,
|
|
23
|
+
)
|
|
21
24
|
|
|
22
25
|
const aTC = computed(() => {
|
|
23
26
|
if (typeof props.columns === 'number') {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang='ts'>
|
|
2
2
|
import type { Placement, PopoutMaybeElement } from '../../shared/types'
|
|
3
|
-
import { autoPlacement, offset, useFloating } from '@floating-ui/vue'
|
|
3
|
+
import { autoPlacement, offset, shift, useFloating } from '@floating-ui/vue'
|
|
4
4
|
import { toRef, useTemplateRef } from 'vue'
|
|
5
5
|
import './popout.scss'
|
|
6
6
|
|
|
@@ -28,6 +28,9 @@ const { floatingStyles } = useFloating(anchorRef, popoutRef, {
|
|
|
28
28
|
middleware: [
|
|
29
29
|
...(props.placement ? [] : [autoPlacement()]),
|
|
30
30
|
offset(props.offset),
|
|
31
|
+
shift({
|
|
32
|
+
padding: 8,
|
|
33
|
+
}),
|
|
31
34
|
],
|
|
32
35
|
})
|
|
33
36
|
</script>
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
<script setup lang='ts'>
|
|
2
|
+
import { useCssVar, useMouse } from '@vueuse/core'
|
|
3
|
+
import { computed, useSlots, useTemplateRef, watch } from 'vue'
|
|
4
|
+
import './sidebar.scss'
|
|
5
|
+
|
|
6
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
7
|
+
width: 224,
|
|
8
|
+
mini: false,
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
width?: number
|
|
13
|
+
/**
|
|
14
|
+
* Controls wether the sidebar is displayed in full size, or a small version
|
|
15
|
+
*/
|
|
16
|
+
mini?: boolean
|
|
17
|
+
/**
|
|
18
|
+
* Allow sidebar showing up, when user hovers at very left of the screen. The
|
|
19
|
+
* sidebar will apear over content, not pushing anything over
|
|
20
|
+
*/
|
|
21
|
+
appear?: boolean
|
|
22
|
+
/**
|
|
23
|
+
* Add edges of background around sidebar
|
|
24
|
+
*/
|
|
25
|
+
floaty?: boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const sidebar = useTemplateRef('sidebar')
|
|
29
|
+
const open = defineModel<boolean>()
|
|
30
|
+
const slots = useSlots()
|
|
31
|
+
const offset = useCssVar('--vui-sidebar-float-offset', sidebar, {
|
|
32
|
+
initialValue: '8px',
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
const width = computed(() => {
|
|
36
|
+
if (props.mini)
|
|
37
|
+
return `65px`
|
|
38
|
+
if (!props.floaty)
|
|
39
|
+
return `${props.width}px`
|
|
40
|
+
return `calc(${props.width}px - ${offset.value})`
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const slotProps = computed(() => ({
|
|
44
|
+
mini: props.mini,
|
|
45
|
+
floaty: props.floaty,
|
|
46
|
+
width: props.width,
|
|
47
|
+
open,
|
|
48
|
+
}))
|
|
49
|
+
|
|
50
|
+
const { x } = useMouse()
|
|
51
|
+
|
|
52
|
+
let appearActive = true
|
|
53
|
+
watch(x, (pos) => {
|
|
54
|
+
if (!props.appear)
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
if (pos < 20 && !open.value) {
|
|
58
|
+
open.value = true
|
|
59
|
+
appearActive = true
|
|
60
|
+
}
|
|
61
|
+
else if (appearActive && pos > props.width) {
|
|
62
|
+
open.value = false
|
|
63
|
+
appearActive = false
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
</script>
|
|
67
|
+
|
|
68
|
+
<template>
|
|
69
|
+
<div class="vui-sidebar-outer" :style="{ width }" :class="{ open }">
|
|
70
|
+
<aside ref="sidebar" class="vui-sidebar" :class="{ open, floaty: props.floaty, mini: props.mini }" :style="{ width: `${props.mini ? 65 : props.width}px` }">
|
|
71
|
+
<div v-if="slots.header" class="vui-sidebar-header">
|
|
72
|
+
<slot name="header" v-bind="slotProps" />
|
|
73
|
+
</div>
|
|
74
|
+
<div class="vui-sidebar-content">
|
|
75
|
+
<div class="vui-sidebar-content-wrap">
|
|
76
|
+
<slot v-bind="slotProps" />
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<div v-if="slots.footer" class="vui-sidebar-footer">
|
|
81
|
+
<slot name="footer" v-bind="slotProps" />
|
|
82
|
+
</div>
|
|
83
|
+
</aside>
|
|
84
|
+
</div>
|
|
85
|
+
</template>
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// TODO: flesh out better
|
|
2
|
+
.vui-sidebar-layout {
|
|
3
|
+
display: flex;
|
|
4
|
+
flex-wrap: nowrap;
|
|
5
|
+
gap: 32px;
|
|
6
|
+
position: relative;
|
|
7
|
+
|
|
8
|
+
main {
|
|
9
|
+
flex: 1;
|
|
10
|
+
padding: 2rem;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.vui-sidebar-outer {
|
|
14
|
+
transition: var(--transition-slow);
|
|
15
|
+
|
|
16
|
+
&:not(.open) {
|
|
17
|
+
width: 0px !important;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.vui-sidebar {
|
|
23
|
+
--vui-sidebar-float-offset: 8px;
|
|
24
|
+
display: flex;
|
|
25
|
+
flex-direction: column;
|
|
26
|
+
gap: var(--space-sm);
|
|
27
|
+
height: 100vh;
|
|
28
|
+
width: 224px;
|
|
29
|
+
position: fixed;
|
|
30
|
+
top: 0;
|
|
31
|
+
z-index: 50;
|
|
32
|
+
background-color: var(--color-bg);
|
|
33
|
+
border-right: 1px solid var(--color-border);
|
|
34
|
+
transition: var(--transition-slow);
|
|
35
|
+
transform: translateX(-100%);
|
|
36
|
+
will-change: transform;
|
|
37
|
+
|
|
38
|
+
&.open {
|
|
39
|
+
transform: translateX(0);
|
|
40
|
+
|
|
41
|
+
&.floaty {
|
|
42
|
+
transform: translateX(0);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
&.floaty {
|
|
47
|
+
top: var(--vui-sidebar-float-offset);
|
|
48
|
+
left: var(--vui-sidebar-float-offset);
|
|
49
|
+
bottom: var(--vui-sidebar-float-offset);
|
|
50
|
+
height: calc(100vh - calc(var(--vui-sidebar-float-offset) * 2));
|
|
51
|
+
border-radius: var(--border-radius-m);
|
|
52
|
+
border: 1px solid var(--color-border);
|
|
53
|
+
transform: translateX(calc(-100% - calc(var(--vui-sidebar-float-offset) * 2)));
|
|
54
|
+
box-shadow: var(--box-shadow);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.vui-sidebar-header,
|
|
58
|
+
.vui-sidebar-footer,
|
|
59
|
+
.vui-sidebar-content .vui-sidebar-content-wrap {
|
|
60
|
+
padding: var(--space-s);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.vui-sidebar-header:not(:only-child) {
|
|
64
|
+
padding-bottom: 0;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.vui-sidebar-footer:not(:only-child) {
|
|
68
|
+
padding-top: 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.vui-sidebar-content {
|
|
72
|
+
flex: 1;
|
|
73
|
+
position: relative;
|
|
74
|
+
|
|
75
|
+
.vui-sidebar-content-wrap {
|
|
76
|
+
position: absolute;
|
|
77
|
+
inset: 0;
|
|
78
|
+
overflow-y: auto;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Note: elements in sidebar (& mini) should have a slight modification
|
|
83
|
+
|
|
84
|
+
// Accordion
|
|
85
|
+
// Does not have a border under content
|
|
86
|
+
.vui-accordion {
|
|
87
|
+
border-bottom: 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// DropdownItems (sidebar items
|
|
91
|
+
// Should have a bit less spacing between slots
|
|
92
|
+
.vui-dropdown-item {
|
|
93
|
+
padding-inline: var(--space-xs);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
&.mini {
|
|
97
|
+
.vui-dropdown-item .vui-dropdown-item-icon {
|
|
98
|
+
width: 20px;
|
|
99
|
+
height: 20px;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.vui-dropdown-item,
|
|
103
|
+
.vui-button {
|
|
104
|
+
--button-height: 40px;
|
|
105
|
+
width: var(--button-height) !important;
|
|
106
|
+
height: var(--button-height) !important;
|
|
107
|
+
justify-content: center;
|
|
108
|
+
align-items: center;
|
|
109
|
+
|
|
110
|
+
svg {
|
|
111
|
+
width: 20px;
|
|
112
|
+
height: 20px;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.vui-dropdown-item-slot,
|
|
116
|
+
.vui-dropdown-item-hint,
|
|
117
|
+
.vui-button-slot-end,
|
|
118
|
+
.vui-button-slot-start {
|
|
119
|
+
display: none;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { TabProps } from './Tab.vue'
|
|
3
|
-
import {
|
|
3
|
+
import { useResizeObserver } from '@vueuse/core'
|
|
4
4
|
import { onMounted, useTemplateRef, type VNode, watch } from 'vue'
|
|
5
5
|
import './tabs.scss'
|
|
6
6
|
|
|
@@ -42,7 +42,7 @@ function computeUnderlinePosition() {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
onMounted(() => {
|
|
45
|
-
|
|
45
|
+
useResizeObserver(tabsRef, computeUnderlinePosition)
|
|
46
46
|
|
|
47
47
|
watch(
|
|
48
48
|
[active, () => expand],
|