@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.
Files changed (41) hide show
  1. package/package.json +3 -3
  2. package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Artists.jpg +0 -0
  3. package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Communicators.jpg +0 -0
  4. package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Designers.jpg +0 -0
  5. package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Disruptors.jpg +0 -0
  6. package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Dreamers.jpg +0 -0
  7. package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Educators.jpg +0 -0
  8. package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Innovators.jpg +0 -0
  9. package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Inventors.jpg +0 -0
  10. package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Makers.jpg +0 -0
  11. package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Problem_Solvers.jpg +0 -0
  12. package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Robiticists.jpg +0 -0
  13. package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Scientists.jpg +0 -0
  14. package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Software_Engineers.jpg +0 -0
  15. package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Thinkers.jpg +0 -0
  16. package/public/img/SwimlaneCTA/backgroundImages/JPL_is__Visualizers.jpg +0 -0
  17. package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Artists.jpg +0 -0
  18. package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Communicators.jpg +0 -0
  19. package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Designers.jpg +0 -0
  20. package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Disruptors.jpg +0 -0
  21. package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Dreamers.jpg +0 -0
  22. package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Educators.jpg +0 -0
  23. package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Innovators.jpg +0 -0
  24. package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Inventors.jpg +0 -0
  25. package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Makers.jpg +0 -0
  26. package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Problem_Solvers.jpg +0 -0
  27. package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Robiticists.jpg +0 -0
  28. package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Scientists.jpg +0 -0
  29. package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Software_Engineers.jpg +0 -0
  30. package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Thinkers.jpg +0 -0
  31. package/public/img/SwimlaneCTA/backgroundImagesSmall/JPL_is__Visualizers.jpg +0 -0
  32. package/src/components/HomepageMissionsCarousel/HomepageMissionsCarousel.vue +95 -91
  33. package/src/components/HomepageMissionsCarousel/HomepageMissionsCarouselItem.vue +35 -31
  34. package/src/components/SwimlaneCTA/SwimlaneCTA.vue +8 -4
  35. package/src/components/TextInput/TextInput.vue +1 -1
  36. package/src/components/TimelineDialog/TimelineDialog.stories.js +1 -1
  37. package/src/components/YearTicker/YearTicker.vue +15 -7
  38. package/src/templates/PageImageDetail/PageImageDetail.vue +1 -4
  39. package/src/templates/www/PageCuratedGallery/PageCuratedGallery.vue +8 -0
  40. package/src/templates/www/PageHomepage/PageHomepage.vue +60 -57
  41. 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.47",
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.4.21",
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.4",
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",
@@ -1,104 +1,108 @@
1
1
  <template>
2
- <section
2
+ <div
3
3
  v-if="data"
4
- class="HomepageMissionsCarousel ThemeVariantDark bg-stars max-w-screen-3xl mx-auto overflow-hidden text-white bg-black"
4
+ class="bg-star-pattern bg-black"
5
5
  >
6
- <div class="lg:BaseGrid lg:py-24 pt-14 container flex flex-col pb-5 mx-auto">
7
- <div class="xl:px-0 lg:col-end-8 xl:col-end-7 order-1 col-start-2 px-4">
8
- <p
9
- v-if="data.label"
10
- class="text-subtitle text-primary edu:text-white mb-3"
11
- >
12
- {{ data.label }}
13
- </p>
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
- <!-- slide -->
48
- <HomepageMissionsCarouselItem
49
- v-for="(item, index) in data.targets"
50
- :key="index"
51
- :data="item"
52
- class="swiper-slide"
53
- />
54
- </nav>
55
- <!-- swiper navigation -->
56
- <div class="swiper-navigation xl:block absolute top-0 left-0 hidden w-full">
57
- <div class="top-1/2 absolute left-0 z-30">
58
- <BaseButton
59
- class="swiper-prev xl:text-base -ml-16"
60
- :aria-label="data.heading + ' - Previous slide'"
61
- >
62
- <template #icon>
63
- <span
64
- class="arrow-wrapper"
65
- aria-hidden="true"
66
- >
67
- <span class="arrow">
68
- <IconPrev />
69
- </span>
70
- <span class="arrow-fixed">
71
- <IconPrev />
72
- </span>
73
- </span>
74
- </template>
75
- </BaseButton>
76
- </div>
77
- <div class="top-1/2 absolute right-0 z-30">
78
- <BaseButton
79
- class="swiper-next xl:text-base -mr-16"
80
- :aria-label="data.heading + ' - Next slide'"
81
- >
82
- <template #icon>
83
- <span
84
- class="arrow-wrapper"
85
- aria-hidden="true"
86
- >
87
- <span class="arrow">
88
- <IconNext />
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
- <span class="arrow-fixed">
91
- <IconNext />
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
- </span>
94
- </template>
95
- </BaseButton>
97
+ </template>
98
+ </BaseButton>
99
+ </div>
96
100
  </div>
97
101
  </div>
98
102
  </div>
99
103
  </div>
100
- </div>
101
- </section>
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 : { name: 'missions', query: { mission_target: data.name } }"
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">is a place for</p>
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 class="text-secondary text-contrast lg:text-7xl mb-0 text-4xl font-medium">
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>
@@ -10,7 +10,7 @@ interface TextInputProps {
10
10
  maxlength?: string
11
11
  pattern?: string
12
12
  title?: string
13
- autofocus?: boolean
13
+ autoFocus?: boolean
14
14
  labelClass?: string
15
15
  inputClass?: string
16
16
  }
@@ -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
- :duration="duration"
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 = this.year > newYear ? 'digits-increment' : 'digits-decrement'
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
- :to="{
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
- <HomepageMissionsCarousel
63
- v-if="data?.missionsCarousel?.targets?.length"
64
- :data="data.missionsCarousel"
65
- class="lg:my-18 my-10"
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
- <HomepageFeaturedRobot
70
- v-if="data?.featuredRobots?.length"
71
- class="lg:my-24 my-12"
72
- :data="data.featuredRobots[0].page"
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
- Top: ''
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-0 bottom-0 col-start-3 md:col-start-7;
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>