Skip to content

Commit 0c754c0

Browse files
authored
Merge pull request #76 from subroh0508/feature/speakerdeck-embed-links
Add SpeakerDeck embed for Conference works
2 parents eedbd63 + f6d5506 commit 0c754c0

File tree

3 files changed

+167
-43
lines changed

3 files changed

+167
-43
lines changed

shared/src/commonMain/kotlin/component/WorkCard.kt

Lines changed: 31 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package component
22

33
import PortfolioTag
4-
import androidx.compose.foundation.Image
54
import androidx.compose.foundation.layout.*
65
import androidx.compose.material.icons.Icons
76
import androidx.compose.material.icons.filled.Tag
@@ -13,12 +12,10 @@ import androidx.compose.ui.Modifier
1312
import androidx.compose.ui.graphics.vector.ImageVector
1413
import androidx.compose.ui.input.pointer.PointerIcon
1514
import androidx.compose.ui.input.pointer.pointerHoverIcon
16-
import androidx.compose.ui.layout.ContentScale
1715
import androidx.compose.ui.platform.testTag
1816
import androidx.compose.ui.unit.dp
1917
import component.work.Time
2018
import component.work.Work
21-
import org.jetbrains.compose.resources.imageResource
2219
import org.jetbrains.compose.resources.stringResource
2320
import utils.openWindow
2421

@@ -45,14 +42,7 @@ internal fun WorkCard(
4542
)
4643
}
4744
},
48-
thumbnail = {
49-
Image(
50-
imageResource(work.thumbnail),
51-
contentDescription = null,
52-
contentScale = ContentScale.Crop,
53-
modifier = Modifier.fillMaxSize(),
54-
)
55-
},
45+
thumbnail = work.thumbnail,
5646
modifier = modifier,
5747
)
5848

@@ -66,10 +56,7 @@ internal fun WorkCard(
6656
thumbnail: @Composable () -> Unit,
6757
modifier: Modifier = Modifier,
6858
) = ElevatedCard(modifier) {
69-
WorkCardThumbnail(
70-
type,
71-
thumbnail,
72-
)
59+
WorkCardThumbnail(thumbnail)
7360

7461
Column(
7562
modifier = Modifier.padding(16.dp),
@@ -90,12 +77,23 @@ internal fun WorkCard(
9077
Spacer(Modifier.height(16.dp))
9178

9279
Row(
93-
modifier = Modifier.align(Alignment.End),
94-
horizontalArrangement = Arrangement.spacedBy(8.dp),
95-
) { linkButtons() }
80+
modifier = Modifier.fillMaxWidth(),
81+
verticalAlignment = Alignment.CenterVertically,
82+
) {
83+
WorkTypeTag(type)
84+
Spacer(Modifier.weight(1f))
85+
LinkButtons(linkButtons)
86+
}
9687
}
9788
}
9889

