LOVE: scaling the floor (#8145)

Signed-off-by: Alexander Platov <alexander.platov@hardcoreeng.com>
This commit is contained in:
Alexander Platov 2025-03-06 11:37:17 +03:00 committed by GitHub
parent d33f7ba28f
commit 618034e6fa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 155 additions and 34 deletions

View File

@ -39,6 +39,7 @@
& > span {
color: var(--theme-caption-color);
transition: font-size .15s ease-in-out;
cursor: default;
}
}
@ -46,12 +47,18 @@
overflow: hidden;
width: 100%;
height: 100%;
max-width: 90%;
max-height: 90%;
transition-property: max-width, max-height;
transition-duration: .15s;
transition-timing-function: ease-in-out;
min-width: .5rem;
min-height: .5rem;
max-width: 100%;
max-height: 100%;
aspect-ratio: 1;
transition: transform .15s ease-in-out;
transform: scale(.9);
.preview & {
min-width: .5rem;
min-height: .5rem;
}
&.hovered:hover {
background-color: transparent;
@ -64,8 +71,8 @@
&.hovered { background-color: var(--theme-popup-hover); }
}
&:not(.preview)::before { top: calc(100% / var(--huly-floor-roomHeight) / 3 * -1.6); }
&.preview::before {
:not(.preview) &::before { top: calc(100% / var(--huly-floor-roomHeight) / 3 * -1.6); }
.preview &::before {
top: calc(100% / var(--huly-floor-roomHeight) / 3 * -1);
bottom: calc(100% / var(--huly-floor-roomHeight) / 3 * -1);
left: calc(100% / var(--huly-floor-roomWidth) / 3 * -1);
@ -80,8 +87,7 @@
cursor: pointer;
&:hover {
max-width: 100%;
max-height: 100%;
transform: scale(1);
}
}
}
@ -95,8 +101,12 @@
.floorGrid-configureRoom {
&__header {
top: calc(100% / var(--huly-floor-roomHeight) / 3 * -1.6 + .75rem - 1px);
// transition: top .15s ease-in-out;
& > .antiEditBox input { font-size: .8125rem; }
& > .antiEditBox input {
font-size: .8125rem;
transition: font-size .15s ease-in-out;
}
}
&__field {
width: 90%;
@ -139,6 +149,33 @@
}
}
// Room conners and header
@container floorGridContainer (max-width: 800px) {
.floorGrid-room::before,
.floorGrid-configureRoom::before { border-radius: 0.75rem; }
.floorGrid-room .floorGrid-room__header {
top: calc(100% / var(--huly-floor-roomHeight) / 3 * -1.6 + .125rem + 1px);
span { font-size: .75rem; }
svg { width: 0.75rem; }
}
.floorGrid-configureRoom .floorGrid-configureRoom__header {
top: calc(100% / var(--huly-floor-roomHeight) / 3 * -1.6 + .25rem);
& > .antiEditBox input { font-size: .75rem; }
}
}
@container floorGridContainer (max-width: 600px) {
.floorGrid-room::before,
.floorGrid-configureRoom::before { border-radius: 0.5rem; }
.floorGrid-room .floorGrid-room__header {
top: calc(100% / var(--huly-floor-roomHeight) / 3 * -1.6 + .125rem);
span { font-size: .7rem; }
}
.floorGrid-configureRoom .floorGrid-configureRoom__header {
top: calc(100% / var(--huly-floor-roomHeight) / 3 * -1.6 + .125rem);
& > .antiEditBox input { font-size: .7rem; }
}
}
// Room
@media only screen and (max-width: 1024px) {
.room-container + .bar > .bar__left-panel button span { display: none; }

View File

@ -41,6 +41,7 @@
export let categoryHeader: boolean = false
export let hiddenHeader: boolean = false
export let background: string | undefined = undefined
export let contentAlign: 'start' | 'center' | 'end' | 'stretch' = 'start'
const dispatch = createEventDispatcher()
@ -125,7 +126,7 @@
<slot name="actions" />
</div>
</button>
<div class="hulyAccordionItem-content">
<div class="hulyAccordionItem-content" style:align-items={contentAlign}>
<slot />
</div>
</div>

View File

@ -14,15 +14,25 @@
-->
<script lang="ts">
import { AccountRole, Ref, getCurrentAccount, hasAccountRole, WithLookup } from '@hcengineering/core'
import { Breadcrumb, Header, IconEdit, ModernButton, Component } from '@hcengineering/ui'
import ui, {
Breadcrumb,
Header,
IconEdit,
ModernButton,
Component,
IconMaxWidth,
IconMinWidth,
Button
} 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 } from '../stores'
import { currentRoom, floors, loveUseMaxWidth } from '../stores'
import ControlBar from './ControlBar.svelte'
import { toggleLoveUseMaxWidth } from '../utils'
export let rooms: Room[] = []
export let floor: Ref<Floor>
@ -48,6 +58,14 @@
<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}

View File

