entasis 0.7.1 → 0.9.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/README.md +7 -1
- package/dist/components/AppShell/appShell.theme.js +2 -2
- package/dist/components/FloatingWindow/FloatingWindow.svelte +26 -2
- package/dist/components/FloatingWindow/floatingWindow.dock.svelte.js +11 -2
- package/dist/components/FloatingWindow/floatingWindow.mcp.d.ts +1 -1
- package/dist/components/FloatingWindow/floatingWindow.mcp.js +7 -4
- package/dist/components/FloatingWindow/floatingWindow.props.d.ts +6 -0
- package/dist/components/FloatingWindow/floatingWindow.state.svelte.d.ts +3 -0
- package/dist/components/FloatingWindow/floatingWindow.state.svelte.js +18 -4
- package/dist/components/FloatingWindow/floatingWindow.theme.d.ts +3 -0
- package/dist/components/FloatingWindow/floatingWindow.theme.js +20 -7
- package/dist/components/Form/File/FileInput.svelte +6 -42
- package/dist/components/Form/Form/form.state.svelte.d.ts +4 -0
- package/dist/components/Form/Form/visibility.d.ts +2 -0
- package/dist/components/Form/MultiStepForm/multiStepForm.state.svelte.d.ts +4 -0
- package/dist/components/Form/Select/Select.svelte +28 -2
- package/dist/components/Form/Select/select.align.d.ts +55 -0
- package/dist/components/Form/Select/select.align.js +41 -0
- package/dist/components/Form/Select/select.mcp.d.ts +1 -1
- package/dist/components/Form/Select/select.mcp.js +7 -3
- package/dist/components/Form/Select/select.props.d.ts +8 -0
- package/dist/components/Form/Select/select.state.svelte.d.ts +24 -0
- package/dist/components/Form/Select/select.state.svelte.js +111 -1
- package/dist/components/Popover/Popover.svelte +4 -0
- package/dist/components/Popover/popover.mcp.d.ts +1 -1
- package/dist/components/Popover/popover.mcp.js +1 -0
- package/dist/components/Popover/popover.props.d.ts +16 -0
- package/dist/components/Popover/popover.state.svelte.d.ts +1 -1
- package/dist/components/Popover/popover.state.svelte.js +13 -0
- package/dist/components/Sidebar/Sidebar.svelte +58 -10
- package/dist/components/Sidebar/Sidebar.svelte.d.ts +1 -1
- package/dist/components/Sidebar/SidebarMenuItem.svelte +19 -2
- package/dist/components/Sidebar/SidebarMenuItem.svelte.d.ts +4 -0
- package/dist/components/Sidebar/SidebarPanel.svelte +199 -88
- package/dist/components/Sidebar/SidebarPanel.svelte.d.ts +3 -0
- package/dist/components/Sidebar/SidebarViewStage.svelte +103 -0
- package/dist/components/Sidebar/SidebarViewStage.svelte.d.ts +18 -0
- package/dist/components/Sidebar/index.d.ts +1 -1
- package/dist/components/Sidebar/sidebar.mcp.d.ts +1 -1
- package/dist/components/Sidebar/sidebar.mcp.js +19 -2
- package/dist/components/Sidebar/sidebar.props.d.ts +56 -1
- package/dist/components/Sidebar/sidebar.state.svelte.d.ts +4 -0
- package/dist/components/Sidebar/sidebar.state.svelte.js +9 -1
- package/dist/components/Sidebar/sidebar.theme.d.ts +128 -5
- package/dist/components/Sidebar/sidebar.theme.js +69 -3
- package/dist/components/Sidebar/sidebar.views.svelte.d.ts +138 -0
- package/dist/components/Sidebar/sidebar.views.svelte.js +303 -0
- package/dist/components/Theme/theme.floatingWindows.d.ts +6 -1
- package/dist/components/Theme/theme.floatingWindows.js +7 -2
- package/dist/components/Theme/theme.mcp.d.ts +1 -1
- package/dist/components/Theme/theme.mcp.js +1 -0
- package/dist/generated/componentContract.d.ts +1 -1
- package/dist/generated/componentContract.js +1 -0
- package/dist/generated/componentMcpRegistry.d.ts +6 -6
- package/dist/tailwind/index.mcp.d.ts +1 -1
- package/dist/tailwind/index.mcp.js +4 -0
- package/dist/tailwind/scales.js +11 -3
- package/dist/tailwind/spacing.js +17 -0
- package/dist/utils/cva/merge.d.ts +7 -0
- package/dist/utils/cva/merge.js +6 -1
- package/dist/utils/pointerDrag.js +7 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -320,7 +320,13 @@ Snippet props: `size` (px number or CSS length, default `1lh`), `color` (a role
|
|
|
320
320
|
|
|
321
321
|
- `pnpm dev` runs the documentation site with a page per component under `/components/<name>`.
|
|
322
322
|
- `.claude/skills/entasis/` (mirrored in `.agents/skills/entasis/`) holds the coding-agent skill: import conventions, per-component references, theming notes.
|
|
323
|
-
- Each component folder ships a `*.mcp.ts` description
|
|
323
|
+
- Each component folder ships a `*.mcp.ts` description, served by the MCP server the docs site runs at
|
|
324
|
+
`https://entasis.beynar.workers.dev/mcp` (one `components` tool that returns a component's documentation).
|
|
325
|
+
Add it to a project for Claude Code with:
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
claude mcp add --transport http --scope project entasis https://entasis.beynar.workers.dev/mcp
|
|
329
|
+
```
|
|
324
330
|
|
|
325
331
|
## Releasing
|
|
326
332
|
|
|
@@ -42,8 +42,8 @@ const defaultPage = cva({
|
|
|
42
42
|
variant: {
|
|
43
43
|
admin: 'bg-surface [--page-shell-chrome:var(--color-surface-canvas)] [--page-shell-surface:var(--color-surface)]',
|
|
44
44
|
floating: 'bg-surface-canvas [--page-shell-chrome:var(--color-surface)] [--page-shell-surface:var(--color-surface-canvas)] [--page-shell-chrome-inline-gap:0.5rem] [--page-shell-chrome-block-gap:0.5rem] [--page-shell-header-top-radius:var(--radius-lg)] [--page-shell-header-bottom-radius:var(--radius-lg)] [--page-shell-footer-top-radius:var(--radius-lg)] [--page-shell-footer-bottom-radius:var(--radius-lg)] [--page-shell-chrome-border:var(--color-neutral-muted)] [--page-shell-chrome-shadow:var(--elevation-1)]',
|
|
45
|
-
inset: 'bg-surface [--page-shell-chrome:var(--color-surface)] [--page-shell-surface:var(--color-surface)] transition-[border-color,box-shadow] md:raised-1 md:group-data-[
|
|
46
|
-
split: 'bg-surface [--page-shell-chrome:var(--color-surface)] [--page-shell-surface:var(--color-surface)] transition-[border-color,box-shadow] md:raised-1 md:group-data-[
|
|
45
|
+
inset: 'bg-surface [--page-shell-chrome:var(--color-surface)] [--page-shell-surface:var(--color-surface)] transition-[border-color,box-shadow] md:raised-1 md:group-data-[page-flush=true]/sidebar-wrapper:border-transparent md:group-data-[page-flush=true]/sidebar-wrapper:shadow-none',
|
|
46
|
+
split: 'bg-surface [--page-shell-chrome:var(--color-surface)] [--page-shell-surface:var(--color-surface)] transition-[border-color,box-shadow] md:raised-1 md:group-data-[page-flush=true]/sidebar-wrapper:border-transparent md:group-data-[page-flush=true]/sidebar-wrapper:shadow-none',
|
|
47
47
|
// The frame already owns the radius, border and elevation, so the page inside it is flat,
|
|
48
48
|
// and its header chrome is the same surface as the content: the white column is one
|
|
49
49
|
// piece whose only corners are the frame's own.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { untrack } from 'svelte';
|
|
3
3
|
import { createBindableValue } from '../../utils/state.svelte.js';
|
|
4
|
-
import { crossfade, scale } from 'svelte/transition';
|
|
4
|
+
import { crossfade, fade, scale } from 'svelte/transition';
|
|
5
5
|
import { easingFunctions } from '../../transitions/easingFunctions.js';
|
|
6
6
|
import Button from '../Button/Button.svelte';
|
|
7
7
|
import { minusIcon } from '../Icons/minus.js';
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
minimizable = true,
|
|
33
33
|
closable = true,
|
|
34
34
|
closeOnEscape = true,
|
|
35
|
+
backdrop = false,
|
|
35
36
|
position = $bindable(),
|
|
36
37
|
dimensions = $bindable({ width: 480, height: 320 }),
|
|
37
38
|
class: className,
|
|
@@ -116,6 +117,9 @@
|
|
|
116
117
|
get closeOnEscape() {
|
|
117
118
|
return closeOnEscape;
|
|
118
119
|
},
|
|
120
|
+
get backdrop() {
|
|
121
|
+
return backdrop;
|
|
122
|
+
},
|
|
119
123
|
get position() {
|
|
120
124
|
return position;
|
|
121
125
|
},
|
|
@@ -202,6 +206,26 @@
|
|
|
202
206
|
};
|
|
203
207
|
</script>
|
|
204
208
|
|
|
209
|
+
{#if backdrop && openState.value && !minimized && windowState.position}
|
|
210
|
+
<!-- Portaled into the window layer one index below the window; it fades on the window's own
|
|
211
|
+
enter / exit timing, so minimizing lifts it while the window flies to the dock. -->
|
|
212
|
+
<div
|
|
213
|
+
{@attach windowState.theme.floatingWindows.portal}
|
|
214
|
+
data-slot="floating-window-backdrop"
|
|
215
|
+
aria-hidden="true"
|
|
216
|
+
class={classes.backdrop()}
|
|
217
|
+
style:z-index={windowState.backdropZIndex}
|
|
218
|
+
in:fade={{
|
|
219
|
+
duration: enter.in.duration ?? 0,
|
|
220
|
+
easing: easingFunctions[enter.in.easing ?? 'cubicOut']
|
|
221
|
+
}}
|
|
222
|
+
out:fade={{
|
|
223
|
+
duration: exit.out.duration ?? 0,
|
|
224
|
+
easing: easingFunctions[exit.out.easing ?? 'cubicOut']
|
|
225
|
+
}}
|
|
226
|
+
></div>
|
|
227
|
+
{/if}
|
|
228
|
+
|
|
205
229
|
{#if openState.value && !minimized && windowState.position}
|
|
206
230
|
<div
|
|
207
231
|
{@attach windowState.theme.floatingWindows.portal}
|
|
@@ -214,7 +238,7 @@
|
|
|
214
238
|
bind:this={ref}
|
|
215
239
|
{id}
|
|
216
240
|
role="dialog"
|
|
217
|
-
aria-modal=
|
|
241
|
+
aria-modal={backdrop ? 'true' : 'false'}
|
|
218
242
|
aria-labelledby={`${id}-title`}
|
|
219
243
|
aria-keyshortcuts={draggable
|
|
220
244
|
? 'Alt+ArrowUp Alt+ArrowRight Alt+ArrowDown Alt+ArrowLeft'
|
|
@@ -5,6 +5,11 @@ const DOCK_ITEM_WIDTH = 176;
|
|
|
5
5
|
const DOCK_ITEM_HEIGHT = 36;
|
|
6
6
|
const DOCK_GAP = 8;
|
|
7
7
|
const DOCK_INLINE_GAP = 8;
|
|
8
|
+
const DOCK_DRAG_ACTIVATION = Object.freeze({
|
|
9
|
+
distancePx: 4,
|
|
10
|
+
touchDelayMs: 300,
|
|
11
|
+
touchTolerancePx: 8
|
|
12
|
+
});
|
|
8
13
|
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
|
|
9
14
|
export class FloatingWindowDockState {
|
|
10
15
|
windowState;
|
|
@@ -98,6 +103,10 @@ export class FloatingWindowDockState {
|
|
|
98
103
|
};
|
|
99
104
|
drag = createPointerDrag({
|
|
100
105
|
disabled: () => !this.windowState.draggable || !this.windowState.minimized,
|
|
106
|
+
// The item is the title button's container, not a handle: a press that never travels must
|
|
107
|
+
// keep the pointer on the button, or its click lands on the item and never restores.
|
|
108
|
+
capture: 'on-activate',
|
|
109
|
+
activation: () => DOCK_DRAG_ACTIVATION,
|
|
101
110
|
onStart: (payload) => this.startMove(payload),
|
|
102
111
|
onMove: (payload) => this.updateMove(payload),
|
|
103
112
|
onEnd: (payload) => this.endMove(payload)
|
|
@@ -115,8 +124,8 @@ export class FloatingWindowDockState {
|
|
|
115
124
|
return Math.max(1, Math.floor((availableLength - DOCK_INLINE_GAP * 2 + DOCK_GAP) / (itemLength + DOCK_GAP)));
|
|
116
125
|
}
|
|
117
126
|
startMove(payload) {
|
|
118
|
-
if (payload.
|
|
119
|
-
payload.
|
|
127
|
+
if (payload.startTarget instanceof Element &&
|
|
128
|
+
payload.startTarget.closest('[data-floating-window-dock-actions]')) {
|
|
120
129
|
return false;
|
|
121
130
|
}
|
|
122
131
|
this.windowState.activateSurface('dock');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const floatingWindowDescription = "\n# FloatingWindow Component\n\nFloatingWindow renders a non-modal
|
|
1
|
+
export declare const floatingWindowDescription = "\n# FloatingWindow Component\n\nFloatingWindow renders a portaled utility window, non-modal unless `backdrop` is set, that can be moved, resized, minimized into a configurable viewport-edge dock, restored, and closed. Multiple windows inside the same Theme provider coordinate their z-order and stack independently by dock placement. The Theme keeps floating windows below modal Dialog surfaces, so an open window remains mounted behind a dialog and returns unchanged when the dialog closes.\n\n## Basic Usage\n\n```svelte\n<script lang=\"ts\">\n import { Button } from '../Button/index.ts';\n import { FloatingWindow } from './index.ts';\n\n let open = $state(false);\n</script>\n\n<Button onclick={() => (open = true)}>Open notes</Button>\n\n<FloatingWindow bind:open title=\"Notes\">\n <p>Window content remains interactive alongside the page.</p>\n</FloatingWindow>\n```\n\n## Props\n\n- **id**: string - Stable DOM id. Generated when omitted.\n- **open**: boolean (default: true) - Bindable rendered state.\n- **defaultOpen**: boolean (default: true) - Initial state when open is not provided.\n- **minimized**: boolean (default: false) - Bindable docked state.\n- **dockPlacement**: 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right' | 'left-top' | 'left-bottom' | 'right-top' | 'right-bottom' (default: 'bottom-left') - Edge and alignment used by the minimized dock. Top and bottom placements stack horizontally; left and right placements use a vertical title bar and stack vertically.\n- **title**: Slot<FloatingWindowPayload> (required) - Window title as text or a snippet.\n- **children**: Slot<FloatingWindowPayload> - Main content.\n- **dragFrom**: 'header' | 'window' (default: 'header') - Restricts dragging to the header or allows any non-interactive surface to start a drag.\n- **draggable**: boolean (default: true) - Enables pointer dragging.\n- **resizable**: boolean (default: true) - Enables four edge and four corner resize handles.\n- **minimizable**: boolean (default: true) - Shows the minimize control.\n- **closable**: boolean (default: true) - Shows the close control.\n- **closeOnEscape**: boolean (default: true) - Closes the topmost expanded floating window when Escape is pressed.\n- **backdrop**: boolean (default: false) - Dims the page behind the expanded window and makes it modal like a Dialog: Tab stays inside, the rest of the page is inert, and page scroll is locked. Minimizing into the dock lifts all of it; restoring brings it back. Clicking the backdrop does nothing: close with the close control or Escape.\n- **position**: { x: number; y: number } - Bindable viewport-relative top-left position. The first render is centered when omitted.\n- **dimensions**: { width: number; height: number; min?: [width, height]; max?: [width, height] } (default: 480 x 320, minimum 280 x 160) - Bindable pixel dimensions and optional constraint tuples. Maximum dimensions remain additionally constrained to the viewport.\n- **class**: string - Additional classes on the visible window.\n- **theme**: FloatingWindowThemeProps - Per-instance theme overrides.\n- **ref**: HTMLDivElement - Bindable reference to the visible window or minimized dock item.\n- **onOpenChange**: (open: boolean) => void - Runs once for each library-requested state change.\n- **onAfterOpen**: (payload) => void - Runs after the open transition finishes.\n- **onAfterClose**: (payload) => void - Runs after the close transition finishes.\n- **onMinimize**: (payload) => void - Runs after minimize state updates.\n- **onRestore**: (payload) => void - Runs after restore state updates.\n- **onMove**: ({ position, window }) => void - Runs when a move commits.\n- **onResize**: ({ dimensions, window }) => void - Runs when a pointer or keyboard resize commits.\n\n## Dragging\n\nHeader dragging is the default because it preserves text selection and content interactions. With `dragFrom=\"window\"`, buttons, links, inputs, editable content, resize handles, and descendants marked `data-floating-window-no-drag` remain excluded from drag starts.\n\n## Accessibility\n\n- The expanded surface uses a `dialog` role labelled by its title: `aria-modal=\"false\"`, or `\"true\"` with `backdrop`.\n- Close, minimize, and restore controls are native Entasis buttons with accessible labels.\n- Edge resize handles use `separator` semantics and support arrow-key resizing; hold Shift for a larger step.\n- Opening focuses the window, closing restores focus to its previous owner, and only the topmost expanded floating window handles Escape.\n- Alt+Arrow moves the focused window; hold Shift for a larger step.\n- Corner handles are pointer-only because a diagonal separator has no valid ARIA orientation.\n- Without `backdrop` it does not trap focus or hide page content. With it, Tab and Shift+Tab stay inside the window, every sibling subtree up to `<body>` is `inert` (other windows and dock items included), and page scroll is locked, until the window closes or is minimized.\n\n## Theme Parts\n\n- **backdrop**: Page dim behind a window opened with `backdrop`, one layer below the window.\n- **root**: Floating window surface and drag/resize states.\n- **header**: Default title bar and drag handle.\n- **title**: Header title.\n- **actions**: Header control group.\n- **control**: Header and dock icon buttons.\n- **scrollArea**: Flexible ScrollArea root that owns body scrolling.\n- **content**: Padded content inside the ScrollArea viewport.\n- **resizeHandle**: Edge and corner handles by direction.\n- **dockItem**: Minimized surface.\n- **dockTitle**: Full-width restore button in the dock item.\n- **dockTitleText**: Truncated title text and lateral writing direction.\n- **dockActions**: Dock restore and close controls.\n\n## Motion\n\n- **motion** theme slot, keyed by `phase`: `flight` times the crossfade between window and\n dock pill, `enter` / `exit` the scale fallback when there is no counterpart.\n- Only `duration` / `easing` (plus the fallback's `scale` / `opacity`) are read.\n- The backdrop fades on the `enter` / `exit` timings.\n- Ladder: `<Theme components={{ 'floating-window': { motion } }}>` \u2192\n `setFloatingWindowTheme({ motion })` \u2192 `theme.motion`. Read once, at mount.\n";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const floatingWindowDescription = `
|
|
2
2
|
# FloatingWindow Component
|
|
3
3
|
|
|
4
|
-
FloatingWindow renders a non-modal
|
|
4
|
+
FloatingWindow renders a portaled utility window, non-modal unless \`backdrop\` is set, that can be moved, resized, minimized into a configurable viewport-edge dock, restored, and closed. Multiple windows inside the same Theme provider coordinate their z-order and stack independently by dock placement. The Theme keeps floating windows below modal Dialog surfaces, so an open window remains mounted behind a dialog and returns unchanged when the dialog closes.
|
|
5
5
|
|
|
6
6
|
## Basic Usage
|
|
7
7
|
|
|
@@ -35,6 +35,7 @@ FloatingWindow renders a non-modal, portaled utility window that can be moved, r
|
|
|
35
35
|
- **minimizable**: boolean (default: true) - Shows the minimize control.
|
|
36
36
|
- **closable**: boolean (default: true) - Shows the close control.
|
|
37
37
|
- **closeOnEscape**: boolean (default: true) - Closes the topmost expanded floating window when Escape is pressed.
|
|
38
|
+
- **backdrop**: boolean (default: false) - Dims the page behind the expanded window and makes it modal like a Dialog: Tab stays inside, the rest of the page is inert, and page scroll is locked. Minimizing into the dock lifts all of it; restoring brings it back. Clicking the backdrop does nothing: close with the close control or Escape.
|
|
38
39
|
- **position**: { x: number; y: number } - Bindable viewport-relative top-left position. The first render is centered when omitted.
|
|
39
40
|
- **dimensions**: { width: number; height: number; min?: [width, height]; max?: [width, height] } (default: 480 x 320, minimum 280 x 160) - Bindable pixel dimensions and optional constraint tuples. Maximum dimensions remain additionally constrained to the viewport.
|
|
40
41
|
- **class**: string - Additional classes on the visible window.
|
|
@@ -54,16 +55,17 @@ Header dragging is the default because it preserves text selection and content i
|
|
|
54
55
|
|
|
55
56
|
## Accessibility
|
|
56
57
|
|
|
57
|
-
- The expanded surface uses a
|
|
58
|
+
- The expanded surface uses a \`dialog\` role labelled by its title: \`aria-modal="false"\`, or \`"true"\` with \`backdrop\`.
|
|
58
59
|
- Close, minimize, and restore controls are native Entasis buttons with accessible labels.
|
|
59
60
|
- Edge resize handles use \`separator\` semantics and support arrow-key resizing; hold Shift for a larger step.
|
|
60
|
-
- Opening focuses the
|
|
61
|
+
- Opening focuses the window, closing restores focus to its previous owner, and only the topmost expanded floating window handles Escape.
|
|
61
62
|
- Alt+Arrow moves the focused window; hold Shift for a larger step.
|
|
62
63
|
- Corner handles are pointer-only because a diagonal separator has no valid ARIA orientation.
|
|
63
|
-
-
|
|
64
|
+
- Without \`backdrop\` it does not trap focus or hide page content. With it, Tab and Shift+Tab stay inside the window, every sibling subtree up to \`<body>\` is \`inert\` (other windows and dock items included), and page scroll is locked, until the window closes or is minimized.
|
|
64
65
|
|
|
65
66
|
## Theme Parts
|
|
66
67
|
|
|
68
|
+
- **backdrop**: Page dim behind a window opened with \`backdrop\`, one layer below the window.
|
|
67
69
|
- **root**: Floating window surface and drag/resize states.
|
|
68
70
|
- **header**: Default title bar and drag handle.
|
|
69
71
|
- **title**: Header title.
|
|
@@ -82,6 +84,7 @@ Header dragging is the default because it preserves text selection and content i
|
|
|
82
84
|
- **motion** theme slot, keyed by \`phase\`: \`flight\` times the crossfade between window and
|
|
83
85
|
dock pill, \`enter\` / \`exit\` the scale fallback when there is no counterpart.
|
|
84
86
|
- Only \`duration\` / \`easing\` (plus the fallback's \`scale\` / \`opacity\`) are read.
|
|
87
|
+
- The backdrop fades on the \`enter\` / \`exit\` timings.
|
|
85
88
|
- Ladder: \`<Theme components={{ 'floating-window': { motion } }}>\` →
|
|
86
89
|
\`setFloatingWindowTheme({ motion })\` → \`theme.motion\`. Read once, at mount.
|
|
87
90
|
`;
|
|
@@ -66,6 +66,12 @@ export type FloatingWindowProps = WithAttachments<{
|
|
|
66
66
|
closable?: boolean;
|
|
67
67
|
/** Closes the topmost expanded floating window when Escape is pressed. */
|
|
68
68
|
closeOnEscape?: boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Dims the page behind the expanded window and makes the window modal, like a Dialog: Tab
|
|
71
|
+
* stays inside, the rest of the page is inert, and page scroll is locked. Minimizing into the
|
|
72
|
+
* dock lifts all of it; restoring brings it back.
|
|
73
|
+
*/
|
|
74
|
+
backdrop?: boolean;
|
|
69
75
|
/** Bindable viewport-relative top-left position in pixels. Defaults to centered. */
|
|
70
76
|
position?: FloatingWindowPosition;
|
|
71
77
|
/** Bindable width and height in pixels, with optional [width, height] constraint tuples. */
|
|
@@ -12,6 +12,7 @@ type FloatingWindowStateOptions = {
|
|
|
12
12
|
minimizable: boolean;
|
|
13
13
|
closable: boolean;
|
|
14
14
|
closeOnEscape: boolean;
|
|
15
|
+
backdrop: boolean;
|
|
15
16
|
position?: FloatingWindowPosition;
|
|
16
17
|
dimensions: FloatingWindowDimensions;
|
|
17
18
|
onOpenChange?: (open: boolean) => void;
|
|
@@ -61,6 +62,8 @@ export declare class FloatingWindowState extends FloatingWindowState_base {
|
|
|
61
62
|
windowDrag: Attachment<HTMLElement>;
|
|
62
63
|
resize(direction: FloatingWindowResizeDirection): Attachment<HTMLElement>;
|
|
63
64
|
activateSurface(type: FloatingWindowSurface): void;
|
|
65
|
+
/** The index reserved just below the window when it activated with a backdrop. */
|
|
66
|
+
get backdropZIndex(): number;
|
|
64
67
|
bringToFront(): void;
|
|
65
68
|
close(): void;
|
|
66
69
|
minimize(): void;
|
|
@@ -4,6 +4,7 @@ import { createPointerDrag } from '../../utils/pointerDrag.js';
|
|
|
4
4
|
import { withOptions } from '../../utils/state.svelte.js';
|
|
5
5
|
import { useTheme } from '../Theme/theme.state.svelte.js';
|
|
6
6
|
import { useFocusScope } from '../../utils/useFocusScope.svelte.js';
|
|
7
|
+
import { useScrollLock } from '../../utils/useScrollLock.svelte.js';
|
|
7
8
|
import { FloatingWindowDockState } from './floatingWindow.dock.svelte.js';
|
|
8
9
|
import { FLOATING_WINDOW_VIEWPORT_GAP, FloatingWindowGeometry } from './floatingWindow.geometry.js';
|
|
9
10
|
const KEYBOARD_STEP = 10;
|
|
@@ -25,17 +26,20 @@ export class FloatingWindowState extends withOptions() {
|
|
|
25
26
|
viewportHeight = $state(0);
|
|
26
27
|
geometry;
|
|
27
28
|
docking;
|
|
28
|
-
//
|
|
29
|
+
// Focus lands on the window when it opens and returns to the opener on close. Non-modal by
|
|
30
|
+
// default; with a backdrop the window is modal like a Dialog: Tab stays inside and every
|
|
31
|
+
// sibling subtree up to <body> is inert.
|
|
29
32
|
focusScope = useFocusScope({
|
|
30
33
|
isActive: () => this.open && !this.minimized,
|
|
31
|
-
trap: () =>
|
|
34
|
+
trap: () => this.backdrop,
|
|
35
|
+
inertSiblings: () => this.backdrop,
|
|
32
36
|
initialFocus: () => 'container'
|
|
33
37
|
});
|
|
34
38
|
// Escape is dispatched by the shared layer stack to the topmost layer only.
|
|
35
39
|
layer = this.theme.layers.register({
|
|
36
40
|
kind: 'floating-window',
|
|
37
41
|
isOpen: () => this.open && !this.minimized,
|
|
38
|
-
isModal: () =>
|
|
42
|
+
isModal: () => this.backdrop,
|
|
39
43
|
dismissOnEscape: () => this.closeOnEscape && this.closable && this.theme.floatingWindows.isTopWindow(this.id),
|
|
40
44
|
onDismiss: () => this.close(),
|
|
41
45
|
state: this
|
|
@@ -47,6 +51,12 @@ export class FloatingWindowState extends withOptions() {
|
|
|
47
51
|
resizeAttachments = plainMap();
|
|
48
52
|
constructor(options) {
|
|
49
53
|
super(options);
|
|
54
|
+
useScrollLock({ isActive: () => this.backdrop && this.open && !this.minimized });
|
|
55
|
+
// A backdrop switched on while the window is open needs the index below it reserved.
|
|
56
|
+
$effect(() => {
|
|
57
|
+
if (this.backdrop && this.open && !this.minimized)
|
|
58
|
+
untrack(() => this.bringToFront());
|
|
59
|
+
});
|
|
50
60
|
this.geometry = new FloatingWindowGeometry(this);
|
|
51
61
|
this.docking = new FloatingWindowDockState(this);
|
|
52
62
|
if (typeof window !== 'undefined') {
|
|
@@ -153,7 +163,11 @@ export class FloatingWindowState extends withOptions() {
|
|
|
153
163
|
return attachment;
|
|
154
164
|
}
|
|
155
165
|
activateSurface(type) {
|
|
156
|
-
this.zIndex = this.theme.floatingWindows.activate(this.id, type);
|
|
166
|
+
this.zIndex = this.theme.floatingWindows.activate(this.id, type, type === 'window' && this.backdrop);
|
|
167
|
+
}
|
|
168
|
+
/** The index reserved just below the window when it activated with a backdrop. */
|
|
169
|
+
get backdropZIndex() {
|
|
170
|
+
return this.zIndex - 1;
|
|
157
171
|
}
|
|
158
172
|
bringToFront() {
|
|
159
173
|
this.activateSurface(this.minimized ? 'dock' : 'window');
|
|
@@ -32,6 +32,7 @@ export declare const floatingWindowTheme: {
|
|
|
32
32
|
};
|
|
33
33
|
};
|
|
34
34
|
}>;
|
|
35
|
+
backdrop: (props?: import("../../utils/cva/types.js").CVAClassProp | undefined) => string;
|
|
35
36
|
root: (props?: (import("../../utils/cva/types.js").CVAVariantSchema<{
|
|
36
37
|
dragFrom: {
|
|
37
38
|
header: string;
|
|
@@ -129,6 +130,7 @@ export declare const setFloatingWindowTheme: (theme: InferComponentTheme<{
|
|
|
129
130
|
};
|
|
130
131
|
};
|
|
131
132
|
}>;
|
|
133
|
+
backdrop: (props?: import("../../utils/cva/types.js").CVAClassProp | undefined) => string;
|
|
132
134
|
root: (props?: (import("../../utils/cva/types.js").CVAVariantSchema<{
|
|
133
135
|
dragFrom: {
|
|
134
136
|
header: string;
|
|
@@ -224,6 +226,7 @@ export declare const useFloatingWindowTheme: import("../../utils/cva/theme.js").
|
|
|
224
226
|
};
|
|
225
227
|
};
|
|
226
228
|
}>;
|
|
229
|
+
backdrop: (props?: import("../../utils/cva/types.js").CVAClassProp | undefined) => string;
|
|
227
230
|
root: (props?: (import("../../utils/cva/types.js").CVAVariantSchema<{
|
|
228
231
|
dragFrom: {
|
|
229
232
|
header: string;
|
|
@@ -24,7 +24,7 @@ const defaultFloatingWindow = cva({
|
|
|
24
24
|
}
|
|
25
25
|
});
|
|
26
26
|
const defaultFloatingWindowHeader = cva({
|
|
27
|
-
base: 'border-neutral-muted flex h-9 shrink-0 items-center gap-sm border-b px-md select-none',
|
|
27
|
+
base: 'border-neutral-muted flex h-9 shrink-0 items-center gap-sm border-b px-md-concentric select-none',
|
|
28
28
|
variants: {
|
|
29
29
|
draggable: {
|
|
30
30
|
true: 'cursor-move touch-none',
|
|
@@ -66,17 +66,19 @@ const defaultFloatingWindowResizeHandle = cva({
|
|
|
66
66
|
}
|
|
67
67
|
});
|
|
68
68
|
const defaultFloatingWindowDockItem = cva({
|
|
69
|
-
base: 'bg-surface-floating text-neutral pointer-events-auto fixed flex touch-none items-center overflow-hidden lift-4 ring-1 ring-neutral-muted',
|
|
69
|
+
base: 'bg-surface-floating text-neutral pointer-events-auto fixed flex touch-none items-center overflow-hidden lift-4 ring-1 ring-neutral-muted [--dock-radius:min(var(--radius-xl),calc(var(--spacing)*3))]',
|
|
70
70
|
variants: {
|
|
71
71
|
orientation: {
|
|
72
72
|
horizontal: 'h-9 flex-row',
|
|
73
73
|
vertical: 'w-9 flex-col'
|
|
74
74
|
},
|
|
75
75
|
side: {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
// The window's step, capped at a third of the 36px (`spacing × 9`) item: any rounder and a
|
|
77
|
+
// round theme turns the tab into a pill. At that cap `md` padding always clears the corner.
|
|
78
|
+
top: 'rounded-[var(--dock-radius)] rounded-t-none',
|
|
79
|
+
right: 'rounded-[var(--dock-radius)] rounded-r-none',
|
|
80
|
+
bottom: 'rounded-[var(--dock-radius)] rounded-b-none',
|
|
81
|
+
left: 'rounded-[var(--dock-radius)] rounded-l-none'
|
|
80
82
|
},
|
|
81
83
|
dragging: {
|
|
82
84
|
true: 'cursor-grabbing select-none',
|
|
@@ -89,8 +91,13 @@ const defaultFloatingWindowDockItem = cva({
|
|
|
89
91
|
dragging: false
|
|
90
92
|
}
|
|
91
93
|
});
|
|
94
|
+
// `!static`: the title is the pill's drag and restore surface, so its hover / press overlay (the
|
|
95
|
+
// Button's `state-layer` ::before) anchors to the fixed dock item instead of the button and tints
|
|
96
|
+
// the whole pill, clipped by its rounded overflow. The dock controls keep their own overlay.
|
|
97
|
+
// `active:translate-none` drops the Button's press nudge: any `translate` but `none`, 0 included,
|
|
98
|
+
// makes the title the anchor of its own overlay again and pulls the press tint back inside it.
|
|
92
99
|
const defaultFloatingWindowDockTitle = cva({
|
|
93
|
-
base: '!min-w-0 !flex-1 !rounded-none !text-xs !font-medium',
|
|
100
|
+
base: '!static active:translate-none !min-w-0 !flex-1 !rounded-none !text-xs !font-medium',
|
|
94
101
|
variants: {
|
|
95
102
|
orientation: {
|
|
96
103
|
horizontal: '!h-full !w-auto !justify-start !px-md !text-left',
|
|
@@ -135,6 +142,11 @@ const defaultFloatingWindowDockActions = cva({
|
|
|
135
142
|
// The window and its dock pill are two halves of one crossfade: `flight` times the
|
|
136
143
|
// shared transform between them, `enter` / `exit` the scale fallback used when there
|
|
137
144
|
// is no counterpart to fly to. Only `duration` / `easing` are read from each.
|
|
145
|
+
// The page dim behind a window opened with `backdrop`: the same wash as Dialog's backdrop, so a
|
|
146
|
+
// modal window and a modal dialog dim the page alike.
|
|
147
|
+
const defaultFloatingWindowBackdrop = cva({
|
|
148
|
+
base: 'fixed inset-0 bg-neutral/40 backdrop-blur-xs'
|
|
149
|
+
});
|
|
138
150
|
export const defaultFloatingWindowMotion = motion({
|
|
139
151
|
base: {
|
|
140
152
|
in: { x: 0, y: 0, scale: 0.97, opacity: 0 },
|
|
@@ -153,6 +165,7 @@ export const defaultFloatingWindowMotion = motion({
|
|
|
153
165
|
});
|
|
154
166
|
export const floatingWindowTheme = {
|
|
155
167
|
motion: defaultFloatingWindowMotion,
|
|
168
|
+
backdrop: defaultFloatingWindowBackdrop,
|
|
156
169
|
root: defaultFloatingWindow,
|
|
157
170
|
header: defaultFloatingWindowHeader,
|
|
158
171
|
title: defaultFloatingWindowTitle,
|
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
import { useFileInputTheme } from './fileInput.theme.js';
|
|
10
10
|
import { slide } from 'svelte/transition';
|
|
11
11
|
import Slot from '../../Slot/Slot.svelte';
|
|
12
|
+
import { fileIcon } from '../../Icons/file.js';
|
|
13
|
+
import { plusIcon } from '../../Icons/plus.js';
|
|
14
|
+
import { xIcon } from '../../Icons/x.js';
|
|
12
15
|
import { useI18n } from '../../../i18n/context.svelte.js';
|
|
13
16
|
|
|
14
17
|
let {
|
|
@@ -153,20 +156,7 @@
|
|
|
153
156
|
/>
|
|
154
157
|
{#if dropzone.files.length === 0}
|
|
155
158
|
<div transition:slide class={classes.placeholder({ class: placeholderClass, size: rest.size })}>
|
|
156
|
-
|
|
157
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
158
|
-
width="40"
|
|
159
|
-
height="40"
|
|
160
|
-
viewBox="0 0 24 24"
|
|
161
|
-
fill="none"
|
|
162
|
-
stroke="currentColor"
|
|
163
|
-
stroke-width="2"
|
|
164
|
-
stroke-linecap="round"
|
|
165
|
-
stroke-linejoin="round"
|
|
166
|
-
>
|
|
167
|
-
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
|
168
|
-
<polyline points="14 2 14 8 20 8" />
|
|
169
|
-
</svg>
|
|
159
|
+
{@render fileIcon({ size: 40 })}
|
|
170
160
|
{#if resolvedPlaceholder}
|
|
171
161
|
<span class="mt-2">{resolvedPlaceholder}</span>
|
|
172
162
|
{/if}
|
|
@@ -192,20 +182,7 @@
|
|
|
192
182
|
</div>
|
|
193
183
|
<Button variant="ghost" size="small" squared onclick={() => dropzone.removeFile(fil)}>
|
|
194
184
|
{#snippet prefix()}
|
|
195
|
-
|
|
196
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
197
|
-
width="16"
|
|
198
|
-
height="16"
|
|
199
|
-
viewBox="0 0 24 24"
|
|
200
|
-
fill="none"
|
|
201
|
-
stroke="currentColor"
|
|
202
|
-
stroke-width="2"
|
|
203
|
-
stroke-linecap="round"
|
|
204
|
-
stroke-linejoin="round"
|
|
205
|
-
>
|
|
206
|
-
<line x1="18" y1="6" x2="6" y2="18" />
|
|
207
|
-
<line x1="6" y1="6" x2="18" y2="18" />
|
|
208
|
-
</svg>
|
|
185
|
+
{@render xIcon({ size: 16 })}
|
|
209
186
|
{/snippet}
|
|
210
187
|
</Button>
|
|
211
188
|
</Slot>
|
|
@@ -214,20 +191,7 @@
|
|
|
214
191
|
{#if mode === 'multiple' && dropzone.files.length > 0 && dropzone.files.length < maxFiles}
|
|
215
192
|
<Button variant="soft" color="primary" fullWidth onclick={() => dropzone.open()}>
|
|
216
193
|
{#snippet prefix()}
|
|
217
|
-
|
|
218
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
219
|
-
width="16"
|
|
220
|
-
height="16"
|
|
221
|
-
viewBox="0 0 24 24"
|
|
222
|
-
fill="none"
|
|
223
|
-
stroke="currentColor"
|
|
224
|
-
stroke-width="2"
|
|
225
|
-
stroke-linecap="round"
|
|
226
|
-
stroke-linejoin="round"
|
|
227
|
-
>
|
|
228
|
-
<line x1="12" y1="5" x2="12" y2="19" />
|
|
229
|
-
<line x1="5" y1="12" x2="19" y2="12" />
|
|
230
|
-
</svg>
|
|
194
|
+
{@render plusIcon({ size: 16 })}
|
|
231
195
|
{/snippet}
|
|
232
196
|
{Number.isFinite(maxFiles)
|
|
233
197
|
? t.addMoreFiles(dropzone.files.length, maxFiles)
|
|
@@ -634,6 +634,7 @@ export declare class FormState<I extends FormInputs = FormInputs> {
|
|
|
634
634
|
items?: import("../Select/select.props.js").SelectItems;
|
|
635
635
|
density?: import("../../../types/theme.js").Density;
|
|
636
636
|
separators?: boolean;
|
|
637
|
+
alignItemWithTrigger?: boolean;
|
|
637
638
|
triggerAttrs?: import("../Select/select.props.js").SelectTriggerAttributes;
|
|
638
639
|
i18n?: Partial<import("../../../i18n/en.js").Messages>;
|
|
639
640
|
}, "visible"> & {
|
|
@@ -1405,6 +1406,7 @@ export declare class FormState<I extends FormInputs = FormInputs> {
|
|
|
1405
1406
|
items?: import("../Select/select.props.js").SelectItems;
|
|
1406
1407
|
density?: import("../../../types/theme.js").Density;
|
|
1407
1408
|
separators?: boolean;
|
|
1409
|
+
alignItemWithTrigger?: boolean;
|
|
1408
1410
|
triggerAttrs?: import("../Select/select.props.js").SelectTriggerAttributes;
|
|
1409
1411
|
i18n?: Partial<import("../../../i18n/en.js").Messages>;
|
|
1410
1412
|
}, "visible"> & {
|
|
@@ -2210,6 +2212,7 @@ export declare class FormState<I extends FormInputs = FormInputs> {
|
|
|
2210
2212
|
items?: import("../Select/select.props.js").SelectItems;
|
|
2211
2213
|
density?: import("../../../types/theme.js").Density;
|
|
2212
2214
|
separators?: boolean;
|
|
2215
|
+
alignItemWithTrigger?: boolean;
|
|
2213
2216
|
triggerAttrs?: import("../Select/select.props.js").SelectTriggerAttributes;
|
|
2214
2217
|
i18n?: Partial<import("../../../i18n/en.js").Messages>;
|
|
2215
2218
|
}, "visible"> & {
|
|
@@ -2981,6 +2984,7 @@ export declare class FormState<I extends FormInputs = FormInputs> {
|
|
|
2981
2984
|
items?: import("../Select/select.props.js").SelectItems;
|
|
2982
2985
|
density?: import("../../../types/theme.js").Density;
|
|
2983
2986
|
separators?: boolean;
|
|
2987
|
+
alignItemWithTrigger?: boolean;
|
|
2984
2988
|
triggerAttrs?: import("../Select/select.props.js").SelectTriggerAttributes;
|
|
2985
2989
|
i18n?: Partial<import("../../../i18n/en.js").Messages>;
|
|
2986
2990
|
}, "visible"> & {
|
|
@@ -3349,6 +3349,7 @@ export declare function prepareInputProps(input: FormFieldInput, size: Sizes, la
|
|
|
3349
3349
|
focused?: boolean | undefined;
|
|
3350
3350
|
i18n?: Partial<import("../../../i18n/en.js").Messages> | undefined;
|
|
3351
3351
|
separators?: boolean | undefined;
|
|
3352
|
+
alignItemWithTrigger?: boolean | undefined;
|
|
3352
3353
|
triggerAttrs?: import("../Select/select.props.js").SelectTriggerAttributes | undefined;
|
|
3353
3354
|
display?: string;
|
|
3354
3355
|
} | {
|
|
@@ -8972,6 +8973,7 @@ export declare function prepareInputProps(input: FormFieldInput, size: Sizes, la
|
|
|
8972
8973
|
focused?: boolean | undefined;
|
|
8973
8974
|
i18n?: Partial<import("../../../i18n/en.js").Messages> | undefined;
|
|
8974
8975
|
separators?: boolean | undefined;
|
|
8976
|
+
alignItemWithTrigger?: boolean | undefined;
|
|
8975
8977
|
triggerAttrs?: import("../Select/select.props.js").SelectTriggerAttributes | undefined;
|
|
8976
8978
|
display?: string;
|
|
8977
8979
|
} | {
|
|
@@ -614,6 +614,7 @@ export declare class MultiStepFormState<I extends MultiStepFormItems = FormStep[
|
|
|
614
614
|
items?: import("../Select/select.props.js").SelectItems;
|
|
615
615
|
density?: import("../../../types/theme.js").Density;
|
|
616
616
|
separators?: boolean;
|
|
617
|
+
alignItemWithTrigger?: boolean;
|
|
617
618
|
triggerAttrs?: import("../Select/select.props.js").SelectTriggerAttributes;
|
|
618
619
|
i18n?: Partial<import("../../../i18n/en.js").Messages>;
|
|
619
620
|
}, "visible"> & {
|
|
@@ -1385,6 +1386,7 @@ export declare class MultiStepFormState<I extends MultiStepFormItems = FormStep[
|
|
|
1385
1386
|
items?: import("../Select/select.props.js").SelectItems;
|
|
1386
1387
|
density?: import("../../../types/theme.js").Density;
|
|
1387
1388
|
separators?: boolean;
|
|
1389
|
+
alignItemWithTrigger?: boolean;
|
|
1388
1390
|
triggerAttrs?: import("../Select/select.props.js").SelectTriggerAttributes;
|
|
1389
1391
|
i18n?: Partial<import("../../../i18n/en.js").Messages>;
|
|
1390
1392
|
}, "visible"> & {
|
|
@@ -2190,6 +2192,7 @@ export declare class MultiStepFormState<I extends MultiStepFormItems = FormStep[
|
|
|
2190
2192
|
items?: import("../Select/select.props.js").SelectItems;
|
|
2191
2193
|
density?: import("../../../types/theme.js").Density;
|
|
2192
2194
|
separators?: boolean;
|
|
2195
|
+
alignItemWithTrigger?: boolean;
|
|
2193
2196
|
triggerAttrs?: import("../Select/select.props.js").SelectTriggerAttributes;
|
|
2194
2197
|
i18n?: Partial<import("../../../i18n/en.js").Messages>;
|
|
2195
2198
|
}, "visible"> & {
|
|
@@ -2961,6 +2964,7 @@ export declare class MultiStepFormState<I extends MultiStepFormItems = FormStep[
|
|
|
2961
2964
|
items?: import("../Select/select.props.js").SelectItems;
|
|
2962
2965
|
density?: import("../../../types/theme.js").Density;
|
|
2963
2966
|
separators?: boolean;
|
|
2967
|
+
alignItemWithTrigger?: boolean;
|
|
2964
2968
|
triggerAttrs?: import("../Select/select.props.js").SelectTriggerAttributes;
|
|
2965
2969
|
i18n?: Partial<import("../../../i18n/en.js").Messages>;
|
|
2966
2970
|
}, "visible"> & {
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
visible,
|
|
31
31
|
items,
|
|
32
32
|
separators = true,
|
|
33
|
+
alignItemWithTrigger = true,
|
|
33
34
|
triggerAttrs,
|
|
34
35
|
label,
|
|
35
36
|
...rest
|
|
@@ -37,6 +38,9 @@
|
|
|
37
38
|
if (value === undefined) value = untrack(() => defaultValue);
|
|
38
39
|
|
|
39
40
|
const id = $props.id();
|
|
41
|
+
let valueEl = $state<HTMLElement | null>(null);
|
|
42
|
+
let listEl = $state<HTMLDivElement | null>(null);
|
|
43
|
+
let viewportEl = $state<HTMLDivElement | null>(null);
|
|
40
44
|
const t = $derived(useI18n(i18n));
|
|
41
45
|
|
|
42
46
|
const field = createFieldState({
|
|
@@ -105,6 +109,18 @@
|
|
|
105
109
|
},
|
|
106
110
|
set triggerEl(_) {
|
|
107
111
|
// field.node is owned by the bind:this below.
|
|
112
|
+
},
|
|
113
|
+
get alignItemWithTrigger() {
|
|
114
|
+
return alignItemWithTrigger;
|
|
115
|
+
},
|
|
116
|
+
get valueEl() {
|
|
117
|
+
return valueEl;
|
|
118
|
+
},
|
|
119
|
+
get listEl() {
|
|
120
|
+
return listEl;
|
|
121
|
+
},
|
|
122
|
+
get viewportEl() {
|
|
123
|
+
return viewportEl;
|
|
108
124
|
}
|
|
109
125
|
});
|
|
110
126
|
|
|
@@ -121,6 +137,7 @@
|
|
|
121
137
|
fitTrigger
|
|
122
138
|
position="bottom"
|
|
123
139
|
ref={field.node?.parentElement}
|
|
140
|
+
positionPanel={select.positionPanel}
|
|
124
141
|
size="small"
|
|
125
142
|
transition={{
|
|
126
143
|
in: { scale: 1, opacity: 0 },
|
|
@@ -143,7 +160,13 @@
|
|
|
143
160
|
event.preventDefault();
|
|
144
161
|
}}
|
|
145
162
|
>
|
|
146
|
-
<ScrollArea
|
|
163
|
+
<ScrollArea
|
|
164
|
+
bind:ref={listEl}
|
|
165
|
+
bind:viewportRef={viewportEl}
|
|
166
|
+
scrollOnEdges
|
|
167
|
+
type="auto"
|
|
168
|
+
class="flex max-h-[240px] flex-col"
|
|
169
|
+
>
|
|
147
170
|
{#each select.renderGroups as group, groupIndex (groupIndex)}
|
|
148
171
|
{#if separators && groupIndex > 0}
|
|
149
172
|
<div role="separator" class={classes.separator({ size })}></div>
|
|
@@ -230,7 +253,10 @@
|
|
|
230
253
|
triggerAttrs?.onblur?.(event);
|
|
231
254
|
}}
|
|
232
255
|
>
|
|
233
|
-
<span
|
|
256
|
+
<span
|
|
257
|
+
bind:this={valueEl}
|
|
258
|
+
class={classes.value({ size, placeholder: !select.selectedOption })}
|
|
259
|
+
>
|
|
234
260
|
{select.selectedOption?.label ?? placeholder ?? t.selectOption}
|
|
235
261
|
</span>
|
|
236
262
|
{@render caretDownIcon({ class: classes.triggerIcon({ size }) })}
|