@x33025/sveltely 0.1.23 → 0.1.25
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.
- package/dist/actions/LoaderOverlay.svelte +10 -3
- package/dist/components/Library/Accordion/Accordion.demo.svelte +21 -0
- package/dist/components/Library/Accordion/Accordion.demo.svelte.d.ts +9 -0
- package/dist/components/Library/Accordion/Accordion.svelte +78 -0
- package/dist/components/Library/Accordion/Accordion.svelte.d.ts +14 -0
- package/dist/components/Library/Accordion/Content.svelte +57 -0
- package/dist/components/Library/Accordion/Content.svelte.d.ts +8 -0
- package/dist/components/Library/Accordion/Header.svelte +98 -0
- package/dist/components/Library/Accordion/Header.svelte.d.ts +10 -0
- package/dist/components/Library/Accordion/context.d.ts +9 -0
- package/dist/components/Library/Accordion/context.js +6 -0
- package/dist/components/Library/Accordion/index.d.ts +9 -0
- package/dist/components/Library/Accordion/index.js +7 -0
- package/dist/components/Library/AnimatedNumber/AnimatedNumber.demo.svelte +3 -2
- package/dist/components/Library/ArticleEditor/ArticleEditor.svelte +1 -2
- package/dist/components/Library/ArticleEditor/Blocks/Table.svelte +133 -172
- package/dist/components/Library/Checkbox/Checkbox.demo.svelte +5 -4
- package/dist/components/Library/Checkbox/Checkbox.svelte +6 -5
- package/dist/components/Library/Checkbox/Checkbox.svelte.d.ts +2 -1
- package/dist/components/Library/ChipInput/ChipInput.demo.svelte +3 -2
- package/dist/components/Library/Dropdown/Dropdown.demo.svelte +20 -15
- package/dist/components/Library/Floating/Floating.svelte +5 -6
- package/dist/components/Library/Grid/Grid.demo.svelte +58 -0
- package/dist/components/Library/Grid/Grid.demo.svelte.d.ts +25 -0
- package/dist/components/Library/Grid/Grid.svelte +128 -25
- package/dist/components/Library/Grid/Grid.svelte.d.ts +38 -9
- package/dist/components/Library/Grid/GridItem.svelte +18 -14
- package/dist/components/Library/Grid/GridItem.svelte.d.ts +2 -1
- package/dist/components/Library/HStack/HStack.svelte +4 -4
- package/dist/components/Library/HStack/HStack.svelte.d.ts +2 -1
- package/dist/components/Library/Image/Image.demo.svelte +3 -1
- package/dist/components/Library/Image/Image.demo.svelte.d.ts +2 -0
- package/dist/components/Library/ImageMask/ImageMask.demo.svelte +8 -6
- package/dist/components/Library/Label/Label.demo.svelte +5 -5
- package/dist/components/Library/Label/Label.svelte +10 -26
- package/dist/components/Library/NavigationStack/Link.svelte +1 -4
- package/dist/components/Library/Notifications/Notifications.demo.svelte +63 -0
- package/dist/components/Library/Notifications/Notifications.demo.svelte.d.ts +9 -0
- package/dist/components/Library/Notifications/Notifications.svelte +155 -0
- package/dist/components/Library/Notifications/Notifications.svelte.d.ts +35 -0
- package/dist/components/Library/Notifications/index.d.ts +2 -0
- package/dist/components/Library/Notifications/index.js +1 -0
- package/dist/components/Library/Notifications/types.d.ts +8 -0
- package/dist/components/Library/Notifications/types.js +1 -0
- package/dist/components/Library/NumberField/NumberField.svelte +25 -19
- package/dist/components/Library/Pagination/Pagination.demo.svelte +3 -2
- package/dist/components/Library/Pagination/Pagination.svelte +6 -18
- package/dist/components/Library/Popover/PopoverDebugOverlay.svelte +3 -3
- package/dist/components/Library/Portal/Content.svelte +20 -0
- package/dist/components/Library/Portal/Content.svelte.d.ts +10 -0
- package/dist/components/Library/Portal/Portal.svelte +4 -0
- package/dist/components/Library/Portal/Portal.svelte.d.ts +1 -0
- package/dist/components/Library/Portal/index.d.ts +1 -0
- package/dist/components/Library/Portal/index.js +1 -0
- package/dist/components/Library/ScrollView/ScrollView.svelte +88 -9
- package/dist/components/Library/ScrollView/ScrollView.svelte.d.ts +9 -2
- package/dist/components/Library/ScrollView/index.d.ts +1 -1
- package/dist/components/Library/SearchField/SearchField.demo.svelte +3 -2
- package/dist/components/Library/SearchField/SearchField.svelte +5 -5
- package/dist/components/Library/SearchField/SearchField.svelte.d.ts +2 -1
- package/dist/components/Library/SegmentedPicker/SegmentedPicker.demo.svelte +3 -2
- package/dist/components/Library/Sheet/Sheet.demo.svelte +3 -2
- package/dist/components/Library/Sheet/Sheet.svelte +3 -3
- package/dist/components/Library/Slider/Slider.demo.svelte +3 -2
- package/dist/components/Library/Spinner/Spinner.demo.svelte +3 -2
- package/dist/components/Library/Switch/Switch.demo.svelte +5 -4
- package/dist/components/Library/Switch/Switch.svelte +6 -5
- package/dist/components/Library/Switch/Switch.svelte.d.ts +2 -1
- package/dist/components/Library/Table/Column.svelte +3 -0
- package/dist/components/Library/Table/Column.svelte.d.ts +1 -0
- package/dist/components/Library/Table/Table.demo.svelte +230 -17
- package/dist/components/Library/Table/Table.svelte +322 -78
- package/dist/components/Library/Table/Table.svelte.d.ts +5 -0
- package/dist/components/Library/Table/types.d.ts +1 -0
- package/dist/components/Library/TextField/TextField.svelte +20 -14
- package/dist/components/Library/TextShimmer/TextShimmer.demo.svelte +3 -2
- package/dist/components/Library/TimePicker/TimePicker.demo.svelte +3 -10
- package/dist/components/Library/TokenSearchField/TokenSearchField.demo.svelte +3 -2
- package/dist/components/Library/VStack/VStack.svelte +4 -4
- package/dist/components/Library/VStack/VStack.svelte.d.ts +2 -1
- package/dist/components/Local/ColorStyleControls.svelte +25 -72
- package/dist/components/Local/ComponentGrid.svelte +99 -27
- package/dist/components/Local/ComponentGrid.svelte.d.ts +2 -1
- package/dist/components/Local/ComponentPage.svelte +74 -0
- package/dist/components/Local/ComponentPage.svelte.d.ts +13 -0
- package/dist/components/Local/HeroCard.svelte +10 -6
- package/dist/components/Local/LayoutStyleControls.svelte +33 -25
- package/dist/components/Local/StyleControls.svelte +1 -1
- package/dist/index.d.ts +8 -3
- package/dist/index.js +4 -1
- package/dist/style/index.css +3 -4
- package/dist/style/layout.d.ts +15 -36
- package/dist/style/layout.js +35 -83
- package/dist/style/surface.d.ts +1 -0
- package/dist/style/surface.js +10 -0
- package/dist/style.css +3 -51
- package/dist/viewport/geometry.d.ts +8 -0
- package/dist/viewport/geometry.js +43 -0
- package/dist/viewport/index.d.ts +4 -0
- package/dist/viewport/index.js +4 -0
- package/dist/viewport/layout.d.ts +4 -0
- package/dist/viewport/layout.js +138 -0
- package/dist/viewport/placement.d.ts +4 -0
- package/dist/viewport/placement.js +14 -0
- package/dist/viewport/types.d.ts +81 -0
- package/dist/viewport/types.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
<script lang="ts" generics="TItem extends NotificationItem = NotificationItem">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import { flip } from 'svelte/animate';
|
|
4
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
5
|
+
import { fade, fly } from 'svelte/transition';
|
|
6
|
+
import { extractLayoutProps, layoutStyle, type LayoutProps } from '../../../style/layout';
|
|
7
|
+
import { extractStyleProps, surfaceStyle, type StyleProps } from '../../../style/surface';
|
|
8
|
+
import type { NotificationItem } from './types';
|
|
9
|
+
|
|
10
|
+
type Props = {
|
|
11
|
+
items?: TItem[];
|
|
12
|
+
children?: Snippet<[TItem, number]>;
|
|
13
|
+
empty?: Snippet;
|
|
14
|
+
key?: (item: TItem, index: number) => string | number;
|
|
15
|
+
animationDuration?: number;
|
|
16
|
+
} & LayoutProps &
|
|
17
|
+
StyleProps &
|
|
18
|
+
Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'class' | 'style'>;
|
|
19
|
+
|
|
20
|
+
let {
|
|
21
|
+
items = $bindable<TItem[]>([]),
|
|
22
|
+
children,
|
|
23
|
+
empty,
|
|
24
|
+
key,
|
|
25
|
+
animationDuration = 180,
|
|
26
|
+
...restProps
|
|
27
|
+
}: Props = $props();
|
|
28
|
+
|
|
29
|
+
const extractedLayoutProps = $derived.by(() => extractLayoutProps(restProps));
|
|
30
|
+
const layoutProps = $derived(extractedLayoutProps.layoutProps);
|
|
31
|
+
const afterLayoutProps = $derived(extractedLayoutProps.restProps);
|
|
32
|
+
const extractedStyleProps = $derived.by(() => extractStyleProps(afterLayoutProps));
|
|
33
|
+
const styleProps = $derived(extractedStyleProps.styleProps);
|
|
34
|
+
const forwardedProps = $derived(extractedStyleProps.restProps);
|
|
35
|
+
const rootStyle = $derived.by(() =>
|
|
36
|
+
[layoutStyle(layoutProps), surfaceStyle(styleProps, 'notifications')].filter(Boolean).join(' ')
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
function itemKey(item: TItem, index: number) {
|
|
40
|
+
return key?.(item, index) ?? item.id ?? index;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function itemTitle(item: TItem) {
|
|
44
|
+
return item.title ?? String(item.id ?? 'Notification');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function itemMessage(item: TItem) {
|
|
48
|
+
return item.message ?? item.description ?? '';
|
|
49
|
+
}
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<div class="notifications" style={rootStyle} {...forwardedProps}>
|
|
53
|
+
{#each items as item, index (itemKey(item, index))}
|
|
54
|
+
<div
|
|
55
|
+
class="notification"
|
|
56
|
+
data-tone={item.tone ?? 'default'}
|
|
57
|
+
in:fly={{ y: -8, duration: animationDuration }}
|
|
58
|
+
out:fade={{ duration: animationDuration * 0.75 }}
|
|
59
|
+
animate:flip={{ duration: animationDuration }}
|
|
60
|
+
>
|
|
61
|
+
{#if children}
|
|
62
|
+
{@render children(item, index)}
|
|
63
|
+
{:else}
|
|
64
|
+
<p class="notification-title">{itemTitle(item)}</p>
|
|
65
|
+
{#if itemMessage(item)}
|
|
66
|
+
<p class="notification-message">{itemMessage(item)}</p>
|
|
67
|
+
{/if}
|
|
68
|
+
{/if}
|
|
69
|
+
</div>
|
|
70
|
+
{:else}
|
|
71
|
+
{#if empty}
|
|
72
|
+
<div class="notifications-empty">
|
|
73
|
+
{@render empty()}
|
|
74
|
+
</div>
|
|
75
|
+
{/if}
|
|
76
|
+
{/each}
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<style>
|
|
80
|
+
.notifications {
|
|
81
|
+
--notifications-font-size: var(--sveltely-font-size);
|
|
82
|
+
--notifications-padding-x: 0px;
|
|
83
|
+
--notifications-padding-y: 0px;
|
|
84
|
+
--notifications-gap: var(--sveltely-gap);
|
|
85
|
+
--notifications-border-radius: var(--sveltely-border-radius);
|
|
86
|
+
display: inline-flex;
|
|
87
|
+
min-width: 0;
|
|
88
|
+
min-height: 0;
|
|
89
|
+
flex-direction: column;
|
|
90
|
+
align-items: stretch;
|
|
91
|
+
gap: var(--notifications-gap);
|
|
92
|
+
border-radius: var(--notifications-border-radius);
|
|
93
|
+
padding: var(--notifications-padding-y) var(--notifications-padding-x);
|
|
94
|
+
font-size: var(--notifications-font-size);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.notification {
|
|
98
|
+
position: relative;
|
|
99
|
+
min-width: min(100%, 16rem);
|
|
100
|
+
border: 1px solid var(--sveltely-border-color);
|
|
101
|
+
border-radius: var(--sveltely-border-radius);
|
|
102
|
+
background: var(--sveltely-background-color);
|
|
103
|
+
box-shadow: var(--sveltely-shadow);
|
|
104
|
+
color: var(--sveltely-text-primary-color);
|
|
105
|
+
padding: calc(var(--sveltely-padding-y) * 0.83) var(--sveltely-padding-x);
|
|
106
|
+
overflow: hidden;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.notification::before {
|
|
110
|
+
position: absolute;
|
|
111
|
+
inset-block: 0;
|
|
112
|
+
inset-inline-start: 0;
|
|
113
|
+
width: 3px;
|
|
114
|
+
background: var(--notification-accent, var(--sveltely-control-active-color));
|
|
115
|
+
content: '';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.notification[data-tone='info'] {
|
|
119
|
+
--notification-accent: #2563eb;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.notification[data-tone='success'] {
|
|
123
|
+
--notification-accent: #16a34a;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.notification[data-tone='warning'] {
|
|
127
|
+
--notification-accent: #d97706;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.notification[data-tone='danger'] {
|
|
131
|
+
--notification-accent: #dc2626;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.notification-title,
|
|
135
|
+
.notification-message {
|
|
136
|
+
margin: 0;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.notification-title {
|
|
140
|
+
font-weight: 600;
|
|
141
|
+
line-height: 1.35;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.notification-message {
|
|
145
|
+
margin-top: 0.125rem;
|
|
146
|
+
color: var(--sveltely-text-secondary-color);
|
|
147
|
+
font-size: 0.875em;
|
|
148
|
+
line-height: 1.45;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.notifications-empty {
|
|
152
|
+
color: var(--sveltely-text-secondary-color);
|
|
153
|
+
font-size: 0.875em;
|
|
154
|
+
}
|
|
155
|
+
</style>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import { type LayoutProps } from '../../../style/layout';
|
|
4
|
+
import { type StyleProps } from '../../../style/surface';
|
|
5
|
+
import type { NotificationItem } from './types';
|
|
6
|
+
declare function $$render<TItem extends NotificationItem = NotificationItem>(): {
|
|
7
|
+
props: {
|
|
8
|
+
items?: TItem[];
|
|
9
|
+
children?: Snippet<[TItem, number]>;
|
|
10
|
+
empty?: Snippet;
|
|
11
|
+
key?: (item: TItem, index: number) => string | number;
|
|
12
|
+
animationDuration?: number;
|
|
13
|
+
} & LayoutProps & StyleProps & Omit<HTMLAttributes<HTMLDivElement>, "style" | "class" | "children">;
|
|
14
|
+
exports: {};
|
|
15
|
+
bindings: "items";
|
|
16
|
+
slots: {};
|
|
17
|
+
events: {};
|
|
18
|
+
};
|
|
19
|
+
declare class __sveltets_Render<TItem extends NotificationItem = NotificationItem> {
|
|
20
|
+
props(): ReturnType<typeof $$render<TItem>>['props'];
|
|
21
|
+
events(): ReturnType<typeof $$render<TItem>>['events'];
|
|
22
|
+
slots(): ReturnType<typeof $$render<TItem>>['slots'];
|
|
23
|
+
bindings(): "items";
|
|
24
|
+
exports(): {};
|
|
25
|
+
}
|
|
26
|
+
interface $$IsomorphicComponent {
|
|
27
|
+
new <TItem extends NotificationItem = NotificationItem>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<TItem>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<TItem>['props']>, ReturnType<__sveltets_Render<TItem>['events']>, ReturnType<__sveltets_Render<TItem>['slots']>> & {
|
|
28
|
+
$$bindings?: ReturnType<__sveltets_Render<TItem>['bindings']>;
|
|
29
|
+
} & ReturnType<__sveltets_Render<TItem>['exports']>;
|
|
30
|
+
<TItem extends NotificationItem = NotificationItem>(internal: unknown, props: ReturnType<__sveltets_Render<TItem>['props']> & {}): ReturnType<__sveltets_Render<TItem>['exports']>;
|
|
31
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
32
|
+
}
|
|
33
|
+
declare const Notifications: $$IsomorphicComponent;
|
|
34
|
+
type Notifications<TItem extends NotificationItem = NotificationItem> = InstanceType<typeof Notifications<TItem>>;
|
|
35
|
+
export default Notifications;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './Notifications.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -120,25 +120,27 @@
|
|
|
120
120
|
data-disabled={disabled ? 'true' : 'false'}
|
|
121
121
|
data-error={error ? 'true' : 'false'}
|
|
122
122
|
>
|
|
123
|
-
<
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
123
|
+
<div class="text-field-control">
|
|
124
|
+
<input
|
|
125
|
+
value={draftValue}
|
|
126
|
+
type="number"
|
|
127
|
+
{disabled}
|
|
128
|
+
{name}
|
|
129
|
+
{autocomplete}
|
|
130
|
+
{required}
|
|
131
|
+
{readonly}
|
|
132
|
+
{inputmode}
|
|
133
|
+
{placeholder}
|
|
134
|
+
{min}
|
|
135
|
+
{max}
|
|
136
|
+
{step}
|
|
137
|
+
aria-invalid={error ? 'true' : undefined}
|
|
138
|
+
aria-describedby={describedBy}
|
|
139
|
+
class="text-field-input"
|
|
140
|
+
oninput={handleInput}
|
|
141
|
+
onblur={handleBlur}
|
|
142
|
+
/>
|
|
143
|
+
</div>
|
|
142
144
|
|
|
143
145
|
{#if help}
|
|
144
146
|
<span id="text-field-message" class="text-field-message">{help}</span>
|
|
@@ -156,6 +158,10 @@
|
|
|
156
158
|
font-size: var(--text-field-font-size, var(--sveltely-font-size));
|
|
157
159
|
}
|
|
158
160
|
|
|
161
|
+
.text-field-control {
|
|
162
|
+
display: contents;
|
|
163
|
+
}
|
|
164
|
+
|
|
159
165
|
.text-field-input {
|
|
160
166
|
width: 100%;
|
|
161
167
|
min-width: 0;
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
</script>
|
|
8
8
|
|
|
9
9
|
<script lang="ts">
|
|
10
|
+
import HStack from '../HStack';
|
|
10
11
|
import Pagination from './Pagination.svelte';
|
|
11
12
|
|
|
12
13
|
let data = $state({
|
|
@@ -21,6 +22,6 @@
|
|
|
21
22
|
};
|
|
22
23
|
</script>
|
|
23
24
|
|
|
24
|
-
<
|
|
25
|
+
<HStack>
|
|
25
26
|
<Pagination {data} showFirstLast={true} onPageChange={(page) => onPageChange(page)} />
|
|
26
|
-
</
|
|
27
|
+
</HStack>
|
|
@@ -157,9 +157,8 @@
|
|
|
157
157
|
.pagination {
|
|
158
158
|
--pagination-font-size: calc(var(--sveltely-font-size) * 0.875);
|
|
159
159
|
--pagination-scale: calc(var(--pagination-font-size) / 0.875rem);
|
|
160
|
-
--pagination-icon-size: calc(var(--pagination-font-size) *
|
|
160
|
+
--pagination-icon-size: calc(var(--pagination-font-size) * 0.95);
|
|
161
161
|
--pagination-gap: calc(var(--sveltely-gap) * 2);
|
|
162
|
-
--pagination-icon-shift: calc(var(--sveltely-gap) / 2);
|
|
163
162
|
display: inline-flex;
|
|
164
163
|
width: fit-content;
|
|
165
164
|
gap: var(--pagination-gap);
|
|
@@ -173,18 +172,12 @@
|
|
|
173
172
|
}
|
|
174
173
|
|
|
175
174
|
.pagination-icon {
|
|
175
|
+
display: block;
|
|
176
|
+
flex: 0 0 auto;
|
|
176
177
|
width: var(--pagination-icon-size);
|
|
177
178
|
height: var(--pagination-icon-size);
|
|
178
179
|
}
|
|
179
180
|
|
|
180
|
-
.pagination-icon-backward {
|
|
181
|
-
transform: translateX(calc(var(--pagination-icon-shift) * -1));
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
.pagination-icon-forward {
|
|
185
|
-
transform: translateX(var(--pagination-icon-shift));
|
|
186
|
-
}
|
|
187
|
-
|
|
188
181
|
.pagination-button {
|
|
189
182
|
border-radius: var(--sveltely-border-radius);
|
|
190
183
|
background: var(--sveltely-control-inactive-color);
|
|
@@ -193,14 +186,9 @@
|
|
|
193
186
|
}
|
|
194
187
|
|
|
195
188
|
.pagination-button-icon {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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
|
-
);
|
|
189
|
+
width: calc(var(--pagination-icon-size) + var(--sveltely-padding-x) * var(--pagination-scale));
|
|
190
|
+
height: calc(var(--pagination-icon-size) + var(--sveltely-padding-y) * var(--pagination-scale));
|
|
191
|
+
padding: 0;
|
|
204
192
|
}
|
|
205
193
|
|
|
206
194
|
.pagination-button:disabled {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { Content as PortalContent } from '../Portal';
|
|
3
3
|
import { getOpenPopoverIds, getParentId } from './registry.svelte';
|
|
4
4
|
|
|
5
5
|
type Point = { x: number; y: number };
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
</script>
|
|
97
97
|
|
|
98
98
|
{#if geometries.length > 0}
|
|
99
|
-
<
|
|
99
|
+
<PortalContent class="pointer-events-none fixed inset-0 z-[9999]">
|
|
100
100
|
<svg width="100%" height="100%" aria-hidden="true">
|
|
101
101
|
{#each geometries as geo (geo.id)}
|
|
102
102
|
<!-- Safe polygon (rendered first so rects draw over it) -->
|
|
@@ -159,5 +159,5 @@
|
|
|
159
159
|
</text>
|
|
160
160
|
{/each}
|
|
161
161
|
</svg>
|
|
162
|
-
</
|
|
162
|
+
</PortalContent>
|
|
163
163
|
{/if}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
import { portalContent } from '../../../actions/portal';
|
|
5
|
+
|
|
6
|
+
type Props = {
|
|
7
|
+
id?: string;
|
|
8
|
+
children?: Snippet;
|
|
9
|
+
element?: HTMLElement | null;
|
|
10
|
+
} & Omit<HTMLAttributes<HTMLDivElement>, 'children'>;
|
|
11
|
+
|
|
12
|
+
let { id = 'default', children, element = $bindable<HTMLElement | null>(null), ...props }: Props =
|
|
13
|
+
$props();
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<div bind:this={element} use:portalContent={id} {...props}>
|
|
17
|
+
{#if children}
|
|
18
|
+
{@render children()}
|
|
19
|
+
{/if}
|
|
20
|
+
</div>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
type Props = {
|
|
4
|
+
id?: string;
|
|
5
|
+
children?: Snippet;
|
|
6
|
+
element?: HTMLElement | null;
|
|
7
|
+
} & Omit<HTMLAttributes<HTMLDivElement>, 'children'>;
|
|
8
|
+
declare const Content: import("svelte").Component<Props, {}, "element">;
|
|
9
|
+
type Content = ReturnType<typeof Content>;
|
|
10
|
+
export default Content;
|
|
@@ -48,12 +48,18 @@
|
|
|
48
48
|
};
|
|
49
49
|
|
|
50
50
|
export type ScrollViewOverscrollBehavior = 'auto' | 'contain' | 'none';
|
|
51
|
+
export type ScrollContentSize = {
|
|
52
|
+
width: number;
|
|
53
|
+
height: number;
|
|
54
|
+
};
|
|
51
55
|
|
|
52
56
|
type Props = {
|
|
53
|
-
children
|
|
57
|
+
children?: Snippet;
|
|
58
|
+
layout?: Snippet<[geometry: ScrollGeometry]>;
|
|
54
59
|
viewport?: ScrollViewHandle | null;
|
|
55
60
|
axis?: ScrollAxis;
|
|
56
61
|
contentStyles?: StyleProps;
|
|
62
|
+
contentSize?: ScrollContentSize | null;
|
|
57
63
|
onScroll?: (geometry: ScrollGeometry) => void;
|
|
58
64
|
scrollGradient?: boolean;
|
|
59
65
|
scrollGradientSize?: number | string;
|
|
@@ -65,16 +71,18 @@
|
|
|
65
71
|
|
|
66
72
|
let {
|
|
67
73
|
children,
|
|
74
|
+
layout,
|
|
68
75
|
viewport = $bindable<ScrollViewHandle | null>(null),
|
|
69
76
|
axis = 'vertical',
|
|
70
77
|
contentStyles = {},
|
|
78
|
+
contentSize = null,
|
|
71
79
|
onScroll,
|
|
72
80
|
scrollGradient = true,
|
|
73
81
|
scrollGradientSize = '2.5rem',
|
|
74
82
|
showIndicators = true,
|
|
75
83
|
overscrollBehavior = 'contain',
|
|
76
84
|
...restProps
|
|
77
|
-
}: Props = $props();
|
|
85
|
+
}: Props & Record<string, unknown> = $props();
|
|
78
86
|
|
|
79
87
|
const extractedLoaderProps = $derived.by(() => extractLoaderProps(restProps));
|
|
80
88
|
const loaderProps = $derived(extractedLoaderProps.loaderProps);
|
|
@@ -108,16 +116,26 @@
|
|
|
108
116
|
let thumbYOffset = $state(0);
|
|
109
117
|
let resizeObserver: ResizeObserver | null = null;
|
|
110
118
|
let mutationObserver: MutationObserver | null = null;
|
|
119
|
+
let scrollAnimationFrame: number | null = null;
|
|
111
120
|
const scrollGradientEnabled = $derived(scrollGradient && axis !== 'horizontal');
|
|
112
121
|
const verticalScrollbarEnabled = $derived(showIndicators && axis !== 'horizontal' && canScrollY);
|
|
113
122
|
const horizontalScrollbarEnabled = $derived(showIndicators && axis !== 'vertical' && canScrollX);
|
|
123
|
+
const syntheticContentStyle = $derived.by(() => {
|
|
124
|
+
if (!contentSize) return '';
|
|
125
|
+
return `width: ${contentSize.width}px; height: ${contentSize.height}px; min-width: ${contentSize.width}px; min-height: ${contentSize.height}px;`;
|
|
126
|
+
});
|
|
114
127
|
|
|
115
128
|
const measuredContentSize = () => {
|
|
129
|
+
if (contentSize) return contentSize;
|
|
116
130
|
if (!contentElement) return { width: 0, height: 0 };
|
|
117
131
|
|
|
118
132
|
const contentRect = contentElement.getBoundingClientRect();
|
|
119
133
|
let width = Math.max(contentElement.scrollWidth, contentElement.offsetWidth, contentRect.width);
|
|
120
|
-
let height = Math.max(
|
|
134
|
+
let height = Math.max(
|
|
135
|
+
contentElement.scrollHeight,
|
|
136
|
+
contentElement.offsetHeight,
|
|
137
|
+
contentRect.height
|
|
138
|
+
);
|
|
121
139
|
|
|
122
140
|
for (const child of Array.from(contentElement.children)) {
|
|
123
141
|
const childElement = child as HTMLElement;
|
|
@@ -165,6 +183,7 @@
|
|
|
165
183
|
}
|
|
166
184
|
};
|
|
167
185
|
}
|
|
186
|
+
const scrollGeometry = $derived.by(() => getScrollGeometry());
|
|
168
187
|
|
|
169
188
|
function syncScrollbarGeometry() {
|
|
170
189
|
if (!viewportElement || !contentElement) {
|
|
@@ -220,7 +239,14 @@
|
|
|
220
239
|
onScroll?.(getScrollGeometry());
|
|
221
240
|
}
|
|
222
241
|
|
|
242
|
+
function stopScrollAnimation() {
|
|
243
|
+
if (scrollAnimationFrame === null || typeof cancelAnimationFrame === 'undefined') return;
|
|
244
|
+
cancelAnimationFrame(scrollAnimationFrame);
|
|
245
|
+
scrollAnimationFrame = null;
|
|
246
|
+
}
|
|
247
|
+
|
|
223
248
|
function scrollTo(nextX: number, nextY: number) {
|
|
249
|
+
stopScrollAnimation();
|
|
224
250
|
const previousX = offsetX;
|
|
225
251
|
const previousY = offsetY;
|
|
226
252
|
offsetX = clamp(axis === 'vertical' ? 0 : nextX, 0, maxOffsetX());
|
|
@@ -229,6 +255,41 @@
|
|
|
229
255
|
return offsetX !== previousX || offsetY !== previousY;
|
|
230
256
|
}
|
|
231
257
|
|
|
258
|
+
function animateScrollTo(nextX: number, nextY: number) {
|
|
259
|
+
if (typeof requestAnimationFrame === 'undefined') {
|
|
260
|
+
scrollTo(nextX, nextY);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
stopScrollAnimation();
|
|
265
|
+
const fromX = offsetX;
|
|
266
|
+
const fromY = offsetY;
|
|
267
|
+
const toX = clamp(axis === 'vertical' ? 0 : nextX, 0, maxOffsetX());
|
|
268
|
+
const toY = clamp(axis === 'horizontal' ? 0 : nextY, 0, maxOffsetY());
|
|
269
|
+
const duration = 260;
|
|
270
|
+
const startedAt = performance.now();
|
|
271
|
+
const easeOutCubic = (value: number) => 1 - Math.pow(1 - value, 3);
|
|
272
|
+
|
|
273
|
+
const step = (now: number) => {
|
|
274
|
+
const progress = Math.min(1, (now - startedAt) / duration);
|
|
275
|
+
const eased = easeOutCubic(progress);
|
|
276
|
+
offsetX = fromX + (toX - fromX) * eased;
|
|
277
|
+
offsetY = fromY + (toY - fromY) * eased;
|
|
278
|
+
publishScroll();
|
|
279
|
+
|
|
280
|
+
if (progress < 1) {
|
|
281
|
+
scrollAnimationFrame = requestAnimationFrame(step);
|
|
282
|
+
} else {
|
|
283
|
+
scrollAnimationFrame = null;
|
|
284
|
+
offsetX = toX;
|
|
285
|
+
offsetY = toY;
|
|
286
|
+
publishScroll();
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
scrollAnimationFrame = requestAnimationFrame(step);
|
|
291
|
+
}
|
|
292
|
+
|
|
232
293
|
function shouldContainOverscroll(consumed: boolean) {
|
|
233
294
|
if (overscrollBehavior === 'none') return true;
|
|
234
295
|
return overscrollBehavior === 'contain' && consumed;
|
|
@@ -270,13 +331,20 @@
|
|
|
270
331
|
scrollTo(options, y ?? offsetY);
|
|
271
332
|
return;
|
|
272
333
|
}
|
|
334
|
+
if (options.behavior === 'smooth') {
|
|
335
|
+
animateScrollTo(options.left ?? offsetX, options.top ?? offsetY);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
273
338
|
scrollTo(options.left ?? offsetX, options.top ?? offsetY);
|
|
274
339
|
}
|
|
275
340
|
};
|
|
276
341
|
|
|
277
342
|
function handleWheel(event: WheelEvent) {
|
|
278
|
-
const
|
|
279
|
-
|
|
343
|
+
const shouldMapWheelYToX =
|
|
344
|
+
event.deltaX === 0 && (axis === 'horizontal' || (axis === 'both' && event.shiftKey));
|
|
345
|
+
const wheelX = shouldMapWheelYToX ? event.deltaY : event.deltaX;
|
|
346
|
+
const wheelY = axis === 'both' && event.shiftKey ? 0 : event.deltaY;
|
|
347
|
+
const consumed = scrollTo(offsetX + wheelX, offsetY + wheelY);
|
|
280
348
|
|
|
281
349
|
if (consumed || shouldContainOverscroll(consumed)) {
|
|
282
350
|
event.preventDefault();
|
|
@@ -333,6 +401,7 @@
|
|
|
333
401
|
});
|
|
334
402
|
|
|
335
403
|
return () => {
|
|
404
|
+
stopScrollAnimation();
|
|
336
405
|
resizeObserver?.disconnect();
|
|
337
406
|
resizeObserver = null;
|
|
338
407
|
mutationObserver?.disconnect();
|
|
@@ -363,7 +432,8 @@
|
|
|
363
432
|
const thumbSize = orientation === 'horizontal' ? thumbXSize : thumbYSize;
|
|
364
433
|
const maxScroll = Math.max(0, contentSize - viewportSize);
|
|
365
434
|
const maxThumbOffset = Math.max(1, viewportSize - thumbSize);
|
|
366
|
-
const delta =
|
|
435
|
+
const delta =
|
|
436
|
+
(orientation === 'horizontal' ? moveEvent.clientX : moveEvent.clientY) - startPointer;
|
|
367
437
|
const nextScroll = startScroll + (delta / maxThumbOffset) * maxScroll;
|
|
368
438
|
|
|
369
439
|
if (orientation === 'horizontal') {
|
|
@@ -383,7 +453,6 @@
|
|
|
383
453
|
window.addEventListener('pointerup', up);
|
|
384
454
|
window.addEventListener('pointercancel', up);
|
|
385
455
|
}
|
|
386
|
-
|
|
387
456
|
</script>
|
|
388
457
|
|
|
389
458
|
<div
|
|
@@ -414,9 +483,14 @@
|
|
|
414
483
|
<div
|
|
415
484
|
bind:this={contentElement}
|
|
416
485
|
class="scroll-view-content"
|
|
417
|
-
|
|
486
|
+
class:scroll-view-content-engine={Boolean(layout)}
|
|
487
|
+
style={`${contentStyle} ${syntheticContentStyle} transform: translate3d(${-offsetX}px, ${-offsetY}px, 0);`}
|
|
418
488
|
>
|
|
419
|
-
{
|
|
489
|
+
{#if layout}
|
|
490
|
+
{@render layout(scrollGeometry)}
|
|
491
|
+
{:else if children}
|
|
492
|
+
{@render children()}
|
|
493
|
+
{/if}
|
|
420
494
|
</div>
|
|
421
495
|
</div>
|
|
422
496
|
{#if scrollGradientEnabled}
|
|
@@ -481,6 +555,11 @@
|
|
|
481
555
|
will-change: transform;
|
|
482
556
|
}
|
|
483
557
|
|
|
558
|
+
.scroll-view-content-engine {
|
|
559
|
+
position: relative;
|
|
560
|
+
padding: 0;
|
|
561
|
+
}
|
|
562
|
+
|
|
484
563
|
.scroll-view-horizontal .scroll-view-content {
|
|
485
564
|
min-height: 100%;
|
|
486
565
|
width: max-content;
|
|
@@ -42,17 +42,24 @@ export type ScrollViewHandle = {
|
|
|
42
42
|
scrollTo: (options: ScrollToOptions | number, y?: number) => void;
|
|
43
43
|
};
|
|
44
44
|
export type ScrollViewOverscrollBehavior = 'auto' | 'contain' | 'none';
|
|
45
|
+
export type ScrollContentSize = {
|
|
46
|
+
width: number;
|
|
47
|
+
height: number;
|
|
48
|
+
};
|
|
45
49
|
type Props = {
|
|
46
|
-
children
|
|
50
|
+
children?: Snippet;
|
|
51
|
+
layout?: Snippet<[geometry: ScrollGeometry]>;
|
|
47
52
|
viewport?: ScrollViewHandle | null;
|
|
48
53
|
axis?: ScrollAxis;
|
|
49
54
|
contentStyles?: StyleProps;
|
|
55
|
+
contentSize?: ScrollContentSize | null;
|
|
50
56
|
onScroll?: (geometry: ScrollGeometry) => void;
|
|
51
57
|
scrollGradient?: boolean;
|
|
52
58
|
scrollGradientSize?: number | string;
|
|
53
59
|
showIndicators?: boolean;
|
|
54
60
|
overscrollBehavior?: ScrollViewOverscrollBehavior;
|
|
55
61
|
} & LayoutProps & StyleProps & LoaderProps;
|
|
56
|
-
|
|
62
|
+
type $$ComponentProps = Props & Record<string, unknown>;
|
|
63
|
+
declare const ScrollView: import("svelte").Component<$$ComponentProps, {}, "viewport">;
|
|
57
64
|
type ScrollView = ReturnType<typeof ScrollView>;
|
|
58
65
|
export default ScrollView;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { default } from './ScrollView.svelte';
|
|
2
|
-
export type { ScrollGeometry, ScrollViewHandle, ScrollViewOverscrollBehavior } from './ScrollView.svelte';
|
|
2
|
+
export type { ScrollGeometry, ScrollContentSize, ScrollViewHandle, ScrollViewOverscrollBehavior } from './ScrollView.svelte';
|
|
@@ -6,12 +6,13 @@
|
|
|
6
6
|
</script>
|
|
7
7
|
|
|
8
8
|
<script lang="ts">
|
|
9
|
+
import VStack from '../VStack';
|
|
9
10
|
import SearchField from './SearchField.svelte';
|
|
10
11
|
|
|
11
12
|
let value = $state('');
|
|
12
13
|
</script>
|
|
13
14
|
|
|
14
|
-
<
|
|
15
|
+
<VStack width="24rem" gap={5}>
|
|
15
16
|
<SearchField bind:value placeholder="Search components..." />
|
|
16
17
|
<p class="text-xs text-[var(--sveltely-text-secondary-color)]">Value: {value || 'empty'}</p>
|
|
17
|
-
</
|
|
18
|
+
</VStack>
|