@x33025/sveltely 0.1.2 → 0.1.4

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 (76) hide show
  1. package/dist/components/Library/AnimatedNumber/AnimatedNumber.demo.svelte +1 -1
  2. package/dist/components/Library/AsyncButton/AsyncButton.svelte +42 -16
  3. package/dist/components/Library/Button/Button.svelte +21 -13
  4. package/dist/components/Library/Calendar/Calendar.svelte +16 -16
  5. package/dist/components/Library/Checkbox/Checkbox.svelte +13 -14
  6. package/dist/components/Library/ChipInput/ChipInput.demo.svelte +1 -1
  7. package/dist/components/Library/ChipInput/ChipInput.svelte +7 -4
  8. package/dist/components/Library/Dropdown/Action.svelte +60 -0
  9. package/dist/components/Library/Dropdown/Action.svelte.d.ts +11 -0
  10. package/dist/components/Library/Dropdown/Divider.svelte +5 -0
  11. package/dist/components/Library/Dropdown/Divider.svelte.d.ts +19 -0
  12. package/dist/components/Library/Dropdown/Dropdown.demo.svelte +182 -72
  13. package/dist/components/Library/Dropdown/Dropdown.demo.svelte.d.ts +2 -1
  14. package/dist/components/Library/Dropdown/Dropdown.svelte +78 -267
  15. package/dist/components/Library/Dropdown/Dropdown.svelte.d.ts +17 -16
  16. package/dist/components/Library/Dropdown/Item.svelte +73 -0
  17. package/dist/components/Library/Dropdown/Item.svelte.d.ts +31 -0
  18. package/dist/components/Library/Dropdown/Section.svelte +34 -0
  19. package/dist/components/Library/Dropdown/Section.svelte.d.ts +8 -0
  20. package/dist/components/Library/Dropdown/context.d.ts +34 -0
  21. package/dist/components/Library/Dropdown/context.js +6 -0
  22. package/dist/components/Library/Dropdown/index.d.ts +13 -2
  23. package/dist/components/Library/Dropdown/index.js +11 -1
  24. package/dist/components/Library/Floating/Floating.svelte +10 -7
  25. package/dist/components/Library/ImageMask/BrushPreview.svelte +6 -6
  26. package/dist/components/Library/ImageMask/ImageMask.demo.svelte +7 -7
  27. package/dist/components/Library/ImageMask/MaskLayer.svelte +3 -3
  28. package/dist/components/Library/Label/Label.svelte +2 -4
  29. package/dist/components/Library/NavigationStack/NavigationStack.svelte +17 -7
  30. package/dist/components/Library/NavigationStack/Toolbar.svelte +7 -2
  31. package/dist/components/Library/NumberField/NumberField.svelte +14 -9
  32. package/dist/components/Library/Pagination/Pagination.svelte +16 -20
  33. package/dist/components/Library/Popover/Popover.demo.svelte +2 -2
  34. package/dist/components/Library/Popover/Popover.svelte +7 -4
  35. package/dist/components/Library/ScrollView/ScrollView.svelte +140 -3
  36. package/dist/components/Library/ScrollView/ScrollView.svelte.d.ts +28 -0
  37. package/dist/components/Library/ScrollView/index.d.ts +1 -0
  38. package/dist/components/Library/{SearchInput/SearchInput.demo.svelte → SearchField/SearchField.demo.svelte} +4 -4
  39. package/dist/components/Library/SearchField/SearchField.demo.svelte.d.ts +8 -0
  40. package/dist/components/Library/{SearchInput/SearchInput.svelte → SearchField/SearchField.svelte} +26 -30
  41. package/dist/components/Library/{SearchInput/SearchInput.svelte.d.ts → SearchField/SearchField.svelte.d.ts} +3 -3
  42. package/dist/components/Library/SearchField/index.d.ts +1 -0
  43. package/dist/components/Library/SearchField/index.js +1 -0
  44. package/dist/components/Library/SegmentedPicker/SegmentedPicker.demo.svelte +1 -1
  45. package/dist/components/Library/SegmentedPicker/SegmentedPicker.svelte +9 -9
  46. package/dist/components/Library/Sheet/Sheet.demo.svelte +1 -1
  47. package/dist/components/Library/Sheet/Sheet.svelte +8 -5
  48. package/dist/components/Library/Slider/Slider.demo.svelte +1 -1
  49. package/dist/components/Library/Slider/Slider.svelte +11 -10
  50. package/dist/components/Library/Spinner/Spinner.demo.svelte +1 -1
  51. package/dist/components/Library/Switch/Switch.svelte +6 -11
  52. package/dist/components/Library/TextField/TextField.svelte +14 -9
  53. package/dist/components/Library/{TokenSearchInput/TokenSearchInput.demo.svelte → TokenSearchField/TokenSearchField.demo.svelte} +4 -4
  54. package/dist/components/Library/TokenSearchField/TokenSearchField.demo.svelte.d.ts +9 -0
  55. package/dist/components/Library/{TokenSearchInput/TokenSearchInput.svelte → TokenSearchField/TokenSearchField.svelte} +70 -66
  56. package/dist/components/Library/{TokenSearchInput/TokenSearchInput.svelte.d.ts → TokenSearchField/TokenSearchField.svelte.d.ts} +3 -3
  57. package/dist/components/Library/TokenSearchField/index.d.ts +1 -0
  58. package/dist/components/Library/TokenSearchField/index.js +1 -0
  59. package/dist/components/Library/WheelPicker/WheelColumn.svelte +4 -10
  60. package/dist/components/Library/WheelPicker/WheelPicker.svelte +5 -9
  61. package/dist/components/Local/HeroCard.svelte +5 -3
  62. package/dist/components/Local/StyleControls.svelte +58 -27
  63. package/dist/components/Local/StyleControls.svelte.d.ts +3 -1
  64. package/dist/index.d.ts +3 -2
  65. package/dist/index.js +2 -2
  66. package/dist/style/index.css +9 -5
  67. package/dist/style.css +60 -29
  68. package/package.json +1 -1
  69. package/dist/components/Library/Dropdown/types.d.ts +0 -30
  70. package/dist/components/Library/Dropdown/types.js +0 -1
  71. package/dist/components/Library/SearchInput/SearchInput.demo.svelte.d.ts +0 -8
  72. package/dist/components/Library/SearchInput/index.d.ts +0 -1
  73. package/dist/components/Library/SearchInput/index.js +0 -1
  74. package/dist/components/Library/TokenSearchInput/TokenSearchInput.demo.svelte.d.ts +0 -9
  75. package/dist/components/Library/TokenSearchInput/index.d.ts +0 -1
  76. package/dist/components/Library/TokenSearchInput/index.js +0 -1
