@lyeve-labs/ui-kit 0.44.0 → 0.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/AccountMenu.svelte +3 -0
- package/dist/components/AppShell.svelte +33 -5
- package/dist/components/AppShell.svelte.d.ts +8 -0
- package/dist/components/Pagination.svelte +9 -22
- package/dist/components/PluginCart.svelte +11 -3
- package/dist/components/PluginCart.svelte.d.ts +8 -0
- package/dist/components/Table.svelte +1 -0
- package/dist/components/Tabs.svelte +74 -9
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/internal/scroll-edges.d.ts +9 -8
- package/dist/internal/scroll-edges.js +9 -8
- package/package.json +1 -1
|
@@ -94,7 +94,10 @@
|
|
|
94
94
|
</script>
|
|
95
95
|
|
|
96
96
|
<details bind:this={root} class="group relative {klass}">
|
|
97
|
+
<!-- The name beside the avatar is hidden below sm, and the avatar is paint,
|
|
98
|
+
so the trigger carries its name itself. -->
|
|
97
99
|
<summary
|
|
100
|
+
aria-label="{name}, account menu"
|
|
98
101
|
class="{TOUCH_GROW} flex cursor-pointer list-none items-center gap-2 rounded-lg px-2 py-1.5 outline-none transition-colors hover:bg-surface-2 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-brand"
|
|
99
102
|
>
|
|
100
103
|
<span
|
|
@@ -9,6 +9,15 @@
|
|
|
9
9
|
export interface SidebarState {
|
|
10
10
|
rail: boolean;
|
|
11
11
|
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* How long a pointer has to rest over the rail before it opens. This waits
|
|
15
|
+
* for the reader, not for an animation. A pointer crossing the 56px rail on
|
|
16
|
+
* its way to the page is still moving and never rests there, so the column
|
|
17
|
+
* does not open over the control it is heading for. One that stops on the
|
|
18
|
+
* rail means to read it.
|
|
19
|
+
*/
|
|
20
|
+
export const RAIL_REST_MS = 100;
|
|
12
21
|
</script>
|
|
13
22
|
|
|
14
23
|
<script lang="ts">
|
|
@@ -161,15 +170,31 @@
|
|
|
161
170
|
* The rail: the icon column, and the labels on demand.
|
|
162
171
|
*
|
|
163
172
|
* While the sidebar is the rail it is 56px wide and the nav is told so. A
|
|
164
|
-
* pointer over it, or focus inside it, opens it to the full column
|
|
165
|
-
* page rather than beside it, so the page keeps its width and the
|
|
166
|
-
* still gets every label. It closes when the pointer or the focus
|
|
173
|
+
* pointer at rest over it, or focus inside it, opens it to the full column
|
|
174
|
+
* over the page rather than beside it, so the page keeps its width and the
|
|
175
|
+
* reader still gets every label. It closes when the pointer or the focus
|
|
176
|
+
* leaves. A pointer that only passes over it leaves it the rail: the open
|
|
177
|
+
* column covers the start of the page, and opening under a pointer bound
|
|
178
|
+
* for a control there held the column open over that control.
|
|
167
179
|
* State rather than a hover rule, because the nav snippet has to be told
|
|
168
180
|
* the labels are wanted, and a stylesheet cannot tell it.
|
|
169
181
|
*/
|
|
170
182
|
const railMode = $derived(!isMobile && !railHidden && (rail || isNarrow));
|
|
171
183
|
let railOpen = $state(false);
|
|
172
184
|
const railIcons = $derived(railMode && !railOpen);
|
|
185
|
+
let railRest: ReturnType<typeof setTimeout> | undefined;
|
|
186
|
+
|
|
187
|
+
/** Every move starts the rest again, so only a pointer that stops opens the column. */
|
|
188
|
+
function onRailPointerMove() {
|
|
189
|
+
if (railOpen) return;
|
|
190
|
+
clearTimeout(railRest);
|
|
191
|
+
railRest = setTimeout(() => (railOpen = true), RAIL_REST_MS);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function onRailPointerLeave() {
|
|
195
|
+
clearTimeout(railRest);
|
|
196
|
+
railOpen = false;
|
|
197
|
+
}
|
|
173
198
|
|
|
174
199
|
$effect(() => {
|
|
175
200
|
const mobile = window.matchMedia('(max-width: 767px)');
|
|
@@ -178,12 +203,14 @@
|
|
|
178
203
|
isMobile = mobile.matches;
|
|
179
204
|
isNarrow = narrow.matches && !mobile.matches;
|
|
180
205
|
if (!mobile.matches) navOpen = false;
|
|
206
|
+
clearTimeout(railRest);
|
|
181
207
|
railOpen = false;
|
|
182
208
|
};
|
|
183
209
|
sync();
|
|
184
210
|
mobile.addEventListener('change', sync);
|
|
185
211
|
narrow.addEventListener('change', sync);
|
|
186
212
|
return () => {
|
|
213
|
+
clearTimeout(railRest);
|
|
187
214
|
mobile.removeEventListener('change', sync);
|
|
188
215
|
narrow.removeEventListener('change', sync);
|
|
189
216
|
};
|
|
@@ -269,8 +296,9 @@
|
|
|
269
296
|
data-testid="app-rail"
|
|
270
297
|
role="presentation"
|
|
271
298
|
class="relative hidden {APP_SIDEBAR_RAIL} shrink-0 md:block"
|
|
272
|
-
onpointerenter={
|
|
273
|
-
|
|
299
|
+
onpointerenter={onRailPointerMove}
|
|
300
|
+
onpointermove={onRailPointerMove}
|
|
301
|
+
onpointerleave={onRailPointerLeave}
|
|
274
302
|
onfocusin={() => (railOpen = true)}
|
|
275
303
|
onfocusout={onRailFocusOut}
|
|
276
304
|
>
|
|
@@ -8,6 +8,14 @@
|
|
|
8
8
|
export interface SidebarState {
|
|
9
9
|
rail: boolean;
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* How long a pointer has to rest over the rail before it opens. This waits
|
|
13
|
+
* for the reader, not for an animation. A pointer crossing the 56px rail on
|
|
14
|
+
* its way to the page is still moving and never rests there, so the column
|
|
15
|
+
* does not open over the control it is heading for. One that stops on the
|
|
16
|
+
* rail means to read it.
|
|
17
|
+
*/
|
|
18
|
+
export declare const RAIL_REST_MS = 100;
|
|
11
19
|
/**
|
|
12
20
|
* The authed application frame: the sidebar, the header bar and the content
|
|
13
21
|
* column, owned once so three apps cannot each invent their own.
|
|
@@ -220,9 +220,9 @@
|
|
|
220
220
|
return label ? `${label} ${range}` : range;
|
|
221
221
|
});
|
|
222
222
|
|
|
223
|
-
/** The target of a control, sanitized, or nothing when the
|
|
224
|
-
function linkTo(target: number
|
|
225
|
-
return href
|
|
223
|
+
/** The target of a control, sanitized, or nothing when the builder's URL is refused. */
|
|
224
|
+
function linkTo(target: number): string | undefined {
|
|
225
|
+
return href ? safeHref(href(target)) : undefined;
|
|
226
226
|
}
|
|
227
227
|
|
|
228
228
|
/**
|
|
@@ -250,16 +250,6 @@
|
|
|
250
250
|
const btnRest = 'text-muted hover:text-fg hover:bg-surface-2 active:bg-line active:text-fg';
|
|
251
251
|
|
|
252
252
|
const btnCurrent = 'bg-brand text-ink';
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* A link with nowhere to go.
|
|
256
|
-
*
|
|
257
|
-
* An anchor has no `disabled`, and `disabled:` never matches one, so the two
|
|
258
|
-
* states have to be painted rather than declared. Dropping the href is what
|
|
259
|
-
* takes it out of the tab order and stops activation. The class is only the
|
|
260
|
-
* part a reader can see, at the same 30% the disabled buttons use.
|
|
261
|
-
*/
|
|
262
|
-
const linkInert = 'pointer-events-none opacity-30';
|
|
263
253
|
</script>
|
|
264
254
|
|
|
265
255
|
{#snippet arrow(d: string)}
|
|
@@ -281,15 +271,12 @@
|
|
|
281
271
|
|
|
282
272
|
<!-- Previous and next, as a link when the caller builds hrefs and as a button
|
|
283
273
|
when it hands over a callback. The two branches carry the same classes and
|
|
284
|
-
the same label, so the pager reads and sounds the same either way.
|
|
274
|
+
the same label, so the pager reads and sounds the same either way. A step
|
|
275
|
+
with nowhere to go is a disabled button in both: an anchor without an href
|
|
276
|
+
is a plain element, and the accessibility tree refuses a label on one. -->
|
|
285
277
|
{#snippet step(target: number, label: string, enabled: boolean, d: string)}
|
|
286
|
-
{#if href}
|
|
287
|
-
<a
|
|
288
|
-
href={linkTo(target, enabled)}
|
|
289
|
-
aria-label={label}
|
|
290
|
-
aria-disabled={enabled ? undefined : 'true'}
|
|
291
|
-
class="{btnBase} {btnRest} {enabled ? '' : linkInert}"
|
|
292
|
-
>
|
|
278
|
+
{#if href && enabled}
|
|
279
|
+
<a href={linkTo(target)} aria-label={label} class="{btnBase} {btnRest}">
|
|
293
280
|
{@render arrow(d)}
|
|
294
281
|
</a>
|
|
295
282
|
{:else}
|
|
@@ -342,7 +329,7 @@
|
|
|
342
329
|
</span>
|
|
343
330
|
{:else if href}
|
|
344
331
|
<a
|
|
345
|
-
href={linkTo(n
|
|
332
|
+
href={linkTo(n)}
|
|
346
333
|
aria-current={safePage === n ? 'page' : undefined}
|
|
347
334
|
class="{btnBase} {slot.only ? 'max-sm:coarse:hidden' : ''} {safePage === n
|
|
348
335
|
? btnCurrent
|
|
@@ -60,6 +60,14 @@
|
|
|
60
60
|
* `'once'` and the total reads "$6.00 once" rather than "$6.00/mo".
|
|
61
61
|
*/
|
|
62
62
|
per?: BillingPeriod | 'once';
|
|
63
|
+
/**
|
|
64
|
+
* What the order charges, in cents, when the host prices more than the
|
|
65
|
+
* selection, such as a discount code. It stands in for the quote's total
|
|
66
|
+
* in the bar and the default summary, and is what `summary` and `actions`
|
|
67
|
+
* are handed. It counts only while `quote` describes the selection, so it
|
|
68
|
+
* never outlives the cart it was priced for.
|
|
69
|
+
*/
|
|
70
|
+
charged?: number | null;
|
|
63
71
|
/** True while a fresh price is in flight. */
|
|
64
72
|
quoting?: boolean;
|
|
65
73
|
/** The count the bill will carry, when the host knows better than the cart. */
|
|
@@ -124,6 +132,7 @@
|
|
|
124
132
|
quotePeriod = null,
|
|
125
133
|
period = 'monthly',
|
|
126
134
|
per,
|
|
135
|
+
charged = null,
|
|
127
136
|
quoting = false,
|
|
128
137
|
billedSkus = null,
|
|
129
138
|
currency = 'USD',
|
|
@@ -187,9 +196,8 @@
|
|
|
187
196
|
|
|
188
197
|
const total = $derived(
|
|
189
198
|
quoted
|
|
190
|
-
?
|
|
191
|
-
|
|
192
|
-
: quoted.total_cents
|
|
199
|
+
? (charged ??
|
|
200
|
+
(period === 'annual' ? (quoted.annual_total_cents ?? null) : quoted.total_cents))
|
|
193
201
|
: null,
|
|
194
202
|
);
|
|
195
203
|
|
|
@@ -26,6 +26,14 @@ interface Props {
|
|
|
26
26
|
* `'once'` and the total reads "$6.00 once" rather than "$6.00/mo".
|
|
27
27
|
*/
|
|
28
28
|
per?: BillingPeriod | 'once';
|
|
29
|
+
/**
|
|
30
|
+
* What the order charges, in cents, when the host prices more than the
|
|
31
|
+
* selection, such as a discount code. It stands in for the quote's total
|
|
32
|
+
* in the bar and the default summary, and is what `summary` and `actions`
|
|
33
|
+
* are handed. It counts only while `quote` describes the selection, so it
|
|
34
|
+
* never outlives the cart it was priced for.
|
|
35
|
+
*/
|
|
36
|
+
charged?: number | null;
|
|
29
37
|
/** True while a fresh price is in flight. */
|
|
30
38
|
quoting?: boolean;
|
|
31
39
|
/** The count the bill will carry, when the host knows better than the cart. */
|
|
@@ -77,6 +77,7 @@
|
|
|
77
77
|
'[&_tbody_tr:hover>td:last-child]:shadow-[inset_-1px_1px_var(--color-brand),inset_-1px_-1px_var(--color-brand)]',
|
|
78
78
|
'[&_tbody_tr:hover>td:first-child:last-child]:shadow-[inset_1px_1px_var(--color-brand),inset_-1px_-1px_var(--color-brand)]',
|
|
79
79
|
'[&_tbody_tr]:transition-colors',
|
|
80
|
+
'[&_tbody_tr>td]:transition-shadow',
|
|
80
81
|
].join(' ');
|
|
81
82
|
|
|
82
83
|
let {
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
*
|
|
30
30
|
* The scrollbar is not drawn. A strip of tabs with a bar under it reads as
|
|
31
31
|
* a second rule under the real one. The fades carry the same message, the
|
|
32
|
-
*
|
|
33
|
-
*
|
|
32
|
+
* arrow keys reach every tab, and the active tab is scrolled into view when
|
|
33
|
+
* it changes.
|
|
34
34
|
*/
|
|
35
35
|
let scroller: HTMLElement | undefined = $state();
|
|
36
36
|
let moreBefore = $state(false);
|
|
@@ -62,20 +62,81 @@
|
|
|
62
62
|
* page down to it as soon as it mounted. A test DOM lays nothing out, so
|
|
63
63
|
* every box is empty and nothing moves.
|
|
64
64
|
*/
|
|
65
|
-
|
|
65
|
+
function reveal(tab: HTMLElement) {
|
|
66
66
|
const el = scroller;
|
|
67
|
-
const id = active;
|
|
68
67
|
if (!el) return;
|
|
69
|
-
const tab = [...el.querySelectorAll<HTMLElement>('[role="tab"]')].find(
|
|
70
|
-
(t) => t.dataset.tab === id,
|
|
71
|
-
);
|
|
72
|
-
if (!tab) return;
|
|
73
68
|
const box = el.getBoundingClientRect();
|
|
74
69
|
const at = tab.getBoundingClientRect();
|
|
75
70
|
if (at.left < box.left) el.scrollLeft -= box.left - at.left;
|
|
76
71
|
else if (at.right > box.right) el.scrollLeft += at.right - box.right;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function tabAt(index: number): HTMLElement | undefined {
|
|
75
|
+
return scroller?.querySelectorAll<HTMLElement>('[role="tab"]')[index];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const activeIndex = $derived(items.findIndex((t) => t.id === active));
|
|
79
|
+
|
|
80
|
+
$effect(() => {
|
|
81
|
+
const tab = activeIndex === -1 ? undefined : tabAt(activeIndex);
|
|
82
|
+
if (tab) reveal(tab);
|
|
77
83
|
});
|
|
78
84
|
|
|
85
|
+
/*
|
|
86
|
+
* The strip is one stop in the tab order. A strip of five tabs cost five
|
|
87
|
+
* presses of Tab to step over, and the arrows did nothing.
|
|
88
|
+
*
|
|
89
|
+
* Activation is manual: Left, Right, Home and End move focus and the stop
|
|
90
|
+
* with it, and Enter, Space or a click chooses. A caller may navigate to
|
|
91
|
+
* another page when a tab is chosen, and a strip that chose on every arrow
|
|
92
|
+
* would then load a page per key press and take the reader off the strip.
|
|
93
|
+
*
|
|
94
|
+
* While focus is inside the strip the stop follows it, by key or by
|
|
95
|
+
* pointer. When focus leaves, the stop goes back to the selected tab. A
|
|
96
|
+
* selection matching no item would leave every tab at -1 and drop the
|
|
97
|
+
* strip out of the tab order, so the first tab holds it then.
|
|
98
|
+
*/
|
|
99
|
+
let roving: number | null = $state(null);
|
|
100
|
+
const tabStop = $derived(roving ?? (activeIndex === -1 ? 0 : activeIndex));
|
|
101
|
+
|
|
102
|
+
function moveTo(index: number) {
|
|
103
|
+
const tab = tabAt(index);
|
|
104
|
+
if (!tab) return;
|
|
105
|
+
// The strip is in view when a key reaches it, and focus() would scroll
|
|
106
|
+
// every box above the tab, the page among them.
|
|
107
|
+
tab.focus({ preventScroll: true });
|
|
108
|
+
reveal(tab);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function onTabKeydown(e: KeyboardEvent, index: number) {
|
|
112
|
+
const last = items.length - 1;
|
|
113
|
+
let next: number;
|
|
114
|
+
switch (e.key) {
|
|
115
|
+
case 'ArrowRight':
|
|
116
|
+
next = index === last ? 0 : index + 1;
|
|
117
|
+
break;
|
|
118
|
+
case 'ArrowLeft':
|
|
119
|
+
next = index === 0 ? last : index - 1;
|
|
120
|
+
break;
|
|
121
|
+
case 'Home':
|
|
122
|
+
next = 0;
|
|
123
|
+
break;
|
|
124
|
+
case 'End':
|
|
125
|
+
next = last;
|
|
126
|
+
break;
|
|
127
|
+
default:
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
// The arrows scroll the strip and the page, and Home and End jump the page.
|
|
131
|
+
e.preventDefault();
|
|
132
|
+
moveTo(next);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function onStripFocusOut(e: FocusEvent) {
|
|
136
|
+
if (scroller && e.relatedTarget instanceof Node && scroller.contains(e.relatedTarget)) return;
|
|
137
|
+
roving = null;
|
|
138
|
+
}
|
|
139
|
+
|
|
79
140
|
/*
|
|
80
141
|
* The focus ring is inset: the tabs sit in a box that clips to scroll, and
|
|
81
142
|
* the global outline sits 2px outside its element, so on the strip's top
|
|
@@ -92,17 +153,21 @@
|
|
|
92
153
|
<div
|
|
93
154
|
bind:this={scroller}
|
|
94
155
|
onscroll={measure}
|
|
156
|
+
onfocusout={onStripFocusOut}
|
|
95
157
|
role="tablist"
|
|
96
158
|
data-testid="tabs-scroll"
|
|
97
159
|
class="flex gap-0.5 overflow-x-auto border-b border-line [scrollbar-width:none]"
|
|
98
160
|
>
|
|
99
|
-
{#each items as tab (tab.id)}
|
|
161
|
+
{#each items as tab, i (tab.id)}
|
|
100
162
|
<button
|
|
101
163
|
type="button"
|
|
102
164
|
role="tab"
|
|
103
165
|
data-tab={tab.id}
|
|
104
166
|
aria-selected={active === tab.id}
|
|
167
|
+
tabindex={i === tabStop ? 0 : -1}
|
|
105
168
|
onclick={() => onchange(tab.id)}
|
|
169
|
+
onfocus={() => (roving = i)}
|
|
170
|
+
onkeydown={(e) => onTabKeydown(e, i)}
|
|
106
171
|
class="{TAB}
|
|
107
172
|
{active === tab.id
|
|
108
173
|
? 'text-fg after:absolute after:bottom-0 after:inset-x-0 after:h-0.5 after:bg-brand after:rounded-t'
|
package/dist/index.d.ts
CHANGED
|
@@ -113,4 +113,4 @@ export { cn, type ClassValue } from './utils/cn.js';
|
|
|
113
113
|
export { getTheme, getThemePreference, nextThemePreference, resolveTheme, setTheme, setThemePreference, systemTheme, themeBootScript, watchThemePreference, toggleTheme, watchSystemTheme, THEME_PREFERENCES, type Theme, type ThemePreference, } from './utils/theme.js';
|
|
114
114
|
export * as motion from './motion.js';
|
|
115
115
|
export type { Rung as MotionRung, Curve as MotionCurve } from './motion.js';
|
|
116
|
-
export declare const VERSION = "0.
|
|
116
|
+
export declare const VERSION = "0.45.0";
|
package/dist/index.js
CHANGED
|
@@ -122,4 +122,4 @@ export * as motion from './motion.js';
|
|
|
122
122
|
// ── Version ────────────────────────────────────────────────────────────────
|
|
123
123
|
// Generated from package.json by `pnpm version:sync`. Bump package.json, never
|
|
124
124
|
// this line. The build and the test suite fail when the two disagree.
|
|
125
|
-
export const VERSION = '0.
|
|
125
|
+
export const VERSION = '0.45.0';
|
|
@@ -20,14 +20,15 @@ export interface ScrollMetrics {
|
|
|
20
20
|
}
|
|
21
21
|
export declare function scrollEdges(el: ScrollMetrics): ScrollEdges;
|
|
22
22
|
/**
|
|
23
|
-
* The fade itself: a
|
|
24
|
-
* does not scroll away with the content it describes.
|
|
25
|
-
*
|
|
26
|
-
* a
|
|
27
|
-
*
|
|
28
|
-
*
|
|
23
|
+
* The fade itself: a wash of the foreground color on the edge, outside the
|
|
24
|
+
* scroll box so it does not scroll away with the content it describes. The
|
|
25
|
+
* foreground token inverts with the theme, so the fade darkens a light card
|
|
26
|
+
* and lightens a dark one. A black fade had nothing to darken on a dark card
|
|
27
|
+
* and read as no fade at all. No surface token is the color to fade to,
|
|
28
|
+
* because the box sits on the page in one place and inside a card in
|
|
29
|
+
* another. The direction is logical, so a right-to-left page fades the other
|
|
29
30
|
* way.
|
|
30
31
|
*/
|
|
31
32
|
export declare const SCROLL_EDGE = "pointer-events-none absolute inset-y-px w-6";
|
|
32
|
-
export declare const SCROLL_EDGE_START = "start-0 bg-linear-to-r rtl:bg-linear-to-l from-
|
|
33
|
-
export declare const SCROLL_EDGE_END = "end-0 bg-linear-to-l rtl:bg-linear-to-r from-
|
|
33
|
+
export declare const SCROLL_EDGE_START = "start-0 bg-linear-to-r rtl:bg-linear-to-l from-fg/25 to-transparent";
|
|
34
|
+
export declare const SCROLL_EDGE_END = "end-0 bg-linear-to-l rtl:bg-linear-to-r from-fg/25 to-transparent";
|
|
@@ -20,14 +20,15 @@ export function scrollEdges(el) {
|
|
|
20
20
|
return { before: traveled > 1, after: traveled < slack - 1 };
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
|
-
* The fade itself: a
|
|
24
|
-
* does not scroll away with the content it describes.
|
|
25
|
-
*
|
|
26
|
-
* a
|
|
27
|
-
*
|
|
28
|
-
*
|
|
23
|
+
* The fade itself: a wash of the foreground color on the edge, outside the
|
|
24
|
+
* scroll box so it does not scroll away with the content it describes. The
|
|
25
|
+
* foreground token inverts with the theme, so the fade darkens a light card
|
|
26
|
+
* and lightens a dark one. A black fade had nothing to darken on a dark card
|
|
27
|
+
* and read as no fade at all. No surface token is the color to fade to,
|
|
28
|
+
* because the box sits on the page in one place and inside a card in
|
|
29
|
+
* another. The direction is logical, so a right-to-left page fades the other
|
|
29
30
|
* way.
|
|
30
31
|
*/
|
|
31
32
|
export const SCROLL_EDGE = 'pointer-events-none absolute inset-y-px w-6';
|
|
32
|
-
export const SCROLL_EDGE_START = 'start-0 bg-linear-to-r rtl:bg-linear-to-l from-
|
|
33
|
-
export const SCROLL_EDGE_END = 'end-0 bg-linear-to-l rtl:bg-linear-to-r from-
|
|
33
|
+
export const SCROLL_EDGE_START = 'start-0 bg-linear-to-r rtl:bg-linear-to-l from-fg/25 to-transparent';
|
|
34
|
+
export const SCROLL_EDGE_END = 'end-0 bg-linear-to-l rtl:bg-linear-to-r from-fg/25 to-transparent';
|
package/package.json
CHANGED