mnfst 0.5.121 → 0.5.122

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.
@@ -1,11 +1,17 @@
1
1
  /* Manifest Data Sources - Configuration */
2
2
 
3
- // Load manifest if not already loaded (loader may set __manifestLoaded / registry.manifest)
3
+ // Load manifest if not already loaded (loader may set __manifestLoaded / registry.manifest).
4
+ // Only trust a cached global if it looks like a real Manifest config — another plugin
5
+ // doing `window.__manifestLoaded.foo = …` before the loader populates it (or in a
6
+ // no-loader setup) can leave a stub without `data`/`components`, which would otherwise
7
+ // mask every data source. Fall through to fetching the real manifest in that case.
4
8
  async function ensureManifest() {
5
- if (window.ManifestComponentsRegistry?.manifest) {
9
+ const looksComplete = (m) => m && typeof m === 'object' &&
10
+ (m.data || m.components || m.preloadedComponents || m.appwrite || m.render);
11
+ if (looksComplete(window.ManifestComponentsRegistry?.manifest)) {
6
12
  return window.ManifestComponentsRegistry.manifest;
7
13
  }
8
- if (window.__manifestLoaded) {
14
+ if (looksComplete(window.__manifestLoaded)) {
9
15
  return window.__manifestLoaded;
10
16
  }
11
17
 
@@ -11423,10 +11429,19 @@ async function loadDataSource(dataSourceName, locale = 'en') {
11423
11429
  const cacheKey = `${dataSourceName}:${locale}`;
11424
11430
  const { dataSourceCache, loadingPromises, isInitializing, updateStore } = window.ManifestDataStore;
11425
11431
 
11426
- // Check memory cache first
11432
+ // Check memory cache first. The store write is guarded against stale
11433
+ // locales: a caller that resolved its locale before a switch (or an effect
11434
+ // re-running mid-switch) can request `name:en` after the locale-change
11435
+ // reload already wrote `name:fr` — serving the cached data is fine, but
11436
+ // writing it to the live store would clobber the current locale. Localized
11437
+ // data carries a `_locale` stamp; unstamped (non-localized) data writes
11438
+ // unconditionally.
11427
11439
  if (dataSourceCache.has(cacheKey)) {
11428
11440
  const cachedData = dataSourceCache.get(cacheKey);
11429
- if (!isInitializing) {
11441
+ const liveLocale = (window.Alpine && Alpine.store('locale')?.current)
11442
+ || document.documentElement.lang || locale;
11443
+ const staleLocaleHit = locale !== liveLocale && !!(cachedData && cachedData._locale);
11444
+ if (!isInitializing && !staleLocaleHit) {
11430
11445
  updateStore(dataSourceName, cachedData, { loading: false, error: null, ready: true });
11431
11446
  }
11432
11447
  return cachedData;
@@ -11691,12 +11706,25 @@ async function loadDataSource(dataSourceName, locale = 'en') {
11691
11706
  enhancedData = [];
11692
11707
  }
11693
11708
 
11694
- // Update cache (store unsealed version for our use)
11709
+ // Update cache (store unsealed version for our use).
11710
+ // Always safe — the cache key carries the locale this load was for.
11695
11711
  dataSourceCache.set(cacheKey, enhancedData);
11696
11712
 
11713
+ // Stale-locale guard: if the app's locale changed while this load was
11714
+ // in flight, a LOCALIZED source's result is stale — the locale-change
11715
+ // listener has already reloaded (or is reloading) the right locale,
11716
+ // and writing this one would clobber it. Non-localized sources are
11717
+ // locale-independent and must still write (the listener never reloads
11718
+ // them, so skipping would orphan an initial load).
11719
+ const localeSensitive = !!(dataSource && typeof dataSource === 'object'
11720
+ && (dataSource.locales || dataSource[locale]));
11721
+ const liveLocale = (window.Alpine && Alpine.store('locale')?.current)
11722
+ || document.documentElement.lang || locale;
11723
+ const staleLocale = localeSensitive && liveLocale !== locale;
11724
+
11697
11725
  // Update store only if not initializing
11698
11726
  // Note: updateStore will seal the data to prevent Alpine from proxying it
11699
- if (!isInitializing) {
11727
+ if (!isInitializing && !staleLocale) {
11700
11728
  updateStore(dataSourceName, enhancedData, { loading: false, error: null, ready: true });
11701
11729
  }
11702
11730
 
@@ -0,0 +1,504 @@
1
+ /* Manifest Date Picker */
2
+
3
+ @layer utilities {
4
+
5
+ :where([x-date], .date-picker):not(.unstyle) {
6
+ width: fit-content;
7
+ min-width: 17rem;
8
+ background: var(--color-popover-surface, oklch(98.5% 0 0));
9
+ color: var(--color-content-stark, oklch(20.5% 0 0));
10
+ border: 1px solid var(--color-line, color-mix(oklch(20.5% 0 0) 10%, transparent));
11
+ border-radius: var(--radius, 0.5rem);
12
+
13
+ /* Shell */
14
+ &>[role=group] {
15
+ display: flex;
16
+ flex-direction: column;
17
+ width: 100%;
18
+ padding: 0.5rem;
19
+ }
20
+
21
+ /* Header */
22
+ & header {
23
+ display: flex;
24
+ align-items: center;
25
+ justify-content: space-between;
26
+ gap: 0.25rem;
27
+ width: 100%;
28
+
29
+ & button {
30
+ width: var(--spacing-field-height, 2.25rem);
31
+ min-width: var(--spacing-field-height, 2.25rem);
32
+ height: var(--spacing-field-height, 2.25rem);
33
+ display: inline-flex;
34
+ align-items: center;
35
+ justify-content: center;
36
+ padding: 0;
37
+ background: transparent;
38
+ border: none;
39
+ border-radius: var(--radius, 0.5rem);
40
+ color: var(--color-content-neutral, oklch(43.9% 0 0));
41
+ cursor: pointer;
42
+ transition: var(--transition, all .05s ease-in-out);
43
+
44
+ &:hover {
45
+ background: var(--color-field-surface, color-mix(oklch(20.5% 0 0) 10%, transparent));
46
+ color: var(--color-content-stark, oklch(20.5% 0 0))
47
+ }
48
+ }
49
+
50
+ /* Heading (between nav arrows) — clickable view jump */
51
+ & button:nth-child(2) {
52
+ flex: 1;
53
+ width: auto;
54
+ padding: 0 0.5rem;
55
+ text-align: center;
56
+ font-weight: 600;
57
+ color: inherit
58
+ }
59
+
60
+ /* Previous / next chevrons — baked-in masks, no icons plugin */
61
+ & button:first-child::before,
62
+ & button:last-child::before {
63
+ content: "";
64
+ width: 1rem;
65
+ height: 1rem;
66
+ background-color: currentColor;
67
+ mask-repeat: no-repeat;
68
+ mask-size: 100% 100%
69
+ }
70
+
71
+ & button:first-child::before {
72
+ mask-image: var(--icon-previous, url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m15 18-6-6 6-6'/%3E%3C/svg%3E"))
73
+ }
74
+
75
+ & button:last-child::before {
76
+ mask-image: var(--icon-next, url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m9 18 6-6-6-6'/%3E%3C/svg%3E"))
77
+ }
78
+ }
79
+
80
+ /* Months row — one <section> per visible month, side by side */
81
+ & div:has(> section) {
82
+ display: flex;
83
+ flex-flow: row nowrap;
84
+ gap: 1rem
85
+ }
86
+
87
+ & section {
88
+ flex: 1;
89
+ min-width: 14rem;
90
+
91
+ /* Month label (multi-month) */
92
+ &>small {
93
+ display: block;
94
+ padding: 0.25rem 0;
95
+ text-align: center;
96
+ font-size: 0.8125rem;
97
+ font-weight: 600;
98
+ color: var(--color-content-neutral, oklch(43.9% 0 0))
99
+ }
100
+ }
101
+
102
+ /* Day grid */
103
+ & [role=grid] {
104
+ display: grid;
105
+ grid-template-columns: repeat(7, minmax(0, 1fr));
106
+ gap: 0;
107
+ padding: 0.5rem 0;
108
+
109
+ /* Day names */
110
+ & abbr {
111
+ display: flex;
112
+ align-items: center;
113
+ justify-content: center;
114
+ height: 1.75rem;
115
+ font-size: 0.75rem;
116
+ font-weight: 500;
117
+ text-decoration: none;
118
+ color: var(--color-content-subtle, oklch(55.6% 0 0))
119
+ }
120
+
121
+ /* Day buttons */
122
+ & button {
123
+ display: flex;
124
+ align-items: center;
125
+ justify-content: center;
126
+ aspect-ratio: 1 / 1;
127
+ padding: 0;
128
+ font-size: 0.875rem;
129
+ background: transparent;
130
+ color: var(--color-content-stark, oklch(20.5% 0 0));
131
+ border: 1px solid transparent;
132
+ border-radius: var(--radius, 0.5rem);
133
+ cursor: pointer;
134
+ transition: var(--transition, all .05s ease-in-out);
135
+
136
+ &:hover:not(:disabled):not([aria-selected=true]) {
137
+ background: var(--color-field-surface, color-mix(oklch(20.5% 0 0) 10%, transparent))
138
+ }
139
+
140
+ &:focus-visible {
141
+ outline: 2px solid var(--color-brand-content, oklch(60% 0.13 80));
142
+ outline-offset: -2px
143
+ }
144
+
145
+ /* Other-month days */
146
+ &.outside {
147
+ color: var(--color-content-subtle, oklch(55.6% 0 0));
148
+ opacity: 0.55
149
+ }
150
+
151
+ /* Today marker */
152
+ &[aria-current=date]:not([aria-selected=true]) {
153
+ border-color: var(--color-line, color-mix(oklch(20.5% 0 0) 18%, transparent));
154
+ font-weight: 600
155
+ }
156
+
157
+ /* Selected day (also range endpoints) */
158
+ &[aria-selected=true] {
159
+ background: var(--color-brand-surface, var(--color-accent-surface, oklch(20.5% 0 0)));
160
+ color: var(--color-brand-inverse, var(--color-accent-inverse, oklch(98.5% 0 0)));
161
+ font-weight: 600
162
+ }
163
+
164
+ /* Days between range endpoints */
165
+ &.in-range {
166
+ background: color-mix(in oklch, var(--color-brand-surface, var(--color-accent-surface, oklch(20.5% 0 0))) 30%, transparent);
167
+ border-radius: 0
168
+ }
169
+
170
+ /* Square inner edges so endpoints connect to the band */
171
+ &.range-start:not(.range-end) {
172
+ border-start-end-radius: 0;
173
+ border-end-end-radius: 0
174
+ }
175
+
176
+ &.range-end:not(.range-start) {
177
+ border-start-start-radius: 0;
178
+ border-end-start-radius: 0
179
+ }
180
+
181
+ &:disabled {
182
+ color: var(--color-content-subtle, oklch(55.6% 0 0));
183
+ opacity: 0.35;
184
+ cursor: not-allowed;
185
+ text-decoration: line-through
186
+ }
187
+ }
188
+ }
189
+
190
+ /* Month / year jump grid */
191
+ & [role=listbox] {
192
+ display: grid;
193
+ grid-template-columns: repeat(4, minmax(0, 1fr));
194
+ gap: 0.25rem;
195
+ width: 100%;
196
+ padding: 0.25rem 0;
197
+
198
+ & button {
199
+ display: flex;
200
+ align-items: center;
201
+ justify-content: center;
202
+ height: 2.75rem;
203
+ width: 100%;
204
+ padding: 0;
205
+ font-size: 0.875rem;
206
+ background: transparent;
207
+ color: var(--color-content-stark, oklch(20.5% 0 0));
208
+ border: 1px solid transparent;
209
+ border-radius: var(--radius, 0.5rem);
210
+ cursor: pointer;
211
+ transition: var(--transition, all .05s ease-in-out);
212
+
213
+ &:hover:not(:disabled) {
214
+ background: var(--color-field-surface, color-mix(oklch(20.5% 0 0) 10%, transparent))
215
+ }
216
+
217
+ &[aria-current=true] {
218
+ border-color: var(--color-line, color-mix(oklch(20.5% 0 0) 18%, transparent));
219
+ font-weight: 600
220
+ }
221
+
222
+ &[aria-selected=true] {
223
+ background: var(--color-brand-surface, var(--color-accent-surface, oklch(20.5% 0 0)));
224
+ color: var(--color-brand-inverse, var(--color-accent-inverse, oklch(98.5% 0 0)));
225
+ font-weight: 600
226
+ }
227
+
228
+ &.outside {
229
+ color: var(--color-content-subtle, oklch(55.6% 0 0));
230
+ opacity: 0.55
231
+ }
232
+
233
+ &:disabled {
234
+ opacity: 0.35;
235
+ cursor: not-allowed
236
+ }
237
+ }
238
+ }
239
+
240
+ /* Time-of-day row */
241
+ & fieldset {
242
+ display: flex;
243
+ flex-direction: row;
244
+ justify-content: space-between;
245
+ align-items: center;
246
+ gap: 0.5rem;
247
+ width: 100%;
248
+ min-width: 0;
249
+ padding-top: 0.5rem;
250
+ padding-bottom: 0.5rem;
251
+ padding-inline-start: 0.5rem;
252
+ border: none;
253
+ border-top: 1px solid var(--color-line, color-mix(oklch(20.5% 0 0) 10%, transparent));
254
+
255
+ /* "Time" label — native invoker for the columns popover (for= the
256
+ chevron button), so it needs width/height resets against
257
+ dropdown.css's menu-item label styling */
258
+ &>label {
259
+ flex: none;
260
+ width: auto;
261
+ min-height: 0;
262
+ padding: 0;
263
+ font-size: 0.75rem;
264
+ color: var(--color-content-subtle, oklch(55.6% 0 0));
265
+ cursor: pointer;
266
+
267
+ &:hover {
268
+ color: var(--color-content-stark, oklch(20.5% 0 0));
269
+ background: transparent
270
+ }
271
+ }
272
+
273
+ /* Segment group */
274
+ &>[role=group] {
275
+ display: flex;
276
+ align-items: center;
277
+ justify-content: end;
278
+ height: var(--spacing-field-height, 2.25rem);
279
+ padding-inline-start: 0.5rem;
280
+ background: transparent;
281
+ border: none;
282
+ border-radius: var(--radius, 0.5rem);
283
+ cursor: text;
284
+ transition: var(--transition, all .05s ease-in-out);
285
+
286
+ /* Hour / minute segments */
287
+ & input {
288
+ box-sizing: content-box;
289
+ width: 2ch;
290
+ min-width: 0;
291
+ margin: 0;
292
+ padding: 0 0.25rem;
293
+ font-size: 0.875rem;
294
+ line-height: 1.5rem;
295
+ font-variant-numeric: tabular-nums;
296
+ text-align: center;
297
+ background: transparent;
298
+ color: var(--color-content-stark, oklch(20.5% 0 0));
299
+ border: none;
300
+ border-radius: calc(var(--radius, 0.5rem) / 2);
301
+ outline: none;
302
+ appearance: none;
303
+
304
+ &:hover,
305
+ &:focus {
306
+ background: var(--color-field-surface, color-mix(oklch(20.5% 0 0) 10%, transparent))
307
+ }
308
+ }
309
+
310
+ /* Separator */
311
+ &>span {
312
+ line-height: 1.5rem;
313
+ color: var(--color-content-subtle, oklch(55.6% 0 0))
314
+ }
315
+
316
+ /* AM/PM toggle */
317
+ &>button {
318
+ width: auto;
319
+ min-width: 0;
320
+ height: 1.5rem;
321
+ padding: 0 0.375rem;
322
+ font-size: 0.875rem;
323
+ line-height: 1.5rem;
324
+ background: transparent;
325
+ color: var(--color-content-stark, oklch(20.5% 0 0));
326
+ border: none;
327
+ border-radius: calc(var(--radius, 0.5rem) / 2);
328
+ cursor: pointer;
329
+ transition: var(--transition, all .05s ease-in-out);
330
+
331
+ &:hover {
332
+ background: var(--color-field-surface, color-mix(oklch(20.5% 0 0) 10%, transparent))
333
+ }
334
+ }
335
+
336
+ /* Chevron that invokes the columns popover — same glyph as
337
+ select buttons (see buttons.css ::picker-icon) */
338
+ &>button[popovertarget] {
339
+ display: inline-flex;
340
+ align-items: center;
341
+ justify-content: center;
342
+ width: 1.5rem;
343
+ padding: 0;
344
+ color: var(--color-content-subtle, oklch(55.6% 0 0));
345
+
346
+ &:hover {
347
+ color: var(--color-content-stark, oklch(20.5% 0 0))
348
+ }
349
+
350
+ &::before {
351
+ content: "⌄";
352
+ height: calc(var(--spacing-field-height, 2.25rem) * 0.5);
353
+ transform: scaleY(0.7);
354
+ font-size: 20px;
355
+ line-height: 0.4
356
+ }
357
+ }
358
+ }
359
+ }
360
+
361
+ /* Standalone time picker — columns inline in the calendar */
362
+ & div.time-options {
363
+ display: flex;
364
+ justify-content: center;
365
+ width: 100%;
366
+ max-height: 14rem;
367
+ }
368
+
369
+ /* Footer */
370
+ & footer {
371
+ display: flex;
372
+ align-items: center;
373
+ justify-content: space-between;
374
+ gap: 0.25rem;
375
+ width: 100%;
376
+ padding: 0.5rem 0;
377
+ border-top: 1px solid var(--color-line, color-mix(oklch(20.5% 0 0) 10%, transparent));
378
+
379
+ /* Today | Clear buttons */
380
+ & button {
381
+ width: fit-content
382
+ }
383
+
384
+ /* Preset chips */
385
+ &>div {
386
+ flex: 1;
387
+ display: flex;
388
+ flex-flow: row wrap;
389
+ align-items: center;
390
+ justify-content: center;
391
+ gap: 0.25rem;
392
+
393
+ & button {
394
+ height: auto;
395
+ min-height: 0;
396
+ padding: 0.25rem 0.625rem;
397
+ font-size: 0.75rem;
398
+ background: var(--color-field-surface, color-mix(oklch(20.5% 0 0) 10%, transparent));
399
+ color: var(--color-content-stark, oklch(20.5% 0 0));
400
+ border: none;
401
+ border-radius: calc(var(--radius, 0.5rem) * 2);
402
+ cursor: pointer;
403
+ transition: var(--transition, all .05s ease-in-out);
404
+
405
+ &:hover {
406
+ background: var(--color-field-surface-hover, color-mix(oklch(20.5% 0 0) 16%, transparent))
407
+ }
408
+ }
409
+ }
410
+ }
411
+ }
412
+
413
+ /* Time dropdown */
414
+ :where(menu.time-options):not(.unstyle) {
415
+ position-area: end center;
416
+ position-try-fallbacks: flip-block;
417
+ width: max-content;
418
+ min-width: 0;
419
+ margin: 0;
420
+ padding: 0.25rem;
421
+ list-style: none;
422
+ }
423
+
424
+ /* Time columns — hours / minutes / (AM-PM + actions) */
425
+ .time-options {
426
+ flex-flow: row nowrap;
427
+ gap: 0.25rem;
428
+
429
+ /* Column */
430
+ &>div {
431
+ flex: 1;
432
+ min-width: 2.75rem;
433
+ max-height: 13rem;
434
+ overflow-y: auto;
435
+ display: flex;
436
+ flex-direction: column;
437
+ gap: 1px;
438
+ scrollbar-width: thin;
439
+ }
440
+
441
+ /* Option */
442
+ & button {
443
+ flex: none;
444
+ width: auto;
445
+ min-width: 0;
446
+ height: auto;
447
+ min-height: 0;
448
+ padding: 0.25rem 0.5rem;
449
+ font-size: 0.8125rem;
450
+ font-variant-numeric: tabular-nums;
451
+ text-align: center;
452
+ justify-content: center;
453
+ background: transparent;
454
+ color: var(--color-content-stark, oklch(20.5% 0 0));
455
+ border: none;
456
+ border-radius: calc(var(--radius, 0.5rem) / 2);
457
+ cursor: pointer;
458
+ transition: var(--transition, all .05s ease-in-out);
459
+
460
+ &:hover {
461
+ background: var(--color-field-surface, color-mix(oklch(20.5% 0 0) 10%, transparent))
462
+ }
463
+
464
+ &[aria-selected=true] {
465
+ background: var(--color-brand-surface, var(--color-accent-surface, oklch(20.5% 0 0)));
466
+ color: var(--color-brand-inverse, var(--color-accent-inverse, oklch(98.5% 0 0)));
467
+ font-weight: 600
468
+ }
469
+ }
470
+
471
+ /* Now / Clear actions */
472
+ & button:not([value]) {
473
+ font-size: 0.75rem;
474
+ font-variant-numeric: normal;
475
+ color: var(--color-content-subtle, oklch(55.6% 0 0));
476
+
477
+ &:hover {
478
+ color: var(--color-content-stark, oklch(20.5% 0 0))
479
+ }
480
+ }
481
+
482
+ /* First action in a column drops to the bottom */
483
+ & button[value]+button:not([value]),
484
+ & button:not([value]):first-child {
485
+ margin-top: auto
486
+ }
487
+ }
488
+
489
+ /* Popover menu presentation */
490
+ :where(menu[popover][x-date], menu[popover].date-picker):not(.unstyle) {
491
+ margin: 0;
492
+ padding: 0;
493
+ list-style: none
494
+ }
495
+
496
+ /* RTL support */
497
+ [dir=rtl] :where([x-date], .date-picker):not(.unstyle) header {
498
+
499
+ & button:first-child::before,
500
+ & button:last-child::before {
501
+ transform: scaleX(-1)
502
+ }
503
+ }
504
+ }