Skip to content
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
14 changes: 13 additions & 1 deletion src/BootstrapBlazor/Components/FlipClock/FlipClock.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,22 @@ public partial class FlipClock
/// <summary>
/// <inheritdoc/>
/// </summary>
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, new { Invoke = Interop, OnCompleted = nameof(OnCompleted), ViewMode = ViewMode.ToString(), StartValue = GetTicks() });
protected override Task InvokeInitAsync() => Reset();

private double GetTicks() => StartValue?.TotalMilliseconds ?? 0;

/// <summary>
/// <para lang="zh">重置方法</para>
/// <para lang="en">Reset method</para>
/// </summary>
/// <returns></returns>
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

XML doc has an empty <returns> tag on a new public API (Reset). Please either remove the tag or fill it with the standard Task description (e.g., “A Task representing the asynchronous operation.”) to match other public methods’ docs.

Suggested change
/// <returns></returns>
/// <returns>A Task representing the asynchronous operation.</returns>

Copilot uses AI. Check for mistakes.
public Task Reset() => InvokeVoidAsync("init", Id, Interop, new
{
OnCompleted = nameof(OnCompleted),
ViewMode = ViewMode.ToString(),
StartValue = GetTicks()
});

/// <summary>
/// <para lang="zh">倒计时结束回调方法由 JSInvoke 调用</para>
/// <para lang="en">Countdown Completed Callback Method invoke by JSInvoke</para>
Expand Down
151 changes: 76 additions & 75 deletions src/BootstrapBlazor/Components/FlipClock/FlipClock.razor.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
export function init(id, options) {
export function init(id, invoke, options) {
options = {
...{
viewMode: 'DateTime',
startValue: 0,
onCompleted: null
onCompleted: null,
counter: 0,
totalMilliseconds : 0,
countDown: false
Comment on lines +7 to +9
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

totalMilliseconds : 0 has inconsistent spacing before the colon compared to typical object literal formatting in this file (startValue: 0, onCompleted: null). Consider normalizing to totalMilliseconds: 0 for consistency.

Copilot uses AI. Check for mistakes.
},
...options
}
Expand All @@ -12,90 +15,22 @@
return;
}

let counter = 0;
let totalMilliseconds = 0;
let countDown = false;
const getDate = () => {
const view = options.viewMode;
countDown = false;
if (view === "DateTime") {
const now = new Date();
return {
years: now.getFullYear(),
months: now.getMonth() + 1,
days: now.getDate(),
hours: now.getHours(),
minutes: now.getMinutes(),
seconds: now.getSeconds()
};
}
else if (view === "Count") {
counter += 1000;
totalMilliseconds = counter - options.startValue;
}
else if (view === "CountDown") {
countDown = true;
counter += 1000;
totalMilliseconds = options.startValue - counter;
if (totalMilliseconds < 0) totalMilliseconds = 0;
}

const seconds = Math.floor(totalMilliseconds / 1000) % 60;
const minutes = Math.floor(totalMilliseconds / (1000 * 60)) % 60;
const hours = Math.floor(totalMilliseconds / (1000 * 60 * 60)) % 24;
const days = Math.floor(totalMilliseconds / (1000 * 60 * 60 * 24));
const months = 0;
const years = 0;
return { years, months, days, hours, minutes, seconds };
};

const getConfig = () => [
{ key: 'years', list: el.querySelector('.bb-flip-clock-list.year'), digits: 4 },
{ key: 'months', list: el.querySelector('.bb-flip-clock-list.month'), digits: 2 },
{ key: 'days', list: el.querySelector('.bb-flip-clock-list.day'), digits: 2 },
{ key: 'hours', list: el.querySelector('.bb-flip-clock-list.hour'), digits: 2 },
{ key: 'minutes', list: el.querySelector('.bb-flip-clock-list.minute'), digits: 2 },
{ key: 'seconds', list: el.querySelector('.bb-flip-clock-list.second'), digits: 2 },
];

const setDigits = (list, value, digits, countDown) => {
list.classList.remove('flip');
for (let i = 0; i < digits; i++) {
const place = digits - 1 - i;
const digit = Math.floor(value / 10 ** place) % 10;
setFlip(list.children[i], digit, countDown);
}
list.classList.add('flip');
};

const go = () => {
const d = getDate();
const unitConfig = getConfig();
unitConfig.forEach(({ key, list, digits }) => {
if (list === null) {
return;
}

setDigits(list, d[key], digits, countDown);
});
return d;
};

let start = void 0
let current;

const flip = ts => {
if (start === void 0) {
start = ts;
current = go();
current = go(el, options);
}
const elapsed = ts - start;
if (elapsed >= 1000) {
start = ts;
current = go();
current = go(el, options);
}

if (countDown && current.hours === 0 && current.minutes === 0 && current.seconds === 0) {
options.invoke.invokeMethodAsync(options.onCompleted);
if (options.countDown && current.hours === 0 && current.minutes === 0 && current.seconds === 0) {
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Countdown completion detection should not rely only on hours/minutes/seconds == 0. For durations >= 24h, when totalMilliseconds lands exactly on a day boundary (e.g., 86400000ms), hours/minutes/seconds become 0 while days is still > 0, causing premature completion. Use options.totalMilliseconds <= 0 (or include days/months/years in the check) to decide completion reliably.

Suggested change
if (options.countDown && current.hours === 0 && current.minutes === 0 && current.seconds === 0) {
if (options.countDown && options.totalMilliseconds <= 0) {

Copilot uses AI. Check for mistakes.
invoke.invokeMethodAsync(options.onCompleted);
return;
}
requestAnimationFrame(flip);
Expand All @@ -104,6 +39,72 @@
requestAnimationFrame(flip);
}

const go = (el, options) => {
const d = getDate(options);
const unitConfig = getConfig(el);
unitConfig.forEach(({ key, list, digits }) => {
if (list === null) {
return;
}

setDigits(list, d[key], digits, options.countDown);
});
return d;
};

const getDate = (options) => {
const view = options.viewMode;
options.countDown = false;
if (view === "DateTime") {
const now = new Date();
return {
years: now.getFullYear(),
months: now.getMonth() + 1,
days: now.getDate(),
hours: now.getHours(),
minutes: now.getMinutes(),
seconds: now.getSeconds()
};
}
else if (view === "Count") {
options.counter += 1000;
options.totalMilliseconds = options.counter - options.startValue;
}
else if (view === "CountDown") {
options.countDown = true;
options.counter += 1000;
options.totalMilliseconds = options.startValue - options.counter;
if (options.totalMilliseconds < 0) options.totalMilliseconds = 0;
}

const seconds = Math.floor(options.totalMilliseconds / 1000) % 60;
const minutes = Math.floor(options.totalMilliseconds / (1000 * 60)) % 60;
const hours = Math.floor(options.totalMilliseconds / (1000 * 60 * 60)) % 24;
const days = Math.floor(options.totalMilliseconds / (1000 * 60 * 60 * 24));
const months = 0;
const years = 0;
return { years, months, days, hours, minutes, seconds };
};

const getConfig = el => [
{ key: 'years', list: el.querySelector('.bb-flip-clock-list.year'), digits: 4 },
{ key: 'months', list: el.querySelector('.bb-flip-clock-list.month'), digits: 2 },
{ key: 'days', list: el.querySelector('.bb-flip-clock-list.day'), digits: 2 },
{ key: 'hours', list: el.querySelector('.bb-flip-clock-list.hour'), digits: 2 },
{ key: 'minutes', list: el.querySelector('.bb-flip-clock-list.minute'), digits: 2 },
{ key: 'seconds', list: el.querySelector('.bb-flip-clock-list.second'), digits: 2 },
];

const setDigits = (list, value, digits, countDown) => {
list.classList.remove('flip');
for (let i = 0; i < digits; i++) {
const place = digits - 1 - i;
const digit = Math.floor(value / 10 ** place) % 10;
setFlip(list.children[i], digit, countDown);
}
list.classList.add('flip');
};

const setFlip = (flip, index, countDown) => {
const before = flip.querySelector('.before');
if (before) {
Expand Down
Loading