@explorer-1/vue 0.2.47 → 0.2.49
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/package.json +3 -3
- package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Artists.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Communicators.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Designers.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Disruptors.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Dreamers.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Educators.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Innovators.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Inventors.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Makers.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Problem_Solvers.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Robiticists.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Scientists.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Software_Engineers.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Thinkers.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Visualizers.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Artists.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Communicators.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Designers.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Disruptors.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Dreamers.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Educators.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Innovators.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Inventors.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Makers.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Problem_Solvers.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Robiticists.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Scientists.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Software_Engineers.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Thinkers.jpg +0 -0
- package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Visualizers.jpg +0 -0
- package/src/components/HomepageMissionsCarousel/HomepageMissionsCarousel.vue +95 -91
- package/src/components/HomepageMissionsCarousel/HomepageMissionsCarouselItem.vue +35 -31
- package/src/components/SwimlaneCTA/SwimlaneCTA.vue +8 -4
- package/src/components/TextInput/TextInput.vue +1 -1
- package/src/components/TimelineDialog/TimelineDialog.stories.js +1 -1
- package/src/components/YearTicker/YearTicker.vue +15 -7
- package/src/templates/PageImageDetail/PageImageDetail.vue +1 -4
- package/src/templates/www/PageCuratedGallery/PageCuratedGallery.vue +8 -0
- package/src/templates/www/PageHomepage/PageHomepage.vue +60 -57
- package/src/templates/www/PageTimeline/PageTimeline.vue +124 -102
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@explorer-1/vue",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.49",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"swiper": "^11.1.3",
|
|
27
27
|
"tailwindcss": "^3.4.3",
|
|
28
28
|
"twitter-widgets": "^2.0.0",
|
|
29
|
-
"vue": "^3.
|
|
29
|
+
"vue": "^3.5.3",
|
|
30
30
|
"vue-bind-once": "^0.2.1",
|
|
31
31
|
"vue3-compare-image": "^1.2.5",
|
|
32
32
|
"vue3-observe-visibility": "^1.0.1",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@vitejs/plugin-vue": "^5.0.4",
|
|
37
|
-
"a11y-dialog": "^8.0
|
|
37
|
+
"a11y-dialog": "^8.1.0",
|
|
38
38
|
"autoprefixer": "^10.4.19",
|
|
39
39
|
"postcss": "^8.4.38",
|
|
40
40
|
"postcss-import": "^16.1.0",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,104 +1,108 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<div
|
|
3
3
|
v-if="data"
|
|
4
|
-
class="
|
|
4
|
+
class="bg-star-pattern bg-black"
|
|
5
5
|
>
|
|
6
|
-
<
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
<BaseHeading
|
|
15
|
-
v-if="data.heading"
|
|
16
|
-
level="h2"
|
|
17
|
-
class="mb-3"
|
|
18
|
-
>
|
|
19
|
-
{{ data.heading }}
|
|
20
|
-
</BaseHeading>
|
|
21
|
-
<p
|
|
22
|
-
v-if="data.summary"
|
|
23
|
-
class="text-body-md"
|
|
24
|
-
>
|
|
25
|
-
{{ data.summary }}
|
|
26
|
-
</p>
|
|
27
|
-
</div>
|
|
28
|
-
<div class="lg:order-2 xl:px-0 flex justify-end order-3 col-start-10 col-end-13 px-4">
|
|
29
|
-
<BaseLink
|
|
30
|
-
:to="{ name: 'missions' }"
|
|
31
|
-
variant="primary"
|
|
32
|
-
>
|
|
33
|
-
All Missions
|
|
34
|
-
</BaseLink>
|
|
35
|
-
</div>
|
|
36
|
-
<!-- Slider main container -->
|
|
37
|
-
<div class="lg:order-3 order-2 col-start-2 col-end-12">
|
|
38
|
-
<div
|
|
39
|
-
ref="HomepageMissionsCarousel"
|
|
40
|
-
class="swiper lg:mt-0 lg:mb-0 mb-14 mt-3"
|
|
41
|
-
>
|
|
42
|
-
<!-- Additional required wrapper -->
|
|
43
|
-
<nav
|
|
44
|
-
:aria-label="data.heading || 'Missions'"
|
|
45
|
-
class="swiper-wrapper"
|
|
6
|
+
<section
|
|
7
|
+
class="HomepageMissionsCarousel ThemeVariantDark max-w-screen-3xl mx-auto overflow-hidden text-white bg-black bg-stars select-none"
|
|
8
|
+
>
|
|
9
|
+
<div class="lg:BaseGrid lg:py-24 pt-14 container flex flex-col pb-5 mx-auto">
|
|
10
|
+
<div class="xl:px-0 lg:col-end-8 xl:col-end-7 order-1 col-start-2 px-4">
|
|
11
|
+
<p
|
|
12
|
+
v-if="data.label"
|
|
13
|
+
class="text-subtitle text-primary edu:text-white mb-3"
|
|
46
14
|
>
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
15
|
+
{{ data.label }}
|
|
16
|
+
</p>
|
|
17
|
+
<BaseHeading
|
|
18
|
+
v-if="data.heading"
|
|
19
|
+
level="h2"
|
|
20
|
+
class="mb-3"
|
|
21
|
+
>
|
|
22
|
+
{{ data.heading }}
|
|
23
|
+
</BaseHeading>
|
|
24
|
+
<p
|
|
25
|
+
v-if="data.summary"
|
|
26
|
+
class="text-body-md"
|
|
27
|
+
>
|
|
28
|
+
{{ data.summary }}
|
|
29
|
+
</p>
|
|
30
|
+
</div>
|
|
31
|
+
<div class="lg:order-2 xl:px-0 flex justify-end order-3 col-start-10 col-end-13 px-4">
|
|
32
|
+
<BaseLink
|
|
33
|
+
to="/missions/"
|
|
34
|
+
variant="primary"
|
|
35
|
+
>
|
|
36
|
+
{{ `All Missions` }}
|
|
37
|
+
</BaseLink>
|
|
38
|
+
</div>
|
|
39
|
+
<!-- Slider main container -->
|
|
40
|
+
<div class="lg:order-3 order-2 col-start-2 col-end-12">
|
|
41
|
+
<div
|
|
42
|
+
ref="HomepageMissionsCarousel"
|
|
43
|
+
class="swiper lg:mt-0 lg:mb-0 mb-14 mt-3"
|
|
44
|
+
>
|
|
45
|
+
<!-- Additional required wrapper -->
|
|
46
|
+
<nav
|
|
47
|
+
:aria-label="data.heading || 'Missions'"
|
|
48
|
+
class="swiper-wrapper"
|
|
49
|
+
>
|
|
50
|
+
<!-- slide -->
|
|
51
|
+
<HomepageMissionsCarouselItem
|
|
52
|
+
v-for="(item, index) in data.targets"
|
|
53
|
+
:key="index"
|
|
54
|
+
:data="item"
|
|
55
|
+
class="swiper-slide"
|
|
56
|
+
/>
|
|
57
|
+
</nav>
|
|
58
|
+
<!-- swiper navigation -->
|
|
59
|
+
<div class="swiper-navigation xl:block absolute top-0 left-0 hidden w-full">
|
|
60
|
+
<div class="top-1/2 absolute left-0 z-30">
|
|
61
|
+
<BaseButton
|
|
62
|
+
class="swiper-prev xl:text-base -ml-16"
|
|
63
|
+
:aria-label="data.heading + ' - Previous slide'"
|
|
64
|
+
>
|
|
65
|
+
<template #icon>
|
|
66
|
+
<span
|
|
67
|
+
class="arrow-wrapper"
|
|
68
|
+
aria-hidden="true"
|
|
69
|
+
>
|
|
70
|
+
<span class="arrow">
|
|
71
|
+
<IconPrev />
|
|
72
|
+
</span>
|
|
73
|
+
<span class="arrow-fixed">
|
|
74
|
+
<IconPrev />
|
|
75
|
+
</span>
|
|
89
76
|
</span>
|
|
90
|
-
|
|
91
|
-
|
|
77
|
+
</template>
|
|
78
|
+
</BaseButton>
|
|
79
|
+
</div>
|
|
80
|
+
<div class="top-1/2 absolute right-0 z-30">
|
|
81
|
+
<BaseButton
|
|
82
|
+
class="swiper-next xl:text-base -mr-16"
|
|
83
|
+
:aria-label="data.heading + ' - Next slide'"
|
|
84
|
+
>
|
|
85
|
+
<template #icon>
|
|
86
|
+
<span
|
|
87
|
+
class="arrow-wrapper"
|
|
88
|
+
aria-hidden="true"
|
|
89
|
+
>
|
|
90
|
+
<span class="arrow">
|
|
91
|
+
<IconNext />
|
|
92
|
+
</span>
|
|
93
|
+
<span class="arrow-fixed">
|
|
94
|
+
<IconNext />
|
|
95
|
+
</span>
|
|
92
96
|
</span>
|
|
93
|
-
</
|
|
94
|
-
</
|
|
95
|
-
</
|
|
97
|
+
</template>
|
|
98
|
+
</BaseButton>
|
|
99
|
+
</div>
|
|
96
100
|
</div>
|
|
97
101
|
</div>
|
|
98
102
|
</div>
|
|
99
103
|
</div>
|
|
100
|
-
</
|
|
101
|
-
</
|
|
104
|
+
</section>
|
|
105
|
+
</div>
|
|
102
106
|
</template>
|
|
103
107
|
<script lang="ts">
|
|
104
108
|
import { defineComponent } from 'vue'
|
|
@@ -1,9 +1,43 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// HomepageMissionsCarouselItem.vue
|
|
3
|
+
/* Slide for the homepage missions carousel.
|
|
4
|
+
Links to the topic page if provided,
|
|
5
|
+
otherwise links to mission target as fallback.
|
|
6
|
+
*/
|
|
7
|
+
import { defineComponent } from 'vue'
|
|
8
|
+
import BaseLink from './../BaseLink/BaseLink.vue'
|
|
9
|
+
import IconCaret from './../Icons/IconCaret.vue'
|
|
10
|
+
|
|
11
|
+
export default defineComponent({
|
|
12
|
+
name: 'HomepageMissionsCarouselItem',
|
|
13
|
+
components: {
|
|
14
|
+
BaseLink,
|
|
15
|
+
IconCaret
|
|
16
|
+
},
|
|
17
|
+
props: {
|
|
18
|
+
data: {
|
|
19
|
+
type: Object,
|
|
20
|
+
required: false,
|
|
21
|
+
default: undefined
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
computed: {
|
|
25
|
+
hasTopic(): boolean {
|
|
26
|
+
if (this.data && this.data.topic && this.data.topic.url) {
|
|
27
|
+
return true
|
|
28
|
+
}
|
|
29
|
+
return false
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
</script>
|
|
34
|
+
|
|
1
35
|
<template>
|
|
2
36
|
<BaseLink
|
|
3
37
|
v-if="data"
|
|
4
38
|
variant="none"
|
|
5
39
|
:aria-label="hasTopic ? 'More about ' + data.topic.title : 'View ' + data.name + ' missions'"
|
|
6
|
-
:to="hasTopic ? data.topic.url :
|
|
40
|
+
:to="hasTopic ? data.topic.url : '/missions/?mission_target=' + data.name"
|
|
7
41
|
class="HomepageMissionsCarouselItem group flex h-full pb-1 text-white"
|
|
8
42
|
link-class="flex flex-col w-full"
|
|
9
43
|
>
|
|
@@ -50,37 +84,7 @@
|
|
|
50
84
|
</div>
|
|
51
85
|
</BaseLink>
|
|
52
86
|
</template>
|
|
53
|
-
<script lang="ts">
|
|
54
|
-
/* Slide for the homepage missions carousel.
|
|
55
|
-
Links to the topic page if provided,
|
|
56
|
-
otherwise links to mission target as fallback.
|
|
57
|
-
*/
|
|
58
|
-
import { defineComponent } from 'vue'
|
|
59
|
-
import BaseLink from './../BaseLink/BaseLink.vue'
|
|
60
|
-
import IconCaret from './../Icons/IconCaret.vue'
|
|
61
87
|
|
|
62
|
-
export default defineComponent({
|
|
63
|
-
name: 'HomepageMissionsCarouselItem',
|
|
64
|
-
components: {
|
|
65
|
-
BaseLink,
|
|
66
|
-
IconCaret
|
|
67
|
-
},
|
|
68
|
-
props: {
|
|
69
|
-
data: {
|
|
70
|
-
type: Object,
|
|
71
|
-
required: false
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
computed: {
|
|
75
|
-
hasTopic(): boolean {
|
|
76
|
-
if (this.data && this.data.topic && this.data.topic.url) {
|
|
77
|
-
return true
|
|
78
|
-
}
|
|
79
|
-
return false
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
})
|
|
83
|
-
</script>
|
|
84
88
|
<style lang="scss">
|
|
85
89
|
.HomepageMissionsCarouselItem {
|
|
86
90
|
.text-wrapper {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="justify-items-center flex w-full">
|
|
2
|
+
<div class="justify-items-center flex w-full relative overflow-hidden">
|
|
3
3
|
<BaseLink
|
|
4
4
|
v-if="targetLink"
|
|
5
5
|
class="w-full"
|
|
@@ -42,7 +42,9 @@
|
|
|
42
42
|
</template>
|
|
43
43
|
</div>
|
|
44
44
|
<div class="lg:p-1 text-contrast relative z-20 pt-2 pl-0">
|
|
45
|
-
<p class="text-secondary lg:text-7xl mb-0 text-4xl font-medium">
|
|
45
|
+
<p class="text-secondary text-white lg:text-7xl mb-0 text-4xl font-medium">
|
|
46
|
+
is a place for
|
|
47
|
+
</p>
|
|
46
48
|
</div>
|
|
47
49
|
</div>
|
|
48
50
|
|
|
@@ -66,7 +68,7 @@
|
|
|
66
68
|
:class="'SwimlaneCTA lg:hidden block' + computedClass"
|
|
67
69
|
>
|
|
68
70
|
<div
|
|
69
|
-
class="lg:flex-row lg:pr-12 lg:py-5 flex flex-col justify-center w-full px-5 py-6 bg-black bg-opacity-25"
|
|
71
|
+
class="lg:flex-row lg:pr-12 lg:py-5 flex flex-col justify-center w-full px-5 py-6 bg-black bg-opacity-25 font-primary"
|
|
70
72
|
>
|
|
71
73
|
<div class="lg:flex-row lg:pr-3 lg:pb-0 flex flex-col items-center justify-center pb-2">
|
|
72
74
|
<div class="relative z-20 p-2 pl-0">
|
|
@@ -92,7 +94,9 @@
|
|
|
92
94
|
</template>
|
|
93
95
|
</div>
|
|
94
96
|
<div class="lg:p-1 relative z-20 pt-2 pl-0">
|
|
95
|
-
<p
|
|
97
|
+
<p
|
|
98
|
+
class="text-secondary text-white text-contrast lg:text-7xl mb-0 text-4xl font-medium"
|
|
99
|
+
>
|
|
96
100
|
is a place for
|
|
97
101
|
</p>
|
|
98
102
|
</div>
|
|
@@ -39,7 +39,7 @@ export const BaseStory = {
|
|
|
39
39
|
setup() {
|
|
40
40
|
return { args }
|
|
41
41
|
},
|
|
42
|
-
template: `<div>
|
|
42
|
+
template: `<div style="min-height: 80vh">
|
|
43
43
|
<button type="button" :data-a11y-dialog-show="args.data.id">Open the dialog</button>
|
|
44
44
|
<TimelineDialog :data="args.data" :dialog-box-class="args.dialogBoxClass"></TimelineDialog>
|
|
45
45
|
</div>`
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
|
+
v-if="mounted"
|
|
3
4
|
class="YearTicker"
|
|
4
5
|
:style="`--duration:${duration}ms`"
|
|
5
6
|
>
|
|
@@ -8,17 +9,19 @@
|
|
|
8
9
|
v-for="(digit, index) in Array.from(targetYear)"
|
|
9
10
|
:key="index"
|
|
10
11
|
:name="animation"
|
|
11
|
-
|
|
12
|
+
mode="out-in"
|
|
13
|
+
tag="span"
|
|
12
14
|
>
|
|
13
15
|
<!-- Key by digit so there can be two digits rendered at the same time. -->
|
|
14
16
|
<span
|
|
15
|
-
:key="digit"
|
|
17
|
+
:key="digit + index"
|
|
16
18
|
class="Digit"
|
|
17
19
|
>{{ digit }}</span
|
|
18
20
|
>
|
|
19
21
|
</transition-group>
|
|
20
22
|
</div>
|
|
21
23
|
</template>
|
|
24
|
+
|
|
22
25
|
<script lang="ts">
|
|
23
26
|
import { defineComponent } from 'vue'
|
|
24
27
|
|
|
@@ -34,17 +37,22 @@ export default defineComponent({
|
|
|
34
37
|
return {
|
|
35
38
|
year: this.targetYear,
|
|
36
39
|
animation: 'digits-increment',
|
|
37
|
-
duration: 100
|
|
40
|
+
duration: 100,
|
|
41
|
+
mounted: false // Track if the component is mounted
|
|
38
42
|
}
|
|
39
43
|
},
|
|
40
44
|
watch: {
|
|
41
|
-
targetYear(newYear) {
|
|
42
|
-
this.animation =
|
|
45
|
+
targetYear(newYear, oldYear) {
|
|
46
|
+
this.animation = newYear > oldYear ? 'digits-increment' : 'digits-decrement'
|
|
43
47
|
this.year = newYear
|
|
44
48
|
}
|
|
49
|
+
},
|
|
50
|
+
mounted() {
|
|
51
|
+
this.mounted = true // Set mounted to true after mounting
|
|
45
52
|
}
|
|
46
53
|
})
|
|
47
54
|
</script>
|
|
55
|
+
|
|
48
56
|
<style lang="scss">
|
|
49
57
|
.YearTicker {
|
|
50
58
|
@apply sticky transform text-gray-light-mid translate-x-0.5 md:translate-x-0 ml-7 md:ml-8 lg:ml-30 pt-10 md:pt-5;
|
|
@@ -78,7 +86,7 @@ export default defineComponent({
|
|
|
78
86
|
|
|
79
87
|
.header-sticky-showing & {
|
|
80
88
|
// For the mobile viewport, offset by the height of the top header and NavSecondary combined.
|
|
81
|
-
top: calc(theme('spacing.18') + theme('spacing.16') + var(--top-offset));
|
|
89
|
+
top: calc(theme('spacing.18') + theme('spacing.16') + var (--top-offset));
|
|
82
90
|
}
|
|
83
91
|
|
|
84
92
|
&::after {
|
|
@@ -103,7 +111,6 @@ export default defineComponent({
|
|
|
103
111
|
}
|
|
104
112
|
|
|
105
113
|
.Digit {
|
|
106
|
-
// Force all digits in the same cell so they superpose.
|
|
107
114
|
grid-area: 1 / 1 / 1 / 1;
|
|
108
115
|
transition: transform var(--duration);
|
|
109
116
|
transform: translateY(0);
|
|
@@ -112,6 +119,7 @@ export default defineComponent({
|
|
|
112
119
|
transition-duration: 0.1ms;
|
|
113
120
|
}
|
|
114
121
|
}
|
|
122
|
+
|
|
115
123
|
// transitions
|
|
116
124
|
.digits-decrement-enter-active {
|
|
117
125
|
transform: translateY(100%);
|
|
@@ -159,10 +159,7 @@
|
|
|
159
159
|
<BaseLink
|
|
160
160
|
variant="none"
|
|
161
161
|
link-class="font-primary text-jpl-red hover:text-jpl-red-light w-full py-3 text-lg"
|
|
162
|
-
|
|
163
|
-
name: 'missions',
|
|
164
|
-
query: { mission_target: item.target }
|
|
165
|
-
}"
|
|
162
|
+
to="/missions/?mission_target={{ item.target }}"
|
|
166
163
|
>
|
|
167
164
|
{{ item.target }}
|
|
168
165
|
</BaseLink>
|
|
@@ -265,6 +265,14 @@ export default defineComponent({
|
|
|
265
265
|
|
|
266
266
|
<style lang="scss">
|
|
267
267
|
.PageCuratedGallery {
|
|
268
|
+
// sharebuttons fix TODO: make better
|
|
269
|
+
@screen lg {
|
|
270
|
+
.ShareButtons {
|
|
271
|
+
transform: scale(0.75);
|
|
272
|
+
top: -1.8rem;
|
|
273
|
+
z-index: 999;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
268
276
|
.CuratedGallery-thumbnails,
|
|
269
277
|
.CuratedGallery-carousel {
|
|
270
278
|
@apply block;
|
|
@@ -1,3 +1,51 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { defineComponent } from 'vue'
|
|
3
|
+
import HomepageCarousel from './../../../components/HomepageCarousel/HomepageCarousel.vue'
|
|
4
|
+
import HomepageFeaturedRobot from './../../../components/HomepageFeaturedRobot/HomepageFeaturedRobot.vue'
|
|
5
|
+
import HomepageTeaserBlock from './../../../components/HomepageTeaserBlock/HomepageTeaserBlock.vue'
|
|
6
|
+
import BlockLinkCarousel from './../../../components/BlockLinkCarousel/BlockLinkCarousel.vue'
|
|
7
|
+
import BlockQuote from './../../../components/BlockQuote/BlockQuote.vue'
|
|
8
|
+
import LayoutHelper from './../../../components/LayoutHelper/LayoutHelper.vue'
|
|
9
|
+
import SwimlaneCTA from './../../../components/SwimlaneCTA/SwimlaneCTA.vue'
|
|
10
|
+
import HomepageMissionsCarousel from './../../../components/HomepageMissionsCarousel/HomepageMissionsCarousel.vue'
|
|
11
|
+
import HomepageStats from './../../../components/HomepageStats/HomepageStats.vue'
|
|
12
|
+
import HomepageEmbedBlock from './../../../components/HomepageEmbedBlock/HomepageEmbedBlock.vue'
|
|
13
|
+
|
|
14
|
+
export default defineComponent({
|
|
15
|
+
name: 'PageHomepage',
|
|
16
|
+
components: {
|
|
17
|
+
HomepageCarousel,
|
|
18
|
+
HomepageFeaturedRobot,
|
|
19
|
+
HomepageTeaserBlock,
|
|
20
|
+
BlockLinkCarousel,
|
|
21
|
+
BlockQuote,
|
|
22
|
+
LayoutHelper,
|
|
23
|
+
SwimlaneCTA,
|
|
24
|
+
HomepageMissionsCarousel,
|
|
25
|
+
HomepageStats,
|
|
26
|
+
HomepageEmbedBlock
|
|
27
|
+
},
|
|
28
|
+
props: {
|
|
29
|
+
data: {
|
|
30
|
+
type: Object,
|
|
31
|
+
required: false,
|
|
32
|
+
default: undefined
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
computed: {
|
|
36
|
+
theNews(): any[] | undefined {
|
|
37
|
+
// check first for featured news
|
|
38
|
+
if (this.data?.featuredNews?.length) {
|
|
39
|
+
return this.data.featuredNews
|
|
40
|
+
} else if (this.data?.latestNews?.length) {
|
|
41
|
+
return this.data.latestNews
|
|
42
|
+
}
|
|
43
|
+
return undefined
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
</script>
|
|
48
|
+
|
|
1
49
|
<template>
|
|
2
50
|
<div
|
|
3
51
|
v-if="data"
|
|
@@ -59,18 +107,20 @@
|
|
|
59
107
|
</template>
|
|
60
108
|
|
|
61
109
|
<!-- mission carousel -->
|
|
62
|
-
<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
110
|
+
<template v-if="data?.missionsCarousel && data?.missionsCarousel[0]?.targets?.length">
|
|
111
|
+
<HomepageMissionsCarousel
|
|
112
|
+
:data="data.missionsCarousel[0]"
|
|
113
|
+
class="lg:my-18 my-10"
|
|
114
|
+
/>
|
|
115
|
+
</template>
|
|
67
116
|
|
|
68
117
|
<!-- featured robot -->
|
|
69
|
-
<
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
118
|
+
<template v-if="data?.featuredRobots?.length">
|
|
119
|
+
<HomepageFeaturedRobot
|
|
120
|
+
class="lg:my-24 my-12"
|
|
121
|
+
:data="data.featuredRobots[0].page"
|
|
122
|
+
/>
|
|
123
|
+
</template>
|
|
74
124
|
|
|
75
125
|
<!-- engage -->
|
|
76
126
|
<div
|
|
@@ -87,50 +137,3 @@
|
|
|
87
137
|
<SwimlaneCTA />
|
|
88
138
|
</div>
|
|
89
139
|
</template>
|
|
90
|
-
<script lang="ts">
|
|
91
|
-
import { defineComponent } from 'vue'
|
|
92
|
-
import HomepageCarousel from './../../../components/HomepageCarousel/HomepageCarousel.vue'
|
|
93
|
-
import HomepageFeaturedRobot from './../../../components/HomepageFeaturedRobot/HomepageFeaturedRobot.vue'
|
|
94
|
-
import HomepageTeaserBlock from './../../../components/HomepageTeaserBlock/HomepageTeaserBlock.vue'
|
|
95
|
-
import BlockLinkCarousel from './../../../components/BlockLinkCarousel/BlockLinkCarousel.vue'
|
|
96
|
-
import BlockQuote from './../../../components/BlockQuote/BlockQuote.vue'
|
|
97
|
-
import LayoutHelper from './../../../components/LayoutHelper/LayoutHelper.vue'
|
|
98
|
-
import SwimlaneCTA from './../../../components/SwimlaneCTA/SwimlaneCTA.vue'
|
|
99
|
-
import HomepageMissionsCarousel from './../../../components/HomepageMissionsCarousel/HomepageMissionsCarousel.vue'
|
|
100
|
-
import HomepageStats from './../../../components/HomepageStats/HomepageStats.vue'
|
|
101
|
-
import HomepageEmbedBlock from './../../../components/HomepageEmbedBlock/HomepageEmbedBlock.vue'
|
|
102
|
-
|
|
103
|
-
export default defineComponent({
|
|
104
|
-
name: 'PageHomepage',
|
|
105
|
-
components: {
|
|
106
|
-
HomepageCarousel,
|
|
107
|
-
HomepageFeaturedRobot,
|
|
108
|
-
HomepageTeaserBlock,
|
|
109
|
-
BlockLinkCarousel,
|
|
110
|
-
BlockQuote,
|
|
111
|
-
LayoutHelper,
|
|
112
|
-
SwimlaneCTA,
|
|
113
|
-
HomepageMissionsCarousel,
|
|
114
|
-
HomepageStats,
|
|
115
|
-
HomepageEmbedBlock
|
|
116
|
-
},
|
|
117
|
-
props: {
|
|
118
|
-
data: {
|
|
119
|
-
type: Object,
|
|
120
|
-
required: false,
|
|
121
|
-
default: undefined
|
|
122
|
-
}
|
|
123
|
-
},
|
|
124
|
-
computed: {
|
|
125
|
-
theNews(): any[] | undefined {
|
|
126
|
-
// check first for featured news
|
|
127
|
-
if (this.data?.featuredNews?.length) {
|
|
128
|
-
return this.data.featuredNews
|
|
129
|
-
} else if (this.data?.latestNews?.length) {
|
|
130
|
-
return this.data.latestNews
|
|
131
|
-
}
|
|
132
|
-
return undefined
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
})
|
|
136
|
-
</script>
|
|
@@ -1,114 +1,29 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div
|
|
3
|
-
v-if="data"
|
|
4
|
-
class="-nav-offset"
|
|
5
|
-
>
|
|
6
|
-
<HeroLarge
|
|
7
|
-
:title="data.heroTitle"
|
|
8
|
-
:summary="data.heroSummary"
|
|
9
|
-
:image="data.heroImage"
|
|
10
|
-
has-overlay
|
|
11
|
-
/>
|
|
12
|
-
<!--
|
|
13
|
-
/**
|
|
14
|
-
* This page contains a lot of overlapping elements. Layers from highest to lowest:
|
|
15
|
-
* - TimelineDialog
|
|
16
|
-
* - Dropdown menus within NavSecondary
|
|
17
|
-
* - BackToTop, Main navigation, NavSecondary
|
|
18
|
-
* - (YearTicker on mobile viewport)
|
|
19
|
-
* - Milestone cards (BlockMilestone / BlockCircleImageCard)
|
|
20
|
-
* - VerticalLine
|
|
21
|
-
* - Milestone background images
|
|
22
|
-
* - (YearTicker on desktop viewport)
|
|
23
|
-
*/ -->
|
|
24
|
-
<NavSecondary>
|
|
25
|
-
<!-- Use a fixed height to simplify sticky positioning. -->
|
|
26
|
-
<div class="w-full flex gap-4 align-content h-16">
|
|
27
|
-
<!-- Use a SearchSelectMenu for consistency with the "sort by" dropdown. -->
|
|
28
|
-
<SearchSelectMenu
|
|
29
|
-
v-model="jumpTo"
|
|
30
|
-
v-model:select-value="jumpTo"
|
|
31
|
-
class="flex"
|
|
32
|
-
:options="getJumpDecades()"
|
|
33
|
-
title="Jump to"
|
|
34
|
-
group-key="jumpTo"
|
|
35
|
-
/>
|
|
36
|
-
<SearchSelectMenu
|
|
37
|
-
v-model="sortBy"
|
|
38
|
-
v-model:select-value="sortBy"
|
|
39
|
-
class="flex"
|
|
40
|
-
:options="sortByOptions"
|
|
41
|
-
title="Sort by"
|
|
42
|
-
group-key="sortBy"
|
|
43
|
-
/>
|
|
44
|
-
</div>
|
|
45
|
-
</NavSecondary>
|
|
46
|
-
<YearTicker :target-year="currentYear as string" />
|
|
47
|
-
<h2 class="sr-only">Milestones</h2>
|
|
48
|
-
<ParallaxContainer
|
|
49
|
-
class="MilestoneList BaseGrid"
|
|
50
|
-
:style="{ '--rows': milestones.length }"
|
|
51
|
-
>
|
|
52
|
-
<div
|
|
53
|
-
v-for="(milestone, index) in milestones"
|
|
54
|
-
:id="`milestone-${milestone.date}`"
|
|
55
|
-
:key="`${milestone.id}-fg`"
|
|
56
|
-
class="MilestoneListItem"
|
|
57
|
-
:style="{ '--row': index + 1 }"
|
|
58
|
-
:data-milestone-year="milestone.date.split('-')[0]"
|
|
59
|
-
>
|
|
60
|
-
<ParallaxElement
|
|
61
|
-
v-if="milestone.backgroundImage"
|
|
62
|
-
class="MilestoneImageWrapper"
|
|
63
|
-
:factor="index % 2 === 0 ? 0.18 : 0.22"
|
|
64
|
-
:offset="index * 80"
|
|
65
|
-
>
|
|
66
|
-
<img
|
|
67
|
-
class="MilestoneImage"
|
|
68
|
-
:src="milestone.backgroundImage.src.url"
|
|
69
|
-
:width="milestone.backgroundImage.src.width"
|
|
70
|
-
:height="milestone.backgroundImage.src.height"
|
|
71
|
-
:srcset="`${milestone.backgroundImage.screenMd.url} ${milestone.backgroundImage.screenMd.width}w ${milestone.backgroundImage.src.url} ${milestone.backgroundImage.src.width}w`"
|
|
72
|
-
:style="{
|
|
73
|
-
// Shift images left or right based on column, and then by a variable amount to create a staggered effect.
|
|
74
|
-
'--x-translate': `${(index % 2 === 0 ? 180 : -130) + 10 * (index % 3)}%`
|
|
75
|
-
}"
|
|
76
|
-
alt=""
|
|
77
|
-
/>
|
|
78
|
-
</ParallaxElement>
|
|
79
|
-
<BlockMilestone
|
|
80
|
-
:data="milestone"
|
|
81
|
-
:handle-click="showMilestone"
|
|
82
|
-
/>
|
|
83
|
-
</div>
|
|
84
|
-
<div class="VerticalLine"></div>
|
|
85
|
-
</ParallaxContainer>
|
|
86
|
-
<BackToTop
|
|
87
|
-
class="fixed right-10 bottom-10"
|
|
88
|
-
:threshold="900"
|
|
89
|
-
@click="handleBackToTop()"
|
|
90
|
-
/>
|
|
91
|
-
|
|
92
|
-
<TimelineDialog
|
|
93
|
-
v-if="activeMilestone"
|
|
94
|
-
:data="activeMilestone"
|
|
95
|
-
dialog-box-class="sm:max-w-xl md:max-w-3xl"
|
|
96
|
-
@hide="() => (activeMilestone = null)"
|
|
97
|
-
></TimelineDialog>
|
|
98
|
-
</div>
|
|
99
|
-
</template>
|
|
100
1
|
<script lang="ts">
|
|
2
|
+
// PageTimeline.vue - Timeline component for displaying milestones.
|
|
101
3
|
import { defineComponent, type PropType } from 'vue'
|
|
102
4
|
import BackToTop from './../../../components/BackToTop/BackToTop.vue'
|
|
103
5
|
import BlockMilestone, { Milestone } from './BlockMilestone.vue'
|
|
104
6
|
import HeroLarge from './../../../components/HeroLarge/HeroLarge.vue'
|
|
105
7
|
import NavSecondary from './../../../components/NavSecondary/NavSecondary.vue'
|
|
8
|
+
// Use a SearchSelectMenu for consistency with the "sort by" dropdown.
|
|
106
9
|
import SearchSelectMenu from './../../../components/SearchSelectMenu/SearchSelectMenu.vue'
|
|
107
10
|
import TimelineDialog from './../../../components/TimelineDialog/TimelineDialog.vue'
|
|
108
11
|
import YearTicker from './../../../components/YearTicker/YearTicker.vue'
|
|
109
12
|
import ParallaxContainer from './../../../components/ParallaxContainer/ParallaxContainer.vue'
|
|
110
13
|
import ParallaxElement from './../../../components/ParallaxElement/ParallaxElement.vue'
|
|
111
14
|
|
|
15
|
+
/**
|
|
16
|
+
* This page contains a lot of overlapping elements. Layers from highest to lowest:
|
|
17
|
+
* - TimelineDialog
|
|
18
|
+
* - Dropdown menus within NavSecondary
|
|
19
|
+
* - BackToTop, Main navigation, NavSecondary
|
|
20
|
+
* - (YearTicker on mobile viewport)
|
|
21
|
+
* - Milestone cards (BlockMilestone / BlockCircleImageCard)
|
|
22
|
+
* - VerticalLine
|
|
23
|
+
* - Milestone background images
|
|
24
|
+
* - (YearTicker on desktop viewport)
|
|
25
|
+
*/
|
|
26
|
+
|
|
112
27
|
type SortBy = '' | 'latestDate' | 'oldestDate'
|
|
113
28
|
|
|
114
29
|
type Decades = {
|
|
@@ -243,7 +158,8 @@ export default defineComponent({
|
|
|
243
158
|
},
|
|
244
159
|
getJumpDecades() {
|
|
245
160
|
const decades: Decades = {
|
|
246
|
-
|
|
161
|
+
// uppercase string to avoid conflict with the lowercase 's' in the decade
|
|
162
|
+
TOP: ''
|
|
247
163
|
}
|
|
248
164
|
|
|
249
165
|
this.milestones.forEach((milestone) => {
|
|
@@ -301,16 +217,112 @@ export default defineComponent({
|
|
|
301
217
|
}
|
|
302
218
|
})
|
|
303
219
|
</script>
|
|
220
|
+
|
|
221
|
+
<template>
|
|
222
|
+
<div
|
|
223
|
+
v-if="data"
|
|
224
|
+
class="-nav-offset"
|
|
225
|
+
>
|
|
226
|
+
<HeroLarge
|
|
227
|
+
:title="data.heroTitle"
|
|
228
|
+
:summary="data.heroSummary"
|
|
229
|
+
:image="data.heroImage"
|
|
230
|
+
has-overlay
|
|
231
|
+
/>
|
|
232
|
+
|
|
233
|
+
<NavSecondary>
|
|
234
|
+
<!-- Use a fixed height to simplify sticky positioning. -->
|
|
235
|
+
<div class="w-full flex gap-4 align-content h-16">
|
|
236
|
+
<SearchSelectMenu
|
|
237
|
+
v-model="jumpTo"
|
|
238
|
+
v-model:select-value="jumpTo"
|
|
239
|
+
class="flex"
|
|
240
|
+
:options="getJumpDecades()"
|
|
241
|
+
title="JUMP TO"
|
|
242
|
+
group-key="jumpTo"
|
|
243
|
+
/>
|
|
244
|
+
<SearchSelectMenu
|
|
245
|
+
v-model="sortBy"
|
|
246
|
+
v-model:select-value="sortBy"
|
|
247
|
+
class="flex"
|
|
248
|
+
:options="sortByOptions"
|
|
249
|
+
title="SORT BY"
|
|
250
|
+
group-key="sortBy"
|
|
251
|
+
/>
|
|
252
|
+
</div>
|
|
253
|
+
</NavSecondary>
|
|
254
|
+
<YearTicker :target-year="currentYear as string" />
|
|
255
|
+
<h2 class="sr-only">Milestones</h2>
|
|
256
|
+
<ParallaxContainer
|
|
257
|
+
class="MilestoneList BaseGrid"
|
|
258
|
+
:style="{ '--rows': milestones.length }"
|
|
259
|
+
>
|
|
260
|
+
<div
|
|
261
|
+
v-for="(milestone, index) in milestones"
|
|
262
|
+
:id="`milestone-${milestone.date}`"
|
|
263
|
+
:key="`${milestone.id}-fg`"
|
|
264
|
+
class="MilestoneListItem"
|
|
265
|
+
:style="{ '--row': index + 1 }"
|
|
266
|
+
:data-milestone-year="milestone.date.split('-')[0]"
|
|
267
|
+
>
|
|
268
|
+
<ParallaxElement
|
|
269
|
+
v-if="milestone.backgroundImage"
|
|
270
|
+
class="MilestoneImageWrapper"
|
|
271
|
+
:factor="0.18"
|
|
272
|
+
:offset="index * 80"
|
|
273
|
+
>
|
|
274
|
+
<img
|
|
275
|
+
class="MilestoneImage"
|
|
276
|
+
:src="milestone.backgroundImage.src.url"
|
|
277
|
+
:width="milestone.backgroundImage.src.width"
|
|
278
|
+
:height="milestone.backgroundImage.src.height"
|
|
279
|
+
:srcset="`${milestone.backgroundImage.screenMd.url} ${milestone.backgroundImage.screenMd.width}w ${milestone.backgroundImage.src.url} ${milestone.backgroundImage.src.width}w`"
|
|
280
|
+
:style="{
|
|
281
|
+
// Shift images left or right based on column, and then by a variable amount to create a staggered effect.
|
|
282
|
+
'--x-translate': `${(index % 2 === 0 ? 180 : -130) + 10 * (index % 3)}%`
|
|
283
|
+
}"
|
|
284
|
+
alt=""
|
|
285
|
+
/>
|
|
286
|
+
</ParallaxElement>
|
|
287
|
+
<BlockMilestone
|
|
288
|
+
:data="milestone"
|
|
289
|
+
:handle-click="showMilestone"
|
|
290
|
+
/>
|
|
291
|
+
</div>
|
|
292
|
+
<div class="VerticalLine"></div>
|
|
293
|
+
</ParallaxContainer>
|
|
294
|
+
<BackToTop
|
|
295
|
+
class="fixed right-10 bottom-10"
|
|
296
|
+
:threshold="900"
|
|
297
|
+
@click="handleBackToTop()"
|
|
298
|
+
/>
|
|
299
|
+
|
|
300
|
+
<TimelineDialog
|
|
301
|
+
v-if="activeMilestone"
|
|
302
|
+
:data="activeMilestone"
|
|
303
|
+
dialog-box-class="sm:max-w-xl md:max-w-3xl"
|
|
304
|
+
@hide="() => (activeMilestone = null)"
|
|
305
|
+
></TimelineDialog>
|
|
306
|
+
</div>
|
|
307
|
+
</template>
|
|
308
|
+
|
|
304
309
|
<style lang="scss">
|
|
305
310
|
$line-width: 2px;
|
|
306
311
|
$column-gap-width: 1.5rem;
|
|
307
312
|
|
|
313
|
+
// makes the years able to have a lowercase 's' in the decade
|
|
314
|
+
select {
|
|
315
|
+
&#select_jumpTo {
|
|
316
|
+
text-transform: none;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
308
320
|
.NavSecondary {
|
|
309
321
|
@apply block;
|
|
310
322
|
}
|
|
311
323
|
|
|
312
324
|
.MilestoneList {
|
|
313
|
-
@apply gap-y-0;
|
|
325
|
+
@apply gap-y-0 max-w-screen-4xl mx-auto;
|
|
314
326
|
|
|
315
327
|
// Offset the list up so the vertical line starts from the secondary nav.
|
|
316
328
|
@screen md {
|
|
@@ -347,6 +359,10 @@ $column-gap-width: 1.5rem;
|
|
|
347
359
|
transform: translateX($line-width);
|
|
348
360
|
grid-row: var(--row);
|
|
349
361
|
|
|
362
|
+
&:nth-of-type(1) {
|
|
363
|
+
@apply md:pt-20;
|
|
364
|
+
}
|
|
365
|
+
|
|
350
366
|
&:nth-of-type(even) {
|
|
351
367
|
@apply md:justify-start md:col-start-7;
|
|
352
368
|
|
|
@@ -369,7 +385,7 @@ $column-gap-width: 1.5rem;
|
|
|
369
385
|
}
|
|
370
386
|
|
|
371
387
|
.VerticalLine {
|
|
372
|
-
@apply border-l border-r border-jpl-red top-5 md:top-
|
|
388
|
+
@apply border-l border-r border-jpl-red top-5 md:top-2 bottom-0 col-start-3 md:col-start-7 mb-12;
|
|
373
389
|
|
|
374
390
|
// Use CSS grid positioning rather than position absolute within relative,
|
|
375
391
|
// so we don’t create a new stacking context within the ParallaxContainer.
|
|
@@ -384,5 +400,11 @@ $column-gap-width: 1.5rem;
|
|
|
384
400
|
border-image-slice: 1;
|
|
385
401
|
transform: translateX(-1 * $column-gap-width);
|
|
386
402
|
}
|
|
403
|
+
|
|
404
|
+
&::before {
|
|
405
|
+
@apply absolute -top-px -left-2px w-1 h-30 bg-gradient-to-b from-white to-transparent;
|
|
406
|
+
|
|
407
|
+
content: '';
|
|
408
|
+
}
|
|
387
409
|
}
|
|
388
410
|
</style>
|