90+
@Composable
91+
private fun LinkButtons(
92+
content: @Composable () -> Unit,
93+
) = Row(
94+
horizontalArrangement = Arrangement.spacedBy(8.dp),
95+
) { content() }
96+
9997
@OptIn(ExperimentalMaterial3Api::class)
10098
@Composable
10199
internal fun LinkButton(
@@ -123,27 +121,11 @@ internal fun LinkButton(
123121

124122
@Composable
125123
private fun WorkCardThumbnail(
126-
type: String,
127124
thumbnail: @Composable () -> Unit,
128125
) = Box(
129126
modifier = Modifier.fillMaxWidth()
130127
.height(WorkCardThumbnailHeight),
131-
) {
132-
thumbnail()
133-
134-
NoRippleAssistChip(
135-
onClick = { },
136-
colors = AssistChipDefaults.assistChipColors(
137-
containerColor = MaterialTheme.colorScheme.surface.copy(alpha = 0.8F),
138-
),
139-
border = null,
140-
leadingIcon = { Icon(Icons.Default.Tag, contentDescription = null) },
141-
label = { Text(type) },
142-
modifier = Modifier.align(Alignment.BottomEnd)
143-
.offset(x = (-8).dp, y = (-4).dp)
144-
.testTag(PortfolioTag.WORK_CARD_TAG),
145-
)
146-
}
128+
) { thumbnail() }
147129

148130
@Composable
149131
private fun WorkHeadline(
@@ -165,3 +147,17 @@ private fun WorkHeadline(
165147
modifier = Modifier.testTag(PortfolioTag.WORK_CARD_TIME),
166148
)
167149
}
150+
151+
@Composable
152+
private fun WorkTypeTag(
153+
type: String,
154+
) = NoRippleAssistChip(
155+
onClick = { },
156+
colors = AssistChipDefaults.assistChipColors(
157+
containerColor = MaterialTheme.colorScheme.surface.copy(alpha = 0.8F),
158+
),
159+
border = null,
160+
leadingIcon = { Icon(Icons.Default.Tag, contentDescription = null) },
161+
label = { Text(type) },
162+
modifier = Modifier.testTag(PortfolioTag.WORK_CARD_TAG),
163+
)
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package component.work
2+
3+
import androidx.compose.foundation.layout.Box
4+
import androidx.compose.foundation.layout.fillMaxSize
5+
import androidx.compose.runtime.Composable
6+
import androidx.compose.ui.ExperimentalComposeUiApi
7+
import androidx.compose.ui.Modifier
8+
import androidx.compose.ui.viewinterop.WebElementView
9+
import kotlinx.browser.document
10+
import org.w3c.dom.HTMLIFrameElement
11+
12+
@OptIn(ExperimentalComposeUiApi::class)
13+
@Composable
14+
internal fun SpeakerDeck(
15+
link: SpeakerDeckLink,
16+
modifier: Modifier = Modifier,
17+
) {
18+
Box(modifier = modifier) {
19+
WebElementView(
20+
factory = { iframe(link.src, link.title) },
21+
modifier = Modifier.fillMaxSize(),
22+
update = { iframe -> iframe.src = iframe.src },
23+
)
24+
}
25+
}
26+
27+
internal data class SpeakerDeckLink(
28+
val src: String,
29+
val title: String,
30+
)
31+
32+
private fun iframe(
33+
src: String,
34+
title: String,
35+
): HTMLIFrameElement {
36+
val element = document.createElement(
37+
"iframe"
38+
) as HTMLIFrameElement
39+
40+
return element.apply {
41+
this.src = src
42+
this.title = title
43+
className = "speakerdeck-iframe"
44+
allowFullscreen = true
45+
setAttribute("data-ratio", "1.7777777777777777")
46+
with(style) {
47+
border = "0px"
48+
background = "padding-box padding-box rgba(0, 0, 0, 0.1)"
49+
margin = "0px"
50+
padding = "0px"
51+
borderRadius = "6px"
52+
boxShadow = "rgba(0, 0, 0, 0.2) 0px 5px 40px"
53+
width = "100%"
54+
height = "auto"
55+
setProperty("aspect-ratio", "560 / 315")
56+
}
57+
}
58+
}

shared/src/commonMain/kotlin/component/work/Work.kt

Lines changed: 78 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
package component.work
22

3+
import androidx.compose.foundation.Image
4+
import androidx.compose.foundation.layout.fillMaxSize
35
import androidx.compose.material.icons.Icons
46
import androidx.compose.material.icons.automirrored.filled.OpenInNew
57
import androidx.compose.material.icons.filled.Code
68
import androidx.compose.material.icons.filled.Description
79
import androidx.compose.material.icons.filled.Movie
10+
import androidx.compose.runtime.Composable
11+
import androidx.compose.ui.Modifier
812
import androidx.compose.ui.graphics.vector.ImageVector
13+
import androidx.compose.ui.layout.ContentScale
914
import org.jetbrains.compose.resources.DrawableResource
1015
import org.jetbrains.compose.resources.StringResource
16+
import org.jetbrains.compose.resources.imageResource
1117
import portfolio.shared.generated.resources.*
1218

1319
internal data class Work(
@@ -16,8 +22,54 @@ internal data class Work(
1622
val type: WorkType,
1723
val links: List<Link>,
1824
val time: Time,
19-
val thumbnail: DrawableResource,
20-
)
25+
val thumbnail: @Composable () -> Unit,
26+
) {
27+
companion object {
28+
operator fun invoke(
29+
headline: StringResource,
30+
description: StringResource,
31+
type: WorkType,
32+
links: List<Link>,
33+
time: Time,
34+
thumbnail: DrawableResource,
35+
) = Work(
36+
headline,
37+
description,
38+
type,
39+
links,
40+
time,
41+
thumbnail = {
42+
Image(
43+
imageResource(thumbnail),
44+
contentDescription = null,
45+
contentScale = ContentScale.Crop,
46+
modifier = Modifier.fillMaxSize(),
47+
)
48+
},
49+
)
50+
51+
operator fun invoke(
52+
headline: StringResource,
53+
description: StringResource,
54+
type: WorkType,
55+
links: List<Link>,
56+
time: Time,
57+
slide: SpeakerDeckLink,
58+
) = Work(
59+
headline,
60+
description,
61+
type,
62+
links,
63+
time,
64+
thumbnail = {
65+
SpeakerDeck(
66+
slide,
67+
modifier = Modifier.fillMaxSize(),
68+
)
69+
},
70+
)
71+
}
72+
}
2173

2274
internal enum class WorkType(val label: StringResource) {
2375
Conference(Res.string.conference),
@@ -79,7 +131,10 @@ internal val DevelopersBoost2025 = Work(
79131
),
80132
),
81133
Time.Event(2025),
82-
Res.drawable.developersboost2025,
134+
SpeakerDeckLink(
135+
src = "https://speakerdeck.com/player/5de4947ef6a34dd299b99abe62d65bc7",
136+
title = "技術以外の世界に『越境』しエンジニアとして進化を遂げる 〜Kotlinへの愛とDevHRとしての挑戦を添えて〜",
137+
),
83138
)
84139

