srcdev-nuxt-components 6.1.3 → 6.1.5

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.
Files changed (52) hide show
  1. package/README.md +3 -3
  2. package/app/components/carousel-basic/CarouselBasic.vue +86 -62
  3. package/app/components/deep-expanding-menu/DeepExpandingMenu.vue +14 -14
  4. package/app/components/deep-expanding-menu/DeepExpandingMenuOld.vue +21 -21
  5. package/app/components/display-prompt/DisplayPromptCore.vue +14 -19
  6. package/app/components/marquee-scroller/MarqueeScroller.vue +122 -0
  7. package/app/components/masonry-grid-ordered/MasonryGridOrdered.vue +44 -41
  8. package/app/components/responsive-header/ResponsiveHeader.vue +3 -3
  9. package/nuxt.config.ts +1 -1
  10. package/package.json +3 -3
  11. package/app/assets/styles/extends-layer/srcdev-components/components/_display-prompt-core.css +0 -72
  12. package/app/assets/styles/extends-layer/srcdev-components/components/_expanding-panel.css +0 -37
  13. package/app/assets/styles/extends-layer/srcdev-components/components/index.css +0 -2
  14. package/app/assets/styles/extends-layer/srcdev-components/index.css +0 -2
  15. package/app/assets/styles/extends-layer/srcdev-components/setup/index.css +0 -1
  16. package/app/assets/styles/extends-layer/srcdev-components/setup/themes/_error.css +0 -15
  17. package/app/assets/styles/extends-layer/srcdev-components/setup/themes/_info.css +0 -15
  18. package/app/assets/styles/extends-layer/srcdev-components/setup/themes/_secondary.css +0 -15
  19. package/app/assets/styles/extends-layer/srcdev-components/setup/themes/_success.css +0 -15
  20. package/app/assets/styles/extends-layer/srcdev-components/setup/themes/_warning.css +0 -15
  21. package/app/assets/styles/extends-layer/srcdev-components/setup/themes/index.css +0 -5
  22. package/app/assets/styles/main.css +0 -2
  23. package/app/assets/styles/setup/_head.css +0 -36
  24. package/app/assets/styles/setup/a11y/_utils.css +0 -20
  25. package/app/assets/styles/setup/a11y/_variables.css +0 -8
  26. package/app/assets/styles/setup/a11y/index.css +0 -2
  27. package/app/assets/styles/setup/index.css +0 -5
  28. package/app/assets/styles/setup/typography/index.css +0 -2
  29. package/app/assets/styles/setup/typography/utility-classes/_generic-font-classes.css +0 -217
  30. package/app/assets/styles/setup/typography/utility-classes/_generic-font-variation-settings.css +0 -29
  31. package/app/assets/styles/setup/typography/utility-classes/_generic-font-weights.css +0 -39
  32. package/app/assets/styles/setup/typography/utility-classes/index.css +0 -3
  33. package/app/assets/styles/setup/typography/vars/_colors.css +0 -14
  34. package/app/assets/styles/setup/typography/vars/_reponsive-font-sizes.css +0 -12
  35. package/app/assets/styles/setup/typography/vars/index.css +0 -2
  36. package/app/assets/styles/setup/utility-classes/_fluid-spacing.css +0 -13
  37. package/app/assets/styles/setup/utility-classes/_margin.css +0 -334
  38. package/app/assets/styles/setup/utility-classes/_padding.css +0 -308
  39. package/app/assets/styles/setup/utility-classes/animations/_auto-rotate.css +0 -13
  40. package/app/assets/styles/setup/utility-classes/animations/_entry-exit-blur.css +0 -16
  41. package/app/assets/styles/setup/utility-classes/animations/_entry-slide-in.css +0 -15
  42. package/app/assets/styles/setup/utility-classes/animations/_entry-zoom-reveal.css +0 -15
  43. package/app/assets/styles/setup/utility-classes/animations/index.css +0 -4
  44. package/app/assets/styles/setup/utility-classes/index.css +0 -4
  45. package/app/assets/styles/setup/variables/colors/_blue.css +0 -15
  46. package/app/assets/styles/setup/variables/colors/_gray.css +0 -16
  47. package/app/assets/styles/setup/variables/colors/_green.css +0 -15
  48. package/app/assets/styles/setup/variables/colors/_orange.css +0 -15
  49. package/app/assets/styles/setup/variables/colors/_red.css +0 -15
  50. package/app/assets/styles/setup/variables/colors/_yellow.css +0 -15
  51. package/app/assets/styles/setup/variables/colors/index.css +0 -6
  52. package/app/assets/styles/setup/variables/index.css +0 -1
