@mezzanine-ui/core 1.0.3 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,5 +2,6 @@
2
2
  @use './input-trigger-popper' as *;
3
3
 
4
4
  .#{$prefix} {
5
+ pointer-events: auto;
5
6
  z-index: z-index.get(popover);
6
7
  }
@@ -76,9 +76,24 @@ export declare const calendarHalfYearYearsCount = 5;
76
76
  export declare function getYearRange(year: number, mod: number): number[];
77
77
  export declare function getCalendarYearRange(year: number): number[];
78
78
  /**
79
- * Locales that use ISO week (Monday as first day of week).
80
- * These are primarily European and Middle Eastern locales.
81
- * Includes both full locale codes (e.g., 'de-de') and short codes (e.g., 'de').
79
+ * Locales that follow ISO 8601 week rules — Monday is the first day of week
80
+ * AND week 1 contains at least 4 days of the new year (`minimalDays = 4`).
81
+ *
82
+ * Audited against `Intl.Locale#weekInfo` (CLDR). Entries previously listed
83
+ * here that disagree with CLDR were removed:
84
+ * - `pt-pt`, `pt` → Sunday-first per CLDR (was emitting wrong week
85
+ * boundaries for Portuguese)
86
+ * - `he-il`, `he` → Sunday-first per CLDR
87
+ * - `ar-sa`, `ar` → Sunday-first / Saturday-first per CLDR
88
+ * - `en-au`, `en-nz` → Monday-first BUT minimalDays=1, NOT ISO
89
+ * - `ro-ro`, `ro`, `sl-si`, `sl`, `hr-hr`, `hr`, `tr-tr`, `tr`,
90
+ * `uk-ua`, `uk`, `lv-lv`, `lv` → Monday-first BUT minimalDays=1
91
+ *
92
+ * The Temporal adapter consumes `Intl.Locale#weekInfo` directly (see
93
+ * `usesISOWeekRules` in calendarMethodsTemporal). The dayjs/moment
94
+ * adapters still use this static set; the removals above also fix
95
+ * silent format/getWeek divergences in those adapters for the affected
96
+ * locales when CLDR data is the desired source of truth.
82
97
  */
83
98
  export declare const ISO_WEEK_LOCALES: Set<string>;
84
99
  /**
@@ -82,9 +82,24 @@ function getCalendarYearRange(year) {
82
82
  return [start, end];
83
83
  }
84
84
  /**
85
- * Locales that use ISO week (Monday as first day of week).
86
- * These are primarily European and Middle Eastern locales.
87
- * Includes both full locale codes (e.g., 'de-de') and short codes (e.g., 'de').
85
+ * Locales that follow ISO 8601 week rules — Monday is the first day of week
86
+ * AND week 1 contains at least 4 days of the new year (`minimalDays = 4`).
87
+ *
88
+ * Audited against `Intl.Locale#weekInfo` (CLDR). Entries previously listed
89
+ * here that disagree with CLDR were removed:
90
+ * - `pt-pt`, `pt` → Sunday-first per CLDR (was emitting wrong week
91
+ * boundaries for Portuguese)
92
+ * - `he-il`, `he` → Sunday-first per CLDR
93
+ * - `ar-sa`, `ar` → Sunday-first / Saturday-first per CLDR
94
+ * - `en-au`, `en-nz` → Monday-first BUT minimalDays=1, NOT ISO
95
+ * - `ro-ro`, `ro`, `sl-si`, `sl`, `hr-hr`, `hr`, `tr-tr`, `tr`,
96
+ * `uk-ua`, `uk`, `lv-lv`, `lv` → Monday-first BUT minimalDays=1
97
+ *
98
+ * The Temporal adapter consumes `Intl.Locale#weekInfo` directly (see
99
+ * `usesISOWeekRules` in calendarMethodsTemporal). The dayjs/moment
100
+ * adapters still use this static set; the removals above also fix
101
+ * silent format/getWeek divergences in those adapters for the affected
102
+ * locales when CLDR data is the desired source of truth.
88
103
  */
