@placeholderco/placeholder-ui 2.1.1 → 2.2.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 (80) hide show
  1. package/dist/editors/CodeMirror.svelte +295 -297
  2. package/dist/editors/tiptap/LinkActionMenu.svelte +6 -1
  3. package/dist/editors/tiptap/extensions/Alert.js +17 -17
  4. package/dist/editors/tiptap/extensions/Div.js +19 -19
  5. package/dist/editors/tiptap/extensions/Span.js +14 -14
  6. package/dist/editors/tiptap/toolbar/AlertButton.svelte +254 -253
  7. package/dist/editors/tiptap/toolbar/AlignCenterButton.svelte +11 -11
  8. package/dist/editors/tiptap/toolbar/AlignJustifyButton.svelte +11 -11
  9. package/dist/editors/tiptap/toolbar/AlignLeftButton.svelte +11 -11
  10. package/dist/editors/tiptap/toolbar/AlignRightButton.svelte +11 -11
  11. package/dist/editors/tiptap/toolbar/BoldButton.svelte +11 -11
  12. package/dist/editors/tiptap/toolbar/BulletListButton.svelte +11 -11
  13. package/dist/editors/tiptap/toolbar/CodeButton.svelte +11 -11
  14. package/dist/editors/tiptap/toolbar/ColorButton.svelte +265 -265
  15. package/dist/editors/tiptap/toolbar/DocumentButton.svelte +51 -51
  16. package/dist/editors/tiptap/toolbar/HeadingButton.svelte +21 -21
  17. package/dist/editors/tiptap/toolbar/HtmlButton.svelte +48 -52
  18. package/dist/editors/tiptap/toolbar/ImageButton.svelte +35 -39
  19. package/dist/editors/tiptap/toolbar/ItalicButton.svelte +11 -11
  20. package/dist/editors/tiptap/toolbar/LinkButton.svelte +249 -249
  21. package/dist/editors/tiptap/toolbar/OrderedListButton.svelte +11 -11
  22. package/dist/editors/tiptap/toolbar/RedoButton.svelte +11 -11
  23. package/dist/editors/tiptap/toolbar/StrikethroughButton.svelte +11 -11
  24. package/dist/editors/tiptap/toolbar/SubscriptButton.svelte +11 -11
  25. package/dist/editors/tiptap/toolbar/SuperscriptButton.svelte +11 -11
  26. package/dist/editors/tiptap/toolbar/TableButton.svelte +231 -224
  27. package/dist/editors/tiptap/toolbar/ToolbarButton.svelte +59 -65
  28. package/dist/editors/tiptap/toolbar/UnderlineButton.svelte +11 -11
  29. package/dist/editors/tiptap/toolbar/UndoButton.svelte +11 -11
  30. package/dist/form/Autocomplete.svelte +24 -22
  31. package/dist/form/AutocompleteMulti.svelte +24 -22
  32. package/dist/form/Checkbox.svelte +4 -4
  33. package/dist/form/Chips.svelte +15 -7
  34. package/dist/form/ColorPicker.svelte +5 -20
  35. package/dist/form/ComboBox.svelte +4 -4
  36. package/dist/form/ComboBoxItemBuilder.svelte +14 -32
  37. package/dist/form/ComboBoxMulti.svelte +7 -3
  38. package/dist/form/CronBuilder.svelte +32 -110
  39. package/dist/form/DatePicker.svelte +9 -28
  40. package/dist/form/DateRangePicker.svelte +46 -36
  41. package/dist/form/DateTimePicker.svelte +11 -30
  42. package/dist/form/Number.svelte +1 -3
  43. package/dist/form/RadioGroup.svelte +1 -3
  44. package/dist/form/Select.svelte +25 -28
  45. package/dist/form/SelectMulti.svelte +36 -38
  46. package/dist/form/StringArrayBuilder.svelte +1 -3
  47. package/dist/form/TextArea.svelte +5 -6
  48. package/dist/form/Textbox.svelte +10 -4
  49. package/dist/form/TimePicker.svelte +10 -4
  50. package/dist/icon/Icon.svelte +5 -1
  51. package/dist/index.d.ts +147 -146
  52. package/dist/index.js +78 -77
  53. package/dist/layout/CustomNavbar.svelte +1 -1
  54. package/dist/layout/NavbarItemDisplay.svelte +1 -1
  55. package/dist/layout/NavbarItemView.svelte +3 -1
  56. package/dist/layout/Sidenav.svelte +1 -1
  57. package/dist/models/ComboBoxItem.d.ts +4 -4
  58. package/dist/models/NotifyModel.js +0 -1
  59. package/dist/theme.svelte.d.ts +1 -1
  60. package/dist/theme.svelte.js +46 -22
  61. package/dist/ui/Accordion.svelte +1 -3
  62. package/dist/ui/Badge.svelte +4 -5
  63. package/dist/ui/Button.svelte +7 -1
  64. package/dist/ui/ContextMenu.svelte +5 -11
  65. package/dist/ui/Dialog.svelte +16 -3
  66. package/dist/ui/Dialog.svelte.d.ts +7 -1
  67. package/dist/ui/Dropdown.svelte +21 -10
  68. package/dist/ui/MultiSortable.svelte +1 -3
  69. package/dist/ui/Pagination.svelte +2 -2
  70. package/dist/ui/Popover.svelte +16 -37
  71. package/dist/ui/ProgressBar.svelte +1 -4
  72. package/dist/ui/ThemeSwitcher.svelte +2 -1
  73. package/dist/ui/Tooltip.svelte +1 -37
  74. package/dist/util/DateFunctions.js +1 -1
  75. package/dist/util/Floating.d.ts +70 -0
  76. package/dist/util/Floating.js +321 -0
  77. package/dist/util/NavigateTo.js +2 -2
  78. package/dist/util/Transitions.d.ts +1 -1
  79. package/dist/util/Transitions.js +1 -1
  80. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,77 +1,78 @@
