mirror of
https://github.com/hcengineering/platform.git
synced 2025-01-05 10:29:51 +03:00
UBER-667: UI fixes, displaying All day, time editor. (#3619)
Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
parent
1988dfca33
commit
998689401a
@ -145,8 +145,6 @@ a.noUnderline {
|
||||
}
|
||||
}
|
||||
|
||||
.text-center { text-align: center; }
|
||||
|
||||
.firstLetter span{
|
||||
display: inline-block;
|
||||
&::first-letter { text-transform: uppercase; }
|
||||
@ -804,6 +802,8 @@ a.no-line {
|
||||
.uppercase { text-transform: uppercase; }
|
||||
.lower { text-transform: lowercase; }
|
||||
.text-left { text-align: left; }
|
||||
.text-center { text-align: center; }
|
||||
.leading-3 { line-height: .75rem; }
|
||||
|
||||
.over-underline {
|
||||
cursor: pointer;
|
||||
|
@ -2,7 +2,6 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
padding: 0 .75rem;
|
||||
min-width: 1.375rem;
|
||||
white-space: nowrap;
|
||||
font-size: .8125rem;
|
||||
|
@ -41,6 +41,7 @@
|
||||
export let iconRight: Asset | AnySvelteComponent | ComponentType | undefined = undefined
|
||||
export let iconRightProps: IconProps = {}
|
||||
export let justify: 'left' | 'center' = 'center'
|
||||
export let padding: string = '0 .75rem'
|
||||
export let disabled: boolean = false
|
||||
export let loading: boolean = false
|
||||
export let width: string | undefined = undefined
|
||||
@ -128,6 +129,7 @@
|
||||
style:width
|
||||
style:height
|
||||
style:flex-shrink={shrink}
|
||||
style:padding
|
||||
{title}
|
||||
type={kind === 'accented' ? 'submit' : 'button'}
|
||||
on:click|stopPropagation|preventDefault
|
||||
|
@ -18,6 +18,9 @@
|
||||
import Label from '../Label.svelte'
|
||||
|
||||
export let currentDate: Date
|
||||
export let size: 'small' | 'medium' = 'medium'
|
||||
export let noBorder: boolean = false
|
||||
export let disabled: boolean = false
|
||||
|
||||
type TEdits = 'hour' | 'min'
|
||||
interface IEdits {
|
||||
@ -92,7 +95,7 @@
|
||||
}
|
||||
|
||||
const keyDown = (ev: KeyboardEvent, ed: TEdits): void => {
|
||||
if (selected === ed) {
|
||||
if (selected === ed && !disabled) {
|
||||
const index = getIndex(ed)
|
||||
if (ev.key >= '0' && ev.key <= '9') {
|
||||
const shouldNext = !startTyping
|
||||
@ -114,6 +117,7 @@
|
||||
dateToEdits(currentDate)
|
||||
}
|
||||
edits = edits
|
||||
dispatch('update', currentDate)
|
||||
|
||||
if (selected === 'hour' && (shouldNext || edits[0].value > 2)) selected = 'min'
|
||||
}
|
||||
@ -130,6 +134,7 @@
|
||||
if (currentDate) {
|
||||
currentDate = setValue(val, currentDate, ed)
|
||||
dateToEdits(currentDate)
|
||||
dispatch('update', currentDate)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -163,59 +168,71 @@
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="datetime-input">
|
||||
<div class="flex-row-center">
|
||||
<span
|
||||
bind:this={edits[0].el}
|
||||
class="digit"
|
||||
tabindex="0"
|
||||
on:keydown={(ev) => keyDown(ev, edits[0].id)}
|
||||
on:focus={() => focused(edits[0].id)}
|
||||
on:blur={() => (selected = null)}
|
||||
>
|
||||
{#if edits[0].value > -1}
|
||||
{edits[0].value.toString().padStart(2, '0')}
|
||||
{:else}<Label label={ui.string.HH} />{/if}
|
||||
</span>
|
||||
<span class="separator">:</span>
|
||||
<span
|
||||
bind:this={edits[1].el}
|
||||
class="digit"
|
||||
tabindex="0"
|
||||
on:keydown={(ev) => keyDown(ev, edits[1].id)}
|
||||
on:focus={() => focused(edits[1].id)}
|
||||
on:blur={() => (selected = null)}
|
||||
>
|
||||
{#if edits[1].value > -1}
|
||||
{edits[1].value.toString().padStart(2, '0')}
|
||||
{:else}<Label label={ui.string.MM} />{/if}
|
||||
</span>
|
||||
</div>
|
||||
<div class="datetime-input {size}" class:noBorder class:disabled>
|
||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||
<span
|
||||
bind:this={edits[0].el}
|
||||
class="digit"
|
||||
tabindex="0"
|
||||
on:keydown={(ev) => keyDown(ev, edits[0].id)}
|
||||
on:focus={() => focused(edits[0].id)}
|
||||
on:blur={() => (selected = null)}
|
||||
>
|
||||
{#if edits[0].value > -1}
|
||||
{edits[0].value.toString().padStart(2, '0')}
|
||||
{:else}<Label label={ui.string.HH} />{/if}
|
||||
</span>
|
||||
<span class="separator">:</span>
|
||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||
<span
|
||||
bind:this={edits[1].el}
|
||||
class="digit"
|
||||
tabindex="0"
|
||||
on:keydown={(ev) => keyDown(ev, edits[1].id)}
|
||||
on:focus={() => focused(edits[1].id)}
|
||||
on:blur={() => (selected = null)}
|
||||
>
|
||||
{#if edits[1].value > -1}
|
||||
{edits[1].value.toString().padStart(2, '0')}
|
||||
{:else}<Label label={ui.string.MM} />{/if}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.datetime-input {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
margin: 0;
|
||||
padding: 0.75rem;
|
||||
height: 3rem;
|
||||
min-width: 0;
|
||||
font-family: inherit;
|
||||
font-size: 1rem;
|
||||
color: var(--theme-content-color);
|
||||
background-color: var(--theme-bg-color);
|
||||
border: 1px solid var(--theme-button-border);
|
||||
border-radius: 0.25rem;
|
||||
transition: border-color 0.15s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--theme-button-default);
|
||||
&.small {
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
&.medium {
|
||||
height: 3rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
&:not(.noBorder) {
|
||||
padding: 0.75rem;
|
||||
background-color: var(--theme-bg-color);
|
||||
border: 1px solid var(--theme-button-border);
|
||||
&:hover {
|
||||
border-color: var(--theme-button-default);
|
||||
}
|
||||
&:focus-within {
|
||||
border-color: var(--primary-edit-border-color);
|
||||
}
|
||||
}
|
||||
&.noBorder {
|
||||
padding: 0.125rem;
|
||||
}
|
||||
&:focus-within {
|
||||
color: var(--theme-caption-color);
|
||||
border-color: var(--primary-edit-border-color);
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
@ -246,10 +263,6 @@
|
||||
outline: none;
|
||||
border-radius: 0.125rem;
|
||||
|
||||
&:focus {
|
||||
color: var(--accented-button-color);
|
||||
background-color: var(--accented-button-default);
|
||||
}
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@ -260,6 +273,10 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
&:not(.disabled) .digit:focus {
|
||||
color: var(--accented-button-color);
|
||||
background-color: var(--accented-button-default);
|
||||
}
|
||||
.time-divider {
|
||||
flex-shrink: 0;
|
||||
margin: 0 0.25rem;
|
||||
|
@ -80,6 +80,7 @@ export { default as DateTimeRangePresenter } from './components/calendar/DateTim
|
||||
export { default as DatePresenter } from './components/calendar/DatePresenter.svelte'
|
||||
export { default as DueDatePresenter } from './components/calendar/DueDatePresenter.svelte'
|
||||
export { default as DateTimePresenter } from './components/calendar/DateTimePresenter.svelte'
|
||||
export { default as TimeInputBox } from './components/calendar/TimeInputBox.svelte'
|
||||
export { default as StylishEdit } from './components/StylishEdit.svelte'
|
||||
export { default as Grid } from './components/Grid.svelte'
|
||||
export { default as Row } from './components/Row.svelte'
|
||||
|
@ -16,11 +16,12 @@
|
||||
import ui, {
|
||||
Button,
|
||||
ButtonKind,
|
||||
ButtonSize,
|
||||
DatePopup,
|
||||
SimpleDatePopup,
|
||||
SimpleTimePopup,
|
||||
eventToHTMLElement,
|
||||
showPopup
|
||||
showPopup,
|
||||
TimeInputBox
|
||||
} from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import DateLocalePresenter from './DateLocalePresenter.svelte'
|
||||
@ -30,22 +31,18 @@
|
||||
export let showDate: boolean = true
|
||||
export let withoutTime: boolean
|
||||
export let kind: ButtonKind = 'ghost'
|
||||
export let size: ButtonSize = 'medium'
|
||||
export let disabled: boolean = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
$: currentDate = new Date(date)
|
||||
|
||||
function timeClick (e: MouseEvent) {
|
||||
if (showDate) {
|
||||
showPopup(SimpleTimePopup, { currentDate: new Date(date) }, eventToHTMLElement(e), (res) => {
|
||||
if (res.value) {
|
||||
date = res.value.getTime()
|
||||
dispatch('update', date)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
if (!showDate) {
|
||||
showPopup(
|
||||
DatePopup,
|
||||
{ currentDate: new Date(date), withTime: !withoutTime, label: ui.string.SelectDate, noShift: true },
|
||||
{ currentDate, withTime: !withoutTime, label: ui.string.SelectDate, noShift: true },
|
||||
undefined,
|
||||
(res) => {
|
||||
if (res) {
|
||||
@ -58,43 +55,47 @@
|
||||
}
|
||||
|
||||
function dateClick (e: MouseEvent) {
|
||||
showPopup(SimpleDatePopup, { currentDate: new Date(date) }, eventToHTMLElement(e), (res) => {
|
||||
showPopup(SimpleDatePopup, { currentDate }, eventToHTMLElement(e), (res) => {
|
||||
if (res) {
|
||||
date = res.getTime()
|
||||
dispatch('update', date)
|
||||
}
|
||||
})
|
||||
}
|
||||
const updateTime = (d?: Date) => {
|
||||
const dat = d ?? currentDate
|
||||
date = dat.getTime()
|
||||
dispatch('update', dat)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="container" class:vertical={direction === 'vertical'} class:horizontal={direction === 'horizontal'}>
|
||||
<div class="dateEditor-container {direction}">
|
||||
{#if showDate || withoutTime}
|
||||
<Button {kind} on:click={dateClick} {disabled}>
|
||||
<Button {kind} {size} padding={'0 .5rem'} on:click={dateClick} {disabled}>
|
||||
<div slot="content">
|
||||
<DateLocalePresenter {date} />
|
||||
<DateLocalePresenter date={currentDate.getTime()} />
|
||||
</div>
|
||||
</Button>
|
||||
{/if}
|
||||
{#if !withoutTime}
|
||||
<Button {kind} on:click={timeClick} {disabled}>
|
||||
<div slot="content">
|
||||
{new Date(date).toLocaleTimeString('default', { hour: 'numeric', minute: '2-digit' })}
|
||||
</div>
|
||||
<Button {kind} {size} padding={'0 .5rem'} on:click={timeClick} {disabled}>
|
||||
<svelte:fragment slot="content">
|
||||
<TimeInputBox bind:currentDate noBorder size={'small'} on:update={(date) => updateTime(date.detail)} />
|
||||
</svelte:fragment>
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
.dateEditor-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
flex-wrap: nowrap;
|
||||
|
||||
.vertical {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.horizontal {
|
||||
flex-direction: row;
|
||||
&.horizontal {
|
||||
align-items: center;
|
||||
}
|
||||
&.vertical {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -242,32 +242,34 @@
|
||||
adRows = []
|
||||
for (let i = 0; i < displayedDaysCount; i++) alldaysGrid[i] = { alldays: [null] }
|
||||
adMaxRow = 1
|
||||
alldays.forEach((event) => {
|
||||
const days = calendarEvents
|
||||
.filter((ev) => ev.allDay && ev.day !== -1 && event._id === ev._id)
|
||||
.map((ev) => {
|
||||
return ev.day
|
||||
})
|
||||
let emptyRow = 0
|
||||
for (let checkRow = 0; checkRow < adMaxRow; checkRow++) {
|
||||
const empty = days.every((day) => alldaysGrid[day].alldays[checkRow] === null)
|
||||
if (empty) {
|
||||
emptyRow = checkRow
|
||||
break
|
||||
} else if (checkRow === adMaxRow - 1) {
|
||||
emptyRow = adMaxRow
|
||||
addNullRow()
|
||||
break
|
||||
alldays
|
||||
.filter((event) => event.day === -1)
|
||||
.forEach((event) => {
|
||||
const days = newEvents
|
||||
.filter((ev) => ev.allDay && ev.day !== -1 && event._id === ev._id)
|
||||
.map((ev) => {
|
||||
return ev.day
|
||||
})
|
||||
let emptyRow = 0
|
||||
for (let checkRow = 0; checkRow < adMaxRow; checkRow++) {
|
||||
const empty = days.every((day) => alldaysGrid[day].alldays[checkRow] === null)
|
||||
if (empty) {
|
||||
emptyRow = checkRow
|
||||
break
|
||||
} else if (checkRow === adMaxRow - 1) {
|
||||
emptyRow = adMaxRow
|
||||
addNullRow()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
adRows.push({ id: event._id, row: emptyRow, startCol: days[0], endCol: days[days.length - 1] })
|
||||
days.forEach((day) => (alldaysGrid[day].alldays[emptyRow] = event._id))
|
||||
})
|
||||
adRows.push({ id: event._id, row: emptyRow, startCol: days[0], endCol: days[days.length - 1] })
|
||||
days.forEach((day) => (alldaysGrid[day].alldays[emptyRow] = event._id))
|
||||
})
|
||||
const shown = minimizedAD ? minAD : maxAD
|
||||
let tempEventID: string = ''
|
||||
for (let r = 0; r < shown; r++) {
|
||||
const lastRow = r === shown - 1
|
||||
for (let d = 0; d < displayedDaysCount; d++) {
|
||||
const lastRow = r === shown - 1
|
||||
if (r < shown - 1 && tempEventID !== alldaysGrid[d].alldays[r] && alldaysGrid[d].alldays[r] !== null) {
|
||||
tempEventID = alldaysGrid[d].alldays[r] ?? ''
|
||||
if (tempEventID !== '') shortAlldays.push({ id: tempEventID, day: d })
|
||||
@ -445,6 +447,7 @@
|
||||
: rem((heightAD + 0.125) * adMaxRow + 0.25) > maxHeightAD && minimizedAD
|
||||
? rem((heightAD + 0.125) * (adMaxRow <= minAD ? adMaxRow : minAD) + 0.25)
|
||||
: rem((heightAD + 0.125) * (adMaxRow <= maxAD ? adMaxRow : maxAD) + 0.25)
|
||||
$: showArrowAD = (!minimizedAD && adMaxRow > maxAD) || (minimizedAD && adMaxRow > minAD)
|
||||
</script>
|
||||
|
||||
<Scroller
|
||||
@ -472,9 +475,11 @@
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
<div class="sticky-header allday-header text-sm content-dark-color" class:top={!showHeader}>
|
||||
<Label label={calendar.string.AllDay} />
|
||||
{#if (!minimizedAD && adMaxRow > maxAD) || (minimizedAD && adMaxRow > minAD)}
|
||||
<div class="sticky-header allday-header content-dark-color" class:top={!showHeader} class:opened={showArrowAD}>
|
||||
<div class="flex-center text-sm leading-3 text-center" style:height={`${heightAD + 0.25}rem`}>
|
||||
<Label label={calendar.string.AllDay} />
|
||||
</div>
|
||||
{#if showArrowAD}
|
||||
<ActionIcon
|
||||
icon={shownAD ? IconUpOutline : IconDownOutline}
|
||||
size={'small'}
|
||||
@ -568,12 +573,12 @@
|
||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class="calendar-element antiButton ghost medium accent cursor-pointer"
|
||||
class="calendar-element withPointer antiButton ghost medium accent cursor-pointer"
|
||||
style:top={`${rect.top}px`}
|
||||
style:height={`${heightAD}rem`}
|
||||
style:left={`${rect.left}px`}
|
||||
style:width={`${rect.width}px`}
|
||||
style:padding-left={'1.25rem'}
|
||||
style:padding={'0 .5rem 0 1.25rem'}
|
||||
style:--mask-image={'none'}
|
||||
tabindex={500 + addon + day}
|
||||
on:click={() => (shownAD = true)}
|
||||
@ -690,7 +695,10 @@
|
||||
mask-image: var(--mask-image, none);
|
||||
--webkit-mask-image: var(--mask-image, none);
|
||||
border-radius: 0.25rem;
|
||||
pointer-events: none;
|
||||
|
||||
&:not(.withPointer) {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
.sticky-header {
|
||||
position: sticky;
|
||||
@ -749,7 +757,12 @@
|
||||
&.allday-header {
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
padding: 0.625rem 0.125rem;
|
||||
align-items: center;
|
||||
padding: 0 0.125rem;
|
||||
|
||||
&.opened {
|
||||
padding: 0 0.125rem 0.625rem;
|
||||
}
|
||||
}
|
||||
&.allday-container {
|
||||
overflow: hidden;
|
||||
|
@ -30,7 +30,6 @@
|
||||
import EventReminders from './EventReminders.svelte'
|
||||
|
||||
export let object: Event
|
||||
|
||||
$: readOnly = isReadOnly(object)
|
||||
|
||||
let title = object.title
|
||||
@ -232,6 +231,7 @@
|
||||
<div>
|
||||
{#if !allDay && rules.length === 0}
|
||||
<div class="flex-row-center flex-gap-3 ext">
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="cursor-pointer" on:click={() => (allDay = true)}>
|
||||
<Label label={calendar.string.AllDay} />
|
||||
</div>
|
||||
@ -239,6 +239,7 @@
|
||||
<Label label={calendar.string.TimeZone} />
|
||||
</div>
|
||||
{#if rules.length > 0}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="cursor-pointer" on:click={setRecurrance}>
|
||||
<Label label={calendar.string.Repeat} />
|
||||
</div>
|
||||
@ -255,6 +256,7 @@
|
||||
<Label label={calendar.string.TimeZone} />
|
||||
</div>
|
||||
{#if rules.length > 0}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="flex-row-center flex-gap-2 mt-1" on:click={setRecurrance}>
|
||||
<Icon size="small" icon={calendar.icon.Repeat} />
|
||||
{#if rules.length > 0}
|
||||
@ -297,7 +299,7 @@
|
||||
min-height: 0;
|
||||
background: var(--theme-popup-color);
|
||||
box-shadow: var(--theme-popup-shadow);
|
||||
width: 25rem;
|
||||
min-width: 25rem;
|
||||
border-radius: 1rem;
|
||||
|
||||
.header {
|
||||
|
@ -36,7 +36,6 @@
|
||||
const newDiff = dueDate - startDate
|
||||
if (newDiff > 0) {
|
||||
dueDate = allDay ? new Date(dueDate).setHours(23, 59, 59, 999) : dueDate
|
||||
diff = dueDate - startDate
|
||||
} else {
|
||||
dueDate = startDate + (allDay ? allDayDuration : diff)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user