@classic-homes/theme-svelte 1.3.0 → 1.5.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.
@@ -19,7 +19,6 @@
19
19
  DEFAULT_CARD_CONFIG,
20
20
  DEFAULT_TAB_WIDTH,
21
21
  CARD_HEADER_HEIGHT,
22
- CARD_EXPANDED_RATIO,
23
22
  CARD_MIN_WIDTH,
24
23
  loadPanelState,
25
24
  savePanelState,
@@ -116,6 +115,12 @@
116
115
  snapThreshold?: number;
117
116
  /** Distance in pixels to drag before detaching from pinned mode (0 to disable) */
118
117
  detachThreshold?: number;
118
+ /**
119
+ * Viewport width (px) below which the panel uses mobile behaviors
120
+ * (card scrim, focus trap, card drag). Default 640. Pass 768 for apps
121
+ * that switch layouts at md.
122
+ */
123
+ mobileBreakpoint?: number;
119
124
  /** Custom header content (replaces title/subtitle) */
120
125
  header?: Snippet;
121
126
  /** Custom header actions */
@@ -155,6 +160,7 @@
155
160
  persistKey,
156
161
  snapThreshold = 20,
157
162
  detachThreshold = 40,
163
+ mobileBreakpoint = 640,
158
164
  header,
159
165
  headerActions,
160
166
  footer,
@@ -166,6 +172,20 @@
166
172
  const resolvedConstraints = $derived({ ...DEFAULT_CONSTRAINTS, ...constraints });
167
173
  const resolvedCardConfig = $derived({ ...DEFAULT_CARD_CONFIG, ...cardConfig });
168
174
 
175
+ /**
176
+ * Default left offset for the card sheet, honoring cardConfig.align.
177
+ * Drag/resize bounds derive from the same offset so existing
178
+ * interactions are unaffected by non-center alignment.
179
+ */
180
+ function cardDefaultLeftOffset(width: number): number {
181
+ const margin = resolvedCardConfig.horizontalMargin;
182
+ if (resolvedCardConfig.align === 'left') return margin;
183
+ if (resolvedCardConfig.align === 'right') {
184
+ return Math.max(margin, viewportWidth - width - margin);
185
+ }
186
+ return Math.max(margin, (viewportWidth - width) / 2);
187
+ }
188
+
169
189
  // Internal state - initialized from props or persistence
170
190
  let internalMode = $state<PanelMode>(DEFAULT_PANEL_STATE.mode);
171
191
  let internalVariant = $state<PanelVariant>(DEFAULT_PANEL_STATE.variant);
@@ -229,7 +249,7 @@
229
249
  let viewportHeight = $state(typeof window !== 'undefined' ? window.innerHeight : 768);
230
250
 
231
251
  // Detect mobile
232
- let isMobile = $derived(viewportWidth < 640);
252
+ let isMobile = $derived(viewportWidth < mobileBreakpoint);
233
253
 
234
254
  // Derived state - use props if provided, otherwise internal state
235
255
  const mode = $derived(propMode ?? internalMode);
@@ -295,13 +315,13 @@
295
315
  viewportHeight = window.innerHeight;
296
316
 
297
317
  // Initialize card expanded height
298
- cardExpandedHeight = viewportHeight * CARD_EXPANDED_RATIO;
318
+ cardExpandedHeight = viewportHeight * resolvedCardConfig.expandedRatio;
299
319
 
300
320
  // Listen for resize
301
321
  const handleResize = () => {
302
322
  viewportWidth = window.innerWidth;
303
323
  viewportHeight = window.innerHeight;
304
- cardExpandedHeight = viewportHeight * CARD_EXPANDED_RATIO;
324
+ cardExpandedHeight = viewportHeight * resolvedCardConfig.expandedRatio;
305
325
  };
306
326
  window.addEventListener('resize', handleResize);
307
327
 
@@ -583,10 +603,7 @@
583
603
  viewportWidth - resolvedCardConfig.horizontalMargin * 2,
584
604
  resolvedCardConfig.maxWidth
585
605
  );
586
- const defaultLeftOffset = Math.max(
587
- resolvedCardConfig.horizontalMargin,
588
- (viewportWidth - cardWidth) / 2
589
- );
606
+ const defaultLeftOffset = cardDefaultLeftOffset(cardWidth);
590
607
  // Calculate bounds: can move from left margin to right margin
591
608
  const minOffset = -(defaultLeftOffset - resolvedCardConfig.horizontalMargin);
