Fixed work slots layout in Planner (#5262)

Signed-off-by: Alexander Platov <alexander.platov@hardcoreeng.com>
This commit is contained in:
Alexander Platov 2024-04-10 14:36:56 +03:00 committed by GitHub
parent babbc490f2
commit 50d5c3bad9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 111 additions and 75 deletions

View File

@ -678,6 +678,7 @@ input.search {
.h-50 { height: 12.5rem; } .h-50 { height: 12.5rem; }
.h-60 { height: 15.0rem; } .h-60 { height: 15.0rem; }
.w-min { width: min-content; } .w-min { width: min-content; }
.w-max { width: max-content; }
.w-full { width: 100%; } .w-full { width: 100%; }
.w-2 { width: .5rem; } .w-2 { width: .5rem; }
.w-4 { width: 1rem; } .w-4 { width: 1rem; }
@ -708,6 +709,7 @@ input.search {
.min-w-144 { min-width: 25rem; } .min-w-144 { min-width: 25rem; }
.min-w-168 { min-width: 42rem; } .min-w-168 { min-width: 42rem; }
.min-w-min { min-width: min-content; } .min-w-min { min-width: min-content; }
.min-w-full { min-width: 100%; }
.min-h-0 { min-height: 0; } .min-h-0 { min-height: 0; }
.min-h-2 { min-height: .5rem; } .min-h-2 { min-height: .5rem; }
.min-h-4 { min-height: 1rem; } .min-h-4 { min-height: 1rem; }
@ -1001,6 +1003,7 @@ a.no-line {
.content-primary-color { color: var(--primary-button-color); } .content-primary-color { color: var(--primary-button-color); }
.red-color { color: var(--highlight-red); } .red-color { color: var(--highlight-red); }
.error-color { color: var(--theme-error-color); } .error-color { color: var(--theme-error-color); }
.sunshine-text-color { color: var(--tag-accent-SunshineText) !important; }
.border-radius-4 { border-radius: 1rem; } .border-radius-4 { border-radius: 1rem; }
.border-radius-3 { border-radius: 0.75rem; } .border-radius-3 { border-radius: 0.75rem; }

View File

@ -37,6 +37,7 @@
export let selected: DropdownIntlItem['id'] | undefined = undefined export let selected: DropdownIntlItem['id'] | undefined = undefined
export let element: HTMLButtonElement | undefined = undefined export let element: HTMLButtonElement | undefined = undefined
export let focusIndex = -1 export let focusIndex = -1
export let id: string | undefined = undefined
let opened: boolean = false let opened: boolean = false
@ -83,5 +84,6 @@
{inheritColor} {inheritColor}
pressed={opened} pressed={opened}
{focusIndex} {focusIndex}
{id}
on:click={openPopup} on:click={openPopup}
/> />

View File

@ -25,6 +25,7 @@
getUserTimezone, getUserTimezone,
showPopup showPopup
} from '@hcengineering/ui' } from '@hcengineering/ui'
import { FixedColumn } from '@hcengineering/view-resources'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
import DateLocalePresenter from './DateLocalePresenter.svelte' import DateLocalePresenter from './DateLocalePresenter.svelte'
@ -38,6 +39,7 @@
export let disabled: boolean = false export let disabled: boolean = false
export let focusIndex = -1 export let focusIndex = -1
export let timeZone: string = getUserTimezone() export let timeZone: string = getUserTimezone()
export let fixed: string | undefined = undefined
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
@ -80,11 +82,19 @@
class:flex-gap-2={direction === 'horizontal'} class:flex-gap-2={direction === 'horizontal'}
> >
{#if showDate || withoutTime} {#if showDate || withoutTime}
{#if fixed === undefined}
<div class="min-w-28"> <div class="min-w-28">
<ButtonBase type="type-button" {kind} {size} {disabled} {focusIndex} on:click={dateClick}> <ButtonBase type="type-button" {kind} {size} {disabled} {focusIndex} on:click={dateClick}>
<DateLocalePresenter date={currentDate.getTime()} {timeZone} /> <span class="overflow-label"><DateLocalePresenter date={currentDate.getTime()} {timeZone} /></span>
</ButtonBase> </ButtonBase>
</div> </div>
{:else}
<FixedColumn key={fixed + '-date'} addClass={'min-w-28'}>
<ButtonBase type="type-button" {kind} {size} {disabled} {focusIndex} on:click={dateClick}>
<span class="overflow-label"><DateLocalePresenter date={currentDate.getTime()} {timeZone} /></span>
</ButtonBase>
</FixedColumn>
{/if}
{/if} {/if}
{#if showDate && !withoutTime && direction === 'horizontal'} {#if showDate && !withoutTime && direction === 'horizontal'}
@ -92,6 +102,7 @@
{/if} {/if}
{#if !withoutTime} {#if !withoutTime}
{#if fixed === undefined}
<ButtonBase <ButtonBase
type="type-button" type="type-button"
{kind} {kind}
@ -110,14 +121,42 @@
}} }}
/> />
</ButtonBase> </ButtonBase>
{:else}
<FixedColumn key={fixed + '-time'} addClass={'min-w-28'}>
<ButtonBase
type="type-button"
{kind}
{size}
{disabled}
focusIndex={focusIndex !== -1 ? focusIndex + 1 : focusIndex}
on:click={timeClick}
>
<TimeInputBox
bind:currentDate
{timeZone}
noBorder
size={'small'}
on:update={(date) => {
updateTime(date.detail)
}}
/>
</ButtonBase>
</FixedColumn>
{/if}
{/if} {/if}
</div> </div>
{#if !withoutTime && difference > 0} {#if !withoutTime && difference > 0}
<div class="divider" /> <div class="divider" />
<div class="duration font-regular-14"> {#if fixed === undefined}
<div class="p-2 font-regular-14 sunshine-text-color">
<TimeShiftPresenter value={date - difference} exact /> <TimeShiftPresenter value={date - difference} exact />
</div> </div>
{:else}
<FixedColumn key={fixed + '-duration'} addClass={'p-2 font-regular-14 sunshine-text-color'}>
<TimeShiftPresenter value={date - difference} exact />
</FixedColumn>
{/if}
{/if} {/if}
<style lang="scss"> <style lang="scss">
@ -140,11 +179,6 @@
} }
} }
.duration {
padding: var(--spacing-1);
color: var(--tag-accent-SunshineText);
}
.divider { .divider {
width: 0; width: 0;
height: 1.25rem; height: 1.25rem;

View File

@ -25,14 +25,11 @@
weekday: 'short', weekday: 'short',
month: 'short' month: 'short'
} }
if (current.getFullYear() !== new Date(date).getFullYear()) { $: options = {
options = {
...options, ...options,
year: '2-digit' timeZone,
year: current.getFullYear() !== new Date(date).getFullYear() ? '2-digit' : undefined
} }
}
$: options.timeZone = timeZone
</script> </script>
{new Date(date).toLocaleDateString('default', options)} {new Date(date).toLocaleDateString('default', options)}

View File

@ -24,6 +24,8 @@
export let disabled: boolean = false export let disabled: boolean = false
export let timeZone: string = getUserTimezone() export let timeZone: string = getUserTimezone()
export let focusIndex = -1 export let focusIndex = -1
export let grow: boolean = false
export let fixed: string | undefined = undefined
$: sameDate = areDatesEqual(utcToZonedTime(startDate, timeZone), utcToZonedTime(dueDate, timeZone)) $: sameDate = areDatesEqual(utcToZonedTime(startDate, timeZone), utcToZonedTime(dueDate, timeZone))
@ -49,7 +51,7 @@
} }
</script> </script>
<div class="flex-row-center flex-gap-2"> <div class="flex-row-center flex-gap-2 flex-no-shrink" class:flex-grow={grow}>
<DateEditor <DateEditor
bind:date={startDate} bind:date={startDate}
direction={sameDate ? 'horizontal' : 'vertical'} direction={sameDate ? 'horizontal' : 'vertical'}
@ -58,6 +60,7 @@
on:update={dateChange} on:update={dateChange}
{disabled} {disabled}
{focusIndex} {focusIndex}
fixed={fixed === undefined ? undefined : fixed + '-startDate'}
/> />
<div class="flex-no-shrink content-darker-color"></div> <div class="flex-no-shrink content-darker-color"></div>
<DateEditor <DateEditor
@ -69,6 +72,7 @@
{disabled} {disabled}
{timeZone} {timeZone}
focusIndex={focusIndex !== -1 ? focusIndex + 1 : focusIndex} focusIndex={focusIndex !== -1 ? focusIndex + 1 : focusIndex}
fixed={fixed === undefined ? undefined : fixed + '-dueDate'}
on:update={dueChange} on:update={dueChange}
/> />
</div> </div>