@@ -1,2 +1,13 @@
1
- export { default } from './Dropdown.svelte';
2
- export type { DropdownAction, DropdownDivider, DropdownEntry, DropdownGroup, DropdownItem, DropdownSubmenu } from './types';
1
+ import DropdownComponent from './Dropdown.svelte';
2
+ import Action from './Action.svelte';
3
+ import Divider from './Divider.svelte';
4
+ import Item from './Item.svelte';
5
+ import Section from './Section.svelte';
6
+ declare const Dropdown: typeof DropdownComponent & {
7
+ Action: typeof Action;
8
+ Divider: typeof Divider;
9
+ Item: typeof Item;
10
+ Section: typeof Section;
11
+ };
12
+ export default Dropdown;
13
+ export type { DropdownActionState, DropdownItemState, DropdownTriggerState } from './context';
@@ -1 +1,11 @@
1
- export { default } from './Dropdown.svelte';
1
+ import DropdownComponent from './Dropdown.svelte';
2
+ import Action from './Action.svelte';
3
+ import Divider from './Divider.svelte';
4
+ import Item from './Item.svelte';
5
+ import Section from './Section.svelte';
6
+ const Dropdown = DropdownComponent;
7
+ Dropdown.Action = Action;
8
+ Dropdown.Divider = Divider;
9
+ Dropdown.Item = Item;
10
+ Dropdown.Section = Section;
11
+ export default Dropdown;
@@ -2,11 +2,7 @@
2
2
  import { tick, onDestroy } from 'svelte';
3
3
  import type { Snippet } from 'svelte';
4
4
  import { portalContent } from '../../../actions/portal';
5
- import {
6
- autoAlignForViewport,
7
- computePosition,
8
- type Anchor
9
- } from '../../../utils/positioning';
5
+ import { autoAlignForViewport, computePosition, type Anchor } from '../../../utils/positioning';
10
6
  import {
11
7
  hasOpenChild,
12
8
  isAncestor,
@@ -255,7 +251,11 @@
255
251
  placement,
256
252
  preferredAlign
257
253
  );
258
- panelCoords = offsetCoords({ top: result.top, left: result.left }, result.anchor, getFloatingInset());
254
+ panelCoords = offsetCoords(
255
+ { top: result.top, left: result.left },
256
+ result.anchor,
257
+ getFloatingInset()
258
+ );
259
259
  panelTransform = result.transform;
260
260
  resolvedAnchor = result.anchor;
