@sp-days-framework/slidev-theme-sykehuspartner 1.0.0

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.
@@ -0,0 +1,39 @@
1
+ <template>
2
+ <div class="slidev-layout fact">
3
+ <div v-if="showLogo" class="logo-container">
4
+ <div class="logo-image logo"></div>
5
+ </div>
6
+ <div class="my-auto">
7
+ <slot />
8
+ </div>
9
+ </div>
10
+ </template>
11
+
12
+ <script setup>
13
+ import { computed } from 'vue'
14
+
15
+ const props = defineProps({
16
+ logo: {
17
+ type: Boolean,
18
+ default: true
19
+ }
20
+ })
21
+
22
+ const showLogo = computed(() => {
23
+ if ($slidev?.configs?.frontmatter?.logo !== undefined) {
24
+ return $slidev.configs.frontmatter.logo
25
+ }
26
+ return props.logo
27
+ })
28
+ </script>
29
+
30
+ <style scoped>
31
+ .fact {
32
+ height: 100%;
33
+ display: flex;
34
+ flex-direction: column;
35
+ justify-content: center;
36
+ }
37
+
38
+ /* Logo styling moved to global layout.css */
39
+ </style>
@@ -0,0 +1,34 @@
1
+ <template>
2
+ <div class="slidev-layout full">
3
+ <div v-if="showLogo" class="logo-container">
4
+ <div class="logo-image logo"></div>
5
+ </div>
6
+ <slot />
7
+ </div>
8
+ </template>
9
+
10
+ <script setup>
11
+ import { computed } from 'vue'
12
+
13
+ const props = defineProps({
14
+ logo: {
15
+ type: Boolean,
16
+ default: false
17
+ }
18
+ })
19
+
20
+ const showLogo = computed(() => {
21
+ if ($slidev?.configs?.frontmatter?.logo !== undefined) {
22
+ return $slidev.configs.frontmatter.logo
23
+ }
24
+ return props.logo
25
+ })
26
+ </script>
27
+
28
+ <style scoped>
29
+ .full {
30
+ padding: 0;
31
+ height: 100%;
32
+ width: 100%;
33
+ }
34
+ </style>
@@ -0,0 +1,222 @@
1
+ <template>
2
+ <div class="slidev-layout image-left">
3
+ <div v-if="showLogo" class="logo-container">
4
+ <div class="logo-image logo"></div>
5
+ </div>
6
+ <div class="split-layout">
7
+ <!-- Image container on the left -->
8
+ <div class="image-container" :style="{ width: imageRatioStyle }">
9
+ <div class="image-wrapper" :style="imageContainerStyle">
10
+ <img
11
+ v-if="props.imageSrc"
12
+ :src="getImageUrl(props.imageSrc)"
13
+ :style="combinedImageStyle"
14
+ class="side-image"
15
+ />
16
+ </div>
17
+ </div>
18
+
19
+ <!-- Content container on the right -->
20
+ <div class="content-container" :style="{ width: contentRatioStyle }">
21
+ <div class="content-layout">
22
+ <div ref="headerRef" class="header"></div>
23
+ <div ref="contentRef" class="content-wrapper">
24
+ <slot />
25
+ </div>
26
+ </div>
27
+ </div>
28
+ </div>
29
+ </div>
30
+ </template>
31
+
32
+ <script setup>
33
+ import { computed, ref, onMounted, onBeforeUnmount } from "vue";
34
+ import { getImageUrl, getImageContainerStyle } from "../utils/layoutHelper";
35
+ import { useHeaderContentSplit } from "../utils/headerContentSplitter";
36
+
37
+ const props = defineProps({
38
+ imageSrc: String,
39
+ imageScale: {
40
+ type: String,
41
+ default: "100%",
42
+ },
43
+ imageAlign: {
44
+ type: String,
45
+ default: "center",
46
+ },
47
+ imageRatio: {
48
+ type: String,
49
+ default: "50%",
50
+ },
51
+ logo: {
52
+ type: Boolean,
53
+ default: true,
54
+ },
55
+ textAlignment: {
56
+ type: String,
57
+ default: "center",
58
+ validator: (value) => ["top", "center", "bottom"].includes(value),
59
+ },
60
+ });
61
+
62
+ // Calculate the image ratio style
63
+ const imageRatioStyle = computed(() => {
64
+ // Ensure it's a percentage string
65
+ const ratio = props.imageRatio.endsWith("%")
66
+ ? props.imageRatio
67
+ : `${parseFloat(props.imageRatio)}%`;
68
+ return ratio;
69
+ });
70
+
71
+ // Calculate the content ratio style (remaining space)
72
+ const contentRatioStyle = computed(() => {
73
+ const imageRatio = parseFloat(imageRatioStyle.value);
74
+ return `${100 - imageRatio}%`;
75
+ });
76
+
77
+ // Get container alignment style based on imageAlign
78
+ const imageContainerStyle = computed(() => {
79
+ return getImageContainerStyle(props.imageAlign);
80
+ });
81
+
82
+ // Calculate the combined image style including scale
83
+ const combinedImageStyle = computed(() => {
84
+ const style = {
85
+ height: "100%", // Always fill height
86
+ width: "auto",
87
+ maxWidth: "none", // Override default max-width
88
+ objectFit: "contain", // Use 'contain' to prevent cropping by default
89
+ objectPosition: getObjectPosition(props.imageAlign),
90
+ };
91
+
92
+ // Apply scaling if specified
93
+ if (props.imageScale) {
94
+ if (props.imageScale.endsWith("%")) {
95
+ const scale = parseFloat(props.imageScale) / 100;
96
+ style.height = `${scale * 100}%`;
97
+ } else if (!isNaN(Number(props.imageScale))) {
98
+ const scale = Number(props.imageScale);
99
+ style.height = `${scale * 100}%`;
100
+ }
101
+ }
102
+
103
+ return style;
104
+ });
105
+
106
+ // Helper function to convert alignment to object-position value
107
+ function getObjectPosition(align) {
108
+ switch (align /* Lines 93-102 omitted */) {
109
+ }
110
+ }
111
+
112
+ const showLogo = computed(() => {
113
+ if ($slidev?.configs?.frontmatter?.logo !== undefined) {
114
+ return $slidev.configs.frontmatter.logo;
115
+ }
116
+ return props.logo;
117
+ });
118
+
119
+ // Compute content alignment style based on textAlignment prop
120
+ const contentAlignmentStyle = computed(() => {
121
+ switch (props.textAlignment) {
122
+ case "top":
123
+ return "flex-start";
124
+ case "bottom":
125
+ return "flex-end";
126
+ case "center":
127
+ default:
128
+ return "center";
129
+ }
130
+ });
131
+
132
+ // Setup for header content split
133
+ const headerRef = ref(null);
134
+ const contentRef = ref(null);
135
+
136
+ // Use our utility to handle the header/content split
137
+ const { setupHeaderSplit } = useHeaderContentSplit(headerRef, contentRef);
138
+ let headerSplitter;
139
+
140
+ onMounted(() => {
141
+ // Setup the header splitter when component is mounted
142
+ headerSplitter = setupHeaderSplit();
143
+ });
144
+
145
+ onBeforeUnmount(() => {
146
+ // Clean up when component is unmounted
147
+ if (headerSplitter) {
148
+ headerSplitter.destroy();
149
+ }
150
+ });
151
+ </script>
152
+
153
+ <style scoped>
154
+ .image-left {
155
+ height: 100%;
156
+ width: 100%;
157
+ padding: 0;
158
+ overflow: hidden;
159
+ }
160
+
161
+ .split-layout {
162
+ display: flex;
163
+ height: 100%;
164
+ width: 100%;
165
+ }
166
+
167
+ .image-container {
168
+ height: 100%;
169
+ overflow: hidden;
170
+ position: relative;
171
+ }
172
+
173
+ .image-wrapper {
174
+ height: 100%;
175
+ width: 100%;
176
+ overflow: hidden;
177
+ }
178
+
179
+ .side-image {
180
+ display: block;
181
+ }
182
+
183
+ .content-container {
184
+ height: 100%;
185
+ overflow: auto;
186
+ }
187
+
188
+ .content-layout {
189
+ display: flex;
190
+ flex-direction: column;
191
+ height: 100%;
192
+ }
193
+
194
+ .content-wrapper {
195
+ padding: 1.5rem 2rem;
196
+ flex: 1;
197
+ overflow-y: auto;
198
+ display: flex;
199
+ flex-direction: column;
200
+ justify-content: v-bind(contentAlignmentStyle);
201
+ }
202
+
203
+ .logo-container {
204
+ position: absolute;
205
+ top: 1rem;
206
+ right: 1rem;
207
+ z-index: 10;
208
+ }
209
+
210
+ .header {
211
+ padding: 0 2rem;
212
+ padding-top: 1.5rem;
213
+ margin-bottom: 0;
214
+ width: 100%;
215
+ }
216
+
217
+ .header:empty {
218
+ display: none;
219
+ }
220
+
221
+ /* Logo styling moved to global layout.css */
222
+ </style>
@@ -0,0 +1,218 @@
1
+ <template>
2
+ <div class="slidev-layout image-right">
3
+ <div v-if="showLogo" class="logo-container custom-position">
4
+ <div class="logo-image logo"></div>
5
+ </div>
6
+ <div class="split-layout">
7
+ <!-- Content container on the left -->
8
+ <div class="content-container" :style="{ width: contentRatioStyle }">
9
+ <div class="content-layout">
10
+ <div ref="headerRef" class="header"></div>
11
+ <div ref="contentRef" class="content-wrapper">
12
+ <slot />
13
+ </div>
14
+ </div>
15
+ </div>
16
+
17
+ <!-- Image container on the right -->
18
+ <div class="image-container" :style="{ width: imageRatioStyle }">
19
+ <div class="image-wrapper" :style="imageContainerStyle">
20
+ <img
21
+ v-if="props.imageSrc"
22
+ :src="getImageUrl(props.imageSrc)"
23
+ :style="combinedImageStyle"
24
+ class="side-image"
25
+ />
26
+ </div>
27
+ </div>
28
+ </div>
29
+ </div>
30
+ </template>
31
+
32
+ <script setup>
33
+ import { computed, ref, onMounted, onBeforeUnmount } from "vue";
34
+ import { getImageUrl, getImageContainerStyle } from "../utils/layoutHelper";
35
+ import { useHeaderContentSplit } from "../utils/headerContentSplitter";
36
+
37
+ const props = defineProps({
38
+ imageSrc: String,
39
+ imageScale: {
40
+ type: String,
41
+ default: "100%",
42
+ },
43
+ imageAlign: {
44
+ type: String,
45
+ default: "center",
46
+ },
47
+ imageRatio: {
48
+ type: String,
49
+ default: "50%",
50
+ },
51
+ logo: {
52
+ type: Boolean,
53
+ default: false,
54
+ },
55
+ textAlignment: {
56
+ type: String,
57
+ default: "center",
58
+ validator: (value) => ["top", "center", "bottom"].includes(value),
59
+ },
60
+ });
61
+
62
+ // Calculate the image ratio style
63
+ const imageRatioStyle = computed(() => {
64
+ // Ensure it's a percentage string
65
+ const ratio = props.imageRatio.endsWith("%")
66
+ ? props.imageRatio
67
+ : `${parseFloat(props.imageRatio)}%`;
68
+ return ratio;
69
+ });
70
+
71
+ // Calculate the content ratio style (remaining space)
72
+ const contentRatioStyle = computed(() => {
73
+ const imageRatio = parseFloat(imageRatioStyle.value);
74
+ return `${100 - imageRatio}%`;
75
+ });
76
+
77
+ // Get container alignment style based on imageAlign
78
+ const imageContainerStyle = computed(() => {
79
+ return getImageContainerStyle(props.imageAlign);
80
+ });
81
+
82
+ // Calculate the combined image style including scale
83
+ const combinedImageStyle = computed(() => {
84
+ const style = {
85
+ height: "100%", // Always fill height
86
+ width: "auto",
87
+ maxWidth: "none", // Override default max-width
88
+ objectFit: "contain", // Use 'contain' to prevent cropping by default
89
+ objectPosition: getObjectPosition(props.imageAlign),
90
+ };
91
+
92
+ // Apply scaling if specified
93
+ if (props.imageScale) {
94
+ if (props.imageScale.endsWith("%")) {
95
+ const scale = parseFloat(props.imageScale) / 100;
96
+ style.height = `${scale * 100}%`;
97
+ } else if (!isNaN(Number(props.imageScale))) {
98
+ const scale = Number(props.imageScale);
99
+ style.height = `${scale * 100}%`;
100
+ }
101
+ }
102
+
103
+ return style;
104
+ });
105
+
106
+ // Helper function to convert alignment to object-position value
107
+ function getObjectPosition(align) {
108
+ switch (align /* Lines 93-102 omitted */) {
109
+ }
110
+ }
111
+
112
+ const showLogo = computed(() => {
113
+ if ($slidev?.configs?.frontmatter?.logo !== undefined) {
114
+ return $slidev.configs.frontmatter.logo;
115
+ }
116
+ return props.logo;
117
+ });
118
+
119
+ // Compute content alignment style based on textAlignment prop
120
+ const contentAlignmentStyle = computed(() => {
121
+ switch (props.textAlignment) {
122
+ case "top":
123
+ return "flex-start";
124
+ case "bottom":
125
+ return "flex-end";
126
+ case "center":
127
+ default:
128
+ return "center";
129
+ }
130
+ });
131
+
132
+ // Setup for header content split
133
+ const headerRef = ref(null);
134
+ const contentRef = ref(null);
135
+
136
+ // Use our utility to handle the header/content split
137
+ const { setupHeaderSplit } = useHeaderContentSplit(headerRef, contentRef);
138
+ let headerSplitter;
139
+
140
+ onMounted(() => {
141
+ // Setup the header splitter when component is mounted
142
+ headerSplitter = setupHeaderSplit();
143
+ });
144
+
145
+ onBeforeUnmount(() => {
146
+ // Clean up when component is unmounted
147
+ if (headerSplitter) {
148
+ headerSplitter.destroy();
149
+ }
150
+ });
151
+ </script>
152
+
153
+ <style scoped>
154
+ .image-right {
155
+ height: 100%;
156
+ width: 100%;
157
+ padding: 0;
158
+ overflow: hidden;
159
+ }
160
+
161
+ .split-layout {
162
+ display: flex;
163
+ height: 100%;
164
+ width: 100%;
165
+ }
166
+
167
+ .image-container {
168
+ height: 100%;
169
+ overflow: hidden;
170
+ position: relative;
171
+ }
172
+
173
+ .image-wrapper {
174
+ height: 100%;
175
+ width: 100%;
176
+ overflow: hidden;
177
+ }
178
+
179
+ .side-image {
180
+ display: block;
181
+ }
182
+
183
+ .content-container {
184
+ height: 100%;
185
+ overflow: auto;
186
+ }
187
+
188
+ .content-layout {
189
+ display: flex;
190
+ flex-direction: column;
191
+ height: 100%;
192
+ }
193
+
194
+ .content-wrapper {
195
+ padding: 1.5rem 2rem;
196
+ flex: 1;
197
+ overflow-y: auto;
198
+ display: flex;
199
+ flex-direction: column;
200
+ justify-content: v-bind(contentAlignmentStyle);
201
+ }
202
+
203
+ .logo-container.custom-position {
204
+ bottom: 1rem;
205
+ left: 1rem;
206
+ }
207
+
208
+ .header {
209
+ padding: 0 2rem;
210
+ padding-top: 1.5rem;
211
+ margin-bottom: 0;
212
+ width: 100%;
213
+ }
214
+
215
+ .header:empty {
216
+ display: none;
217
+ }
218
+ </style>
@@ -0,0 +1,143 @@
1
+ <template>
2
+ <div class="slidev-layout image-layout">
3
+ <div v-if="showLogo" class="logo-container">
4
+ <div class="logo-image logo"></div>
5
+ </div>
6
+ <!-- Background image mode - image covers entire slide -->
7
+ <div v-if="useBackground" class="image-background" :style="bgStyle">
8
+ <div class="content">
9
+ <slot />
10
+ </div>
11
+ </div>
12
+
13
+ <!-- Foreground image mode - text at top, image below -->
14
+ <div v-else class="foreground-mode-container">
15
+ <!-- Content area at the top -->
16
+ <div class="content-area">
17
+ <slot />
18
+ </div>
19
+
20
+ <!-- Image area below the content -->
21
+ <div class="image-container" :style="containerStyle">
22
+ <img
23
+ v-if="props.imageSrc"
24
+ :src="getImageUrl(props.imageSrc)"
25
+ :style="imageStyle"
26
+ class="foreground-image"
27
+ />
28
+ </div>
29
+ </div>
30
+ </div>
31
+ </template>
32
+
33
+ <script setup>
34
+ import { computed } from 'vue'
35
+ import { getImageStyle, getForegroundImageStyle, getImageContainerStyle, getImageUrl } from '../utils/layoutHelper'
36
+
37
+ const props = defineProps({
38
+ imageSrc: String,
39
+ imageScale: {
40
+ type: String,
41
+ default: '70%' // Default to 70% scale for foreground mode
42
+ },
43
+ imageAlign: {
44
+ type: String,
45
+ default: 'center'
46
+ },
47
+ imageBackgroundMode: {
48
+ type: Boolean,
49
+ default: true
50
+ },
51
+ logo: {
52
+ type: Boolean,
53
+ default: false
54
+ },
55
+ })
56
+
57
+ // Determine whether to use background mode or foreground image
58
+ const useBackground = computed(() => props.imageBackgroundMode)
59
+
60
+ // Get background style if using background mode
61
+ const bgStyle = computed(() => getImageStyle(props.imageSrc, props.imageScale, props.imageAlign))
62
+
63
+ // Get foreground image style if using foreground image
64
+ const imageStyle = computed(() => getForegroundImageStyle(props.imageScale))
65
+
66
+ // Get container style for alignment of foreground image
67
+ const containerStyle = computed(() => getImageContainerStyle(props.imageAlign))
68
+
69
+ // Other utility computed props
70
+ const flexRow = computed(() => props.position === 'left' ? 'flex-row-reverse' : 'flex-row')
71
+ const textItems = computed(() => props.position === 'left' ? 'items-start' : 'items-end')
72
+ const textAlign = computed(() => props.position === 'left' ? 'text-left' : 'text-right')
73
+
74
+ const showLogo = computed(() => {
75
+ if ($slidev?.configs?.frontmatter?.logo !== undefined) {
76
+ return $slidev.configs.frontmatter.logo
77
+ }
78
+ return props.logo
79
+ })
80
+ </script>
81
+
82
+ <style scoped>
83
+ .image-layout {
84
+ height: 100%;
85
+ width: 100%;
86
+ padding: 0;
87
+ overflow: hidden;
88
+ position: relative;
89
+ }
90
+
91
+ /* Background image mode - full screen with content at bottom */
92
+ .image-background {
93
+ height: 100%;
94
+ width: 100%;
95
+ display: flex;
96
+ flex-direction: column;
97
+ justify-content: flex-end;
98
+ overflow: hidden;
99
+ }
100
+
101
+ /* Foreground image mode - text at top, image below */
102
+ .foreground-mode-container {
103
+ display: flex;
104
+ flex-direction: column;
105
+ height: 100%;
106
+ width: 100%;
107
+ overflow: hidden;
108
+ }
109
+
110
+ .content-area {
111
+ padding: 1.5rem 2rem;
112
+ z-index: 20;
113
+ }
114
+
115
+ .image-container {
116
+ flex-grow: 1;
117
+ display: flex;
118
+ align-items: center;
119
+ justify-content: center;
120
+ overflow: hidden;
121
+ padding: 1rem;
122
+ }
123
+
124
+ .foreground-image {
125
+ max-height: 100%;
126
+ max-width: 100%;
127
+ object-fit: contain;
128
+ }
129
+
130
+ .content {
131
+ padding: 1.5rem;
132
+ background-color: rgba(0, 0, 0, 0.164);
133
+ border-radius: 0.5rem;
134
+ }
135
+
136
+ .dark .content {
137
+ background-color: rgba(255, 255, 255, 0.116);
138
+ }
139
+
140
+ /* Using global logo container styling */
141
+
142
+ /* Logo styling moved to global layout.css */
143
+ </style>