1
- export { default as Accordion } from "./ui/Accordion.svelte";
2
- export { default as AccordionItem } from "./ui/AccordionItem.svelte";
3
- export { default as Button } from "./ui/Button.svelte";
4
- export { default as ActionIcon } from "./ui/ActionIcon.svelte";
5
- export { default as Badge } from "./ui/Badge.svelte";
6
- export { default as Dialog } from "./ui/Dialog.svelte";
7
- export { default as Drawer } from "./ui/Drawer.svelte";
8
- export { default as Dropdown } from "./ui/Dropdown.svelte";
9
- export { default as Dropzone } from "./ui/Dropzone.svelte";
10
- export { default as Link } from "./ui/Link.svelte";
11
- export { default as Loader } from "./ui/Loader.svelte";
12
- export { default as Logo } from "./ui/Logo.svelte";
13
- export { default as Pagination } from "./ui/Pagination.svelte";
14
- export { default as Popover } from "./ui/Popover.svelte";
15
- export { default as ContextMenu } from "./ui/ContextMenu.svelte";
16
- export { default as ProgressBar } from "./ui/ProgressBar.svelte";
17
- export { default as Tabs } from "./ui/Tabs.svelte";
18
- export { default as Table } from "./ui/Table.svelte";
19
- export { default as ThemeSwitcher } from "./ui/ThemeSwitcher.svelte";
20
- export { default as Sortable } from "./ui/Sortable.svelte";
21
- export { default as MultiSortable } from "./ui/MultiSortable.svelte";
22
- export { default as Toaster } from "./ui/Toaster.svelte";
23
- export { default as Tooltip } from "./ui/Tooltip.svelte";
24
- export { default as Alert } from "./display/alert/Alert.svelte";
25
- export { default as LinkCollection } from "./display/LinkCollection.svelte";
26
- export { default as Navbar } from "./layout/Navbar.svelte";
27
- export { default as NavbarItemView } from "./layout/NavbarItemView.svelte";
28
- export { default as CustomNavbar } from "./layout/CustomNavbar.svelte";
29
- export { default as AppShell } from "./layout/AppShell.svelte";
30
- export { default as Sidenav } from "./layout/Sidenav.svelte";
31
- export { default as Paper } from "./display/Paper.svelte";
32
- export { default as Avatar } from "./display/Avatar.svelte";
33
- export { default as Autocomplete } from "./form/Autocomplete.svelte";
34
- export { default as AutocompleteMulti } from "./form/AutocompleteMulti.svelte";
35
- export { default as Checkbox } from "./form/Checkbox.svelte";
36
- export { default as ComboBoxItemBuilder } from "./form/ComboBoxItemBuilder.svelte";
37
- export { default as StringArrayBuilder } from "./form/StringArrayBuilder.svelte";
38
- export { default as CronBuilder } from "./form/CronBuilder.svelte";
39
- export { default as DatePicker } from "./form/DatePicker.svelte";
40
- export { default as DateRangePicker } from "./form/DateRangePicker.svelte";
41
- export { default as TimePicker } from "./form/TimePicker.svelte";
42
- export { default as DateTimePicker } from "./form/DateTimePicker.svelte";
43
- export { default as FormGroup } from "./form/FormGroup.svelte";
44
- export { default as Number } from "./form/Number.svelte";
45
- export { default as Radio } from "./form/Radio.svelte";
46
- export { default as RadioGroup } from "./form/RadioGroup.svelte";
47
- export { default as Select } from "./form/Select.svelte";
48
- export { default as SelectMulti } from "./form/SelectMulti.svelte";
49
- export { default as SegmentedControl } from "./form/SegmentedControl.svelte";
50
- export { default as TextArea } from "./form/TextArea.svelte";
51
- export { default as Textbox } from "./form/Textbox.svelte";
52
- export { default as Slider } from "./form/Slider.svelte";
53
- export { default as Switch } from "./form/Switch.svelte";
54
- export { default as Chips } from "./form/Chips.svelte";
55
- export { default as ColorPicker } from "./form/ColorPicker.svelte";
56
- export { parseColor, parseHex, toHex, formatColor, rgbToHsv, hsvToRgb, rgbToHsl, } from "./form/ColorPicker.svelte";
57
- export { default as TipTap } from "./editors/tiptap/TipTap.svelte";
58
- export { default as TipTapBubble } from "./editors/tiptap/TipTapBubble.svelte";
59
- export { CodeBlockEditor, codeBlockLanguages, resolveCodeBlockLanguage, } from "./editors/tiptap/extensions/index.js";
60
- export { default as CodeMirror } from "./editors/CodeMirror.svelte";
61
- export { default as GitCard } from "./cards/GitCard.svelte";
62
- export * from "./icon/index.js";
63
- export * from "./models/ComboBoxItem.js";
64
- export * from "./models/NavbarItem.js";
65
- export * from "./models/NotifyModel.js";
66
- export * from "./util/ClickOutside.js";
67
- export * from "./util/CronParser.js";
68
- export { dayjs } from "./util/dayjs.js";
69
- export * from "./util/DateFunctions.js";
70
- export * from "./util/DragDrop.js";
71
- export * from "./util/Transitions.js";
72
- export * from "./util/interceptLinkClick.js";
73
- export * from "./util/NavigateTo.js";
74
- export * from "./display/filetree/index.js";
75
- export * from "./theme.svelte.js";
76
- export * from "./uiTheme.svelte.js";
77
- export * from "./ui/Toast.svelte.js";
1
+ export { default as Accordion } from './ui/Accordion.svelte';
2
+ export { default as AccordionItem } from './ui/AccordionItem.svelte';
3
+ export { default as Button } from './ui/Button.svelte';
4
+ export { default as ActionIcon } from './ui/ActionIcon.svelte';
5
+ export { default as Badge } from './ui/Badge.svelte';
6
+ export { default as Dialog } from './ui/Dialog.svelte';
7
+ export { default as Drawer } from './ui/Drawer.svelte';
8
+ export { default as Dropdown } from './ui/Dropdown.svelte';
9
+ export { default as Dropzone } from './ui/Dropzone.svelte';
10
+ export { default as Link } from './ui/Link.svelte';
11
+ export { default as Loader } from './ui/Loader.svelte';
12
+ export { default as Logo } from './ui/Logo.svelte';
13
+ export { default as Pagination } from './ui/Pagination.svelte';
14
+ export { default as Popover } from './ui/Popover.svelte';
15
+ export { default as ContextMenu } from './ui/ContextMenu.svelte';
16
+ export { default as ProgressBar } from './ui/ProgressBar.svelte';
17
+ export { default as Tabs } from './ui/Tabs.svelte';
18
+ export { default as Table } from './ui/Table.svelte';
19
+ export { default as ThemeSwitcher } from './ui/ThemeSwitcher.svelte';
20
+ export { default as Sortable } from './ui/Sortable.svelte';
21
+ export { default as MultiSortable } from './ui/MultiSortable.svelte';
22
+ export { default as Toaster } from './ui/Toaster.svelte';
23
+ export { default as Tooltip } from './ui/Tooltip.svelte';
24
+ export { default as Alert } from './display/alert/Alert.svelte';
25
+ export { default as LinkCollection } from './display/LinkCollection.svelte';
26
+ export { default as Navbar } from './layout/Navbar.svelte';
27
+ export { default as NavbarItemView } from './layout/NavbarItemView.svelte';
28
+ export { default as CustomNavbar } from './layout/CustomNavbar.svelte';
29
+ export { default as AppShell } from './layout/AppShell.svelte';
30
+ export { default as Sidenav } from './layout/Sidenav.svelte';
31
+ export { default as Paper } from './display/Paper.svelte';
32
+ export { default as Avatar } from './display/Avatar.svelte';
33
+ export { default as Autocomplete } from './form/Autocomplete.svelte';
34
+ export { default as AutocompleteMulti } from './form/AutocompleteMulti.svelte';
35
+ export { default as Checkbox } from './form/Checkbox.svelte';
36
+ export { default as ComboBoxItemBuilder } from './form/ComboBoxItemBuilder.svelte';
37
+ export { default as StringArrayBuilder } from './form/StringArrayBuilder.svelte';
38
+ export { default as CronBuilder } from './form/CronBuilder.svelte';
39
+ export { default as DatePicker } from './form/DatePicker.svelte';
40
+ export { default as DateRangePicker } from './form/DateRangePicker.svelte';
41
+ export { default as TimePicker } from './form/TimePicker.svelte';
42
+ export { default as DateTimePicker } from './form/DateTimePicker.svelte';
43
+ export { default as FormGroup } from './form/FormGroup.svelte';
44
+ export { default as Number } from './form/Number.svelte';
45
+ export { default as Radio } from './form/Radio.svelte';
46
+ export { default as RadioGroup } from './form/RadioGroup.svelte';
47
+ export { default as Select } from './form/Select.svelte';
48
+ export { default as SelectMulti } from './form/SelectMulti.svelte';
49
+ export { default as SegmentedControl } from './form/SegmentedControl.svelte';
50
+ export { default as TextArea } from './form/TextArea.svelte';
51
+ export { default as Textbox } from './form/Textbox.svelte';
52
+ export { default as Slider } from './form/Slider.svelte';
53
+ export { default as Switch } from './form/Switch.svelte';
54
+ export { default as Chips } from './form/Chips.svelte';
55
+ export { default as ColorPicker } from './form/ColorPicker.svelte';
56
+ export { parseColor, parseHex, toHex, formatColor, rgbToHsv, hsvToRgb, rgbToHsl } from './form/ColorPicker.svelte';
57
+ export { default as TipTap } from './editors/tiptap/TipTap.svelte';
58
+ export { default as TipTapBubble } from './editors/tiptap/TipTapBubble.svelte';
59
+ export { CodeBlockEditor, codeBlockLanguages, resolveCodeBlockLanguage } from './editors/tiptap/extensions/index.js';
60
+ export { default as CodeMirror } from './editors/CodeMirror.svelte';
61
+ export { default as GitCard } from './cards/GitCard.svelte';
62
+ export * from './icon/index.js';
63
+ export * from './models/ComboBoxItem.js';
64
+ export * from './models/NavbarItem.js';
65
+ export * from './models/NotifyModel.js';
66
+ export * from './util/ClickOutside.js';
67
+ export * from './util/Floating.js';
68
+ export * from './util/CronParser.js';
69
+ export { dayjs } from './util/dayjs.js';
70
+ export * from './util/DateFunctions.js';
71
+ export * from './util/DragDrop.js';
72
+ export * from './util/Transitions.js';
73
+ export * from './util/interceptLinkClick.js';
74
+ export * from './util/NavigateTo.js';
75
+ export * from './display/filetree/index.js';
76
+ export * from './theme.svelte.js';
77
+ export * from './uiTheme.svelte.js';
78
+ export * from './ui/Toast.svelte.js';
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
-
3
+
4
4
  export interface CustomNavbarProps {
5
5
  /** Header text for the mobile drawer */
6
6
  drawerHeader?: string | undefined;
@@ -27,4 +27,4 @@
27
27
  {/if}
28
28
  </div>
29
29
  {/if}
30
- </div> -->
30
+ </div> -->
@@ -61,7 +61,9 @@
61
61
  <div
62
62
  class="navbar-submenu"
63
63
  class:flyout={depth > 0}
64
- style={item.submenuWidth ? `width: ${item.submenuWidth}; min-width: ${item.submenuWidth};` : ''}
64
+ style={item.submenuWidth
65
+ ? `width: ${item.submenuWidth}; min-width: ${item.submenuWidth};`
66
+ : ''}
65
67
  onclick={(e) => {
66
68
  // Close the fold-out once an actual link is chosen (but not when toggling a nested parent)
67
69
  if ((e.target as HTMLElement).closest('a')) close();
@@ -471,7 +471,7 @@
471
471
  border-top: 1px solid var(--border-color);
472
472
  border-bottom: 1px solid var(--border-color);
473
473
  }
474
-
474
+
475
475
  .section-title:first-child {
476
476
  border-top: none;
477
477
  }
@@ -1,4 +1,4 @@
1
- import type { NotifyModel } from "./NotifyModel.js";
1
+ import type { NotifyModel } from './NotifyModel.js';
2
2
  export interface ComboBoxGroup {
3
3
  label: string;
4
4
  items: ComboBoxItem[];
@@ -20,6 +20,6 @@ export interface ComboBoxItem {
20
20
  /** Marks a synthetic "add new value" suggestion (see Autocomplete) */
21
21
  isAddNew?: boolean;
22
22
  }
23
- export type FetchFunctionType = (() => Promise<NotifyModel<ComboBoxItem[]>>);
24
- export type SearchFunctionType = ((query: string) => Promise<NotifyModel<ComboBoxItem[]>>);
25
- export type RetrieveLabelFunctionType = ((value: any) => Promise<NotifyModel<string | undefined>>);
23
+ export type FetchFunctionType = () => Promise<NotifyModel<ComboBoxItem[]>>;
24
+ export type SearchFunctionType = (query: string) => Promise<NotifyModel<ComboBoxItem[]>>;
25
+ export type RetrieveLabelFunctionType = (value: any) => Promise<NotifyModel<string | undefined>>;
@@ -6,4 +6,3 @@ export var MessageTypes;
6
6
  MessageTypes["notFound"] = "NotFound";
7
7
  MessageTypes["accessDenied"] = "AccessDenied";
8
8
  })(MessageTypes || (MessageTypes = {}));
9
- ;
@@ -1,4 +1,4 @@
1
- type Theme = "system" | "light" | "dark";
1
+ type Theme = 'system' | 'light' | 'dark';
2
2
  export declare const breakpoints: {
3
3
  readonly sm: 640;
4
4
  readonly md: 768;
@@ -4,12 +4,12 @@ const browser = typeof window !== 'undefined' && typeof document !== 'undefined'
4
4
  // Theme State
5
5
  // ============================================
6
6
  // Svelte 5 runes for reactive state
7
- let theme = $state("system");
7
+ let theme = $state('system');
8
8
  let systemDarkMode = $state(browser && isSystemDarkMode());
9
9
  let isDarkMode = $derived.by(() => {
10
- if (theme === "system")
10
+ if (theme === 'system')
11
11
  return systemDarkMode;
12
- return theme === "dark";
12
+ return theme === 'dark';
13
13
  });
14
14
  // Initialize theme on module load (browser only - SSR has no localStorage/document)
15
15
  if (browser)
@@ -18,8 +18,8 @@ if (browser)
18
18
  if (browser && window.matchMedia) {
19
19
  window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
20
20
  systemDarkMode = e.matches;
21
- if (theme === "system")
22
- applyThemeClass(e.matches ? "dark" : "light");
21
+ if (theme === 'system')
22
+ applyThemeClass(e.matches ? 'dark' : 'light');
23
23
  });
24
24
  }
