@spaethtech/svelte-ui 0.7.1-dev.44.97afb0d → 0.7.1-dev.46.8e420d1

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.
@@ -35,6 +35,7 @@
35
35
  align = "start",
36
36
  boundary = "viewport",
37
37
  offset = 4,
38
+ margin = 8,
38
39
  matchWidth = false,
39
40
  surface = false,
40
41
  variant,
@@ -51,6 +52,9 @@
51
52
  /** Default the viewport; pass a container element or CSS selector to bound flipping to it. */
52
53
  boundary?: Boundary;
53
54
  offset?: number;
55
+ /** Minimum gap kept from the boundary edges, in px (default 8). Set `0` to let the box sit flush
56
+ * against an edge — e.g. a sidebar popout that must align to the sidebar's (viewport) edge. */
57
+ margin?: number;
54
58
  matchWidth?: boolean;
55
59
  /** Render a themed floating PANEL around the children (background, border, padding, shadow) instead
56
60
  * of the default bare positioner. `variant`/`size`/`borderless` configure that panel. Consumers
@@ -132,6 +136,7 @@
132
136
  align,
133
137
  boundary,
134
138
  offset,
139
+ margin,
135
140
  matchWidth,
136
141
  onplaced: (p) => (placed = p),
137
142
  }}
@@ -12,6 +12,9 @@ type $$ComponentProps = {
12
12
  /** Default the viewport; pass a container element or CSS selector to bound flipping to it. */
13
13
  boundary?: Boundary;
14
14
  offset?: number;
15
+ /** Minimum gap kept from the boundary edges, in px (default 8). Set `0` to let the box sit flush
16
+ * against an edge — e.g. a sidebar popout that must align to the sidebar's (viewport) edge. */
17
+ margin?: number;
15
18
  matchWidth?: boolean;
