@c4h/chuci 0.1.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.
- package/CHANGELOG.md +33 -0
- package/LICENSE +21 -0
- package/README.ja.md +144 -0
- package/README.md +225 -0
- package/dist/assets/azumaya_panorama1.png +0 -0
- package/dist/chuci.cjs +4483 -0
- package/dist/chuci.js +21710 -0
- package/dist/chuci.umd.js +4737 -0
- package/dist/index-8VMexD2a.cjs +255 -0
- package/dist/index-kvsisbKS.js +2125 -0
- package/dist/index.d.ts +203 -0
- package/dist/index.html +241 -0
- package/dist/test-image.html +63 -0
- package/package.json +86 -0
- package/src/components/swiper/cc-swiper-slide.ts +50 -0
- package/src/components/swiper/cc-swiper-styles.ts +29 -0
- package/src/components/swiper/cc-swiper.ts +362 -0
- package/src/components/swiper/swiper-styles.css +5 -0
- package/src/components/viewer/cc-viewer-3dmodel.ts +492 -0
- package/src/components/viewer/cc-viewer-base.ts +279 -0
- package/src/components/viewer/cc-viewer-gaussian.ts +381 -0
- package/src/components/viewer/cc-viewer-image.ts +190 -0
- package/src/components/viewer/cc-viewer-panorama.ts +86 -0
- package/src/components/viewer/cc-viewer-styles.ts +56 -0
- package/src/components/viewer/cc-viewer-video.ts +110 -0
- package/src/components/viewer/cc-viewer-youtube.ts +76 -0
- package/src/components/viewer/cc-viewer.ts +291 -0
- package/src/index.ts +25 -0
- package/src/types/css-modules.d.ts +1 -0
- package/src/types/global.d.ts +11 -0
- package/src/utils/base-element.ts +77 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { CcViewerBase } from './cc-viewer-base'
|
|
2
|
+
import Viewer from 'viewerjs'
|
|
3
|
+
import viewerStyles from 'viewerjs/dist/viewer.css?inline'
|
|
4
|
+
|
|
5
|
+
export class CcViewerImage extends CcViewerBase {
|
|
6
|
+
private viewer?: Viewer
|
|
7
|
+
private container?: HTMLDivElement
|
|
8
|
+
private imageUrl = ''
|
|
9
|
+
|
|
10
|
+
protected doOpen(url: string): void {
|
|
11
|
+
this.imageUrl = url
|
|
12
|
+
|
|
13
|
+
setTimeout(() => {
|
|
14
|
+
this.container = this.query('#imageContainer') ?? undefined
|
|
15
|
+
if (!this.container) return
|
|
16
|
+
|
|
17
|
+
const img = document.createElement('img')
|
|
18
|
+
img.src = this.imageUrl
|
|
19
|
+
img.style.display = 'none' // 元の画像を非表示に
|
|
20
|
+
|
|
21
|
+
this.container.appendChild(img)
|
|
22
|
+
|
|
23
|
+
// ViewerJSをインラインモードで使用して、独自のコンテナ内で表示
|
|
24
|
+
this.viewer = new Viewer(img, {
|
|
25
|
+
inline: true, // インラインモードに変更
|
|
26
|
+
container: this.container, // コンテナを指定
|
|
27
|
+
toolbar: {
|
|
28
|
+
zoomIn: true,
|
|
29
|
+
zoomOut: true,
|
|
30
|
+
oneToOne: true,
|
|
31
|
+
reset: true,
|
|
32
|
+
prev: false,
|
|
33
|
+
play: false,
|
|
34
|
+
next: false,
|
|
35
|
+
rotateLeft: true,
|
|
36
|
+
rotateRight: true,
|
|
37
|
+
flipHorizontal: true,
|
|
38
|
+
flipVertical: true,
|
|
39
|
+
},
|
|
40
|
+
navbar: false,
|
|
41
|
+
title: false,
|
|
42
|
+
keyboard: true,
|
|
43
|
+
backdrop: false, // インラインモードでは背景を無効化
|
|
44
|
+
button: false, // 閉じるボタンを無効化
|
|
45
|
+
movable: true,
|
|
46
|
+
zoomable: true,
|
|
47
|
+
rotatable: true,
|
|
48
|
+
scalable: true,
|
|
49
|
+
transition: true,
|
|
50
|
+
fullscreen: false,
|
|
51
|
+
ready: () => {
|
|
52
|
+
this.addNavigationListeners()
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
// show()を呼ばない - インラインモードでは自動的に表示される
|
|
57
|
+
}, 0)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
protected doClose(): void {
|
|
61
|
+
if (this.viewer) {
|
|
62
|
+
this.viewer.destroy()
|
|
63
|
+
this.viewer = undefined
|
|
64
|
+
}
|
|
65
|
+
this.imageUrl = ''
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
protected getViewerContent(): string {
|
|
69
|
+
return `
|
|
70
|
+
<div id="imageContainer">
|
|
71
|
+
${this.isLoading ? '<div class="loading">Loading...</div>' : ''}
|
|
72
|
+
</div>
|
|
73
|
+
`
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
protected getCustomStyles(): string {
|
|
77
|
+
const styles = this.css`
|
|
78
|
+
${viewerStyles}
|
|
79
|
+
|
|
80
|
+
:host {
|
|
81
|
+
--cc-viewer-z-index-each: 1000;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
#imageContainer {
|
|
85
|
+
position: fixed;
|
|
86
|
+
top: 0;
|
|
87
|
+
left: 0;
|
|
88
|
+
width: 100%;
|
|
89
|
+
height: 100%;
|
|
90
|
+
z-index: var(--cc-viewer-z-index-each, 1000);
|
|
91
|
+
background: rgba(0, 0, 0, 0.9);
|
|
92
|
+
display: flex;
|
|
93
|
+
align-items: center;
|
|
94
|
+
justify-content: center;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* ViewerJS inline mode adjustments */
|
|
98
|
+
.viewer-container {
|
|
99
|
+
width: 100% !important;
|
|
100
|
+
height: 100% !important;
|
|
101
|
+
position: relative !important;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.viewer-canvas {
|
|
105
|
+
width: 100% !important;
|
|
106
|
+
height: 100% !important;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* Hide the original image */
|
|
110
|
+
#imageContainer > img {
|
|
111
|
+
display: none !important;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* Ensure viewer takes full space */
|
|
115
|
+
.viewer-container.viewer-inline {
|
|
116
|
+
width: 100% !important;
|
|
117
|
+
height: 100% !important;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
${this.getNavigationStyles()}
|
|
121
|
+
|
|
122
|
+
/* Override viewerjs styles for navigation */
|
|
123
|
+
:host ::ng-deep .viewer-container {
|
|
124
|
+
position: relative;
|
|
125
|
+
}
|
|
126
|
+
`
|
|
127
|
+
|
|
128
|
+
return styles
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Override to do setup after render
|
|
132
|
+
protected onAfterRender(): void {
|
|
133
|
+
if (!this.isShow || !this.imageUrl) return
|
|
134
|
+
|
|
135
|
+
// Avoid duplicate initialization
|
|
136
|
+
if (this.viewer) return
|
|
137
|
+
|
|
138
|
+
this.container = this.query('#imageContainer') as HTMLDivElement
|
|
139
|
+
if (!this.container) return
|
|
140
|
+
|
|
141
|
+
const img = document.createElement('img')
|
|
142
|
+
img.src = this.imageUrl
|
|
143
|
+
img.style.display = 'none' // 元の画像を非表示に
|
|
144
|
+
|
|
145
|
+
this.container.appendChild(img)
|
|
146
|
+
|
|
147
|
+
// ViewerJSをインラインモードで使用して、独自のコンテナ内で表示
|
|
148
|
+
this.viewer = new Viewer(img, {
|
|
149
|
+
inline: true, // インラインモードに変更
|
|
150
|
+
container: this.container, // コンテナを指定
|
|
151
|
+
toolbar: {
|
|
152
|
+
zoomIn: true,
|
|
153
|
+
zoomOut: true,
|
|
154
|
+
oneToOne: true,
|
|
155
|
+
reset: true,
|
|
156
|
+
prev: false,
|
|
157
|
+
play: false,
|
|
158
|
+
next: false,
|
|
159
|
+
rotateLeft: true,
|
|
160
|
+
rotateRight: true,
|
|
161
|
+
flipHorizontal: true,
|
|
162
|
+
flipVertical: true,
|
|
163
|
+
},
|
|
164
|
+
navbar: false,
|
|
165
|
+
title: false,
|
|
166
|
+
keyboard: true,
|
|
167
|
+
backdrop: false, // インラインモードでは背景を無効化
|
|
168
|
+
button: false, // 閉じるボタンを無効化
|
|
169
|
+
movable: true,
|
|
170
|
+
zoomable: true,
|
|
171
|
+
rotatable: true,
|
|
172
|
+
scalable: true,
|
|
173
|
+
transition: true,
|
|
174
|
+
fullscreen: false,
|
|
175
|
+
ready: () => {
|
|
176
|
+
this.isLoading = false
|
|
177
|
+
}
|
|
178
|
+
})
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (!customElements.get('cc-viewer-image')) {
|
|
183
|
+
customElements.define('cc-viewer-image', CcViewerImage)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
declare global {
|
|
187
|
+
interface HTMLElementTagNameMap {
|
|
188
|
+
'cc-viewer-image': CcViewerImage
|
|
189
|
+
}
|
|
190
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { CcViewerBase } from './cc-viewer-base'
|
|
2
|
+
|
|
3
|
+
export class CcViewerPanorama extends CcViewerBase {
|
|
4
|
+
private imgUrl = ''
|
|
5
|
+
|
|
6
|
+
static get observedAttributes() {
|
|
7
|
+
return ['show']
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) {
|
|
11
|
+
if (name === 'show') {
|
|
12
|
+
this.isShow = newValue === 'true'
|
|
13
|
+
}
|
|
14
|
+
super.attributeChangedCallback(name, oldValue, newValue)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
protected doOpen(imgUrl: string): void {
|
|
18
|
+
this.imgUrl = imgUrl
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
protected doClose(): void {
|
|
22
|
+
const iframeEl = this.query('.iframe') as HTMLIFrameElement
|
|
23
|
+
if (iframeEl) {
|
|
24
|
+
iframeEl.srcdoc = ''
|
|
25
|
+
}
|
|
26
|
+
this.imgUrl = ''
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
protected getViewerContent(): string {
|
|
30
|
+
return `<iframe class="iframe"></iframe>`
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
protected getCustomStyles(): string {
|
|
34
|
+
return `
|
|
35
|
+
.iframe {
|
|
36
|
+
width: 100%;
|
|
37
|
+
height: 100%;
|
|
38
|
+
border: 0;
|
|
39
|
+
}
|
|
40
|
+
`
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
protected onAfterRender(): void {
|
|
44
|
+
if (this.imgUrl && this.isShow) {
|
|
45
|
+
const iframeEl = this.query('.iframe') as HTMLIFrameElement
|
|
46
|
+
if (iframeEl) {
|
|
47
|
+
const iframeHtml = `
|
|
48
|
+
<!DOCTYPE html>
|
|
49
|
+
<html>
|
|
50
|
+
<head>
|
|
51
|
+
<title>A-Frame Panorama</title>
|
|
52
|
+
<style>
|
|
53
|
+
html,body {
|
|
54
|
+
width:100%;
|
|
55
|
+
height:100vh;
|
|
56
|
+
overflow: hidden;
|
|
57
|
+
}
|
|
58
|
+
.a-enter-vr, .a-enter-ar {
|
|
59
|
+
display: none;
|
|
60
|
+
}
|
|
61
|
+
</style>
|
|
62
|
+
<script src="https://aframe.io/releases/1.4.0/aframe.min.js"></script>
|
|
63
|
+
</head>
|
|
64
|
+
<body>
|
|
65
|
+
<a-scene embedded xr-mode-ui="enabled: false; XRMode: false;">
|
|
66
|
+
<a-sky src="${this.imgUrl}" rotation="0 -90 0"></a-sky>
|
|
67
|
+
<a-entity camera look-controls="reverseMouseDrag: true"></a-entity>
|
|
68
|
+
</a-scene>
|
|
69
|
+
</body>
|
|
70
|
+
</html>
|
|
71
|
+
`
|
|
72
|
+
iframeEl.srcdoc = iframeHtml
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (!customElements.get('cc-viewer-panorama')) {
|
|
79
|
+
customElements.define('cc-viewer-panorama', CcViewerPanorama)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
declare global {
|
|
83
|
+
interface HTMLElementTagNameMap {
|
|
84
|
+
'cc-viewer-panorama': CcViewerPanorama
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export const viewerStyles = `
|
|
2
|
+
:host {
|
|
3
|
+
display: block;
|
|
4
|
+
position: fixed;
|
|
5
|
+
top: 0;
|
|
6
|
+
left: 0;
|
|
7
|
+
width: 100%;
|
|
8
|
+
height: 100%;
|
|
9
|
+
z-index: var(--cc-viewer-z-index, 1000);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.viewer-container {
|
|
13
|
+
position: fixed;
|
|
14
|
+
top: 0;
|
|
15
|
+
left: 0;
|
|
16
|
+
width: 100%;
|
|
17
|
+
height: 100%;
|
|
18
|
+
background: rgba(0, 0, 0, 0.9);
|
|
19
|
+
display: flex;
|
|
20
|
+
align-items: center;
|
|
21
|
+
justify-content: center;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.viewer-content {
|
|
25
|
+
position: relative;
|
|
26
|
+
max-width: 90%;
|
|
27
|
+
max-height: 90%;
|
|
28
|
+
width: 100%;
|
|
29
|
+
height: 100%;
|
|
30
|
+
display: flex;
|
|
31
|
+
align-items: center;
|
|
32
|
+
justify-content: center;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.close-button {
|
|
36
|
+
position: absolute;
|
|
37
|
+
top: 20px;
|
|
38
|
+
right: 20px;
|
|
39
|
+
background: rgba(0, 0, 0, 0.5);
|
|
40
|
+
color: white;
|
|
41
|
+
border: none;
|
|
42
|
+
border-radius: 4px;
|
|
43
|
+
width: 48px;
|
|
44
|
+
height: 48px;
|
|
45
|
+
display: flex;
|
|
46
|
+
align-items: center;
|
|
47
|
+
justify-content: center;
|
|
48
|
+
cursor: pointer;
|
|
49
|
+
transition: background 0.3s;
|
|
50
|
+
z-index: 10;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.close-button:hover {
|
|
54
|
+
background: rgba(0, 0, 0, 0.7);
|
|
55
|
+
}
|
|
56
|
+
`
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { CcViewerBase } from './cc-viewer-base'
|
|
2
|
+
|
|
3
|
+
export class CcViewerVideo extends CcViewerBase {
|
|
4
|
+
private videoUrl = ''
|
|
5
|
+
private fitToContainer = false
|
|
6
|
+
|
|
7
|
+
static get observedAttributes() {
|
|
8
|
+
return ['show', 'fit-to-container']
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) {
|
|
12
|
+
if (name === 'show') {
|
|
13
|
+
this.isShow = newValue === 'true'
|
|
14
|
+
} else if (name === 'fit-to-container') {
|
|
15
|
+
this.fitToContainer = newValue === 'true'
|
|
16
|
+
}
|
|
17
|
+
super.attributeChangedCallback(name, oldValue, newValue)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
protected doOpen(url: string): void {
|
|
21
|
+
this.videoUrl = url
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
protected doClose(): void {
|
|
25
|
+
// Pause video before closing
|
|
26
|
+
const video = this.query('video')
|
|
27
|
+
if (video && 'pause' in video) {
|
|
28
|
+
(video as HTMLVideoElement).pause()
|
|
29
|
+
}
|
|
30
|
+
this.videoUrl = ''
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
protected getViewerContent(): string {
|
|
34
|
+
return `
|
|
35
|
+
<div class="video-container">
|
|
36
|
+
${this.videoUrl ? `
|
|
37
|
+
<video
|
|
38
|
+
src="${this.videoUrl}"
|
|
39
|
+
controls
|
|
40
|
+
controlsList="nodownload"
|
|
41
|
+
class="${this.fitToContainer ? 'fit-to-container' : ''}"
|
|
42
|
+
>
|
|
43
|
+
Your browser does not support the video tag.
|
|
44
|
+
</video>
|
|
45
|
+
` : '<div class="video-error">No video URL provided</div>'}
|
|
46
|
+
</div>
|
|
47
|
+
`
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
protected getCustomStyles(): string {
|
|
51
|
+
return `
|
|
52
|
+
.video-container {
|
|
53
|
+
display: flex;
|
|
54
|
+
justify-content: center;
|
|
55
|
+
align-items: center;
|
|
56
|
+
width: 100%;
|
|
57
|
+
height: 100%;
|
|
58
|
+
background: #000;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
video {
|
|
62
|
+
max-width: 100%;
|
|
63
|
+
max-height: 100%;
|
|
64
|
+
width: auto;
|
|
65
|
+
height: auto;
|
|
66
|
+
outline: none;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
video.fit-to-container {
|
|
70
|
+
width: 100%;
|
|
71
|
+
height: 100%;
|
|
72
|
+
object-fit: contain;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.video-error {
|
|
76
|
+
color: #fff;
|
|
77
|
+
text-align: center;
|
|
78
|
+
padding: 20px;
|
|
79
|
+
}
|
|
80
|
+
`
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
protected onAfterRender(): void {
|
|
84
|
+
const video = this.query('video')
|
|
85
|
+
if (video) {
|
|
86
|
+
video.addEventListener('error', (e) => this.handleVideoError(e))
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private handleVideoError(_e: Event) {
|
|
91
|
+
const container = this.query('.video-container')
|
|
92
|
+
if (container) {
|
|
93
|
+
container.innerHTML = `
|
|
94
|
+
<div class="video-error">
|
|
95
|
+
Failed to load video: ${this.videoUrl}
|
|
96
|
+
</div>
|
|
97
|
+
`
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!customElements.get('cc-viewer-video')) {
|
|
103
|
+
customElements.define('cc-viewer-video', CcViewerVideo)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
declare global {
|
|
107
|
+
interface HTMLElementTagNameMap {
|
|
108
|
+
'cc-viewer-video': CcViewerVideo
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { CcViewerBase } from './cc-viewer-base'
|
|
2
|
+
|
|
3
|
+
export class CcViewerYoutube extends CcViewerBase {
|
|
4
|
+
private videoUrl = ''
|
|
5
|
+
|
|
6
|
+
static get observedAttributes() {
|
|
7
|
+
return ['show']
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) {
|
|
11
|
+
if (name === 'show') {
|
|
12
|
+
this.isShow = newValue === 'true'
|
|
13
|
+
}
|
|
14
|
+
super.attributeChangedCallback(name, oldValue, newValue)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
protected doOpen(videoUrl: string): void {
|
|
18
|
+
// Convert YouTube URL to embed format
|
|
19
|
+
const videoId = this.extractYouTubeId(videoUrl)
|
|
20
|
+
if (videoId) {
|
|
21
|
+
this.videoUrl = `https://www.youtube.com/embed/${videoId}`
|
|
22
|
+
} else {
|
|
23
|
+
this.videoUrl = videoUrl
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
private extractYouTubeId(url: string): string | null {
|
|
28
|
+
// Handle various YouTube URL formats
|
|
29
|
+
const patterns = [
|
|
30
|
+
/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)/,
|
|
31
|
+
/youtube\.com\/watch\?.*v=([^&\n?#]+)/
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
for (const pattern of patterns) {
|
|
35
|
+
const match = url.match(pattern)
|
|
36
|
+
if (match) {
|
|
37
|
+
return match[1]
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return null
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
protected doClose(): void {
|
|
45
|
+
const iframeEl = this.query('.iframe') as HTMLIFrameElement
|
|
46
|
+
if (iframeEl) {
|
|
47
|
+
iframeEl.src = ''
|
|
48
|
+
}
|
|
49
|
+
this.videoUrl = ''
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
protected getViewerContent(): string {
|
|
53
|
+
return `<iframe class="iframe" src="${this.videoUrl}" allowfullscreen></iframe>`
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
protected getCustomStyles(): string {
|
|
57
|
+
return `
|
|
58
|
+
.iframe {
|
|
59
|
+
position: relative;
|
|
60
|
+
width: 100%;
|
|
61
|
+
height: 100%;
|
|
62
|
+
border: 0;
|
|
63
|
+
}
|
|
64
|
+
`
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!customElements.get('cc-viewer-youtube')) {
|
|
69
|
+
customElements.define('cc-viewer-youtube', CcViewerYoutube)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
declare global {
|
|
73
|
+
interface HTMLElementTagNameMap {
|
|
74
|
+
'cc-viewer-youtube': CcViewerYoutube
|
|
75
|
+
}
|
|
76
|
+
}
|