mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-06 15:59:38 +00:00
LOVE: updated layout of floors and ParticipantView (#8270)
Signed-off-by: Alexander Platov <alexander.platov@hardcoreeng.com>
This commit is contained in:
parent
f140f03539
commit
c9d688c115
@ -724,7 +724,7 @@
|
||||
&.categoryHeader {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
z-index: 100;
|
||||
}
|
||||
&.selectable.large {
|
||||
.hulyAccordionItem-header__label-wrapper {
|
||||
|
@ -175,6 +175,18 @@
|
||||
& > .antiEditBox input { font-size: .7rem; }
|
||||
}
|
||||
}
|
||||
@container floorGridContainer (max-width: 380px) {
|
||||
.floorGrid-room::before,
|
||||
.floorGrid-configureRoom::before { border-radius: 0.25rem; }
|
||||
.floorGrid-room .floorGrid-room__header {
|
||||
top: calc(100% / var(--huly-floor-roomHeight) / 3 * -1.6 + .125rem);
|
||||
span { font-size: .5rem; }
|
||||
}
|
||||
.floorGrid-configureRoom .floorGrid-configureRoom__header {
|
||||
top: calc(100% / var(--huly-floor-roomHeight) / 3 * -1.6 + .125rem);
|
||||
& > .antiEditBox input { font-size: .5rem; }
|
||||
}
|
||||
}
|
||||
|
||||
// Room
|
||||
@media only screen and (max-width: 1024px) {
|
||||
|
@ -64,7 +64,7 @@ export function checkMobile (): boolean {
|
||||
* @public
|
||||
*/
|
||||
export function isSafari (): boolean {
|
||||
return navigator.userAgent.toLowerCase().includes('safari/')
|
||||
return /^((?!chrome|android).)*safari/i.test(navigator.userAgent.toLowerCase())
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -14,25 +14,15 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { AccountRole, Ref, getCurrentAccount, hasAccountRole, WithLookup } from '@hcengineering/core'
|
||||
import ui, {
|
||||
Breadcrumb,
|
||||
Header,
|
||||
IconEdit,
|
||||
ModernButton,
|
||||
Component,
|
||||
IconMaxWidth,
|
||||
IconMinWidth,
|
||||
Button
|
||||
} from '@hcengineering/ui'
|
||||
import { Breadcrumb, Header, IconEdit, ModernButton, Component } from '@hcengineering/ui'
|
||||
import { Floor, Room } from '@hcengineering/love'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { ViewletSelector } from '@hcengineering/view-resources'
|
||||
import { Viewlet, ViewletPreference } from '@hcengineering/view'
|
||||
|
||||
import lovePlg from '../plugin'
|
||||
import { currentRoom, floors, loveUseMaxWidth } from '../stores'
|
||||
import { currentRoom, floors } from '../stores'
|
||||
import ControlBar from './ControlBar.svelte'
|
||||
import { toggleLoveUseMaxWidth } from '../utils'
|
||||
|
||||
export let rooms: Room[] = []
|
||||
export let floor: Ref<Floor>
|
||||
@ -58,14 +48,6 @@
|
||||
<ViewletSelector bind:viewlet bind:preference bind:loading viewletQuery={{ attachTo: lovePlg.class.Floor }} />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="actions">
|
||||
<Button
|
||||
icon={$loveUseMaxWidth ? IconMaxWidth : IconMinWidth}
|
||||
kind={'regular'}
|
||||
pressed={$loveUseMaxWidth}
|
||||
size={'medium'}
|
||||
showTooltip={{ label: ui.string.UseMaxWidth }}
|
||||
on:click={toggleLoveUseMaxWidth}
|
||||
/>
|
||||
{#if editable}
|
||||
<ModernButton
|
||||
icon={IconEdit}
|
||||
|
@ -15,10 +15,7 @@
|
||||
<script lang="ts">
|
||||
import { DocumentUpdate, Ref } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import ui, {
|
||||
Button,
|
||||
IconMaxWidth,
|
||||
IconMinWidth,
|
||||
import {
|
||||
Breadcrumb,
|
||||
ButtonIcon,
|
||||
ModernButton,
|
||||
@ -30,9 +27,9 @@
|
||||
} from '@hcengineering/ui'
|
||||
import { Floor, GRID_WIDTH, Room, getFreeSpace } from '@hcengineering/love'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { floors, lockedRoom, loveUseMaxWidth } from '../stores'
|
||||
import { floors, lockedRoom } from '../stores'
|
||||
import { FloorSize, RGBAColor, ResizeInitParams, RoomSide, shadowError, shadowNormal } from '../types'
|
||||
import { calculateFloorSize, toggleLoveUseMaxWidth } from '../utils'
|
||||
import { calculateFloorSize } from '../utils'
|
||||
import AddRoomPopup from './AddRoomPopup.svelte'
|
||||
import FloorGrid from './FloorGrid.svelte'
|
||||
import RoomConfigure from './RoomConfigure.svelte'
|
||||
@ -298,14 +295,6 @@
|
||||
<svelte:fragment slot="actions">
|
||||
<ButtonIcon icon={IconAdd} size={'small'} on:click={addRoom} />
|
||||
<div class="hulyHeader-divider short" />
|
||||
<Button
|
||||
icon={$loveUseMaxWidth ? IconMaxWidth : IconMinWidth}
|
||||
kind={'regular'}
|
||||
pressed={$loveUseMaxWidth}
|
||||
size={'medium'}
|
||||
showTooltip={{ label: ui.string.UseMaxWidth }}
|
||||
on:click={toggleLoveUseMaxWidth}
|
||||
/>
|
||||
<ModernButton
|
||||
label={lovePlg.string.FinalizeEditing}
|
||||
kind={'primary'}
|
||||
|
@ -13,10 +13,9 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { resizeObserver } from '@hcengineering/ui'
|
||||
import { createEventDispatcher, afterUpdate } from 'svelte'
|
||||
import { resizeObserver, isSafari, deviceOptionsStore as deviceInfo } from '@hcengineering/ui'
|
||||
import { GRID_WIDTH } from '@hcengineering/love'
|
||||
import { loveUseMaxWidth } from '../stores'
|
||||
|
||||
export let floorContainer: HTMLDivElement
|
||||
export let marginInline: string = 'auto'
|
||||
@ -24,41 +23,89 @@
|
||||
export let useResize: boolean = false
|
||||
export let rows: number = 5
|
||||
|
||||
const MAX_SIZE_REM = 2 // Maximum cell size in rems
|
||||
const MIN_SIZE_REM = 1.5
|
||||
const MAX_SIZE_UMW_REM = 4 // Maximum cell size in rems (Use max width)
|
||||
const MIN_SIZE_UMW_REM = 2
|
||||
const MAX_SIZE_PRE_REM = 2 // Maximum cell size in rems (preview mode)
|
||||
const MIN_SIZE_PRE_REM = 0.5
|
||||
const MIN_SIZE_REM = 1 // Minimum cell size in rems
|
||||
const MAX_SIZE_REM_UMW = 4 // Maximum cell size in rems (Use max width)
|
||||
const MIN_SIZE_REM_UMW = 2 // Minimum cell size in rems (Use max width)
|
||||
const MAX_SIZE_REM_PRE = 2 // Maximum cell size in rems (preview mode)
|
||||
const FULL_GW = GRID_WIDTH + 2
|
||||
$: minWidth = preview
|
||||
? `${FULL_GW * MIN_SIZE_PRE_REM}rem`
|
||||
: `${FULL_GW * ($loveUseMaxWidth ? MIN_SIZE_UMW_REM : MIN_SIZE_REM)}rem`
|
||||
$: minHeight = preview
|
||||
? `${rows * MIN_SIZE_PRE_REM}rem`
|
||||
: `${rows * ($loveUseMaxWidth ? MIN_SIZE_UMW_REM : MIN_SIZE_REM)}rem`
|
||||
$: maxWidth = preview
|
||||
? `${FULL_GW * MAX_SIZE_PRE_REM}rem`
|
||||
: `${FULL_GW * ($loveUseMaxWidth ? MAX_SIZE_UMW_REM : MAX_SIZE_REM)}rem`
|
||||
$: maxHeight = preview
|
||||
? `${rows * MAX_SIZE_PRE_REM}rem`
|
||||
: `${rows * ($loveUseMaxWidth ? MAX_SIZE_UMW_REM : MAX_SIZE_REM)}rem`
|
||||
|
||||
let wrapperWidth: number, wrapperHeight: number
|
||||
let oldRows: number
|
||||
let overflow: boolean[] = [false, false]
|
||||
|
||||
const mode: 'resize' | 'fit' | 'width' = useResize
|
||||
? 'resize'
|
||||
: preview || isSafari() || $deviceInfo.isMobile
|
||||
? 'width'
|
||||
: 'fit'
|
||||
const useMaxWidth = mode === 'resize' || (mode === 'width' && !preview)
|
||||
|
||||
const minW = FULL_GW * MIN_SIZE_REM * $deviceInfo.fontSize
|
||||
$: minH = rows * MIN_SIZE_REM * $deviceInfo.fontSize
|
||||
|
||||
let minWidth = useMaxWidth ? `${FULL_GW * MIN_SIZE_REM_UMW}rem` : `${FULL_GW * MIN_SIZE_REM}rem`
|
||||
$: minHeight = useMaxWidth ? `${rows * MIN_SIZE_REM_UMW}rem` : `${rows * MIN_SIZE_REM}rem`
|
||||
|
||||
let maxWidth = useMaxWidth
|
||||
? `${FULL_GW * MAX_SIZE_REM_UMW}rem`
|
||||
: preview
|
||||
? `${FULL_GW * MAX_SIZE_REM_PRE}rem`
|
||||
: '100%'
|
||||
$: maxHeight = useMaxWidth ? `${rows * MAX_SIZE_REM_UMW}rem` : preview ? `${rows * MAX_SIZE_REM_PRE}rem` : '100%'
|
||||
|
||||
$: style = `min-width: ${minWidth}; min-height: ${minHeight}; max-width: ${maxWidth}; max-height: ${maxHeight};`
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const checkGrid = (): void => {
|
||||
overflow = [wrapperWidth < minW, wrapperHeight < minH]
|
||||
const minK = Math.min(
|
||||
(overflow[0] ? minW : wrapperWidth) / FULL_GW,
|
||||
(overflow[1] ? minH : wrapperHeight - 16) / rows
|
||||
)
|
||||
minWidth = maxWidth = `${(FULL_GW * minK) / $deviceInfo.fontSize}rem`
|
||||
minHeight = maxHeight = `${(rows * minK) / $deviceInfo.fontSize}rem`
|
||||
oldRows = rows
|
||||
}
|
||||
afterUpdate(() => {
|
||||
if (rows !== oldRows && mode === 'fit') checkGrid()
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if !useResize}
|
||||
{#if mode === 'fit'}
|
||||
<div
|
||||
class="floorGrid-wrapper"
|
||||
style:overflow-x={overflow[0] ? 'scroll' : 'hidden'}
|
||||
style:overflow-y={overflow[1] ? 'scroll' : 'hidden'}
|
||||
use:resizeObserver={(element) => {
|
||||
wrapperWidth = element.clientWidth
|
||||
wrapperHeight = element.clientHeight
|
||||
checkGrid()
|
||||
}}
|
||||
>
|
||||
<div
|
||||
bind:this={floorContainer}
|
||||
class="floorGrid"
|
||||
{style}
|
||||
style:grid-template-columns={`repeat(${FULL_GW}, 1fr)`}
|
||||
style:grid-template-rows={`repeat(${rows}, 1fr)`}
|
||||
style:aspect-ratio={`${FULL_GW} / ${rows}`}
|
||||
style:margin-inline={marginInline}
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
{:else if mode === 'width'}
|
||||
{@const minSize = preview ? MIN_SIZE_REM : MIN_SIZE_REM_UMW}
|
||||
{@const maxSize = preview ? MAX_SIZE_REM_PRE : MAX_SIZE_REM_UMW}
|
||||
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
bind:this={floorContainer}
|
||||
class="floorGrid"
|
||||
class="floorGrid full"
|
||||
{style}
|
||||
style:grid-template-columns={`repeat(${FULL_GW}, ${preview ? `minmax(${MIN_SIZE_PRE_REM}rem, ${MAX_SIZE_PRE_REM}rem)` : '1fr'})`}
|
||||
style:grid-template-rows={rows
|
||||
? `repeat(${rows}, ${preview ? `minmax(${MIN_SIZE_PRE_REM}rem, ${MAX_SIZE_PRE_REM}rem)` : '1fr'})`
|
||||
: undefined}
|
||||
style:grid-template-columns={`repeat(${FULL_GW}, minmax(${minSize}rem, ${maxSize}rem))`}
|
||||
style:grid-template-rows={`repeat(${rows}, minmax(${minSize}rem, ${maxSize}rem))`}
|
||||
style:aspect-ratio={`${FULL_GW} / ${rows}`}
|
||||
style:margin-inline={marginInline}
|
||||
on:mouseover
|
||||
@ -71,10 +118,10 @@
|
||||
use:resizeObserver={(element) => {
|
||||
dispatch('resize', { width: element.clientWidth, height: element.clientHeight })
|
||||
}}
|
||||
class="floorGrid"
|
||||
class="floorGrid w-full"
|
||||
{style}
|
||||
style:grid-template-columns={`repeat(${FULL_GW}, 1fr)`}
|
||||
style:grid-template-rows={rows ? `repeat(${rows}, 1fr)` : undefined}
|
||||
style:grid-template-columns={`repeat(${FULL_GW}, minmax(${MIN_SIZE_REM_UMW}rem, ${MAX_SIZE_REM_UMW}rem))`}
|
||||
style:grid-template-rows={`repeat(${rows}, minmax(${MIN_SIZE_REM_UMW}rem, ${MAX_SIZE_REM_UMW}rem))`}
|
||||
style:aspect-ratio={`${FULL_GW} / ${rows}`}
|
||||
style:margin-inline={marginInline}
|
||||
>
|
||||
@ -88,11 +135,26 @@
|
||||
display: grid;
|
||||
grid-auto-flow: row;
|
||||
grid-auto-rows: 1fr;
|
||||
place-items: stretch;
|
||||
place-content: start center;
|
||||
flex-shrink: 0;
|
||||
gap: 0;
|
||||
width: 100%;
|
||||
container: floorGridContainer / inline-size;
|
||||
|
||||
&.full {
|
||||
width: 100%;
|
||||
width: -moz-available;
|
||||
}
|
||||
}
|
||||
.floorGrid-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.floorGrid {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -32,7 +32,7 @@
|
||||
$: rows = calculateFloorSize(rooms) - 1
|
||||
</script>
|
||||
|
||||
<Scroller padding="1rem" bottomPadding="4rem" align={'center'} horizontal>
|
||||
<Scroller padding="1rem" bottomPadding="1rem" align={'center'} horizontal>
|
||||
<FloorGrid bind:floorContainer {rows}>
|
||||
{#each rooms as room}
|
||||
<RoomPreview {room} info={getInfo(room._id, $infos)} on:open />
|
||||
|
@ -69,6 +69,7 @@
|
||||
configure={configure && floor?._id === _floor._id}
|
||||
rooms={getRooms($rooms, _floor._id)}
|
||||
selected={floor?._id === _floor._id}
|
||||
background={'var(--theme-panel-color)'}
|
||||
on:configure={() => {
|
||||
if (floor?._id === _floor._id) {
|
||||
configure = !configure
|
||||
|
@ -16,7 +16,7 @@
|
||||
import { Person, formatName } from '@hcengineering/contact'
|
||||
import { Avatar, personByIdStore } from '@hcengineering/contact-resources'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { Icon, Loading, resizeObserver } from '@hcengineering/ui'
|
||||
import { Icon, Loading } from '@hcengineering/ui'
|
||||
import love from '../plugin'
|
||||
|
||||
export let _id: string
|
||||
@ -28,7 +28,6 @@
|
||||
|
||||
let parent: HTMLDivElement
|
||||
let activeTrack: boolean = false
|
||||
let filled: boolean = false
|
||||
|
||||
export function appendChild (track: HTMLMediaElement): void {
|
||||
const video = parent.querySelector('.video')
|
||||
@ -53,36 +52,20 @@
|
||||
activeTrack = !value
|
||||
}
|
||||
|
||||
let labelWidth: number
|
||||
let parentWidth: number
|
||||
$: filled = labelWidth === parentWidth && labelWidth > 0
|
||||
$: user = $personByIdStore.get(_id as Ref<Person>)
|
||||
</script>
|
||||
|
||||
<div
|
||||
id={_id}
|
||||
class="parent"
|
||||
class:small
|
||||
use:resizeObserver={(element) => {
|
||||
parentWidth = element.clientWidth
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class="label"
|
||||
class:filled
|
||||
use:resizeObserver={(element) => {
|
||||
labelWidth = element.clientWidth
|
||||
}}
|
||||
>
|
||||
<div id={_id} class="parent" class:small>
|
||||
<div class="label">
|
||||
<span class="overflow-label">{formatName(name)}</span>
|
||||
{#if connecting}
|
||||
<div class="loading">
|
||||
<Loading size={'small'} />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="icon" class:muted>
|
||||
<Icon size="small" icon={love.icon.MicDisabled} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="icon" class:muted>
|
||||
<Icon size="medium" icon={love.icon.MicDisabled} />
|
||||
</div>
|
||||
<div bind:this={parent} class="cover" class:active={activeTrack} class:mirror={mirror && activeTrack}>
|
||||
<div class="ava">
|
||||
@ -139,40 +122,40 @@
|
||||
|
||||
.label {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
max-width: 100%;
|
||||
padding: 0.5rem 0.5rem 0.25rem 1rem;
|
||||
color: white;
|
||||
font-weight: 500;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
border-radius: 0.75rem 0 0.25rem 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
z-index: 1;
|
||||
gap: 0.375rem;
|
||||
top: 0.5rem;
|
||||
left: 0.5rem;
|
||||
max-width: calc(100% - 1rem);
|
||||
padding: 0.375rem;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
|
||||
span {
|
||||
text-shadow: 0 0 0.25rem black;
|
||||
}
|
||||
&.filled {
|
||||
padding: 0.5rem 1rem 0.25rem 1rem;
|
||||
border-radius: 0.75rem 0.75rem 0 0;
|
||||
}
|
||||
font-weight: 500;
|
||||
font-size: 0.75rem;
|
||||
color: rgba(35, 37, 45, 0.75);
|
||||
background-color: rgba(255, 255, 255, 0.4);
|
||||
border-radius: 0.375rem;
|
||||
z-index: 1;
|
||||
}
|
||||
&.small .label {
|
||||
font-size: 0.75rem;
|
||||
padding: 0.25rem 0.25rem 0.25rem 0.5rem;
|
||||
|
||||
&.filled {
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
font-size: 0.625rem;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
right: 0.5rem;
|
||||
bottom: 0.5rem;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
background-color: #36373d;
|
||||
border-radius: 0.5rem;
|
||||
z-index: 1;
|
||||
|
||||
&:not(.muted) {
|
||||
display: none;
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ import { aiBotEmailSocialId } from '@hcengineering/ai-bot'
|
||||
|
||||
import love from './plugin'
|
||||
import { personRefByPersonIdStore } from '@hcengineering/contact-resources'
|
||||
import { getLoveUseMaxWidth } from './utils'
|
||||
|
||||
export const rooms = writable<Room[]>([])
|
||||
export const myOffice = derived(rooms, (val) => {
|
||||
@ -142,5 +141,3 @@ onClient(() => {
|
||||
)
|
||||
})
|
||||
export const lockedRoom = writable<string>('')
|
||||
|
||||
export const loveUseMaxWidth = writable<boolean>(getLoveUseMaxWidth())
|
||||
|
@ -86,14 +86,7 @@ import { get, writable } from 'svelte/store'
|
||||
import { sendMessage } from './broadcast'
|
||||
import RoomSettingsPopup from './components/RoomSettingsPopup.svelte'
|
||||
import love from './plugin'
|
||||
import {
|
||||
$myPreferences,
|
||||
currentMeetingMinutes,
|
||||
currentRoom,
|
||||
myOffice,
|
||||
selectedRoomPlace,
|
||||
loveUseMaxWidth
|
||||
} from './stores'
|
||||
import { $myPreferences, currentMeetingMinutes, currentRoom, myOffice, selectedRoomPlace } from './stores'
|
||||
|
||||
export const selectedCamId = 'selectedDevice_cam'
|
||||
export const selectedMicId = 'selectedDevice_mic'
|
||||
@ -1168,18 +1161,3 @@ export async function getMeetingMinutesTitle (
|
||||
|
||||
return meeting?.title ?? ''
|
||||
}
|
||||
|
||||
// Floor Scaling (Max Width)
|
||||
const useMaxWidthKey = 'loveUseMaxWidth'
|
||||
export const saveLoveUseMaxWidth = (useMaxWidth: boolean): void => {
|
||||
if (useMaxWidth) localStorage.setItem(useMaxWidthKey, 'true')
|
||||
else localStorage.removeItem(useMaxWidthKey)
|
||||
}
|
||||
export const getLoveUseMaxWidth = (): boolean => {
|
||||
return localStorage.getItem(useMaxWidthKey) === 'true'
|
||||
}
|
||||
export const toggleLoveUseMaxWidth = (): void => {
|
||||
const value = !get(loveUseMaxWidth)
|
||||
loveUseMaxWidth.set(value)
|
||||
saveLoveUseMaxWidth(value)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user