25
25
  // ============================================
@@ -55,20 +55,44 @@ if (typeof window !== 'undefined') {
55
55
  }
56
56
  // Export reactive viewport state object
57
57
  export const viewportState = {
58
- get width() { return windowWidth; },
59
- get height() { return windowHeight; },
58
+ get width() {
59
+ return windowWidth;
60
+ },
61
+ get height() {
62
+ return windowHeight;
63
+ },
60
64
  // Boolean helpers
61
- get isMobile() { return isMobile; },
62
- get isTablet() { return isTablet; },
63
- get isDesktop() { return isDesktop; },
64
- get isSmall() { return isSmall; },
65
- get isMedium() { return isMedium; },
66
- get isLarge() { return isLarge; },
67
- get isExtraLarge() { return isExtraLarge; },
68
- get isUltrawide() { return isUltrawide; },
65
+ get isMobile() {
66
+ return isMobile;
67
+ },
68
+ get isTablet() {
69
+ return isTablet;
70
+ },
71
+ get isDesktop() {
72
+ return isDesktop;
73
+ },
74
+ get isSmall() {
75
+ return isSmall;
76
+ },
77
+ get isMedium() {
78
+ return isMedium;
79
+ },
80
+ get isLarge() {
81
+ return isLarge;
82
+ },
83
+ get isExtraLarge() {
84
+ return isExtraLarge;
85
+ },
86
+ get isUltrawide() {
87
+ return isUltrawide;
88
+ },
69
89
  // Utility function for custom breakpoints
70
- isBelow(breakpoint) { return windowWidth < breakpoint; },
71
- isAbove(breakpoint) { return windowWidth >= breakpoint; }
90
+ isBelow(breakpoint) {
91
+ return windowWidth < breakpoint;
92
+ },
93
+ isAbove(breakpoint) {
94
+ return windowWidth >= breakpoint;
95
+ }
72
96
  };
