@celestia-island/hikari 0.54.8 → 0.55.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.
Files changed (52) hide show
  1. package/package.json +1 -1
  2. package/src/DemoApp.tsx +5 -3
  3. package/src/animation/registerAnimations.ts +2 -2
  4. package/src/components/HkDrawer.test.tsx +57 -0
  5. package/src/components/HkDrawer.tsx +6 -1
  6. package/src/components/HkInput.password.test.tsx +821 -0
  7. package/src/components/HkInput.scss +4 -33
  8. package/src/components/HkInput.tsx +91 -31
  9. package/src/components/HkKanban.scss +81 -0
  10. package/src/components/HkKanban.test.tsx +219 -0
  11. package/src/components/HkKanban.tsx +264 -0
  12. package/src/components/HkModal.test.tsx +76 -0
  13. package/src/components/HkModal.tsx +8 -1
  14. package/src/components/HkModalBreadcrumb.overflow.contract.test.ts +115 -0
  15. package/src/components/HkModalBreadcrumb.scss +114 -2
  16. package/src/components/HkModalBreadcrumb.test.tsx +549 -6
  17. package/src/components/HkModalBreadcrumb.tsx +433 -37
  18. package/src/components/{HkPasswordInput.scss → HkPasswordSurface.scss} +93 -57
  19. package/src/components/HkPasswordSurface.styles.test.ts +68 -0
  20. package/src/components/HkPasswordSurface.tsx +1006 -0
  21. package/src/components/HkPopover.tsx +8 -0
  22. package/src/components/HkPopoverSheet.test.tsx +21 -0
  23. package/src/components/HkSelectPanel.test.tsx +21 -0
  24. package/src/components/HkSelectPanel.tsx +3 -0
  25. package/src/components/HkSignInCard.tsx +6 -6
  26. package/src/components/HkTooltip.test.tsx +67 -0
  27. package/src/components/HkTooltip.tsx +54 -1
  28. package/src/i18n/flatKeyParity.test.ts +97 -0
  29. package/src/i18n/locales/ar/components.json +9 -2
  30. package/src/i18n/locales/de/components.json +9 -2
  31. package/src/i18n/locales/en/components.json +4 -2
  32. package/src/i18n/locales/es/components.json +9 -2
  33. package/src/i18n/locales/fr/components.json +9 -2
  34. package/src/i18n/locales/ja/components.json +9 -2
  35. package/src/i18n/locales/ko/components.json +9 -2
  36. package/src/i18n/locales/pt/components.json +9 -2
  37. package/src/i18n/locales/ru/components.json +9 -2
  38. package/src/i18n/locales/zh-Hans/components.json +9 -7
  39. package/src/i18n/locales/zh-Hant/components.json +9 -7
  40. package/src/i18n/modalKeys.test.ts +59 -0
  41. package/src/index.ts +8 -3
  42. package/src/runtime/backStack.test.ts +171 -0
  43. package/src/runtime/backStack.ts +118 -39
  44. package/src/runtime/credentialAutofill.test.ts +4 -4
  45. package/src/runtime/displayWidth.test.ts +102 -0
  46. package/src/runtime/displayWidth.ts +76 -0
  47. package/src/runtime/index.ts +6 -0
  48. package/src/runtime/usePopupManager.test.ts +60 -0
  49. package/src/runtime/usePopupManager.ts +42 -1
  50. package/src/utils/password.ts +9 -0
  51. package/src/components/HkPasswordInput.test.ts +0 -329
  52. package/src/components/HkPasswordInput.tsx +0 -820
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@celestia-island/hikari",
3
- "version": "0.54.8",
3
+ "version": "0.55.0",
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",
package/src/DemoApp.tsx CHANGED
@@ -8,7 +8,7 @@ import { utf8ToBase64 } from "./utils/base64";
8
8
  import {
9
9
  HButton, HIconButton, HIconButtonGroup, HTooltip, HBadge, HTag, HIcon, HSpinner,
10
10
  HProgressBar, HProgressRing, HGaugeRing,
11
- HInput, HSearchInput, HNumberInput, HPasswordInput, HTextarea,
11
+ HInput, HSearchInput, HNumberInput, HTextarea,
12
12
  HFileField,
13
13
  HFilePickerField,
14
14
  type PickedFile, type RemoteFsAdapter,
@@ -360,13 +360,15 @@ export default defineComponent({
360
360
  </section>
361
361
 
362
362
  <section>
363
- <h2>HInput / HSearchInput / HNumberInput / HPasswordInput / HTextarea</h2>
363
+ <h2>HInput (text / password) / HSearchInput / HNumberInput / HTextarea</h2>
364
364
  <div class="form-grid">
365
365
  <HInput modelValue={form.value.text} onUpdate:modelValue={(v: string) => (form.value.text = v)} placeholder="Text input" />
366
366
  <HInput modelValue={form.value.text} onUpdate:modelValue={(v: string) => (form.value.text = v)} placeholder="Disabled" disabled />
367
367
  <HSearchInput modelValue={form.value.search} onUpdate:modelValue={(v: string | number) => (form.value.search = String(v))} placeholder="Search..." />
368
368
  <HNumberInput modelValue={form.value.number} onUpdate:modelValue={(v: number) => (form.value.number = v)} placeholder="Number" />
369
- <HPasswordInput modelValue={form.value.password} onUpdate:modelValue={(v: string) => (form.value.password = v)} placeholder="Password" />
369
+ <HInput variant="password" modelValue={form.value.password} onUpdate:modelValue={(v: string) => (form.value.password = v)} placeholder="Password (hold the eye)" />
370
+ <HInput variant="password" passwordTrailing="strength" modelValue={form.value.password} onUpdate:modelValue={(v: string) => (form.value.password = v)} placeholder="Password (strength light)" />
371
+ <HInput variant="password" passwordTrailing="none" modelValue={form.value.password} onUpdate:modelValue={(v: string) => (form.value.password = v)} placeholder="Password (no affordance)" />
370
372
  <HTextarea modelValue={form.value.textarea} onUpdate:modelValue={(v: string) => (form.value.textarea = v)} placeholder="Textarea" rows={3} />
371
373
  </div>
372
374
  {anyForm.value ? <p class="result">Form: {anyForm.value}</p> : null}
@@ -82,9 +82,9 @@ registerCssAnimation("hk-skeleton-shimmer", { infinite: true });
82
82
  registerCssAnimation("hk-image-preview-shimmer", { infinite: true });
83
83
  // components/HkProgressBar.scss — indeterminate bar sweep (loops forever).
84
84
  registerCssAnimation("hk-progress-indeterminate", { infinite: true });
85
- // components/HkPasswordInput.scss — caps-lock warning flash (plays once).
85
+ // components/HkPasswordSurface.scss — caps-lock warning flash (plays once).
86
86
  registerCssAnimation("hk-pwd-flash");
87
- // components/HkPasswordInput.scss — breathing hint glow (loops forever).
87
+ // components/HkPasswordSurface.scss — breathing hint glow (loops forever).
88
88
  registerCssAnimation("hk-pwd-breathe", { infinite: true });
89
89
  // components/HkPickerPane.scss — date/time picker view drill forward
90
90
  // (plays once, shared by HkDatePicker and HkDateTimePicker).
@@ -2,6 +2,7 @@ import { afterEach, describe, expect, it } from "vitest";
2
2
  import { createApp, defineComponent, h, nextTick, ref } from "vue";
3
3
 
4
4
  import HkDrawer from "./HkDrawer";
5
+ import { usePopupManager } from "../runtime/usePopupManager";
5
6
 
6
7
  const mounts: ReturnType<typeof createApp>[] = [];
7
8
  const containers: HTMLElement[] = [];
@@ -89,3 +90,59 @@ describe("HkDrawer back-guard (window-first back priority)", () => {
89
90
  app.unmount();
90
91
  });
91
92
  });
93
+
94
+ describe("HkDrawer window-stack close channel", () => {
95
+ function mount(open: { value: boolean }, closable: boolean, title: string) {
96
+ const container = document.createElement("div");
97
+ document.body.appendChild(container);
98
+ containers.push(container);
99
+ const Wrapper = defineComponent({
100
+ setup() {
101
+ return () =>
102
+ h(HkDrawer, {
103
+ modelValue: open.value,
104
+ closable,
105
+ title,
106
+ "onUpdate:modelValue": (v: boolean) => { open.value = v; },
107
+ }, { default: () => h("div", "content") });
108
+ },
109
+ });
110
+ const app = createApp(Wrapper);
111
+ mounts.push(app);
112
+ app.mount(container);
113
+ return app;
114
+ }
115
+
116
+ it("closes through the popup manager when the stack navigates back", async () => {
117
+ const manager = usePopupManager();
118
+ const below = manager.register("modal", true, "Below");
119
+ try {
120
+ const open = ref(true);
121
+ mount(open, true, "Details");
122
+ await nextTick();
123
+
124
+ expect(manager.closeAbove(below.id)).toBe(1);
125
+ await nextTick();
126
+ expect(open.value).toBe(false);
127
+ } finally {
128
+ manager.unregister(below.id);
129
+ }
130
+ });
131
+
132
+ it("cannot close a non-closable drawer", async () => {
133
+ const manager = usePopupManager();
134
+ const below = manager.register("modal", true, "Below");
135
+ try {
136
+ const open = ref(true);
137
+ mount(open, false, "Locked");
138
+ await nextTick();
139
+
140
+ // Asked, but the drawer refuses — exactly as it does for Escape.
141
+ expect(manager.closeAbove(below.id)).toBe(1);
142
+ await nextTick();
143
+ expect(open.value).toBe(true);
144
+ } finally {
145
+ manager.unregister(below.id);
146
+ }
147
+ });
148
+ });
@@ -126,7 +126,12 @@ export default defineComponent({
126
126
  // Register with the drawer title so the modal-stack breadcrumb
127
127
  // labels this layer by name — a drawer is a window on every
128
128
  // form factor and must never fall back to a generic label.
129
- handle.value = manager.register("drawer", true, props.title);
129
+ // Close channel for the modal-stack breadcrumb (see
130
+ // usePopupManager): the drawer owns its dismissal, and a
131
+ // non-closable drawer refuses like it does for Escape.
132
+ handle.value = manager.register("drawer", true, props.title, false, () => {
133
+ if (props.closable) close();
134
+ });
130
135
  overlayHook.open();
131
136
  previouslyFocused = document.activeElement as HTMLElement | null;
132
137
  if (backGuardEnabled() && backGuard.entries === 0) {