@placeholderco/placeholder-ui 2.1.1 → 2.2.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.
- package/dist/app.css +0 -10
- package/dist/editors/CodeMirror.svelte +295 -297
- package/dist/editors/tiptap/LinkActionMenu.svelte +6 -1
- package/dist/editors/tiptap/TipTap.svelte +1 -1
- package/dist/editors/tiptap/extensions/Alert.js +17 -17
- package/dist/editors/tiptap/extensions/Div.js +19 -19
- package/dist/editors/tiptap/extensions/Span.js +14 -14
- package/dist/editors/tiptap/toolbar/AlertButton.svelte +254 -253
- package/dist/editors/tiptap/toolbar/AlignCenterButton.svelte +11 -11
- package/dist/editors/tiptap/toolbar/AlignJustifyButton.svelte +11 -11
- package/dist/editors/tiptap/toolbar/AlignLeftButton.svelte +11 -11
- package/dist/editors/tiptap/toolbar/AlignRightButton.svelte +11 -11
- package/dist/editors/tiptap/toolbar/BoldButton.svelte +11 -11
- package/dist/editors/tiptap/toolbar/BulletListButton.svelte +11 -11
- package/dist/editors/tiptap/toolbar/CodeButton.svelte +11 -11
- package/dist/editors/tiptap/toolbar/ColorButton.svelte +265 -265
- package/dist/editors/tiptap/toolbar/DocumentButton.svelte +51 -51
- package/dist/editors/tiptap/toolbar/HeadingButton.svelte +21 -21
- package/dist/editors/tiptap/toolbar/HtmlButton.svelte +48 -52
- package/dist/editors/tiptap/toolbar/ImageButton.svelte +35 -39
- package/dist/editors/tiptap/toolbar/ItalicButton.svelte +11 -11
- package/dist/editors/tiptap/toolbar/LinkButton.svelte +249 -249
- package/dist/editors/tiptap/toolbar/OrderedListButton.svelte +11 -11
- package/dist/editors/tiptap/toolbar/RedoButton.svelte +11 -11
- package/dist/editors/tiptap/toolbar/StrikethroughButton.svelte +11 -11
- package/dist/editors/tiptap/toolbar/SubscriptButton.svelte +11 -11
- package/dist/editors/tiptap/toolbar/SuperscriptButton.svelte +11 -11
- package/dist/editors/tiptap/toolbar/TableButton.svelte +231 -224
- package/dist/editors/tiptap/toolbar/ToolbarButton.svelte +59 -65
- package/dist/editors/tiptap/toolbar/UnderlineButton.svelte +11 -11
- package/dist/editors/tiptap/toolbar/UndoButton.svelte +11 -11
- package/dist/form/Autocomplete.svelte +24 -22
- package/dist/form/AutocompleteMulti.svelte +24 -22
- package/dist/form/Checkbox.svelte +4 -4
- package/dist/form/Chips.svelte +15 -7
- package/dist/form/ColorPicker.svelte +5 -20
- package/dist/form/ComboBox.svelte +4 -4
- package/dist/form/ComboBoxItemBuilder.svelte +14 -32
- package/dist/form/ComboBoxMulti.svelte +7 -3
- package/dist/form/CronBuilder.svelte +32 -110
- package/dist/form/DatePicker.svelte +9 -28
- package/dist/form/DateRangePicker.svelte +46 -36
- package/dist/form/DateTimePicker.svelte +11 -30
- package/dist/form/Number.svelte +1 -3
- package/dist/form/RadioGroup.svelte +1 -3
- package/dist/form/Select.svelte +25 -28
- package/dist/form/SelectMulti.svelte +36 -54
- package/dist/form/StringArrayBuilder.svelte +1 -3
- package/dist/form/TextArea.svelte +5 -6
- package/dist/form/Textbox.svelte +10 -4
- package/dist/form/TimePicker.svelte +10 -4
- package/dist/icon/Icon.svelte +8 -4
- package/dist/index.d.ts +147 -146
- package/dist/index.js +78 -77
- package/dist/layout/CustomNavbar.svelte +1 -1
- package/dist/layout/Navbar.svelte +3 -4
- package/dist/layout/Navbar.svelte.d.ts +1 -2
- package/dist/layout/NavbarItemDisplay.svelte +1 -1
- package/dist/layout/NavbarItemView.svelte +3 -1
- package/dist/layout/Sidenav.svelte +1 -1
- package/dist/models/ComboBoxItem.d.ts +4 -4
- package/dist/models/NotifyModel.js +0 -1
- package/dist/theme.svelte.d.ts +1 -1
- package/dist/theme.svelte.js +46 -22
- package/dist/ui/Accordion.svelte +1 -3
- package/dist/ui/AccordionItem.svelte +8 -16
- package/dist/ui/Badge.svelte +11 -19
- package/dist/ui/Button.svelte +15 -17
- package/dist/ui/ButtonVariant.d.ts +1 -7
- package/dist/ui/ContextMenu.svelte +5 -11
- package/dist/ui/Dialog.svelte +74 -30
- package/dist/ui/Dialog.svelte.d.ts +8 -2
- package/dist/ui/Dropdown.svelte +21 -10
- package/dist/ui/MultiSortable.svelte +1 -3
- package/dist/ui/Pagination.svelte +2 -2
- package/dist/ui/Popover.svelte +16 -37
- package/dist/ui/ProgressBar.svelte +4 -10
- package/dist/ui/ProgressBarVariant.d.ts +2 -7
- package/dist/ui/ThemeSwitcher.svelte +2 -1
- package/dist/ui/Tooltip.svelte +1 -37
- package/dist/util/DateFunctions.js +1 -1
- package/dist/util/Floating.d.ts +70 -0
- package/dist/util/Floating.js +321 -0
- package/dist/util/NavigateTo.js +2 -2
- package/dist/util/Transitions.d.ts +1 -1
- package/dist/util/Transitions.js +1 -1
- package/package.json +1 -1
|
@@ -5,11 +5,5 @@
|
|
|
5
5
|
* - `primary` → `--ui-primary` (dark brand colour)
|
|
6
6
|
* - `accent` → `--ui-accent` (light brand colour)
|
|
7
7
|
* - `tertiary` → `--ui-tertiary` (supporting colour)
|
|
8
|
-
*
|
|
9
|
-
* The `midnight`, `pistachio` and `turquoise` names are legacy aliases for
|
|
10
|
-
* `primary`, `accent` and `tertiary` respectively. They keep working but new code
|
|
11
|
-
* should use the semantic names.
|
|
12
8
|
*/
|
|
13
|
-
export type ButtonVariant = 'white' | 'secondary' | 'secondary-subtle' | 'secondary-selected' | 'danger' | 'danger-subtle' | 'auto-subtle' | 'auto-outline' | 'primary' | 'primary-outline' | 'primary-subtle' | 'accent' | 'accent-outline' | 'accent-subtle' | 'tertiary' | 'tertiary-subtle' | 'shaded'
|
|
14
|
-
/** @deprecated Use `primary`, `accent` and `tertiary` instead. */
|
|
15
|
-
export type LegacyButtonVariant = 'pistachio' | 'pistachio-outline' | 'pistachio-subtle' | 'midnight' | 'midnight-outline' | 'midnight-subtle' | 'turquoise' | 'turquoise-subtle';
|
|
9
|
+
export type ButtonVariant = 'white' | 'secondary' | 'secondary-subtle' | 'secondary-selected' | 'danger' | 'danger-subtle' | 'auto-subtle' | 'auto-outline' | 'primary' | 'primary-outline' | 'primary-subtle' | 'accent' | 'accent-outline' | 'accent-subtle' | 'tertiary' | 'tertiary-subtle' | 'shaded';
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
|
|
39
39
|
<script lang="ts">
|
|
40
40
|
import ContextMenuList from './ContextMenuList.svelte';
|
|
41
|
+
import { topLayer } from '../util/Floating.js';
|
|
41
42
|
|
|
42
43
|
let {
|
|
43
44
|
open = $bindable(false),
|
|
@@ -54,16 +55,6 @@
|
|
|
54
55
|
let posY = $state(0);
|
|
55
56
|
let placed = $state(false);
|
|
56
57
|
|
|
57
|
-
/** Move the menu to <body> so it can't be clipped by overflow/transform ancestors. */
|
|
58
|
-
function portal(node: HTMLElement) {
|
|
59
|
-
document.body.appendChild(node);
|
|
60
|
-
return {
|
|
61
|
-
destroy() {
|
|
62
|
-
node.remove();
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
58
|
function close() {
|
|
68
59
|
if (!open) return;
|
|
69
60
|
open = false;
|
|
@@ -127,8 +118,11 @@
|
|
|
127
118
|
|
|
128
119
|
{#if open}
|
|
129
120
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
121
|
+
<!-- Promoted to the top layer so overflow/transform ancestors cannot clip it. Unlike a
|
|
122
|
+
body portal, the menu stays inside its subtree, so it remains usable inside a
|
|
123
|
+
modal Dialog (everything outside an open modal is inert). -->
|
|
130
124
|
<div
|
|
131
|
-
use:
|
|
125
|
+
use:topLayer
|
|
132
126
|
bind:this={menuEl}
|
|
133
127
|
class="pui-ctxmenu {classes}"
|
|
134
128
|
style="left: {posX}px; top: {posY}px; visibility: {placed ? 'visible' : 'hidden'};"
|
package/dist/ui/Dialog.svelte
CHANGED
|
@@ -25,10 +25,16 @@
|
|
|
25
25
|
title?: string | undefined;
|
|
26
26
|
/** Render without the Paper wrapper component */
|
|
27
27
|
noPaper?: boolean;
|
|
28
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* Let content overflow the dialog bounds instead of scrolling inside it.
|
|
30
|
+
* The body scrolls by default; dropdowns, pickers and tooltips render in
|
|
31
|
+
* the browser's top layer, so they are never clipped by that scroll.
|
|
32
|
+
*/
|
|
33
|
+
allowOverflow?: boolean;
|
|
34
|
+
/** @deprecated Scrolling is now the default; use `allowOverflow` to opt out. */
|
|
29
35
|
preventOverflow?: boolean;
|
|
30
36
|
/** Size preset for the dialog width */
|
|
31
|
-
size?: '
|
|
37
|
+
size?: 'md' | 'lg' | 'xl' | 'full';
|
|
32
38
|
/** Additional CSS classes */
|
|
33
39
|
class?: string;
|
|
34
40
|
/** Footer content (typically action buttons) */
|
|
@@ -42,6 +48,7 @@
|
|
|
42
48
|
allowImplicitClose = true,
|
|
43
49
|
title = undefined,
|
|
44
50
|
noPaper = false,
|
|
51
|
+
allowOverflow = false,
|
|
45
52
|
preventOverflow = false,
|
|
46
53
|
size = 'md',
|
|
47
54
|
class: classes = '',
|
|
@@ -50,6 +57,12 @@
|
|
|
50
57
|
}: DialogProps = $props();
|
|
51
58
|
|
|
52
59
|
const uid = $props.id();
|
|
60
|
+
|
|
61
|
+
// preventOverflow is the legacy opt-in for the behaviour that is now default,
|
|
62
|
+
// so it wins over allowOverflow when both are set
|
|
63
|
+
const overflowClass = $derived(
|
|
64
|
+
allowOverflow && !preventOverflow ? 'overflow-visible' : 'overflow-y-auto'
|
|
65
|
+
);
|
|
53
66
|
const titleId = `${uid}-title`;
|
|
54
67
|
|
|
55
68
|
let dialogElement: HTMLDialogElement | undefined = $state(undefined);
|
|
@@ -129,12 +142,12 @@
|
|
|
129
142
|
{#snippet DialogInner()}
|
|
130
143
|
{#if title}
|
|
131
144
|
<div class="dialog-title">
|
|
132
|
-
<span id={titleId} class="
|
|
145
|
+
<span id={titleId} class="dialog-title-text">{title}</span>
|
|
133
146
|
{#if allowImplicitClose}
|
|
134
147
|
<ActionIcon
|
|
135
148
|
onclick={() => (show = false)}
|
|
136
149
|
variant="secondary-subtle"
|
|
137
|
-
class="
|
|
150
|
+
class="dialog-close"
|
|
138
151
|
svg={iconX}
|
|
139
152
|
size="1rem"
|
|
140
153
|
ariaLabel="Close dialog"
|
|
@@ -142,7 +155,7 @@
|
|
|
142
155
|
{/if}
|
|
143
156
|
</div>
|
|
144
157
|
{/if}
|
|
145
|
-
<div class="dialog-body {
|
|
158
|
+
<div class="dialog-body {overflowClass}">
|
|
146
159
|
{@render children?.()}
|
|
147
160
|
</div>
|
|
148
161
|
{#if footer}
|
|
@@ -169,8 +182,9 @@
|
|
|
169
182
|
{#if noPaper}
|
|
170
183
|
{@render DialogInner()}
|
|
171
184
|
{:else}
|
|
172
|
-
<div class="dialog-paper
|
|
173
|
-
|
|
185
|
+
<div class="dialog-paper">
|
|
186
|
+
<!-- Dialog owns the section padding so header and footer dividers span the card -->
|
|
187
|
+
<Paper containerClass={classes} noPadding noGap>
|
|
174
188
|
{@render DialogInner()}
|
|
175
189
|
</Paper>
|
|
176
190
|
</div>
|
|
@@ -247,7 +261,7 @@
|
|
|
247
261
|
background-color: transparent;
|
|
248
262
|
overflow: visible;
|
|
249
263
|
margin: auto;
|
|
250
|
-
max-width:
|
|
264
|
+
max-width: calc(100% - 2rem);
|
|
251
265
|
/* Cap to the visible viewport with equal margins; the flex chain below
|
|
252
266
|
(dialog > inner > paper > body, each min-height: 0) passes this limit
|
|
253
267
|
down so the body shrinks instead of spilling past the bottom edge.
|
|
@@ -281,36 +295,77 @@
|
|
|
281
295
|
min-height: 0;
|
|
282
296
|
}
|
|
283
297
|
|
|
284
|
-
/* Paper sits inside the height chain
|
|
285
|
-
|
|
286
|
-
|
|
298
|
+
/* Paper sits inside the height chain. Without a gap its body is a plain
|
|
299
|
+
block, so make it a shrinkable flex column here; the sections below rely
|
|
300
|
+
on that to give the body the leftover height */
|
|
301
|
+
.dialog :global(.paper),
|
|
302
|
+
.dialog :global(.paper-body) {
|
|
303
|
+
display: flex;
|
|
304
|
+
flex-direction: column;
|
|
287
305
|
min-height: 0;
|
|
288
306
|
}
|
|
289
307
|
|
|
308
|
+
/* Section padding: 1rem inset, 0.75rem vertical for the header and footer,
|
|
309
|
+
1rem for the body. Dividers run edge to edge */
|
|
290
310
|
.dialog-title {
|
|
291
311
|
display: flex;
|
|
312
|
+
align-items: center;
|
|
292
313
|
justify-content: space-between;
|
|
293
|
-
|
|
294
|
-
padding
|
|
314
|
+
gap: 0.75rem;
|
|
315
|
+
padding: 0.75rem 1rem;
|
|
295
316
|
border-bottom: 1px solid var(--border-color);
|
|
317
|
+
flex-shrink: 0;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.dialog-title-text {
|
|
321
|
+
font-size: 1.0625rem;
|
|
296
322
|
font-weight: 600;
|
|
323
|
+
line-height: 1.4;
|
|
324
|
+
min-width: 0;
|
|
325
|
+
overflow-wrap: anywhere;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/* Keep the close button from setting the header height: its hit area
|
|
329
|
+
overhangs the text line and the right inset instead */
|
|
330
|
+
.dialog-title :global(button.dialog-close) {
|
|
297
331
|
flex-shrink: 0;
|
|
332
|
+
padding: 0.25rem;
|
|
333
|
+
margin: -0.25rem -0.5rem -0.25rem 0;
|
|
334
|
+
line-height: 0;
|
|
298
335
|
}
|
|
299
336
|
|
|
337
|
+
/* The only scroll container. Padding lives inside it so focus rings and
|
|
338
|
+
negative-margin rows are not clipped, and the scrollbar hugs the edge */
|
|
300
339
|
.dialog-body {
|
|
301
340
|
display: flex;
|
|
302
341
|
flex-direction: column;
|
|
303
342
|
min-width: 0;
|
|
304
343
|
min-height: 0;
|
|
305
344
|
flex-shrink: 1;
|
|
345
|
+
padding: 1rem;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/* The body padding is the spacing; don't let the first and last elements
|
|
349
|
+
(or a wrapper's first and last) add their own margins on top of it */
|
|
350
|
+
.dialog-body > :global(:first-child),
|
|
351
|
+
.dialog-body > :global(:first-child > :first-child) {
|
|
352
|
+
margin-top: 0;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.dialog-body > :global(:last-child),
|
|
356
|
+
.dialog-body > :global(:last-child > :last-child) {
|
|
357
|
+
margin-bottom: 0;
|
|
306
358
|
}
|
|
307
359
|
|
|
360
|
+
/* Actions align right. A secondary action can sit on the left with
|
|
361
|
+
`margin-right: auto` */
|
|
308
362
|
.dialog-footer {
|
|
309
363
|
display: flex;
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
border-top: 1px solid var(--border-color);
|
|
364
|
+
align-items: center;
|
|
365
|
+
justify-content: flex-end;
|
|
313
366
|
gap: 0.5rem;
|
|
367
|
+
padding: 0.75rem 1rem;
|
|
368
|
+
border-top: 1px solid var(--border-color);
|
|
314
369
|
flex-shrink: 0;
|
|
315
370
|
}
|
|
316
371
|
|
|
@@ -322,33 +377,22 @@
|
|
|
322
377
|
overflow-y: auto;
|
|
323
378
|
}
|
|
324
379
|
|
|
325
|
-
.dialog-no-paper {
|
|
326
|
-
padding: 1rem;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
380
|
.dialog-paper,
|
|
330
381
|
.dialog-no-paper {
|
|
331
382
|
display: flex;
|
|
332
383
|
flex-direction: column;
|
|
333
|
-
box-sizing: border-box;
|
|
334
|
-
border-radius: 0.25rem;
|
|
335
|
-
background: #f1f3f5;
|
|
336
384
|
min-height: 0;
|
|
337
385
|
}
|
|
338
386
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
background-color:
|
|
387
|
+
.dialog-no-paper {
|
|
388
|
+
border-radius: 0.25rem;
|
|
389
|
+
background-color: var(--paper-body-bg);
|
|
342
390
|
}
|
|
343
391
|
|
|
344
392
|
dialog::backdrop {
|
|
345
393
|
background-color: #0008;
|
|
346
394
|
}
|
|
347
395
|
|
|
348
|
-
.dialog.sm {
|
|
349
|
-
width: 20rem;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
396
|
.dialog.md {
|
|
353
397
|
width: 30rem;
|
|
354
398
|
}
|
|
@@ -18,10 +18,16 @@ export interface DialogProps {
|
|
|
18
18
|
title?: string | undefined;
|
|
19
19
|
/** Render without the Paper wrapper component */
|
|
20
20
|
noPaper?: boolean;
|
|
21
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Let content overflow the dialog bounds instead of scrolling inside it.
|
|
23
|
+
* The body scrolls by default; dropdowns, pickers and tooltips render in
|
|
24
|
+
* the browser's top layer, so they are never clipped by that scroll.
|
|
25
|
+
*/
|
|
26
|
+
allowOverflow?: boolean;
|
|
27
|
+
/** @deprecated Scrolling is now the default; use `allowOverflow` to opt out. */
|
|
22
28
|
preventOverflow?: boolean;
|
|
23
29
|
/** Size preset for the dialog width */
|
|
24
|
-
size?: '
|
|
30
|
+
size?: 'md' | 'lg' | 'xl' | 'full';
|
|
25
31
|
/** Additional CSS classes */
|
|
26
32
|
class?: string;
|
|
27
33
|
/** Footer content (typically action buttons) */
|
package/dist/ui/Dropdown.svelte
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
import type { Hyperlink } from '../models/Hyperlink.js';
|
|
6
6
|
import LinkCollection from '../display/LinkCollection.svelte';
|
|
7
7
|
import { iconChevronDown } from '../icon/index.js';
|
|
8
|
+
import { floating } from '../util/Floating.js';
|
|
9
|
+
import { clickOutside } from '../util/ClickOutside.js';
|
|
8
10
|
|
|
9
11
|
export interface DropdownProps {
|
|
10
12
|
/** Whether the dropdown is expanded (bindable) */
|
|
@@ -32,16 +34,32 @@
|
|
|
32
34
|
class: classes = '',
|
|
33
35
|
alignToButton = 'left'
|
|
34
36
|
}: DropdownProps = $props();
|
|
37
|
+
|
|
38
|
+
let triggerElement: HTMLElement | undefined = $state(undefined);
|
|
35
39
|
</script>
|
|
36
40
|
|
|
37
|
-
|
|
41
|
+
<!-- The floating content is still a DOM descendant of this wrapper (only its rendering
|
|
42
|
+
moves to the top layer), so a contains() check covers both the button and the menu -->
|
|
43
|
+
<div
|
|
44
|
+
class="dropdown {classes} {alignToButton}"
|
|
45
|
+
bind:this={triggerElement}
|
|
46
|
+
use:clickOutside={() => (show = false)}
|
|
47
|
+
>
|
|
38
48
|
<Button class="ps-2" {variant} svg={buttonSvg} onclick={() => (show = !show)}>
|
|
39
49
|
{label}
|
|
40
50
|
</Button>
|
|
41
51
|
{#if show}
|
|
52
|
+
<!-- Rendered in the top layer so a scrolling Dialog body cannot clip it -->
|
|
42
53
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
43
54
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
44
|
-
<div
|
|
55
|
+
<div
|
|
56
|
+
class="dropdown-content"
|
|
57
|
+
use:floating={{
|
|
58
|
+
anchor: () => triggerElement,
|
|
59
|
+
placement: alignToButton === 'right' ? 'bottom-end' : 'bottom-start'
|
|
60
|
+
}}
|
|
61
|
+
onclick={(e) => e.stopPropagation()}
|
|
62
|
+
>
|
|
45
63
|
{#if links.length > 0 && Array.isArray(links[0])}
|
|
46
64
|
{#each links as group}
|
|
47
65
|
<div class="dropdown-group">
|
|
@@ -60,10 +78,8 @@
|
|
|
60
78
|
position: relative;
|
|
61
79
|
}
|
|
62
80
|
|
|
81
|
+
/* Position and top-layer promotion come from use:floating */
|
|
63
82
|
.dropdown-content {
|
|
64
|
-
position: absolute;
|
|
65
|
-
top: 100%;
|
|
66
|
-
left: 0;
|
|
67
83
|
background-color: var(--paper-body-bg);
|
|
68
84
|
min-width: 160px;
|
|
69
85
|
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
|
|
@@ -94,11 +110,6 @@
|
|
|
94
110
|
background-color: var(--border-color);
|
|
95
111
|
}
|
|
96
112
|
|
|
97
|
-
.dropdown.right .dropdown-content {
|
|
98
|
-
left: auto;
|
|
99
|
-
right: 0;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
113
|
:global(.dark) {
|
|
103
114
|
.dropdown-content :global(a) {
|
|
104
115
|
color: var(--ui-accent);
|
|
@@ -114,9 +114,7 @@
|
|
|
114
114
|
if (!accepts(ctx)) return;
|
|
115
115
|
const host = container;
|
|
116
116
|
if (!host) return;
|
|
117
|
-
const itemEls = Array.from(
|
|
118
|
-
host.querySelectorAll<HTMLElement>(':scope > .pui-msortable-item')
|
|
119
|
-
);
|
|
117
|
+
const itemEls = Array.from(host.querySelectorAll<HTMLElement>(':scope > .pui-msortable-item'));
|
|
120
118
|
if (itemEls.length === 0) {
|
|
121
119
|
dropIndex = null;
|
|
122
120
|
return;
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
// For maxPageButtons, we need to fit: first, last, and some middle pages + ellipsis
|
|
49
49
|
// Reserve 2 slots for first and last pages, leaving maxPageButtons - 2 for middle + ellipsis
|
|
50
50
|
const middleSlots = maxPageButtons - 2;
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
// Calculate how many pages we can show around the selected page
|
|
53
53
|
const sidePages = Math.floor((middleSlots - 1) / 2); // -1 to account for potential ellipsis
|
|
54
54
|
|
|
@@ -134,7 +134,7 @@
|
|
|
134
134
|
display: flex;
|
|
135
135
|
justify-content: center;
|
|
136
136
|
}
|
|
137
|
-
|
|
137
|
+
|
|
138
138
|
:global(.dark) .pagination-container :global(.paginate-button.secondary-selected) {
|
|
139
139
|
color: black;
|
|
140
140
|
}
|
package/dist/ui/Popover.svelte
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import type { Snippet } from 'svelte';
|
|
4
4
|
import Button from './Button.svelte';
|
|
5
5
|
import { clickOutside } from '../util/ClickOutside.js';
|
|
6
|
+
import { floating } from '../util/Floating.js';
|
|
6
7
|
|
|
7
8
|
type PopoverPosition = 'top' | 'bottom' | 'left' | 'right';
|
|
8
9
|
type PopoverTrigger = 'click' | 'hover';
|
|
@@ -40,6 +41,7 @@
|
|
|
40
41
|
hoverDelay = 1000
|
|
41
42
|
}: PopoverProps = $props();
|
|
42
43
|
|
|
44
|
+
let triggerElement: HTMLElement | undefined = $state(undefined);
|
|
43
45
|
let hoverTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
44
46
|
let isHovering = false;
|
|
45
47
|
|
|
@@ -99,7 +101,8 @@
|
|
|
99
101
|
{#if button}
|
|
100
102
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
101
103
|
<!-- svelte-ignore a11y_mouse_events_have_key_events -->
|
|
102
|
-
<button
|
|
104
|
+
<button
|
|
105
|
+
bind:this={triggerElement}
|
|
103
106
|
onclick={handleClick}
|
|
104
107
|
onmouseenter={handleMouseEnter}
|
|
105
108
|
onmouseleave={handleMouseLeave}
|
|
@@ -110,6 +113,7 @@
|
|
|
110
113
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
111
114
|
<!-- svelte-ignore a11y_mouse_events_have_key_events -->
|
|
112
115
|
<div
|
|
116
|
+
bind:this={triggerElement}
|
|
113
117
|
onmouseenter={handleMouseEnter}
|
|
114
118
|
onmouseleave={handleMouseLeave}
|
|
115
119
|
>
|
|
@@ -122,8 +126,12 @@
|
|
|
122
126
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
123
127
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
124
128
|
<!-- svelte-ignore a11y_mouse_events_have_key_events -->
|
|
129
|
+
<!-- Rendered in the top layer so a scrolling Dialog body cannot clip it.
|
|
130
|
+
data-placement (set by use:floating, after any flip) drives the arrow. -->
|
|
125
131
|
<div
|
|
126
|
-
class="popover-content
|
|
132
|
+
class="popover-content"
|
|
133
|
+
data-placement={position}
|
|
134
|
+
use:floating={{ anchor: () => triggerElement, placement: position, offset: 6 }}
|
|
127
135
|
onclick={(e) => e.stopPropagation()}
|
|
128
136
|
onmouseenter={handleContentMouseEnter}
|
|
129
137
|
onmouseleave={handleContentMouseLeave}
|
|
@@ -140,9 +148,9 @@
|
|
|
140
148
|
position: relative;
|
|
141
149
|
}
|
|
142
150
|
|
|
151
|
+
/* Position and top-layer promotion come from use:floating */
|
|
143
152
|
.popover-content {
|
|
144
153
|
display: block;
|
|
145
|
-
position: absolute;
|
|
146
154
|
background-color: var(--paper-body-bg);
|
|
147
155
|
border: 1px solid var(--border-color);
|
|
148
156
|
min-width: 160px;
|
|
@@ -151,64 +159,35 @@
|
|
|
151
159
|
border-radius: 4px;
|
|
152
160
|
}
|
|
153
161
|
|
|
154
|
-
/*
|
|
155
|
-
.popover-bottom {
|
|
156
|
-
top: 100%;
|
|
157
|
-
left: 50%;
|
|
158
|
-
transform: translateX(-50%);
|
|
159
|
-
margin-top: 4px;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
.popover-top {
|
|
163
|
-
bottom: 100%;
|
|
164
|
-
left: 50%;
|
|
165
|
-
transform: translateX(-50%);
|
|
166
|
-
margin-bottom: 4px;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
.popover-right {
|
|
170
|
-
top: 50%;
|
|
171
|
-
left: 100%;
|
|
172
|
-
transform: translateY(-50%);
|
|
173
|
-
margin-left: 4px;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
.popover-left {
|
|
177
|
-
top: 50%;
|
|
178
|
-
right: 100%;
|
|
179
|
-
transform: translateY(-50%);
|
|
180
|
-
margin-right: 4px;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/* Arrow indicators for each position */
|
|
162
|
+
/* Arrow indicators for each resolved placement */
|
|
184
163
|
.popover-content::before {
|
|
185
164
|
content: '';
|
|
186
165
|
position: absolute;
|
|
187
166
|
border: 6px solid transparent;
|
|
188
167
|
}
|
|
189
168
|
|
|
190
|
-
.popover-bottom::before {
|
|
169
|
+
.popover-content[data-placement='bottom']::before {
|
|
191
170
|
top: -12px;
|
|
192
171
|
left: 50%;
|
|
193
172
|
transform: translateX(-50%);
|
|
194
173
|
border-bottom-color: var(--border-color);
|
|
195
174
|
}
|
|
196
175
|
|
|
197
|
-
.popover-top::before {
|
|
176
|
+
.popover-content[data-placement='top']::before {
|
|
198
177
|
bottom: -12px;
|
|
199
178
|
left: 50%;
|
|
200
179
|
transform: translateX(-50%);
|
|
201
180
|
border-top-color: var(--border-color);
|
|
202
181
|
}
|
|
203
182
|
|
|
204
|
-
.popover-right::before {
|
|
183
|
+
.popover-content[data-placement='right']::before {
|
|
205
184
|
top: 50%;
|
|
206
185
|
left: -12px;
|
|
207
186
|
transform: translateY(-50%);
|
|
208
187
|
border-right-color: var(--border-color);
|
|
209
188
|
}
|
|
210
189
|
|
|
211
|
-
.popover-left::before {
|
|
190
|
+
.popover-content[data-placement='left']::before {
|
|
212
191
|
top: 50%;
|
|
213
192
|
right: -12px;
|
|
214
193
|
transform: translateY(-50%);
|
|
@@ -63,10 +63,7 @@
|
|
|
63
63
|
</div>
|
|
64
64
|
{/each}
|
|
65
65
|
{:else}
|
|
66
|
-
<div
|
|
67
|
-
class="fill {variant} {animated ? 'animated' : ''}"
|
|
68
|
-
style="width: {clampedValue}%"
|
|
69
|
-
>
|
|
66
|
+
<div class="fill {variant} {animated ? 'animated' : ''}" style="width: {clampedValue}%">
|
|
70
67
|
{#if label}
|
|
71
68
|
<span class="label">{label}</span>
|
|
72
69
|
{/if}
|
|
@@ -135,20 +132,17 @@
|
|
|
135
132
|
}
|
|
136
133
|
|
|
137
134
|
/* Variant colors */
|
|
138
|
-
.primary
|
|
139
|
-
.midnight {
|
|
135
|
+
.primary {
|
|
140
136
|
background-color: var(--ui-primary);
|
|
141
137
|
color: #fff;
|
|
142
138
|
}
|
|
143
139
|
|
|
144
|
-
.accent
|
|
145
|
-
.pistachio {
|
|
140
|
+
.accent {
|
|
146
141
|
background-color: var(--ui-accent);
|
|
147
142
|
color: var(--ui-primary);
|
|
148
143
|
}
|
|
149
144
|
|
|
150
|
-
.tertiary
|
|
151
|
-
.turquoise {
|
|
145
|
+
.tertiary {
|
|
152
146
|
background-color: var(--ui-tertiary);
|
|
153
147
|
color: var(--ui-primary);
|
|
154
148
|
}
|
|
@@ -1,7 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
* legacy aliases for `primary`, `accent` and `tertiary`.
|
|
4
|
-
*/
|
|
5
|
-
export type ProgressBarVariant = 'primary' | 'accent' | 'tertiary' | 'danger' | 'secondary' | LegacyProgressBarVariant;
|
|
6
|
-
/** @deprecated Use `primary`, `accent` and `tertiary` instead. */
|
|
7
|
-
export type LegacyProgressBarVariant = 'midnight' | 'pistachio' | 'turquoise';
|
|
1
|
+
/** Colour variants for ProgressBar, named after the `--ui-*` tokens. */
|
|
2
|
+
export type ProgressBarVariant = 'primary' | 'accent' | 'tertiary' | 'danger' | 'secondary';
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
darkVariant?: ButtonVariant;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
let { lightVariant = 'primary-subtle', darkVariant = 'accent-subtle' }: ThemeSwitcherProps =
|
|
17
|
+
let { lightVariant = 'primary-subtle', darkVariant = 'accent-subtle' }: ThemeSwitcherProps =
|
|
18
|
+
$props();
|
|
18
19
|
|
|
19
20
|
const themeCycle = ['light', 'dark', 'system'] as const;
|
|
20
21
|
|
package/dist/ui/Tooltip.svelte
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
import { fade } from 'svelte/transition';
|
|
4
|
+
import { findFixedContainingBlock } from '../util/Floating.js';
|
|
4
5
|
|
|
5
6
|
export type TooltipLocation =
|
|
6
7
|
| 'top'
|
|
@@ -140,43 +141,6 @@
|
|
|
140
141
|
return Math.max(min, Math.min(value, max));
|
|
141
142
|
}
|
|
142
143
|
|
|
143
|
-
/**
|
|
144
|
-
* A transform, filter, perspective, containment or container-type on an ancestor
|
|
145
|
-
* makes that ancestor the containing block for `position: fixed` descendants —
|
|
146
|
-
* left/top stop meaning "from the viewport" while getBoundingClientRect() keeps
|
|
147
|
-
* reporting viewport coordinates. Dialog hits this: its open animation uses a
|
|
148
|
-
* `forwards` fill, which leaves `transform: translateY(0)` applied while open.
|
|
149
|
-
*/
|
|
150
|
-
function findFixedContainingBlock(element: HTMLElement): HTMLElement | null {
|
|
151
|
-
let node = element.parentElement;
|
|
152
|
-
|
|
153
|
-
while (node) {
|
|
154
|
-
const style = getComputedStyle(node);
|
|
155
|
-
// Newer properties go through getPropertyValue so an older DOM lib (or a
|
|
156
|
-
// browser without support) reports '' instead of failing to compile.
|
|
157
|
-
const containerType = style.getPropertyValue('container-type');
|
|
158
|
-
const backdropFilter =
|
|
159
|
-
style.getPropertyValue('backdrop-filter') ||
|
|
160
|
-
style.getPropertyValue('-webkit-backdrop-filter');
|
|
161
|
-
|
|
162
|
-
if (
|
|
163
|
-
style.transform !== 'none' ||
|
|
164
|
-
style.perspective !== 'none' ||
|
|
165
|
-
style.filter !== 'none' ||
|
|
166
|
-
(backdropFilter !== '' && backdropFilter !== 'none') ||
|
|
167
|
-
(containerType !== '' && containerType !== 'normal') ||
|
|
168
|
-
/\b(paint|layout|strict|content)\b/.test(style.contain) ||
|
|
169
|
-
/\b(transform|perspective|filter)\b/.test(style.willChange)
|
|
170
|
-
) {
|
|
171
|
-
return node;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
node = node.parentElement;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
return null;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
144
|
/** Viewport coordinates of the origin that left/top are measured from. */
|
|
181
145
|
function containingBlockOrigin(): { x: number; y: number } {
|
|
182
146
|
if (!fixedContainer) return { x: 0, y: 0 };
|