tangled
alpha
login
or
join now
vt3e.cat
/
www
2
fork
atom
this repo has no descr,ription
vt3e.cat
2
fork
atom
overview
issues
pulls
pipelines
feat: close cardview when clicking outside of it
vt3e.cat
2 months ago
9b9f5580
b91a4e05
verified
This commit was signed with the committer's
known signature
.
vt3e.cat
SSH Key Fingerprint:
SHA256:bC12nO0d6wKnJ426YBbLO7LVxmZlwJ1l2X0eqOroDV0=
+96
-14
1 changed file
expand all
collapse all
unified
split
pkgs
web
src
components
Card
CardLayout.vue
+96
-14
pkgs/web/src/components/Card/CardLayout.vue
···
1
1
<script setup lang="ts">
2
2
-
import { onMounted, onUnmounted, ref } from 'vue'
2
2
+
import { onMounted, onUnmounted, ref, useId, nextTick } from 'vue'
3
3
import { useRouter } from 'vue-router'
4
4
import { useUIStore } from '@/stores/ui'
5
5
6
6
+
const id = useId()
6
7
defineProps<{
7
8
title: string
8
9
}>()
9
10
10
11
const router = useRouter()
11
12
const ui = useUIStore()
13
13
+
12
14
const showHint = ref(false)
13
15
14
16
const pageAccent = ui.activeLayerColour ? `var(--${ui.activeLayerColour})` : `var(--accent)`
···
17
19
if (e.key === 'Escape') router.push('/')
18
20
}
19
21
20
20
-
onMounted(() => {
22
22
+
const MOVE_THRESHOLD = 10
23
23
+
const tracking = ref(false)
24
24
+
const moved = ref(false)
25
25
+
const startX = ref(0)
26
26
+
const startY = ref(0)
27
27
+
28
28
+
const getCardEl = () => document.querySelector(`#card-sheet-${id}`)
29
29
+
30
30
+
const onPointerDown = (e: PointerEvent) => {
31
31
+
const card = getCardEl()
32
32
+
if (card && !card.contains(e.target as Node)) {
33
33
+
tracking.value = true
34
34
+
moved.value = false
35
35
+
startX.value = e.clientX
36
36
+
startY.value = e.clientY
37
37
+
}
38
38
+
}
39
39
+
40
40
+
const onPointerMove = (e: PointerEvent) => {
41
41
+
if (!tracking.value) return
42
42
+
const dx = e.clientX - startX.value
43
43
+
const dy = e.clientY - startY.value
44
44
+
if (Math.hypot(dx, dy) > MOVE_THRESHOLD) {
45
45
+
moved.value = true
46
46
+
}
47
47
+
}
48
48
+
49
49
+
const onPointerUp = (e: PointerEvent) => {
50
50
+
if (!tracking.value) return
51
51
+
const card = getCardEl()
52
52
+
if (!moved.value && card && !card.contains(e.target as Node)) router.push('/')
53
53
+
tracking.value = false
54
54
+
moved.value = false
55
55
+
}
56
56
+
57
57
+
const onPointerCancel = () => {
58
58
+
tracking.value = false
59
59
+
moved.value = false
60
60
+
}
61
61
+
62
62
+
onMounted(async () => {
21
63
window.addEventListener('keydown', onEscape)
22
64
65
65
+
window.addEventListener('pointerdown', onPointerDown)
66
66
+
window.addEventListener('pointermove', onPointerMove)
67
67
+
window.addEventListener('pointerup', onPointerUp)
68
68
+
window.addEventListener('pointercancel', onPointerCancel)
69
69
+
70
70
+
await nextTick()
71
71
+
const el = document.querySelector(`#card-sheet-${id}`) as HTMLElement | null
72
72
+
if (el) {
73
73
+
if (!el.hasAttribute('tabindex')) el.setAttribute('tabindex', '-1')
74
74
+
el.focus()
75
75
+
}
76
76
+
23
77
if (!ui.hasSeenEscHint) {
24
78
setTimeout(() => {
25
79
showHint.value = true
···
31
85
}, 4000)
32
86
}
33
87
})
34
34
-
onUnmounted(() => window.removeEventListener('keydown', onEscape))
88
88
+
89
89
+
onUnmounted(() => {
90
90
+
window.removeEventListener('keydown', onEscape)
91
91
+
92
92
+
window.removeEventListener('pointerdown', onPointerDown)
93
93
+
window.removeEventListener('pointermove', onPointerMove)
94
94
+
window.removeEventListener('pointerup', onPointerUp)
95
95
+
window.removeEventListener('pointercancel', onPointerCancel)
96
96
+
})
35
97
</script>
36
98
37
99
<template>
38
100
<div class="view-container">
39
101
<div
102
102
+
:id="`card-sheet-${id}`"
40
103
class="card-sheet"
41
104
role="dialog"
42
105
aria-modal="true"
···
63
126
</template>
64
127
65
128
<style scoped lang="scss">
129
129
+
@use '@/styles/variables.scss' as *;
130
130
+
131
131
+
.view-container {
132
132
+
width: 100%;
133
133
+
height: 100dvh;
134
134
+
135
135
+
&:active:has(.card-sheet:not(:hover)) .card-sheet {
136
136
+
transform: scale(0.95) translateY(10px);
137
137
+
opacity: 0.8;
138
138
+
filter: blur(2px);
139
139
+
}
140
140
+
}
141
141
+
66
142
.card-sheet {
67
143
--bg-colour: color-mix(in srgb, hsl(var(--page-accent)) 5%, hsl(var(--base)));
68
144
width: 100%;
69
145
max-width: 900px;
70
146
71
71
-
height: 100%;
72
147
max-height: calc(100dvh - 2rem);
73
148
overflow-y: auto;
74
149
display: block;
···
133
208
cursor: default;
134
209
135
210
background: hsla(var(--overlay2) / 0.1);
211
211
+
box-shadow: 0 0 0 0.1rem hsla(var(--surface2) / 0.4);
136
212
color: hsl(var(--page-accent));
213
213
+
font-size: 1.25rem;
214
214
+
font-weight: 900;
215
215
+
137
216
border: none;
138
217
border-radius: 50%;
139
218
text-decoration: none;
219
219
+
outline: none;
140
220
141
141
-
width: 3rem;
142
142
-
height: 3rem;
221
221
+
width: 2.25rem;
222
222
+
aspect-ratio: 1 / 1;
143
223
144
144
-
font-size: 1.25rem;
145
145
-
font-weight: 900;
224
224
+
transition: all 0.35s $ease-spring;
146
225
147
147
-
&:hover {
148
148
-
background: hsla(var(--overlay2) / 0.2);
149
149
-
transform: scale(1.1) rotate(90deg);
226
226
+
&:hover,
227
227
+
&:focus-visible {
228
228
+
background: hsla(var(--surface2) / 0.5);
229
229
+
color: hsl(var(--accent));
230
230
+
box-shadow: 0 0 0 0.3rem hsla(var(--accent) / 1);
150
231
}
151
151
-
&:active {
152
152
-
background: hsla(var(--overlay2) / 0.05);
153
153
-
transform: scale(0.95) rotate(90deg);
232
232
+
&:active,
233
233
+
&.active {
234
234
+
background: hsla(var(--surface2) / 0.3);
235
235
+
box-shadow: 0 0 0 0.15rem hsla(var(--accent) / 1);
154
236
}
155
237
}
156
238
}