рдирдорд╕реНрддреЗ!
рдХреЛрдгреАрдп рд╕реАрдбреАрдХреЗ рдореЗрдВ, рд╕рд╛рддрд╡реЗрдВ рд╕рдВрд╕реНрдХрд░рдг рдореЗрдВ рдПрдХ рдЖрднрд╛рд╕реА рд╕реНрдХреНрд░реЙрд▓ рджрд┐рдЦрд╛рдИ рджрд┐рдпрд╛ ред
рдпрд╣ рдмрд╣реБрдд рдЕрдЪреНрдЫрд╛ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ рдЬрдм рдкреНрд░рддреНрдпреЗрдХ рддрддреНрд╡ рдХрд╛ рдЖрдХрд╛рд░ рд╕рдорд╛рди рд╣реЛрддрд╛ рд╣реИ - рдФрд░ рдмреЙрдХреНрд╕ рдХреЗ рдареАрдХ рдмрд╛рд╣рд░ред рд╣рдо рдмрд╕ рдкрд┐рдХреНрд╕реЗрд▓ рдореЗрдВ рдЖрдХрд╛рд░ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рдЗрдВрдЧрд┐рдд рдХрд░рддреЗ рд╣реИрдВ рдХрд┐ рдХрдВрдЯреЗрдирд░ рдХреЛ рдХрд┐рд╕ рддрддреНрд╡ рддрдХ рд╕реНрдХреНрд░реЙрд▓ рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдП, рдХреНрдпрд╛ рдЗрд╕реЗ рдЖрд╕рд╛рдиреА рд╕реЗ рдХрд░рдирд╛ рд╣реИ, рдФрд░ рд╣рдо рд╡рд░реНрддрдорд╛рди рддрддреНрд╡ рдХреЗ рд╕реВрдЪрдХрд╛рдВрдХ рдХреА рд╕рджрд╕реНрдпрддрд╛ рднреА рд▓реЗ рд╕рдХрддреЗ рд╣реИрдВред рд╣рд╛рд▓рд╛рдВрдХрд┐, рдХреНрдпрд╛ рд╣реЛрдЧрд╛ рдЕрдЧрд░ рддрддреНрд╡реЛрдВ рдХрд╛ рдЖрдХрд╛рд░ рдмрджрд▓ рдЬрд╛рдП? рдЗрд╕рдХреЗ рд▓рд┐рдП, VirtualScrollStrategy
рдореЗрдВ VirtualScrollStrategy
рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдкреНрд░рджрд╛рди рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рдЬрд┐рд╕реЗ рд▓рд╛рдЧреВ рдХрд░рдХреЗ рд╣рдо рдЕрдкрдиреА рд╕реВрдЪреА рдореЗрдВ рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕реНрдХреНрд░реЙрд▓ рд╕рд┐рдЦрд╛рдПрдВрдЧреЗред
рдореЗрд░реЗ рдорд╛рдорд▓реЗ рдореЗрдВ, рдореЛрдмрд╛рдЗрд▓ рдкреНрд░рд╕реНрддреБрддрд┐ рдХреЗ рд▓рд┐рдП рдПрдХ рдХреИрд▓реЗрдВрдбрд░ рдмрдирд╛рдирд╛ рдЖрд╡рд╢реНрдпрдХ рдерд╛, рдЬрд┐рд╕реЗ рд▓рдЧрд╛рддрд╛рд░ рд╕реНрдХреНрд░реЙрд▓ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ, рдФрд░ рдПрдХ рдорд╣реАрдиреЗ рдореЗрдВ рд╣рдлреНрддреЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ рд╣рдореЗрд╢рд╛ рдЕрд▓рдЧ рд╣реЛрддреА рд╣реИред рдЖрдЗрдП рдЬрд╛рдирдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░реЗрдВ рдХрд┐ рд╡рд░реНрдЪреБрдЕрд▓ рд╕реНрдХреНрд░реЙрд▓рд┐рдВрдЧ рд╕реНрдЯреНрд░реИрдЯреЗрдЬреА рдХреНрдпрд╛ рд╣реИ рдФрд░ рдЕрдкрдирд╛ рдЦреБрдж рдХрд╛ рд▓рд┐рдЦреЗрдВред