16
19
  /** Render a themed floating PANEL around the children (background, border, padding, shadow) instead
17
20
  * of the default bare positioner. `variant`/`size`/`borderless` configure that panel. Consumers
@@ -216,8 +216,13 @@
216
216
  return active;
217
217
  }
218
218
 
219
- // SSR-safe default the effect below corrects on mount.
220
- let currentMode = $state<SideBarMenuMode>("expanded");
219
+ // Resolve the mode synchronously on the CLIENT at init (from the real viewport width) so the first
220
+ // client render is already correct — no post-hydration flash from `expanded` → the actual mode. On
221
+ // the server `window` is undefined, so SSR emits `expanded`; the client corrects during hydration
222
+ // (not via a delayed effect). The effect below keeps it in sync on subsequent viewport changes.
223
+ let currentMode = $state<SideBarMenuMode>(
224
+ typeof window !== "undefined" ? resolveMode(effectiveModes, window.innerWidth) : "expanded",
225
+ );
221
226
 
222
227
  $effect(() => {
223
228
  const map = effectiveModes;
@@ -276,6 +281,15 @@
276
281
  // Labels show in every mode except a collapsed (not hover-expanded) icon rail.
277
282
  const labelsVisible = $derived(currentMode !== "icon" || effectivelyExpanded);
278
283
 
284
+ // Aside width in px, driven by mode (inline style — robust, no Tailwind arbitrary-var class).
285
+ const asideWidthCss = $derived(
286
+ currentMode === "stepped"
287
+ ? "100%"
288
+ : currentMode === "icon" && !effectivelyExpanded
289
+ ? `${collapsedWidth}px`
290
+ : `${expandedWidth}px`,
291
+ );
292
+
279
293
  // ── DEV depth warning ───────────────────────────────────────────
280
294
  if (DEV) {
281
295
  const walk = (list: SideBarMenuItem[], depth: number, path: string[]) => {
@@ -699,7 +713,8 @@
699
713
  role="menu"
700
714
  tabindex="-1"
701
715
  id={popoutId}
702
- class="w-[var(--sbm-expanded-w)] overflow-hidden shadow-lg [background-color:var(--ui-color-background)] [color:var(--ui-color-text)] [border-color:color-mix(in_srgb,var(--ui-color-text)_15%,transparent)] {borderClass}"
716
+ style="width: {expandedWidth}px;"
717
+ class="overflow-hidden shadow-lg [background-color:var(--ui-color-background)] [color:var(--ui-color-text)] [border-color:color-mix(in_srgb,var(--ui-color-text)_15%,transparent)] {borderClass}"
703
718
  onmouseenter={cancelClose}
704
719
  onmouseleave={scheduleClose}
705
720
  >
@@ -749,27 +764,28 @@
749
764
  width, so the expanded float below is positioned against the SIDEBAR's own box (container-relative,
750
765
  never the viewport). In every other mode it's `display: contents` (no box) so layout is unchanged. -->
751
766
  <div
752
- class={hoverExpandActive ? "relative shrink-0 [width:var(--sbm-collapsed-w)]" : "contents"}
753
- style="--sbm-collapsed-w: {collapsedWidth}px; --sbm-expanded-w: {expandedWidth}px;"
767
+ class={hoverExpandActive ? "relative shrink-0" : "contents"}
768
+ style="{hoverExpandActive ? `width: ${collapsedWidth}px;` : ''}"
754
769
  >
755
770
  <aside
756
771
  bind:this={asideRef}
772
+ style="width: {asideWidthCss};"
757
773
  onmouseenter={hoverExpandActive ? openHoverExpand : undefined}
758
774
  onmouseleave={hoverExpandActive ? scheduleHoverCollapse : undefined}
759
775
  class="
760
- flex flex-col z-[60] overflow-hidden
776
+ flex flex-col z-[60] overflow-hidden shrink-0
761
777
  [background-color:var(--ui-color-background)]
762
778
  [color:var(--ui-color-text)]
763
779
  {dockBorder}
764
780
  {currentMode === 'stepped'
765
- ? `absolute inset-y-0 ${dockEdge} w-full z-[75] transition-transform duration-200 ${open ? 'translate-x-0' : dockOff}`
781
+ ? `absolute inset-y-0 ${dockEdge} z-[75] transition-transform duration-200 ${open ? 'translate-x-0' : dockOff}`
766
782
  : currentMode === 'drawer'
767
- ? `absolute inset-y-0 ${dockEdge} w-[var(--sbm-expanded-w)] z-[75] transition-transform duration-200 ${open ? 'translate-x-0' : dockOff}`
783
+ ? `absolute inset-y-0 ${dockEdge} z-[75] transition-transform duration-200 ${open ? 'translate-x-0' : dockOff}`
768
784
  : currentMode === 'icon'
769
785
  ? hoverExpandActive
770
- ? `absolute inset-y-0 ${dockEdge} z-[65] transition-[width] duration-200 ${effectivelyExpanded ? 'w-[var(--sbm-expanded-w)] shadow-lg' : 'w-[var(--sbm-collapsed-w)]'}`
771
- : 'relative w-[var(--sbm-collapsed-w)]'
772
- : 'relative w-[var(--sbm-expanded-w)]'}
786
+ ? `absolute inset-y-0 ${dockEdge} z-[65] transition-[width] duration-200 ${effectivelyExpanded ? 'shadow-lg' : ''}`
787
+ : 'relative'
788
+ : 'relative'}
773
789
  {additionalClass}
774
790
  "
775
791
  >
@@ -802,6 +818,7 @@
802
818
  side={popoutSide}
803
819
  align={geom.align}
804
820
  offset={0}
821
+ margin={0}
805
822
  onclose={() => {
806
823
  if (openId === id) openId = null;
807
824
  }}
@@ -838,6 +855,7 @@
838
855
  side={popoutSide}
839
856
  align={geom.align}
840
857
  offset={0}
858
+ margin={0}
841
859
  onclose={() => {
842
860
  if (openId === id) openId = null;
843
861
  }}
package/dist/index.d.ts CHANGED
@@ -41,7 +41,7 @@ export type { Columns, Gap } from "./components/field-group.js";
41
41
  export { default as Disclosure } from "./components/Disclosure.svelte";
42
42
  export { default as Accordion } from "./components/Accordion.svelte";
43
43
  export { default as SideBarMenu } from "./components/SideBarMenu/SideBarMenu.svelte";
44
- export type { SideBarMenuItem, SideBarMenuBadge, SideBarMenuBadgeVariant, SideBarMenuApi, } from "./components/SideBarMenu/SideBarMenu.svelte";
44
+ export type { SideBarMenuItem, SideBarMenuBadge, SideBarMenuBadgeVariant, SideBarMenuApi, SideBarMenuMode, } from "./components/SideBarMenu/SideBarMenu.svelte";
45
45
  export { default as TabStrip } from "./components/TabStrip/TabStrip.svelte";
46
46
  export type { TabDefinition } from "./components/TabStrip/TabStrip.svelte";
47
47
  export { default as DataTable } from "./components/DataTable.svelte";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spaethtech/svelte-ui",
3
- "version": "0.7.1-dev.44.97afb0d",
3
+ "version": "0.7.1-dev.46.8e420d1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/spaethtech/svelte-ui.git"