package/README.md CHANGED
@@ -12,8 +12,8 @@ npm install --save srcdev-nuxt-components
12
12
 
13
13
  ```ts
14
14
  defineNuxtConfig({
15
- extends: 'srcdev-nuxt-components',
16
- });
15
+ extends: "srcdev-nuxt-components",
16
+ })
17
17
  ```
18
18
 
19
19
  ## Styles Architecture
@@ -49,7 +49,7 @@ The setup layer provides the foundational styles and utilities:
49
49
  - **Responsive Font Sizes** - Fluid typography using clamp() functions:
50
50
  - `--step-8` to `--step-1` providing 8 responsive font size steps
51
51
  - **Utility Classes** - Pre-built typography classes:
52
- - `.heading-1` through `.heading-5` - Semantic heading styles
52
+ - `.page-heading-1` through `.page-heading-5` - Semantic heading styles
53
53
  - Generic font weight and variation settings
54
54
  - **Font Variation Settings** - Advanced typography controls
55
55
 
@@ -1,11 +1,30 @@
1
1
  <template>
2
- <section class="carousel-basic" :class="[elementClasses]" ref="carouselWrapperRef" role="region" aria-label="Image carousel">
2
+ <section
3
+ class="carousel-basic"
4
+ :class="[elementClasses]"
5
+ ref="carouselWrapperRef"
6
+ role="region"
7
+ aria-label="Image carousel"
8
+ >
3
9
  <!-- Screen reader announcement for current item -->
4
10
  <div aria-live="polite" aria-atomic="true" class="sr-only">Item {{ currentIndex + 1 }} of {{ itemCount }}</div>
5
11
 
6
12
  <LayoutRow tag="div" variant="full-width" :style-class-passthrough="['mbe-20']">
7
- <div tabindex="0" class="item-container" :class="{ 'allow-overflow': allowCarouselOverflow }" ref="carouselContainerRef" role="group" aria-label="Carousel items">
8
- <div v-for="(item, index) in carouselDataIds" :key="index" class="item" ref="carouselItems" :aria-current="currentIndex === index ? 'true' : 'false'">
13
+ <div
14
+ tabindex="0"
15
+ class="item-container"
16
+ :class="{ 'allow-overflow': allowCarouselOverflow }"
17
+ ref="carouselContainerRef"
18
+ role="group"
19
+ aria-label="Carousel items"
20
+ >
21
+ <div
22
+ v-for="(item, index) in carouselDataIds"
23
+ :key="index"
24
+ class="item"
25
+ ref="carouselItems"
26
+ :aria-current="currentIndex === index ? 'true' : 'false'"
27
+ >
9
28
  <slot :name="item"></slot>
10
29
  </div>
11
30
  </div>
@@ -24,7 +43,12 @@
24
43
  <div class="markers-container">
25
44
  <ul class="markers-list">
26
45
  <li v-for="index in itemCount" :key="index" class="markers-item">
27
- <button @click.prevent="jumpToFrame(index - 1)" class="btn-marker" :class="[{ active: currentIndex === index - 1 }]" :aria-label="`Jump to item ${Math.floor(index + 1)}`"></button>
46
+ <button
47
+ @click.prevent="jumpToFrame(index - 1)"
48
+ class="btn-marker"
49
+ :class="[{ active: currentIndex === index - 1 }]"
50
+ :aria-label="`Jump to item ${Math.floor(index + 1)}`"
51
+ ></button>
28
52
  </li>
29
53
  </ul>
30
54
  </div>
@@ -42,7 +66,7 @@
42
66
  </template>
43
67
 
44
68
  <script setup lang="ts">
45
- import { useEventListener, useResizeObserver, useSwipe } from '@vueuse/core';
69
+ import { useEventListener, useResizeObserver, useSwipe } from "@vueuse/core"
46
70
 
47
71
  const props = defineProps({
48
72
  carouselDataIds: {
@@ -65,108 +89,108 @@ const props = defineProps({
65
89
  type: Boolean,
66
90
  default: false,
67
91
  },
68
- });
92
+ })
69
93
 