73
97
  // Export getter for the theme state
74
98
  export function getTheme() {
@@ -86,7 +110,7 @@ export const themeState = {
86
110
  export function loadTheme() {
87
111
  if (!browser)
88
112
  return;
89
- const themeString = localStorage.getItem("theme") || "system";
113
+ const themeString = localStorage.getItem('theme') || 'system';
90
114
  if (themeString == 'light')
91
115
  setTheme('light');
92
116
  else if (themeString == 'dark')
@@ -96,9 +120,9 @@ export function loadTheme() {
96
120
  }
97
121
  export function setTheme(newTheme) {
98
122
  let appliedTheme;
99
- if (newTheme === "system") {
123
+ if (newTheme === 'system') {
100
124
  systemDarkMode = isSystemDarkMode();
101
- appliedTheme = systemDarkMode ? "dark" : "light";
125
+ appliedTheme = systemDarkMode ? 'dark' : 'light';
102
126
  }
103
127
  else {
104
128
  appliedTheme = newTheme;
@@ -108,13 +132,13 @@ export function setTheme(newTheme) {
108
132
  if (!browser)
109
133
  return;
110
134
  applyThemeClass(appliedTheme);
111
- localStorage.setItem("theme", newTheme);
135
+ localStorage.setItem('theme', newTheme);
112
136
  }
113
137
  function applyThemeClass(appliedTheme) {
114
138
  if (!browser)
115
139
  return;
116
140
  const classList = document.documentElement.classList;
117
- classList.remove("light", "dark");
141
+ classList.remove('light', 'dark');
118
142
  classList.add(appliedTheme);
119
143
  }
120
144
  function isSystemDarkMode() {
@@ -59,9 +59,7 @@
59
59
  children
60
60
  }: AccordionProps = $props();
61
61
 
62
- const openValues = $derived(
63
- Array.isArray(value) ? value : value ? [value] : []
64
- );
62
+ const openValues = $derived(Array.isArray(value) ? value : value ? [value] : []);
65
63
 
66
64
  function isOpen(itemValue: string): boolean {
67
65
  return openValues.includes(itemValue);
@@ -43,12 +43,12 @@
43
43
  let isDisabled: boolean = $derived(loading || disabled);
44
44
  let type: 'a' | 'button' | 'div' = $derived(onClick ? 'button' : href ? 'a' : 'div');
45
45
 
46
- const elClass = $derived(
47
- `badge ${isDisabled ? 'disabled' : ''}`
48
- );
46
+ const elClass = $derived(`badge ${isDisabled ? 'disabled' : ''}`);
49
47
  </script>
50
48
 
51
- <div class="badge-container {variant} shape-{shape} size-{size} {onDelete ? 'badge-with-button' : ''} ">
49
+ <div
50
+ class="badge-container {variant} shape-{shape} size-{size} {onDelete ? 'badge-with-button' : ''} "
51
+ >
52
52
  {#if type == 'a' && !disabled}
53
53
  <a class={elClass} {href} {target}> {@render children?.()}</a>
54
54
  {:else if type == 'button'}
@@ -334,5 +334,4 @@
334
334
  .size-lg .badge-button {
335
335
  padding: 6px;
336
336
  }
337
-
338
337
  </style>
@@ -87,7 +87,13 @@
87
87
  {/snippet}
88
88
 
89
89
  {#if isLink && href && !disabled}
90
- <a {target} class={elClass} aria-label={ariaLabel} href={href?.startsWith('/') ? resolve(href as any) : href} onclick={(e) => interceptLinkClick(e, { href, target, onclick })}>
90
+ <a
91
+ {target}
92
+ class={elClass}
93
+ aria-label={ariaLabel}
94
+ href={href?.startsWith('/') ? resolve(href as any) : href}
95
+ onclick={(e) => interceptLinkClick(e, { href, target, onclick })}
96
+ >
91
97
  {@render buttonContent?.()}
92
98
  </a>
93
99
  {:else}
@@ -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:portal
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'};"
@@ -25,7 +25,13 @@
25
25
  title?: string | undefined;
26
26
  /** Render without the Paper wrapper component */
27
27
  noPaper?: boolean;
28
- /** Prevent content from overflowing the dialog bounds */
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
37
  size?: 'sm' | 'md' | 'lg' | 'xl' | 'full';
@@ -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);
@@ -142,7 +155,7 @@
142
155
  {/if}
143
156
  </div>
144
157
  {/if}
145
- <div class="dialog-body {preventOverflow ? 'overflow-y-auto' : 'overflow-visible'}">
158
+ <div class="dialog-body {overflowClass}">
146
159
  {@render children?.()}
147
160
  </div>
148
161
  {#if footer}
@@ -169,7 +182,7 @@
169
182
  {#if noPaper}
170
183
  {@render DialogInner()}
171
184
  {:else}
172
- <div class="dialog-paper {preventOverflow ? 'overflow-y-auto' : 'overflow-visible'}">
185
+ <div class="dialog-paper {overflowClass}">
173
186
  <Paper containerClass={classes}>
174
187
  {@render DialogInner()}
175
188
  </Paper>
@@ -18,7 +18,13 @@ export interface DialogProps {
18
18
  title?: string | undefined;
19
19
  /** Render without the Paper wrapper component */
20
20
  noPaper?: boolean;
21
- /** Prevent content from overflowing the dialog bounds */
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
30
  size?: 'sm' | 'md' | 'lg' | 'xl' | 'full';
@@ -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
- <div class="dropdown {classes} {alignToButton}">
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 class="dropdown-content" onclick={(e) => e.stopPropagation()}>
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
  }