@soubiran/ui 0.9.2 → 0.10.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.
@@ -1,138 +0,0 @@
1
- <script lang="ts">
2
- import type { FunctionalComponent, SVGAttributes } from 'vue'
3
- import { tv } from 'tailwind-variants'
4
- import { computed } from 'vue'
5
- import discord from '~icons/simple-icons/discord'
6
- import github from '~icons/simple-icons/github'
7
- import linkedin from '~icons/simple-icons/linkedin'
8
- import twitch from '~icons/simple-icons/twitch'
9
- import x from '~icons/simple-icons/x'
10
- import useUmami from '../composables/useUmami'
11
- import Container from './Container.vue'
12
-
13
- const header = tv({
14
- slots: {
15
- base: 'h-(--ui-header-height) flex flex-row gap-4 items-center justify-end',
16
- link: 'p-0 text-dimmed',
17
- },
18
- })
19
-
20
- export interface HeaderProps {
21
- links: {
22
- icon: FunctionalComponent<SVGAttributes>
23
- label: string
24
- to?: string
25
- href?: string
26
- target?: string
27
- rel?: string
28
- }[]
29
- class?: any
30
- ui?: Partial<typeof header.slots>
31
- }
32
- export interface HeaderEmits {}
33
- export interface HeaderSlots {}
34
- </script>
35
-
36
- <script lang="ts" setup>
37
- const props = defineProps<HeaderProps>()
38
- defineEmits<HeaderEmits>()
39
- defineSlots<HeaderSlots>()
40
-
41
- const { track } = useUmami()
42
- function trackClick(label: string) {
43
- track('header_click', { label })
44
- }
45
-
46
- const ui = computed(() => header())
47
- </script>
48
-
49
- <template>
50
- <Container :ui="{ inner: 'max-w-5xl' }">
51
- <header :class="ui.base({ class: [props.ui?.base, props.class] })">
52
- <UTooltip
53
- v-for="link of props.links"
54
- :key="link.label"
55
- :text="link.label"
56
- >
57
- <UButton
58
- variant="link"
59
- color="neutral"
60
- :to="link.to"
61
- :href="link.href"
62
- :aria-label="link.label"
63
- :icon="link.icon"
64
- :target="link.target"
65
- :rel="link.rel"
66
- :class="ui.link({ class: props.ui?.link })"
67
- @click="trackClick(link.label)"
68
- />
69
- </UTooltip>
70
- <USeparator orientation="vertical" class="h-5" />
71
- <UTooltip text="LinkedIn">
72
- <UButton
73
- href="https://www.linkedin.com/in/esteban25/"
74
- target="_blank"
75
- variant="link"
76
- color="neutral"
77
- aria-label="LinkedIn"
78
- :icon="linkedin"
79
- :class="ui.link({ class: props.ui?.link })"
80
- :ui="{ leadingIcon: 'size-4' }"
81
- @click="trackClick('LinkedIn')"
82
- />
83
- </UTooltip>
84
- <UTooltip text="X">
85
- <UButton
86
- href="https://x.com/soubiran_"
87
- target="_blank"
88
- variant="link"
89
- color="neutral"
90
- aria-label="X"
91
- :icon="x"
92
- :class="ui.link({ class: props.ui?.link })"
93
- :ui="{ leadingIcon: 'size-4' }"
94
- @click="trackClick('X')"
95
- />
96
- </UTooltip>
97
- <UTooltip text="Twitch">
98
- <UButton
99
- href="https://www.twitch.tv/barbapapazes"
100
- target="_blank"
101
- variant="link"
102
- color="neutral"
103
- aria-label="Twitch"
104
- :icon="twitch"
105
- :class="ui.link({ class: props.ui?.link })"
106
- :ui="{ leadingIcon: 'size-4' }"
107
- @click="trackClick('Twitch')"
108
- />
109
- </UTooltip>
110
- <UTooltip text="GitHub">
111
- <UButton
112
- href="https://github.com/barbapapazes"
113
- target="_blank"
114
- variant="link"
115
- color="neutral"
116
- aria-label="GitHub"
117
- :icon="github"
118
- :class="ui.link({ class: props.ui?.link })"
119
- :ui="{ leadingIcon: 'size-4' }"
120
- @click="trackClick('GitHub')"
121
- />
122
- </UTooltip>
123
- <UTooltip text="Discord">
124
- <UButton
125
- href="https://discord.gg/q2ghCGUuFR"
126
- target="_blank"
127
- variant="link"
128
- color="neutral"
129
- aria-label="Discord"
130
- :icon="discord"
131
- :class="ui.link({ class: props.ui?.link })"
132
- :ui="{ leadingIcon: 'size-4' }"
133
- @click="trackClick('Discord')"
134
- />
135
- </UTooltip>
136
- </header>
137
- </Container>
138
- </template>
@@ -1,63 +0,0 @@
1
- <script lang="ts">
2
- import { tv } from 'tailwind-variants'
3
- import { computed } from 'vue'
4
- import Container from './Container.vue'
5
-
6
- const page = tv({
7
- slots: {
8
- base: 'py-12',
9
- header: 'max-w-3xl mx-auto xl:max-w-none xl:grid xl:grid-cols-[256px_768px_256px]',
10
- headerInner: 'xl:col-start-2',
11
- content: 'mt-6 max-w-3xl mx-auto xl:max-w-none xl:grid xl:grid-cols-[256px_768px_256px] xl:mx-auto',
12
- contentInner: 'xl:col-start-2',
13
- right: 'hidden xl:block xl:col-start-3 xl:pl-8 xl:h-full',
14
- rightInner: 'sticky top-4',
15
- },
16
- })
17
-
18
- export interface PageProps {
19
- class?: any
20
- ui?: Partial<typeof page.slots>
21
- }
22
- export interface PageEmits {}
23
- export interface PageSlots {
24
- default: (props: any) => any
25
- header: (props: any) => any
26
- right: (props: any) => any
27
- bottom: (props: any) => any
28
- }
29
- </script>
30
-
31
- <script lang="ts" setup>
32
- const props = defineProps<PageProps>()
33
- defineEmits<PageEmits>()
34
- defineSlots<PageSlots>()
35
-
36
- const ui = computed(() => page())
37
- </script>
38
-
39
- <template>
40
- <div :class="ui.base({ class: [props.ui?.base, props.class] })">
41
- <Container>
42
- <div :class="ui.header({ class: props.ui?.header })">
43
- <div :class="ui.headerInner({ class: props.ui?.headerInner })">
44
- <slot name="header" />
45
- </div>
46
- </div>
47
-
48
- <div :class="ui.content({ class: props.ui?.content })">
49
- <div :class="ui.contentInner({ class: props.ui?.contentInner })">
50
- <slot />
51
- </div>
52
-
53
- <div :class="ui.right({ class: props.ui?.right })">
54
- <div :class="ui.rightInner({ class: props.ui?.rightInner })">
55
- <slot name="right" />
56
- </div>
57
- </div>
58
- </div>
59
- </Container>
60
-
61
- <slot name="bottom" />
62
- </div>
63
- </template>
@@ -1,36 +0,0 @@
1
- <script lang="ts">
2
- import { tv } from 'tailwind-variants'
3
- import { computed } from 'vue'
4
- import PageTitle from './PageTitle.vue'
5
-
6
- const pageHeader = tv({
7
- slots: {
8
- base: '',
9
- },
10
- })
11
-
12
- export interface PageHeaderProps {
13
- title: string
14
- class?: any
15
- ui?: Partial<typeof pageHeader.slots>
16
- }
17
- export interface PageHeaderEmits {}
18
- export interface PageHeaderSlots {
19
- after: (props: any) => any
20
- }
21
- </script>
22
-
23
- <script lang="ts" setup>
24
- const props = defineProps<PageHeaderProps>()
25
- defineEmits<PageHeaderEmits>()
26
- defineSlots<PageHeaderSlots>()
27
-
28
- const ui = computed(() => pageHeader())
29
- </script>
30
-
31
- <template>
32
- <div :class="ui.base({ class: [props.ui?.base, props.class] })">
33
- <PageTitle :title="props.title" />
34
- <slot name="after" />
35
- </div>
36
- </template>
@@ -1,32 +0,0 @@
1
- <script lang="ts">
2
- import { tv } from 'tailwind-variants'
3
- import { computed } from 'vue'
4
-
5
- const pageTitle = tv({
6
- slots: {
7
- base: 'text-xl font-bold text-highlighted',
8
- },
9
- })
10
-
11
- export interface PageTitleProps {
12
- title: string
13
- class?: any
14
- ui?: Partial<typeof pageTitle.slots>
15
- }
16
- export interface PageTitleEmits {}
17
- export interface PageTitleSlots {}
18
- </script>
19
-
20
- <script lang="ts" setup>
21
- const props = defineProps<PageTitleProps>()
22
- defineEmits<PageTitleEmits>()
23
- defineSlots<PageTitleSlots>()
24
-
25
- const ui = computed(() => pageTitle())
26
- </script>
27
-
28
- <template>
29
- <h1 :class="ui.base({ class: [props.ui?.base, props.class] })">
30
- {{ title }}
31
- </h1>
32
- </template>
@@ -1,82 +0,0 @@
1
- <script lang="ts">
2
- import { tv } from 'tailwind-variants'
3
- import { computed } from 'vue'
4
- import github from '~icons/simple-icons/github'
5
- import linkedin from '~icons/simple-icons/linkedin'
6
- import twitch from '~icons/simple-icons/twitch'
7
- import x from '~icons/simple-icons/x'
8
- import youtube from '~icons/simple-icons/youtube'
9
-
10
- const socials = tv({
11
- slots: {
12
- base: 'flex flex-wrap gap-2',
13
- link: 'flex items-center gap-1',
14
- icon: 'size-4',
15
- },
16
- })
17
-
18
- export interface SocialsProps {
19
- class?: any
20
- ui?: Partial<typeof socials.slots>
21
- }
22
- export interface SocialsEmits {}
23
- export interface SocialsSlots {}
24
- </script>
25
-
26
- <script lang="ts" setup>
27
- const props = defineProps<SocialsProps>()
28
- defineEmits<SocialsEmits>()
29
- defineSlots<SocialsSlots>()
30
-
31
- const ui = computed(() => socials())
32
- </script>
33
-
34
- <template>
35
- <p :class="ui.base({ class: [props.ui?.base, props.class] })">
36
- <a
37
- href="https://www.linkedin.com/in/esteban25/"
38
- rel="noopener noreferrer"
39
- target="_blank"
40
- :class="ui.link({ class: props.ui?.link })"
41
- >
42
- <UIcon :name="linkedin" :class="ui.icon({ class: props.ui?.icon })" />
43
- <span>LinkedIn</span>
44
- </a>
45
- <a
46
- href="https://x.com/soubiran_"
47
- rel="noopener noreferrer"
48
- target="_blank"
49
- :class="ui.link({ class: props.ui?.link })"
50
- >
51
- <UIcon :name="x" :class="ui.icon({ class: props.ui?.icon })" />
52
- <span>X (Twitter)</span>
53
- </a>
54
- <a
55
- href="https://github.com/barbapapazes"
56
- rel="noopener noreferrer"
57
- target="_blank"
58
- :class="ui.link({ class: props.ui?.link })"
59
- >
60
- <UIcon :name="github" :class="ui.icon({ class: props.ui?.icon })" />
61
- <span>GitHub</span>
62
- </a>
63
- <a
64
- href="https://www.twitch.tv/barbapapazes"
65
- rel="noopener noreferrer"
66
- target="_blank"
67
- :class="ui.link({ class: props.ui?.link })"
68
- >
69
- <UIcon :name="twitch" :class="ui.icon({ class: props.ui?.icon })" />
70
- <span>Twitch</span>
71
- </a>
72
- <a
73
- href="https://www.youtube.com/@barbapapazes"
74
- target="_blank"
75
- rel="noopener noreferrer"
76
- :class="ui.link({ class: props.ui?.link })"
77
- >
78
- <UIcon :name="youtube" :class="ui.icon({ class: props.ui?.icon })" />
79
- <span>YouTube</span>
80
- </a>
81
- </p>
82
- </template>
@@ -1,34 +0,0 @@
1
- <script lang="ts">
2
- import { tv } from 'tailwind-variants'
3
- import { computed } from 'vue'
4
-
5
- const sponsors = tv({
6
- slots: {
7
- base: 'not-prose',
8
- img: 'border-0',
9
- },
10
- })
11
-
12
- export interface SponsorsProps {
13
- class?: any
14
- ui?: Partial<typeof sponsors.slots>
15
- }
16
- export interface SponsorsEmits {}
17
- export interface SponsorsSlots {}
18
- </script>
19
-
20
- <script lang="ts" setup>
21
- const props = defineProps<SponsorsProps>()
22
- defineEmits<SponsorsEmits>()
23
- defineSlots<SponsorsSlots>()
24
-
25
- const ui = computed(() => sponsors())
26
- </script>
27
-
28
- <template>
29
- <p :class="ui.base({ class: [props.ui?.base, props.class] })">
30
- <a href="https://soubiran.dev/sponsorship?utm_source=infra.soubiran.dev&utm_medium=link" target="_blank">
31
- <img src="https://raw.githubusercontent.com/Barbapapazes/static/refs/heads/main/sponsors.svg" alt="Sponsors" :class="ui.img({ class: props.ui?.img })">
32
- </a>
33
- </p>
34
- </template>
@@ -1,74 +0,0 @@
1
- <script lang="ts">
2
- import { tv } from 'tailwind-variants'
3
- import { computed } from 'vue'
4
- import useTableOfContents from '../composables/useTableOfContents'
5
- import useUmami from '../composables/useUmami'
6
-
7
- const tableOfContents = tv({
8
- slots: {
9
- base: 'space-y-1 text-sm text-dimmed font-medium',
10
- title: '',
11
- list: 'font-sofia',
12
- link: 'flex gap-1 px-0 py-1 text-dimmed hover:text-default active:text-default transition-colors focus:outline-none focus-visible:ring-inset focus-visible:ring-2 focus-visible:ring-inverted data-active:text-default rounded-md',
13
- },
14
- })
15
-
16
- export interface TableOfContentsItem {
17
- level: number
18
- anchor: string | null
19
- text: string
20
- children?: TableOfContentsItem[]
21
- }
22
-
23
- export interface TableOfContentsProps {
24
- toc: TableOfContentsItem[]
25
- class?: any
26
- ui?: Partial<typeof tableOfContents.slots>
27
- }
28
- export interface TableOfContentsEmits {}
29
- export interface TableOfContentsSlots {}
30
- </script>
31
-
32
- <script lang="ts" setup>
33
- const props = defineProps<TableOfContentsProps>()
34
- defineEmits<TableOfContentsEmits>()
35
- defineSlots<TableOfContentsSlots>()
36
-
37
- const { track } = useUmami()
38
- function trackClick(text: string, hash: string) {
39
- track('table_of_contents_click', {
40
- toc_text: text,
41
- toc_hash: hash,
42
- })
43
- }
44
-
45
- const { activeHeadings } = useTableOfContents()
46
-
47
- const ui = computed(() => tableOfContents())
48
- </script>
49
-
50
- <template>
51
- <div :class="ui.base({ class: [props.ui?.base, props.class] })">
52
- <div :class="ui.title({ class: props.ui?.title })">
53
- Table of Contents
54
- </div>
55
- <ul :class="ui.list({ class: props.ui?.list })">
56
- <li v-for="(item, index) in props.toc" :key="item.anchor || index">
57
- <!-- Cannot use RouterLink as the anchor will be prefixed with the route path. -->
58
- <a
59
- v-if="item.anchor"
60
- custom
61
- variant="link"
62
- color="neutral"
63
- :href="`#${item.anchor}`"
64
- :data-active="activeHeadings.includes(item.anchor || '') ? true : undefined"
65
- :class="ui.link({ class: props.ui?.link })"
66
- @click="trackClick(item.text, item.anchor)"
67
- >
68
- <span>{{ index + 1 }}.</span>
69
- {{ item.text }}
70
- </a>
71
- </li>
72
- </ul>
73
- </div>
74
- </template>
@@ -1,75 +0,0 @@
1
- <script lang="ts">
2
- import PartySocket from 'partysocket'
3
- import { tv } from 'tailwind-variants'
4
- import { computed, onMounted, ref } from 'vue'
5
-
6
- const viewersCounter = tv({
7
- slots: {
8
- base: 'flex items-center justify-center gap-[0.375rem] border border-green-400 rounded-full px-[0.375rem] py-[0.125rem] text-xs text-muted font-light leading-3 dark:border-green-700',
9
- dot: 'inline-block h-[0.375rem] w-[0.375rem] animate-[pulse_4s_cubic-bezier(0.4,_0,_0.6,_1)_infinite] rounded-full bg-green-400 ring ring-2 ring-green-200 dark:bg-green-700 dark:ring-green-500',
10
- },
11
- })
12
-
13
- interface Data {
14
- count: number
15
- connections: { [key: string]: number }
16
- }
17
-
18
- export interface ViewersCounterProps {
19
- class?: any
20
- ui?: Partial<typeof viewersCounter.slots>
21
- }
22
- export interface ViewersCounterEmits {}
23
- export interface ViewersCounterSlots {}
24
- </script>
25
-
26
- <script lang="ts" setup>
27
- const props = defineProps<ViewersCounterProps>()
28
- defineEmits<ViewersCounterEmits>()
29
- defineSlots<ViewersCounterSlots>()
30
-
31
- const count = ref<number>(0)
32
- const connections = ref<{ [key: string]: number }>({})
33
-
34
- function connect() {
35
- return new PartySocket({
36
- host: import.meta.env.VITE_PARTYKIT_URL,
37
- room: 'soubiran-dev',
38
- })
39
- }
40
-
41
- onMounted(() => {
42
- const ws = connect()
43
-
44
- ws.addEventListener('message', (event) => {
45
- const data = JSON.parse(event.data) as Data
46
-
47
- count.value = data.count
48
- connections.value = data.connections
49
- })
50
- })
51
-
52
- const title = computed(() => {
53
- return `${count.value} viewer${count.value > 1 ? 's' : ''} currently online (${Object.keys(connections.value)
54
- .map(host => `${connections.value[host]} from ${host}`)
55
- .join(', ')})`
56
- })
57
-
58
- const ui = computed(() => viewersCounter())
59
- </script>
60
-
61
- <template>
62
- <Transition name="fade">
63
- <div
64
- v-if="count"
65
- :title
66
- :class="ui.base({ class: [props.class, props.ui?.base] })"
67
- >
68
- <span>{{ count }}</span>
69
- <span
70
- :class="ui.dot({ class: props.ui?.dot })"
71
- aria-hidden="true"
72
- />
73
- </div>
74
- </Transition>
75
- </template>
@@ -1,11 +0,0 @@
1
- import * as _$vue from "vue";
2
-
3
- //#region src/composables/useTableOfContents.d.ts
4
- declare function _useTableOfContents(): {
5
- setActive: (id: string) => void;
6
- unsetActive: (id: string) => void;
7
- activeHeadings: Readonly<_$vue.Ref<readonly string[], readonly string[]>>;
8
- };
9
- declare const _default: typeof _useTableOfContents;
10
- //#endregion
11
- export { _default };
@@ -1,20 +0,0 @@
1
- import { createSharedComposable } from "@vueuse/core";
2
- import { readonly, ref } from "vue";
3
- //#region src/composables/useTableOfContents.ts
4
- function _useTableOfContents() {
5
- const activeHeadings = ref([]);
6
- function setActive(id) {
7
- activeHeadings.value.push(id);
8
- }
9
- function unsetActive(id) {
10
- activeHeadings.value = activeHeadings.value.filter((h) => h !== id);
11
- }
12
- return {
13
- setActive,
14
- unsetActive,
15
- activeHeadings: readonly(activeHeadings)
16
- };
17
- }
18
- var useTableOfContents_default = createSharedComposable(_useTableOfContents);
19
- //#endregion
20
- export { useTableOfContents_default as default };
@@ -1,6 +0,0 @@
1
- //#region src/composables/useUmami.d.ts
2
- declare function useUmami(): {
3
- track: (event: string, data?: Record<string, unknown>) => void;
4
- };
5
- //#endregion
6
- export { useUmami };
@@ -1,14 +0,0 @@
1
- import { useRoute } from "vue-router";
2
- //#region src/composables/useUmami.ts
3
- function useUmami() {
4
- const route = useRoute();
5
- function track(event, data) {
6
- window.umami?.track(event, {
7
- ...data,
8
- page_path: route.path
9
- });
10
- }
11
- return { track };
12
- }
13
- //#endregion
14
- export { useUmami as default };