70
- const { elementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
94
+ const { elementClasses } = useStyleClassPassthrough(props.styleClassPassthrough)
71
95
 
72
- const carouselWrapperRef = ref<HTMLDivElement | null>(null);
73
- const carouselContainerRef = ref<HTMLDivElement | null>(null);
74
- const carouselItemsRef = useTemplateRef<HTMLDivElement[]>('carouselItems');
75
- const controlsContainerRef = ref<HTMLDivElement | null>(null);
76
- const carouselInitComplete = ref(false);
96
+ const carouselWrapperRef = ref<HTMLDivElement | null>(null)
97
+ const carouselContainerRef = ref<HTMLDivElement | null>(null)
98
+ const carouselItemsRef = useTemplateRef<HTMLDivElement[]>("carouselItems")
99
+ const controlsContainerRef = ref<HTMLDivElement | null>(null)
100
+ const carouselInitComplete = ref(false)
77
101
 
78
- const currentIndex = ref(0);
79
- const itemCount = ref(props.carouselDataIds.length);
80
- const offset = ref(0);
81
- const transitionSpeedStr = props.transitionSpeed + 'ms';
102
+ const currentIndex = ref(0)
103
+ const itemCount = ref(props.carouselDataIds.length)
104
+ const offset = ref(0)
105
+ const transitionSpeedStr = props.transitionSpeed + "ms"
82
106
  const itemTransform = computed(() => {
83
- return `translateX(calc(${offset.value} * (${itemWidth.value} + var(--_carousel-item-track-gap))))`;
84
- });
107
+ return `translateX(calc(${offset.value} * (${itemWidth.value} + var(--_carousel-item-track-gap))))`
108
+ })
85
109
 
86
- const itemWidth = ref('0px');
110
+ const itemWidth = ref("0px")
87
111
 
88
112
  const actionPrevious = () => {
89
113
  if (props.returnToStart && currentIndex.value === 0) {
90
- offset.value = -itemCount.value;
91
- doAction();
114
+ offset.value = -itemCount.value
115
+ doAction()
92
116
  }
93
117
 
94
118
  if (offset.value >= 0) {
95
- return;
119
+ return
96
120
  }
97
121
 
98
- offset.value = Math.min(offset.value + 1);
99
- doAction();
100
- };
122
+ offset.value = Math.min(offset.value + 1)
123
+ doAction()
124
+ }
101
125
 
102
126
  const actionNext = () => {
103
127
  if (props.returnToStart && offset.value <= -1 * (itemCount.value - 1)) {
104
- offset.value = 0;
105
- doAction();
106
- return;
128
+ offset.value = 0
129
+ doAction()
130
+ return
107
131
  }
108
132
 
109
133
  if (offset.value <= -1 * (itemCount.value - 1)) {
110
- return;
134
+ return
111
135
  }
112
136
 
113
- offset.value = Math.min(offset.value - 1);
114
- doAction();
115
- };
137
+ offset.value = Math.min(offset.value - 1)
138
+ doAction()
139
+ }
116
140
 
117
141
  const doAction = () => {
118
- currentIndex.value = Math.abs(offset.value);
119
- };
142
+ currentIndex.value = Math.abs(offset.value)
143
+ }
120
144
 
121
145
  const jumpToFrame = (index: number) => {
122
146
  if (index >= 0 && index < itemCount.value) {
123
- offset.value = -index;
124
- doAction();
147
+ offset.value = -index
148
+ doAction()
125
149
  }
126
- };
150
+ }
127
151
 
128
152
  const initialSetup = () => {
129
153
  if (carouselItemsRef?.value && carouselItemsRef.value.length > 0 && carouselItemsRef.value[0]) {
130
- itemWidth.value = carouselItemsRef.value[0].offsetWidth + 'px';
154
+ itemWidth.value = carouselItemsRef.value[0].offsetWidth + "px"
131
155
  }
132
156
 
133
- carouselInitComplete.value = true;
134
- };
157
+ carouselInitComplete.value = true
158
+ }
135
159
 
