Co-website management move to Svelte
This commit is contained in:
parent
0bf1acfefb
commit
4f068c72be
@ -6,13 +6,14 @@ export const isOpenCoWebsiteEvent = new tg.IsInterface()
|
||||
allowApi: tg.isOptional(tg.isBoolean),
|
||||
allowPolicy: tg.isOptional(tg.isString),
|
||||
position: tg.isOptional(tg.isNumber),
|
||||
closable: tg.isOptional(tg.isBoolean),
|
||||
lazy: tg.isOptional(tg.isBoolean),
|
||||
})
|
||||
.get();
|
||||
|
||||
export const isCoWebsite = new tg.IsInterface()
|
||||
.withProperties({
|
||||
id: tg.isString,
|
||||
position: tg.isNumber,
|
||||
})
|
||||
.get();
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { IframeApiContribution, sendToWorkadventure, queryWorkadventure } from "./IframeApiContribution";
|
||||
|
||||
export class CoWebsite {
|
||||
constructor(private readonly id: string, public readonly position: number) {}
|
||||
constructor(private readonly id: string) {}
|
||||
|
||||
close() {
|
||||
return queryWorkadventure({
|
||||
@ -41,7 +41,14 @@ export class WorkadventureNavigationCommands extends IframeApiContribution<Worka
|
||||
});
|
||||
}
|
||||
|
||||
async openCoWebSite(url: string, allowApi?: boolean, allowPolicy?: string, position?: number): Promise<CoWebsite> {
|
||||
async openCoWebSite(
|
||||
url: string,
|
||||
allowApi?: boolean,
|
||||
allowPolicy?: string,
|
||||
position?: number,
|
||||
closable?: boolean,
|
||||
lazy?: boolean
|
||||
): Promise<CoWebsite> {
|
||||
const result = await queryWorkadventure({
|
||||
type: "openCoWebsite",
|
||||
data: {
|
||||
@ -49,9 +56,11 @@ export class WorkadventureNavigationCommands extends IframeApiContribution<Worka
|
||||
allowApi,
|
||||
allowPolicy,
|
||||
position,
|
||||
closable,
|
||||
lazy,
|
||||
},
|
||||
});
|
||||
return new CoWebsite(result.id, result.position);
|
||||
return new CoWebsite(result.id);
|
||||
}
|
||||
|
||||
async getCoWebSites(): Promise<CoWebsite[]> {
|
||||
@ -59,7 +68,7 @@ export class WorkadventureNavigationCommands extends IframeApiContribution<Worka
|
||||
type: "getCoWebsites",
|
||||
data: undefined,
|
||||
});
|
||||
return result.map((cowebsiteEvent) => new CoWebsite(cowebsiteEvent.id, cowebsiteEvent.position));
|
||||
return result.map((cowebsiteEvent) => new CoWebsite(cowebsiteEvent.id));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -166,7 +166,7 @@
|
||||
margin-right: auto;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 200;
|
||||
z-index: 550;
|
||||
|
||||
background-color: rgb(0, 0, 0, 0.5);
|
||||
display: grid;
|
||||
|
32
front/src/Components/EmbedScreens/CamerasContainer.svelte
Normal file
32
front/src/Components/EmbedScreens/CamerasContainer.svelte
Normal file
@ -0,0 +1,32 @@
|
||||
<script lang="typescript">
|
||||
import type { EmbedScreen } from "../../Stores/EmbedScreensStore";
|
||||
import { streamableCollectionStore } from "../../Stores/StreamableCollectionStore";
|
||||
import MediaBox from "../Video/MediaBox.svelte";
|
||||
|
||||
export let highlightedEmbedScreen: EmbedScreen | null;
|
||||
export let full = false;
|
||||
$: clickable = !full;
|
||||
</script>
|
||||
|
||||
<aside class="cameras-container" class:full>
|
||||
{#each [...$streamableCollectionStore.values()] as peer (peer.uniqueId)}
|
||||
{#if !highlightedEmbedScreen || highlightedEmbedScreen.type !== "streamable" || (highlightedEmbedScreen.type === "streamable" && highlightedEmbedScreen.embed !== peer)}
|
||||
<MediaBox streamable={peer} isClickable={clickable} />
|
||||
{/if}
|
||||
{/each}
|
||||
</aside>
|
||||
|
||||
<style lang="scss">
|
||||
.cameras-container {
|
||||
flex: 0 0 25%;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
&:first-child {
|
||||
margin-top: 2%;
|
||||
}
|
||||
|
||||
&.full {
|
||||
flex: 0 0 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
100
front/src/Components/EmbedScreens/CoWebsiteThumbnailSlot.svelte
Normal file
100
front/src/Components/EmbedScreens/CoWebsiteThumbnailSlot.svelte
Normal file
@ -0,0 +1,100 @@
|
||||
<script lang="typescript">
|
||||
import { onMount } from "svelte";
|
||||
|
||||
import { ICON_URL } from "../../Enum/EnvironmentVariable";
|
||||
import { mainCoWebsite } from "../../Stores/CoWebsiteStore";
|
||||
import { highlightedEmbedScreen } from "../../Stores/EmbedScreensStore";
|
||||
import type { CoWebsite } from "../../WebRtc/CoWebsiteManager";
|
||||
import { coWebsiteManager } from "../../WebRtc/CoWebsiteManager";
|
||||
|
||||
export let index: number;
|
||||
export let coWebsite: CoWebsite;
|
||||
export let vertical: boolean;
|
||||
|
||||
let icon: HTMLImageElement;
|
||||
let state = coWebsite.state;
|
||||
|
||||
const coWebsiteUrl = coWebsite.iframe.src;
|
||||
const urlObject = new URL(coWebsiteUrl);
|
||||
|
||||
onMount(() => {
|
||||
icon.src = `${ICON_URL}/icon?url=${urlObject.hostname}&size=64..96..256&fallback_icon_color=14304c`;
|
||||
icon.alt = urlObject.hostname;
|
||||
});
|
||||
|
||||
async function toggleHighlightEmbedScreen() {
|
||||
if (vertical) {
|
||||
coWebsiteManager.goToMain(coWebsite);
|
||||
} else if ($mainCoWebsite) {
|
||||
highlightedEmbedScreen.toggleHighlight({
|
||||
type: "cowebsite",
|
||||
embed: coWebsite,
|
||||
});
|
||||
}
|
||||
|
||||
if ($state === "asleep") {
|
||||
await coWebsiteManager.loadCoWebsite(coWebsite);
|
||||
}
|
||||
|
||||
coWebsiteManager.resizeAllIframes();
|
||||
}
|
||||
|
||||
function noDrag() {
|
||||
return false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
id={"cowebsite-thumbnail-" + index}
|
||||
class="cowebsite-thumbnail nes-container is-rounded nes-pointer"
|
||||
class:asleep={$state === "asleep"}
|
||||
class:loading={$state === "loading"}
|
||||
class:ready={$state === "ready"}
|
||||
class:vertical
|
||||
on:click={toggleHighlightEmbedScreen}
|
||||
>
|
||||
<img class="cowebsite-icon noselect nes-pointer" bind:this={icon} on:dragstart|preventDefault={noDrag} alt="" />
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.cowebsite-thumbnail {
|
||||
padding: 0;
|
||||
background-color: rgba(#000000, 0.6);
|
||||
margin: 1%;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
|
||||
.cowebsite-icon {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
&.vertical {
|
||||
margin: 7px;
|
||||
.cowebsite-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
&.asleep {
|
||||
filter: grayscale(100%);
|
||||
--webkit-filter: grayscale(100%);
|
||||
}
|
||||
|
||||
&.loading {
|
||||
animation: 2500ms ease-in-out 0s infinite alternate backgroundLoading;
|
||||
}
|
||||
|
||||
@keyframes backgroundLoading {
|
||||
0% {
|
||||
background-color: rgba(#000000, 0.6);
|
||||
}
|
||||
|
||||
100% {
|
||||
background-color: #25598e;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
34
front/src/Components/EmbedScreens/CoWebsitesContainer.svelte
Normal file
34
front/src/Components/EmbedScreens/CoWebsitesContainer.svelte
Normal file
@ -0,0 +1,34 @@
|
||||
<script lang="typescript">
|
||||
import { coWebsiteThumbails } from "../../Stores/CoWebsiteStore";
|
||||
import CoWebsiteThumbnail from "./CoWebsiteThumbnailSlot.svelte";
|
||||
|
||||
export let vertical = false;
|
||||
</script>
|
||||
|
||||
{#if $coWebsiteThumbails.length > 0}
|
||||
<div id="cowebsite-thumbnail-container" class:vertical>
|
||||
{#each [...$coWebsiteThumbails.values()] as coWebsite, index (coWebsite.iframe.id)}
|
||||
<CoWebsiteThumbnail {index} {coWebsite} {vertical} />
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
#cowebsite-thumbnail-container {
|
||||
pointer-events: all;
|
||||
height: 12%;
|
||||
display: flex;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
|
||||
&.vertical {
|
||||
height: auto !important;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,22 @@
|
||||
<script lang="typescript">
|
||||
import PresentationLayout from "./Layouts/PresentationLayout.svelte";
|
||||
import MozaicLayout from "./Layouts/MozaicLayout.svelte";
|
||||
import { LayoutMode } from "../../WebRtc/LayoutManager";
|
||||
import { embedScreenLayout } from "../../Stores/EmbedScreensStore";
|
||||
</script>
|
||||
|
||||
<div id="embedScreensContainer">
|
||||
{#if $embedScreenLayout === LayoutMode.Presentation}
|
||||
<PresentationLayout />
|
||||
{:else}
|
||||
<MozaicLayout />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
#embedScreensContainer {
|
||||
display: flex;
|
||||
padding-top: 2%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,61 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import { highlightedEmbedScreen } from "../../../Stores/EmbedScreensStore";
|
||||
import { streamableCollectionStore } from "../../../Stores/StreamableCollectionStore";
|
||||
import MediaBox from "../../Video/MediaBox.svelte";
|
||||
|
||||
let layoutDom: HTMLDivElement;
|
||||
|
||||
const resizeObserver = new ResizeObserver(() => {});
|
||||
|
||||
onMount(() => {
|
||||
resizeObserver.observe(layoutDom);
|
||||
highlightedEmbedScreen.removeHighlight();
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="mozaic-layout" bind:this={layoutDom}>
|
||||
<div
|
||||
class="media-container"
|
||||
class:full-width={$streamableCollectionStore.size === 1 || $streamableCollectionStore.size === 2}
|
||||
class:quarter={$streamableCollectionStore.size === 3 || $streamableCollectionStore.size === 4}
|
||||
>
|
||||
{#each [...$streamableCollectionStore.values()] as peer (peer.uniqueId)}
|
||||
<MediaBox
|
||||
streamable={peer}
|
||||
mozaicFullWidth={$streamableCollectionStore.size === 1 || $streamableCollectionStore.size === 2}
|
||||
mozaicQuarter={$streamableCollectionStore.size === 3 || $streamableCollectionStore.size === 4}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
#mozaic-layout {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
.media-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: 33.3% 33.3% 33.3%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
&.full-width {
|
||||
grid-template-columns: 100%;
|
||||
grid-template-rows: 50% 50%;
|
||||
}
|
||||
|
||||
&.quarter {
|
||||
grid-template-columns: 50% 50%;
|
||||
grid-template-rows: 50% 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,169 @@
|
||||
<script lang="ts">
|
||||
import { highlightedEmbedScreen } from "../../../Stores/EmbedScreensStore";
|
||||
import CamerasContainer from "../CamerasContainer.svelte";
|
||||
import CoWebsitesContainer from "../CoWebsitesContainer.svelte";
|
||||
import MediaBox from "../../Video/MediaBox.svelte";
|
||||
import { coWebsiteManager } from "../../../WebRtc/CoWebsiteManager";
|
||||
import { afterUpdate, onMount } from "svelte";
|
||||
import { isMediaBreakpointDown, isMediaBreakpointUp } from "../../../Utils/BreakpointsUtils";
|
||||
import { peerStore } from "../../../Stores/PeerStore";
|
||||
|
||||
function closeCoWebsite() {
|
||||
if ($highlightedEmbedScreen?.type === "cowebsite") {
|
||||
if ($highlightedEmbedScreen.embed.closable) {
|
||||
coWebsiteManager.closeCoWebsite($highlightedEmbedScreen.embed).catch(() => {
|
||||
console.error("Error during co-website highlighted closing");
|
||||
});
|
||||
} else {
|
||||
coWebsiteManager.unloadCoWebsite($highlightedEmbedScreen.embed).catch(() => {
|
||||
console.error("Error during co-website highlighted unloading");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function minimiseCoWebsite() {
|
||||
if ($highlightedEmbedScreen?.type === "cowebsite") {
|
||||
highlightedEmbedScreen.removeHighlight();
|
||||
coWebsiteManager.resizeAllIframes();
|
||||
}
|
||||
}
|
||||
|
||||
function expandCoWebsite() {
|
||||
if ($highlightedEmbedScreen?.type === "cowebsite") {
|
||||
coWebsiteManager.goToMain($highlightedEmbedScreen.embed);
|
||||
}
|
||||
}
|
||||
|
||||
afterUpdate(() => {
|
||||
if ($highlightedEmbedScreen) {
|
||||
coWebsiteManager.resizeAllIframes();
|
||||
}
|
||||
});
|
||||
|
||||
let layoutDom: HTMLDivElement;
|
||||
|
||||
let displayCoWebsiteContainer = isMediaBreakpointDown("lg");
|
||||
let displayFullMedias = isMediaBreakpointUp("sm");
|
||||
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
displayCoWebsiteContainer = isMediaBreakpointDown("lg");
|
||||
displayFullMedias = isMediaBreakpointUp("sm");
|
||||
|
||||
if (!displayCoWebsiteContainer && $highlightedEmbedScreen && $highlightedEmbedScreen.type === "cowebsite") {
|
||||
highlightedEmbedScreen.removeHighlight();
|
||||
}
|
||||
|
||||
if (displayFullMedias) {
|
||||
highlightedEmbedScreen.removeHighlight();
|
||||
}
|
||||
});
|
||||
|
||||
onMount(() => {
|
||||
resizeObserver.observe(layoutDom);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="presentation-layout" bind:this={layoutDom} class:full-medias={displayFullMedias}>
|
||||
{#if displayFullMedias}
|
||||
<div id="full-medias">
|
||||
<CamerasContainer full={true} highlightedEmbedScreen={$highlightedEmbedScreen} />
|
||||
</div>
|
||||
{:else}
|
||||
<div id="embed-left-block" class:full={$peerStore.size === 0}>
|
||||
<div id="main-embed-screen">
|
||||
{#if $highlightedEmbedScreen}
|
||||
{#if $highlightedEmbedScreen.type === "streamable"}
|
||||
{#key $highlightedEmbedScreen.embed.uniqueId}
|
||||
<MediaBox
|
||||
isHightlighted={true}
|
||||
isClickable={true}
|
||||
streamable={$highlightedEmbedScreen.embed}
|
||||
/>
|
||||
{/key}
|
||||
{:else if $highlightedEmbedScreen.type === "cowebsite"}
|
||||
{#key $highlightedEmbedScreen.embed.iframe.id}
|
||||
<div
|
||||
id={"cowebsite-slot-" + $highlightedEmbedScreen.embed.iframe.id}
|
||||
class="highlighted-cowebsite nes-container is-rounded"
|
||||
>
|
||||
<div class="actions">
|
||||
<button type="button" class="nes-btn is-primary expand" on:click={expandCoWebsite}
|
||||
>></button
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="nes-btn is-secondary minimise"
|
||||
on:click={minimiseCoWebsite}>=</button
|
||||
>
|
||||
<button type="button" class="nes-btn is-error close" on:click={closeCoWebsite}
|
||||
>×</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{/key}
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if displayCoWebsiteContainer}
|
||||
<CoWebsitesContainer />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if $peerStore.size > 0}
|
||||
<CamerasContainer highlightedEmbedScreen={$highlightedEmbedScreen} />
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
#presentation-layout {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
||||
&.full-medias {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
#embed-left-block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 0 0 75%;
|
||||
height: 100%;
|
||||
width: 75%;
|
||||
|
||||
&.full {
|
||||
flex: 0 0 98% !important;
|
||||
width: 98% !important;
|
||||
}
|
||||
}
|
||||
|
||||
#main-embed-screen {
|
||||
height: 82%;
|
||||
margin-bottom: 3%;
|
||||
|
||||
.highlighted-cowebsite {
|
||||
height: 100% !important;
|
||||
width: 96%;
|
||||
background-color: rgba(#000000, 0.6);
|
||||
margin: 0 !important;
|
||||
|
||||
.actions {
|
||||
z-index: 200;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: end;
|
||||
gap: 2%;
|
||||
|
||||
button {
|
||||
pointer-events: all;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -87,7 +87,7 @@
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
z-index: 101;
|
||||
z-index: 300;
|
||||
}
|
||||
|
||||
.emote-menu {
|
||||
|
@ -121,7 +121,7 @@
|
||||
right: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
z-index: 150;
|
||||
z-index: 400;
|
||||
}
|
||||
|
||||
div.interact-menu {
|
||||
|
@ -60,7 +60,7 @@
|
||||
margin-top: 4%;
|
||||
max-height: 80vh;
|
||||
max-width: 80vw;
|
||||
z-index: 250;
|
||||
z-index: 600;
|
||||
overflow: auto;
|
||||
text-align: center;
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script lang="typescript">
|
||||
import { onMount } from "svelte";
|
||||
import { audioManagerVisibilityStore } from "../Stores/AudioManagerStore";
|
||||
import { hasEmbedScreen } from "../Stores/EmbedScreensStore";
|
||||
import { embedScreenLayout, hasEmbedScreen } from "../Stores/EmbedScreensStore";
|
||||
import { emoteMenuStore } from "../Stores/EmoteStore";
|
||||
import { myCameraVisibilityStore } from "../Stores/MyCameraStoreVisibility";
|
||||
import { requestVisitCardsStore } from "../Stores/GameStore";
|
||||
@ -30,11 +30,12 @@
|
||||
import BanMessageContainer from "./TypeMessage/BanMessageContainer.svelte";
|
||||
import { textMessageStore } from "../Stores/TypeMessageStore/TextMessageStore";
|
||||
import TextMessageContainer from "./TypeMessage/TextMessageContainer.svelte";
|
||||
import { soundPlayingStore } from "../Stores/SoundPlayingStore";
|
||||
import AudioPlaying from "./UI/AudioPlaying.svelte";
|
||||
import { showLimitRoomModalStore, showShareLinkMapModalStore } from "../Stores/ModalStore";
|
||||
import LimitRoomModal from "./Modal/LimitRoomModal.svelte";
|
||||
import ShareLinkMapModal from "./Modal/ShareLinkMapModal.svelte";
|
||||
import { soundPlayingStore } from "../Stores/SoundPlayingStore";
|
||||
import AudioPlaying from "./UI/AudioPlaying.svelte";
|
||||
import { showLimitRoomModalStore, showShareLinkMapModalStore } from "../Stores/ModalStore";
|
||||
import LimitRoomModal from "./Modal/LimitRoomModal.svelte";
|
||||
import ShareLinkMapModal from "./Modal/ShareLinkMapModal.svelte";
|
||||
import { LayoutMode } from "../WebRtc/LayoutManager";
|
||||
|
||||
let mainLayout: HTMLDivElement;
|
||||
|
||||
@ -55,7 +56,7 @@ import ShareLinkMapModal from "./Modal/ShareLinkMapModal.svelte";
|
||||
<MenuIcon />
|
||||
{/if}
|
||||
|
||||
{#if displayCoWebsiteContainer}
|
||||
{#if $embedScreenLayout === LayoutMode.VideoChat || displayCoWebsiteContainer}
|
||||
<CoWebsitesContainer vertical={true} />
|
||||
{/if}
|
||||
</aside>
|
||||
@ -75,14 +76,14 @@ import ShareLinkMapModal from "./Modal/ShareLinkMapModal.svelte";
|
||||
<AudioPlaying url={$soundPlayingStore} />
|
||||
{/if}
|
||||
|
||||
{#if $showReportScreenStore !== userReportEmpty}
|
||||
<ReportMenu />
|
||||
{/if}
|
||||
|
||||
{#if $warningContainerStore}
|
||||
<WarningContainer />
|
||||
{/if}
|
||||
|
||||
{#if $showReportScreenStore !== userReportEmpty}
|
||||
<ReportMenu />
|
||||
{/if}
|
||||
|
||||
{#if $helpCameraSettingsVisibleStore}
|
||||
<HelpCameraSettingsPopup />
|
||||
{/if}
|
||||
@ -133,7 +134,7 @@ import ShareLinkMapModal from "./Modal/ShareLinkMapModal.svelte";
|
||||
|
||||
#main-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 7% 93%;
|
||||
grid-template-columns: 120px calc(100% - 120px);
|
||||
grid-template-rows: 80% 20%;
|
||||
|
||||
&-left-aside {
|
||||
|
@ -74,6 +74,7 @@
|
||||
|
||||
<style lang="scss">
|
||||
@import "../../../style/breakpoints.scss";
|
||||
|
||||
.menuIcon {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -89,9 +90,11 @@
|
||||
margin: 5%;
|
||||
}
|
||||
}
|
||||
|
||||
.menuIcon img:hover {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
.menuIcon {
|
||||
margin-top: 10%;
|
||||
|
@ -32,6 +32,7 @@
|
||||
max-width: 80vw;
|
||||
overflow: auto;
|
||||
text-align: center;
|
||||
z-index: 500;
|
||||
|
||||
h2 {
|
||||
font-family: "Press Start 2P";
|
||||
|
@ -75,6 +75,7 @@
|
||||
max-width: 80vw;
|
||||
overflow: auto;
|
||||
text-align: center;
|
||||
z-index: 450;
|
||||
|
||||
h2 {
|
||||
font-family: "Press Start 2P";
|
||||
|
@ -108,7 +108,7 @@
|
||||
pointer-events: auto;
|
||||
background-color: #333333;
|
||||
color: whitesmoke;
|
||||
z-index: 300;
|
||||
z-index: 650;
|
||||
position: absolute;
|
||||
height: 70vh;
|
||||
width: 50vw;
|
||||
|
@ -11,3 +11,9 @@
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.main-ban-message-container {
|
||||
z-index: 800;
|
||||
}
|
||||
</style>
|
||||
|
@ -15,7 +15,8 @@
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
div.main-text-message-container {
|
||||
.main-text-message-container {
|
||||
padding-top: 16px;
|
||||
z-index: 800;
|
||||
}
|
||||
</style>
|
||||
|
@ -37,6 +37,7 @@
|
||||
background-color: black;
|
||||
border-radius: 30px 0 0 30px;
|
||||
display: inline-flex;
|
||||
z-index: 750;
|
||||
|
||||
img {
|
||||
border-radius: 50%;
|
||||
|
@ -8,16 +8,23 @@
|
||||
|
||||
export let streamable: Streamable;
|
||||
export let isHightlighted = false;
|
||||
export let clickable = false;
|
||||
export let isClickable = false;
|
||||
export let mozaicFullWidth = false;
|
||||
export let mozaicQuarter = false;
|
||||
</script>
|
||||
|
||||
<div class="media-container nes-container is-rounded {isHightlighted ? 'hightlighted' : ''}" class:clickable>
|
||||
<div
|
||||
class="media-container nes-container is-rounded {isHightlighted ? 'hightlighted' : ''}"
|
||||
class:clickable={isClickable}
|
||||
class:mozaic-full-width={mozaicFullWidth}
|
||||
class:mozaic-quarter={mozaicQuarter}
|
||||
>
|
||||
{#if streamable instanceof VideoPeer}
|
||||
<VideoMediaBox peer={streamable} {clickable} />
|
||||
<VideoMediaBox peer={streamable} clickable={isClickable} />
|
||||
{:else if streamable instanceof ScreenSharingPeer}
|
||||
<ScreenSharingMediaBox peer={streamable} {clickable} />
|
||||
<ScreenSharingMediaBox peer={streamable} clickable={isClickable} />
|
||||
{:else}
|
||||
<LocalStreamMediaBox peer={streamable} {clickable} cssClass="" />
|
||||
<LocalStreamMediaBox peer={streamable} clickable={isClickable} cssClass="" />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@ -57,6 +64,32 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.mozaic-full-width {
|
||||
width: 95%;
|
||||
max-width: 95%;
|
||||
margin-left: 3%;
|
||||
margin-right: 3%;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
|
||||
&:hover {
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
}
|
||||
|
||||
&.mozaic-quarter {
|
||||
width: 95%;
|
||||
max-width: 95%;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
|
||||
&:hover {
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
}
|
||||
|
||||
&.clickable {
|
||||
cursor: url("../../../style/images/cursor_pointer.png"), pointer;
|
||||
}
|
||||
|
@ -57,7 +57,7 @@
|
||||
height: 120px;
|
||||
margin: auto;
|
||||
animation: spin 2s linear infinite;
|
||||
z-index: 102;
|
||||
z-index: 350;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
|
@ -42,7 +42,7 @@
|
||||
font-family: Lato;
|
||||
min-width: 300px;
|
||||
opacity: 0.9;
|
||||
z-index: 270;
|
||||
z-index: 700;
|
||||
h2 {
|
||||
padding: 5px;
|
||||
}
|
||||
|
@ -9,21 +9,21 @@ import { get } from "svelte/store";
|
||||
import { ON_ACTION_TRIGGER_BUTTON } from "../../WebRtc/LayoutManager";
|
||||
import type { ITiledMapLayer } from "../Map/ITiledMap";
|
||||
import { GameMapProperties } from "./GameMapProperties";
|
||||
import { highlightedEmbedScreen } from "../../Stores/EmbedScreensStore";
|
||||
|
||||
enum OpenCoWebsiteState {
|
||||
LOADING,
|
||||
ASLEEP,
|
||||
OPENED,
|
||||
MUST_BE_CLOSE,
|
||||
}
|
||||
|
||||
interface OpenCoWebsite {
|
||||
coWebsite: CoWebsite | undefined;
|
||||
coWebsite: CoWebsite;
|
||||
state: OpenCoWebsiteState;
|
||||
}
|
||||
|
||||
export class GameMapPropertiesListener {
|
||||
private coWebsitesOpenByLayer = new Map<ITiledMapLayer, OpenCoWebsite>();
|
||||
private coWebsitesActionTriggerByLayer = new Map<ITiledMapLayer, string>();
|
||||
|
||||
constructor(private scene: GameScene, private gameMap: GameMap) {}
|
||||
|
||||
@ -64,10 +64,8 @@ export class GameMapPropertiesListener {
|
||||
let openWebsiteProperty: string | undefined;
|
||||
let allowApiProperty: boolean | undefined;
|
||||
let websitePolicyProperty: string | undefined;
|
||||
let websiteWidthProperty: number | undefined;
|
||||
let websitePositionProperty: number | undefined;
|
||||
let websiteTriggerProperty: string | undefined;
|
||||
let websiteTriggerMessageProperty: string | undefined;
|
||||
|
||||
layer.properties.forEach((property) => {
|
||||
switch (property.name) {
|
||||
@ -80,18 +78,12 @@ export class GameMapPropertiesListener {
|
||||
case GameMapProperties.OPEN_WEBSITE_POLICY:
|
||||
websitePolicyProperty = property.value as string | undefined;
|
||||
break;
|
||||
case GameMapProperties.OPEN_WEBSITE_WIDTH:
|
||||
websiteWidthProperty = property.value as number | undefined;
|
||||
break;
|
||||
case GameMapProperties.OPEN_WEBSITE_POSITION:
|
||||
websitePositionProperty = property.value as number | undefined;
|
||||
break;
|
||||
case GameMapProperties.OPEN_WEBSITE_TRIGGER:
|
||||
websiteTriggerProperty = property.value as string | undefined;
|
||||
break;
|
||||
case GameMapProperties.OPEN_WEBSITE_TRIGGER_MESSAGE:
|
||||
websiteTriggerMessageProperty = property.value as string | undefined;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
@ -105,27 +97,30 @@ export class GameMapPropertiesListener {
|
||||
return;
|
||||
}
|
||||
|
||||
const coWebsite = coWebsiteManager.addCoWebsite(
|
||||
openWebsiteProperty,
|
||||
this.scene.MapUrlFile,
|
||||
allowApiProperty,
|
||||
websitePolicyProperty,
|
||||
websitePositionProperty,
|
||||
false
|
||||
);
|
||||
|
||||
this.coWebsitesOpenByLayer.set(layer, {
|
||||
coWebsite: undefined,
|
||||
state: OpenCoWebsiteState.LOADING,
|
||||
coWebsite: coWebsite,
|
||||
state: OpenCoWebsiteState.ASLEEP,
|
||||
});
|
||||
|
||||
const openWebsiteFunction = () => {
|
||||
coWebsiteManager
|
||||
.loadCoWebsite(
|
||||
openWebsiteProperty as string,
|
||||
this.scene.MapUrlFile,
|
||||
allowApiProperty,
|
||||
websitePolicyProperty,
|
||||
websiteWidthProperty,
|
||||
websitePositionProperty
|
||||
)
|
||||
.loadCoWebsite(coWebsite)
|
||||
.then((coWebsite) => {
|
||||
const coWebsiteOpen = this.coWebsitesOpenByLayer.get(layer);
|
||||
if (coWebsiteOpen && coWebsiteOpen.state === OpenCoWebsiteState.MUST_BE_CLOSE) {
|
||||
coWebsiteManager.closeCoWebsite(coWebsite).catch((e) => console.error(e));
|
||||
coWebsiteManager.closeCoWebsite(coWebsite).catch(() => {
|
||||
console.error("Error during a co-website closing");
|
||||
});
|
||||
this.coWebsitesOpenByLayer.delete(layer);
|
||||
this.coWebsitesActionTriggerByLayer.delete(layer);
|
||||
} else {
|
||||
this.coWebsitesOpenByLayer.set(layer, {
|
||||
coWebsite,
|
||||
@ -133,27 +128,17 @@ export class GameMapPropertiesListener {
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((e) => console.error(e));
|
||||
.catch(() => {
|
||||
console.error("Error during loading a co-website: " + coWebsite.url);
|
||||
});
|
||||
|
||||
layoutManagerActionStore.removeAction(actionUuid);
|
||||
};
|
||||
|
||||
const forceTrigger = localUserStore.getForceCowebsiteTrigger();
|
||||
if (forceTrigger || websiteTriggerProperty === ON_ACTION_TRIGGER_BUTTON) {
|
||||
if (!websiteTriggerMessageProperty) {
|
||||
websiteTriggerMessageProperty = "Press SPACE or touch here to open web site";
|
||||
}
|
||||
|
||||
this.coWebsitesActionTriggerByLayer.set(layer, actionUuid);
|
||||
|
||||
layoutManagerActionStore.addAction({
|
||||
uuid: actionUuid,
|
||||
type: "message",
|
||||
message: websiteTriggerMessageProperty,
|
||||
callback: () => openWebsiteFunction(),
|
||||
userInputManager: this.scene.userInputManager,
|
||||
});
|
||||
} else {
|
||||
if (
|
||||
!localUserStore.getForceCowebsiteTrigger() &&
|
||||
websiteTriggerProperty !== ON_ACTION_TRIGGER_BUTTON
|
||||
) {
|
||||
openWebsiteFunction();
|
||||
}
|
||||
});
|
||||
@ -194,7 +179,7 @@ export class GameMapPropertiesListener {
|
||||
return;
|
||||
}
|
||||
|
||||
if (coWebsiteOpen.state === OpenCoWebsiteState.LOADING) {
|
||||
if (coWebsiteOpen.state === OpenCoWebsiteState.ASLEEP) {
|
||||
coWebsiteOpen.state = OpenCoWebsiteState.MUST_BE_CLOSE;
|
||||
}
|
||||
|
||||
@ -203,26 +188,6 @@ export class GameMapPropertiesListener {
|
||||
}
|
||||
|
||||
this.coWebsitesOpenByLayer.delete(layer);
|
||||
|
||||
if (!websiteTriggerProperty) {
|
||||
return;
|
||||
}
|
||||
|
||||
const actionStore = get(layoutManagerActionStore);
|
||||
const actionTriggerUuid = this.coWebsitesActionTriggerByLayer.get(layer);
|
||||
|
||||
if (!actionTriggerUuid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const action =
|
||||
actionStore && actionStore.length > 0
|
||||
? actionStore.find((action) => action.uuid === actionTriggerUuid)
|
||||
: undefined;
|
||||
|
||||
if (action) {
|
||||
layoutManagerActionStore.removeAction(actionTriggerUuid);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -2145,8 +2145,8 @@ ${escapedMessage}
|
||||
public stopJitsi(): void {
|
||||
const coWebsite = coWebsiteManager.searchJitsi();
|
||||
if (coWebsite) {
|
||||
coWebsiteManager.closeCoWebsite(coWebsite).catch(() => {
|
||||
console.error("Error during Jitsi co-website closing");
|
||||
coWebsiteManager.closeCoWebsite(coWebsite).catch((e) => {
|
||||
console.error("Error during Jitsi co-website closing", e);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
66
front/src/Stores/CoWebsiteStore.ts
Normal file
66
front/src/Stores/CoWebsiteStore.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { derived, get, writable } from "svelte/store";
|
||||
import type { CoWebsite } from "../WebRtc/CoWebsiteManager";
|
||||
import { highlightedEmbedScreen } from "./EmbedScreensStore";
|
||||
|
||||
function createCoWebsiteStore() {
|
||||
const { subscribe, set, update } = writable(Array<CoWebsite>());
|
||||
|
||||
set(Array<CoWebsite>());
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
add: (coWebsite: CoWebsite, position?: number) => {
|
||||
coWebsite.state.subscribe((value) => {
|
||||
update((currentArray) => currentArray);
|
||||
});
|
||||
|
||||
if (position || position === 0) {
|
||||
update((currentArray) => {
|
||||
if (position === 0) {
|
||||
return [coWebsite, ...currentArray];
|
||||
} else if (currentArray.length > position) {
|
||||
const test = [...currentArray.splice(position, 0, coWebsite)];
|
||||
return [...currentArray.splice(position, 0, coWebsite)];
|
||||
}
|
||||
|
||||
return [...currentArray, coWebsite];
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
update((currentArray) => [...currentArray, coWebsite]);
|
||||
},
|
||||
remove: (coWebsite: CoWebsite) => {
|
||||
update((currentArray) => [
|
||||
...currentArray.filter((currentCoWebsite) => currentCoWebsite.iframe.id !== coWebsite.iframe.id),
|
||||
]);
|
||||
},
|
||||
empty: () => {
|
||||
set(Array<CoWebsite>());
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const coWebsites = createCoWebsiteStore();
|
||||
|
||||
export const coWebsitesNotAsleep = derived([coWebsites], ([$coWebsites]) =>
|
||||
$coWebsites.filter((coWebsite) => get(coWebsite.state) !== "asleep")
|
||||
);
|
||||
|
||||
export const mainCoWebsite = derived([coWebsites], ([$coWebsites]) =>
|
||||
$coWebsites.find((coWebsite) => get(coWebsite.state) !== "asleep")
|
||||
);
|
||||
|
||||
export const coWebsiteThumbails = derived(
|
||||
[coWebsites, highlightedEmbedScreen, mainCoWebsite],
|
||||
([$coWebsites, highlightedEmbedScreen, $mainCoWebsite]) =>
|
||||
$coWebsites.filter((coWebsite, index) => {
|
||||
return (
|
||||
(!$mainCoWebsite || $mainCoWebsite.iframe.id !== coWebsite.iframe.id) &&
|
||||
(!highlightedEmbedScreen ||
|
||||
highlightedEmbedScreen.type !== "cowebsite" ||
|
||||
(highlightedEmbedScreen.type === "cowebsite" &&
|
||||
highlightedEmbedScreen.embed.iframe.id !== coWebsite.iframe.id))
|
||||
);
|
||||
})
|
||||
);
|
51
front/src/Stores/EmbedScreensStore.ts
Normal file
51
front/src/Stores/EmbedScreensStore.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { derived, get, writable } from "svelte/store";
|
||||
import type { CoWebsite } from "../WebRtc/CoWebsiteManager";
|
||||
import { LayoutMode } from "../WebRtc/LayoutManager";
|
||||
import { coWebsites } from "./CoWebsiteStore";
|
||||
import { Streamable, streamableCollectionStore } from "./StreamableCollectionStore";
|
||||
|
||||
export type EmbedScreen =
|
||||
| {
|
||||
type: "streamable";
|
||||
embed: Streamable;
|
||||
}
|
||||
| {
|
||||
type: "cowebsite";
|
||||
embed: CoWebsite;
|
||||
};
|
||||
|
||||
function createHighlightedEmbedScreenStore() {
|
||||
const { subscribe, set, update } = writable<EmbedScreen | null>(null);
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
highlight: (embedScreen: EmbedScreen) => {
|
||||
set(embedScreen);
|
||||
},
|
||||
removeHighlight: () => {
|
||||
set(null);
|
||||
},
|
||||
toggleHighlight: (embedScreen: EmbedScreen) => {
|
||||
update((currentEmbedScreen) =>
|
||||
!currentEmbedScreen ||
|
||||
embedScreen.type !== currentEmbedScreen.type ||
|
||||
(embedScreen.type === "cowebsite" &&
|
||||
currentEmbedScreen.type === "cowebsite" &&
|
||||
embedScreen.embed.iframe.id !== currentEmbedScreen.embed.iframe.id) ||
|
||||
(embedScreen.type === "streamable" &&
|
||||
currentEmbedScreen.type === "streamable" &&
|
||||
embedScreen.embed.uniqueId !== currentEmbedScreen.embed.uniqueId)
|
||||
? embedScreen
|
||||
: null
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const highlightedEmbedScreen = createHighlightedEmbedScreenStore();
|
||||
export const embedScreenLayout = writable<LayoutMode>(LayoutMode.Presentation);
|
||||
|
||||
export const hasEmbedScreen = derived(
|
||||
[streamableCollectionStore],
|
||||
($values) => get(streamableCollectionStore).size + get(coWebsites).length > 0
|
||||
);
|
File diff suppressed because it is too large
Load Diff
@ -132,17 +132,20 @@ class JitsiFactory {
|
||||
return slugify(instance.replace("/", "-") + "-" + roomName);
|
||||
}
|
||||
|
||||
public start(
|
||||
public async start(
|
||||
roomName: string,
|
||||
playerName: string,
|
||||
jwt?: string,
|
||||
config?: object,
|
||||
interfaceConfig?: object,
|
||||
jitsiUrl?: string,
|
||||
jitsiWidth?: number
|
||||
): Promise<CoWebsite> {
|
||||
return coWebsiteManager.addCoWebsite(
|
||||
async (cowebsiteDiv) => {
|
||||
jitsiUrl?: string
|
||||
) {
|
||||
const coWebsite = coWebsiteManager.searchJitsi();
|
||||
|
||||
if (coWebsite) {
|
||||
await coWebsiteManager.closeCoWebsite(coWebsite);
|
||||
}
|
||||
|
||||
// Jitsi meet external API maintains some data in local storage
|
||||
// which is sent via the appData URL parameter when joining a
|
||||
// conference. Problem is that this data grows indefinitely. Thus
|
||||
@ -162,22 +165,24 @@ class JitsiFactory {
|
||||
jwt: jwt,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
parentNode: cowebsiteDiv,
|
||||
parentNode: coWebsiteManager.getCoWebsiteBuffer(),
|
||||
configOverwrite: mergeConfig(config),
|
||||
interfaceConfigOverwrite: { ...defaultInterfaceConfig, ...interfaceConfig },
|
||||
};
|
||||
|
||||
if (!options.jwt) {
|
||||
delete options.jwt;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const doResolve = (): void => {
|
||||
const iframe = cowebsiteDiv.querySelector<HTMLIFrameElement>('[id*="jitsi" i]');
|
||||
if (iframe === null) {
|
||||
throw new Error("Could not find Jitsi Iframe");
|
||||
const iframe = coWebsiteManager.getCoWebsiteBuffer().querySelector<HTMLIFrameElement>('[id*="jitsi" i]');
|
||||
if (iframe) {
|
||||
coWebsiteManager.addCoWebsiteFromIframe(iframe, false, undefined, 0, false, true);
|
||||
}
|
||||
resolve(iframe);
|
||||
|
||||
coWebsiteManager.resizeAllIframes();
|
||||
};
|
||||
|
||||
options.onload = () => doResolve(); //we want for the iframe to be loaded before triggering animations.
|
||||
setTimeout(() => doResolve(), 2000); //failsafe in case the iframe is deleted before loading or too long to load
|
||||
this.jitsiApi = new window.JitsiMeetExternalAPI(domain, options);
|
||||
@ -185,11 +190,6 @@ class JitsiFactory {
|
||||
|
||||
this.jitsiApi.addListener("audioMuteStatusChanged", this.audioCallback);
|
||||
this.jitsiApi.addListener("videoMuteStatusChanged", this.videoCallback);
|
||||
});
|
||||
},
|
||||
jitsiWidth,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
public stop() {
|
||||
@ -197,12 +197,6 @@ class JitsiFactory {
|
||||
return;
|
||||
}
|
||||
|
||||
const jitsiCoWebsite = coWebsiteManager.searchJitsi();
|
||||
|
||||
if (jitsiCoWebsite) {
|
||||
coWebsiteManager.closeJitsi().catch((e) => console.error(e));
|
||||
}
|
||||
|
||||
this.jitsiApi.removeListener("audioMuteStatusChanged", this.audioCallback);
|
||||
this.jitsiApi.removeListener("videoMuteStatusChanged", this.videoCallback);
|
||||
this.jitsiApi?.dispose();
|
||||
|
@ -1,220 +1,3 @@
|
||||
/* A potentially shared website could appear in an iframe in the cowebsite space. */
|
||||
|
||||
#cowebsite {
|
||||
position: fixed;
|
||||
z-index: 200;
|
||||
transition: transform 0.5s;
|
||||
background-color: whitesmoke;
|
||||
display: none;
|
||||
|
||||
&.loading {
|
||||
background-color: gray;
|
||||
}
|
||||
|
||||
main {
|
||||
iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: 100vw;
|
||||
max-height: 100vh;
|
||||
}
|
||||
}
|
||||
|
||||
aside {
|
||||
background: gray;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
|
||||
#cowebsite-aside-holder {
|
||||
background: gray;
|
||||
height: 20px;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
|
||||
img {
|
||||
width: 80%;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
#cowebsite-aside-buttons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: auto;
|
||||
flex: 1;
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
.top-right-btn{
|
||||
transform: scale(0.5);
|
||||
cursor: url('./images/cursor_pointer.png'), pointer;
|
||||
}
|
||||
|
||||
#cowebsite-sub-icons {
|
||||
display: flex;
|
||||
margin-top: auto;
|
||||
visibility: hidden;
|
||||
justify-content: end;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&-container {
|
||||
position: absolute;
|
||||
display: none;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
&-main {
|
||||
padding: 2% 5%;
|
||||
height: 50%;
|
||||
}
|
||||
|
||||
&-sub {
|
||||
position: absolute !important;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
bottom: 23%;
|
||||
height: 20% !important;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&-slot-0 {
|
||||
z-index: 70 !important;
|
||||
background-color: whitesmoke;
|
||||
}
|
||||
|
||||
@for $i from 1 through 4 {
|
||||
&-slot-#{$i} {
|
||||
transition: transform 0.5s;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
display: none;
|
||||
background-color: #333333;
|
||||
|
||||
@if $i == 1 {
|
||||
width: 100%;
|
||||
} @else {
|
||||
width: 33%;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 50;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.actions-move {
|
||||
display: none;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
gap: 10%;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: rgba($color: #333333, $alpha: 0.6);
|
||||
|
||||
.actions-move {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
pointer-events: all !important;
|
||||
margin: 3% 2%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: end;
|
||||
position: relative;
|
||||
z-index: 50;
|
||||
|
||||
button {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin: 8px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-buffer {
|
||||
iframe {
|
||||
z-index: 45 !important;
|
||||
pointer-events: none !important;
|
||||
overflow: hidden;
|
||||
border: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.main {
|
||||
pointer-events: all !important;
|
||||
z-index: 205 !important;
|
||||
}
|
||||
|
||||
.sub-main {
|
||||
pointer-events: all !important;
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
transform: scale(0.5, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
.pixel {
|
||||
visibility: hidden;
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-aspect-ratio: 1/1) {
|
||||
#cowebsite {
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 50%;
|
||||
height: 100vh;
|
||||
display: none;
|
||||
|
||||
&.loading {
|
||||
transform: translateX(90%);
|
||||
}
|
||||
&.hidden {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
main {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
aside {
|
||||
width: 30px;
|
||||
|
||||
img {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
|
||||
&-aside-holder {
|
||||
cursor: ew-resize;
|
||||
}
|
||||
}
|
||||
}
|
||||
@import "cowebsite/global";
|
||||
@import "cowebsite/short-screens";
|
||||
@import "cowebsite/wide-screens";
|
||||
|
119
front/style/cowebsite/_global.scss
Normal file
119
front/style/cowebsite/_global.scss
Normal file
@ -0,0 +1,119 @@
|
||||
#cowebsite {
|
||||
position: fixed;
|
||||
z-index: 820;
|
||||
transition: transform 0.5s;
|
||||
background-color: rgba(10, 9, 9, 0.8);
|
||||
display: none;
|
||||
|
||||
main {
|
||||
iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: 100vw;
|
||||
max-height: 100vh;
|
||||
}
|
||||
}
|
||||
|
||||
aside {
|
||||
background: gray;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
|
||||
#cowebsite-aside-holder {
|
||||
background: gray;
|
||||
height: 20px;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
|
||||
img {
|
||||
width: 80%;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
#cowebsite-aside-buttons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: auto;
|
||||
flex: 1;
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
.top-right-btn {
|
||||
transform: scale(0.5);
|
||||
cursor: url("./images/cursor_pointer.png"), pointer;
|
||||
}
|
||||
|
||||
#cowebsite-other-actions {
|
||||
display: flex;
|
||||
margin-top: auto;
|
||||
visibility: hidden;
|
||||
justify-content: end;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&-loader {
|
||||
width: 20%;
|
||||
|
||||
#smoke {
|
||||
@for $i from 1 through 3 {
|
||||
#trail-#{$i} {
|
||||
@for $y from 1 through 3 {
|
||||
#trail-#{$i}-state-#{$y} {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-slot-main {
|
||||
z-index: 70 !important;
|
||||
background-color: rgba(10, 9, 9, 0);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&-buffer {
|
||||
iframe {
|
||||
z-index: 45 !important;
|
||||
pointer-events: none !important;
|
||||
overflow: hidden;
|
||||
border: 0;
|
||||
position: absolute;
|
||||
|
||||
&.pixel {
|
||||
height: 1px !important;
|
||||
width: 1px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.main {
|
||||
pointer-events: all !important;
|
||||
z-index: 821 !important;
|
||||
}
|
||||
|
||||
.highlighted {
|
||||
pointer-events: all !important;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
transform: scale(0.5, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
.pixel {
|
||||
visibility: hidden;
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
}
|
||||
}
|
84
front/style/cowebsite/_short-screens.scss
Normal file
84
front/style/cowebsite/_short-screens.scss
Normal file
@ -0,0 +1,84 @@
|
||||
@include media-breakpoint-up(md) {
|
||||
#main-container {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
#cowebsite {
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 50%;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
|
||||
visibility: collapse;
|
||||
transform: translateY(-100%);
|
||||
|
||||
&.loading {
|
||||
visibility: visible;
|
||||
transform: translateY(0%);
|
||||
}
|
||||
|
||||
&.opened {
|
||||
visibility: visible;
|
||||
transform: translateY(0%);
|
||||
}
|
||||
|
||||
&.closing {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
&-loader {
|
||||
height: 20%;
|
||||
}
|
||||
|
||||
main {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
aside {
|
||||
height: 50px;
|
||||
flex-direction: row-reverse;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
#cowebsite-aside-holder {
|
||||
height: 100%;
|
||||
cursor: ns-resize;
|
||||
|
||||
img {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
#cowebsite-aside-buttons {
|
||||
flex-direction: row-reverse;
|
||||
margin-left: auto;
|
||||
margin-bottom: 0;
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
#cowebsite-fullscreen {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
#cowebsite-other-actions {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-top: 0;
|
||||
height: 100%;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.top-right-btn {
|
||||
img {
|
||||
width: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
41
front/style/cowebsite/_wide-screens.scss
Normal file
41
front/style/cowebsite/_wide-screens.scss
Normal file
@ -0,0 +1,41 @@
|
||||
@include media-breakpoint-down(lg) {
|
||||
#cowebsite {
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 50%;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
visibility: collapse;
|
||||
transform: translateX(100%);
|
||||
|
||||
&.loading {
|
||||
visibility: visible;
|
||||
transform: translateX(0%);
|
||||
}
|
||||
|
||||
&.opened {
|
||||
visibility: visible;
|
||||
transform: translateX(0%);
|
||||
}
|
||||
|
||||
&.closing {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
main {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
aside {
|
||||
width: 30px;
|
||||
|
||||
img {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
|
||||
&-aside-holder {
|
||||
cursor: ew-resize;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user