85140
internal val EngineerAnime2025 = Work(
@@ -95,7 +150,10 @@ internal val EngineerAnime2025 = Work(
95150
),
96151
),
97152
Time.Event(2025),
98-
Res.drawable.engineeranime2025,
153+
SpeakerDeckLink(
154+
src = "https://speakerdeck.com/player/60ea30fcfad241239cba1b136736fc20",
155+
title = "MustをWillに変える技術 〜アイドル・郁田はるきが&quot;すべき&quot;の壁を超えるまで〜",
156+
),
99157
)
100158

101159
internal val KotlinFest2024 = Work(
@@ -117,7 +175,10 @@ internal val KotlinFest2024 = Work(
117175
),
118176
),
119177
Time.Event(2024),
120-
Res.drawable.kotlinfest2024,
178+
SpeakerDeckLink(
179+
src = "https://speakerdeck.com/player/a537a3def3ae4c23a5bfe8d763b089b0",
180+
title = "あらゆるアプリをCompose Multiplatformで書きたい! -ネイティブアプリの「あの機能」を私たちはどう作るか-",
181+
),
121182
)
122183

123184
internal val KotlinFest2022 = Work(
@@ -139,7 +200,10 @@ internal val KotlinFest2022 = Work(
139200
),
140201
),
141202
Time.Event(2022),
142-
Res.drawable.kotlinfest2022,
203+
SpeakerDeckLink(
204+
src = "https://speakerdeck.com/player/f023b131957a4c409d633498272cc6bf",
205+
title = "フロントエンドもJetpack Composeで書きたい! -Compose for WebはモダンWebアプリケーションの夢を見るか?-",
206+
),
143207
)
144208

145209
internal val KotlinFest2019 = Work(
@@ -161,7 +225,10 @@ internal val KotlinFest2019 = Work(
161225
),
162226
),
163227
Time.Event(2019),
164-
Res.drawable.kotlinfest2019,
228+
SpeakerDeckLink(
229+
src = "https://speakerdeck.com/player/cc46277d17c240b5955026ab25cbc083",
230+
title = "フロントエンドもKotlinで書きたい! -WebページをKotlin/JSで作った軌跡-",
231+
),
165232
)
166233

167234
internal val DroidKaigi2019 = Work(
@@ -183,7 +250,10 @@ internal val DroidKaigi2019 = Work(
183250
),
184251
),
185252
Time.Event(2019),
186-
Res.drawable.droidkaigi2019,
253+
SpeakerDeckLink(
254+
src = "https://speakerdeck.com/player/52a0289ebfe649f09524739bb4f7ddd5",
255+
title = "Spek2+MockK+JaCoCoでイケてる Unit Test環境を手に入れろ!",
256+
),
187257
)
188258

189259
internal val KotlinMaterialUi = Work(

0 commit comments

Comments
 (0)