@celestia-island/hikari 0.55.28 → 0.55.29

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.55.28",
3
+ "version": "0.55.29",
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",
@@ -107,6 +107,7 @@
107
107
  }
108
108
 
109
109
  .hk-icon-group-item {
110
+ position: relative;
110
111
  display: inline-flex;
111
112
  align-items: center;
112
113
  justify-content: center;
@@ -168,3 +169,29 @@
168
169
  font-weight: 600;
169
170
  line-height: 1;
170
171
  }
172
+
173
+ /* Status marker (HkIconButtonGroupOption.marker) — a small dot pinned to
174
+ * the item's bottom-right corner. "success" = the theme's success green
175
+ * with a surface-colored ring so it stays legible on top of the icon's
176
+ * corner and across active/muted tints. Purely decorative: the accessible
177
+ * meaning lives in the option's label/tooltip the host controls. */
178
+ .hk-icon-group-item-marker {
179
+ position: absolute;
180
+ right: calc(var(--space-1, 0.25rem) / 2);
181
+ bottom: calc(var(--space-1, 0.25rem) / 2);
182
+ width: 0.5rem;
183
+ height: 0.5rem;
184
+ border-radius: 999px;
185
+ pointer-events: none;
186
+ }
187
+
188
+ .hk-icon-group-item-marker-success {
189
+ background: rgb(var(--color-success, 14 184 64));
190
+ box-shadow: 0 0 0 0.125rem rgb(var(--color-surface, 240 244 248));
191
+ }
192
+
193
+ /* Slider items already establish position:relative for the thumb stack —
194
+ * keep the marker above the gliding pill, not under it. */
195
+ .hk-icon-group-slider .hk-icon-group-item .hk-icon-group-item-marker {
196
+ z-index: 1;
197
+ }
@@ -1,6 +1,6 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import { afterEach, describe, expect, it } from "vitest";
3
+ import { afterEach, describe, expect, it, vi } from "vitest";
4
4
  import { createApp, defineComponent, h, nextTick, ref } from "vue";
5
5
 
6
6
  import HkIconButtonGroup from "./HkIconButtonGroup";
@@ -303,3 +303,41 @@ describe("HkIconButtonGroup", () => {
303
303
  });
304
304
  });
305
305
  });
306
+ describe("option markers", () => {
307
+ const baseOpts = (marker?: "success", disabled?: boolean) => [
308
+ { key: "totp", label: "TOTP" },
309
+ { key: "password", label: "Password", marker, disabled },
310
+ ];
311
+
312
+ it("renders the success marker dot and data-marker attribute", () => {
313
+ const c = mountComp(() => h(HkIconButtonGroup, {
314
+ mode: "single",
315
+ variant: "slider",
316
+ options: baseOpts("success"),
317
+ modelValue: "totp",
318
+ }));
319
+ const item = c.querySelector('.hk-icon-group-item[data-key="password"]') as HTMLElement;
320
+ expect(item).toBeTruthy();
321
+ expect(item.getAttribute("data-marker")).toBe("success");
322
+ expect(item.querySelector(".hk-icon-group-item-marker-success")).toBeTruthy();
323
+ // The untouched sibling carries no marker.
324
+ const sibling = c.querySelector('.hk-icon-group-item[data-key="totp"]') as HTMLElement;
325
+ expect(sibling.getAttribute("data-marker")).toBeNull();
326
+ });
327
+
328
+ it("ignores activation of a disabled marked option", async () => {
329
+ const onSelect = vi.fn();
330
+ const c = mountComp(() => h(HkIconButtonGroup, {
331
+ mode: "single",
332
+ variant: "slider",
333
+ options: baseOpts("success", true),
334
+ modelValue: "totp",
335
+ onSelect,
336
+ }));
337
+ const item = c.querySelector('.hk-icon-group-item[data-key="password"]') as HTMLButtonElement;
338
+ expect(item.getAttribute("disabled")).not.toBeNull();
339
+ item.click();
340
+ await Promise.resolve();
341
+ expect(onSelect).not.toHaveBeenCalled();
342
+ });
343
+ });
@@ -17,6 +17,11 @@ export interface HkIconButtonGroupOption {
17
17
  * host's vue differs. Missing icons fall back to the label's initial. */
18
18
  icon?: unknown;
19
19
  disabled?: boolean;
20
+ /** Status dot pinned to the item's bottom-right corner. `"success"`
21
+ * draws a small green dot (step-proven factor, completed requirement);
22
+ * the dot is decorative — pair it with `disabled` and an updated
23
+ * tooltip when it means "this one is done". */
24
+ marker?: "success";
20
25
  /** Tooltip text override; an EMPTY string suppresses the tooltip. */
21
26
  tooltip?: string;
22
27
  }
@@ -168,6 +173,7 @@ export default defineComponent({
168
173
  class="hk-icon-group-item"
169
174
  data-key={option.key}
170
175
  data-active={active || undefined}
176
+ data-marker={option.marker || undefined}
171
177
  role={props.mode === "single" ? "radio" : undefined}
172
178
  aria-checked={props.mode === "single" ? active : undefined}
173
179
  aria-pressed={props.mode === "multiple" ? active : undefined}
@@ -185,6 +191,12 @@ export default defineComponent({
185
191
  </span>
186
192
  )}
187
193
  </span>
194
+ {option.marker && (
195
+ <span
196
+ class={`hk-icon-group-item-marker hk-icon-group-item-marker-${option.marker}`}
197
+ aria-hidden="true"
198
+ />
199
+ )}
188
200
  </button>
189
201
  );
190
202
  // An empty tooltip override suppresses the wrapper entirely.