136
160
  const { direction } = useSwipe(carouselContainerRef, {
137
161
  passive: false,
138
162
  onSwipeEnd() {
139
- if (direction.value === 'left') {
140
- actionNext();
141
- } else if (direction.value === 'right') {
142
- actionPrevious();
163
+ if (direction.value === "left") {
164
+ actionNext()
165
+ } else if (direction.value === "right") {
166
+ actionPrevious()
143
167
  }
144
168
  },
145
- });
169
+ })
146
170
 
147
- useEventListener(carouselContainerRef, 'keydown', (event: KeyboardEvent) => {
148
- if (event.key === 'ArrowLeft') {
149
- actionPrevious();
150
- } else if (event.key === 'ArrowRight') {
151
- actionNext();
171
+ useEventListener(carouselContainerRef, "keydown", (event: KeyboardEvent) => {
172
+ if (event.key === "ArrowLeft") {
173
+ actionPrevious()
174
+ } else if (event.key === "ArrowRight") {
175
+ actionNext()
152
176
  }
153
- });
177
+ })
154
178
 
155
- useEventListener(controlsContainerRef, 'keydown', (event: KeyboardEvent) => {
156
- if (event.key === 'ArrowLeft') {
157
- actionPrevious();
158
- } else if (event.key === 'ArrowRight') {
159
- actionNext();
179
+ useEventListener(controlsContainerRef, "keydown", (event: KeyboardEvent) => {
180
+ if (event.key === "ArrowLeft") {
181
+ actionPrevious()
182
+ } else if (event.key === "ArrowRight") {
183
+ actionNext()
160
184
  }
161
- });
185
+ })
162
186
 
163
187
  useResizeObserver(carouselWrapperRef, async () => {
164
- initialSetup();
165
- });
188
+ initialSetup()
189
+ })
166
190
 
167
191
  onMounted(() => {
168
- initialSetup();
169
- });
192
+ initialSetup()
193
+ })
170
194
  </script>
171
195
 
172
196
  <style lang="css">
