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
4 changes: 2 additions & 2 deletions packages/bbui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
},
"scripts": {
"build": "vite build",
"dev": "vite build --watch --mode=dev"
"dev": "vite build --watch --mode=dev",
"test": "vitest run"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^6.1.3",
Expand Down Expand Up @@ -75,7 +76,6 @@
"@spectrum-css/vars": "3.0.1",
"@supertoolmake/shared-core": "*",
"@supertoolmake/string-templates": "*",
"date-fns": "4.1.0",
"dayjs": "1.10.8",
"easymde": "2.16.1",
"sanitize-html": "2.17.6",
Expand Down
19 changes: 19 additions & 0 deletions packages/bbui/src/Form/Core/DatePicker/utils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { describe, expect, it } from "vitest"
import { getLocaleStartDayOfWeek } from "./utils"

describe("getLocaleStartDayOfWeek", () => {
it("uses the locale's first day of the week", () => {
expect(getLocaleStartDayOfWeek(["en-US"])).toBe("Sunday")
expect(getLocaleStartDayOfWeek(["en-GB"])).toBe("Monday")
expect(getLocaleStartDayOfWeek(["ar-AF"])).toBe("Saturday")
})

it("normalizes locale separators", () => {
expect(getLocaleStartDayOfWeek(["en_US"])).toBe("Sunday")
})

it("falls back for missing or invalid locale metadata", () => {
expect(getLocaleStartDayOfWeek([])).toBe("Monday")
expect(getLocaleStartDayOfWeek(["not-a-locale"])).toBe("Monday")
})
})
82 changes: 28 additions & 54 deletions packages/bbui/src/Form/Core/DatePicker/utils.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import type { Locale } from "date-fns"
import * as dateFnsLocales from "date-fns/locale"

interface Input {
max: number
pad: number
fallback: string
}

interface LocaleWeekInfo {
firstDay?: number
}

interface LocaleWithWeekInfo {
readonly weekInfo?: LocaleWeekInfo
getWeekInfo?: () => LocaleWeekInfo
}

export type Weekday =
| "Sunday"
| "Monday"
Expand Down Expand Up @@ -34,52 +40,6 @@ const normalizeLocaleCode = (code?: string | null) => {
return code.toLowerCase().replace(/_/g, "-")
}

const normalizeLocaleKey = (key?: string) => {
if (!key) {
return null
}
return key
.replace(/_/g, "-")
.replace(/([a-z])([A-Z])/g, "$1-$2")
.toLowerCase()
}

const localeLookup = (() => {
const lookup = new Map<string, Locale>()

const register = (key: string, locale: Locale | undefined) => {
if (!locale) {
return
}
const codeCandidates = new Set<string>()
const normalizedKey = normalizeLocaleKey(key)
if (normalizedKey) {
codeCandidates.add(normalizedKey)
}
const normalizedLocaleCode = normalizeLocaleCode(locale.code)
if (normalizedLocaleCode) {
codeCandidates.add(normalizedLocaleCode)
}
for (const candidate of codeCandidates) {
if (!lookup.has(candidate)) {
lookup.set(candidate, locale)
}
const base = candidate.split("-")[0]
if (base && !lookup.has(base)) {
lookup.set(base, locale)
}
}
}

for (const [key, locale] of Object.entries(dateFnsLocales)) {
if (typeof locale === "object" && locale) {
register(key, locale as Locale)
}
}

return lookup
})()

const getNavigatorLocales = (): readonly string[] => {
if (typeof navigator === "undefined") {
return []
Expand All @@ -90,6 +50,22 @@ const getNavigatorLocales = (): readonly string[] => {
return navigator.language ? [navigator.language] : []
}

const getLocaleWeekInfo = (code: string): LocaleWeekInfo | undefined => {
if (typeof Intl.Locale !== "function") {
return undefined
}

try {
if (Intl.DateTimeFormat.supportedLocalesOf([code]).length === 0) {
return undefined
}
const locale = new Intl.Locale(code) as Intl.Locale & LocaleWithWeekInfo
return locale.weekInfo ?? locale.getWeekInfo?.()
} catch {
return undefined
}
}

export const getLocaleStartDayOfWeek = (
locales: readonly string[] = getNavigatorLocales()
): Weekday => {
Expand All @@ -98,11 +74,9 @@ export const getLocaleStartDayOfWeek = (
if (!normalized) {
continue
}
const match = localeLookup.get(normalized) || localeLookup.get(normalized.split("-")[0])
const weekStartsOn = match?.options?.weekStartsOn
if (typeof weekStartsOn === "number") {
const index = ((weekStartsOn % 7) + 7) % 7
return WEEKDAY_BY_INDEX[index]
const firstDay = getLocaleWeekInfo(normalized)?.firstDay
if (typeof firstDay === "number" && firstDay >= 1 && firstDay <= 7) {
return WEEKDAY_BY_INDEX[firstDay % 7]
}
}
return DEFAULT_WEEKDAY
Expand Down
5 changes: 0 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7607,11 +7607,6 @@ data-view-byte-offset@^1.0.1:
es-errors "^1.3.0"
is-data-view "^1.0.1"

date-fns@4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-4.1.0.tgz#64b3d83fff5aa80438f5b1a633c2e83b8a1c2d14"
integrity sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==

dayjs@1.10.8:
version "1.10.8"
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.8.tgz#267df4bc6276fcb33c04a6735287e3f429abec41"
Expand Down
Loading