@placeholderco/placeholder-ui 2.5.5 → 2.6.1

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.
@@ -8,7 +8,7 @@
8
8
  required?: boolean;
9
9
  /** CSS classes for the container element */
10
10
  containerClass?: string;
11
- /** Selected time value in HH:mm format (bindable) */
11
+ /** Selected time value in HH:mm:ss format (bindable). Accepts HH:mm or HH:mm:ss on input; always emits HH:mm:ss. */
12
12
  value?: string | undefined;
13
13
  /** Use rounded time increments */
14
14
  rounded?: boolean;
@@ -19,7 +19,7 @@
19
19
  /** Currently selected date (for date-time combinations) */
20
20
  selectedDate?: string | undefined;
21
21
  /** Callback when selected time changes */
22
- onchange?: (date: string | undefined) => void;
22
+ onchange?: (time: string | undefined) => void;
23
23
  }
24
24
  </script>
25
25
 
@@ -151,7 +151,7 @@
151
151
  value = timeString;
152
152
  return;
153
153
  }
154
- value = dayjs().format('HH:mm');
154
+ value = dayjs().format('HH:mm:ss');
155
155
  };
156
156
 
157
157
  if (value === undefined) {
@@ -7,7 +7,7 @@ export interface TimePickerProps {
7
7
  required?: boolean;
8
8
  /** CSS classes for the container element */
9
9
  containerClass?: string;
10
- /** Selected time value in HH:mm format (bindable) */
10
+ /** Selected time value in HH:mm:ss format (bindable). Accepts HH:mm or HH:mm:ss on input; always emits HH:mm:ss. */
11
11
  value?: string | undefined;
12
12
  /** Use rounded time increments */
13
13
  rounded?: boolean;
@@ -18,7 +18,7 @@ export interface TimePickerProps {
18
18
  /** Currently selected date (for date-time combinations) */
19
19
  selectedDate?: string | undefined;
20
20
  /** Callback when selected time changes */
21
- onchange?: (date: string | undefined) => void;
21
+ onchange?: (time: string | undefined) => void;
22
22
  }
23
23
  declare const TimePicker: import("svelte").Component<TimePickerProps, {}, "value">;
24
24
  type TimePicker = ReturnType<typeof TimePicker>;
package/dist/index.d.ts CHANGED
@@ -10,6 +10,8 @@ export { default as Badge } from './ui/Badge.svelte';
10
10
  export type { BadgeProps } from './ui/Badge.svelte';
11
11
  export { default as Dialog } from './ui/Dialog.svelte';
12
12
  export type { DialogProps, DialogContext } from './ui/Dialog.svelte';
13
+ export { default as ConfirmDialog } from './ui/ConfirmDialog.svelte';
14
+ export type { ConfirmDialogProps } from './ui/ConfirmDialog.svelte';
13
15
  export { default as Drawer } from './ui/Drawer.svelte';
14
16
  export type { DrawerProps } from './ui/Drawer.svelte';
15
17
  export { default as Dropdown } from './ui/Dropdown.svelte';
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ export { default as Button } from './ui/Button.svelte';
4
4
  export { default as ActionIcon } from './ui/ActionIcon.svelte';
5
5
  export { default as Badge } from './ui/Badge.svelte';
6
6
  export { default as Dialog } from './ui/Dialog.svelte';
7
+ export { default as ConfirmDialog } from './ui/ConfirmDialog.svelte';
7
8
  export { default as Drawer } from './ui/Drawer.svelte';
8
9
  export { default as Dropdown } from './ui/Dropdown.svelte';
9
10
  export { default as Dropzone } from './ui/Dropzone.svelte';
@@ -0,0 +1,191 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import Button from './Button.svelte';
4
+ import Dialog from './Dialog.svelte';
5
+ import type { ButtonVariant } from './ButtonVariant.js';
6
+
7
+ export interface ConfirmDialogProps {
8
+ /** Whether the dialog is visible (bindable) */
9
+ show: boolean;
10
+ /** Dialog title displayed in the header */
11
+ title?: string | undefined;
12
+ /**
13
+ * Plain-text body. Ignored when `children` is provided, so pass a snippet
14
+ * for richer content.
15
+ */
16
+ message?: string | undefined;
17
+ /** Label for the confirming button */
18
+ okText?: string;
19
+ /** Label for the dismissing button */
20
+ cancelText?: string;
21
+ /** Visual variant of the confirming button (use `danger` for destructive actions) */
22
+ okVariant?: ButtonVariant;
23
+ /** Visual variant of the dismissing button */
24
+ cancelVariant?: ButtonVariant;
25
+ /** Icon shown on the left of the confirming button */
26
+ okSvg?: string;
27
+ /** Disable the confirming button (e.g. while a form is invalid) */
28
+ okDisabled?: boolean;
29
+ /** Hide the Cancel button entirely, leaving an acknowledgement-style dialog */
30
+ hideCancel?: boolean;
31
+ /**
32
+ * Which button receives focus when the dialog opens. Defaults to the
33
+ * confirming button, or to Cancel when `okVariant` is a danger variant so
34
+ * a stray Enter cannot trigger a destructive action.
35
+ */
36
+ initialFocus?: 'ok' | 'cancel' | 'none';
37
+ /**
38
+ * Called when OK is pressed. Returning a promise puts the OK button into a
39
+ * loading state and keeps the dialog open until it settles. Return `false`
40
+ * (or resolve to `false`) to keep the dialog open.
41
+ */
42
+ onConfirm?: () => void | boolean | Promise<void | boolean>;
43
+ /** Called when the dialog is dismissed via Cancel, the close icon, Escape or the backdrop */
44
+ onCancel?: () => void;
45
+ /** Allow closing by clicking backdrop or pressing Escape */
46
+ allowImplicitClose?: boolean;
47
+ /** Size preset for the dialog width */
48
+ size?: 'md' | 'lg' | 'xl' | 'full';
49
+ /** Additional CSS classes passed through to the dialog */
50
+ class?: string;
51
+ /** Custom body content; overrides `message` */
52
+ children?: Snippet;
53
+ }
54
+
55
+ let {
56
+ show = $bindable(false),
57
+ title = undefined,
58
+ message = undefined,
59
+ okText = 'Save',
60
+ cancelText = 'Cancel',
61
+ okVariant = 'primary',
62
+ cancelVariant = 'secondary',
63
+ okSvg = undefined,
64
+ okDisabled = false,
65
+ hideCancel = false,
66
+ initialFocus = undefined,
67
+ onConfirm = undefined,
68
+ onCancel = undefined,
69
+ allowImplicitClose = true,
70
+ size = 'md',
71
+ class: classes = '',
72
+ children
73
+ }: ConfirmDialogProps = $props();
74
+
75
+ let loading = $state(false);
76
+ let footerElement: HTMLDivElement | undefined = $state(undefined);
77
+
78
+ // Track whether the close was the result of OK so the cancel callback only
79
+ // fires for genuine dismissals (Cancel button, close icon, Escape, backdrop)
80
+ let confirmed = false;
81
+ let wasOpen = false;
82
+
83
+ const resolvedFocus = $derived(
84
+ initialFocus ?? (okVariant.startsWith('danger') && !hideCancel ? 'cancel' : 'ok')
85
+ );
86
+
87
+ $effect(() => {
88
+ if (show && !wasOpen) {
89
+ wasOpen = true;
90
+ confirmed = false;
91
+ loading = false;
92
+ focusInitialButton();
93
+ } else if (!show && wasOpen) {
94
+ wasOpen = false;
95
+ loading = false;
96
+ if (!confirmed) onCancel?.();
97
+ }
98
+ });
99
+
100
+ function focusInitialButton() {
101
+ if (resolvedFocus === 'none') return;
102
+ // showModal() runs in the parent Dialog's effect and moves focus to the
103
+ // first focusable element; wait a frame so this focus call lands after it
104
+ requestAnimationFrame(() => {
105
+ const target = footerElement?.querySelector<HTMLButtonElement>(
106
+ `.confirm-dialog-${resolvedFocus} button`
107
+ );
108
+ target?.focus();
109
+ });
110
+ }
111
+
112
+ async function handleConfirm() {
113
+ if (loading) return;
114
+ let result: void | boolean;
115
+ try {
116
+ const pending = onConfirm?.();
117
+ if (pending instanceof Promise) {
118
+ loading = true;
119
+ result = await pending;
120
+ } else {
121
+ result = pending;
122
+ }
123
+ } finally {
124
+ loading = false;
125
+ }
126
+ if (result === false) return;
127
+ confirmed = true;
128
+ show = false;
129
+ }
130
+
131
+ function handleCancel() {
132
+ if (loading) return;
133
+ show = false;
134
+ }
135
+ </script>
136
+
137
+ <Dialog
138
+ bind:show
139
+ {title}
140
+ {size}
141
+ class={classes}
142
+ allowImplicitClose={allowImplicitClose && !loading}
143
+ >
144
+ {#if children}
145
+ {@render children()}
146
+ {:else if message}
147
+ <p class="confirm-dialog-message">{message}</p>
148
+ {/if}
149
+ {#snippet footer()}
150
+ <div class="confirm-dialog-actions" bind:this={footerElement}>
151
+ {#if !hideCancel}
152
+ <span class="confirm-dialog-cancel">
153
+ <Button variant={cancelVariant} disabled={loading} onclick={handleCancel}>
154
+ {cancelText}
155
+ </Button>
156
+ </span>
157
+ {/if}
158
+ <span class="confirm-dialog-ok">
159
+ <Button
160
+ variant={okVariant}
161
+ svg={okSvg}
162
+ {loading}
163
+ disabled={okDisabled}
164
+ onclick={handleConfirm}
165
+ >
166
+ {okText}
167
+ </Button>
168
+ </span>
169
+ </div>
170
+ {/snippet}
171
+ </Dialog>
172
+
173
+ <style>
174
+ .confirm-dialog-message {
175
+ margin: 0;
176
+ white-space: pre-line;
177
+ overflow-wrap: anywhere;
178
+ }
179
+
180
+ .confirm-dialog-actions {
181
+ display: flex;
182
+ align-items: center;
183
+ justify-content: flex-end;
184
+ gap: 0.5rem;
185
+ }
186
+
187
+ .confirm-dialog-cancel,
188
+ .confirm-dialog-ok {
189
+ display: contents;
190
+ }
191
+ </style>
@@ -0,0 +1,52 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { ButtonVariant } from './ButtonVariant.js';
3
+ export interface ConfirmDialogProps {
4
+ /** Whether the dialog is visible (bindable) */
5
+ show: boolean;
6
+ /** Dialog title displayed in the header */
7
+ title?: string | undefined;
8
+ /**
9
+ * Plain-text body. Ignored when `children` is provided, so pass a snippet
10
+ * for richer content.
11
+ */
12
+ message?: string | undefined;
13
+ /** Label for the confirming button */
14
+ okText?: string;
15
+ /** Label for the dismissing button */
16
+ cancelText?: string;
17
+ /** Visual variant of the confirming button (use `danger` for destructive actions) */
18
+ okVariant?: ButtonVariant;
19
+ /** Visual variant of the dismissing button */
20
+ cancelVariant?: ButtonVariant;
21
+ /** Icon shown on the left of the confirming button */
22
+ okSvg?: string;
23
+ /** Disable the confirming button (e.g. while a form is invalid) */
24
+ okDisabled?: boolean;
25
+ /** Hide the Cancel button entirely, leaving an acknowledgement-style dialog */
26
+ hideCancel?: boolean;
27
+ /**
28
+ * Which button receives focus when the dialog opens. Defaults to the
29
+ * confirming button, or to Cancel when `okVariant` is a danger variant so
30
+ * a stray Enter cannot trigger a destructive action.
31
+ */
32
+ initialFocus?: 'ok' | 'cancel' | 'none';
33
+ /**
34
+ * Called when OK is pressed. Returning a promise puts the OK button into a
35
+ * loading state and keeps the dialog open until it settles. Return `false`
36
+ * (or resolve to `false`) to keep the dialog open.
37
+ */
38
+ onConfirm?: () => void | boolean | Promise<void | boolean>;
39
+ /** Called when the dialog is dismissed via Cancel, the close icon, Escape or the backdrop */
40
+ onCancel?: () => void;
41
+ /** Allow closing by clicking backdrop or pressing Escape */
42
+ allowImplicitClose?: boolean;
43
+ /** Size preset for the dialog width */
44
+ size?: 'md' | 'lg' | 'xl' | 'full';
45
+ /** Additional CSS classes passed through to the dialog */
46
+ class?: string;
47
+ /** Custom body content; overrides `message` */
48
+ children?: Snippet;
49
+ }
50
+ declare const ConfirmDialog: import("svelte").Component<ConfirmDialogProps, {}, "show">;
51
+ type ConfirmDialog = ReturnType<typeof ConfirmDialog>;
52
+ export default ConfirmDialog;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@placeholderco/placeholder-ui",
3
- "version": "2.5.5",
3
+ "version": "2.6.1",
4
4
  "description": "A modern, customizable Svelte 5 UI component library with comprehensive theming support. Re-brand it with a single config object.",
5
5
  "license": "MIT",
6
6
  "author": "Placeholderco",
@@ -56,7 +56,7 @@
56
56
  "@sveltejs/kit": "^2.70.3",
57
57
  "@sveltejs/package": "^2.5.8",
58
58
  "@sveltejs/vite-plugin-svelte": "^7.3.0",
59
- "@types/node": "^26.4.1",
59
+ "@types/node": "^26.5.1",
60
60
  "globals": "^17.12.0",
61
61
  "prettier": "^3.9.6",
62
62
  "prettier-plugin-svelte": "^4.1.1",
@@ -65,7 +65,7 @@
65
65
  "svelte": "^5.57.0",
66
66
  "svelte-check": "^4.7.6",
67
67
  "typescript": "^6.0.3",
68
- "vite": "^8.2.2"
68
+ "vite": "^8.3.0"
69
69
  },
70
70
  "dependencies": {
71
71
  "@codemirror/autocomplete": "^6.20.3",