@dryui/ui 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.
- package/dist/accordion/accordion-trigger.svelte +5 -6
- package/dist/alert-dialog/alert-dialog-action.svelte +42 -6
- package/dist/alert-dialog/alert-dialog-cancel.svelte +44 -5
- package/dist/alert-dialog/alert-dialog-footer.svelte +3 -2
- package/dist/aurora/aurora.svelte.d.ts +6 -0
- package/dist/beam/beam.svelte +17 -10
- package/dist/button/button.svelte +51 -1
- package/dist/button-group/button-group.svelte +7 -62
- package/dist/button-group/context.svelte.d.ts +5 -0
- package/dist/button-group/context.svelte.js +11 -0
- package/dist/chromatic-aberration/chromatic-aberration.svelte +60 -18
- package/dist/collapsible/collapsible-trigger.svelte +4 -7
- package/dist/color-picker/color-picker-eyedropper.svelte +4 -11
- package/dist/combobox/combobox-group.svelte +1 -1
- package/dist/data-grid/data-grid-pagination.svelte +20 -2
- package/dist/data-grid/data-grid-root.svelte +1 -0
- package/dist/date-field/date-field-root.svelte +66 -20
- package/dist/date-field/date-field-segment.svelte +11 -9
- package/dist/date-field/date-field-separator.svelte +9 -1
- package/dist/date-picker/datepicker-calendar.svelte +168 -13
- package/dist/date-picker/datepicker-trigger.svelte +3 -8
- package/dist/date-range-picker/date-range-picker-calendar.svelte +177 -13
- package/dist/date-range-picker/date-range-picker-root.svelte +0 -6
- package/dist/date-range-picker/date-range-picker-trigger.svelte +18 -12
- package/dist/dialog/dialog-content.svelte +1 -0
- package/dist/field/field-root.svelte +0 -1
- package/dist/file-select/file-select-clear.svelte +2 -8
- package/dist/file-upload/file-upload-item-delete.svelte +4 -7
- package/dist/file-upload/file-upload-root.svelte +0 -4
- package/dist/flip-card/flip-card-back.svelte +2 -2
- package/dist/flip-card/flip-card-front.svelte +2 -2
- package/dist/flip-card/flip-card-root.svelte +2 -0
- package/dist/float-button/float-button-root.svelte +2 -1
- package/dist/image-comparison/image-comparison.svelte +16 -24
- package/dist/input-group/input-group-action.svelte +5 -0
- package/dist/input-group/input-group-input.svelte +7 -2
- package/dist/input-group/input-group-prefix.svelte +5 -0
- package/dist/input-group/input-group-root.svelte +10 -2
- package/dist/input-group/input-group-select.svelte +5 -0
- package/dist/input-group/input-group-separator.svelte +10 -0
- package/dist/input-group/input-group-suffix.svelte +5 -0
- package/dist/list/list-item-icon.svelte +8 -0
- package/dist/list/list-item-text.svelte +19 -0
- package/dist/list/list-item.svelte +42 -0
- package/dist/list/list-root.svelte +0 -71
- package/dist/list/list-subheader.svelte +11 -0
- package/dist/map/map-marker.svelte +10 -0
- package/dist/map/map-popup.svelte +7 -0
- package/dist/map/map-root.svelte +0 -30
- package/dist/multi-select-combobox/multi-select-combobox-group.svelte +1 -1
- package/dist/option-swatch-group/option-swatch-group-item.svelte +46 -0
- package/dist/option-swatch-group/option-swatch-group-label.svelte +10 -0
- package/dist/option-swatch-group/option-swatch-group-meta.svelte +10 -0
- package/dist/option-swatch-group/option-swatch-group-root.svelte +0 -79
- package/dist/option-swatch-group/option-swatch-group-swatch.svelte +25 -6
- package/dist/pin-input/pin-input-cell.svelte +4 -1
- package/dist/radio-group/radio-group-item.svelte +90 -0
- package/dist/radio-group/radio-group.svelte +0 -89
- package/dist/range-calendar/range-calendar-grid.svelte +217 -179
- package/dist/range-calendar/range-calendar-root.svelte +24 -10
- package/dist/rich-text-editor/rich-text-editor-content.svelte +91 -3
- package/dist/rich-text-editor/rich-text-editor-root.svelte +168 -3
- package/dist/rich-text-editor/rich-text-editor-toolbar.svelte +318 -275
- package/dist/select/select-trigger.svelte +5 -8
- package/dist/shader-canvas/shader-canvas.svelte +0 -3
- package/dist/sidebar/sidebar-trigger.svelte +3 -2
- package/dist/system-map/system-map.svelte +120 -674
- package/dist/tabs/tabs-trigger.svelte +7 -4
- package/dist/tags-input/tags-input-input.svelte +3 -0
- package/dist/tags-input/tags-input-root.svelte +4 -13
- package/dist/tags-input/tags-input-tag.svelte +3 -0
- package/dist/themes/dark.css +6 -0
- package/dist/themes/default.css +3 -0
- package/dist/toast/toast-action.svelte +1 -0
- package/dist/toast/toast-close.svelte +4 -0
- package/dist/toast/toast-provider.svelte +5 -26
- package/dist/toast/toast-root.svelte +5 -10
- package/dist/virtual-list/virtual-list.svelte +187 -3
- package/package.json +3 -3
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
}}
|
|
46
46
|
{...rest}
|
|
47
47
|
>
|
|
48
|
-
{@render children()}
|
|
48
|
+
<span data-tabs-trigger-content>{@render children()}</span>
|
|
49
49
|
</button>
|
|
50
50
|
|
|
51
51
|
<style>
|
|
@@ -118,8 +118,11 @@
|
|
|
118
118
|
--dry-tabs-font-size: var(--dry-type-heading-4-size, var(--dry-text-base-size));
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
[data-tabs-trigger]
|
|
122
|
-
|
|
123
|
-
|
|
121
|
+
[data-tabs-trigger-content] {
|
|
122
|
+
display: inline-grid;
|
|
123
|
+
grid-auto-flow: column;
|
|
124
|
+
align-items: center;
|
|
125
|
+
gap: var(--dry-space-1_5);
|
|
124
126
|
}
|
|
127
|
+
|
|
125
128
|
</style>
|
|
@@ -62,6 +62,8 @@
|
|
|
62
62
|
|
|
63
63
|
<style>
|
|
64
64
|
[data-part='input'] {
|
|
65
|
+
display: inline-grid;
|
|
66
|
+
vertical-align: middle;
|
|
65
67
|
border: none;
|
|
66
68
|
outline: none;
|
|
67
69
|
background: transparent;
|
|
@@ -69,6 +71,7 @@
|
|
|
69
71
|
font-size: var(--dry-tags-input-font-size, var(--dry-type-small-size, var(--dry-text-sm-size)));
|
|
70
72
|
color: var(--dry-color-text-strong);
|
|
71
73
|
padding: 0;
|
|
74
|
+
margin-block: calc(var(--dry-tags-input-gap, var(--dry-space-1_5)) / 2);
|
|
72
75
|
line-height: var(--dry-type-small-leading, var(--dry-text-sm-leading));
|
|
73
76
|
|
|
74
77
|
&::placeholder {
|
|
@@ -84,15 +84,12 @@
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
[data-part='root'] {
|
|
87
|
-
display:
|
|
88
|
-
grid-template-columns: repeat(auto-fill, minmax(min-content, max-content));
|
|
89
|
-
gap: var(--dry-space-1_5);
|
|
87
|
+
display: block;
|
|
90
88
|
padding: var(--dry-space-2);
|
|
91
89
|
border: 1px solid var(--dry-color-stroke-strong);
|
|
92
90
|
border-radius: var(--dry-radius-md);
|
|
93
91
|
background: var(--dry-color-bg-raised);
|
|
94
92
|
min-height: 44px;
|
|
95
|
-
align-items: center;
|
|
96
93
|
box-sizing: border-box;
|
|
97
94
|
transition:
|
|
98
95
|
border-color var(--dry-duration-fast) var(--dry-ease-default),
|
|
@@ -123,7 +120,7 @@
|
|
|
123
120
|
[data-size='sm'] {
|
|
124
121
|
padding: var(--dry-space-1) var(--dry-space-1_5);
|
|
125
122
|
min-height: 32px;
|
|
126
|
-
gap: var(--dry-space-1);
|
|
123
|
+
--dry-tags-input-gap: var(--dry-space-1);
|
|
127
124
|
--dry-tags-input-tag-padding-x: var(--dry-space-1_5);
|
|
128
125
|
--dry-tags-input-tag-font-size: var(--dry-type-tiny-size, var(--dry-text-xs-size));
|
|
129
126
|
--dry-tags-input-font-size: var(--dry-type-tiny-size, var(--dry-text-xs-size));
|
|
@@ -132,21 +129,15 @@
|
|
|
132
129
|
[data-size='md'] {
|
|
133
130
|
padding: var(--dry-space-2);
|
|
134
131
|
min-height: 44px;
|
|
135
|
-
gap: var(--dry-space-1_5);
|
|
132
|
+
--dry-tags-input-gap: var(--dry-space-1_5);
|
|
136
133
|
}
|
|
137
134
|
|
|
138
135
|
[data-size='lg'] {
|
|
139
136
|
padding: var(--dry-space-2_5);
|
|
140
137
|
min-height: 52px;
|
|
141
|
-
gap: var(--dry-space-2);
|
|
138
|
+
--dry-tags-input-gap: var(--dry-space-2);
|
|
142
139
|
--dry-tags-input-tag-padding-x: var(--dry-space-2_5);
|
|
143
140
|
--dry-tags-input-tag-font-size: var(--dry-type-small-size, var(--dry-text-sm-size));
|
|
144
141
|
--dry-tags-input-font-size: var(--dry-type-small-size, var(--dry-text-base-size));
|
|
145
142
|
}
|
|
146
|
-
|
|
147
|
-
@container (max-width: 200px) {
|
|
148
|
-
[data-part='root'] {
|
|
149
|
-
grid-template-columns: 1fr;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
143
|
</style>
|
|
@@ -33,9 +33,12 @@
|
|
|
33
33
|
display: inline-grid;
|
|
34
34
|
grid-auto-flow: column;
|
|
35
35
|
grid-auto-columns: max-content;
|
|
36
|
+
vertical-align: middle;
|
|
36
37
|
align-items: center;
|
|
37
38
|
gap: var(--dry-space-1);
|
|
38
39
|
padding: var(--dry-space-1) var(--dry-tags-input-tag-padding-x, var(--dry-space-2));
|
|
40
|
+
margin-inline-end: var(--dry-tags-input-gap, var(--dry-space-1_5));
|
|
41
|
+
margin-block: calc(var(--dry-tags-input-gap, var(--dry-space-1_5)) / 2);
|
|
39
42
|
background: color-mix(in srgb, var(--dry-color-fill-brand) 15%, transparent);
|
|
40
43
|
color: var(--dry-color-fill-brand);
|
|
41
44
|
border-radius: var(--dry-radius-full);
|
package/dist/themes/dark.css
CHANGED
|
@@ -79,6 +79,9 @@
|
|
|
79
79
|
--dry-toggle-label-color: var(--dry-color-text-strong);
|
|
80
80
|
--dry-toggle-label-disabled-color: #ffffff1f;
|
|
81
81
|
|
|
82
|
+
/* ─── Component: Beam ─────────────────────────────────────────── */
|
|
83
|
+
--dry-beam-default-blend: screen;
|
|
84
|
+
|
|
82
85
|
/* ─── Status: Error ───────────────────────────────────────────── */
|
|
83
86
|
--dry-color-text-error: hsl(0, 60%, 70%);
|
|
84
87
|
--dry-color-fill-error: hsl(0, 65%, 55%);
|
|
@@ -220,6 +223,9 @@
|
|
|
220
223
|
--dry-toggle-label-color: var(--dry-color-text-strong);
|
|
221
224
|
--dry-toggle-label-disabled-color: #ffffff1f;
|
|
222
225
|
|
|
226
|
+
/* ─── Component: Beam ─────────────────────────────────────────── */
|
|
227
|
+
--dry-beam-default-blend: screen;
|
|
228
|
+
|
|
223
229
|
/* ─── Status: Error ───────────────────────────────────────────── */
|
|
224
230
|
--dry-color-text-error: hsl(0, 60%, 70%);
|
|
225
231
|
--dry-color-fill-error: hsl(0, 65%, 55%);
|
package/dist/themes/default.css
CHANGED
|
@@ -87,6 +87,9 @@ code, pre, kbd, samp {
|
|
|
87
87
|
--dry-toggle-label-color: rgba(0, 6, 38, 0.9);
|
|
88
88
|
--dry-toggle-label-disabled-color: rgba(0, 17, 102, 0.1);
|
|
89
89
|
|
|
90
|
+
/* ─── Component: Beam ─────────────────────────────────────────── */
|
|
91
|
+
--dry-beam-default-blend: multiply;
|
|
92
|
+
|
|
90
93
|
/* ─── Status: Error (hue 0) ───────────────────────────────────── */
|
|
91
94
|
--dry-color-text-error: hsl(0, 70%, 35%);
|
|
92
95
|
--dry-color-fill-error: hsl(0, 70%, 50%);
|
|
@@ -19,31 +19,11 @@
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
let { class: className, position = 'bottom-right', children, ...rest }: Props = $props();
|
|
22
|
-
|
|
23
|
-
let popoverEl = $state<HTMLDivElement>();
|
|
24
|
-
|
|
25
|
-
$effect(() => {
|
|
26
|
-
if (!popoverEl) return;
|
|
27
|
-
|
|
28
|
-
if (toastStore.toasts.length > 0) {
|
|
29
|
-
try {
|
|
30
|
-
popoverEl.showPopover();
|
|
31
|
-
} catch {
|
|
32
|
-
// Already showing
|
|
33
|
-
}
|
|
34
|
-
} else {
|
|
35
|
-
try {
|
|
36
|
-
popoverEl.hidePopover();
|
|
37
|
-
} catch {
|
|
38
|
-
// Already hidden
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
});
|
|
22
|
+
const visible = $derived(Boolean(children) || toastStore.toasts.length > 0);
|
|
42
23
|
</script>
|
|
43
24
|
|
|
44
25
|
<div
|
|
45
|
-
|
|
46
|
-
popover="manual"
|
|
26
|
+
hidden={!visible}
|
|
47
27
|
role="region"
|
|
48
28
|
aria-label="Notifications"
|
|
49
29
|
data-part="provider"
|
|
@@ -82,6 +62,8 @@
|
|
|
82
62
|
|
|
83
63
|
<style>
|
|
84
64
|
[data-part='provider'] {
|
|
65
|
+
position: fixed;
|
|
66
|
+
z-index: var(--dry-layer-overlay);
|
|
85
67
|
display: grid;
|
|
86
68
|
grid-template-columns: minmax(0, min(420px, calc(100vw - var(--dry-space-8))));
|
|
87
69
|
gap: var(--dry-space-3);
|
|
@@ -92,10 +74,6 @@
|
|
|
92
74
|
overflow: visible;
|
|
93
75
|
container-type: inline-size;
|
|
94
76
|
|
|
95
|
-
&:not(:popover-open) {
|
|
96
|
-
display: none;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
77
|
&[data-position='top-right'] {
|
|
100
78
|
inset: var(--dry-space-4) var(--dry-space-4) auto auto;
|
|
101
79
|
}
|
|
@@ -125,6 +103,7 @@
|
|
|
125
103
|
|
|
126
104
|
[data-part='content'] {
|
|
127
105
|
display: grid;
|
|
106
|
+
grid-template-columns: minmax(0, 1fr);
|
|
128
107
|
gap: var(--dry-space-1);
|
|
129
108
|
}
|
|
130
109
|
</style>
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
|
|
36
36
|
function applyProgressStyles(node: HTMLElement) {
|
|
37
37
|
$effect(() => {
|
|
38
|
-
node.style.
|
|
38
|
+
node.style.cssText = `--progress-width: ${progress}%`;
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
41
|
</script>
|
|
@@ -67,11 +67,11 @@
|
|
|
67
67
|
border-radius: var(--dry-radius-lg);
|
|
68
68
|
box-shadow: var(--dry-shadow-lg);
|
|
69
69
|
padding: var(--dry-space-4);
|
|
70
|
+
padding-right: calc(var(--dry-space-4) + var(--dry-space-12) + var(--dry-space-2));
|
|
70
71
|
display: grid;
|
|
71
|
-
grid-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
gap: var(--dry-space-3);
|
|
72
|
+
grid-template-columns: minmax(0, 1fr);
|
|
73
|
+
align-items: start;
|
|
74
|
+
gap: var(--dry-space-2);
|
|
75
75
|
animation: slideIn var(--dry-duration-normal) var(--dry-ease-out);
|
|
76
76
|
|
|
77
77
|
&[data-variant='info'] {
|
|
@@ -101,11 +101,6 @@
|
|
|
101
101
|
--dry-toast-border: var(--dry-color-stroke-error);
|
|
102
102
|
--dry-toast-action-hover-bg: var(--dry-color-fill-error-weak);
|
|
103
103
|
}
|
|
104
|
-
|
|
105
|
-
@container (max-width: 300px) {
|
|
106
|
-
grid-auto-flow: row;
|
|
107
|
-
grid-auto-columns: initial;
|
|
108
|
-
}
|
|
109
104
|
}
|
|
110
105
|
|
|
111
106
|
@keyframes slideIn {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<script lang="ts" generics="T">
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
-
import { VirtualList as VirtualListPrimitive } from '@dryui/primitives/virtual-list';
|
|
5
4
|
|
|
6
5
|
interface Props extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
7
6
|
items: T[];
|
|
@@ -10,13 +9,198 @@
|
|
|
10
9
|
children: Snippet<[{ item: T; index: number; style: string }]>;
|
|
11
10
|
}
|
|
12
11
|
|
|
13
|
-
let {
|
|
12
|
+
let {
|
|
13
|
+
items,
|
|
14
|
+
itemHeight,
|
|
15
|
+
overscan = 5,
|
|
16
|
+
children,
|
|
17
|
+
class: className,
|
|
18
|
+
style,
|
|
19
|
+
...rest
|
|
20
|
+
}: Props = $props();
|
|
21
|
+
|
|
22
|
+
let container: HTMLDivElement | undefined = $state(undefined);
|
|
23
|
+
let scrollTop = $state(0);
|
|
24
|
+
let containerHeight = $state(0);
|
|
25
|
+
|
|
26
|
+
// Detect mode: fixed (number) vs variable (function)
|
|
27
|
+
let isFixed = $derived(typeof itemHeight === 'number');
|
|
28
|
+
|
|
29
|
+
// ─── Fixed-height mode helpers ──────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
let fixedHeight = $derived(isFixed ? (itemHeight as number) : 0);
|
|
32
|
+
|
|
33
|
+
let fixedTotalHeight = $derived(isFixed ? items.length * fixedHeight : 0);
|
|
34
|
+
|
|
35
|
+
let fixedStartIndex = $derived.by(() => {
|
|
36
|
+
if (!isFixed) return 0;
|
|
37
|
+
return Math.max(0, Math.floor(scrollTop / fixedHeight) - overscan);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
let fixedEndIndex = $derived.by(() => {
|
|
41
|
+
if (!isFixed) return 0;
|
|
42
|
+
const visible = Math.ceil(containerHeight / fixedHeight);
|
|
43
|
+
return Math.min(items.length, Math.floor(scrollTop / fixedHeight) + visible + overscan);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
let fixedVisibleItems = $derived.by(() => {
|
|
47
|
+
if (!isFixed) return [];
|
|
48
|
+
const result: Array<{ item: T; index: number; style: string }> = [];
|
|
49
|
+
for (let i = fixedStartIndex; i < fixedEndIndex; i++) {
|
|
50
|
+
result.push({
|
|
51
|
+
item: items[i]!,
|
|
52
|
+
index: i,
|
|
53
|
+
style: `position:absolute;top:${i * fixedHeight}px;width:100%;height:${fixedHeight}px;`
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return result;
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// ─── Variable-height mode helpers ───────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
// Pre-compute offsets from the height function
|
|
62
|
+
let variableOffsets = $derived.by(() => {
|
|
63
|
+
if (isFixed) return [];
|
|
64
|
+
const heightFn = itemHeight as (index: number) => number;
|
|
65
|
+
const offsets: number[] = new Array(items.length);
|
|
66
|
+
let cumulative = 0;
|
|
67
|
+
for (let i = 0; i < items.length; i++) {
|
|
68
|
+
offsets[i] = cumulative;
|
|
69
|
+
cumulative += heightFn(i);
|
|
70
|
+
}
|
|
71
|
+
return offsets;
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
let variableTotalHeight = $derived.by(() => {
|
|
75
|
+
if (isFixed || items.length === 0) return 0;
|
|
76
|
+
const heightFn = itemHeight as (index: number) => number;
|
|
77
|
+
const lastIdx = items.length - 1;
|
|
78
|
+
return variableOffsets[lastIdx]! + heightFn(lastIdx);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// Binary search to find the first item whose bottom edge is past scrollTop
|
|
82
|
+
function binarySearchStart(offsets: number[], target: number): number {
|
|
83
|
+
let lo = 0;
|
|
84
|
+
let hi = offsets.length - 1;
|
|
85
|
+
while (lo < hi) {
|
|
86
|
+
const mid = (lo + hi) >>> 1;
|
|
87
|
+
if (offsets[mid]! < target) {
|
|
88
|
+
lo = mid + 1;
|
|
89
|
+
} else {
|
|
90
|
+
hi = mid;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return lo;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let variableStartIndex = $derived.by(() => {
|
|
97
|
+
if (isFixed || items.length === 0) return 0;
|
|
98
|
+
const raw = binarySearchStart(variableOffsets, scrollTop);
|
|
99
|
+
// Step back to find the item that actually contains scrollTop
|
|
100
|
+
const idx = raw > 0 ? raw - 1 : 0;
|
|
101
|
+
return Math.max(0, idx - overscan);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
let variableEndIndex = $derived.by(() => {
|
|
105
|
+
if (isFixed || items.length === 0) return 0;
|
|
106
|
+
const bottom = scrollTop + containerHeight;
|
|
107
|
+
const raw = binarySearchStart(variableOffsets, bottom);
|
|
108
|
+
return Math.min(items.length, raw + 1 + overscan);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
let variableVisibleItems = $derived.by(() => {
|
|
112
|
+
if (isFixed) return [];
|
|
113
|
+
const heightFn = itemHeight as (index: number) => number;
|
|
114
|
+
const result: Array<{ item: T; index: number; style: string }> = [];
|
|
115
|
+
for (let i = variableStartIndex; i < variableEndIndex; i++) {
|
|
116
|
+
const h = heightFn(i);
|
|
117
|
+
result.push({
|
|
118
|
+
item: items[i]!,
|
|
119
|
+
index: i,
|
|
120
|
+
style: `position:absolute;top:${variableOffsets[i]}px;width:100%;height:${h}px;`
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
return result;
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// ─── Combined derivations ──────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
let totalHeight = $derived(isFixed ? fixedTotalHeight : variableTotalHeight);
|
|
129
|
+
let visibleItems = $derived(isFixed ? fixedVisibleItems : variableVisibleItems);
|
|
130
|
+
|
|
131
|
+
// ─── Scroll handling with RAF throttle ──────────────────────────────────────
|
|
132
|
+
|
|
133
|
+
let rafId = 0;
|
|
134
|
+
|
|
135
|
+
function handleScroll() {
|
|
136
|
+
if (rafId) return;
|
|
137
|
+
rafId = requestAnimationFrame(() => {
|
|
138
|
+
rafId = 0;
|
|
139
|
+
if (container) {
|
|
140
|
+
scrollTop = container.scrollTop;
|
|
141
|
+
containerHeight = container.clientHeight;
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function applyContainerStyles(node: HTMLElement) {
|
|
147
|
+
$effect(() => {
|
|
148
|
+
node.style.cssText = style || '';
|
|
149
|
+
node.style.setProperty('position', 'relative');
|
|
150
|
+
node.style.setProperty('overflow-y', 'auto');
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function applyInnerStyles(node: HTMLElement) {
|
|
155
|
+
$effect(() => {
|
|
156
|
+
node.style.setProperty('height', `${totalHeight}px`);
|
|
157
|
+
node.style.setProperty('position', 'relative');
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Measure container on mount and when it resizes
|
|
162
|
+
$effect(() => {
|
|
163
|
+
if (!container) return;
|
|
164
|
+
|
|
165
|
+
containerHeight = container.clientHeight;
|
|
166
|
+
scrollTop = container.scrollTop;
|
|
167
|
+
|
|
168
|
+
const ro = new ResizeObserver(() => {
|
|
169
|
+
if (container) {
|
|
170
|
+
containerHeight = container.clientHeight;
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
ro.observe(container);
|
|
174
|
+
|
|
175
|
+
return () => {
|
|
176
|
+
ro.disconnect();
|
|
177
|
+
if (rafId) {
|
|
178
|
+
cancelAnimationFrame(rafId);
|
|
179
|
+
rafId = 0;
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
});
|
|
14
183
|
</script>
|
|
15
184
|
|
|
16
|
-
<
|
|
185
|
+
<div
|
|
186
|
+
bind:this={container}
|
|
187
|
+
class={className}
|
|
188
|
+
use:applyContainerStyles
|
|
189
|
+
onscroll={handleScroll}
|
|
190
|
+
role="list"
|
|
191
|
+
data-virtual-list
|
|
192
|
+
{...rest}
|
|
193
|
+
>
|
|
194
|
+
<div use:applyInnerStyles>
|
|
195
|
+
{#each visibleItems as entry (entry.index)}
|
|
196
|
+
{@render children(entry)}
|
|
197
|
+
{/each}
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
17
200
|
|
|
18
201
|
<style>
|
|
19
202
|
[data-virtual-list] {
|
|
203
|
+
height: 100%;
|
|
20
204
|
--dry-virtual-list-scrollbar-width: 8px;
|
|
21
205
|
--dry-virtual-list-scrollbar-track: var(--dry-color-bg-raised, transparent);
|
|
22
206
|
--dry-virtual-list-scrollbar-thumb: color-mix(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dryui/ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"author": "Rob Balfre",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -1502,7 +1502,7 @@
|
|
|
1502
1502
|
"thumbnail:create": "bun scripts/create-thumbnail.ts"
|
|
1503
1503
|
},
|
|
1504
1504
|
"dependencies": {
|
|
1505
|
-
"@dryui/primitives": "^0.1.
|
|
1505
|
+
"@dryui/primitives": "^0.1.4"
|
|
1506
1506
|
},
|
|
1507
1507
|
"peerDependencies": {
|
|
1508
1508
|
"svelte": "^5.55.1"
|
|
@@ -1510,7 +1510,7 @@
|
|
|
1510
1510
|
"devDependencies": {
|
|
1511
1511
|
"svelte": "^5.55.1",
|
|
1512
1512
|
"@sveltejs/package": "^2.5.7",
|
|
1513
|
-
"svelte-check": "^4.4.
|
|
1513
|
+
"svelte-check": "^4.4.6",
|
|
1514
1514
|
"typescript": "^6.0.2"
|
|
1515
1515
|
}
|
|
1516
1516
|
}
|