@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.
Files changed (87) hide show
  1. package/dist/app.css +0 -10
  2. package/dist/editors/CodeMirror.svelte +295 -297
  3. package/dist/editors/tiptap/LinkActionMenu.svelte +6 -1
  4. package/dist/editors/tiptap/TipTap.svelte +1 -1
  5. package/dist/editors/tiptap/extensions/Alert.js +17 -17
  6. package/dist/editors/tiptap/extensions/Div.js +19 -19
  7. package/dist/editors/tiptap/extensions/Span.js +14 -14
  8. package/dist/editors/tiptap/toolbar/AlertButton.svelte +254 -253
  9. package/dist/editors/tiptap/toolbar/AlignCenterButton.svelte +11 -11
  10. package/dist/editors/tiptap/toolbar/AlignJustifyButton.svelte +11 -11
  11. package/dist/editors/tiptap/toolbar/AlignLeftButton.svelte +11 -11
  12. package/dist/editors/tiptap/toolbar/AlignRightButton.svelte +11 -11
  13. package/dist/editors/tiptap/toolbar/BoldButton.svelte +11 -11
  14. package/dist/editors/tiptap/toolbar/BulletListButton.svelte +11 -11
  15. package/dist/editors/tiptap/toolbar/CodeButton.svelte +11 -11
  16. package/dist/editors/tiptap/toolbar/ColorButton.svelte +265 -265
  17. package/dist/editors/tiptap/toolbar/DocumentButton.svelte +51 -51
  18. package/dist/editors/tiptap/toolbar/HeadingButton.svelte +21 -21
  19. package/dist/editors/tiptap/toolbar/HtmlButton.svelte +48 -52
  20. package/dist/editors/tiptap/toolbar/ImageButton.svelte +35 -39
  21. package/dist/editors/tiptap/toolbar/ItalicButton.svelte +11 -11
  22. package/dist/editors/tiptap/toolbar/LinkButton.svelte +249 -249
  23. package/dist/editors/tiptap/toolbar/OrderedListButton.svelte +11 -11
  24. package/dist/editors/tiptap/toolbar/RedoButton.svelte +11 -11
  25. package/dist/editors/tiptap/toolbar/StrikethroughButton.svelte +11 -11
  26. package/dist/editors/tiptap/toolbar/SubscriptButton.svelte +11 -11
  27. package/dist/editors/tiptap/toolbar/SuperscriptButton.svelte +11 -11
  28. package/dist/editors/tiptap/toolbar/TableButton.svelte +231 -224
  29. package/dist/editors/tiptap/toolbar/ToolbarButton.svelte +59 -65
  30. package/dist/editors/tiptap/toolbar/UnderlineButton.svelte +11 -11
  31. package/dist/editors/tiptap/toolbar/UndoButton.svelte +11 -11
  32. package/dist/form/Autocomplete.svelte +24 -22
  33. package/dist/form/AutocompleteMulti.svelte +24 -22
  34. package/dist/form/Checkbox.svelte +4 -4
  35. package/dist/form/Chips.svelte +15 -7
  36. package/dist/form/ColorPicker.svelte +5 -20
  37. package/dist/form/ComboBox.svelte +4 -4
  38. package/dist/form/ComboBoxItemBuilder.svelte +14 -32
  39. package/dist/form/ComboBoxMulti.svelte +7 -3
  40. package/dist/form/CronBuilder.svelte +32 -110
  41. package/dist/form/DatePicker.svelte +9 -28
  42. package/dist/form/DateRangePicker.svelte +46 -36
  43. package/dist/form/DateTimePicker.svelte +11 -30
  44. package/dist/form/Number.svelte +1 -3
  45. package/dist/form/RadioGroup.svelte +1 -3
  46. package/dist/form/Select.svelte +25 -28
  47. package/dist/form/SelectMulti.svelte +36 -54
  48. package/dist/form/StringArrayBuilder.svelte +1 -3
  49. package/dist/form/TextArea.svelte +5 -6
  50. package/dist/form/Textbox.svelte +10 -4
  51. package/dist/form/TimePicker.svelte +10 -4
  52. package/dist/icon/Icon.svelte +8 -4
  53. package/dist/index.d.ts +147 -146
  54. package/dist/index.js +78 -77
  55. package/dist/layout/CustomNavbar.svelte +1 -1
  56. package/dist/layout/Navbar.svelte +3 -4
  57. package/dist/layout/Navbar.svelte.d.ts +1 -2
  58. package/dist/layout/NavbarItemDisplay.svelte +1 -1
  59. package/dist/layout/NavbarItemView.svelte +3 -1
  60. package/dist/layout/Sidenav.svelte +1 -1
  61. package/dist/models/ComboBoxItem.d.ts +4 -4
  62. package/dist/models/NotifyModel.js +0 -1
  63. package/dist/theme.svelte.d.ts +1 -1
  64. package/dist/theme.svelte.js +46 -22
  65. package/dist/ui/Accordion.svelte +1 -3
  66. package/dist/ui/AccordionItem.svelte +8 -16
  67. package/dist/ui/Badge.svelte +11 -19
  68. package/dist/ui/Button.svelte +15 -17
  69. package/dist/ui/ButtonVariant.d.ts +1 -7
  70. package/dist/ui/ContextMenu.svelte +5 -11
  71. package/dist/ui/Dialog.svelte +74 -30
  72. package/dist/ui/Dialog.svelte.d.ts +8 -2
  73. package/dist/ui/Dropdown.svelte +21 -10
  74. package/dist/ui/MultiSortable.svelte +1 -3
  75. package/dist/ui/Pagination.svelte +2 -2
  76. package/dist/ui/Popover.svelte +16 -37
  77. package/dist/ui/ProgressBar.svelte +4 -10
  78. package/dist/ui/ProgressBarVariant.d.ts +2 -7
  79. package/dist/ui/ThemeSwitcher.svelte +2 -1
  80. package/dist/ui/Tooltip.svelte +1 -37
  81. package/dist/util/DateFunctions.js +1 -1
  82. package/dist/util/Floating.d.ts +70 -0
  83. package/dist/util/Floating.js +321 -0
  84. package/dist/util/NavigateTo.js +2 -2
  85. package/dist/util/Transitions.d.ts +1 -1
  86. package/dist/util/Transitions.js +1 -1
  87. package/package.json +1 -1
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Floating panel positioning for dropdowns, pickers and popovers.
3
+ *
4
+ * Panels are promoted to the browser's top layer with the Popover API
5
+ * (`popover="manual"` + `showPopover()`), so they are never clipped by an
6
+ * `overflow: auto` ancestor — a scrolling Dialog body in particular — and they
7
+ * paint above modal dialogs without any z-index coordination. The panel stays
8
+ * where it is in the DOM, so focus, keyboard handling and click-outside logic
9
+ * keep working; only rendering moves.
10
+ *
11
+ * Browsers without the Popover API fall back to `position: fixed`, correcting
12
+ * for any transformed ancestor that would otherwise become the containing block.
13
+ */
14
+ export type FloatingSide = 'top' | 'bottom' | 'left' | 'right';
15
+ export type FloatingAlignment = 'start' | 'end';
16
+ export type FloatingPlacement = FloatingSide | `${FloatingSide}-${FloatingAlignment}`;
17
+ export interface FloatingOptions {
18
+ /** Element the panel is positioned against. A getter defers resolution until layout. */
19
+ anchor: HTMLElement | null | undefined | (() => HTMLElement | null | undefined);
20
+ /** Preferred side and alignment relative to the anchor. Default `bottom-start`. */
21
+ placement?: FloatingPlacement;
22
+ /** Gap between anchor and panel in px. Default 0. */
23
+ offset?: number;
24
+ /** Give the panel the anchor's width. Default false. */
25
+ matchWidth?: boolean;
26
+ /** Flip to the opposite side when the preferred side has no room. Default true. */
27
+ flip?: boolean;
28
+ /** Slide along the anchor's edge to stay inside the viewport. Default true. */
29
+ shift?: boolean;
30
+ /** Minimum distance from the viewport edges in px. Default 8. */
31
+ padding?: number;
32
+ /** Notified with the resolved placement after each positioning pass. */
33
+ onPlacement?: (placement: FloatingPlacement) => void;
34
+ }
35
+ export declare function supportsPopover(): boolean;
36
+ /**
37
+ * Promote an element to the top layer for as long as it is mounted. The element
38
+ * keeps its DOM position; only its rendering escapes overflow and transform
39
+ * ancestors. Falls back to `position: fixed` where the Popover API is missing.
40
+ */
41
+ export declare function topLayer(node: HTMLElement): {
42
+ destroy(): void;
43
+ };
44
+ /**
45
+ * A transform, filter, perspective, containment or container-type on an ancestor
46
+ * makes that ancestor the containing block for `position: fixed` descendants.
47
+ * Dialog hits this: its open animation uses a `forwards` fill, leaving a
48
+ * transform applied while open. Only relevant to the non-popover fallback —
49
+ * top-layer elements always resolve against the viewport.
50
+ */
51
+ export declare function findFixedContainingBlock(element: HTMLElement): HTMLElement | null;
52
+ /**
53
+ * Svelte action: keep `node` anchored to another element while it is mounted.
54
+ *
55
+ * ```svelte
56
+ * {#if open}
57
+ * <div use:floating={{ anchor: () => triggerEl, placement: 'bottom-start', matchWidth: true }}>
58
+ * ...
59
+ * </div>
60
+ * {/if}
61
+ * ```
62
+ *
63
+ * The node receives `data-placement` with the resolved placement (after any
64
+ * flip) and a `--ui-floating-max-height` custom property holding the space
65
+ * available on the chosen side, which descendants can use to cap their height.
66
+ */
67
+ export declare function floating(node: HTMLElement, options: FloatingOptions): {
68
+ update(next: FloatingOptions): void;
69
+ destroy(): void;
70
+ };
@@ -0,0 +1,321 @@
1
+ /**
2
+ * Floating panel positioning for dropdowns, pickers and popovers.
3
+ *
4
+ * Panels are promoted to the browser's top layer with the Popover API
5
+ * (`popover="manual"` + `showPopover()`), so they are never clipped by an
6
+ * `overflow: auto` ancestor — a scrolling Dialog body in particular — and they
7
+ * paint above modal dialogs without any z-index coordination. The panel stays
8
+ * where it is in the DOM, so focus, keyboard handling and click-outside logic
9
+ * keep working; only rendering moves.
10
+ *
11
+ * Browsers without the Popover API fall back to `position: fixed`, correcting
12
+ * for any transformed ancestor that would otherwise become the containing block.
13
+ */
14
+ const CLASS = 'ui-floating';
15
+ const STYLE_ID = 'ui-floating-style';
16
+ const OPPOSITE = {
17
+ top: 'bottom',
18
+ bottom: 'top',
19
+ left: 'right',
20
+ right: 'left'
21
+ };
22
+ export function supportsPopover() {
23
+ return typeof HTMLElement !== 'undefined' && 'showPopover' in HTMLElement.prototype;
24
+ }
25
+ /**
26
+ * Zero-specificity resets (`:where`) so component styles on the panel always win.
27
+ * The UA stylesheet gives `[popover]` a centred inset, a border, padding and a
28
+ * canvas background; all of that is undone here so the panel looks exactly as
29
+ * it did before being promoted.
30
+ */
31
+ function ensureStyles() {
32
+ if (typeof document === 'undefined' || document.getElementById(STYLE_ID))
33
+ return;
34
+ const style = document.createElement('style');
35
+ style.id = STYLE_ID;
36
+ style.textContent = `
37
+ :where(.${CLASS}) { position: fixed; top: 0; left: 0; }
38
+ :where(.${CLASS}[popover]) {
39
+ inset: auto; margin: 0; padding: 0; border: none;
40
+ background: transparent; color: inherit; overflow: visible;
41
+ width: auto; height: auto; max-width: none; max-height: none;
42
+ }
43
+ `;
44
+ document.head.appendChild(style);
45
+ }
46
+ function isPopoverOpen(node) {
47
+ try {
48
+ return node.matches(':popover-open');
49
+ }
50
+ catch {
51
+ return false;
52
+ }
53
+ }
54
+ /**
55
+ * Promote an element to the top layer for as long as it is mounted. The element
56
+ * keeps its DOM position; only its rendering escapes overflow and transform
57
+ * ancestors. Falls back to `position: fixed` where the Popover API is missing.
58
+ */
59
+ export function topLayer(node) {
60
+ ensureStyles();
61
+ node.classList.add(CLASS);
62
+ const popover = supportsPopover();
63
+ if (popover) {
64
+ node.setAttribute('popover', 'manual');
65
+ try {
66
+ if (!isPopoverOpen(node))
67
+ node.showPopover();
68
+ }
69
+ catch {
70
+ /* not connected yet, or already shown */
71
+ }
72
+ }
73
+ return {
74
+ destroy() {
75
+ if (popover && node.isConnected && isPopoverOpen(node)) {
76
+ try {
77
+ node.hidePopover();
78
+ }
79
+ catch {
80
+ /* already hidden */
81
+ }
82
+ }
83
+ }
84
+ };
85
+ }
86
+ /**
87
+ * A transform, filter, perspective, containment or container-type on an ancestor
88
+ * makes that ancestor the containing block for `position: fixed` descendants.
89
+ * Dialog hits this: its open animation uses a `forwards` fill, leaving a
90
+ * transform applied while open. Only relevant to the non-popover fallback —
91
+ * top-layer elements always resolve against the viewport.
92
+ */
93
+ export function findFixedContainingBlock(element) {
94
+ let node = element.parentElement;
95
+ while (node) {
96
+ if (isPopoverOpen(node))
97
+ return null;
98
+ const style = getComputedStyle(node);
99
+ const containerType = style.getPropertyValue('container-type');
100
+ const backdropFilter = style.getPropertyValue('backdrop-filter') ||
101
+ style.getPropertyValue('-webkit-backdrop-filter');
102
+ if (style.transform !== 'none' ||
103
+ style.perspective !== 'none' ||
104
+ style.filter !== 'none' ||
105
+ (backdropFilter !== '' && backdropFilter !== 'none') ||
106
+ (containerType !== '' && containerType !== 'normal') ||
107
+ /\b(paint|layout|strict|content)\b/.test(style.contain) ||
108
+ /\b(transform|perspective|filter)\b/.test(style.willChange)) {
109
+ return node;
110
+ }
111
+ node = node.parentElement;
112
+ }
113
+ return null;
114
+ }
115
+ /** Ancestors that clip their content; scrolling one of them can hide the anchor. */
116
+ function findClippingAncestors(element) {
117
+ const result = [];
118
+ let node = element.parentElement;
119
+ while (node && node !== document.body) {
120
+ const style = getComputedStyle(node);
121
+ if (style.overflow !== 'visible' ||
122
+ style.overflowX !== 'visible' ||
123
+ style.overflowY !== 'visible') {
124
+ result.push(node);
125
+ }
126
+ node = node.parentElement;
127
+ }
128
+ return result;
129
+ }
130
+ /** Whether any part of the anchor is still visible through its clipping ancestors. */
131
+ function anchorIsVisible(rect, clippers) {
132
+ let { top, bottom, left, right } = rect;
133
+ for (const clipper of clippers) {
134
+ const c = clipper.getBoundingClientRect();
135
+ top = Math.max(top, c.top);
136
+ bottom = Math.min(bottom, c.bottom);
137
+ left = Math.max(left, c.left);
138
+ right = Math.min(right, c.right);
139
+ if (bottom <= top || right <= left)
140
+ return false;
141
+ }
142
+ return true;
143
+ }
144
+ function containingBlockOrigin(container) {
145
+ if (!container)
146
+ return { x: 0, y: 0 };
147
+ const rect = container.getBoundingClientRect();
148
+ const style = getComputedStyle(container);
149
+ // Fixed descendants resolve against the padding box, so drop the border.
150
+ return {
151
+ x: rect.left + (parseFloat(style.borderLeftWidth) || 0),
152
+ y: rect.top + (parseFloat(style.borderTopWidth) || 0)
153
+ };
154
+ }
155
+ function splitPlacement(placement) {
156
+ const [side, alignment] = placement.split('-');
157
+ return [side, alignment];
158
+ }
159
+ function joinPlacement(side, alignment) {
160
+ return alignment ? `${side}-${alignment}` : side;
161
+ }
162
+ function clamp(value, min, max) {
163
+ return Math.max(min, Math.min(value, max));
164
+ }
165
+ /**
166
+ * Svelte action: keep `node` anchored to another element while it is mounted.
167
+ *
168
+ * ```svelte
169
+ * {#if open}
170
+ * <div use:floating={{ anchor: () => triggerEl, placement: 'bottom-start', matchWidth: true }}>
171
+ * ...
172
+ * </div>
173
+ * {/if}
174
+ * ```
175
+ *
176
+ * The node receives `data-placement` with the resolved placement (after any
177
+ * flip) and a `--ui-floating-max-height` custom property holding the space
178
+ * available on the chosen side, which descendants can use to cap their height.
179
+ */
180
+ export function floating(node, options) {
181
+ let opts = options;
182
+ let rafId = null;
183
+ let observedAnchor = null;
184
+ let clippers = [];
185
+ let clippersFor = null;
186
+ const layer = topLayer(node);
187
+ const usingPopover = supportsPopover();
188
+ // Walking the ancestor chain with getComputedStyle is too costly to repeat on
189
+ // every scroll frame, so resolve the containing block once per mount.
190
+ const fixedContainer = usingPopover ? null : findFixedContainingBlock(node);
191
+ function resolveAnchor() {
192
+ const a = opts.anchor;
193
+ const el = typeof a === 'function' ? a() : a;
194
+ return el ?? null;
195
+ }
196
+ function position() {
197
+ const anchor = resolveAnchor();
198
+ if (!anchor || !node.isConnected)
199
+ return;
200
+ const placement = opts.placement ?? 'bottom-start';
201
+ const offset = opts.offset ?? 0;
202
+ const padding = opts.padding ?? 8;
203
+ const flip = opts.flip ?? true;
204
+ const shift = opts.shift ?? true;
205
+ const a = anchor.getBoundingClientRect();
206
+ // A panel floating in the top layer is not clipped along with its anchor,
207
+ // so hide it once the anchor is scrolled out of a clipping ancestor (a
208
+ // Dialog body, for instance) rather than leaving it stranded over the
209
+ // dialog chrome.
210
+ if (clippersFor !== anchor) {
211
+ clippers = findClippingAncestors(anchor);
212
+ clippersFor = anchor;
213
+ }
214
+ node.style.visibility = anchorIsVisible(a, clippers) ? '' : 'hidden';
215
+ // Size before measuring so the measured box is the one that gets placed.
216
+ if (opts.matchWidth)
217
+ node.style.width = `${a.width}px`;
218
+ const n = node.getBoundingClientRect();
219
+ // A zero-sized box means nothing is laid out yet; the ResizeObserver will
220
+ // call again once content arrives.
221
+ if (n.width === 0 && n.height === 0)
222
+ return;
223
+ // clientWidth/Height exclude scrollbars, which window.innerWidth includes.
224
+ const vw = document.documentElement.clientWidth;
225
+ const vh = document.documentElement.clientHeight;
226
+ const [preferredSide, alignment] = splitPlacement(placement);
227
+ const space = {
228
+ top: a.top - padding,
229
+ bottom: vh - a.bottom - padding,
230
+ left: a.left - padding,
231
+ right: vw - a.right - padding
232
+ };
233
+ const vertical = preferredSide === 'top' || preferredSide === 'bottom';
234
+ const required = (vertical ? n.height : n.width) + offset;
235
+ // Flip only when the preferred side cannot fit *and* the opposite side is
236
+ // roomier; flipping into an equally cramped side just moves the problem.
237
+ let side = preferredSide;
238
+ if (flip && space[side] < required && space[OPPOSITE[side]] > space[side]) {
239
+ side = OPPOSITE[side];
240
+ }
241
+ let left = 0;
242
+ let top = 0;
243
+ if (side === 'top' || side === 'bottom') {
244
+ top = side === 'bottom' ? a.bottom + offset : a.top - n.height - offset;
245
+ if (alignment === 'start')
246
+ left = a.left;
247
+ else if (alignment === 'end')
248
+ left = a.right - n.width;
249
+ else
250
+ left = a.left + a.width / 2 - n.width / 2;
251
+ if (shift)
252
+ left = clamp(left, padding, Math.max(padding, vw - n.width - padding));
253
+ }
254
+ else {
255
+ left = side === 'right' ? a.right + offset : a.left - n.width - offset;
256
+ if (alignment === 'start')
257
+ top = a.top;
258
+ else if (alignment === 'end')
259
+ top = a.bottom - n.height;
260
+ else
261
+ top = a.top + a.height / 2 - n.height / 2;
262
+ if (shift)
263
+ top = clamp(top, padding, Math.max(padding, vh - n.height - padding));
264
+ }
265
+ const available = side === 'bottom'
266
+ ? vh - top - padding
267
+ : side === 'top'
268
+ ? a.top - offset - padding
269
+ : vh - padding * 2;
270
+ const origin = containingBlockOrigin(fixedContainer);
271
+ node.style.left = `${Math.round(left - origin.x)}px`;
272
+ node.style.top = `${Math.round(top - origin.y)}px`;
273
+ node.style.setProperty('--ui-floating-max-height', `${Math.max(0, Math.floor(available))}px`);
274
+ const resolved = joinPlacement(side, alignment);
275
+ if (node.dataset.placement !== resolved) {
276
+ node.dataset.placement = resolved;
277
+ opts.onPlacement?.(resolved);
278
+ }
279
+ }
280
+ /** Coalesce bursts of scroll/resize events into one reposition per frame. */
281
+ function schedule() {
282
+ if (rafId !== null)
283
+ return;
284
+ rafId = requestAnimationFrame(() => {
285
+ rafId = null;
286
+ position();
287
+ });
288
+ }
289
+ const observer = new ResizeObserver(() => position());
290
+ observer.observe(node);
291
+ function observeAnchor() {
292
+ const anchor = resolveAnchor();
293
+ if (anchor === observedAnchor)
294
+ return;
295
+ if (observedAnchor)
296
+ observer.unobserve(observedAnchor);
297
+ observedAnchor = anchor;
298
+ if (anchor)
299
+ observer.observe(anchor);
300
+ }
301
+ // Capture phase so scrolling any ancestor container counts, not just the page.
302
+ window.addEventListener('scroll', schedule, true);
303
+ window.addEventListener('resize', schedule);
304
+ observeAnchor();
305
+ position();
306
+ return {
307
+ update(next) {
308
+ opts = next;
309
+ observeAnchor();
310
+ position();
311
+ },
312
+ destroy() {
313
+ if (rafId !== null)
314
+ cancelAnimationFrame(rafId);
315
+ observer.disconnect();
316
+ window.removeEventListener('scroll', schedule, true);
317
+ window.removeEventListener('resize', schedule);
318
+ layer.destroy();
319
+ }
320
+ };
321
+ }
@@ -1,5 +1,5 @@
1
- import { goto } from "$app/navigation";
2
- import { resolve } from "$app/paths";
1
+ import { goto } from '$app/navigation';
2
+ import { resolve } from '$app/paths';
3
3
  export function navigateTo(url) {
4
4
  goto(resolve(url));
5
5
  }
@@ -1,4 +1,4 @@
1
- import type { TransitionConfig } from "svelte/transition";
1
+ import type { TransitionConfig } from 'svelte/transition';
2
2
  type FlyAndScaleParams = {
3
3
  y?: number;
4
4
  x?: number;
@@ -1,4 +1,4 @@
1
- import { cubicOut } from "svelte/easing";
1
+ import { cubicOut } from 'svelte/easing';
2
2
  export const flyAndScale = (node, params = { y: -8, x: 0, start: 0.95, duration: 150 }) => {
3
3
  const style = getComputedStyle(node);
4
4
  const transform = style.transform === 'none' ? '' : style.transform;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@placeholderco/placeholder-ui",
3
- "version": "2.1.1",
3
+ "version": "2.2.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",