@@ -203,7 +227,7 @@ onMounted(() => {
203
227
  position: relative;
204
228
 
205
229
  &::before {
206
- content: '';
230
+ content: "";
207
231
  position: absolute;
208
232
  height: 2px;
209
233
  background-color: #fff;
@@ -11,7 +11,7 @@
11
11
  </button>
12
12
 
13
13
  <div class="navigation-group-panel" popover role="menu" :id="`popovertarget-nav-1-${key}`">
14
- <h4 class="heading-4 mb-6">{{ link.childLinksTitle }}</h4>
14
+ <h4 class="page-heading-4 mb-6">{{ link.childLinksTitle }}</h4>
15
15
  <ul class="navigation-group-list">
16
16
  <li class="navigation-group-item" v-for="childLink in link.childLinks" :key="childLink.name">
17
17
  <NuxtLink :to="childLink.path" class="navigation-group-link">{{ childLink.name }}</NuxtLink>
@@ -25,14 +25,14 @@
25
25
  </template>
26
26
 
27
27
  <script lang="ts">
28
- const TAGS_ALLOWED = <string[]>['div', 'section', 'nav', 'ul', 'ol'];
28
+ const TAGS_ALLOWED = <string[]>["div", "section", "nav", "ul", "ol"]
29
29
 
30
30
  interface ResponsiveHeaderNavItem {
31
- name: string;
32
- path?: string;
33
- isExternal?: boolean;
34
- childLinks?: ResponsiveHeaderNavItem[];
35
- childLinksTitle?: string;
31
+ name: string
32
+ path?: string
33
+ isExternal?: boolean
34
+ childLinks?: ResponsiveHeaderNavItem[]
35
+ childLinksTitle?: string
36
36
  }
37
37
  </script>
38
38
 
@@ -40,9 +40,9 @@ interface ResponsiveHeaderNavItem {
40
40
  const props = defineProps({
41
41
  tag: {
42
42
  type: String,
43
- default: 'nav',
43
+ default: "nav",
44
44
  validator(value: string) {
45
- return TAGS_ALLOWED.includes(value);
45
+ return TAGS_ALLOWED.includes(value)
46
46
  },
47
47
  },
48
48
  navLinks: {
@@ -53,17 +53,17 @@ const props = defineProps({
53
53
  type: Array as PropType<string[]>,
54
54
  default: () => [],
55
55
  },
56
- });
56
+ })
57
57
 
58
- const { elementClasses, resetElementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
58
+ const { elementClasses, resetElementClasses } = useStyleClassPassthrough(props.styleClassPassthrough)
59
59
  // const detailsRef = useTemplateRef('detailsRef');
60
60
 
61
61
  watch(
62
62
  () => props.styleClassPassthrough,
63
63
  () => {
64
- resetElementClasses(props.styleClassPassthrough);
64
+ resetElementClasses(props.styleClassPassthrough)
65
65
  }
66
- );
66
+ )
67
67
  </script>
68
68
 
69
69
  <style lang="css">
@@ -85,7 +85,7 @@ watch(
85
85
 
86
86
  container-type: inline-size;
87
87
  display: grid;
88
- grid-template-areas: 'element-stack';
88
+ grid-template-areas: "element-stack";
89
89
 
90
90
  .inner {
91
91
  grid-area: element-stack;
@@ -9,7 +9,7 @@
9
9
  <Icon name="bi:caret-down-fill" class="icon" />
10
10
  </summary>
11
11
  <div class="navigation-group-panel" :id="`popovertarget-nav-1-${key}`">
12
- <h4 class="heading-4 mb-6">{{ link.childLinksTitle }}</h4>
12
+ <h4 class="page-heading-4 mb-6">{{ link.childLinksTitle }}</h4>
13
13
  <ul class="navigation-group-list">
14
14
  <li class="navigation-group-item" v-for="childLink in link.childLinks" :key="childLink.name">
15
15
  <NuxtLink :to="childLink.path" class="navigation-group-link">{{ childLink.name }}</NuxtLink>
@@ -23,14 +23,14 @@
23
23
  </template>
24
24
 
25
25
  <script setup lang="ts">
26
- import { onClickOutside } from '@vueuse/core';
26
+ import { onClickOutside } from "@vueuse/core"
27
27
 
28
28
  const props = defineProps({
29
29
  tag: {
30
30
  type: String,
31
- default: 'nav',
31
+ default: "nav",
32
32
  validator(value: string) {
33
- return TAGS_ALLOWED.includes(value);
33
+ return TAGS_ALLOWED.includes(value)
34
34
  },
35
35
  },
36
36
  navLinks: {
@@ -41,37 +41,37 @@ const props = defineProps({
41
41
  type: Array as PropType<string[]>,
42
42
  default: () => [],
43
43
  },
44
- });
44
+ })
45
45
 
46
- const { elementClasses, resetElementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
46
+ const { elementClasses, resetElementClasses } = useStyleClassPassthrough(props.styleClassPassthrough)
47
47
 
48
- const navigationGroupRef = useTemplateRef<HTMLElement[]>('navigationGroupRef');
48
+ const navigationGroupRef = useTemplateRef<HTMLElement[]>("navigationGroupRef")
49
49
 
50
50
  watch(
51
51
  () => props.styleClassPassthrough,
52
52
  () => {
53
- resetElementClasses(props.styleClassPassthrough);
53
+ resetElementClasses(props.styleClassPassthrough)
54
54
  }
55
- );
55
+ )
56
56
 
57
57
  onMounted(() => {
58
58
  navigationGroupRef.value?.forEach((element, index) => {
59
59
  onClickOutside(element, () => {
60
- navigationGroupRef.value?.[index]?.removeAttribute('open');
61
- });
62
- });
63
- });
60
+ navigationGroupRef.value?.[index]?.removeAttribute("open")
61
+ })
62
+ })
63
+ })
64
64
  </script>
65
65
 
66
66
  <script lang="ts">
67
- const TAGS_ALLOWED = <string[]>['div', 'section', 'nav', 'ul', 'ol'];
67
+ const TAGS_ALLOWED = <string[]>["div", "section", "nav", "ul", "ol"]
68
68
 
69
69
  interface ResponsiveHeaderNavItem {
70
- name: string;
71
- path?: string;
72
- isExternal?: boolean;
73
- childLinksTitle?: string;
74
- childLinks?: ResponsiveHeaderNavItem[];
70
+ name: string
71
+ path?: string
72
+ isExternal?: boolean
73
+ childLinksTitle?: string
74
+ childLinks?: ResponsiveHeaderNavItem[]
75
75
  }
76
76
  </script>
77
77
 