261
261
  };
@@ -339,7 +339,10 @@
339
339
 
340
340
  $effect(() => {
341
341
  if (typeof window === 'undefined') return;
342
- const handleWindowChange = () => closePanel();
342
+ const handleWindowChange = (event: Event) => {
343
+ if (event.type === 'scroll' && isEventInFloatingTree(event)) return;
344
+ closePanel();
345
+ };
343
346
  window.addEventListener('scroll', handleWindowChange, true);
344
347
  window.addEventListener('resize', handleWindowChange);
345
348
  return () => {
@@ -56,9 +56,9 @@
56
56
  pointer-events: none;
57
57
  border: 1.5px solid rgb(255 255 255 / 0.95);
58
58
  border-radius: 9999px;
59
- background: color-mix(in oklab, var(--sveltely-primary-color) 16%, transparent);
59
+ background: color-mix(in oklab, var(--sveltely-active-color) 16%, transparent);
60
60
  box-shadow:
61
- 0 0 0 1px color-mix(in oklab, var(--sveltely-primary-color) 90%, transparent),
61
+ 0 0 0 1px color-mix(in oklab, var(--sveltely-active-color) 90%, transparent),
62
62
  0 1px 4px rgb(0 0 0 / 0.22);
63
63
  will-change: transform, width, height;
64
64
  }
@@ -88,16 +88,16 @@
88
88
  box-sizing: border-box;
89
89
  border-radius: 9999px;
90
90
  border: 1.5px solid rgb(255 255 255 / 0.95);
91
- background: color-mix(in oklab, var(--sveltely-primary-color) 20%, transparent);
91
+ background: color-mix(in oklab, var(--sveltely-active-color) 20%, transparent);
92
92
  box-shadow:
93
- 0 0 0 1px color-mix(in oklab, var(--sveltely-primary-color) 95%, transparent),
93
+ 0 0 0 1px color-mix(in oklab, var(--sveltely-active-color) 95%, transparent),
94
94
  0 8px 24px rgb(0 0 0 / 0.24);
95
95
  }
96
96
 
97
97
  .brush-size-preview-label {
98
98
  border-radius: 9999px;
99
99
  background: rgb(255 255 255 / 0.88);
100
- color: var(--color-zinc-950);
100
+ color: var(--sveltely-primary-color);
101
101
  font-size: 0.75rem;
102
102
  font-weight: 650;
103
103
  line-height: 1;
@@ -113,7 +113,7 @@
113
113
  .brush-size-preview-erase .brush-size-preview-circle {
114
114
  background: rgb(255 255 255 / 0.2);
115
115
  box-shadow:
116
- 0 0 0 1px color-mix(in oklab, var(--sveltely-primary-color) 85%, transparent),
116
+ 0 0 0 1px color-mix(in oklab, var(--sveltely-active-color) 85%, transparent),
117
117
  0 8px 24px rgb(0 0 0 / 0.24);
118
118
  }
119
119
  </style>
@@ -30,15 +30,15 @@
30
30
  fit="cover"
31
31
  class="aspect-square"
32
32
  />
33
- <div class="vstack gap-2 text-sm text-zinc-600">
34
- <div class="font-medium text-zinc-900">Mask</div>
33
+ <div class="vstack gap-2 text-sm text-[var(--sveltely-secondary-color)]">
34
+ <div class="font-medium text-[var(--sveltely-primary-color)]">Mask</div>
35
35
  <div class="aspect-square overflow-hidden rounded-md border border-zinc-200 bg-black">
36
36
  {#if mask}
37
37
  <img src={mask.dataUrl} alt="Exported mask" class="size-full object-contain" />
38
38
  {/if}
39
39
  </div>
40
40
  <label class="vstack gap-2 bg-white p-3">
41
- <span class="font-medium text-zinc-900">Brush size {brushSize}px</span>
41
+ <span class="font-medium text-[var(--sveltely-primary-color)]">Brush size {brushSize}px</span>
42
42
  <Slider bind:value={brushSize} min={6} max={72} step={1} />
43
43
  <div class="image-mask-controls">
44
44
  <button
@@ -86,7 +86,7 @@
86
86
  width: fit-content;
87
87
  align-items: center;
88
88
  gap: 0.25rem;
89
- background: white;
89
+ background: var(--sveltely-background-color);
90
90
  padding: 0.25rem;
91
91
  }
92
92
 
@@ -97,7 +97,7 @@
97
97
  align-items: center;
98
98
  justify-content: center;
99
99
  border-radius: 9999px;
100
- color: var(--color-zinc-500);
100
+ color: var(--sveltely-secondary-color);
101
101
  transition:
102
102
  background-color 150ms,
103
103
  color 150ms,
@@ -106,8 +106,8 @@
106
106
 
107
107
  .image-mask-controls button:hover:not(:disabled),
108
108
  .image-mask-controls button.active {
109
- background: var(--sveltely-primary-color);
110
- color: white;
109
+ background: var(--sveltely-active-color);
110
+ color: var(--sveltely-background-color);
111
111
  }
112
112
 
113
113
  .image-mask-controls button:disabled {
@@ -145,9 +145,9 @@
145
145
  context.clearRect(0, 0, viewportWidth, viewportHeight);
146
146
  if (!surfaceCanvas) return;
147
147
  const primaryColor = canvas
148
- ? getComputedStyle(canvas).getPropertyValue('--sveltely-primary-color').trim() ||
149
- 'var(--sveltely-primary-color)'
150
- : 'var(--sveltely-primary-color)';
148
+ ? getComputedStyle(canvas).getPropertyValue('--sveltely-active-color').trim() ||
149
+ 'var(--sveltely-active-color)'
150
+ : 'var(--sveltely-active-color)';
151
151
  context.save();
152
152
  context.drawImage(surfaceCanvas, 0, 0, viewportWidth, viewportHeight);
153
153
  context.globalCompositeOperation = 'source-in';
@@ -127,11 +127,9 @@
127
127
  min-height: min-content;
128
128
  gap: var(--label-gap, calc(var(--sveltely-gap) * 0.75));
129
129
  border-radius: var(--label-border-radius, 0px);
130
- color: var(--label-color, var(--color-zinc-700));
130
+ color: var(--label-color, var(--sveltely-primary-color));
131
131
  font-size: var(--label-font-size);
132
- padding:
133
- var(--label-padding-y, 0px)
134
- var(--label-padding-x, 0px);
132
+ padding: var(--label-padding-y, 0px) var(--label-padding-x, 0px);
135
133
  }
136
134
 
137
135
  .label[data-orientation='top'] {
@@ -122,7 +122,9 @@
122
122
  </div>
123
123
  {/if}
124
124
 
125
- <div class="navigation-stack-pane navigation-stack-pane-center navigation-stack-pane-body navigation-stack-content vstack">
125
+ <div
126
+ class="navigation-stack-pane navigation-stack-pane-center navigation-stack-pane-body navigation-stack-content vstack"
127
+ >
126
128
  {#if children}
127
129
  {@render children()}
128
130
  {/if}
@@ -180,7 +182,7 @@
180
182
  .navigation-stack-pane-header {
181
183
  grid-row: 1;
182
184
  border-bottom: 1px solid var(--sveltely-border-color);
183
- background: white;
185
+ background: var(--sveltely-background-color);
184
186
  }
185
187
 
186
188
  .navigation-stack-pane-body {
@@ -204,20 +206,28 @@
204
206
  }
205
207
 
206
208
  .navigation-stack-sidebar-inner {
207
- background: var(--color-zinc-50);
209
+ background: var(--sveltely-background-color);
208
210
  transition:
209
211
  visibility 0ms linear 300ms,
210
212
  background-color 300ms ease-in-out;
211
213
  }
212
214
 
213
- .navigation-stack[data-left-open='true'] .navigation-stack-pane-left.navigation-stack-pane-body .navigation-stack-sidebar-inner,
214
- .navigation-stack[data-right-open='true'] .navigation-stack-pane-right.navigation-stack-pane-body .navigation-stack-sidebar-inner {
215
+ .navigation-stack[data-left-open='true']
216
+ .navigation-stack-pane-left.navigation-stack-pane-body
217
+ .navigation-stack-sidebar-inner,
218
+ .navigation-stack[data-right-open='true']
219
+ .navigation-stack-pane-right.navigation-stack-pane-body
220
+ .navigation-stack-sidebar-inner {
215
221
  visibility: visible;
216
222
  transition-delay: 0ms;
217
223
  }
218
224
 
219
- .navigation-stack[data-left-open='false'] .navigation-stack-pane-left.navigation-stack-pane-body .navigation-stack-sidebar-inner,
220
- .navigation-stack[data-right-open='false'] .navigation-stack-pane-right.navigation-stack-pane-body .navigation-stack-sidebar-inner {
225
+ .navigation-stack[data-left-open='false']
226
+ .navigation-stack-pane-left.navigation-stack-pane-body
227
+ .navigation-stack-sidebar-inner,
228
+ .navigation-stack[data-right-open='false']
229
+ .navigation-stack-pane-right.navigation-stack-pane-body
230
+ .navigation-stack-sidebar-inner {
221
231
  visibility: hidden;
222
232
  pointer-events: none;
223
233
  }
@@ -21,7 +21,12 @@
21
21
  const rootStyle = $derived.by(() => surfaceStyle(styleProps, 'navigation-stack-toolbar'));
22
22
  </script>
23
23
 
24
- <div class="navigation-stack-toolbar-shell vstack h-full" style={rootStyle} data-chrome={chrome ? 'true' : 'false'} {...props}>
24
+ <div
25
+ class="navigation-stack-toolbar-shell vstack h-full"
26
+ style={rootStyle}
27
+ data-chrome={chrome ? 'true' : 'false'}
28
+ {...props}
29
+ >
25
30
  {#if header}
26
31
  <div
27
32
  class="navigation-stack-toolbar flex items-center gap-2"
@@ -41,7 +46,7 @@
41
46
  <style>
42
47
  .navigation-stack-toolbar {
43
48
  border-bottom: 1px solid var(--sveltely-border-color);
44
- background: white;
49
+ background: var(--sveltely-background-color);
45
50
  }
46
51
 
47
52
  .navigation-stack-toolbar-shell[data-chrome='false'] .navigation-stack-toolbar {
@@ -64,7 +64,9 @@
64
64
  });
65
65
  const textEditorProps = $derived({ textAlign });
66
66
  const rootStyle = $derived.by(() =>
67
- [surfaceStyle(styleProps, 'text-field'), textEditorStyle(textEditorProps)].filter(Boolean).join(' ')
67
+ [surfaceStyle(styleProps, 'text-field'), textEditorStyle(textEditorProps)]
68
+ .filter(Boolean)
69
+ .join(' ')
68
70
  );
69
71
  const describedBy = $derived(help ? 'text-field-message' : undefined);
70
72
 
@@ -148,7 +150,7 @@
148
150
  min-width: 0;
149
151
  flex-direction: column;
150
152
  gap: var(--text-field-gap, calc(var(--sveltely-gap) * 0.75));
151
- color: var(--text-field-color, var(--color-zinc-900));
153
+ color: var(--text-field-color, var(--sveltely-primary-color));
152
154
  font-size: var(--text-field-font-size, calc(var(--sveltely-font-size) * 0.875));
153
155
  }
154
156
 
@@ -157,28 +159,31 @@
157
159
  min-width: 0;
158
160
  border: 1px solid var(--text-field-border-color, var(--sveltely-border-color));
159
161
  border-radius: var(--text-field-border-radius, var(--sveltely-border-radius));
160
- background: var(--text-field-background, white);
162
+ background: var(--text-field-background, var(--sveltely-background-color));
161
163
  color: inherit;
162
164
  font-variant-numeric: tabular-nums;
163
165
  line-height: 1.25rem;
164
166
  outline: none;
165
- padding:
166
- var(--text-field-padding-y, calc(var(--sveltely-padding-y) * 0.67))
167
+ padding: var(--text-field-padding-y, calc(var(--sveltely-padding-y) * 0.67))
167
168
  var(--text-field-padding-x, var(--sveltely-padding-x));
168
169
  text-align: var(--text-field-text-align, start);
169
- transition: color 150ms, border-color 150ms, background-color 150ms, box-shadow 150ms;
170
+ transition:
171
+ color 150ms,
172
+ border-color 150ms,
173
+ background-color 150ms,
174
+ box-shadow 150ms;
170
175
  }
171
176
 
172
177
  .text-field-input:focus {
173
- border-color: color-mix(in oklab, var(--sveltely-primary-color) 50%, white);
178
+ border-color: color-mix(in oklab, var(--sveltely-active-color) 50%, white);
174
179
  }
175
180
 
176
181
  .text-field-input::placeholder {
177
- color: var(--color-zinc-400);
182
+ color: var(--sveltely-secondary-color);
178
183
  }
179
184
 
180
185
  .text-field-message {
181
- color: var(--color-zinc-500);
186
+ color: var(--sveltely-secondary-color);
182
187
  font-size: calc(var(--text-field-font-size, 0.875rem) * 0.857);
183
188
  line-height: 1.25;
184
189
  }
@@ -26,7 +26,8 @@
26
26
  onPageChange?: (page: number, href: string) => void | Promise<void>;
27
27
  showFirstLast?: boolean;
28
28
  variant?: 'icon' | 'label';
29
- } & StyleProps & Record<string, unknown> = $props();
29
+ } & StyleProps &
30
+ Record<string, unknown> = $props();
30
31
 
31
32
  const extractedStyleProps = $derived.by(() => extractStyleProps(restProps));
32
33
  const styleProps = $derived(extractedStyleProps.styleProps);
@@ -48,7 +49,7 @@
48
49
  const hasNext = () => (typeof data.hasNext === 'boolean' ? data.hasNext : safePage() < maxPage());
49
50
  const hasPrevious = () => safePage() > 1;
50
51
  const buttonClass = () =>
51
- `pagination-button action disabled:bg-zinc-200 disabled:text-zinc-500 inline-flex items-center justify-center leading-none ${
52
+ `pagination-button action disabled:bg-zinc-200 disabled:text-[var(--sveltely-secondary-color)] inline-flex items-center justify-center leading-none ${
52
53
  variant === 'icon' ? 'pagination-button-icon' : ''
53
54
  }`;
54
55
 
@@ -114,14 +115,8 @@
114
115
 
115
116
  <form method="GET" style="display: inline;" novalidate onsubmit={handleSubmit}>
116
117
  <input type="hidden" name="view" value={data.view} />
117
- <input
118
- id="page"
119
- name="page"
120
- type="number"
121
- class="pagination-input"
122
- value={safePage()}
123
- />
124
- </form>
118
+ <input id="page" name="page" type="number" class="pagination-input" value={safePage()} />
119
+ </form>
125
120
 
126
121
  <span>of {maxPage()}</span>
127
122
 
@@ -192,18 +187,20 @@
192
187
 
193
188
  .pagination-button {
194
189
  border-radius: var(--sveltely-border-radius);
195
- background: var(--color-zinc-100);
196
- padding:
197
- calc(var(--sveltely-padding-y) * 0.67 * var(--pagination-scale))
190
+ background: var(--sveltely-inactive-color);
191
+ padding: calc(var(--sveltely-padding-y) * 0.67 * var(--pagination-scale))
198
192
  calc(var(--sveltely-padding-x) * 0.67 * var(--pagination-scale));
199
193
  }
200
194
 
201
195
  .pagination-button-icon {
202
- padding:
203
- calc(var(--sveltely-padding-y) * 0.67 * var(--pagination-scale))
196
+ padding: calc(var(--sveltely-padding-y) * 0.67 * var(--pagination-scale))
204
197
  calc(var(--sveltely-padding-x) * 0.67 * var(--pagination-scale));
205
- min-width: calc((var(--sveltely-padding-x) * 1.34 * var(--pagination-scale)) + var(--pagination-icon-size));
206
- min-height: calc((var(--sveltely-padding-y) * 1.34 * var(--pagination-scale)) + var(--pagination-icon-size));
198
+ min-width: calc(
199
+ (var(--sveltely-padding-x) * 1.34 * var(--pagination-scale)) + var(--pagination-icon-size)
200
+ );
201
+ min-height: calc(
202
+ (var(--sveltely-padding-y) * 1.34 * var(--pagination-scale)) + var(--pagination-icon-size)
203
+ );
207
204
  }
208
205
 
209
206
  .pagination-button:disabled {
@@ -214,12 +211,11 @@
214
211
  .pagination-input {
215
212
  width: 10ch;
216
213
  border-radius: var(--sveltely-border-radius);
217
- background: var(--color-zinc-100);
214
+ background: var(--sveltely-inactive-color);
218
215
  font-size: inherit;
219
216
  line-height: 1.25;
220
217
  outline: none;
221
- padding:
222
- calc(var(--sveltely-padding-y) * 0.33 * var(--pagination-scale))
218
+ padding: calc(var(--sveltely-padding-y) * 0.33 * var(--pagination-scale))
223
219
  calc(var(--sveltely-padding-y) * 0.33 * var(--pagination-scale))
224
220
  calc(var(--sveltely-padding-y) * 0.33 * var(--pagination-scale))
225
221
  calc(var(--sveltely-padding-x) * var(--pagination-scale));
@@ -7,7 +7,7 @@
7
7
 
8
8
  <script lang="ts">
9
9
  import Popover from './Popover.svelte';
10
- import SearchInput from '../SearchInput/SearchInput.svelte';
10
+ import SearchField from '../SearchField/SearchField.svelte';
11
11
 
12
12
  let search = $state('');
13
13
  </script>
@@ -17,5 +17,5 @@
17
17
  <span>Open popover</span>
18
18
  {/snippet}
19
19
 
20
- <SearchInput id="popover-demo-search" bind:value={search} placeholder="Type to search..." />
20
+ <SearchField id="popover-demo-search" bind:value={search} placeholder="Type to search..." />
21
21
  </Popover>
@@ -68,7 +68,7 @@
68
68
  --sveltely-nested-inset: var(--popover-inset);
69
69
  border: 1px solid var(--sveltely-border-color);
70
70
  border-radius: var(--sveltely-border-radius);
71
- background: white;
71
+ background: var(--sveltely-background-color);
72
72
  padding: var(--popover-inset);
73
73
  }
74
74
 
@@ -77,13 +77,16 @@
77
77
  align-items: center;
78
78
  border: 1px solid var(--sveltely-border-color);
79
79
  border-radius: var(--sveltely-border-radius);
80
- background: white;
81
- color: var(--color-zinc-800);
80
+ background: var(--sveltely-background-color);
81
+ color: var(--sveltely-primary-color);
82
82
  gap: var(--sveltely-gap);
83
83
  padding: calc(var(--sveltely-padding-y) * 0.67) var(--sveltely-padding-x);
84
84
  font-size: 0.875rem;
85
85
  line-height: 1.25rem;
86
- transition: color 150ms, border-color 150ms, background-color 150ms;
86
+ transition:
87
+ color 150ms,
88
+ border-color 150ms,
89
+ background-color 150ms;
87
90
  }
88
91
 
89
92
  :global(.popover-trigger[data-styled='true']:hover) {
@@ -1,13 +1,43 @@
1
1
  <script lang="ts">
2
+ import { tick } from 'svelte';
2
3
  import type { Snippet } from 'svelte';
3
4
  import { surfaceStyle, type StyleProps } from '../../../style/surface';
4
5
  import type { ScrollAxis } from '../../../style/scroll';
5
6
 
7
+ export type ScrollGeometry = {
8
+ axis: ScrollAxis;
9
+ offset: {
10
+ x: number;
11
+ y: number;
12
+ };
13
+ viewport: {
14
+ width: number;
15
+ height: number;
16
+ };
17
+ content: {
18
+ width: number;
19
+ height: number;
20
+ };
21
+ remaining: {
22
+ top: number;
23
+ right: number;
24
+ bottom: number;
25
+ left: number;
26
+ };
27
+ progress: {
28
+ x: number;
29
+ y: number;
30
+ };
31
+ };
32
+
6
33
  type Props = {
7
34
  children: Snippet;
8
35
  viewport?: HTMLElement | null;
9
36
  axis?: ScrollAxis;
10
37
  contentStyles?: StyleProps;
38
+ onScroll?: (geometry: ScrollGeometry) => void;
39
+ scrollGradient?: boolean;
40
+ scrollGradientSize?: number | string;
11
41
  } & StyleProps;
12
42
 
13
43
  let {
@@ -15,6 +45,9 @@
15
45
  viewport = $bindable<HTMLElement | null>(null),
16
46
  axis = 'vertical',
17
47
  contentStyles = {},
48
+ onScroll,
49
+ scrollGradient = true,
50
+ scrollGradientSize = '1rem',
18
51
  fontSize,
19
52
  paddingX,
20
53
  paddingY,
@@ -39,6 +72,72 @@
39
72
  });
40
73
  const viewportStyle = $derived.by(() => surfaceStyle(styleProps, 'scroll-view'));
41
74
  const contentStyle = $derived.by(() => surfaceStyle(contentStyles, 'scroll-view-content'));
75
+ let canScrollToTop = $state(false);
76
+ let canScrollToBottom = $state(false);
77
+ const scrollGradientEnabled = $derived(scrollGradient && axis !== 'horizontal');
78
+ const scrollGradientStyle = $derived(
79
+ `--scroll-view-gradient-size: ${typeof scrollGradientSize === 'number' ? `${scrollGradientSize}rem` : scrollGradientSize};`
80
+ );
81
+
82
+ function syncScrollGradient(geometry: ScrollGeometry) {
83
+ if (!scrollGradientEnabled) return;
84
+ canScrollToTop = geometry.remaining.top > 0;
85
+ canScrollToBottom = geometry.remaining.bottom > 0;
86
+ }
87
+
88
+ function getScrollGeometry(node: HTMLElement): ScrollGeometry {
89
+ const maxX = Math.max(0, node.scrollWidth - node.clientWidth);
90
+ const maxY = Math.max(0, node.scrollHeight - node.clientHeight);
91
+ const x = node.scrollLeft;
92
+ const y = node.scrollTop;
93
+
94
+ return {
95
+ axis,
96
+ offset: { x, y },
97
+ viewport: {
98
+ width: node.clientWidth,
99
+ height: node.clientHeight
100
+ },
101
+ content: {
102
+ width: node.scrollWidth,
103
+ height: node.scrollHeight
104
+ },
105
+ remaining: {
106
+ top: y,
107
+ right: maxX - x,
108
+ bottom: maxY - y,
109
+ left: x
110
+ },
111
+ progress: {
112
+ x: maxX === 0 ? 1 : x / maxX,
113
+ y: maxY === 0 ? 1 : y / maxY
114
+ }
115
+ };
116
+ }
117
+
118
+ function handleScroll(event: Event) {
119
+ const geometry = getScrollGeometry(event.currentTarget as HTMLElement);
120
+ syncScrollGradient(geometry);
121
+ onScroll?.(geometry);
122
+ }
123
+
124
+ function handleWheel(event: WheelEvent) {
125
+ const node = event.currentTarget as HTMLElement;
126
+
127
+ event.preventDefault();
128
+ event.stopPropagation();
129
+
130
+ if (axis !== 'horizontal') node.scrollTop += event.deltaY;
131
+ if (axis !== 'vertical') node.scrollLeft += event.deltaX;
132
+ }
133
+
134
+ $effect(() => {
135
+ if (!scrollGradientEnabled || !viewport) return;
136
+ void tick().then(() => {
137
+ if (!viewport) return;
138
+ syncScrollGradient(getScrollGeometry(viewport));
139
+ });
140
+ });
42
141
  </script>
43
142
 
44
143
  <div
@@ -47,11 +146,22 @@
47
146
  class:scroll-view-vertical={axis === 'vertical'}
48
147
  class:scroll-view-horizontal={axis === 'horizontal'}
49
148
  class:scroll-view-both={axis === 'both'}
50
- style={viewportStyle}
149
+ class:scroll-view-gradient={scrollGradientEnabled}
150
+ class:scroll-view-can-scroll-up={canScrollToTop}
151
+ class:scroll-view-can-scroll-down={canScrollToBottom}
152
+ style={[viewportStyle, scrollGradientStyle].filter(Boolean).join(' ')}
153
+ onscroll={handleScroll}
154
+ onwheel={handleWheel}
51
155
  >
156
+ {#if scrollGradientEnabled}
157
+ <div class="scroll-view-gradient scroll-view-gradient-top"></div>
158
+ {/if}
52
159
  <div class="scroll-view-content" style={contentStyle}>
53
160
  {@render children()}
54
161
  </div>
162
+ {#if scrollGradientEnabled}
163
+ <div class="scroll-view-gradient scroll-view-gradient-bottom"></div>
164
+ {/if}
55
165
  </div>
56
166
 
57
167
  <style>
@@ -65,6 +175,7 @@
65
175
  color: var(--scroll-view-color, inherit);
66
176
  scrollbar-gutter: stable;
67
177
  overscroll-behavior: contain;
178
+ position: relative;
68
179
  }
69
180
 
70
181
  .scroll-view-vertical {
@@ -84,8 +195,7 @@
84
195
  .scroll-view-content {
85
196
  min-width: 100%;
86
197
  min-height: 100%;
87
- padding:
88
- var(--scroll-view-content-padding-y, var(--sveltely-inset))
198
+ padding: var(--scroll-view-content-padding-y, var(--sveltely-inset))
89
199
  var(--scroll-view-content-padding-x, var(--sveltely-inset));
90
200
  }
91
201
 
@@ -102,4 +212,31 @@
102
212
  width: max-content;
103
213
  min-width: 100%;
104
214
  }
215
+
216
+ .scroll-view-gradient {
217
+ position: sticky;
218
+ z-index: 2;
219
+ height: var(--scroll-view-gradient-size, 1rem);
220
+ flex: 0 0 var(--scroll-view-gradient-size, 1rem);
221
+ pointer-events: none;
222
+ opacity: 0;
223
+ transition: opacity 120ms ease;
224
+ }
225
+
226
+ .scroll-view-gradient-top {
227
+ top: 0;
228
+ margin-bottom: calc(var(--scroll-view-gradient-size, 1rem) * -1);
229
+ background: linear-gradient(to bottom, var(--scroll-view-background, white), transparent);
230
+ }
231
+
232
+ .scroll-view-gradient-bottom {
233
+ bottom: 0;
234
+ margin-top: calc(var(--scroll-view-gradient-size, 1rem) * -1);
235
+ background: linear-gradient(to top, var(--scroll-view-background, white), transparent);
236
+ }
237
+
238
+ .scroll-view-can-scroll-up .scroll-view-gradient-top,
239
+ .scroll-view-can-scroll-down .scroll-view-gradient-bottom {
240
+ opacity: 1;
241
+ }
105
242
  </style>