srcdev-nuxt-components 6.1.19 → 6.1.21

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.
@@ -5,12 +5,10 @@
5
5
  class="display-toast"
6
6
  :class="[
7
7
  elementClasses,
8
+ cssStateClass,
8
9
  {
9
- [theme]: !slots.default,
10
10
  'has-theme': !slots.default,
11
- show: publicToastState && !isHiding && displayDurationInt === 0,
12
- 'use-timer': displayDurationInt > 0,
13
- hide: isHiding,
11
+ 'auto-dismiss': autoDismiss,
14
12
  },
15
13
  ]"
16
14
  :data-theme="theme"
@@ -25,13 +23,13 @@
25
23
  </div>
26
24
  <div class="toast-message">{{ toastDisplayText }}</div>
27
25
  <div class="toast-action">
28
- <button @click.prevent="closeToast">
26
+ <button @click.prevent="updateToHiding()">
29
27
  <Icon name="material-symbols:close" class="icon" />
30
28
  <span class="sr-only">Close</span>
31
29
  </button>
32
30
  </div>
33
31
  </div>
34
- <div v-if="displayDurationInt > 0" @transitionend="closeToast()" class="display-toast-progress"></div>
32
+ <div v-if="autoDismiss" class="display-toast-progress"></div>
35
33
  </div>
36
34
  </Teleport>
37
35
  </template>
