@classic-homes/theme-svelte 1.6.0 → 1.7.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.
@@ -97,7 +97,7 @@
97
97
  />
98
98
  <AlertDialogPrimitive.Content
99
99
  class={cn(
100
- 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-black bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
100
+ 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-[calc(100vw-2rem)] sm:max-w-lg max-h-[calc(100dvh-2rem)] overflow-y-auto translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border border-black bg-background p-4 sm:p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]',
101
101
  className
102
102
  )}
103
103
  >
@@ -224,7 +224,7 @@
224
224
  {/if}
225
225
 
226
226
  <div
227
- class={cn('flex gap-2', className)}
227
+ class={cn('flex gap-1.5 sm:gap-2', className)}
228
228
  role="group"
229
229
  aria-label={label || 'One-time password input'}
230
230
  aria-describedby={describedBy}
@@ -244,7 +244,7 @@
244
244
  onpaste={handlePaste}
245
245
  onfocus={handleFocus}
246
246
  class={cn(
247
- 'h-12 w-12 text-center text-lg font-semibold rounded-md border border-input bg-background ring-offset-background transition-colors',
247
+ 'h-10 w-10 text-base sm:h-12 sm:w-12 sm:text-lg text-center font-semibold rounded-md border border-input bg-background ring-offset-background transition-colors',
248
248
  'focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
249
249
  'disabled:cursor-not-allowed disabled:opacity-50',
250
250
  error && 'border-destructive focus:ring-destructive'
@@ -275,7 +275,7 @@
275
275
  </div>
276
276
  {:else}
277
277
  <div
278
- class={cn('flex gap-2', className)}
278
+ class={cn('flex gap-1.5 sm:gap-2', className)}
279
279
  role="group"
280
280
  aria-label="One-time password input"
281
281
  aria-describedby={describedBy}
@@ -295,7 +295,7 @@
295
295
  onpaste={handlePaste}
296
296
  onfocus={handleFocus}
297
297
  class={cn(
298
- 'h-12 w-12 text-center text-lg font-semibold rounded-md border border-input bg-background ring-offset-background transition-colors',
298
+ 'h-10 w-10 text-base sm:h-12 sm:w-12 sm:text-lg text-center font-semibold rounded-md border border-input bg-background ring-offset-background transition-colors',
299
299
  'focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
300
300
  'disabled:cursor-not-allowed disabled:opacity-50',
301
301
  error && 'border-destructive focus:ring-destructive'
@@ -50,6 +50,35 @@
50
50
  // Store references to tab button elements for direct focus management
51
51
  let tabRefs: Record<string, HTMLButtonElement | undefined> = $state({});
52
52
 
53
+ // Horizontal-scroll handling for tab lists wider than their container
54
+ let tabList: HTMLDivElement | undefined = $state();
55
+ let canScrollLeft = $state(false);
56
+ let canScrollRight = $state(false);
57
+
58
+ function updateScrollShadows() {
59
+ if (!tabList) return;
60
+ canScrollLeft = tabList.scrollLeft > 1;
61
+ canScrollRight = tabList.scrollLeft + tabList.clientWidth < tabList.scrollWidth - 1;
62
+ }
63
+
64
+ // Scroll only the tab list (never the page) so deep-linked tabs are visible
65
+ function scrollTabIntoView(el: HTMLButtonElement) {
66
+ if (!tabList) return;
67
+ const listRect = tabList.getBoundingClientRect();
68
+ const rect = el.getBoundingClientRect();
69
+ if (rect.left < listRect.left) {
70
+ tabList.scrollLeft += rect.left - listRect.left - 8;
71
+ } else if (rect.right > listRect.right) {
72
+ tabList.scrollLeft += rect.right - listRect.right + 8;
73
+ }
74
+ }
75
+
76
+ $effect(() => {
77
+ const el = tabRefs[value];
78
+ if (el) scrollTabIntoView(el);
79
+ updateScrollShadows();
80
+ });
81
+
53
82
  // Create a reactive object for context
54
83
  // Initialize with empty values - the effect will set them immediately
55
84
  const tabsState = $state({ activeTab: '', tabsId: '' });
@@ -108,34 +137,50 @@
108
137
 
109
138
  <div class={cn('w-full', className)}>
110
139
  <!-- Tab List -->
111
- <div
112
- role="tablist"
113
- aria-label={label}
114
- class="inline-flex h-10 max-w-full items-center justify-center overflow-x-auto rounded-md bg-muted p-1 text-muted-foreground [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
115
- >
116
- {#each tabs as tab, index (tab.id)}
117
- <button
118
- bind:this={tabRefs[tab.id]}
119
- id={`${id}-tab-${tab.id}`}
120
- role="tab"
121
- type="button"
122
- aria-selected={value === tab.id}
123
- aria-controls={children && value === tab.id ? `${id}-panel-${tab.id}` : undefined}
124
- tabindex={value === tab.id ? 0 : -1}
125
- disabled={tab.disabled}
126
- class={cn(
127
- 'inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
128
- value === tab.id
129
- ? 'bg-background text-foreground shadow-sm'
130
- : 'hover:bg-background-hover hover:text-foreground',
131
- tab.disabled && 'opacity-50 pointer-events-none cursor-not-allowed'
132
- )}
133
- onclick={() => selectTab(tab.id)}
134
- onkeydown={(e) => handleKeyDown(e, index)}
135
- >
136
- {tab.label}
137
- </button>
138
- {/each}
140
+ <div class="relative inline-flex max-w-full">
141
+ {#if canScrollLeft}
142
+ <div
143
+ class="pointer-events-none absolute inset-y-0 left-0 z-10 w-6 rounded-l-md bg-gradient-to-r from-muted to-transparent"
144
+ aria-hidden="true"
145
+ ></div>
146
+ {/if}
147
+ {#if canScrollRight}
148
+ <div
149
+ class="pointer-events-none absolute inset-y-0 right-0 z-10 w-6 rounded-r-md bg-gradient-to-l from-muted to-transparent"
150
+ aria-hidden="true"
151
+ ></div>
152
+ {/if}
153
+ <div
154
+ bind:this={tabList}
155
+ onscroll={updateScrollShadows}
156
+ role="tablist"
157
+ aria-label={label}
158
+ class="inline-flex h-10 max-w-full items-center justify-center overflow-x-auto rounded-md bg-muted p-1 text-muted-foreground [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
159
+ >
160
+ {#each tabs as tab, index (tab.id)}
161
+ <button
162
+ bind:this={tabRefs[tab.id]}
163
+ id={`${id}-tab-${tab.id}`}
164
+ role="tab"
165
+ type="button"
166
+ aria-selected={value === tab.id}
167
+ aria-controls={children && value === tab.id ? `${id}-panel-${tab.id}` : undefined}
168
+ tabindex={value === tab.id ? 0 : -1}
169
+ disabled={tab.disabled}
170
+ class={cn(
171
+ 'inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
172
+ value === tab.id
173
+ ? 'bg-background text-foreground shadow-sm'
174
+ : 'hover:bg-background-hover hover:text-foreground',
175
+ tab.disabled && 'opacity-50 pointer-events-none cursor-not-allowed'
176
+ )}
177
+ onclick={() => selectTab(tab.id)}
178
+ onkeydown={(e) => handleKeyDown(e, index)}
179
+ >
180
+ {tab.label}
181
+ </button>
182
+ {/each}
183
+ </div>
139
184
  </div>
140
185
 
141
186
  <!-- Tab Panels Container -->
@@ -80,7 +80,7 @@
80
80
  <AppShell>
81
81
  <div
82
82
  class={cn(
83
- 'flex h-screen flex-col items-center justify-center overflow-auto bg-content-bg px-4 py-8 sm:px-6 lg:px-8',
83
+ 'flex min-h-[100dvh] flex-col items-center justify-center bg-content-bg px-4 py-8 sm:px-6 lg:px-8',
84
84
  showBackground && backgroundClasses[backgroundVariant],
85
85
  className
86
86
  )}
@@ -323,7 +323,10 @@
323
323
  {/if}
324
324
 
325
325
  <!-- Main Content -->
326
- <main id="main-content" class="flex-1 bg-content-bg {contentPaddingClass}">
326
+ <main
327
+ id="main-content"
328
+ class="min-w-0 flex-1 overflow-x-clip bg-content-bg {contentPaddingClass}"
329
+ >
327
330
  {@render children()}
328
331
  </main>
329
332
  </div>
@@ -51,10 +51,13 @@
51
51
  </script>
52
52
 
53
53
  <div
54
- class={cn('flex items-center justify-between gap-4 rounded-lg border bg-muted/50 p-3', className)}
54
+ class={cn(
55
+ 'flex flex-wrap items-center justify-between gap-x-4 gap-y-2 rounded-lg border bg-muted/50 p-3',
56
+ className
57
+ )}
55
58
  >
56
59
  <!-- Selection controls -->
57
- <div class="flex items-center gap-3">
60
+ <div class="flex flex-wrap items-center gap-3">
58
61
  <Checkbox checked={allSelected} onCheckedChange={handleSelectAllChange}>
59
62
  {#snippet children()}
60
63
  <span class="text-sm">
@@ -80,7 +83,7 @@
80
83
 
81
84
  <!-- Bulk actions -->
82
85
  {#if hasSelection}
83
- <div class="flex items-center gap-2">
86
+ <div class="flex flex-wrap items-center gap-2">
84
87
  {#if onMarkRead}
85
88
  <Button variant="outline" size="sm" onclick={onMarkRead}>
86
89
  {#snippet children()}
@@ -149,7 +149,7 @@
149
149
 
150
150
  <!-- Metadata -->
151
151
  <div class="border-t pt-4 mt-4">
152
- <dl class="grid grid-cols-2 gap-4 text-sm">
152
+ <dl class="grid grid-cols-1 gap-4 text-sm sm:grid-cols-2">
153
153
  <div>
154
154
  <dt class="text-muted-foreground">Category</dt>
155
155
  <dd class="font-medium capitalize">{notification.category}</dd>
@@ -69,7 +69,7 @@
69
69
  role="tablist"
70
70
  aria-label="Filter notifications"
71
71
  class={cn(
72
- 'inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground',
72
+ 'inline-flex h-10 max-w-full items-center justify-center overflow-x-auto rounded-md bg-muted p-1 text-muted-foreground [scrollbar-width:none] [&::-webkit-scrollbar]:hidden',
73
73
  className
74
74
  )}
75
75
  >
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classic-homes/theme-svelte",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "description": "Svelte components for the Classic theme system",
5
5
  "type": "module",
6
6
  "svelte": "./dist/lib/index.js",