592
609
  const maxOffset =
@@ -898,10 +915,7 @@
898
915
  cardWidthResizeStartWidth = currentWidth;
899
916
 
900
917
  // Calculate current left position
901
- const defaultLeftOffset = Math.max(
902
- resolvedCardConfig.horizontalMargin,
903
- (viewportWidth - defaultCardWidth) / 2
904
- );
918
+ const defaultLeftOffset = cardDefaultLeftOffset(defaultCardWidth);
905
919
  cardWidthResizeStartLeft = defaultLeftOffset + cardHorizontalPosition;
906
920
 
907
921
  document.body.style.cursor = 'ew-resize';
@@ -940,10 +954,7 @@
940
954
  viewportWidth - resolvedCardConfig.horizontalMargin * 2,
941
955
  resolvedCardConfig.maxWidth
942
956
  );
943
- const defaultLeftOffset = Math.max(
944
- resolvedCardConfig.horizontalMargin,
945
- (viewportWidth - defaultCardWidth) / 2
946
- );
957
+ const defaultLeftOffset = cardDefaultLeftOffset(defaultCardWidth);
947
958
  cardHorizontalPosition = newLeft - defaultLeftOffset;
948
959
  }
949
960
  }
@@ -1087,10 +1098,7 @@
1087
1098
  );
1088
1099
  // Use custom cardWidth if set, otherwise use default
1089
1100
  const actualCardWidth = cardWidth ?? defaultCardWidth;
1090
- const defaultLeftOffset = Math.max(
1091
- resolvedCardConfig.horizontalMargin,
1092
- (viewportWidth - defaultCardWidth) / 2
1093
- );
1101
+ const defaultLeftOffset = cardDefaultLeftOffset(defaultCardWidth);
1094
1102
 
1095
1103
  // Height based on expanded state or drag
1096
1104
  let height: number;
@@ -64,6 +64,12 @@ interface Props {
64
64
  snapThreshold?: number;
65
65
  /** Distance in pixels to drag before detaching from pinned mode (0 to disable) */
66
66
  detachThreshold?: number;
67
+ /**
68
+ * Viewport width (px) below which the panel uses mobile behaviors
69
+ * (card scrim, focus trap, card drag). Default 640. Pass 768 for apps
70
+ * that switch layouts at md.
71
+ */
72
+ mobileBreakpoint?: number;
67
73
  /** Custom header content (replaces title/subtitle) */
68
74
  header?: Snippet;
69
75
  /** Custom header actions */
@@ -117,7 +117,7 @@
117
117
  {/if}
118
118
  {#if showClose}
119
119
  <DialogPrimitive.Close
120
- class="flex h-9 w-9 items-center justify-center rounded-md bg-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none"
120
+ class="flex h-9 w-9 items-center justify-center rounded-md bg-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none relative after:absolute after:-inset-1 after:content-['']"
121
121
  aria-label="Close dialog"
122
122
  >
123
123
  <svg
@@ -10,7 +10,7 @@
10
10
  root: 'relative flex w-full touch-none select-none items-center',
11
11
  range: 'absolute h-full bg-primary rounded-full',
12
12
  thumb:
13
- 'block rounded-full border-2 border-primary bg-background ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
13
+ "block rounded-full border-2 border-primary bg-background ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 relative after:absolute after:left-1/2 after:top-1/2 after:h-11 after:w-11 after:-translate-x-1/2 after:-translate-y-1/2 after:rounded-full after:content-['']",
14
14
  },
15
15
  variants: {
16
16
  size: {
@@ -87,7 +87,7 @@
87
87
  const positionLabel = $derived(`${index + 1} of ${items.length}`);
88
88
 
89
89
  const buttonClass =
90
- 'inline-flex h-10 w-10 items-center justify-center rounded-full border border-border bg-background/90 text-foreground shadow-sm outline-none hover:bg-accent hover:text-accent-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-40';
90
+ "inline-flex h-10 w-10 items-center justify-center rounded-full border border-border bg-background/90 text-foreground shadow-sm outline-none hover:bg-accent hover:text-accent-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-40 relative after:absolute after:-inset-0.5 after:rounded-full after:content-['']";
91
91
 
92
92
  function goTo(next: number, smooth = true) {
93
93
  if (items.length === 0) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classic-homes/theme-svelte",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "Svelte components for the Classic theme system",
5
5
  "type": "module",
6
6
  "svelte": "./dist/lib/index.js",