@celestia-island/hikari 0.49.3 → 0.49.5

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@celestia-island/hikari",
3
- "version": "0.49.3",
3
+ "version": "0.49.5",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Hikari Vue 3 component library — production-grade UI components based on shittim-chest design system",
@@ -393,7 +393,7 @@ describe("HkDatePicker", () => {
393
393
  await nextTick();
394
394
  panel()?.querySelector<HTMLButtonElement>(".hk-dp-title-btn")?.click();
395
395
  await settle();
396
- expect(panel()?.querySelector<HTMLButtonElement>(".hk-dp-title-btn")?.textContent).toBe("2026");
396
+ await waitForTitle("2026");
397
397
  const navs = () => panel()?.querySelectorAll<HTMLButtonElement>(".hk-dp-nav");
398
398
  navs()?.[1].click();
399
399
  await nextTick();
@@ -413,23 +413,36 @@ describe("HkDatePicker", () => {
413
413
  const stage = panel()?.querySelector<HTMLElement>(".hk-dp-stage");
414
414
  expect(stage).not.toBeNull();
415
415
  expect(stage?.children.length).toBe(1); // the single days pane
416
+ const pickCount = () =>
417
+ stage?.querySelectorAll<HTMLButtonElement>(".hk-dp-cell[data-variant='pick']").length ?? 0;
418
+ // Destination state = the entering pane mounted AND the leaving pane
419
+ // gone; mid-transition both conditions half-hold.
420
+ const settledDrill = (days: boolean) => (): boolean =>
421
+ days
422
+ ? stage?.children.length === 1 &&
423
+ stage?.querySelectorAll<HTMLButtonElement>(".hk-dp-cell:not([data-variant])").length === 42
424
+ : pickCount() === 12 && stage?.children.length === 1;
416
425
  panel()?.querySelector<HTMLButtonElement>(".hk-dp-title-btn")?.click();
417
426
  await settle();
427
+ await waitForView("the months grid settled to one pane", settledDrill(false));
418
428
  expect(panel()?.querySelector<HTMLElement>(".hk-dp-stage")).toBe(stage);
419
429
  expect(stage?.getAttribute("data-dir")).toBe("fwd");
420
430
  expect(stage?.children.length).toBe(1); // one pane at a time after settle
421
- expect(stage?.querySelectorAll<HTMLButtonElement>(".hk-dp-cell[data-variant='pick']").length).toBe(12);
431
+ expect(pickCount()).toBe(12);
422
432
  panel()?.querySelector<HTMLButtonElement>(".hk-dp-title-btn")?.click();
423
433
  await settle();
434
+ await waitForView("the years grid settled to one pane", settledDrill(false));
424
435
  expect(stage?.getAttribute("data-dir")).toBe("fwd");
425
- expect(stage?.querySelectorAll<HTMLButtonElement>(".hk-dp-cell[data-variant='pick']").length).toBe(12);
436
+ expect(pickCount()).toBe(12);
426
437
  // back steps down the stack one level at a time: years → months → days.
427
438
  panel()?.querySelector<HTMLButtonElement>(".hk-dp-back")?.click();
428
439
  await settle();
440
+ await waitForView("the months grid again, one pane", settledDrill(false));
429
441
  expect(stage?.getAttribute("data-dir")).toBe("back");
430
- expect(stage?.querySelectorAll<HTMLButtonElement>(".hk-dp-cell[data-variant='pick']").length).toBe(12);
442
+ expect(pickCount()).toBe(12);
431
443
  panel()?.querySelector<HTMLButtonElement>(".hk-dp-back")?.click();
432
444
  await settle();
445
+ await waitForView("the days grid settled to one pane", settledDrill(true));
433
446
  expect(stage?.getAttribute("data-dir")).toBe("back");
434
447
  expect(stage?.querySelectorAll<HTMLButtonElement>(".hk-dp-cell:not([data-variant])").length).toBe(42);
435
448
  });
@@ -83,6 +83,20 @@ async function waitForTitle(expected: string): Promise<void> {
83
83
  }
84
84
  }
85
85
 
86
+ /** The pick cells of the drilled months view (exactly 12 once mounted). */
87
+ function pickCells(): HTMLButtonElement[] {
88
+ return Array.from(picker()?.querySelectorAll<HTMLButtonElement>(".hk-dtp-cell[data-variant='pick']") ?? []);
89
+ }
90
+
91
+ /** Generic state poll over the same drill race: wait until `probe` holds. */
92
+ async function waitForView(desc: string, probe: () => boolean): Promise<void> {
93
+ const deadline = Date.now() + 2000;
94
+ while (!probe()) {
95
+ if (Date.now() > deadline) throw new Error(`view never reached: ${desc}`);
96
+ await new Promise((r) => setTimeout(r, 10));
97
+ }
98
+ }
99
+
86
100
  function picker(): HTMLElement | null {
87
101
  return document.querySelector<HTMLElement>(".hk-dtp");
88
102
  }
@@ -178,6 +192,10 @@ describe("HkDateTimePicker", () => {
178
192
  const monthBtn = picker()?.querySelectorAll<HTMLButtonElement>(".hk-dtp-title-btn")[0];
179
193
  monthBtn?.click();
180
194
  await settle();
195
+ // Destination state = the months pane mounted AND the leaving days
196
+ // pane gone — mid-transition both conditions half-hold.
197
+ await waitForView("the months grid settled to one pane", () =>
198
+ pickCells().length === 12 && picker()?.querySelectorAll(".hk-dtp-cell").length === 12);
181
199
  const cells = Array.from(picker()?.querySelectorAll<HTMLElement>(".hk-dtp-cell") ?? [])
182
200
  .map((c) => c.textContent ?? "");
183
201
  const expected = Array.from({ length: 12 }, (_, i) =>
@@ -196,6 +214,7 @@ describe("HkDateTimePicker", () => {
196
214
  const monthBtn = picker()?.querySelectorAll<HTMLButtonElement>(".hk-dtp-title-btn")[0];
197
215
  monthBtn?.click();
198
216
  await settle();
217
+ await waitForView("the months grid", () => pickCells().length === 12);
199
218
  expect(stage?.getAttribute("data-dir")).toBe("fwd");
200
219
  expect(picker()?.querySelector<HTMLElement>(".hk-dtp-stage")).toBe(stage);
201
220
  expect(stage?.children.length).toBe(1); // one pane at a time after settle
@@ -205,8 +224,9 @@ describe("HkDateTimePicker", () => {
205
224
  expect(picker()?.querySelectorAll(".hk-dtp-step").length).toBe(2);
206
225
  picker()?.querySelector<HTMLButtonElement>(".hk-dtp-back")?.click();
207
226
  await settle();
227
+ await waitForView("the days grid settled to one pane", () =>
228
+ dayCells().length === 42 && stage?.children.length === 1);
208
229
  expect(stage?.getAttribute("data-dir")).toBe("back");
209
- expect(dayCells().length).toBe(42);
210
230
  });
211
231
 
212
232
  it("shifts the year with the chevrons inside the month view", async () => {
@@ -229,7 +249,8 @@ describe("HkDateTimePicker", () => {
229
249
  const monthBtn = picker()?.querySelectorAll<HTMLButtonElement>(".hk-dtp-title-btn")[0];
230
250
  monthBtn?.click();
231
251
  await settle();
232
- expect(picker()?.querySelectorAll<HTMLButtonElement>(".hk-dtp-cell[data-variant='pick']").length).toBe(12);
252
+ await waitForView("the months grid", () => pickCells().length === 12);
253
+ expect(pickCells().length).toBe(12);
233
254
  // The first step button is "Hour +"; bumping it changes only the time
234
255
  // part of the model, which must not yank the view back to days.
235
256
  const upBtn = picker()?.querySelector<HTMLButtonElement>(".hk-dtp-step-btn");
@@ -118,32 +118,43 @@
118
118
  will-change: transform, opacity, clip-path;
119
119
  }
120
120
 
121
+ // Close: the whole frame fades and drifts down as ONE piece — the
122
+ // unveil's clip does NOT re-run on close. Re-clipping first made the
123
+ // bottom band (usually the footer) vanish at the very first frame of
124
+ // the close while the rest was still on screen, which read as broken
125
+ // (user report 2026-09-14).
121
126
  .hk-modal-content-leave-active {
122
127
  transition:
123
128
  opacity var(--hk-modal-duration, 0.25s)
124
129
  var(--hk-modal-ease-in, cubic-bezier(0.4, 0, 1, 1)),
125
130
  transform var(--hk-modal-duration, 0.25s)
126
- var(--hk-modal-ease-in, cubic-bezier(0.4, 0, 1, 1)),
127
- clip-path var(--hk-modal-duration, 0.25s)
128
131
  var(--hk-modal-ease-in, cubic-bezier(0.4, 0, 1, 1));
129
- will-change: transform, opacity, clip-path;
132
+ will-change: transform, opacity;
130
133
  }
131
134
 
132
- .hk-modal-content-enter-from,
133
- .hk-modal-content-leave-to {
135
+ .hk-modal-content-enter-from {
134
136
  opacity: 0;
135
137
  transform: translate(-50%, -50%) translateY(5%);
136
138
  clip-path: inset(0 0 10% 0 round 0 0 var(--hk-modal-radius, var(--radius-lg, 12px))
137
139
  var(--hk-modal-radius, var(--radius-lg, 12px)));
138
140
  }
139
141
 
140
- .hk-modal-content-enter-to,
141
- .hk-modal-content-leave-from {
142
+ .hk-modal-content-enter-to {
142
143
  opacity: 1;
143
144
  transform: translate(-50%, -50%);
144
145
  clip-path: inset(0 0 0 0 round 0 0 var(--hk-modal-radius, var(--radius-lg, 12px))
145
146
  var(--hk-modal-radius, var(--radius-lg, 12px)));
146
147
  }
148
+
149
+ .hk-modal-content-leave-from {
150
+ opacity: 1;
151
+ transform: translate(-50%, -50%);
152
+ }
153
+
154
+ .hk-modal-content-leave-to {
155
+ opacity: 0;
156
+ transform: translate(-50%, -50%) translateY(5%);
157
+ }
147
158
  }
148
159
 
149
160
  // ------