@lyeve-labs/ui-kit 0.22.1 → 0.22.2

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.
@@ -42,6 +42,9 @@
42
42
  let confirmLabel = $derived(
43
43
  typeof entry.meta?.confirmLabel === 'string' ? entry.meta.confirmLabel : 'Confirm',
44
44
  );
45
+ // An identifier the message refers to. Set apart in monospace on its own
46
+ // line rather than inside the sentence, where a UUID wraps mid-string.
47
+ let detail = $derived(typeof entry.meta?.confirmDetail === 'string' ? entry.meta.confirmDetail : '');
45
48
 
46
49
  async function handleConfirm() {
47
50
  if (loading) return;
@@ -80,12 +83,16 @@
80
83
  {#if message}
81
84
  <p class="text-sm text-muted mt-1">{message}</p>
82
85
  {/if}
86
+ {#if detail}
87
+ <p class="mt-2 break-all font-mono text-xs text-faint">{detail}</p>
88
+ {/if}
83
89
  </div>
84
90
  </div>
85
91
 
86
- <!-- Actions -->
92
+ <!-- Actions. Cancel takes focus first: a destructive dialog must not
93
+ accept an Enter pressed before it was read. -->
87
94
  <div class="flex items-center justify-end gap-3 pt-2">
88
- <Button variant="secondary" size="sm" onclick={handleCancel}>
95
+ <Button variant="secondary" size="sm" onclick={handleCancel} data-initial-focus>
89
96
  {cancelLabel}
90
97
  </Button>
91
98
  <Button variant="danger" size="sm" onclick={handleConfirm} {loading}>
@@ -137,34 +137,39 @@
137
137
  tabindex="-1"
138
138
  onkeydown={handleKeydown}
139
139
  >
140
- <!-- Header -->
141
- <div class="flex items-center justify-between px-6 pt-6 pb-2">
142
- <div class="flex-1 min-w-0">
143
- {#if entry.options.title && entry.meta?.confirmTitle === undefined}
144
- {#if typeof entry.options.title === 'string'}
145
- <h2 class="text-lg font-semibold text-fg truncate">
146
- {entry.options.title}
147
- </h2>
148
- {:else}
149
- {@render entry.options.title()}
140
+ <!-- Header. A confirm dialog has none: it renders its own title beside
141
+ the warning icon and offers Cancel, so a header here was an empty row
142
+ with a close control in it, and that control was the first thing
143
+ focused. Escape still dismisses. -->
144
+ {#if entry.meta?.confirmTitle === undefined}
145
+ <div class="flex items-center justify-between px-6 pt-6 pb-2">
146
+ <div class="flex-1 min-w-0">
147
+ {#if entry.options.title}
148
+ {#if typeof entry.options.title === 'string'}
149
+ <h2 class="text-lg font-semibold text-fg truncate">
150
+ {entry.options.title}
151
+ </h2>
152
+ {:else}
153
+ {@render entry.options.title()}
154
+ {/if}
150
155
  {/if}
151
- {/if}
152
- </div>
156
+ </div>
153
157
 
154
- {#if !entry.options.persistent}
155
- <button
156
- class="inline-flex items-center justify-center w-8 h-8 -me-2 rounded-lg
158
+ {#if !entry.options.persistent}
159
+ <button
160
+ class="inline-flex items-center justify-center w-8 h-8 -me-2 rounded-lg
157
161
  text-muted hover:text-fg hover:bg-surface-2 transition-colors duration-150 shrink-0"
158
- onclick={() => handleClose()}
159
- aria-label="Close"
160
- >
161
- <X class="w-4 h-4" />
162
- </button>
163
- {/if}
164
- </div>
162
+ onclick={() => handleClose()}
163
+ aria-label="Close"
164
+ >
165
+ <X class="w-4 h-4" />
166
+ </button>
167
+ {/if}
168
+ </div>
169
+ {/if}
165
170
 
166
171
  <!-- Body -->
167
- <div class="px-6 py-2">
172
+ <div class={entry.meta?.confirmTitle === undefined ? 'px-6 py-2' : 'px-6 pt-6 pb-2'}>
168
173
  {#if entry.options.body}
169
174
  {@render entry.options.body()}
170
175
  {:else if entry.meta?.confirmTitle !== undefined}
@@ -55,6 +55,12 @@ export interface ConfirmOptions {
55
55
  confirmLabel?: string;
56
56
  /** The verb on the dismissing button. Defaults to `Cancel`. */
57
57
  cancelLabel?: string;
58
+ /**
59
+ * An identifier the message refers to, such as the id of the record about
60
+ * to be deleted. Rendered on its own line in monospace, so a UUID never
61
+ * wraps inside the sentence.
62
+ */
63
+ detail?: string;
58
64
  }
59
65
  /**
60
66
  * Convenience: confirm dialog.
@@ -151,6 +151,7 @@ export function confirm(title, message, options) {
151
151
  // would be indistinguishable from an override to the empty case.
152
152
  ...(options?.confirmLabel !== undefined ? { confirmLabel: options.confirmLabel } : {}),
153
153
  ...(options?.cancelLabel !== undefined ? { cancelLabel: options.cancelLabel } : {}),
154
+ ...(options?.detail !== undefined ? { confirmDetail: options.detail } : {}),
154
155
  });
155
156
  // Cancelling a dialog dismisses it, and dismissal rejects. Callers write
156
157
  // `if (await confirm(...))`, so a rejection on Cancel is an unhandled
package/dist/index.d.ts CHANGED
@@ -89,4 +89,4 @@ export type { ConfirmOptions } from './components/dialog/dialog-manager.svelte.j
89
89
  export type { DialogOptions, DialogEntry, DialogSize } from './components/dialog/types.js';
90
90
  export { cn, type ClassValue } from './utils/cn.js';
91
91
  export { getTheme, getThemePreference, nextThemePreference, resolveTheme, setTheme, setThemePreference, systemTheme, themeBootScript, toggleTheme, watchSystemTheme, THEME_PREFERENCES, type Theme, type ThemePreference, } from './utils/theme.js';
92
- export declare const VERSION = "0.22.1";
92
+ export declare const VERSION = "0.22.2";
package/dist/index.js CHANGED
@@ -96,4 +96,4 @@ export { getTheme, getThemePreference, nextThemePreference, resolveTheme, setThe
96
96
  // ── Version ────────────────────────────────────────────────────────────────
97
97
  // Generated from package.json by `pnpm version:sync`. Bump package.json, never
98
98
  // this line; the build and the test suite fail when the two disagree.
99
- export const VERSION = '0.22.1';
99
+ export const VERSION = '0.22.2';
@@ -50,9 +50,13 @@ function focusable(node) {
50
50
  export function overlay(node) {
51
51
  const previous = document.activeElement instanceof HTMLElement ? document.activeElement : null;
52
52
  lockBodyScroll();
53
- // A panel with nothing focusable still has to receive focus, or the screen
54
- // reader stays on the element behind the overlay and reads the wrong thing.
55
- const first = focusable(node)[0];
53
+ // A panel may name where focus lands with data-initial-focus: a confirm
54
+ // dialog puts it on Cancel, so that Enter on a dialog a person has not read
55
+ // yet does nothing, and the close control is never the first thing focused.
56
+ // Otherwise the first focusable element takes it. A panel with nothing
57
+ // focusable still has to receive focus, or the screen reader stays on the
58
+ // element behind the overlay and reads the wrong thing.
59
+ const first = node.querySelector('[data-initial-focus]') ?? focusable(node)[0];
56
60
  if (first) {
57
61
  first.focus();
58
62
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lyeve-labs/ui-kit",
3
- "version": "0.22.1",
3
+ "version": "0.22.2",
4
4
  "description": "A clean, accessible, palette-aware Svelte 5 component library. The design system behind LyEve.",
5
5
  "license": "MIT",
6
6
  "author": "LyEve Labs <hello@lyeve.com>",