@ -15,7 +15,10 @@
<script lang="ts">
import { DocumentUpdate, Ref } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation'
import {
import ui, {
Button,
IconMaxWidth,
IconMinWidth,
Breadcrumb,
ButtonIcon,
ModernButton,
@ -27,9 +30,9 @@
} from '@hcengineering/ui'
import { Floor, GRID_WIDTH, Room, getFreeSpace } from '@hcengineering/love'
import { createEventDispatcher } from 'svelte'
import { floors, lockedRoom } from '../stores'
import { floors, lockedRoom, loveUseMaxWidth } from '../stores'
import { FloorSize, RGBAColor, ResizeInitParams, RoomSide, shadowError, shadowNormal } from '../types'
import { calculateFloorSize } from '../utils'
import { calculateFloorSize, toggleLoveUseMaxWidth } from '../utils'
import AddRoomPopup from './AddRoomPopup.svelte'
import FloorGrid from './FloorGrid.svelte'
import RoomConfigure from './RoomConfigure.svelte'
@ -295,6 +298,14 @@
<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'}
@ -308,6 +319,7 @@
<FloorGrid
bind:floorContainer
{rows}
useResize
on:resize={(event) => {
if (event.detail === undefined) return
const { width, height } = event.detail

View File

@ -16,24 +16,50 @@
import { createEventDispatcher } from 'svelte'
import { resizeObserver } from '@hcengineering/ui'
import { GRID_WIDTH } from '@hcengineering/love'
import { loveUseMaxWidth } from '../stores'
export let floorContainer: HTMLDivElement
export let marginInline: string = 'auto'
export let preview: boolean = false
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 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`
$: style = `min-width: ${minWidth}; min-height: ${minHeight}; max-width: ${maxWidth}; max-height: ${maxHeight};`
const dispatch = createEventDispatcher()
</script>
{#if preview}
{#if !useResize}
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
bind:this={floorContainer}
class="floorGrid"
style:grid-template-columns={`repeat(${GRID_WIDTH + 2}, 1fr)`}
style:grid-template-rows={rows ? `repeat(${rows}, 1fr)` : undefined}
style:max-width={`${(GRID_WIDTH + 2) * 4}rem`}
{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:aspect-ratio={`${FULL_GW} / ${rows}`}
style:margin-inline={marginInline}
on:mouseover
>
@ -46,9 +72,10 @@
dispatch('resize', { width: element.clientWidth, height: element.clientHeight })
}}
class="floorGrid"
style:grid-template-columns={`repeat(${GRID_WIDTH + 2}, 1fr)`}
{style}
style:grid-template-columns={`repeat(${FULL_GW}, 1fr)`}
style:grid-template-rows={rows ? `repeat(${rows}, 1fr)` : undefined}
style:max-width={`${(GRID_WIDTH + 2) * 4}rem`}
style:aspect-ratio={`${FULL_GW} / ${rows}`}
style:margin-inline={marginInline}
>
<slot />
@ -59,12 +86,13 @@
.floorGrid {
position: relative;
display: grid;
grid-template-columns: repeat(17, 1fr);
grid-auto-flow: row;
grid-auto-rows: 1fr;
place-items: stretch;
place-content: center;
place-content: start center;
flex-shrink: 0;
gap: 0;
width: 100%;
container: floorGridContainer / inline-size;
}
</style>

View File

@ -107,6 +107,7 @@
{disabled}
{kind}
categoryHeader
contentAlign={'center'}
on:select
>
<svelte:fragment slot="actions">

View File

@ -32,8 +32,8 @@
$: rows = calculateFloorSize(rooms) - 1
</script>
<Scroller padding="1rem" bottomPadding="4rem" horizontal>
<FloorGrid bind:floorContainer {rows} preview>
<Scroller padding="1rem" bottomPadding="4rem" align={'center'} horizontal>
<FloorGrid bind:floorContainer {rows}>
{#each rooms as room}
<RoomPreview {room} info={getInfo(room._id, $infos)} on:open />
{/each}

View File

@ -139,14 +139,14 @@
.label {
position: absolute;
bottom: 0;
top: 0;
left: 0;
max-width: 100%;
padding: 0.25rem 0.5rem 0.5rem 1rem;
padding: 0.5rem 0.5rem 0.25rem 1rem;
color: white;
font-weight: 500;
background-color: rgba(0, 0, 0, 0.3);
border-radius: 0 0.25rem 0 0.75rem;
border-radius: 0.75rem 0 0.25rem 0;
display: flex;
align-items: center;
gap: 0.25rem;
@ -158,8 +158,8 @@
text-shadow: 0 0 0.25rem black;
}
&.filled {
padding: 0.25rem 1rem 0.5rem 1rem;
border-radius: 0 0 0.75rem 0.75rem;
padding: 0.5rem 1rem 0.25rem 1rem;
border-radius: 0.75rem 0.75rem 0 0;
}
}
&.small .label {

View File

@ -144,7 +144,6 @@
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="floorGrid-room"
class:preview
class:hovered
class:disabled
class:myOffice={$myInfo?.room === room._id}

View File

@ -17,6 +17,7 @@ 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) => {
@ -141,3 +142,5 @@ onClient(() => {
)
})
export const lockedRoom = writable<string>('')
export const loveUseMaxWidth = writable<boolean>(getLoveUseMaxWidth())

View File

@ -86,7 +86,14 @@ 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 } from './stores'
import {
$myPreferences,
currentMeetingMinutes,
currentRoom,
myOffice,
selectedRoomPlace,
loveUseMaxWidth
} from './stores'
export const selectedCamId = 'selectedDevice_cam'
export const selectedMicId = 'selectedDevice_mic'
@ -1158,3 +1165,18 @@ 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)
}