@dryui/ui 0.1.2 → 0.1.3
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/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/combobox/combobox-group.svelte +1 -1
- package/dist/date-range-picker/date-range-picker-root.svelte +0 -6
- package/dist/file-upload/file-upload-root.svelte +0 -4
- 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/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 +6 -6
- 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/shader-canvas/shader-canvas.svelte +0 -3
- package/dist/sidebar/sidebar-trigger.svelte +3 -2
- package/dist/tabs/tabs-trigger.svelte +7 -4
- package/dist/virtual-list/virtual-list.svelte +187 -3
- package/package.json +2 -2
|
@@ -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>
|
|
@@ -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.3",
|
|
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.3"
|
|
1506
1506
|
},
|
|
1507
1507
|
"peerDependencies": {
|
|
1508
1508
|
"svelte": "^5.55.1"
|