рдЖрдХрд╛рд░ рдХреА рдЧрдгрдирд╛
рдЬреИрд╕рд╛ рдХрд┐ рдЖрдк рдЬрд╛рдирддреЗ рд╣реИрдВ, рд╣рд░ 28 рд╕рд╛рд▓ рдореЗрдВ рдХреИрд▓реЗрдВрдбрд░ рджреЛрд╣рд░рд╛рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред
рдРрд╕рд╛ рддрдм рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рдЬрдм рдЖрдк рдЗрд╕ рдмрд╛рдд рдХрд╛ рдзреНрдпрд╛рди рдирд╣реАрдВ рд░рдЦрддреЗ рд╣реИрдВ рдХрд┐ рд╡рд░реНрд╖ рд▓реАрдк рд╡рд░реНрд╖ рдирд╣реАрдВ рд╣реИ, рдпрджрд┐ рдпрд╣ 100 рд╕реЗ рд╡рд┐рднрд╛рдЬреНрдп рд╣реИ, рд▓реЗрдХрд┐рди 400 рд╕реЗ рдирд╣реАрдВред рд╣рдорд╛рд░реЗ рдорд╛рдорд▓реЗ рдореЗрдВ, рд╣рдореЗрдВ 1900 рд╕реЗ рдкрд╣рд▓реЗ рдФрд░ 2100 рдХреЗ рдмрд╛рдж рдХреЗ рд╡рд░реНрд╖реЛрдВ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИред рдЬрдирд╡рд░реА рдХреЛ рд╕реЛрдорд╡рд╛рд░ рдХреЛ рдкрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдо 1900 рд╕реЗ рдПрдХ рднреА рдЦрд╛рддреЗ рдХреЗ рд▓рд┐рдП рд╢реБрд░реВ рдХрд░реЗрдВрдЧреЗ рдФрд░ рд╣рдо рдХрд░реЗрдВрдЧреЗ 196 рд╕рд╛рд▓ рдкреАрдЫреЗ рд╣рдЯреЗрдВред рдЗрд╕ рдкреНрд░рдХрд╛рд░, рд╣рдорд╛рд░реЗ рдХреИрд▓реЗрдВрдбрд░ рдореЗрдВ 7 рджреЛрд╣рд░рд╛рдП рдЬрд╛рдиреЗ рд╡рд╛рд▓реЗ рдЪрдХреНрд░ рд╣реЛрдВрдЧреЗред 1900 рдореЗрдВ 29 рдлрд░рд╡рд░реА рдХреА рдЕрдиреБрдкрд╕реНрдерд┐рддрд┐ рдЪреЛрдЯ рдирд╣реАрдВ рдкрд╣реБрдВрдЪрд╛рдПрдЧреА, рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рдЧреБрд░реБрд╡рд╛рд░ рд╣реЛрдЧрд╛ред