@@ -46,7 +44,11 @@ const props = defineProps({
46
44
  },
47
45
  revealDuration: {
48
46
  type: Number,
49
- default: 3000,
47
+ default: 550,
48
+ },
49
+ autoDismiss: {
50
+ type: Boolean,
51
+ default: true,
50
52
  },
51
53
  duration: {
52
54
  type: Number,
@@ -76,10 +78,18 @@ const defaultThemeIcons = {
76
78
  const slots = useSlots()
77
79
  const { elementClasses, resetElementClasses } = useStyleClassPassthrough(props.styleClassPassthrough)
78
80
 
79
- const privateToastState = ref(false)
80
- const isHiding = ref(false)
81
+ // single state ref
82
+ const state = ref<"idle" | "entering" | "visible" | "hiding">("idle")
83
+ const cssStateClass = computed(() => {
84
+ return state.value !== "idle" && !props.autoDismiss ? state.value : ""
85
+ })
86
+
87
+ // external toggle
81
88
  const publicToastState = defineModel<boolean>({ default: false })
82
89
 
90
+ // computed helpers
91
+ const privateToastState = ref(false)
92
+
83
93
  const revealDurationInt = computed(() => props.revealDuration)
84
94
  const revealDuration = computed(() => revealDurationInt.value + "ms")
85
95
  const displayDurationInt = computed(() => props.duration)
@@ -88,16 +98,28 @@ const displayDuration = computed(() => displayDurationInt.value + "ms")
88
98
  const progressDurationInt = computed(() => Math.floor(displayDurationInt.value - revealDurationInt.value / 2))
89
99
  const progressDuration = computed(() => progressDurationInt.value + "ms")
90
100
 
91
- const sendCloseEvent = () => {
101
+ const removeToast = () => {
92
102
  publicToastState.value = false
93
103
  privateToastState.value = false
94
- isHiding.value = false
95
104
  }
96
105
 
97
- const closeToast = async () => {
98
- isHiding.value = true
106
+ const updateToIdle = () => {
107
+ state.value = "idle"
108
+ removeToast()
109
+ }
110
+ const updateToEntering = async () => {
111
+ privateToastState.value = true
112
+ state.value = "entering"
113
+ await useSleep(revealDurationInt.value)
114
+ updateToVisible()
115
+ }
116
+ const updateToVisible = () => {
117
+ state.value = "visible"
118
+ }
119
+ const updateToHiding = async () => {
120
+ state.value = "hiding"
99
121
  await useSleep(revealDurationInt.value)
100
- sendCloseEvent()
122
+ updateToIdle()
101
123
  }
102
124
 
103
125
  watch(
@@ -110,22 +132,52 @@ watch(
110
132
  watch(
111
133
  () => publicToastState.value,
112
134
  async (newValue, previousValue) => {
113
- if (!previousValue && newValue) {
114
- privateToastState.value = true
135
+ if (props.autoDismiss) {
136
+ privateToastState.value = newValue
137
+ await useSleep(displayDurationInt.value)
138
+ updateToIdle()
139
+ return
140
+ }
115
141
 
116
- if (newValue && displayDurationInt.value > 0) {
117
- await useSleep(displayDurationInt.value)
118
- sendCloseEvent()
119
- }
120
- } else if (previousValue && !newValue) {
121
- closeToast()
142
+ if (!previousValue && newValue && state.value === "idle") {
143
+ updateToEntering()
144
+ }
145
+
146
+ if (previousValue && !newValue && state.value == "visible") {
147
+ updateToHiding()
122
148
  }
123
149
  }
124
150
  )
125
151
  </script>
126
152
 
127
153
  <style scoped lang="css">
128
- @keyframes fade-in {
154
+ @keyframes slide-in {
155
+ from {
156
+ opacity: 0;
157
+ visibility: hidden;
158
+ transform: translateY(20px);
159
+ }
160
+ to {
161
+ opacity: 1;
162
+ visibility: visible;
163
+ transform: translateY(0);
164
+ }
165
+ }
166
+
167
+ @keyframes slide-out {
168
+ from {
169
+ opacity: 1;
170
+ visibility: visible;
171
+ transform: translateY(0);
172
+ }
173
+ to {
174
+ opacity: 0;
175
+ visibility: hidden;
176
+ transform: translateY(20px);
177
+ }
178
+ }
179
+
180
+ @keyframes slide-in-out {
129
181
  5% {
130
182
  opacity: 1;
131
183
  visibility: visible;
@@ -174,48 +226,29 @@ watch(
174
226
 
175
227
  z-index: 100;
176
228
 
177
- &.use-timer {
178
- animation: fade-in v-bind(displayDuration) linear;
229
+ &.auto-dismiss {
230
+ /* first run slide-in, then slide-out after a delay */
231
+ animation: slide-in 400ms var(--spring-in-easing) forwards,
232
+ slide-out 400ms var(--spring-out-easing) forwards v-bind(displayDuration);
179
233
  }
180
234
 
181
- &.show {
182
- animation: show v-bind(revealDuration)
183
- linear(
184
- 0,
185
- 0.029 1.6%,
186
- 0.123 3.5%,
187
- 0.651 10.6%,
188
- 0.862 14.1%,
189
- 1.002 17.7%,
190
- 1.046 19.6%,
191
- 1.074 21.6%,
192
- 1.087 23.9%,
193
- 1.086 26.6%,
194
- 1.014 38.5%,
195
- 0.994 46.3%,
196
- 1
197
- )
198
- forwards;
199
- }
235
+ &:not(&.auto-dismiss) {
236
+ &.show,
237
+ &.entering {
238
+ animation: show v-bind(revealDuration) var(--spring-easing) forwards;
239
+ }
240
+
241
+ &.visible {
242
+ /* if you want a steady state style, add here */
243
+ opacity: 1;
244
+ visibility: visible;
245
+ transform: translateY(0);
246
+ }
200
247
 
201
- &.hide {
202
- animation: hide v-bind(revealDuration)
203
- linear(
204
- 0,
205
- 0.006 53.7%,
206
- 0.986 61.5%,
207
- 1.014 73.4%,
208
- 1.087 76.1%,
209
- 1.074 78.4%,
210
- 1.046 80.4%,
211
- 1.002 82.3%,
212
- 0.862 85.9%,
213
- 0.651 89.4%,
214
- 0.123 96.5%,
215
- 0.029 98.4%,
216
- 0
217
- )
218
- forwards;
248
+ &.hide,
249
+ &.hiding {
250
+ animation: hide v-bind(revealDuration) var(--spring-easing) forwards;
251
+ }
219
252
  }
220
253
 
221
254
  &:hover {
@@ -264,6 +297,9 @@ watch(
264
297
  border: 0.1rem solid var(--colour-theme-8);
265
298
  border-start-start-radius: 8px;
266
299
  border-end-start-radius: 8px;
300
+ border-start-end-radius: 4px;
301
+ border-end-end-radius: 4px;
302
+
267
303
  overflow: hidden;
268
304
 
269
305
  .display-toast-inner {
@@ -0,0 +1,160 @@
1
+ <template>
2
+ <component :is="tag" class="magnetic-navigation" :class="[elementClasses]">
3
+ <nav>
4
+ <ul>
5
+ <li><a href="#">Home</a></li>
6
+ <li><a href="#">About</a></li>
7
+ <li><a href="#">Blog</a></li>
8
+ <li><a href="#">Contact</a></li>
9
+ </ul>
10
+ </nav>
11
+ </component>
12
+ </template>
13
+
14
+ <script setup lang="ts">
15
+ const props = defineProps({
16
+ tag: {
17
+ type: String,
18
+ default: "div",
19
+ validator(value: string) {
20
+ return ["div", "header", "footer", "nav"].includes(value)
21
+ },
22
+ },
23
+ styleClassPassthrough: {
24
+ type: Array as PropType<string[]>,
25
+ default: () => [],
26
+ },
27
+ })
28
+
29
+ const { elementClasses, resetElementClasses } = useStyleClassPassthrough(props.styleClassPassthrough)
30
+
31
+ watch(
32
+ () => props.styleClassPassthrough,
33
+ () => {
34
+ resetElementClasses(props.styleClassPassthrough)
35
+ }
36
+ )
37
+ </script>
38
+
39
+ <style lang="css">
40
+ .magnetic-navigation {
41
+ /*
42
+ --_background-image: url("/images/rotating-carousel/image-3.webp");
43
+
44
+ background-image: var(--_background-image);
45
+ background-size: cover;
46
+ background-position: 0 -400px;
47
+ background-attachment: fixed;
48
+ */
49
+ /* padding-block: 200px 2000px; */
50
+
51
+ nav {
52
+ width: fit-content;
53
+ margin: 3rem auto;
54
+ background: hsl(0 0% 0% / 0.8);
55
+
56
+ padding: 8px;
57
+ border-radius: 8px;
58
+
59
+ isolation: isolate;
60
+
61
+ anchor-name: --hovered-link;
62
+
63
+ li:hover {
64
+ anchor-name: --hovered-link;
65
+ }
66
+
67
+ &::before,
68
+ &::after {
69
+ content: "";
70
+ position: absolute;
71
+ top: calc(anchor(bottom) - 10px);
72
+ left: calc(anchor(left) + 1rem);
73
+ right: calc(anchor(right) + 1rem);
74
+ bottom: calc(anchor(bottom) + 5px);
75
+ border-radius: 10px;
76
+
77
+ position-anchor: --hovered-link;
78
+
79
+ transition: 500ms
80
+ linear(
81
+ 0,
82
+ 0.029 1.6%,
83
+ 0.123 3.5%,
84
+ 0.651 10.6%,
85
+ 0.862 14.1%,
86
+ 1.002 17.7%,
87
+ 1.046 19.6%,
88
+ 1.074 21.6%,
89
+ 1.087 23.9%,
90
+ 1.086 26.6%,
91
+ 1.014 38.5%,
92
+ 0.994 46.3%,
93
+ 1
94
+ );
95
+ }
96
+
97
+ &::before {
98
+ z-index: -1;
99
+ background: rgb(0 0 0 / 0.2);
100
+ backdrop-filter: blur(2px);
101
+ }
102
+
103
+ &::after {
104
+ z-index: -2;
105
+ background-image: var(--_background-image);
106
+ background-size: cover;
107
+ background-position: var(--_background-position);
108
+ background-attachment: fixed;
109
+ }
110
+
111
+ &:has(a:hover)::before,
112
+ &:has(a:hover)::after {
113
+ top: anchor(top);
114
+ left: anchor(left);
115
+ right: anchor(right);
116
+ bottom: anchor(bottom);
117
+
118
+ @supports (corner-shape: squircle) {
119
+ corner-shape: squircle;
120
+ border-radius: 50%;
121
+ }
122
+ }
123
+
124
+ &:has(li:first-of-type a:hover)::before,
125
+ &:has(li:first-of-type a:hover)::after {
126
+ @supports (corner-shape: squircle) {
127
+ border-radius: 32px 50% 50% 32px;
128
+ }
129
+ }
130
+
131
+ &:has(li:last-of-type a:hover)::before,
132
+ &:has(li:last-of-type a:hover)::after {
133
+ @supports (corner-shape: squircle) {
134
+ border-radius: 50% 32px 32px 50%;
135
+ }
136
+ }
137
+
138
+ @supports (corner-shape: squircle) {
139
+ border-radius: 24px;
140
+ corner-shape: squircle;
141
+ }
142
+
143
+ > ul {
144
+ padding: 0;
145
+ margin: 0;
146
+ list-style: none;
147
+ display: flex;
148
+ gap: 24px;
149
+
150
+ a {
151
+ display: block;
152
+ padding: 1rem;
153
+ text-decoration: none;
154
+ color: white;
155
+ font-size: var(--step-7);
156
+ }
157
+ }
158
+ }
159
+ }
160
+ </style>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "srcdev-nuxt-components",
3
3
  "type": "module",
4
- "version": "6.1.19",
4
+ "version": "6.1.21",
5
5
  "main": "nuxt.config.ts",
6
6
  "scripts": {
7
7
  "clean": "rm -rf .nuxt && rm -rf .output && rm -rf .playground/.nuxt && rm -rf .playground/.output",