@@ -80,7 +80,7 @@ interface ResponsiveHeaderNavItem {
80
80
  .deep-expanding-menu-old {
81
81
  container-type: inline-size;
82
82
  display: grid;
83
- grid-template-areas: 'element-stack';
83
+ grid-template-areas: "element-stack";
84
84
  align-items: center;
85
85
  gap: 12px;
86
86
 
@@ -117,7 +117,7 @@ interface ResponsiveHeaderNavItem {
117
117
  --_icon-transform: scaleY(1);
118
118
 
119
119
  display: grid;
120
- grid-template-areas: 'details-stack';
120
+ grid-template-areas: "details-stack";
121
121
  z-index: 1;
122
122
  position: relative;
123
123
 
@@ -4,12 +4,7 @@
4
4
  :class="[{ dismissed: hide }, { 'use-local-style-overrides': useLocalStyleOverrides }]"
5
5
  :data-test-id="`display-prompt-core-${theme}`"
6
6
  >
7
- <div
8
- class="display-prompt-wrapper"
9
- :data-component-theme="theme"
10
- :class="[elementClasses]"
11
- data-test-id="display-prompt"
12
- >
7
+ <div class="display-prompt-wrapper" :data-theme="theme" :class="[elementClasses]" data-test-id="display-prompt">
13
8
  <div class="display-prompt-inner">
14
9
  <div class="display-prompt-icon" data-test-id="prompt-icon">
15
10
  <slot name="customDecoratorIcon">
@@ -106,11 +101,11 @@ const dismissPrompt = () => {
106
101
  }
107
102
 
108
103
  .display-prompt-wrapper {
109
- background-color: var(--component-theme-0);
110
- border: 1px solid var(--component-theme-8);
104
+ background-color: var(--colour-theme-0);
105
+ border: 1px solid var(--colour-theme-8);
111
106
  border-radius: 4px;
112
107
 
113
- border-inline-start: 8px solid var(--component-theme-8);
108
+ border-inline-start: 8px solid var(--colour-theme-8);
114
109
  border-start-start-radius: 8px;
115
110
  border-end-start-radius: 8px;
116
111
 
@@ -127,7 +122,7 @@ const dismissPrompt = () => {
127
122
  .display-prompt-icon {
128
123
  display: inline-flex;
129
124
  .icon {
130
- color: var(--component-theme-8);
125
+ color: var(--colour-theme-8);
131
126
  display: inline-block;
132
127
  font-size: 3rem;
133
128
  font-style: normal;
@@ -148,7 +143,7 @@ const dismissPrompt = () => {
148
143
  font-size: var(--step-5);
149
144
  font-weight: bold;
150
145
  line-height: 1.3;
151
- color: var(--component-theme-8);
146
+ color: var(--colour-theme-8);
152
147
  margin: 0;
153
148
  padding: 0;
154
149
  }
@@ -157,7 +152,7 @@ const dismissPrompt = () => {
157
152
  font-size: var(--step-5);
158
153
  font-weight: normal;
159
154
  line-height: 1.3;
160
- color: var(--component-theme-8);
155
+ color: var(--colour-theme-8);
161
156
  margin: 0;
162
157
  padding: 0;
163
158
  }
@@ -169,26 +164,26 @@ const dismissPrompt = () => {
169
164
  justify-content: center;
170
165
  margin: 1rem;
171
166
  padding: 0.5rem;
172
- border: 0.1rem solid var(--component-theme-8);
167
+ border: 0.1rem solid var(--colour-theme-8);
173
168
  border-radius: 50%;
174
- outline: 1px solid var(--component-theme-3);
169
+ outline: 1px solid var(--colour-theme-3);
175
170
 
176
171
  transition: border 200ms ease-in-out, outline 200ms ease-in-out;
177
172
 
178
173
  &:hover {
179
174
  cursor: pointer;
180
- border: 0.1rem solid var(--component-theme-12);
181
- outline: 2px solid var(--component-theme-6);
175
+ border: 0.1rem solid var(--colour-theme-12);
176
+ outline: 2px solid var(--colour-theme-6);
182
177
  }
183
178
 
184
179
  &:focus-visible {
185
180
  box-shadow: var(--focus-box-shadow-colour-on);
186
- border: 0.1rem solid var(--component-theme-12);
187
- outline: 2px solid var(--component-theme-6);
181
+ border: 0.1rem solid var(--colour-theme-12);
182
+ outline: 2px solid var(--colour-theme-6);
188
183
  }
189
184
 
190
185
  .icon {
191
- color: var(--component-theme-8);
186
+ color: var(--colour-theme-8);
192
187
  display: block;
193
188
  font-size: var(--step-5);
194
189
  padding: 1rem;
@@ -0,0 +1,122 @@
1
+ <template>
2
+ <div v-if="displayComponent" class="marquee-scroller" :reverse>
3
+ <ul class="list">
4
+ <li v-for="item in marqueeData" :key="item.id" class="item" :style="{ '--position': item.id }">
5
+ <slot :name="item.id"></slot>
6
+ </li>
7
+ </ul>
8
+ </div>
9
+ </template>
10
+
11
+ <script setup lang="ts">
12
+ const props = defineProps({
13
+ animationRuntime: {
14
+ type: String,
15
+ default: "40s",
16
+ },
17
+ reverse: {
18
+ type: Boolean,
19
+ default: false,
20
+ },
21
+ marqueeData: {
22
+ type: Array as PropType<Array<{ id: number; content: string }>>,
23
+ default: () => [],
24
+ },
25
+ itemConfig: {
26
+ type: Object,
27
+ default: () => ({
28
+ width: "50px",
29
+ height: "50px",
30
+ quantity: 30,
31
+ }),
32
+ required: true,
33
+ },
34
+ })
35
+
36
+ const displayComponent = ref(false)
37
+
38
+ const height = computed(() => props.itemConfig.height)
39
+ const quantity = computed(() => props.itemConfig.quantity)
40
+ const width = computed(() => props.itemConfig.width)
41
+
42
+ const animationRuntimeNumber = computed(() => {
43
+ const [seconds] = props.animationRuntime.split("s")
44
+ return parseFloat(seconds ?? "0")
45
+ })
46
+
47
+ const animationDelay = computed(() => {
48
+ return Math.floor(animationRuntimeNumber.value * 1.25) + "s"
49
+ })
50
+
51
+ onMounted(() => {
52
+ console.log(`Mounted: quantity(${quantity.value}) | animationDelay(${animationDelay.value})`)
53
+ displayComponent.value = true
54
+ })
55
+ </script>
56
+
57
+ <style lang="css">
58
+ .marquee-scroller {
59
+ width: 100%;
60
+ height: v-bind(height);
61
+ overflow: hidden;
62
+ mask-image: linear-gradient(to right, transparent, #000 10% 90%, transparent);
63
+
64
+ &:hover .item {
65
+ animation-play-state: paused !important;
66
+ filter: grayscale(1);
67
+ }
68
+
69
+ &[reverse="true"] .item {
70
+ animation: reversePlay v-bind(animationRuntime) linear infinite;
71
+ }
72
+
73
+ .list {
74
+ display: flex;
75
+ width: 100%;
76
+ height: v-bind(height);
77
+ min-width: calc(v-bind(width) * v-bind(quantity));
78
+ position: relative;
79
+
80
+ .item {
81
+ width: v-bind(width);
82
+ height: v-bind(height);
83
+ display: grid;
84
+ place-items: center;
85
+ position: absolute;
86
+ aspect-ratio: 1 / 1;
87
+ left: 100%;
88
+ animation: autoRun v-bind(animationRuntime) linear infinite;
89
+ transition: filter 0.5s;
90
+ /* animation-delay: calc((50s / v-bind(quantity)) * (var(--position) - 1) - 50s) !important; */
91
+ animation-delay: calc(
92
+ (v-bind(animationDelay) / v-bind(quantity)) * (var(--position) - 1) - v-bind(animationDelay)
93
+ ) !important;
94
+
95
+ border: 1px solid light-dark(var(--gray-12), var(--gray-0));
96
+ border-radius: 4px;
97
+
98
+ &:hover {
99
+ filter: grayscale(0);
100
+ }
101
+ }
102
+ }
103
+ }
104
+
105
+ @keyframes autoRun {
106
+ from {
107
+ left: 100%;
108
+ }
109
+ to {
110
+ left: calc(v-bind(width) * -1);
111
+ }
112
+ }
113
+
114
+ @keyframes reversePlay {
115
+ from {
116
+ left: calc(v-bind(width) * -1);
117
+ }
118
+ to {
119
+ left: 100%;
120
+ }
121
+ }
122
+ </style>