рдЧрдгрдирд╛ рд╕реНрдХреНрд░реЙрд▓ рдХреЗ рджреМрд░рд╛рди рдХреА рдЬрд╛рдПрдЧреА, рдЗрд╕рд▓рд┐рдП рдЧрдгрдирд╛ рдЬрд┐рддрдиреА рд╕рд░рд▓ рд╣реЛрдЧреА, рдкреНрд░рджрд░реНрд╢рди рдЙрддрдирд╛ рд╣реА рдЕрдзрд┐рдХ рд╣реЛрдЧрд╛ред рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдо рдПрдХ рд▓реВрдк рд╕реНрдерд┐рд░рд╛рдВрдХ рдмрдирд╛рдПрдВрдЧреЗ, рдЬрд┐рд╕рдореЗрдВ 12 рд╕рдВрдЦреНрдпрд╛рдУрдВ рдореЗрдВ рд╕реЗ 28 рд╕рд░рдгрд┐рдпрд╛рдБ рд╣реЛрдВрдЧреА, рдЬреЛ рдкреНрд░рддреНрдпреЗрдХ рдорд╣реАрдиреЗ рдХреА рдКрдБрдЪрд╛рдИ рдХреЗ рд▓рд┐рдП рдЬрд╝рд┐рдореНрдореЗрджрд╛рд░ рд╣реИрдВ:
function getCycle(label: number, week: number): ReadonlyArray<ReadonlyArray<number>> { return Array.from({length: 28}, (_, i) => Array.from( {length: 12}, (_, month) => label + weekCount(i, month) * week, ), ); }
рдЗрдирдкреБрдЯ рдореЗрдВ, рдЗрд╕ рдлрд╝рдВрдХреНрд╢рди рдХреЛ рдорд╣реАрдиреЗ рдХреЗ рд╢реАрд░реНрд╖рдХ рдХреА рдКрдВрдЪрд╛рдИ рдФрд░ рдПрдХ рд╕рдкреНрддрд╛рд╣ рдХреА рдКрдВрдЪрд╛рдИ (64 рдФрд░ 48 рдкрд┐рдХреНрд╕рд▓, рдХреНрд░рдорд╢рдГ рдКрдкрд░ рдЬреАрдЖрдИрдПрдл рдХреЗ рд▓рд┐рдП) рдкреНрд░рд╛рдкреНрдд рд╣реЛрддреА рд╣реИред рдПрдХ рдорд╣реАрдиреЗ рдореЗрдВ рд╣рдлреНрддреЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ рд╣рдореЗрдВ рдЗрд╕ рддрд░рд╣ рдХреЗ рдПрдХ рд╕рд░рд▓ рдХрд╛рд░реНрдп рдХреА рдЧрдгрдирд╛ рдХрд░рдиреЗ рдореЗрдВ рдорджрдж рдХрд░реЗрдЧреА:
function weekCount(year: number, month: number): number { const firstOfMonth = new Date(year + STARTING_YEAR, month, 1); const lastOfMonth = new Date(year + STARTING_YEAR, month + 1, 0); const days = lastOfMonth.getDate() + (firstOfMonth.getDay() || 7) - 1; return Math.ceil(days / 7); }
рдкрд░рд┐рдгрд╛рдо рд╕реНрдерд┐рд░ const CYCLE = getCycle(64, 48);
рдореЗрдВ рд╕рдВрдЧреНрд░рд╣реАрдд рд╣реИ const CYCLE = getCycle(64, 48);
ред
рд╣рдо рдПрдХ рдлрд╝рдВрдХреНрд╢рди рд▓рд┐рдЦреЗрдВрдЧреЗ рдЬреЛ рдЖрдкрдХреЛ рдЪрдХреНрд░ рдХреЗ рдЕрдВрджрд░ рд╡рд░реНрд╖ рдФрд░ рдорд╣реАрдиреЗ рджреНрд╡рд╛рд░рд╛ рдКрдВрдЪрд╛рдИ рдХреА рдЧрдгрдирд╛ рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ:
function reduceCycle(lastYear: number = 28, lastMonth: number = 12): number { return CYCLE.reduce( (total, year, yearIndex) => yearIndex <= lastYear ? total + year.reduce( (sum, month, monthIndex) => yearIndex < lastYear || (yearIndex === lastYear && monthIndex < lastMonth) ? sum + month : sum, 0, ) : total, 0, ); }
рдЗрд╕ рдлрд╝рдВрдХреНрд╢рди рдХреЛ рддрд░реНрдХреЛрдВ рдХреЗ рдмрд┐рдирд╛ рдХреЙрд▓ рдХрд░рдиреЗ рд╕реЗ, рд╣рдореЗрдВ рдПрдХ рдЪрдХреНрд░ рдХрд╛ рдЖрдХрд╛рд░ рдорд┐рд▓рддрд╛ рд╣реИ, рдФрд░ рднрд╡рд┐рд╖реНрдп рдореЗрдВ рд╣рдо рдЕрдкрдиреА рд╕реАрдорд╛ рд╕реЗ рдХрд┐рд╕реА рднреА рд╡рд░реНрд╖ рдХреЗ рдХрд┐рд╕реА рднреА рдорд╣реАрдиреЗ рдХреЗ рд▓рд┐рдП рдХрдВрдЯреЗрдирд░ рдХреЗ рд╢реАрд░реНрд╖ рд╕реЗ рдЗрдВрдбреЗрдВрдЯ рдкрд╛ рд╕рдХрддреЗ рд╣реИрдВред
рдЖрдк рдЯреЛрдХрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЕрдкрдиреА рд░рдгрдиреАрддрд┐ рдХреЛ рд╡рд░реНрдЪреБрдЕрд▓ рд╕реНрдХреНрд░реЙрд▓ рдореЗрдВ рд╕рдмрдорд┐рдЯ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ
VIRTUAL_SCROLL_STRATEGY
:
{ provide: VIRTUAL_SCROLL_STRATEGY, useClass: MobileCalendarStrategy, },
рд╣рдорд╛рд░реА рдХрдХреНрд╖рд╛ рдХреЛ VirtualScrollStrategy
рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рд▓рд╛рдЧреВ рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдП:
export interface VirtualScrollStrategy { scrolledIndexChange: Observable<number>; attach(viewport: CdkVirtualScrollViewport): void; detach(): void; onContentScrolled(): void; onDataLengthChanged(): void; onContentRendered(): void; onRenderedOffsetChanged(): void; scrollToIndex(index: number, behavior: ScrollBehavior): void; }
attach
рдФрд░ detach
рдлрд╝рдВрдХреНрд╢рдВрд╕ рд╢реБрд░реВ рдХрд░рдиреЗ рдФрд░ рдмрдВрдж рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЬрд┐рдореНрдореЗрджрд╛рд░ рд╣реИрдВред рд╣рдорд╛рд░реЗ рд▓рд┐рдП рд╕рдмрд╕реЗ рдорд╣рддреНрд╡рдкреВрд░реНрдг onContentScrolled
рдкрджреНрдзрддрд┐ рдХреЛ рд╣рд░ рдмрд╛рд░ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рджреНрд╡рд╛рд░рд╛ рдХрдВрдЯреЗрдирд░ рдХреЛ рд╕реНрдХреНрд░реЙрд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИ (рдЕрдирд╛рд╡рд╢реНрдпрдХ requestAnimationFrame
рд╕реЗ рдмрдЪрдиреЗ рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдЕрдирд╛рд╡рд╢реНрдпрдХ рдХреЙрд▓ рд╕реЗ рдмрдЪрдиреЗ рдХреЗ рд▓рд┐рдП рдЖрдВрддрд░рд┐рдХ рд░реВрдк рд╕реЗ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ)ред
onDataLengthChanged
рдХреЛ рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИ рдЬрдм onDataLengthChanged
рдореЗрдВ рддрддреНрд╡реЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ рдмрджрд▓ рдЧрдИ рд╣реИ - рд╣рдорд╛рд░реЗ рдорд╛рдорд▓реЗ рдореЗрдВ рдРрд╕рд╛ рдХрднреА рдирд╣реАрдВ рд╣реЛрдЧрд╛ред рдПрдХ рдирд┐рдпрдо рдХреЗ рд░реВрдк рдореЗрдВ, рдРрд╕реА рд╕реНрдерд┐рддрд┐ рдореЗрдВ, рдХреБрд▓ рдКрдВрдЪрд╛рдИ рдФрд░ рд╡рд░реНрддрдорд╛рди рдореЗрдВ рдкреНрд░рджрд░реНрд╢рд┐рдд рддрддреНрд╡реЛрдВ рдХреЛ рдлрд┐рд░ рд╕реЗ attach
рдЖрд╡рд╢реНрдпрдХ рд╣реЛрдЧрд╛, рд▓рдЧрднрдЧ рд╡рд╣реА рдЬреЛ attach
рдлрд╝рдВрдХреНрд╢рди рдореЗрдВ рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред
onContentRendered
рдФрд░ onRenderedOffsetChanged
рдХреЛ рддрдм рдмреБрд▓рд╛рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рдЬрдм рддрддреНрд╡реЛрдВ рдХрд╛ рдкреНрд░рджрд░реНрд╢рд┐рдд рд╣рд┐рд╕реНрд╕рд╛ рдмрджрд▓рддрд╛ рд╣реИ рдФрд░ рдкрд╣рд▓реЗ рддрддреНрд╡ рдореЗрдВ рдкрд░рд┐рд╡рд░реНрддрди рд╣реЛрддрд╛ рд╣реИред CdkVirtualScrollViewport
рдЗрди рд╡рд┐рдзрд┐рдпреЛрдВ рддрдХ рдкрд╣реБрдБрдЪрддрд╛ рд╣реИ рдЬрдм рдЗрд╕реЗ рдкреНрд░рджрд░реНрд╢рд┐рдд рд╡рд╕реНрддреБрдУрдВ рдХреА рдПрдХ рдирдИ рд╢реНрд░реЗрдгреА рджреА рдЬрд╛рддреА рд╣реИ рдФрд░ рдХреНрд░рдорд╢рдГ рдЙрдирдореЗрдВ рд╕реЗ рдкрд╣рд▓реЗ рдХреЗ рд▓рд┐рдП рдЗрдВрдбреЗрдВрдЯ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рд╣рдореЗрдВ рдЗрд╕рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рд╣рд╛рде рд╕реЗ CdkVirtualScrollViewport
рддрд░реАрдХреЛрдВ рдХреЛ рдХреЙрд▓ рдХрд░рдиреЗ рдХреА рдХреЛрдИ рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИред рдпрджрд┐ рдЖрдкрдХреЛ рдЗрд╕рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рддреЛ onContentRendered
рдЕрдВрджрд░ рдЖрдк рдПрдХ рдирдП рдЗрдВрдбреЗрдВрдЯ рдХреА рдЧрдгрдирд╛ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рдФрд░ onRenderedOffsetChanged
- рдЗрд╕рдХреЗ рд╡рд┐рдкрд░реАрдд, рдкрд░рд┐рдгрд╛рдорд╕реНрд╡рд░реВрдк рдЗрдВрдбреЗрдВрдЯ рдХреЗ рд▓рд┐рдП рджреГрд╢реНрдп рддрддреНрд╡реЛрдВ рдХреА рд╢реНрд░реЗрдгреАред
рд╣рдорд╛рд░реЗ рд▓рд┐рдП рджреВрд╕рд░реА рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╡рд┐рдзрд┐ - scrollToIndex
- рдЖрдкрдХреЛ рдХрдВрдЯреЗрдирд░ рдХреЛ рд╡рд╛рдВрдЫрд┐рдд рддрддреНрд╡ рддрдХ рд╕реНрдХреНрд░реЙрд▓ рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддреА рд╣реИ, рдФрд░ рдЗрд╕рдХреЗ рд╡рд┐рдкрд░реАрдд - scrolledIndexChange
- рд╡рд░реНрддрдорд╛рди рджреГрд╢реНрдп рддрддреНрд╡ рдХреЛ рдЯреНрд░реИрдХ рдХрд░рдирд╛ рд╕рдВрднрд╡ рдмрдирд╛ рджреЗрдЧрд╛ред
рд╢реБрд░реВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдо рд╕рднреА рд╕рд░рд▓ рддрд░реАрдХреЛрдВ рдХрд╛ рдирд┐рд░реНрдорд╛рдг рдХрд░реЗрдВрдЧреЗ, рдФрд░ рдлрд┐рд░ рдореБрдЦреНрдп рдХреЛрдб рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░реЗрдВрдЧреЗ:
export class MobileCalendarStrategy implements VirtualScrollStrategy { private index$ = new Subject<number>(); private viewport: CdkVirtualScrollViewport | null = null; scrolledIndexChange = this.index$.pipe(distinctUntilChanged()); attach(viewport: CdkVirtualScrollViewport) { this.viewport = viewport; this.viewport.setTotalContentSize(CYCLE_HEIGHT * 7); this.updateRenderedRange(this.viewport); } detach() { this.index$.complete(); this.viewport = null; } onContentScrolled() { if (this.viewport) { this.updateRenderedRange(this.viewport); } } scrollToIndex(index: number, behavior: ScrollBehavior): void { if (this.viewport) { this.viewport.scrollToOffset(this.getOffsetForIndex(index), behavior); } }
рд╕реНрдХреНрд░реЙрд▓ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдореЗрдВ рдЗрдВрдбреЗрдВрдЯ рджреНрд╡рд╛рд░рд╛ рддрддреНрд╡ рдХреЗ рдЗрдВрдбреЗрдХреНрд╕ рдХреЛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдФрд░ рдЗрд╕рдХреЗ рд╡рд┐рдкрд░реАрдд рдЗрдВрдбреЗрдХреНрд╕ рджреНрд╡рд╛рд░рд╛ рдЗрдВрдбреЗрдВрдЯ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдПред рд╣рдорд╛рд░реЗ рджреНрд╡рд╛рд░рд╛ рд▓рд┐рдЦрд╛ рдЧрдпрд╛ рдХрд╛рд░реНрдп reduceCycle
рдкрд╣рд▓реЗ рдХрд╛рд░реНрдп рдХреЗ рд▓рд┐рдП рдЙрдкрдпреБрдХреНрдд рд╣реИ:
private getOffsetForIndex(index: number): number { const month = index % 12; const year = (index - month) / 12; return this.computeHeight(year, month); } private computeHeight(year: number, month: number): number { const remainder = year % 28; const remainderHeight = reduceCycle(remainder, month); const fullCycles = (year - remainder) / 28; const fullCyclesHeight = fullCycles * CYCLE_HEIGHT; return fullCyclesHeight + remainderHeight; }
рдпрд╣реА рд╣реИ, рдЗрдВрдбреЗрдХреНрд╕ рджреНрд╡рд╛рд░рд╛ рдКрдВрдЪрд╛рдИ рдкрд╛рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдо рдкрд╛рддреЗ рд╣реИрдВ рдХрд┐ рдХрд┐рддрдиреЗ рдкреВрд░реНрдг 28 рд╕рд╛рд▓ рдХреЗ рдЪрдХреНрд░ рд╡рд░реНрддрдорд╛рди рддрд╛рд░реАрдЦ рдХреЗ рд▓рд┐рдП рдлрд┐рдЯ рд╣реИрдВ, рдФрд░ рдлрд┐рд░ рд╣рдо рджрд┐рдП рдЧрдП рдорд╣реАрдиреЗ рддрдХ рдЕрдкрдиреЗ рд╕рд░рдгреА рдХреЛ рдЬреЛрдбрд╝рддреЗ рд╣реИрдВред рд░рд┐рд╡рд░реНрд╕ рдСрдкрд░реЗрд╢рди рдХреБрдЫ рдЕрдзрд┐рдХ рдЬрдЯрд┐рд▓ рд╣реИ:
private getIndexForOffset(offset: number): number { const remainder = offset % CYCLE_HEIGHT; const years = ((offset - remainder) / CYCLE_HEIGHT) * 28; let accumulator = 0; for (let year = 0; year < CYCLE.length; year++) { for (let month = 0; month < CYCLE[year].length; month++) { accumulator += CYCLE[year][month]; if (accumulator - CYCLE[year][month] / 2 > remainder) { return Math.max((years + year) * MONTHS_IN_YEAR + month, 0); } } } return 196; }
рдЬрдм рд╣рдо рдкреВрд░реНрдг 28-рд╡рд░реНрд╖реАрдп рдЪрдХреНрд░реЛрдВ рдХреА рдХреБрд▓ рдКрдВрдЪрд╛рдИ рдкреНрд░рд╛рдкреНрдд рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рд╣рдо рд╕рднреА рдорд╣реАрдиреЛрдВ рдХреА рдХреБрд▓ рдКрдВрдЪрд╛рдИ рдХреЛ рдЗрдХрдЯреНрдард╛ рдХрд░рддреЗ рд╣реБрдП, рд╕рд░рдгреА рдкрд░ рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ рдХрд░реЗрдВрдЧреЗ, рдЬрдм рддрдХ рдХрд┐ рдпрд╣ рд╡рд╛рдВрдЫрд┐рдд рдЗрдВрдбреЗрдВрдЯ рд╕реЗ рдЕрдзрд┐рдХ рди рд╣реЛ рдЬрд╛рдПред рдЙрд╕реА рд╕рдордп, рд╣рдо рди рдХреЗрд╡рд▓ рдЙрдЪреНрдЪрддрдо рджреГрд╢реНрдпрдорд╛рди рдорд╣реАрдиреЗ рдХреЛ рдЦреЛрдЬрдиреЗ рдХреЗ рд▓рд┐рдП рдкреНрд░рддреНрдпреЗрдХ рдорд╣реАрдиреЗ ( CYCLE[year][month] / 2
) рдХреА рдЖрдзреА рд╕реЗ рдЕрдзрд┐рдХ рдКрдВрдЪрд╛рдИ рдХреА рдЬрд╛рдВрдЪ рдХрд░реЗрдВрдЧреЗ, рд▓реЗрдХрд┐рди рдКрдкрд░реА рд╕реАрдорд╛ рдХреЗ рд╕рдмрд╕реЗ рдХрд░реАрдмред рд╕реНрдХреНрд░реЙрд▓ рдкреВрд░рд╛ рд╣реЛрдиреЗ рдХреЗ рдмрд╛рдж рдорд╣реАрдиреЗ рдХреА рд╢реБрд░реБрдЖрдд рдореЗрдВ рдЪрд┐рдХрдиреА рдШреБрдорд╛ рдХреЗ рд▓рд┐рдП рднрд╡рд┐рд╖реНрдп рдореЗрдВ рдЗрд╕рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрдЧреАред
рдпрд╣ рд╕рдмрд╕реЗ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдХрд╛рд░реНрдп рд▓рд┐рдЦрдиреЗ рдХреЗ рд▓рд┐рдП рдмрдирд╛ рд╣реБрдЖ рд╣реИ рдЬреЛ рджреГрд╢реНрдп рдХреНрд╖реЗрддреНрд░ рдХреЗ рддрддреНрд╡реЛрдВ рдХреЛ рдкреНрд░рд╕реНрддреБрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЬрд┐рдореНрдореЗрджрд╛рд░ рд╣реИ:
private updateRenderedRange(viewport: CdkVirtualScrollViewport) { const offset = viewport.measureScrollOffset(); const viewportSize = viewport.getViewportSize(); const {start, end} = viewport.getRenderedRange(); const dataLength = viewport.getDataLength(); const newRange = {start, end}; const firstVisibleIndex = this.getIndexForOffset(offset); const startBuffer = offset - this.getOffsetForIndex(start); if (startBuffer < BUFFER && start !== 0) { newRange.start = Math.max(0, this.getIndexForOffset(offset - BUFFER * 2)); newRange.end = Math.min( dataLength, this.getIndexForOffset(offset + viewportSize + BUFFER), ); } else { const endBuffer = this.getOffsetForIndex(end) - offset - viewportSize; if (endBuffer < BUFFER && end !== dataLength) { newRange.start = Math.max(0, this.getIndexForOffset(offset - BUFFER)); newRange.end = Math.min( dataLength, this.getIndexForOffset(offset + viewportSize + BUFFER * 2), ); } } viewport.setRenderedRange(newRange); viewport.setRenderedContentOffset(this.getOffsetForIndex(newRange.start)); this.index$.next(firstVisibleIndex); }
рдЪрд▓реЛ рд╕рдм рдХреБрдЫ рдХреНрд░рдо рдореЗрдВ рд╡рд┐рдЪрд╛рд░ рдХрд░реЗрдВред
рд╣рдо рд╡рд░реНрддрдорд╛рди рдЗрдВрдбреЗрдВрдЯреЗрд╢рди, рдХрдВрдЯреЗрдирд░ рдЖрдХрд╛рд░, рджрд┐рдЦрд╛рдП рдЧрдП рд╡рд░реНрддрдорд╛рди рд░реЗрдВрдЬ рдФрд░ рд╡рд╕реНрддреБрдУрдВ рдХреА рдХреБрд▓ рд╕рдВрдЦреНрдпрд╛ рдХреЗ CdkVirtualScrollViewport
рдкреВрдЫрддреЗ рд╣реИрдВред рддрдм рд╣рдо рдкрд╣рд▓реЗ рджрд┐рдЦрд╛рдИ рджреЗрдиреЗ рд╡рд╛рд▓реЗ рддрддреНрд╡ рдФрд░ рдмрд╣реБрдд рдкрд╣рд▓реЗ рдкреНрд░рджрд╛рди рдХрд┐рдП рдЧрдП рддрддреНрд╡ рдкрд░ рдЗрдВрдбреЗрдВрдЯ рдкрд╛рддреЗ рд╣реИрдВред
рдЙрд╕рдХреЗ рдмрд╛рдж, рд╣рдореЗрдВ рдпрд╣ рд╕рдордЭрдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рдХрд┐ рд╡рд░реНрддрдорд╛рди рддрддреНрд╡реЛрдВ рдХреА рд╕реАрдорд╛ рдХреЛ рдХреИрд╕реЗ рдмрджрд▓рдирд╛ рд╣реИ рдФрд░ рдЙрдирдореЗрдВ рд╕реЗ рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ рдЗрдВрдбреЗрдВрдЯ рдХрд░рдирд╛ рд╣реИ, рддрд╛рдХрд┐ рд╡рд░реНрдЪреБрдЕрд▓ рд╕реНрдХреНрд░реЙрд▓ рддрддреНрд╡реЛрдВ рдХреЛ рд╕реБрдЪрд╛рд░реВ рд░реВрдк рд╕реЗ рд▓реЛрдб рдХрд░ рд╕рдХреЗ рдФрд░ рдКрдВрдЪрд╛рдИ рдХреЛ рдкреБрди: рдЧрдгрдирд╛ рдХрд░рддреЗ рд╕рдордп рдЪрд┐рдХреЛрдЯреА рди рд╣реЛред рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдорд╛рд░реЗ рдкрд╛рд╕ BUFFER
рд╕реНрдерд┐рд░рд╛рдВрдХ рд╣реИ, рдЬреЛ BUFFER
рдХрд┐ рд╣рдо рджрд┐рдЦрд╛рдИ рджреЗрдиреЗ рд╡рд╛рд▓реЗ рддрддреНрд╡реЛрдВ рд╕реЗ рдХрд┐рддрдиреЗ рдкрд┐рдХреНрд╕реЗрд▓ рдКрдкрд░ рдФрд░ рдиреАрдЪреЗ рджрд┐рдЦрд╛рдИ рджреЗрддреЗ рд╣реИрдВред рдореЗрд░реЗ рдорд╛рдорд▓реЗ рдореЗрдВ, рдореИрдВ 500px рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реВрдВред рдпрджрд┐ рд╢реАрд░реНрд╖ рдЗрдВрдбреЗрдВрдЯ рдмрдлрд░ рд╕реЗ рдЫреЛрдЯрд╛ рд╣реИ рдФрд░ рдКрдкрд░ рдЕрдзрд┐рдХ рддрддреНрд╡ рд╣реИрдВ, рддреЛ рд╣рдо рдмрдлрд░ рдХреЛ рдбрдмрд▓ рдХрд╡рд░ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╢реАрд░реНрд╖ рдкрд░ рдкрд░реНрдпрд╛рдкреНрдд рддрддреНрд╡ рдЬреЛрдбрд╝рдХрд░ рд╕реАрдорд╛ рдмрджрд▓ рджреЗрдВрдЧреЗред рд╣рдо рд╕реАрдорд╛ рдХреЗ рдЕрдВрдд рдХреЛ рднреА рд╕рдорд╛рдпреЛрдЬрд┐рдд рдХрд░рддреЗ рд╣реИрдВред рдЪреВрдВрдХрд┐ рд╣рдо рдКрдкрд░ рд╕реНрдХреНрд░реЙрд▓ рдХрд░рддреЗ рд╣реИрдВ - рдПрдХ рдмрдлрд╝рд░ рд╕рдмрд╕реЗ рдиреАрдЪреЗ рд╣реИред рдПрдХ рд╣реА рдмрд╛рдд рд╣реИ, рд▓реЗрдХрд┐рди рд╡рд┐рдкрд░реАрдд рджрд┐рд╢рд╛ рдореЗрдВ рд╣рдо рдиреАрдЪреЗ рд╕реНрдХреНрд░реЙрд▓ рдХрд░рддреЗ рд╕рдордп рдкреНрд░рджрд░реНрд╢рди рдХрд░рддреЗ рд╣реИрдВред
рдлрд┐рд░ рд╣рдо CdkVirtualScrollViewport
рдирдИ рд╢реНрд░реЗрдгреА рдкреНрд░рджрд╛рди рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рдЗрд╕рдХреЗ рдкрд╣рд▓реЗ рддрддреНрд╡ рдХреЗ рд▓рд┐рдП рдЗрдВрдбреЗрдВрдЯ рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░рддреЗ рд╣реИрдВред рд╡рд░реНрддрдорд╛рди рджреГрд╢реНрдпрдорд╛рди рд╕реВрдЪрдХрд╛рдВрдХ рдХреЛ рдкрд╛рд╕ рдХрд░реЗрдВред
рдХреЗ рдЙрдкрдпреЛрдЧ
рд╣рдорд╛рд░реА рд░рдгрдиреАрддрд┐ рддреИрдпрд╛рд░ рд╣реИред рдШрдЯрдХ рдкреНрд░рджрд╛рддрд╛рдУрдВ рдореЗрдВ рдЬреЛрдбрд╝реЗрдВ, рдЬреИрд╕рд╛ рдХрд┐ рдКрдкрд░ рджрд┐рдЦрд╛рдпрд╛ рдЧрдпрд╛ рд╣реИ, рдФрд░ рдЯреЗрдореНрдкрд▓реЗрдЯ рдореЗрдВ CdkVirtualScrollViewport
рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ:
<cdk-virtual-scroll-viewport (scrolledIndexChange)="activeMonth = $event" > <section *cdkVirtualFor="let month of months; templateCacheSize: 10" > <h1>{{month.name}}</h2> <our-calendar [month]="month"></our-calendar> </section> </cdk-virtual-scroll-viewport>
рдпрд╣ рд╕реНрдХреНрд░реЙрд▓ рдХреЗ рдЕрдВрдд рдореЗрдВ рдЕрдЧрд▓реЗ рдорд╣реАрдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдЪрд┐рдХрдиреА рдореЛрдбрд╝ рдХрд╛ рдПрд╣рд╕рд╛рд╕ рдХрд░рддрд╛ рд╣реИред рдмрд╛рд░реАрдХрд┐рдпрд╛рдВ рд╣реИрдВред
рддрдереНрдп рдпрд╣ рд╣реИ рдХрд┐ рдЙрдВрдЧрд▓реА рдЬрд╛рд░реА рд╣реЛрдиреЗ рдХреЗ рдмрд╛рдж рдореЛрдмрд╛рдЗрд▓ рдЙрдкрдХрд░рдгреЛрдВ рдкрд░ рд╕реНрдХреНрд░реЙрд▓ рдХрд░рдирд╛ рдЬрд╛рд░реА рд░рд╣рддрд╛ рд╣реИред рдЗрд╕рд▓рд┐рдП, рд╣рдорд╛рд░реЗ рд▓рд┐рдП рдЙрд╕ рдкрд▓ рдХреЛ рд╕рдордЭрдирд╛ рдореБрд╢реНрдХрд┐рд▓ рд╣реЛрдЧрд╛ рдЬрдм рдЪрд╛рд▓реВ рдорд╣реАрдиреЗ рдХреЛ рд╕рдВрд░реЗрдЦрд┐рдд рдХрд░рдирд╛ рдЖрд╡рд╢реНрдпрдХ рд╣реЛред рдЗрд╕рдХреЗ рд▓рд┐рдП рд╣рдо RxJs рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВред touchstart
рдИрд╡реЗрдВрдЯ рдХреА touchstart
рдФрд░ рдЕрдЧрд▓реЗ touchend
рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд░рддреЗ рд╣реИрдВред рдЗрд╕рдХреА рд╢реБрд░реБрдЖрдд рдХреЗ рдмрд╛рдж, рд╣рдо рдпрд╣ рдЬрд╛рдирдиреЗ рдХреЗ рд▓рд┐рдП рджреМрдбрд╝ рдСрдкрд░реЗрдЯрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ рдХрд┐ рдХреНрдпрд╛ рд╕реНрдХреНрд░реЙрд▓ рдЬрд╛рд░реА рд╣реИ, рдпрд╛ рдпрджрд┐ рдЙрдВрдЧрд▓реА рдмрд┐рдирд╛ рддреНрд╡рд░рдг рдХреЗ рдЬрд╛рд░реА рдХреА рдЧрдИ рдереАред рдпрджрд┐ SCROLL_DEBOUNCE_TIME
рдЕрд╡рдзрд┐ рдХреЗ рджреМрд░рд╛рди рдХреЛрдИ рд╕реНрдХреНрд░реЙрд▓ рдИрд╡реЗрдВрдЯ рдирд╣реАрдВ рд╣реЛрддрд╛ рд╣реИ, рддреЛ рд╣рдо рд╡рд░реНрддрдорд╛рди рдорд╣реАрдиреЗ рдХреЛ рд╕рдВрд░реЗрдЦрд┐рдд рдХрд░рддреЗ рд╣реИрдВред рдЕрдиреНрдпрдерд╛, рд╣рдо рддрдм рддрдХ рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд░рддреЗ рд╣реИрдВ рдЬрдм рддрдХ рдЕрд╡рд╢рд┐рд╖реНрдЯ рд╕реНрдХреНрд░реЙрд▓ рдмрдВрдж рдирд╣реАрдВ рд╣реЛ рдЬрд╛рддрд╛ред рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ, рдЖрдкрдХреЛ takeUntil(touchstart$)
рдХреЛ рдЬреЛрдбрд╝рдиреЗ рдХреА рдЬрд░реВрд░рдд рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдЬрдбрд╝рддреНрд╡реАрдп рд╕реНрдХреНрд░реЙрд▓ рдХреЛ рдПрдХ рдирдП рд╕реНрдкрд░реНрд╢ рдХреЗ рд╕рд╛рде рд░реЛрдХрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ рдФрд░ рдлрд┐рд░ рдкреВрд░реА рдзрд╛рд░рд╛ рдХреЛ рд╢реБрд░реБрдЖрдд рдореЗрдВ рд╡рд╛рдкрд╕ рдЖрдирд╛ рдЪрд╛рд╣рд┐рдП:
const touchstart$ = touchStartFrom(monthsScrollRef.elementRef.nativeElement); const touchend$ = touchEndFrom(monthsScrollRef.elementRef.nativeElement);
рдпрд╣рд╛рдВ рдпрд╣ рдзреНрдпрд╛рди рджрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдП рдХрд┐ scrollToIndex
рд╕реАрдбреАрдХреЗ рдореЗрдВ рд╕реНрдХреНрд░реЙрд▓рд┐рдВрдЧ scrollToIndex
рдХреЗ рд▓рд┐рдП рдПрдХ рджреЗрд╢реА рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдФрд░ рдпрд╣ рд╕рдлрд╛рд░реА рдореЗрдВ рдХрд╛рдо рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИред рдЖрдк рдЗрд╕реЗ рд╕реНрдХреНрд░реЙрд▓ рдХрд░рдХреЗ рд▓рд┐рдЦ рд╕рдХрддреЗ рд╣реИрдВред рд╣рдорд╛рд░реЗ рджреНрд╡рд╛рд░рд╛ рд╕реНрдХреНрд░реЙрд▓ requestAnimationFrame
рдЧрдП рд░рдгрдиреАрддрд┐ рдХреЗ рдЕрдВрджрд░ рд╣рдордиреЗ рдЬреЛ рд░рдгрдиреАрддрд┐ requestAnimationFrame
рд╣реИ, requestAnimationFrame
рдЕрдВрджрд░ requestAnimationFrame
рдорд╛рдзреНрдпрдо рд╕реЗ рдЕрдкрдиреА рдЪрд┐рдХрдиреА рд╕реНрдХреНрд░реЙрд▓ рд▓рд┐рдЦрдХрд░ред
рдирд┐рд╖реНрдХрд░реНрд╖
DI рдФрд░ Angular рдЯреАрдо рдХреА рд╕рдордЭрджрд╛рд░реА рдХреА рдмрджреМрд▓рдд, рд╣рдо рдЕрдкрдиреЗ рд▓рд┐рдП рд╡рд░реНрдЪреБрдЕрд▓ рд╕реНрдХреНрд░реЙрд▓ рдХреЛ рд▓рдЪреАрд▓реЗ рдврдВрдЧ рд╕реЗ рдХрд╕реНрдЯрдорд╛рдЗрдЬрд╝ рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рдереЗред рдкрд╣рд▓реА рдирдЬрд╝рд░ рдореЗрдВ, рдЕрд▓рдЧ-рдЕрд▓рдЧ рдКрдВрдЪрд╛рдЗрдпреЛрдВ рд╡рд╛рд▓реЗ рддрддреНрд╡реЛрдВ рдХреЗ рд▓рд┐рдП рдПрдХ рдЖрднрд╛рд╕реА рд╕реНрдХреНрд░реЙрд▓ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдирд╛ рдПрдХ рдХрдард┐рди рдХрд╛рдо рдХреА рддрд░рд╣ рд▓рдЧрддрд╛ рд╣реИред
рд╣рд╛рд▓рд╛рдВрдХрд┐, рдЬрдм рдкреНрд░рддреНрдпреЗрдХ рддрддреНрд╡ рдХреА рдКрдВрдЪрд╛рдИ рдХреА рдЧрдгрдирд╛ рдХрд░рдирд╛ рд╕рдВрднрд╡ рд╣реИ, рддреЛ рдЖрдкрдХреА рд░рдгрдиреАрддрд┐ рд▓рд┐рдЦрдирд╛ рдХрд╛рдлреА рд╕рд░рд▓ рд╣реЛ рдЧрдпрд╛ред рдореБрдЦреНрдп рдмрд╛рдд рдпрд╣ рд╣реИ рдХрд┐ рдпрд╣ рдЧрдгрдирд╛ рдЬрд▓реНрджреА рд╕реЗ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдЗрд╕реЗ рдЕрдХреНрд╕рд░ рдХрд╣рд╛ рдЬрд╛рдПрдЧрд╛ред рдпрджрд┐ рдЖрдкрдХреЛ рдмрдбрд╝реА рд╕рдВрдЦреНрдпрд╛ рдореЗрдВ рдХрд╛рд░реНрдб рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рдЬрд┐рд╕ рдкрд░ рдЙрдирдХреА рдКрдВрдЪрд╛рдИ рдХреЛ рдкреНрд░рднрд╛рд╡рд┐рдд рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рддрддреНрд╡ рд╣реЛ рд╕рдХрддреЗ рд╣реИрдВ рдпрд╛ рдирд╣реАрдВ рд╣реЛ рд╕рдХрддреЗ рд╣реИрдВ, рддреЛ рдКрдВрдЪрд╛рдИ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдкреНрд░рднрд╛рд╡реА рдПрд▓реНрдЧреЛрд░рд┐рджрдо рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░реЗрдВ рдФрд░ рдЕрдкрдиреА рд░рдгрдиреАрддрд┐ рд▓рд┐рдЦрдиреЗ рд╕реЗ рдбрд░реЛ рдорддред