-
Notifications
You must be signed in to change notification settings - Fork 3.1k
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
feat: ✨ product shipping countdown timer #3324
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,5 +8,6 @@ | |
}, | ||
"[liquid]": { | ||
"editor.formatOnSave": true | ||
} | ||
}, | ||
"editor.defaultFormatter": "Shopify.theme-check-vscode" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
product-shipping-countdown { | ||
display: flex; | ||
padding: 1rem 1.65rem; | ||
|
||
background: rgba(var(--color-button), 1); | ||
color: rgba(var(--color-button-text), 1); | ||
border-radius: var(--buttons-radius); | ||
font-weight: 600; | ||
justify-content: center; | ||
|
||
&:empty { | ||
display: none; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
class ProductShippingCountdown extends HTMLElement { | ||
constructor() { | ||
super(); | ||
this.intervalId = null; | ||
} | ||
connectedCallback() { | ||
this.data = { | ||
message: this.dataset.message, | ||
timeThreshold: this.dataset.timeThreshold, | ||
timeZone: this.dataset.timeZone, | ||
showOnWeekends: this.dataset.showOnWeekends.toLowerCase() === 'true', | ||
excludedDates: this.dataset.excludedDates.split(',').map(date => date.trim()), | ||
} | ||
|
||
this.updateMessage(); | ||
this.intervalId = setInterval(() => this.updateMessage(), 60000); | ||
} | ||
disconnectedCallback() { | ||
if (this.intervalId) { | ||
clearInterval(this.intervalId); | ||
} | ||
} | ||
get timeThreshold() { | ||
const [hours, minutes] = this.data.timeThreshold.split(':').map(Number) | ||
const timeZone = this.data.timeZone | ||
|
||
const dateOptions = { | ||
timeZone, | ||
year: 'numeric', | ||
month: '2-digit', | ||
day: '2-digit', | ||
hour: '2-digit', | ||
minute: '2-digit', | ||
second: '2-digit', | ||
hour12: false | ||
} | ||
const formatter = new Intl.DateTimeFormat('en-US', dateOptions) | ||
const formattedDate = formatter.format(new Date()) | ||
const dateParts = formattedDate.match(/(\d{2})\/(\d{2})\/(\d{4}), (\d{2}):(\d{2}):(\d{2})/); | ||
|
||
const threshold = new Date(`${dateParts[3]}-${dateParts[1]}-${dateParts[2]}T${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:00Z`); | ||
threshold.setTime(threshold.getTime() + threshold.getTimezoneOffset() * 60 * 1000) | ||
return threshold | ||
} | ||
get isVisible() { | ||
// Returns true if current time is within the threshold | ||
const now = new Date() | ||
|
||
// Weekend check | ||
const isWeekend = now.getDay() === 0 || now.getDay() === 6 | ||
if (isWeekend && !this.data.showOnWeekends) return false | ||
|
||
// Excluded dates check | ||
if (this.data.excludedDates.length) { | ||
const todayFormatted = now.toLocaleDateString('en-US', { month: '2-digit', day: '2-digit' }) | ||
if (this.data.excludedDates.includes(todayFormatted)) return false | ||
} | ||
|
||
return now < this.timeThreshold | ||
} | ||
get messageCountdownVariable() { | ||
// Returns replacement for {countdown} in the message | ||
// {countdown} -> '3 hours, 2 minutes' | ||
const now = new Date() | ||
const diff = this.timeThreshold - now | ||
const hours = Math.floor(diff / (1000 * 60 * 60)) | ||
const minutes = Math.floor((diff / (1000 * 60)) % 60) | ||
|
||
const hoursString = hours > 0 ? `${hours} hour${hours > 1 ? 's' : ''}` : '' | ||
const minutesString = minutes > 0 ? `${minutes} minute${minutes > 1 ? 's' : ''}` : '' | ||
const separator = hoursString && minutesString ? ', ' : '' | ||
return `${hoursString}${separator}${minutesString}` | ||
} | ||
get messageTimeVariable() { | ||
const options = { hour: '2-digit', minute: '2-digit', hour12: true, timeZone: this.data.timeZone }; | ||
const formatter = new Intl.DateTimeFormat('en-US', options); | ||
return formatter.format(this.timeThreshold); | ||
} | ||
get formattedMessage() { | ||
// Returns message with countdown and time variables replaced | ||
const message = this.data.message | ||
.replace('{countdown}', this.messageCountdownVariable) | ||
.replace('{time}', this.messageTimeVariable) | ||
|
||
return message | ||
} | ||
updateMessage() { | ||
console.log('updating message') | ||
if (this.isVisible) { | ||
this.innerText = this.formattedMessage; | ||
} | ||
} | ||
} | ||
|
||
customElements.define('product-shipping-countdown', ProductShippingCountdown) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1955,6 +1955,30 @@ | |
"price": { | ||
"name": "Price" | ||
}, | ||
"shipping_countdown": { | ||
"name": "Shipping countdown", | ||
"settings": { | ||
"message": { | ||
"label": "Message", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Localization quality issue found The following issues may affect the quality of localized translations if they are not addressed:
Please look out for other instances of this issue in your PR and fix them as well if possible. Questions about these messages? Hop in the #help-localization Slack channel. |
||
"info": "{countdown}: Active countdown timer until time threshold.\n{time}: Show time threshold." | ||
}, | ||
"time_threshold": { | ||
"label": "Time threshold", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Localization quality issue found The following issues may affect the quality of localized translations if they are not addressed:
Please look out for other instances of this issue in your PR and fix them as well if possible. Questions about these messages? Hop in the #help-localization Slack channel. |
||
"info": "Time threshold in 24-hour format. Example: 14:00 for 2:00 PM." | ||
}, | ||
"time_zone": { | ||
"label": "Time zone", | ||
"info": "Time zone in [IANA format](https:\/\/en.wikipedia.org\/wiki\/List_of_tz_database_time_zones). Example: America\/Los_Angeles." | ||
}, | ||
"show_on_weekends": { | ||
"label": "Show widget on weekends" | ||
}, | ||
"excluded_dates": { | ||
"label": "Excluded dates", | ||
"info": "Include a list of dates to hide the widget in format DD\/MM separated by commas. E.g. 12\/25, 01\/01." | ||
} | ||
} | ||
}, | ||
"inventory": { | ||
"name": "Inventory status", | ||
"settings": { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Localization quality issue found
The following issues may affect the quality of localized translations if they are not addressed:
Shipping countdown
for keysections.main-product.blocks.shipping_countdown.name
is very short. Short strings are more likely to be misunderstood by translators without context. Please provide additional context for the translators if possible.Please look out for other instances of this issue in your PR and fix them as well if possible.
Questions about these messages? Hop in the #help-localization Slack channel.