View File

@ -64,6 +64,7 @@
{size} {size}
{disabled} {disabled}
{focusIndex} {focusIndex}
id={'visibleButton'}
on:selected={(event) => { on:selected={(event) => {
if (event.detail) change(event.detail) if (event.detail) change(event.detail)
}} }}

View File

@ -249,6 +249,7 @@
<Workslots <Workslots
bind:slots bind:slots
shortcuts={false} shortcuts={false}
fixed={'createToDo'}
on:remove={removeSlot} on:remove={removeSlot}
on:create={createSlot} on:create={createSlot}
on:change={changeSlot} on:change={changeSlot}

View File

@ -236,6 +236,7 @@
.slots-content { .slots-content {
gap: var(--spacing-2); gap: var(--spacing-2);
padding: var(--spacing-2) var(--spacing-4); padding: var(--spacing-2) var(--spacing-4);
min-width: 0;
border-top: 1px solid var(--theme-divider-color); border-top: 1px solid var(--theme-divider-color);
} }
.eventPopup-container { .eventPopup-container {

View File

@ -92,4 +92,4 @@
} }
</script> </script>
<Workslots {slots} on:change={change} on:dueChange={dueChange} on:create={create} on:remove={remove} /> <Workslots {slots} fixed={'toDo'} on:change={change} on:dueChange={dueChange} on:create={create} on:remove={remove} />

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
--> -->
<script lang="ts"> <script lang="ts">
import { ButtonBase, ButtonIcon, IconDelete, themeStore, Hotkey, HotkeyGroup } from '@hcengineering/ui' import { ButtonBase, ButtonIcon, IconDelete, themeStore, Hotkey, HotkeyGroup, Scroller } from '@hcengineering/ui'
import { EventTimeEditor } from '@hcengineering/calendar-resources' import { EventTimeEditor } from '@hcengineering/calendar-resources'
import { WorkSlot } from '@hcengineering/time' import { WorkSlot } from '@hcengineering/time'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
@ -23,6 +23,7 @@
export let slots: WorkSlot[] = [] export let slots: WorkSlot[] = []
export let shortcuts: boolean = true export let shortcuts: boolean = true
export let fixed: string | undefined = undefined
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
@ -51,18 +52,20 @@
<svelte:window on:keydown={handleKeyDown} /> <svelte:window on:keydown={handleKeyDown} />
<div class="flex-col w-full flex-gap-1"> <Scroller gap={'flex-gap-1'} horizontal>
{#each slots as slot, i} {#each slots as slot, i}
<div class="flex justify-start items-center flex-gap-2 w-full pr-4 slot"> <div class="flex-between flex-no-shrink flex-gap-2 min-w-full w-max slot">
<Hotkey key={(i + 1).toString()} /> <Hotkey key={(i + 1).toString()} />
<EventTimeEditor <EventTimeEditor
allDay={false} allDay={false}
startDate={slot.date} startDate={slot.date}
dueDate={slot.dueDate} dueDate={slot.dueDate}
grow
{fixed}
on:change={(e) => change(e, slot)} on:change={(e) => change(e, slot)}
on:dueChange={(e) => dueChange(e, slot)} on:dueChange={(e) => dueChange(e, slot)}
/> />
<div class="tool"> <div class="tool flex-no-shrink">
<ButtonIcon <ButtonIcon
kind="tertiary" kind="tertiary"
size="small" size="small"
@ -74,7 +77,7 @@
</div> </div>
</div> </div>
{/each} {/each}
</div> </Scroller>
<div class="flex-row-center flex-gap-4"> <div class="flex-row-center flex-gap-4">
<ButtonBase <ButtonBase
kind="secondary" kind="secondary"
@ -98,25 +101,11 @@
<style lang="scss"> <style lang="scss">
.slot { .slot {
position: relative; padding: var(--spacing-1) var(--spacing-1) var(--spacing-1) var(--spacing-2);
padding: var(--spacing-1) var(--spacing-1) var(--spacing-1) var(--spacing-2_5);
border-radius: var(--small-BorderRadius);
background-color: var(--tag-nuance-SunshineBackground); background-color: var(--tag-nuance-SunshineBackground);
border-left: var(--extra-small-BorderRadius) solid var(--tag-accent-SunshineBackground);
&:before { border-radius: var(--extra-small-BorderRadius) var(--small-BorderRadius) var(--small-BorderRadius)
content: ''; var(--extra-small-BorderRadius);
position: absolute;
top: 0;
left: 0;
width: 0.25rem;
height: 100%;
background-color: var(--tag-accent-SunshineBackground);
border-radius: var(--small-BorderRadius) 0 0 var(--small-BorderRadius);
}
.tool {
margin-left: auto;
}
} }
.duration { .duration {

View File

@ -48,10 +48,8 @@ export class PlanningPage extends CalendarPage {
) )
this.buttonPopupCreatePriority = page.locator('div.popup button#priorityButton') this.buttonPopupCreatePriority = page.locator('div.popup button#priorityButton')
this.buttonPanelCreatePriority = page.locator('div.hulyModal-container button#priorityButton') this.buttonPanelCreatePriority = page.locator('div.hulyModal-container button#priorityButton')
this.buttonPopupCreateVisible = page.locator('div.popup button.type-button.menu', { hasText: 'visible' }) this.buttonPopupCreateVisible = page.locator('div.popup button#visibleButton')
this.buttonPanelCreateVisible = page.locator('div.hulyModal-container button.type-button.menu', { this.buttonPanelCreateVisible = page.locator('div.hulyModal-container button#visibleButton')
hasText: 'visible'
})
this.buttonPopupCreateAddLabel = page.locator('div.popup button.antiButton', { hasText: 'Add label' }) this.buttonPopupCreateAddLabel = page.locator('div.popup button.antiButton', { hasText: 'Add label' })
this.buttonPanelCreateAddLabel = page.locator('.hulyHeader-titleGroup > button:nth-child(2)') this.buttonPanelCreateAddLabel = page.locator('.hulyHeader-titleGroup > button:nth-child(2)')
this.buttonPopupCreateAddSlot = page.locator('div.popup button', { hasText: 'Add Slot' }) this.buttonPopupCreateAddSlot = page.locator('div.popup button', { hasText: 'Add Slot' })
@ -137,12 +135,12 @@ export class PlanningPage extends CalendarPage {
public async setTimeSlot (rowNumber: number, slot: Slot, popup: boolean = false): Promise<void> { public async setTimeSlot (rowNumber: number, slot: Slot, popup: boolean = false): Promise<void> {
const p = popup const p = popup
? 'div.popup div.horizontalBox div.end div.flex-col div.flex' ? 'div.popup div.horizontalBox div.end div.scroller-container div.box div.flex-between.min-w-full'
: 'div.hulyModal-container div.slots-content div.flex-col div.flex' : 'div.hulyModal-container div.slots-content div.scroller-container div.box div.flex-between.min-w-full'
const row = this.page.locator(p).nth(rowNumber) const row = this.page.locator(p).nth(rowNumber)
// dateStart // dateStart
await row.locator('div.dateEditor-container:nth-child(1) button:first-child').click() await row.locator('div.dateEditor-container:first-child > div.min-w-28:first-child button').click()
if (slot.dateStart === 'today') { if (slot.dateStart === 'today') {
await this.buttonCalendarToday.click() await this.buttonCalendarToday.click()
} else { } else {
@ -177,8 +175,8 @@ export class PlanningPage extends CalendarPage {
private async checkTimeSlot (rowNumber: number, slot: Slot, popup: boolean = false): Promise<void> { private async checkTimeSlot (rowNumber: number, slot: Slot, popup: boolean = false): Promise<void> {
const p = popup const p = popup
? 'div.popup div.horizontalBox div.end div.flex-col div.flex' ? 'div.popup div.horizontalBox div.end div.scroller-container div.box div.flex-between.min-w-full'
: 'div.hulyModal-container div.slots-content div.flex-col div.flex' : 'div.hulyModal-container div.slots-content div.scroller-container div.box div.flex-between.min-w-full'
const row = this.page.locator(p).nth(rowNumber) const row = this.page.locator(p).nth(rowNumber)
// timeStart // timeStart
await expect(row.locator('div.dateEditor-container:nth-child(1) button:last-child div.datetime-input')).toHaveText( await expect(row.locator('div.dateEditor-container:nth-child(1) button:last-child div.datetime-input')).toHaveText(
@ -260,7 +258,9 @@ export class PlanningPage extends CalendarPage {
public async deleteTimeSlot (rowNumber: number): Promise<void> { public async deleteTimeSlot (rowNumber: number): Promise<void> {
const row = this.page const row = this.page
.locator('div.hulyModal-container div.slots-content div.flex-col div.flex div.tool') .locator(
'div.hulyModal-container div.slots-content div.scroller-container div.box div.flex-between.min-w-full div.tool'
)
.nth(rowNumber) .nth(rowNumber)
await row.locator('xpath=..').hover() await row.locator('xpath=..').hover()
await row.locator('button').click() await row.locator('button').click()
@ -268,8 +268,12 @@ export class PlanningPage extends CalendarPage {
} }
public async checkTimeSlotEndDate (rowNumber: number, dateEnd: string): Promise<void> { public async checkTimeSlotEndDate (rowNumber: number, dateEnd: string): Promise<void> {
const row = this.page.locator('div.hulyModal-container div.slots-content div.flex-col div.flex').nth(rowNumber) const row = this.page
.locator('div.hulyModal-container div.slots-content div.scroller-container div.box div.flex-between.min-w-full')
.nth(rowNumber)
// dateEnd // dateEnd
await expect(row.locator('div.dateEditor-container:nth-child(1) button:first-child')).toContainText(dateEnd) await expect(row.locator('div.dateEditor-container:first-child > div.min-w-28:first-child button')).toContainText(
dateEnd
)
} }
} }