Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(sbb-clock): treat a specific date consistently #2838

Merged
merged 8 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ snapshots["sbb-clock renders with fixed time DOM"] =
`<sbb-clock
data-initialized=""
now="12:30:00"
style="--sbb-clock-hours-animation-start-angle: 15deg; --sbb-clock-hours-animation-duration: 41400s; --sbb-clock-seconds-animation-start-angle: 0deg; --sbb-clock-seconds-animation-duration: 60s; --sbb-clock-animation-play-state: running;"
style="--sbb-clock-animation-play-state: paused; --sbb-clock-hours-animation-start-angle: 15deg; --sbb-clock-hours-animation-duration: 41400s; --sbb-clock-seconds-animation-start-angle: 0deg; --sbb-clock-seconds-animation-duration: 60s;"
>
</sbb-clock>
`;
Expand Down
180 changes: 91 additions & 89 deletions src/elements/clock/clock.ts
jeripeierSBB marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -86,20 +86,28 @@
/** Move the minutes hand every minute. */
private _handMovement?: ReturnType<typeof setInterval>;

protected override willUpdate(changedProperties: PropertyValues<this>): void {
protected override async willUpdate(changedProperties: PropertyValues<this>): Promise<void> {
super.willUpdate(changedProperties);

if (!isServer && changedProperties.has('now')) {
if (this.now) {
this._removeSecondsAnimationStyles();
this._removeHoursAnimationStyles();
this._stopClock();
} else {
this._startClock();
}
await this._startOrConfigureClock();
}
}

protected override async firstUpdated(changedProperties: PropertyValues<this>): Promise<void> {
super.firstUpdated(changedProperties);

if (!isServer) {
document.addEventListener('visibilitychange', this._handlePageVisibilityChange, false);
await this._startOrConfigureClock();
}
}

public override disconnectedCallback(): void {
super.disconnectedCallback();
this._removeEventListeners();
}

private _handlePageVisibilityChange = async (): Promise<void> => {
if (document.visibilityState === 'hidden') {
await this._stopClock();
Expand All @@ -108,40 +116,51 @@
}
};

private _addEventListeners(): void {
document.addEventListener('visibilitychange', this._handlePageVisibilityChange, false);
private async _startOrConfigureClock(): Promise<void> {
if (this.now) {
await this._stopClock();
this._resetSecondsHandAnimation();
this._setHandsStartingPosition();
} else {
await this._startClock();
}

Check warning on line 126 in src/elements/clock/clock.ts

View check run for this annotation

Codecov / codecov/patch

src/elements/clock/clock.ts#L126

Added line #L126 was not covered by tests
}

private _removeEventListeners(): void {
document?.removeEventListener('visibilitychange', this._handlePageVisibilityChange);
this._clockHandHours?.removeEventListener('animationend', this._moveHoursHandFn);
this._clockHandSeconds?.removeEventListener('animationend', this._moveMinutesHandFn);
clearInterval(this._handMovement);
}
/** Starts the clock by defining the hands starting position then starting the animations. */
private async _startClock(): Promise<void> {
this._clockHandHours?.addEventListener(
'animationend',
this._moveHoursHandFn,
ADD_EVENT_LISTENER_OPTIONS,
);
this._clockHandSeconds?.addEventListener(
'animationend',
this._moveMinutesHandFn,
ADD_EVENT_LISTENER_OPTIONS,
);

private _removeHoursAnimationStyles(): void {
this._clockHandHours?.classList.remove('sbb-clock__hand-hours--initial-hour');
this.style.removeProperty('--sbb-clock-hours-animation-start-angle');
this.style.removeProperty('--sbb-clock-hours-animation-duration');
}
await new Promise(() =>
setTimeout(() => {
this._setHandsStartingPosition();

private _removeSecondsAnimationStyles(): void {
this._clockHandSeconds?.classList.remove('sbb-clock__hand-seconds--initial-minute');
this._clockHandMinutes?.classList.remove('sbb-clock__hand-minutes--no-transition');
this.style.removeProperty('--sbb-clock-seconds-animation-start-angle');
this.style.removeProperty('--sbb-clock-seconds-animation-duration');
this.style?.setProperty('--sbb-clock-animation-play-state', 'running');
}, INITIAL_TIMEOUT_DURATION),
);
}

/** Given the current date, calculates the hh/mm/ss values and the hh/mm/ss left to the next midnight. */
private _assignCurrentTime(): void {
const date = this.now ? null : new Date();
const [hours, minutes, seconds] = date
? [date.getHours(), date.getMinutes(), date.getSeconds()]
: this.now!.split(':').map((p) => +p);
/** Stops the clock by removing all the animations. */
private async _stopClock(): Promise<void> {
clearInterval(this._handMovement);

this._hours = hours % 12;
this._minutes = minutes;
this._seconds = seconds;
this._removeSecondsAnimationStyles();
this._removeHoursAnimationStyles();

this._clockHandHours?.removeEventListener('animationend', this._moveHoursHandFn);
this._clockHandSeconds?.removeEventListener('animationend', this._moveMinutesHandFn);

this._clockHandMinutes?.classList.add('sbb-clock__hand-minutes--no-transition');

this.style?.setProperty('--sbb-clock-animation-play-state', 'paused');
}

/** Set the starting position for the three hands on the clock face. */
Expand Down Expand Up @@ -185,11 +204,22 @@

this._clockHandSeconds?.classList.add('sbb-clock__hand-seconds--initial-minute');
this._clockHandHours?.classList.add('sbb-clock__hand-hours--initial-hour');
this.style?.setProperty('--sbb-clock-animation-play-state', 'running');

this.toggleAttribute('data-initialized', true);
}

/** Given the current date, calculates the hh/mm/ss values and the hh/mm/ss left to the next midnight. */
private _assignCurrentTime(): void {
const date = this.now ? null : new Date();
const [hours, minutes, seconds] = date
? [date.getHours(), date.getMinutes(), date.getSeconds()]
: this.now!.split(':').map((p) => +p);

this._hours = hours % 12;
this._minutes = minutes;
this._seconds = seconds;
}

/** Set the starting position for the minutes hand. */
private _setMinutesHand(): void {
this._clockHandMinutes?.style.setProperty(
Expand Down Expand Up @@ -230,67 +260,39 @@
this._setMinutesHand();
}

/** Stops the clock by removing all the animations. */
private async _stopClock(): Promise<void> {
clearInterval(this._handMovement);

if (this.now) {
this._setHandsStartingPosition();

jeripeierSBB marked this conversation as resolved.
Show resolved Hide resolved
// Wait a tick to before animation is added. Otherwise, the animation gets not completely
// removed which can lead to a mispositioned seconds hand.
await new Promise((resolve) => setTimeout(resolve));

this._clockHandSeconds?.classList.add('sbb-clock__hand-seconds--initial-minute');
this._clockHandHours?.classList.add('sbb-clock__hand-hours--initial-hour');
} else {
this._removeSecondsAnimationStyles();
this._removeHoursAnimationStyles();
/**
* Removing animation by overriding with empty string,
* then triggering a reflow and re add original animation by removing override.
* @private
*/
private _resetSecondsHandAnimation(): void {
if (!this._clockHandSeconds) {
return;

Check warning on line 270 in src/elements/clock/clock.ts

View check run for this annotation

Codecov / codecov/patch

src/elements/clock/clock.ts#L270

Added line #L270 was not covered by tests
}
this._clockHandSeconds.style.animation = '';
// Hack to trigger reflow
this._clockHandSeconds.offsetHeight;
this._clockHandSeconds.style.removeProperty('animation');
}

private _removeEventListeners(): void {
document?.removeEventListener('visibilitychange', this._handlePageVisibilityChange);
this._clockHandHours?.removeEventListener('animationend', this._moveHoursHandFn);
this._clockHandSeconds?.removeEventListener('animationend', this._moveMinutesHandFn);

this._clockHandMinutes?.classList.add('sbb-clock__hand-minutes--no-transition');

this.style?.setProperty('--sbb-clock-animation-play-state', 'paused');
}

/** Starts the clock by defining the hands starting position then starting the animations. */
private async _startClock(): Promise<void> {
this._clockHandHours?.addEventListener(
'animationend',
this._moveHoursHandFn,
ADD_EVENT_LISTENER_OPTIONS,
);
this._clockHandSeconds?.addEventListener(
'animationend',
this._moveMinutesHandFn,
ADD_EVENT_LISTENER_OPTIONS,
);

await new Promise(() =>
setTimeout(() => this._setHandsStartingPosition(), INITIAL_TIMEOUT_DURATION),
);
clearInterval(this._handMovement);
}

protected override async firstUpdated(changedProperties: PropertyValues<this>): Promise<void> {
super.firstUpdated(changedProperties);

if (!isServer) {
this._addEventListeners();

if (this.now) {
await this._stopClock();
} else {
await this._startClock();
}
}
private _removeHoursAnimationStyles(): void {
this._clockHandHours?.classList.remove('sbb-clock__hand-hours--initial-hour');
this.style.removeProperty('--sbb-clock-hours-animation-start-angle');
this.style.removeProperty('--sbb-clock-hours-animation-duration');
}

public override disconnectedCallback(): void {
super.disconnectedCallback();
this._removeEventListeners();
private _removeSecondsAnimationStyles(): void {
this._clockHandSeconds?.classList.remove('sbb-clock__hand-seconds--initial-minute');
this._clockHandMinutes?.classList.remove('sbb-clock__hand-minutes--no-transition');
this.style.removeProperty('--sbb-clock-seconds-animation-start-angle');
this.style.removeProperty('--sbb-clock-seconds-animation-duration');
}

protected override render(): TemplateResult {
Expand Down
Loading