89
104
  const ISO_WEEK_LOCALES = new Set([
90
105
  'de-de',
@@ -99,8 +114,6 @@ const ISO_WEEK_LOCALES = new Set([
99
114
  'nl',
100
115
  'pl-pl',
101
116
  'pl',
102
- 'pt-pt',
103
- 'pt',
104
117
  'ru-ru',
105
118
  'ru',
106
119
  'sv-se',
@@ -115,35 +128,17 @@ const ISO_WEEK_LOCALES = new Set([
115
128
  'cs',
116
129
  'hu-hu',
117
130
  'hu',
118
- 'ro-ro',
119
- 'ro',
120
131
  'sk-sk',
121
132
  'sk',
122
- 'sl-si',
123
- 'sl',
124
- 'hr-hr',
125
- 'hr',
126
133
  'bg-bg',
127
134
  'bg',
128
135
  'el-gr',
129
136
  'el',
130
- 'tr-tr',
131
- 'tr',
132
- 'uk-ua',
133
- 'uk',
134
- 'he-il',
135
- 'he',
136
- 'ar-sa',
137
- 'ar',
138
137
  'et-ee',
139
138
  'et',
140
- 'lv-lv',
141
- 'lv',
142
139
  'lt-lt',
143
140
  'lt',
144
141
  'en-gb',
145
- 'en-au',
146
- 'en-nz',
147
142
  ]);
148
143
  /**
149
144
  * Check if a locale uses ISO week (Monday as first day of week)
@@ -150,6 +150,17 @@ const getActualFirstDayOfWeek = (locale) => {
150
150
  const isMondayFirst = (locale) => {
151
151
  return getActualFirstDayOfWeek(locale) === 1;
152
152
  };
153
+ /**
154
+ * Whether the locale should be week-numbered using the ISO 8601 algorithm.
155
+ * Requires BOTH Monday-first AND `isISOWeekLocale` (i.e. minimalDays=4 per
156
+ * CLDR). For Monday-first locales whose CLDR `minimalDays` is 1 (en-AU,
157
+ * en-NZ, ro-RO, tr-TR, sl-SI, hr-HR, uk-UA, lv-LV …) this returns false so
158
+ * `getWeek`/`getWeekYear`/format `gggg/ww` use locale week numbering and
159
+ * stay consistent with `getDefaultModeFormat`.
160
+ */
161
+ const usesISOWeekRules = (locale) => {
162
+ return isMondayFirst(locale) && isISOWeekLocale(locale);
163
+ };
153
164
  const CalendarMethodsDayjs = {
154
165
  /** Locale helpers */
155
166
  getFirstDayOfWeek: (locale) => getActualFirstDayOfWeek(locale),
@@ -161,16 +172,36 @@ const CalendarMethodsDayjs = {
161
172
  getHour: (date) => dayjs(date).hour(),
162
173
  getDate: (date) => dayjs(date).date(),
163
174
  getWeek: (date, locale) => {
164
- if (isMondayFirst(locale)) {
175
+ if (usesISOWeekRules(locale)) {
165
176
  return dayjs(date).isoWeek();
166
177
  }
167
- return dayjs(date).week();
178
+ // `.week()` reads the dayjs instance's locale (firstDayOfWeek + doy).
179
+ // Apply the requested locale explicitly — otherwise the global default
180
+ // (usually `en` Sunday-first) would be used, breaking Monday-first
181
+ // non-ISO locales such as en-AU / ro-RO.
182
+ return dayjs(date).locale(localeMapping(locale)).week();
168
183
  },
169
184
  getWeekYear: (date, locale) => {
170
- if (isMondayFirst(locale)) {
185
+ if (usesISOWeekRules(locale)) {
171
186
  return dayjs(date).isoWeekYear();
172
187
  }
173
- return dayjs(date).year(); // dayjs doesn't have weekYear, use year as approximation
188
+ // dayjs has no native locale `.weekYear()`. Use the same heuristic as
189
+ // moment.js: when the date sits on a year boundary that crosses week
190
+ // boundaries, the weekYear differs from the calendar year.
191
+ // • early-January (month=0) with week >= 52 → previous calendar's
192
+ // final week. weekYear = year - 1.
193
+ // • late-December (month=11) with week === 1 → next calendar's first
194
+ // week. weekYear = year + 1.
195
+ // • otherwise weekYear = year.
196
+ const d = dayjs(date).locale(localeMapping(locale));
197
+ const week = d.week();
198
+ const month = d.month();
199
+ const year = d.year();
200
+ if (month === 0 && week >= 52)
201
+ return year - 1;
202
+ if (month === 11 && week === 1)
203
+ return year + 1;
204
+ return year;
174
205
  },
175
206
  getWeekDay: (date) => {
176
207
  const clone = dayjs(date).locale('en');
@@ -223,7 +254,7 @@ const CalendarMethodsDayjs = {
223
254
  setDate: (date, target) => dayjs(date).date(target).toISOString(),
224
255
  startOf: (target, granularity) => dayjs(target).startOf(granularity).toISOString(),
225
256
  getCurrentWeekFirstDate: (value, locale) => {
226
- if (isMondayFirst(locale)) {
257
+ if (usesISOWeekRules(locale)) {
227
258
  return dayjs(value)
228
259
  .startOf('isoWeek')
229
260
  .hour(0)
@@ -232,7 +263,10 @@ const CalendarMethodsDayjs = {
232
263
  .millisecond(0)
233
264
  .toISOString();
234
265
  }
266
+ // `.startOf('week')` honors the instance locale's firstDayOfWeek, so
267
+ // apply the requested locale before truncating.
235
268
  return dayjs(value)
269
+ .locale(localeMapping(locale))
236
270
  .startOf('week')
237
271
  .hour(0)
238
272
  .minute(0)
@@ -314,18 +348,23 @@ const CalendarMethodsDayjs = {
314
348
  isBetween: (value, target1, target2, granularity) => dayjs(value).isBetween(target1, target2, granularity),
315
349
  isSameDate: (dateOne, dateTwo) => dayjs(dateOne).isSame(dayjs(dateTwo), 'date'),
316
350
  isSameWeek: (dateOne, dateTwo, locale) => {
317
- if (isMondayFirst(locale)) {
351
+ if (usesISOWeekRules(locale)) {
318
352
  return dayjs(dateOne).isSame(dayjs(dateTwo), 'isoWeek');
319
353
  }
320
- return dayjs(dateOne).isSame(dayjs(dateTwo), 'week');
354
+ // `'week'` granularity uses the calling instance's locale — set locale
355
+ // on the comparator so non-ISO Monday-first locales align correctly.
356
+ return dayjs(dateOne)
357
+ .locale(localeMapping(locale))
358
+ .isSame(dayjs(dateTwo), 'week');
321
359
  },
322
360
  isInMonth: (target, month) => dayjs(target).month() === month,
323
361
  isDateIncluded: (date, targets) => targets.some((target) => dayjs(date).isSame(dayjs(target), 'day')),
324
362
  isWeekIncluded: (firstDateOfWeek, targets, locale) => {
325
- if (isMondayFirst(locale)) {
363
+ if (usesISOWeekRules(locale)) {
326
364
  return targets.some((target) => dayjs(firstDateOfWeek).isSame(dayjs(target), 'isoWeek'));
327
365
  }
328
- return targets.some((target) => dayjs(firstDateOfWeek).isSame(dayjs(target), 'week'));
366
+ const mappedLocale = localeMapping(locale);
367
+ return targets.some((target) => dayjs(firstDateOfWeek).locale(mappedLocale).isSame(dayjs(target), 'week'));
329
368
  },
330
369
  isMonthIncluded: (date, targets) => targets.some((target) => dayjs(date).isSame(dayjs(target), 'month')),
331
370
  isYearIncluded: (date, targets) => targets.some((target) => dayjs(date).isSame(dayjs(target), 'year')),
@@ -353,9 +392,12 @@ const CalendarMethodsDayjs = {
353
392
  // ISO values because dayjs does not expose a locale `weekYear()`).
354
393
  // - GGGG / WW: ISO-8601 week year / number regardless of locale.
355
394
  if (format.includes('gggg') || format.includes('GGGG')) {
356
- const isMon = isMondayFirst(locale);
357
- const localeYear = isMon ? result.isoWeekYear() : result.year();
358
- const localeWeek = isMon ? result.isoWeek() : result.week();
395
+ // Use ISO values for `gggg`/`ww` only when the locale truly uses
396
+ // ISO 8601 week rules. Monday-first non-ISO locales (e.g. en-AU,
397
+ // ro-RO) must emit locale-week values so format and getWeek agree.
398
+ const useISO = usesISOWeekRules(locale);
399
+ const localeYear = useISO ? result.isoWeekYear() : result.year();
400
+ const localeWeek = useISO ? result.isoWeek() : result.week();
359
401
  const isoYear = result.isoWeekYear();
360
402
  const isoWeek = result.isoWeek();
361
403
  return format
@@ -17,6 +17,17 @@ const getActualFirstDayOfWeek = (locale) => {
17
17
  const isMondayFirst = (locale) => {
18
18
  return getActualFirstDayOfWeek(locale) === 1;
19
19
  };
20
+ /**
21
+ * Whether the locale should be week-numbered using the ISO 8601 algorithm.
22
+ * Requires BOTH Monday-first AND `isISOWeekLocale` (i.e. minimalDays=4 per
23
+ * CLDR). For Monday-first locales whose CLDR `minimalDays` is 1 (en-AU,
24
+ * en-NZ, ro-RO, tr-TR, sl-SI, hr-HR, uk-UA, lv-LV …) this returns false so
25
+ * `getWeek`/`getWeekYear` use locale week numbering and stay consistent
26
+ * with `getDefaultModeFormat`.
27
+ */
28
+ const usesISOWeekRules = (locale) => {
29
+ return isMondayFirst(locale) && isISOWeekLocale(locale);
30
+ };
20
31
  const CalendarMethodsMoment = {
21
32
  /** Get date infos */
22
33
  getNow: () => moment().toISOString(),
@@ -25,16 +36,20 @@ const CalendarMethodsMoment = {
25
36
  getHour: (date) => moment(date).hour(),
26
37
  getDate: (date) => moment(date).date(),
27
38
  getWeek: (date, locale) => {
28
- if (isMondayFirst(locale)) {
39
+ if (usesISOWeekRules(locale)) {
29
40
  return moment(date).isoWeek();
30
41
  }
31
- return moment(date).week();
42
+ // `.week()` reads the moment instance's locale (firstDayOfWeek + doy).
43
+ // Apply the requested locale explicitly — otherwise the global default
44
+ // (usually `en-US` Sunday-first) would be used, breaking Monday-first
45
+ // non-ISO locales such as ro-RO / tr-TR.
46
+ return moment(date).locale(locale).week();
32
47
  },
33
48
  getWeekYear: (date, locale) => {
34
- if (isMondayFirst(locale)) {
49
+ if (usesISOWeekRules(locale)) {
35
50
  return moment(date).isoWeekYear();
36
51
  }
37
- return moment(date).weekYear();
52
+ return moment(date).locale(locale).weekYear();
38
53
  },
39
54
  getWeekDay: (date) => {
40
55
  const clone = moment(date).locale('en_US');
@@ -127,10 +142,9 @@ const CalendarMethodsMoment = {
127
142
  },
128
143
  startOf: (target, granularity) => moment(target).startOf(granularity).toISOString(),
129
144
  getCurrentWeekFirstDate: (value, locale) => {
130
- const m = moment(value);
131
- if (isMondayFirst(locale)) {
132
- // Monday-first: starts on Monday
133
- return m
145
+ if (usesISOWeekRules(locale)) {
146
+ // Monday-first AND ISO: starts on Monday using ISO week.
147
+ return moment(value)
134
148
  .startOf('isoWeek')
135
149
  .hour(0)
136
150
  .minute(0)
@@ -138,8 +152,10 @@ const CalendarMethodsMoment = {
138
152
  .millisecond(0)
139
153
  .toISOString();
140
154
  }
141
- // Locale week: starts on Sunday (for en-us, zh-tw, etc.)
142
- return m
155
+ // Otherwise: locale-week start. `.startOf('week')` uses the instance
156
+ // locale's firstDayOfWeek, so apply the requested locale first.
157
+ return moment(value)
158
+ .locale(locale)
143
159
  .startOf('week')
144
160
  .hour(0)
145
161
  .minute(0)
@@ -220,18 +236,19 @@ const CalendarMethodsMoment = {
220
236
  isBetween: (value, target1, target2, granularity) => moment(value).isBetween(target1, target2, granularity),
221
237
  isSameDate: (dateOne, dateTwo) => moment(dateOne).isSame(moment(dateTwo), 'date'),
222
238
  isSameWeek: (dateOne, dateTwo, locale = 'en-us') => {
223
- if (isMondayFirst(locale)) {
239
+ if (usesISOWeekRules(locale)) {
224
240
  return moment(dateOne).isSame(moment(dateTwo), 'isoWeek');
225
241
  }
226
- return moment(dateOne).isSame(moment(dateTwo), 'week');
242
+ // `'week'` granularity uses the calling instance's locale.
243
+ return moment(dateOne).locale(locale).isSame(moment(dateTwo), 'week');
227
244
  },
228
245
  isInMonth: (target, month) => moment(target).month() === month,
229
246
  isDateIncluded: (date, targets) => targets.some((target) => moment(date).isSame(moment(target), 'day')),
230
247
  isWeekIncluded: (firstDateOfWeek, targets, locale = 'en-us') => {
231
- if (isMondayFirst(locale)) {
248
+ if (usesISOWeekRules(locale)) {
232
249
  return targets.some((target) => moment(firstDateOfWeek).isSame(moment(target), 'isoWeek'));
233
250
  }
234
- return targets.some((target) => moment(firstDateOfWeek).isSame(moment(target), 'week'));
251
+ return targets.some((target) => moment(firstDateOfWeek).locale(locale).isSame(moment(target), 'week'));
235
252
  },
236
253
  isMonthIncluded: (date, targets) => targets.some((target) => moment(date).isSame(moment(target), 'month')),
237
254
  isYearIncluded: (date, targets) => targets.some((target) => moment(date).isSame(moment(target), 'year')),
@@ -0,0 +1,27 @@
1
+ /**
2
+ * CalendarMethodsTemporal — JS-native Temporal implementation of CalendarMethods.
3
+ *
4
+ * Wire format: ISO 8601 string (matches the moment/dayjs adapters, NOT the
5
+ * `Temporal.PlainDateTime#toString()` form). The internal representation is
6
+ * `Temporal.ZonedDateTime` in the system time zone, mirroring how dayjs/moment
7
+ * default to local time when reading/writing ISO strings.
8
+ *
9
+ * Locale week support: uses `Intl.Locale(locale).weekInfo` (Stage 3 / widely
10
+ * available) to determine the first day of week and minimal days in week 1.
11
+ * Falls back to the static ISO_WEEK_LOCALES set when `weekInfo` is missing
12
+ * from the runtime.
13
+ *
14
+ * Polyfill: this module relies on `globalThis.Temporal`. Apps that need to
15
+ * support Safari or Node SSR must install `@js-temporal/polyfill` and
16
+ * register it on `globalThis` from a Client Component (or equivalent client
17
+ * entry point) BEFORE this module is imported:
18
+ *
19
+ * import { Temporal } from '@js-temporal/polyfill';
20
+ * (globalThis as { Temporal?: unknown }).Temporal = Temporal;
21
+ *
22
+ * If Temporal is not present at the time any method is invoked, the call
23
+ * throws with the same installation guide.
24
+ */
25
+ import { CalendarMethods as CalendarMethodsType } from '../calendar/typings';
26
+ declare const CalendarMethodsTemporal: CalendarMethodsType;
27
+ export default CalendarMethodsTemporal;