Я разрабатываю компонент календаря для Angular, и мне нужно расположить некоторый элемент div (представляющий события) в сетке.
this.days.forEach((day: SchedulerViewDay, dayIndex: number) => {
day.events = this.getEventsInPeriod({...}).map((event: CalendarSchedulerEvent) => {
const segmentDuration: number = 60.0 / this.hourSegments;
const dayStartDate: Date =
setSeconds(setMinutes(setHours(
setDate(setMonth(setYear(new Date(), day.date.getFullYear()), day.date.getMonth()), day.date.getDate()), this.dayStartHour), this.dayStartMinute), 0);
const segmentsNumber: number = (differenceInMinutes(event.start, dayStartDate) / segmentDuration);
return <CalendarSchedulerEvent>{
...
height: this.hourSegmentHeight * (differenceInMinutes(event.end, event.start) / segmentDuration),
top: (this.hourSegmentHeight * segmentsNumber)
};
});
С этим кодом, это результат:
Как видите, div не правильно расположен. Я думаю, что это связано с границами ячейки сетки, поэтому я применил это исправление:
top: (this.hourSegmentHeight * segmentsNumber) + (segmentsNumber / 2) // 1px for each separator line
Каждая граница имеет ширину 1px, и я рассматриваю только сплошные, а не пунктирные (это мотивация / 2
.
Это почти решило проблему. Там все еще смещение.
Более того, это не элегантное решение, но я не могу придумать, как сделать его более чистым.
Как правильно расположить эти элементы?
Большое спасибо!
РЕДАКТИРОВАТЬ: реализация решения inorganik (первый ответ)
Я создал следующее mixins
в моем scss
файле:
@mixin day-x($attr, $attr-count: 7, $attr-steps: 1, $unit: '%') {
$attr-list: null;
@for $i from 1 through $attr-count {
$attr-value: $attr-steps * $i;
&.day#{$i} {
#{$attr}: #{$attr-value}#{$unit};
}
$attr-list: append($attr-list, unquote(".#{$attr}-#{$attr-value}"), comma);
}
#{$attr-list} {
//append style to all classes
}
}
@mixin time-x($attr, $start-hour: 0, $end-hour: 23, $attr-steps: 2, $minutes-steps: 15, $unit: '%') {
$attr-list: null;
$hours: $start-hour;
$minutes: 0;
$attr-count: ((($end-hour + 1) - $start-hour) * 60) / $minutes-steps;
@for $i from 0 through $attr-count {
$attr-value: $attr-steps * $i;
@if($i > 0) {
$minutes: $minutes + $minutes-steps;
@if($minutes == 60) {
$minutes: 0;
$hours: $hours + 1;
}
}
$hoursString: '#{$hours}';
@if($hours < 10) {
$hoursString: '0#{$hours}';
}
$minutesString: '#{$minutes}';
@if($minutes < 10) {
$minutesString: '0#{$minutes}';
}
&.time#{$hoursString}#{$minutesString} {
#{$attr}: #{$attr-value}#{$unit};
}
$attr-list: append($attr-list, unquote(".#{$attr}-#{$attr-value}"), comma);
}
#{$attr-list} {
//append style to all classes
}
}
@mixin length-x($attr, $attr-steps: 2, $minutes-steps: 15, $unit: '%') {
$attr-list: null;
$attr-count: 24 * 60 / $minutes-steps;
@for $i from 0 through $attr-count {
$attr-name: $minutes-steps * $i;
$attr-value: $attr-steps * $i;
&.length#{$attr-name} {
#{$attr}: #{$attr-value}#{$unit};
}
$attr-list: append($attr-list, unquote(".#{$attr}-#{$attr-value}"), comma);
}
#{$attr-list} {
//append style to all classes
}
}
@include day-x('left', 7, 5);
@include time-x('top', 6, 22, 1.47);
@include length-x('height', 1.47);
Затем я применяю эти стили с атрибутом [ngClass]
, вызывая следующий метод (пока мне не нужен getDay):
getPositioningClasses(event: CalendarSchedulerEvent): string {
const classes: string[] = [
this.getDayClass(event.start),
this.getTimeClass(event.start),
this.getLengthClass(differenceInMinutes(event.end, event.start))
];
return classes.join(' ');
}
private getDayClass(date: Date): string {
return '';
}
private getTimeClass(date: Date): string {
let hours: string = date.getHours() < 10 ? `0${date.getHours()}` : `${date.getHours()}`;
let minutes: string = date.getMinutes() < 10 ? `0${date.getMinutes()}` : `${date.getMinutes()}`;
return `time${hours}${minutes}`;
}
private getLengthClass(durationInMinutes: number): string {
return `length${durationInMinutes}`;
}
Это результат:
Графически это работает как шарм, но мне нужно, чтобы высота сегментов (58px
) была выровнена с процентным приращением, используемым для генерации классов позиционирования (теперь это 1.47%
).
Есть ли способ сделать эти переменные (высоту сегментов и процентную высоту и приращения верхней позиции) из углового кода, делая высоту сегмента настраиваемой пользователем и позволяя этим приращениям адаптироваться?
Большое спасибо!
РЕДАКТИРОВАТЬ 2: еще одна проблема
Привет еще раз! Я столкнулся с другой проблемой.
Диапазон часов компонента не фиксирован, но настраивается снаружи.
В примере в вопросе 6 утра - 22 вечера. Для этого диапазона подойдет процент 1,47, но если я изменю диапазон (например, 0:00 - 22:00), высота календаря будет выше, и процент больше не будет в порядке.
Я думаю, мне нужно вычислить эти значения позиционирования из Typescript. Но я не могу понять, как это сделать.
Чтобы попробовать что-то, я пытаюсь это:
ngAfterViewInit(): void {
this.calendarContainerHeight = this.calendarContainer.nativeElement.clientHeight;
const segmentPercentage: number = 100.0 * this.hourSegmentHeight / this.calendarContainerHeight;
console.log("calendarContainerHeight: ", this.calendarContainerHeight);
console.log("segmentPercentage: ", segmentPercentage);
setTimeout(() => {
this.view.days.forEach(d => {
d.events.forEach((event: CalendarSchedulerEvent) => {
let hours: number = event.start.getHours();
if (this.dayStartHour > 0) { hours = hours - this.dayStartHour; }
const numberOfSegments: number = hours * this.hourSegments;
event.top = numberOfSegments * segmentPercentage;
});
});
});
}
Затем я добавил [style.top.%]="event.top"
к событию div. Вот результат (пока игнорируйте высоту, я еще не управлял ими):
Как вы можете видеть, процент не является точным, а те события, которые происходят в середине дня (или ближе к концу дня), расположены неправильно.
Как я мог решить эту проблему?
Большое спасибо, еще раз!