@mkatogui/uds-vue 0.2.1
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 +102 -0
- package/package.json +27 -0
- package/src/components/UdsAccordion.vue +82 -0
- package/src/components/UdsAlert.vue +61 -0
- package/src/components/UdsAvatar.vue +59 -0
- package/src/components/UdsBadge.vue +48 -0
- package/src/components/UdsBreadcrumb.vue +73 -0
- package/src/components/UdsButton.vue +43 -0
- package/src/components/UdsCard.vue +49 -0
- package/src/components/UdsCheckbox.vue +56 -0
- package/src/components/UdsCodeBlock.vue +86 -0
- package/src/components/UdsCommandPalette.vue +144 -0
- package/src/components/UdsDataTable.vue +142 -0
- package/src/components/UdsDatePicker.vue +69 -0
- package/src/components/UdsDropdown.vue +132 -0
- package/src/components/UdsFileUpload.vue +148 -0
- package/src/components/UdsFooter.vue +39 -0
- package/src/components/UdsHero.vue +44 -0
- package/src/components/UdsInput.vue +95 -0
- package/src/components/UdsModal.vue +114 -0
- package/src/components/UdsNavbar.vue +57 -0
- package/src/components/UdsPagination.vue +96 -0
- package/src/components/UdsPricing.vue +58 -0
- package/src/components/UdsProgress.vue +92 -0
- package/src/components/UdsRadio.vue +56 -0
- package/src/components/UdsSelect.vue +84 -0
- package/src/components/UdsSideNav.vue +102 -0
- package/src/components/UdsSkeleton.vue +51 -0
- package/src/components/UdsSocialProof.vue +58 -0
- package/src/components/UdsTabs.vue +106 -0
- package/src/components/UdsTestimonial.vue +57 -0
- package/src/components/UdsToast.vue +70 -0
- package/src/components/UdsToggle.vue +60 -0
- package/src/components/UdsTooltip.vue +62 -0
- package/src/index.ts +32 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
|
|
4
|
+
interface NavItem {
|
|
5
|
+
label: string
|
|
6
|
+
href?: string
|
|
7
|
+
icon?: string
|
|
8
|
+
active?: boolean
|
|
9
|
+
children?: NavItem[]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface Section {
|
|
13
|
+
title: string
|
|
14
|
+
items: NavItem[]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface Props {
|
|
18
|
+
variant?: 'default' | 'collapsed' | 'with-sections'
|
|
19
|
+
collapsed?: boolean
|
|
20
|
+
items?: NavItem[]
|
|
21
|
+
sections?: Section[]
|
|
22
|
+
activeItem?: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
26
|
+
variant: 'default',
|
|
27
|
+
items: () => [],
|
|
28
|
+
sections: () => [],
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const emit = defineEmits<{
|
|
32
|
+
navigate: [href: string]
|
|
33
|
+
}>()
|
|
34
|
+
|
|
35
|
+
const classes = computed(() =>
|
|
36
|
+
[
|
|
37
|
+
'uds-side-nav',
|
|
38
|
+
`uds-side-nav--${props.variant}`,
|
|
39
|
+
props.collapsed && 'uds-side-nav--collapsed',
|
|
40
|
+
]
|
|
41
|
+
.filter(Boolean)
|
|
42
|
+
.join(' ')
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
function handleNavigate(href: string) {
|
|
46
|
+
emit('navigate', href)
|
|
47
|
+
}
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<template>
|
|
51
|
+
<nav :class="classes" aria-label="Side navigation">
|
|
52
|
+
<template v-if="variant === 'with-sections' && sections.length">
|
|
53
|
+
<div v-for="(section, si) in sections" :key="si" class="uds-side-nav__section">
|
|
54
|
+
<h3 class="uds-side-nav__section-title">{{ section.title }}</h3>
|
|
55
|
+
<ul class="uds-side-nav__list">
|
|
56
|
+
<li v-for="(item, i) in section.items" :key="i" class="uds-side-nav__item">
|
|
57
|
+
<a
|
|
58
|
+
:href="item.href || '#'"
|
|
59
|
+
class="uds-side-nav__link"
|
|
60
|
+
:class="{ 'uds-side-nav__link--active': item.active || item.href === activeItem }"
|
|
61
|
+
:aria-current="item.active || item.href === activeItem ? 'page' : undefined"
|
|
62
|
+
@click.prevent="handleNavigate(item.href || '')"
|
|
63
|
+
>
|
|
64
|
+
<span v-if="item.icon" class="uds-side-nav__icon" aria-hidden="true">{{ item.icon }}</span>
|
|
65
|
+
<span v-if="!collapsed" class="uds-side-nav__label">{{ item.label }}</span>
|
|
66
|
+
</a>
|
|
67
|
+
</li>
|
|
68
|
+
</ul>
|
|
69
|
+
</div>
|
|
70
|
+
</template>
|
|
71
|
+
<template v-else>
|
|
72
|
+
<ul class="uds-side-nav__list">
|
|
73
|
+
<li v-for="(item, i) in items" :key="i" class="uds-side-nav__item">
|
|
74
|
+
<a
|
|
75
|
+
:href="item.href || '#'"
|
|
76
|
+
class="uds-side-nav__link"
|
|
77
|
+
:class="{ 'uds-side-nav__link--active': item.active || item.href === activeItem }"
|
|
78
|
+
:aria-current="item.active || item.href === activeItem ? 'page' : undefined"
|
|
79
|
+
:aria-expanded="item.children?.length ? undefined : undefined"
|
|
80
|
+
@click.prevent="handleNavigate(item.href || '')"
|
|
81
|
+
>
|
|
82
|
+
<span v-if="item.icon" class="uds-side-nav__icon" aria-hidden="true">{{ item.icon }}</span>
|
|
83
|
+
<span v-if="!collapsed" class="uds-side-nav__label">{{ item.label }}</span>
|
|
84
|
+
</a>
|
|
85
|
+
<ul v-if="item.children?.length && !collapsed" class="uds-side-nav__sublist">
|
|
86
|
+
<li v-for="(child, ci) in item.children" :key="ci" class="uds-side-nav__subitem">
|
|
87
|
+
<a
|
|
88
|
+
:href="child.href || '#'"
|
|
89
|
+
class="uds-side-nav__sublink"
|
|
90
|
+
:class="{ 'uds-side-nav__sublink--active': child.active || child.href === activeItem }"
|
|
91
|
+
:aria-current="child.active || child.href === activeItem ? 'page' : undefined"
|
|
92
|
+
@click.prevent="handleNavigate(child.href || '')"
|
|
93
|
+
>
|
|
94
|
+
{{ child.label }}
|
|
95
|
+
</a>
|
|
96
|
+
</li>
|
|
97
|
+
</ul>
|
|
98
|
+
</li>
|
|
99
|
+
</ul>
|
|
100
|
+
</template>
|
|
101
|
+
</nav>
|
|
102
|
+
</template>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
variant?: 'text' | 'card' | 'avatar' | 'table'
|
|
6
|
+
size?: 'sm' | 'md' | 'lg'
|
|
7
|
+
lines?: number
|
|
8
|
+
animated?: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
12
|
+
variant: 'text',
|
|
13
|
+
size: 'md',
|
|
14
|
+
lines: 3,
|
|
15
|
+
animated: true,
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const classes = computed(() =>
|
|
19
|
+
[
|
|
20
|
+
'uds-skeleton',
|
|
21
|
+
`uds-skeleton--${props.variant}`,
|
|
22
|
+
`uds-skeleton--${props.size}`,
|
|
23
|
+
props.animated && 'uds-skeleton--animated',
|
|
24
|
+
]
|
|
25
|
+
.filter(Boolean)
|
|
26
|
+
.join(' ')
|
|
27
|
+
)
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<template>
|
|
31
|
+
<div :class="classes" aria-busy="true" aria-hidden="true">
|
|
32
|
+
<template v-if="variant === 'text'">
|
|
33
|
+
<div v-for="i in lines" :key="i" class="uds-skeleton__line" />
|
|
34
|
+
</template>
|
|
35
|
+
<template v-else-if="variant === 'avatar'">
|
|
36
|
+
<div class="uds-skeleton__circle" />
|
|
37
|
+
</template>
|
|
38
|
+
<template v-else-if="variant === 'card'">
|
|
39
|
+
<div class="uds-skeleton__image" />
|
|
40
|
+
<div class="uds-skeleton__line" />
|
|
41
|
+
<div class="uds-skeleton__line uds-skeleton__line--short" />
|
|
42
|
+
</template>
|
|
43
|
+
<template v-else-if="variant === 'table'">
|
|
44
|
+
<div v-for="i in lines" :key="i" class="uds-skeleton__row">
|
|
45
|
+
<div class="uds-skeleton__cell" />
|
|
46
|
+
<div class="uds-skeleton__cell" />
|
|
47
|
+
<div class="uds-skeleton__cell" />
|
|
48
|
+
</div>
|
|
49
|
+
</template>
|
|
50
|
+
</div>
|
|
51
|
+
</template>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
|
|
4
|
+
interface Logo {
|
|
5
|
+
src: string
|
|
6
|
+
alt: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface Stat {
|
|
10
|
+
value: string
|
|
11
|
+
label: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface Props {
|
|
15
|
+
variant?: 'logo-strip' | 'stats-counter' | 'testimonial-mini' | 'combined'
|
|
16
|
+
size?: 'standard' | 'compact'
|
|
17
|
+
logos?: Logo[]
|
|
18
|
+
stats?: Stat[]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
22
|
+
variant: 'logo-strip',
|
|
23
|
+
size: 'standard',
|
|
24
|
+
logos: () => [],
|
|
25
|
+
stats: () => [],
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const classes = computed(() =>
|
|
29
|
+
[
|
|
30
|
+
'uds-social-proof',
|
|
31
|
+
`uds-social-proof--${props.variant}`,
|
|
32
|
+
`uds-social-proof--${props.size}`,
|
|
33
|
+
]
|
|
34
|
+
.filter(Boolean)
|
|
35
|
+
.join(' ')
|
|
36
|
+
)
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<template>
|
|
40
|
+
<div :class="classes">
|
|
41
|
+
<div v-if="logos.length" class="uds-social-proof__logos">
|
|
42
|
+
<img
|
|
43
|
+
v-for="(logo, i) in logos"
|
|
44
|
+
:key="i"
|
|
45
|
+
:src="logo.src"
|
|
46
|
+
:alt="logo.alt"
|
|
47
|
+
class="uds-social-proof__logo"
|
|
48
|
+
/>
|
|
49
|
+
</div>
|
|
50
|
+
<div v-if="stats.length" class="uds-social-proof__stats">
|
|
51
|
+
<div v-for="(stat, i) in stats" :key="i" class="uds-social-proof__stat">
|
|
52
|
+
<span class="uds-social-proof__stat-value">{{ stat.value }}</span>
|
|
53
|
+
<span class="uds-social-proof__stat-label">{{ stat.label }}</span>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
<slot />
|
|
57
|
+
</div>
|
|
58
|
+
</template>
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, ref, useId } from 'vue'
|
|
3
|
+
|
|
4
|
+
interface TabItem {
|
|
5
|
+
label: string
|
|
6
|
+
disabled?: boolean
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
variant?: 'line' | 'pill' | 'segmented'
|
|
11
|
+
size?: 'sm' | 'md' | 'lg'
|
|
12
|
+
tabs: TabItem[]
|
|
13
|
+
defaultActive?: number
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
17
|
+
variant: 'line',
|
|
18
|
+
size: 'md',
|
|
19
|
+
defaultActive: 0,
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const emit = defineEmits<{
|
|
23
|
+
change: [index: number]
|
|
24
|
+
}>()
|
|
25
|
+
|
|
26
|
+
const activeTab = ref(props.defaultActive)
|
|
27
|
+
const baseId = useId()
|
|
28
|
+
const tabRefs = ref<(HTMLElement | null)[]>([])
|
|
29
|
+
|
|
30
|
+
const classes = computed(() =>
|
|
31
|
+
[
|
|
32
|
+
'uds-tabs',
|
|
33
|
+
`uds-tabs--${props.variant}`,
|
|
34
|
+
`uds-tabs--${props.size}`,
|
|
35
|
+
]
|
|
36
|
+
.filter(Boolean)
|
|
37
|
+
.join(' ')
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
function selectTab(index: number) {
|
|
41
|
+
if (props.tabs[index]?.disabled) return
|
|
42
|
+
activeTab.value = index
|
|
43
|
+
emit('change', index)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function handleKeydown(e: KeyboardEvent) {
|
|
47
|
+
const enabledIndices = props.tabs
|
|
48
|
+
.map((t, i) => (t.disabled ? -1 : i))
|
|
49
|
+
.filter((i) => i >= 0)
|
|
50
|
+
const currentPos = enabledIndices.indexOf(activeTab.value)
|
|
51
|
+
let nextIndex: number | undefined
|
|
52
|
+
|
|
53
|
+
if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
|
|
54
|
+
e.preventDefault()
|
|
55
|
+
nextIndex = enabledIndices[(currentPos + 1) % enabledIndices.length]
|
|
56
|
+
} else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
|
|
57
|
+
e.preventDefault()
|
|
58
|
+
nextIndex = enabledIndices[(currentPos - 1 + enabledIndices.length) % enabledIndices.length]
|
|
59
|
+
} else if (e.key === 'Home') {
|
|
60
|
+
e.preventDefault()
|
|
61
|
+
nextIndex = enabledIndices[0]
|
|
62
|
+
} else if (e.key === 'End') {
|
|
63
|
+
e.preventDefault()
|
|
64
|
+
nextIndex = enabledIndices[enabledIndices.length - 1]
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (nextIndex !== undefined) {
|
|
68
|
+
selectTab(nextIndex)
|
|
69
|
+
tabRefs.value[nextIndex]?.focus()
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
</script>
|
|
73
|
+
|
|
74
|
+
<template>
|
|
75
|
+
<div :class="classes">
|
|
76
|
+
<div class="uds-tabs__list" role="tablist" aria-orientation="horizontal" @keydown="handleKeydown">
|
|
77
|
+
<button
|
|
78
|
+
v-for="(tab, i) in tabs"
|
|
79
|
+
:key="i"
|
|
80
|
+
:ref="(el) => { tabRefs[i] = el as HTMLElement }"
|
|
81
|
+
class="uds-tabs__trigger"
|
|
82
|
+
role="tab"
|
|
83
|
+
:id="`${baseId}-tab-${i}`"
|
|
84
|
+
:aria-selected="activeTab === i"
|
|
85
|
+
:aria-controls="`${baseId}-panel-${i}`"
|
|
86
|
+
:tabindex="activeTab === i ? 0 : -1"
|
|
87
|
+
:disabled="tab.disabled"
|
|
88
|
+
@click="selectTab(i)"
|
|
89
|
+
>
|
|
90
|
+
{{ tab.label }}
|
|
91
|
+
</button>
|
|
92
|
+
</div>
|
|
93
|
+
<div
|
|
94
|
+
v-for="(tab, i) in tabs"
|
|
95
|
+
:key="i"
|
|
96
|
+
class="uds-tabs__panel"
|
|
97
|
+
role="tabpanel"
|
|
98
|
+
:id="`${baseId}-panel-${i}`"
|
|
99
|
+
:aria-labelledby="`${baseId}-tab-${i}`"
|
|
100
|
+
:hidden="activeTab !== i"
|
|
101
|
+
:tabindex="0"
|
|
102
|
+
>
|
|
103
|
+
<slot :name="`tab-${i}`" />
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
</template>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
variant?: 'quote-card' | 'video' | 'metric' | 'carousel'
|
|
6
|
+
size?: 'sm' | 'md' | 'lg'
|
|
7
|
+
quote?: string
|
|
8
|
+
name?: string
|
|
9
|
+
title?: string
|
|
10
|
+
company?: string
|
|
11
|
+
avatar?: string
|
|
12
|
+
rating?: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
16
|
+
variant: 'quote-card',
|
|
17
|
+
size: 'md',
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const classes = computed(() =>
|
|
21
|
+
[
|
|
22
|
+
'uds-testimonial',
|
|
23
|
+
`uds-testimonial--${props.variant}`,
|
|
24
|
+
`uds-testimonial--${props.size}`,
|
|
25
|
+
]
|
|
26
|
+
.filter(Boolean)
|
|
27
|
+
.join(' ')
|
|
28
|
+
)
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<template>
|
|
32
|
+
<figure :class="classes">
|
|
33
|
+
<blockquote v-if="quote" class="uds-testimonial__quote">
|
|
34
|
+
<p>{{ quote }}</p>
|
|
35
|
+
</blockquote>
|
|
36
|
+
<slot />
|
|
37
|
+
<figcaption class="uds-testimonial__attribution">
|
|
38
|
+
<img
|
|
39
|
+
v-if="avatar"
|
|
40
|
+
:src="avatar"
|
|
41
|
+
:alt="`Photo of ${name || ''}`"
|
|
42
|
+
class="uds-testimonial__avatar"
|
|
43
|
+
/>
|
|
44
|
+
<div class="uds-testimonial__info">
|
|
45
|
+
<cite v-if="name" class="uds-testimonial__name">{{ name }}</cite>
|
|
46
|
+
<span v-if="title || company" class="uds-testimonial__role">
|
|
47
|
+
{{ [title, company].filter(Boolean).join(', ') }}
|
|
48
|
+
</span>
|
|
49
|
+
</div>
|
|
50
|
+
<div v-if="rating" class="uds-testimonial__rating" :aria-label="`${rating} out of 5 stars`">
|
|
51
|
+
<span v-for="i in 5" :key="i" :class="i <= rating ? 'uds-testimonial__star--filled' : 'uds-testimonial__star'" aria-hidden="true">
|
|
52
|
+
★
|
|
53
|
+
</span>
|
|
54
|
+
</div>
|
|
55
|
+
</figcaption>
|
|
56
|
+
</figure>
|
|
57
|
+
</template>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, onMounted, ref } from 'vue'
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
variant?: 'success' | 'error' | 'warning' | 'info' | 'neutral'
|
|
6
|
+
size?: 'sm' | 'md' | 'lg'
|
|
7
|
+
message?: string
|
|
8
|
+
duration?: number
|
|
9
|
+
action?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
13
|
+
variant: 'info',
|
|
14
|
+
size: 'md',
|
|
15
|
+
duration: 5000,
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const emit = defineEmits<{
|
|
19
|
+
dismiss: []
|
|
20
|
+
action: []
|
|
21
|
+
}>()
|
|
22
|
+
|
|
23
|
+
const visible = ref(true)
|
|
24
|
+
|
|
25
|
+
const classes = computed(() =>
|
|
26
|
+
[
|
|
27
|
+
'uds-toast',
|
|
28
|
+
`uds-toast--${props.variant}`,
|
|
29
|
+
`uds-toast--${props.size}`,
|
|
30
|
+
visible.value && 'uds-toast--visible',
|
|
31
|
+
]
|
|
32
|
+
.filter(Boolean)
|
|
33
|
+
.join(' ')
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
const toastRole = computed(() =>
|
|
37
|
+
props.variant === 'error' || props.variant === 'warning' ? 'alert' : 'status'
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
function dismiss() {
|
|
41
|
+
visible.value = false
|
|
42
|
+
emit('dismiss')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
onMounted(() => {
|
|
46
|
+
if (props.duration > 0) {
|
|
47
|
+
setTimeout(dismiss, props.duration)
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<template>
|
|
53
|
+
<div v-if="visible" :class="classes" :role="toastRole">
|
|
54
|
+
<p class="uds-toast__message">
|
|
55
|
+
<slot>{{ message }}</slot>
|
|
56
|
+
</p>
|
|
57
|
+
<button
|
|
58
|
+
v-if="action"
|
|
59
|
+
class="uds-toast__action"
|
|
60
|
+
@click="emit('action')"
|
|
61
|
+
>
|
|
62
|
+
{{ action }}
|
|
63
|
+
</button>
|
|
64
|
+
<button class="uds-toast__dismiss" aria-label="Dismiss notification" @click="dismiss">
|
|
65
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
|
66
|
+
<path d="M18 6L6 18M6 6l12 12" />
|
|
67
|
+
</svg>
|
|
68
|
+
</button>
|
|
69
|
+
</div>
|
|
70
|
+
</template>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, useId } from 'vue'
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
variant?: 'standard' | 'with-label'
|
|
6
|
+
checked?: boolean
|
|
7
|
+
disabled?: boolean
|
|
8
|
+
label?: string
|
|
9
|
+
modelValue?: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
13
|
+
variant: 'standard',
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
const emit = defineEmits<{
|
|
17
|
+
'update:modelValue': [value: boolean]
|
|
18
|
+
change: [value: boolean]
|
|
19
|
+
}>()
|
|
20
|
+
|
|
21
|
+
const toggleId = useId()
|
|
22
|
+
|
|
23
|
+
const classes = computed(() =>
|
|
24
|
+
[
|
|
25
|
+
'uds-toggle',
|
|
26
|
+
`uds-toggle--${props.variant}`,
|
|
27
|
+
props.disabled && 'uds-toggle--disabled',
|
|
28
|
+
(props.modelValue ?? props.checked) && 'uds-toggle--on',
|
|
29
|
+
]
|
|
30
|
+
.filter(Boolean)
|
|
31
|
+
.join(' ')
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
function handleClick() {
|
|
35
|
+
if (props.disabled) return
|
|
36
|
+
const next = !(props.modelValue ?? props.checked)
|
|
37
|
+
emit('update:modelValue', next)
|
|
38
|
+
emit('change', next)
|
|
39
|
+
}
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<template>
|
|
43
|
+
<div :class="classes">
|
|
44
|
+
<button
|
|
45
|
+
:id="toggleId"
|
|
46
|
+
type="button"
|
|
47
|
+
role="switch"
|
|
48
|
+
class="uds-toggle__track"
|
|
49
|
+
:aria-checked="modelValue ?? checked ?? false"
|
|
50
|
+
:aria-label="label"
|
|
51
|
+
:disabled="disabled"
|
|
52
|
+
@click="handleClick"
|
|
53
|
+
>
|
|
54
|
+
<span class="uds-toggle__thumb" aria-hidden="true" />
|
|
55
|
+
</button>
|
|
56
|
+
<label v-if="variant === 'with-label' && label" :for="toggleId" class="uds-toggle__label">
|
|
57
|
+
{{ label }}
|
|
58
|
+
</label>
|
|
59
|
+
</div>
|
|
60
|
+
</template>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, ref, useId } from 'vue'
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
variant?: 'simple' | 'rich'
|
|
6
|
+
size?: 'sm' | 'md'
|
|
7
|
+
content?: string
|
|
8
|
+
position?: 'top' | 'bottom' | 'left' | 'right'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
12
|
+
variant: 'simple',
|
|
13
|
+
size: 'sm',
|
|
14
|
+
position: 'top',
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const visible = ref(false)
|
|
18
|
+
const tooltipId = useId()
|
|
19
|
+
|
|
20
|
+
const classes = computed(() =>
|
|
21
|
+
[
|
|
22
|
+
'uds-tooltip',
|
|
23
|
+
`uds-tooltip--${props.variant}`,
|
|
24
|
+
`uds-tooltip--${props.size}`,
|
|
25
|
+
`uds-tooltip--${props.position}`,
|
|
26
|
+
visible.value && 'uds-tooltip--visible',
|
|
27
|
+
]
|
|
28
|
+
.filter(Boolean)
|
|
29
|
+
.join(' ')
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
function show() {
|
|
33
|
+
visible.value = true
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function hide() {
|
|
37
|
+
visible.value = false
|
|
38
|
+
}
|
|
39
|
+
</script>
|
|
40
|
+
|
|
41
|
+
<template>
|
|
42
|
+
<div :class="classes">
|
|
43
|
+
<div
|
|
44
|
+
class="uds-tooltip__trigger"
|
|
45
|
+
:aria-describedby="tooltipId"
|
|
46
|
+
@mouseenter="show"
|
|
47
|
+
@mouseleave="hide"
|
|
48
|
+
@focusin="show"
|
|
49
|
+
@focusout="hide"
|
|
50
|
+
>
|
|
51
|
+
<slot />
|
|
52
|
+
</div>
|
|
53
|
+
<div
|
|
54
|
+
v-if="visible"
|
|
55
|
+
:id="tooltipId"
|
|
56
|
+
role="tooltip"
|
|
57
|
+
class="uds-tooltip__content"
|
|
58
|
+
>
|
|
59
|
+
<slot name="content">{{ content }}</slot>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</template>
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export { default as UdsButton } from './components/UdsButton.vue'
|
|
2
|
+
export { default as UdsNavbar } from './components/UdsNavbar.vue'
|
|
3
|
+
export { default as UdsHero } from './components/UdsHero.vue'
|
|
4
|
+
export { default as UdsCard } from './components/UdsCard.vue'
|
|
5
|
+
export { default as UdsPricing } from './components/UdsPricing.vue'
|
|
6
|
+
export { default as UdsSocialProof } from './components/UdsSocialProof.vue'
|
|
7
|
+
export { default as UdsTestimonial } from './components/UdsTestimonial.vue'
|
|
8
|
+
export { default as UdsFooter } from './components/UdsFooter.vue'
|
|
9
|
+
export { default as UdsCodeBlock } from './components/UdsCodeBlock.vue'
|
|
10
|
+
export { default as UdsModal } from './components/UdsModal.vue'
|
|
11
|
+
export { default as UdsInput } from './components/UdsInput.vue'
|
|
12
|
+
export { default as UdsSelect } from './components/UdsSelect.vue'
|
|
13
|
+
export { default as UdsCheckbox } from './components/UdsCheckbox.vue'
|
|
14
|
+
export { default as UdsRadio } from './components/UdsRadio.vue'
|
|
15
|
+
export { default as UdsToggle } from './components/UdsToggle.vue'
|
|
16
|
+
export { default as UdsAlert } from './components/UdsAlert.vue'
|
|
17
|
+
export { default as UdsBadge } from './components/UdsBadge.vue'
|
|
18
|
+
export { default as UdsTabs } from './components/UdsTabs.vue'
|
|
19
|
+
export { default as UdsAccordion } from './components/UdsAccordion.vue'
|
|
20
|
+
export { default as UdsBreadcrumb } from './components/UdsBreadcrumb.vue'
|
|
21
|
+
export { default as UdsTooltip } from './components/UdsTooltip.vue'
|
|
22
|
+
export { default as UdsDropdown } from './components/UdsDropdown.vue'
|
|
23
|
+
export { default as UdsAvatar } from './components/UdsAvatar.vue'
|
|
24
|
+
export { default as UdsSkeleton } from './components/UdsSkeleton.vue'
|
|
25
|
+
export { default as UdsToast } from './components/UdsToast.vue'
|
|
26
|
+
export { default as UdsPagination } from './components/UdsPagination.vue'
|
|
27
|
+
export { default as UdsDataTable } from './components/UdsDataTable.vue'
|
|
28
|
+
export { default as UdsDatePicker } from './components/UdsDatePicker.vue'
|
|
29
|
+
export { default as UdsCommandPalette } from './components/UdsCommandPalette.vue'
|
|
30
|
+
export { default as UdsProgress } from './components/UdsProgress.vue'
|
|
31
|
+
export { default as UdsSideNav } from './components/UdsSideNav.vue'
|
|
32
|
+
export { default as UdsFileUpload } from './components/UdsFileUpload.vue'
|