@selvajs/ui 6.6.0-beta.14 → 6.6.0-beta.16
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 +0 -3
- package/dist/components/compute/AppLayout.svelte +10 -5
- package/dist/components/compute/CollapsedPanelStrip.svelte +1 -1
- package/dist/components/compute/ParameterPresetManager.svelte +1 -1
- package/dist/components/compute/SolveMessages.svelte +2 -2
- package/dist/components/layout/FooterItemRenderer.svelte +1 -1
- package/dist/components/layout/PageFooter.svelte +2 -2
- package/dist/components/layout/PageHeader.svelte +1 -1
- package/dist/components/layout/SideNav.svelte +3 -3
- package/dist/components/layout/SubNav.svelte +2 -2
- package/dist/components/preview/ChartOutput.svelte +3 -3
- package/dist/components/preview/Group.svelte +7 -7
- package/dist/components/preview/ImageOutput.svelte +6 -6
- package/dist/components/preview/InputControl.svelte +1 -0
- package/dist/components/preview/OutputDisplay.svelte +8 -6
- package/dist/components/preview/TabBar.svelte +6 -4
- package/dist/components/preview/TabBar.svelte.d.ts +2 -0
- package/dist/components/preview/TabContent.svelte +1 -1
- package/dist/components/preview/TabLayout.svelte +3 -4
- package/dist/components/preview/inputs/ChecklistInput.svelte +3 -3
- package/dist/components/preview/inputs/ColorInput.svelte +356 -14
- package/dist/components/preview/inputs/ColorInput.svelte.d.ts +1 -0
- package/dist/components/preview/inputs/FileInput.svelte +3 -3
- package/dist/components/preview/inputs/SegmentedInput.svelte +2 -1
- package/dist/components/preview/inputs/TileInput.svelte +3 -2
- package/dist/components/primitives/CalculateButton.svelte +2 -2
- package/dist/components/primitives/Callout.svelte +7 -5
- package/dist/components/primitives/Callout.svelte.d.ts +4 -4
- package/dist/components/primitives/DataTable.svelte +1 -1
- package/dist/components/primitives/ImageUploadField.svelte +1 -1
- package/dist/components/primitives/alert-dialog/alert-dialog-content.svelte +1 -1
- package/dist/components/primitives/badge/badge.svelte +2 -2
- package/dist/components/primitives/badge/badge.svelte.d.ts +6 -6
- package/dist/components/primitives/button/button.svelte +6 -7
- package/dist/components/primitives/button/button.svelte.d.ts +18 -18
- package/dist/components/primitives/card/card.svelte +1 -1
- package/dist/components/primitives/card/card.svelte.d.ts +1 -1
- package/dist/components/primitives/checkbox/checkbox.svelte +1 -1
- package/dist/components/primitives/command/command-input.svelte +1 -1
- package/dist/components/primitives/command/command-item.svelte +1 -1
- package/dist/components/primitives/command/command-link-item.svelte +1 -1
- package/dist/components/primitives/command/command.svelte +1 -1
- package/dist/components/primitives/context-menu/context-menu-checkbox-item.svelte +1 -1
- package/dist/components/primitives/context-menu/context-menu-content.svelte +1 -1
- package/dist/components/primitives/context-menu/context-menu-item.svelte +1 -1
- package/dist/components/primitives/context-menu/context-menu-radio-item.svelte +1 -1
- package/dist/components/primitives/context-menu/context-menu-sub-content.svelte +1 -1
- package/dist/components/primitives/context-menu/context-menu-sub-trigger.svelte +1 -1
- package/dist/components/primitives/dialog/dialog-content.svelte +2 -2
- package/dist/components/primitives/drawer/Drawer.svelte +1 -1
- package/dist/components/primitives/dropdown-menu/dropdown-menu-checkbox-item.svelte +31 -11
- package/dist/components/primitives/dropdown-menu/dropdown-menu-checkbox-item.svelte.d.ts +2 -0
- package/dist/components/primitives/dropdown-menu/dropdown-menu-content.svelte +5 -1
- package/dist/components/primitives/dropdown-menu/dropdown-menu-content.svelte.d.ts +2 -0
- package/dist/components/primitives/dropdown-menu/dropdown-menu-item.svelte +4 -2
- package/dist/components/primitives/dropdown-menu/dropdown-menu-item.svelte.d.ts +2 -1
- package/dist/components/primitives/dropdown-menu/dropdown-menu-radio-item.svelte +1 -1
- package/dist/components/primitives/dropdown-menu/dropdown-menu-shortcut.svelte +1 -1
- package/dist/components/primitives/dropdown-menu/dropdown-menu-sub-content.svelte +1 -1
- package/dist/components/primitives/dropdown-menu/dropdown-menu-sub-trigger.svelte +1 -1
- package/dist/components/primitives/index.d.ts +1 -1
- package/dist/components/primitives/index.js +1 -1
- package/dist/components/primitives/input/input.svelte +2 -2
- package/dist/components/primitives/input-group/input-group.svelte +1 -1
- package/dist/components/primitives/popover/popover-content.svelte +1 -1
- package/dist/components/primitives/resizable/resizable-handle.svelte +2 -2
- package/dist/components/primitives/search/search.svelte +1 -1
- package/dist/components/primitives/select/select-content.svelte +1 -1
- package/dist/components/primitives/select/select-item.svelte +1 -1
- package/dist/components/primitives/select/select-trigger.svelte +1 -1
- package/dist/components/primitives/slider/slider.svelte +1 -1
- package/dist/components/primitives/switch/switch.svelte +1 -1
- package/dist/components/primitives/tabs/tabs-list.svelte +1 -1
- package/dist/components/primitives/tabs/tabs-list.svelte.d.ts +3 -3
- package/dist/components/primitives/tabs/tabs-trigger.svelte +1 -1
- package/dist/components/primitives/tabs/tabs-trigger.svelte.d.ts +3 -3
- package/dist/components/primitives/textarea/textarea.svelte +1 -1
- package/dist/components/primitives/toggle/toggle.svelte +8 -2
- package/dist/components/primitives/toggle/toggle.svelte.d.ts +10 -4
- package/dist/components/primitives/toggle-group/toggle-group-item.svelte +1 -1
- package/dist/components/primitives/toggle-group/toggle-group.svelte +1 -1
- package/dist/components/primitives/tooltip/index.d.ts +6 -0
- package/dist/components/primitives/tooltip/index.js +8 -0
- package/dist/components/primitives/tooltip/tooltip-content.svelte +34 -0
- package/dist/components/primitives/tooltip/tooltip-content.svelte.d.ts +10 -0
- package/dist/components/primitives/tooltip/tooltip-portal.svelte +7 -0
- package/dist/components/primitives/tooltip/tooltip-portal.svelte.d.ts +4 -0
- package/dist/components/primitives/tooltip/tooltip-provider.svelte +7 -0
- package/dist/components/primitives/tooltip/tooltip-provider.svelte.d.ts +4 -0
- package/dist/components/primitives/tooltip/tooltip-trigger.svelte +7 -0
- package/dist/components/primitives/tooltip/tooltip-trigger.svelte.d.ts +4 -0
- package/dist/components/primitives/tooltip/tooltip.svelte +7 -0
- package/dist/components/primitives/tooltip/tooltip.svelte.d.ts +4 -0
- package/dist/components/viewer/MeshMetadataPanel.svelte +1 -1
- package/dist/components/viewer/SceneManager.svelte +19 -19
- package/dist/components/viewer/Viewer.svelte +90 -164
- package/dist/components/viewer/Viewer.svelte.d.ts +3 -1
- package/dist/components/viewer/ViewerDock.svelte +404 -0
- package/dist/components/viewer/ViewerDock.svelte.d.ts +42 -0
- package/dist/i18n/messages.d.ts +12 -0
- package/dist/i18n/messages.js +20 -0
- package/dist/styles/base.css +81 -80
- package/dist/utils/color-space.d.ts +21 -0
- package/dist/utils/color-space.js +90 -0
- package/package.json +12 -13
- package/src/lib/components/compute/AppLayout.svelte +10 -5
- package/src/lib/components/compute/CollapsedPanelStrip.svelte +1 -1
- package/src/lib/components/compute/ParameterPresetManager.svelte +1 -1
- package/src/lib/components/compute/SolveMessages.svelte +2 -2
- package/src/lib/components/layout/FooterItemRenderer.svelte +1 -1
- package/src/lib/components/layout/PageFooter.svelte +2 -2
- package/src/lib/components/layout/PageHeader.svelte +1 -1
- package/src/lib/components/layout/SideNav.svelte +3 -3
- package/src/lib/components/layout/SubNav.svelte +2 -2
- package/src/lib/components/preview/ChartOutput.svelte +3 -3
- package/src/lib/components/preview/Group.svelte +7 -7
- package/src/lib/components/preview/ImageOutput.svelte +6 -6
- package/src/lib/components/preview/InputControl.svelte +1 -0
- package/src/lib/components/preview/OutputDisplay.svelte +8 -6
- package/src/lib/components/preview/TabBar.svelte +6 -4
- package/src/lib/components/preview/TabContent.svelte +1 -1
- package/src/lib/components/preview/TabLayout.svelte +3 -4
- package/src/lib/components/preview/inputs/ChecklistInput.svelte +3 -3
- package/src/lib/components/preview/inputs/ColorInput.svelte +356 -14
- package/src/lib/components/preview/inputs/FileInput.svelte +3 -3
- package/src/lib/components/preview/inputs/SegmentedInput.svelte +2 -1
- package/src/lib/components/preview/inputs/TileInput.svelte +3 -2
- package/src/lib/components/primitives/CalculateButton.svelte +2 -2
- package/src/lib/components/primitives/Callout.svelte +7 -5
- package/src/lib/components/primitives/DataTable.svelte +1 -1
- package/src/lib/components/primitives/ImageUploadField.svelte +1 -1
- package/src/lib/components/primitives/alert-dialog/alert-dialog-content.svelte +1 -1
- package/src/lib/components/primitives/badge/badge.svelte +2 -2
- package/src/lib/components/primitives/button/button.svelte +6 -7
- package/src/lib/components/primitives/card/card.svelte +1 -1
- package/src/lib/components/primitives/checkbox/checkbox.svelte +1 -1
- package/src/lib/components/primitives/command/command-input.svelte +1 -1
- package/src/lib/components/primitives/command/command-item.svelte +1 -1
- package/src/lib/components/primitives/command/command-link-item.svelte +1 -1
- package/src/lib/components/primitives/command/command.svelte +1 -1
- package/src/lib/components/primitives/context-menu/context-menu-checkbox-item.svelte +1 -1
- package/src/lib/components/primitives/context-menu/context-menu-content.svelte +1 -1
- package/src/lib/components/primitives/context-menu/context-menu-item.svelte +1 -1
- package/src/lib/components/primitives/context-menu/context-menu-radio-item.svelte +1 -1
- package/src/lib/components/primitives/context-menu/context-menu-sub-content.svelte +1 -1
- package/src/lib/components/primitives/context-menu/context-menu-sub-trigger.svelte +1 -1
- package/src/lib/components/primitives/dialog/dialog-content.svelte +2 -2
- package/src/lib/components/primitives/drawer/Drawer.svelte +1 -1
- package/src/lib/components/primitives/dropdown-menu/dropdown-menu-checkbox-item.svelte +31 -11
- package/src/lib/components/primitives/dropdown-menu/dropdown-menu-content.svelte +5 -1
- package/src/lib/components/primitives/dropdown-menu/dropdown-menu-item.svelte +4 -2
- package/src/lib/components/primitives/dropdown-menu/dropdown-menu-radio-item.svelte +1 -1
- package/src/lib/components/primitives/dropdown-menu/dropdown-menu-shortcut.svelte +1 -1
- package/src/lib/components/primitives/dropdown-menu/dropdown-menu-sub-content.svelte +1 -1
- package/src/lib/components/primitives/dropdown-menu/dropdown-menu-sub-trigger.svelte +1 -1
- package/src/lib/components/primitives/index.ts +1 -1
- package/src/lib/components/primitives/input/input.svelte +2 -2
- package/src/lib/components/primitives/input-group/input-group.svelte +1 -1
- package/src/lib/components/primitives/popover/popover-content.svelte +1 -1
- package/src/lib/components/primitives/resizable/resizable-handle.svelte +2 -2
- package/src/lib/components/primitives/search/search.svelte +1 -1
- package/src/lib/components/primitives/select/select-content.svelte +1 -1
- package/src/lib/components/primitives/select/select-item.svelte +1 -1
- package/src/lib/components/primitives/select/select-trigger.svelte +1 -1
- package/src/lib/components/primitives/slider/slider.svelte +1 -1
- package/src/lib/components/primitives/switch/switch.svelte +1 -1
- package/src/lib/components/primitives/tabs/tabs-list.svelte +1 -1
- package/src/lib/components/primitives/tabs/tabs-trigger.svelte +1 -1
- package/src/lib/components/primitives/textarea/textarea.svelte +1 -1
- package/src/lib/components/primitives/toggle/toggle.svelte +8 -2
- package/src/lib/components/primitives/toggle-group/toggle-group-item.svelte +1 -1
- package/src/lib/components/primitives/toggle-group/toggle-group.svelte +1 -1
- package/src/lib/components/primitives/tooltip/index.ts +19 -0
- package/src/lib/components/primitives/tooltip/tooltip-content.svelte +34 -0
- package/src/lib/components/primitives/tooltip/tooltip-portal.svelte +7 -0
- package/src/lib/components/primitives/tooltip/tooltip-provider.svelte +7 -0
- package/src/lib/components/primitives/tooltip/tooltip-trigger.svelte +7 -0
- package/src/lib/components/primitives/tooltip/tooltip.svelte +7 -0
- package/src/lib/components/viewer/MeshMetadataPanel.svelte +1 -1
- package/src/lib/components/viewer/SceneManager.svelte +19 -19
- package/src/lib/components/viewer/Viewer.svelte +90 -164
- package/src/lib/components/viewer/ViewerDock.svelte +404 -0
- package/src/lib/i18n/messages.ts +32 -0
- package/src/lib/styles/base.css +81 -80
- package/src/lib/utils/color-space.ts +107 -0
- package/dist/components/primitives/theme-switcher/ThemeSwitcher.svelte +0 -39
- package/dist/components/primitives/theme-switcher/ThemeSwitcher.svelte.d.ts +0 -3
- package/dist/components/primitives/theme-switcher/index.d.ts +0 -1
- package/dist/components/primitives/theme-switcher/index.js +0 -1
- package/dist/styles/themes/cyberpunk.css +0 -98
- package/dist/styles/themes/neutral.css +0 -72
- package/dist/styles/themes/ocean.css +0 -75
- package/dist/styles/themes/selva.css +0 -105
- package/dist/theme/themeStore.svelte.d.ts +0 -12
- package/dist/theme/themeStore.svelte.js +0 -40
- package/dist/theme/themes.d.ts +0 -5
- package/dist/theme/themes.js +0 -13
- package/src/lib/components/primitives/theme-switcher/ThemeSwitcher.svelte +0 -39
- package/src/lib/components/primitives/theme-switcher/index.ts +0 -1
- package/src/lib/styles/themes/cyberpunk.css +0 -98
- package/src/lib/styles/themes/neutral.css +0 -72
- package/src/lib/styles/themes/ocean.css +0 -75
- package/src/lib/styles/themes/selva.css +0 -105
- package/src/lib/theme/themeStore.svelte.ts +0 -50
- package/src/lib/theme/themes.ts +0 -18
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { MediaQuery } from 'svelte/reactivity';
|
|
3
|
+
import {
|
|
4
|
+
Camera,
|
|
5
|
+
Grid3x3,
|
|
6
|
+
Layers,
|
|
7
|
+
LocateFixed,
|
|
8
|
+
Maximize,
|
|
9
|
+
Minimize,
|
|
10
|
+
Moon,
|
|
11
|
+
Ellipsis,
|
|
12
|
+
Palette,
|
|
13
|
+
Ruler,
|
|
14
|
+
Settings2,
|
|
15
|
+
Spline,
|
|
16
|
+
Cuboid
|
|
17
|
+
} from '@lucide/svelte';
|
|
18
|
+
import type { CameraProjection, Look, ViewPreset } from '@selvajs/visualization/render';
|
|
19
|
+
import * as DropdownMenu from '$lib/components/primitives/dropdown-menu/index.js';
|
|
20
|
+
import type { ViewerMessages } from '$lib/i18n/messages';
|
|
21
|
+
|
|
22
|
+
interface Props {
|
|
23
|
+
t: ViewerMessages;
|
|
24
|
+
/** Fades the dock out while a host drawer covers the viewer. */
|
|
25
|
+
hidden?: boolean;
|
|
26
|
+
showDisplay: boolean;
|
|
27
|
+
showGrid: boolean;
|
|
28
|
+
showSceneManager: boolean;
|
|
29
|
+
showScreenshot: boolean;
|
|
30
|
+
showFullscreen: boolean;
|
|
31
|
+
styleOptions: { look: Look; label: string }[];
|
|
32
|
+
viewPresets: { preset: ViewPreset; label: string }[];
|
|
33
|
+
renderStyle: Look;
|
|
34
|
+
nightOn: boolean;
|
|
35
|
+
edgesVisible: boolean;
|
|
36
|
+
measureActive: boolean;
|
|
37
|
+
gridVisible: boolean;
|
|
38
|
+
sceneManagerOpen: boolean;
|
|
39
|
+
projection: CameraProjection;
|
|
40
|
+
isFullscreen: boolean;
|
|
41
|
+
onSetLook: (look: Look) => void;
|
|
42
|
+
onToggleNight: () => void;
|
|
43
|
+
onToggleEdges: () => void;
|
|
44
|
+
onToggleMeasure: () => void;
|
|
45
|
+
onToggleGrid: () => void;
|
|
46
|
+
onToggleSceneManager: () => void;
|
|
47
|
+
onToggleProjection: () => void;
|
|
48
|
+
onSetView: (preset: ViewPreset) => void;
|
|
49
|
+
onFit: () => void;
|
|
50
|
+
onScreenshot: () => void;
|
|
51
|
+
onToggleFullscreen: () => void;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let {
|
|
55
|
+
t,
|
|
56
|
+
hidden = false,
|
|
57
|
+
showDisplay,
|
|
58
|
+
showGrid,
|
|
59
|
+
showSceneManager,
|
|
60
|
+
showScreenshot,
|
|
61
|
+
showFullscreen,
|
|
62
|
+
styleOptions,
|
|
63
|
+
viewPresets,
|
|
64
|
+
renderStyle,
|
|
65
|
+
nightOn,
|
|
66
|
+
edgesVisible,
|
|
67
|
+
measureActive,
|
|
68
|
+
gridVisible,
|
|
69
|
+
sceneManagerOpen,
|
|
70
|
+
projection,
|
|
71
|
+
isFullscreen,
|
|
72
|
+
onSetLook,
|
|
73
|
+
onToggleNight,
|
|
74
|
+
onToggleEdges,
|
|
75
|
+
onToggleMeasure,
|
|
76
|
+
onToggleGrid,
|
|
77
|
+
onToggleSceneManager,
|
|
78
|
+
onToggleProjection,
|
|
79
|
+
onSetView,
|
|
80
|
+
onFit,
|
|
81
|
+
onScreenshot,
|
|
82
|
+
onToggleFullscreen
|
|
83
|
+
}: Props = $props();
|
|
84
|
+
|
|
85
|
+
// A hairline pill at the bottom edge is the iOS home indicator on a phone, and swiping it leaves
|
|
86
|
+
// the app. Touch gets a visible round button instead.
|
|
87
|
+
const touch = new MediaQuery('(pointer: coarse)');
|
|
88
|
+
const reducedMotion = new MediaQuery('(prefers-reduced-motion: reduce)');
|
|
89
|
+
|
|
90
|
+
const COLLAPSED = $derived(touch.current ? { w: 44, h: 44 } : { w: 72, h: 8 });
|
|
91
|
+
const EXPANDED_H = $derived(touch.current ? 54 : 46);
|
|
92
|
+
const PAD = 4;
|
|
93
|
+
|
|
94
|
+
let dockEl: HTMLDivElement | undefined = $state();
|
|
95
|
+
let rowWidth = $state(0);
|
|
96
|
+
let engaged = $state(false);
|
|
97
|
+
// Set by an outside press: the dock was dismissed, so it shrinks faster.
|
|
98
|
+
let dismissed = $state(false);
|
|
99
|
+
let menusOpen = $state(0);
|
|
100
|
+
let focusWithin = $state(false);
|
|
101
|
+
let idleTimer: ReturnType<typeof setTimeout> | undefined;
|
|
102
|
+
|
|
103
|
+
const wantOpen = $derived(engaged || menusOpen > 0 || focusWithin);
|
|
104
|
+
|
|
105
|
+
// Opening leads: the pill grows first and the buttons pop in behind it. Closing mirrors it: the
|
|
106
|
+
// buttons fade first, then the pill shrinks. Without the delay on close the row is clipped mid-fade.
|
|
107
|
+
const EASE = 'cubic-bezier(0.32, 0.72, 0, 1)';
|
|
108
|
+
const containerStyle = $derived.by(() => {
|
|
109
|
+
const bottom = touch.current ? 'max(1rem,env(safe-area-inset-bottom))' : '0.75rem';
|
|
110
|
+
const geometry = wantOpen
|
|
111
|
+
? `width:${rowWidth + PAD * 2 + 2}px;height:${EXPANDED_H}px;border-radius:14px;padding:${PAD}px`
|
|
112
|
+
: `width:${COLLAPSED.w}px;height:${COLLAPSED.h}px;border-radius:${touch.current ? 22 : 99}px;padding:0px`;
|
|
113
|
+
const size = `bottom:${bottom};${geometry}`;
|
|
114
|
+
if (reducedMotion.current) return size;
|
|
115
|
+
const ms = wantOpen ? 420 : dismissed ? 130 : 170;
|
|
116
|
+
const delay = wantOpen || dismissed ? 0 : 20;
|
|
117
|
+
const props = ['width', 'height', 'border-radius', 'padding'];
|
|
118
|
+
return `${size};transition:${props.map((p) => `${p} ${ms}ms ${EASE} ${delay}ms`).join(',')},opacity 200ms ease`;
|
|
119
|
+
});
|
|
120
|
+
const itemStyle = (index: number) => {
|
|
121
|
+
if (reducedMotion.current) return `opacity:${wantOpen ? 1 : 0}`;
|
|
122
|
+
const delay = 110 + index * 35;
|
|
123
|
+
return wantOpen
|
|
124
|
+
? `opacity:1;transform:none;transition:opacity 240ms ease ${delay}ms,transform 420ms cubic-bezier(0.34,1.45,0.5,1) ${delay}ms`
|
|
125
|
+
: 'opacity:0;transform:translateY(6px) scale(0.85);transition:opacity 100ms ease,transform 100ms ease';
|
|
126
|
+
};
|
|
127
|
+
// The touch button's settings glyph hands over to the row.
|
|
128
|
+
const glyphStyle = $derived(
|
|
129
|
+
`opacity:${touch.current && !wantOpen ? 1 : 0};${reducedMotion.current ? '' : `transition:opacity ${wantOpen ? 100 : 100}ms ease ${wantOpen || dismissed ? 0 : 60}ms`}`
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
function scheduleCollapse(delay = touch.current ? 3500 : 150) {
|
|
133
|
+
clearTimeout(idleTimer);
|
|
134
|
+
idleTimer = setTimeout(() => (engaged = false), delay);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function engage() {
|
|
138
|
+
engaged = true;
|
|
139
|
+
dismissed = false;
|
|
140
|
+
clearTimeout(idleTimer);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function handlePointerMove(event: PointerEvent) {
|
|
144
|
+
if (!dockEl || touch.current || event.pointerType === 'touch') return;
|
|
145
|
+
// Orbiting or box-dragging with the button down should never pop the dock open.
|
|
146
|
+
if (event.buttons) return;
|
|
147
|
+
const host = dockEl.offsetParent;
|
|
148
|
+
if (!host || !(event.target instanceof Node) || !host.contains(event.target)) {
|
|
149
|
+
if (engaged) scheduleCollapse();
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const r = dockEl.getBoundingClientRect();
|
|
153
|
+
const near =
|
|
154
|
+
event.clientY > r.top - 36 && Math.abs(event.clientX - (r.left + r.width / 2)) < 110;
|
|
155
|
+
if (near) engage();
|
|
156
|
+
else if (engaged) scheduleCollapse();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function handleWindowPointerDown(event: PointerEvent) {
|
|
160
|
+
if (!engaged || !dockEl || !(event.target instanceof Node)) return;
|
|
161
|
+
if (!dockEl.contains(event.target)) {
|
|
162
|
+
clearTimeout(idleTimer);
|
|
163
|
+
dismissed = true;
|
|
164
|
+
engaged = false;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function openFromCollapsed() {
|
|
169
|
+
if (wantOpen) return;
|
|
170
|
+
engage();
|
|
171
|
+
if (touch.current) scheduleCollapse();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Any use of the open dock on touch pushes the idle collapse back.
|
|
175
|
+
function keepAlive() {
|
|
176
|
+
if (touch.current && engaged) scheduleCollapse();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function trackMenu(open: boolean) {
|
|
180
|
+
menusOpen += open ? 1 : -1;
|
|
181
|
+
if (!open && !touch.current) scheduleCollapse(250);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Buttons in the row, in order, so the stagger index stays right when some are hidden.
|
|
185
|
+
const buttons = $derived(
|
|
186
|
+
[
|
|
187
|
+
showDisplay && 'look',
|
|
188
|
+
showSceneManager && 'scene',
|
|
189
|
+
'measure',
|
|
190
|
+
'fit',
|
|
191
|
+
showFullscreen && 'full',
|
|
192
|
+
'more'
|
|
193
|
+
].filter(Boolean) as string[]
|
|
194
|
+
);
|
|
195
|
+
const idx = (id: string) => buttons.indexOf(id);
|
|
196
|
+
|
|
197
|
+
const BTN =
|
|
198
|
+
'relative grid size-9.5 shrink-0 place-items-center rounded-lg text-muted-foreground outline-none transition-colors hover:bg-hover hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring aria-pressed:text-foreground data-[state=open]:bg-selected data-[state=open]:text-foreground pointer-coarse:size-11';
|
|
199
|
+
const DOT =
|
|
200
|
+
'aria-pressed:after:absolute aria-pressed:after:bottom-0.5 aria-pressed:after:size-1 aria-pressed:after:rounded-full aria-pressed:after:bg-primary';
|
|
201
|
+
</script>
|
|
202
|
+
|
|
203
|
+
<!-- eslint-disable shadcn/no-inline-styles -- the pill animates between measured pixel sizes and staggered per-item delays; no class can express those -->
|
|
204
|
+
|
|
205
|
+
<svelte:window onpointermove={handlePointerMove} onpointerdown={handleWindowPointerDown} />
|
|
206
|
+
|
|
207
|
+
<!-- Resting state is a pill; it grows into the row. Only the container is clickable while collapsed. -->
|
|
208
|
+
<div
|
|
209
|
+
bind:this={dockEl}
|
|
210
|
+
role="toolbar"
|
|
211
|
+
aria-label={t.toolsMenu}
|
|
212
|
+
tabindex="-1"
|
|
213
|
+
class="shadow-lg backdrop-blur-md absolute left-1/2 z-50 -translate-x-1/2 overflow-hidden border {hidden
|
|
214
|
+
? 'pointer-events-none opacity-0'
|
|
215
|
+
: ''} {wantOpen
|
|
216
|
+
? 'bg-background/70'
|
|
217
|
+
: touch.current
|
|
218
|
+
? 'cursor-pointer border-border bg-background/90'
|
|
219
|
+
: nightOn
|
|
220
|
+
? 'border-white/50 bg-white/40 hover:bg-white/65 cursor-pointer'
|
|
221
|
+
: 'cursor-pointer border-border-strong bg-faint-foreground hover:bg-muted-foreground'}"
|
|
222
|
+
style={containerStyle}
|
|
223
|
+
onclick={openFromCollapsed}
|
|
224
|
+
onkeydown={(e) => (e.key === 'Escape' ? (engaged = false) : undefined)}
|
|
225
|
+
onfocusin={() => (focusWithin = true)}
|
|
226
|
+
onfocusout={(e) => {
|
|
227
|
+
if (!dockEl?.contains(e.relatedTarget as Node | null)) focusWithin = false;
|
|
228
|
+
}}
|
|
229
|
+
onpointerdown={keepAlive}
|
|
230
|
+
>
|
|
231
|
+
{#if touch.current}
|
|
232
|
+
<span
|
|
233
|
+
class="inset-0 pointer-events-none absolute grid place-items-center text-foreground"
|
|
234
|
+
style={glyphStyle}
|
|
235
|
+
aria-hidden="true"
|
|
236
|
+
>
|
|
237
|
+
<Settings2 class="size-5" />
|
|
238
|
+
</span>
|
|
239
|
+
{/if}
|
|
240
|
+
|
|
241
|
+
<div
|
|
242
|
+
bind:offsetWidth={rowWidth}
|
|
243
|
+
class="gap-0.5 mx-auto flex w-max items-center"
|
|
244
|
+
inert={!wantOpen}
|
|
245
|
+
>
|
|
246
|
+
{#if showDisplay}
|
|
247
|
+
<div style={itemStyle(idx('look'))}>
|
|
248
|
+
<DropdownMenu.Root onOpenChange={trackMenu}>
|
|
249
|
+
<DropdownMenu.Trigger>
|
|
250
|
+
{#snippet child({ props })}
|
|
251
|
+
<button {...props} class={BTN} title={t.display} aria-label={t.display}>
|
|
252
|
+
<Palette class="size-4.5" />
|
|
253
|
+
</button>
|
|
254
|
+
{/snippet}
|
|
255
|
+
</DropdownMenu.Trigger>
|
|
256
|
+
<DropdownMenu.Content
|
|
257
|
+
side="top"
|
|
258
|
+
align="center"
|
|
259
|
+
sideOffset={12}
|
|
260
|
+
variant="glass"
|
|
261
|
+
class="min-w-52 z-10001 w-auto whitespace-nowrap"
|
|
262
|
+
>
|
|
263
|
+
<DropdownMenu.RadioGroup
|
|
264
|
+
value={renderStyle}
|
|
265
|
+
onValueChange={(look) => onSetLook(look as Look)}
|
|
266
|
+
>
|
|
267
|
+
{#each styleOptions as { look, label } (look)}
|
|
268
|
+
<DropdownMenu.RadioItem value={look} closeOnSelect={false}>
|
|
269
|
+
{label}
|
|
270
|
+
</DropdownMenu.RadioItem>
|
|
271
|
+
{/each}
|
|
272
|
+
</DropdownMenu.RadioGroup>
|
|
273
|
+
<DropdownMenu.Separator />
|
|
274
|
+
<DropdownMenu.CheckboxItem
|
|
275
|
+
switch
|
|
276
|
+
checked={edgesVisible}
|
|
277
|
+
onCheckedChange={onToggleEdges}
|
|
278
|
+
>
|
|
279
|
+
<Spline />
|
|
280
|
+
{t.edges}
|
|
281
|
+
<DropdownMenu.Shortcut class="pointer-coarse:hidden">E</DropdownMenu.Shortcut>
|
|
282
|
+
</DropdownMenu.CheckboxItem>
|
|
283
|
+
</DropdownMenu.Content>
|
|
284
|
+
</DropdownMenu.Root>
|
|
285
|
+
</div>
|
|
286
|
+
{/if}
|
|
287
|
+
|
|
288
|
+
{#if showSceneManager}
|
|
289
|
+
<div style={itemStyle(idx('scene'))}>
|
|
290
|
+
<button
|
|
291
|
+
class="{BTN} {DOT}"
|
|
292
|
+
title="{t.sceneManager} (S)"
|
|
293
|
+
aria-label={t.sceneManager}
|
|
294
|
+
aria-pressed={sceneManagerOpen}
|
|
295
|
+
onclick={onToggleSceneManager}
|
|
296
|
+
>
|
|
297
|
+
<Layers class="size-4.5" />
|
|
298
|
+
</button>
|
|
299
|
+
</div>
|
|
300
|
+
{/if}
|
|
301
|
+
|
|
302
|
+
<div style={itemStyle(idx('measure'))}>
|
|
303
|
+
<button
|
|
304
|
+
class="{BTN} {DOT}"
|
|
305
|
+
title="{t.measure} (M)"
|
|
306
|
+
aria-label={t.measure}
|
|
307
|
+
aria-pressed={measureActive}
|
|
308
|
+
onclick={onToggleMeasure}
|
|
309
|
+
>
|
|
310
|
+
<Ruler class="size-4.5" />
|
|
311
|
+
</button>
|
|
312
|
+
</div>
|
|
313
|
+
|
|
314
|
+
<div style={itemStyle(idx('fit'))}>
|
|
315
|
+
<button class={BTN} title="{t.fitToView} (F)" aria-label="{t.fitToView} (F)" onclick={onFit}>
|
|
316
|
+
<LocateFixed class="size-4.5" />
|
|
317
|
+
</button>
|
|
318
|
+
</div>
|
|
319
|
+
|
|
320
|
+
{#if showFullscreen}
|
|
321
|
+
<div style={itemStyle(idx('full'))}>
|
|
322
|
+
<button
|
|
323
|
+
class={BTN}
|
|
324
|
+
title={isFullscreen ? t.exitFullscreen : t.fullscreen}
|
|
325
|
+
aria-label={isFullscreen ? t.exitFullscreen : t.fullscreen}
|
|
326
|
+
onclick={onToggleFullscreen}
|
|
327
|
+
>
|
|
328
|
+
{#if isFullscreen}<Minimize class="size-4.5" />{:else}<Maximize class="size-4.5" />{/if}
|
|
329
|
+
</button>
|
|
330
|
+
</div>
|
|
331
|
+
{/if}
|
|
332
|
+
|
|
333
|
+
<div style={itemStyle(idx('more'))}>
|
|
334
|
+
<DropdownMenu.Root onOpenChange={trackMenu}>
|
|
335
|
+
<DropdownMenu.Trigger>
|
|
336
|
+
{#snippet child({ props })}
|
|
337
|
+
<button {...props} class={BTN} title={t.toolsMenu} aria-label={t.toolsMenu}>
|
|
338
|
+
<Ellipsis class="size-4.5" />
|
|
339
|
+
</button>
|
|
340
|
+
{/snippet}
|
|
341
|
+
</DropdownMenu.Trigger>
|
|
342
|
+
<DropdownMenu.Content
|
|
343
|
+
side="top"
|
|
344
|
+
align="end"
|
|
345
|
+
sideOffset={12}
|
|
346
|
+
variant="glass"
|
|
347
|
+
class="min-w-60 z-10001 w-auto whitespace-nowrap"
|
|
348
|
+
>
|
|
349
|
+
<DropdownMenu.Label>{t.views}</DropdownMenu.Label>
|
|
350
|
+
<div class="gap-1 px-1 pb-1 grid grid-cols-4">
|
|
351
|
+
{#each viewPresets as { preset, label } (preset)}
|
|
352
|
+
<DropdownMenu.Item
|
|
353
|
+
closeOnSelect={false}
|
|
354
|
+
onSelect={() => onSetView(preset)}
|
|
355
|
+
variant="chip"
|
|
356
|
+
>
|
|
357
|
+
{label}
|
|
358
|
+
</DropdownMenu.Item>
|
|
359
|
+
{/each}
|
|
360
|
+
</div>
|
|
361
|
+
|
|
362
|
+
<DropdownMenu.Separator />
|
|
363
|
+
|
|
364
|
+
<DropdownMenu.CheckboxItem
|
|
365
|
+
switch
|
|
366
|
+
checked={projection === 'orthographic'}
|
|
367
|
+
onCheckedChange={onToggleProjection}
|
|
368
|
+
>
|
|
369
|
+
<Cuboid />
|
|
370
|
+
{t.switchToOrthographic}
|
|
371
|
+
<DropdownMenu.Shortcut class="pointer-coarse:hidden">O</DropdownMenu.Shortcut>
|
|
372
|
+
</DropdownMenu.CheckboxItem>
|
|
373
|
+
|
|
374
|
+
{#if showGrid}
|
|
375
|
+
<DropdownMenu.CheckboxItem switch checked={gridVisible} onCheckedChange={onToggleGrid}>
|
|
376
|
+
<Grid3x3 />
|
|
377
|
+
{t.grid}
|
|
378
|
+
<DropdownMenu.Shortcut class="pointer-coarse:hidden">G</DropdownMenu.Shortcut>
|
|
379
|
+
</DropdownMenu.CheckboxItem>
|
|
380
|
+
{/if}
|
|
381
|
+
|
|
382
|
+
{#if showDisplay}
|
|
383
|
+
<DropdownMenu.CheckboxItem switch checked={nightOn} onCheckedChange={onToggleNight}>
|
|
384
|
+
<Moon />
|
|
385
|
+
{t.nightMode}
|
|
386
|
+
<DropdownMenu.Shortcut class="pointer-coarse:hidden">N</DropdownMenu.Shortcut>
|
|
387
|
+
</DropdownMenu.CheckboxItem>
|
|
388
|
+
{/if}
|
|
389
|
+
|
|
390
|
+
{#if showScreenshot}
|
|
391
|
+
<DropdownMenu.Separator />
|
|
392
|
+
{/if}
|
|
393
|
+
|
|
394
|
+
{#if showScreenshot}
|
|
395
|
+
<DropdownMenu.Item onSelect={onScreenshot}>
|
|
396
|
+
<Camera />
|
|
397
|
+
{t.screenshot}
|
|
398
|
+
</DropdownMenu.Item>
|
|
399
|
+
{/if}
|
|
400
|
+
</DropdownMenu.Content>
|
|
401
|
+
</DropdownMenu.Root>
|
|
402
|
+
</div>
|
|
403
|
+
</div>
|
|
404
|
+
</div>
|
package/src/lib/i18n/messages.ts
CHANGED
|
@@ -40,6 +40,8 @@ export interface ViewerMessages {
|
|
|
40
40
|
lookTechnical: string;
|
|
41
41
|
lookStudio: string;
|
|
42
42
|
lookShowcase: string;
|
|
43
|
+
lookNight: string;
|
|
44
|
+
nightMode: string;
|
|
43
45
|
lookArctic: string;
|
|
44
46
|
lookXray: string;
|
|
45
47
|
lookLineart: string;
|
|
@@ -131,6 +133,16 @@ export interface ViewerMessages {
|
|
|
131
133
|
/** `{max}` is replaced with the field's character limit. */
|
|
132
134
|
maxLength: string;
|
|
133
135
|
invalidFormat: string;
|
|
136
|
+
/** Trigger label; `{color}` is the current hex value. */
|
|
137
|
+
colorPick: string;
|
|
138
|
+
colorArea: string;
|
|
139
|
+
/** `{s}` and `{v}` are percentages. */
|
|
140
|
+
colorAreaValue: string;
|
|
141
|
+
colorHue: string;
|
|
142
|
+
colorHex: string;
|
|
143
|
+
colorHexInvalid: string;
|
|
144
|
+
colorPresets: string;
|
|
145
|
+
colorEyedropper: string;
|
|
134
146
|
invalidPattern: string;
|
|
135
147
|
|
|
136
148
|
// Geometry input (Rhino only)
|
|
@@ -268,6 +280,8 @@ const en: ViewerMessages = {
|
|
|
268
280
|
lookTechnical: 'Technical',
|
|
269
281
|
lookStudio: 'Studio',
|
|
270
282
|
lookShowcase: 'Showcase',
|
|
283
|
+
lookNight: 'Night',
|
|
284
|
+
nightMode: 'Night mode',
|
|
271
285
|
lookArctic: 'Arctic',
|
|
272
286
|
lookXray: 'X-Ray',
|
|
273
287
|
lookLineart: 'Line Art',
|
|
@@ -340,6 +354,14 @@ const en: ViewerMessages = {
|
|
|
340
354
|
fineDragHint: 'Hold Shift to drag finely',
|
|
341
355
|
maxLength: 'Maximum {max} characters allowed',
|
|
342
356
|
invalidFormat: 'Invalid format',
|
|
357
|
+
colorPick: 'Choose color, currently {color}',
|
|
358
|
+
colorArea: 'Saturation and brightness',
|
|
359
|
+
colorAreaValue: 'Saturation {s}%, brightness {v}%',
|
|
360
|
+
colorHue: 'Hue',
|
|
361
|
+
colorHex: 'Hex color',
|
|
362
|
+
colorHexInvalid: 'Enter a hex color such as #2f6fd6',
|
|
363
|
+
colorPresets: 'Presets',
|
|
364
|
+
colorEyedropper: 'Pick a color from the screen',
|
|
343
365
|
invalidPattern: 'Invalid validation pattern configured',
|
|
344
366
|
|
|
345
367
|
geometryPickAnyOne: 'Pick an object',
|
|
@@ -461,6 +483,8 @@ const de: ViewerMessages = {
|
|
|
461
483
|
lookTechnical: 'Technisch',
|
|
462
484
|
lookStudio: 'Studio',
|
|
463
485
|
lookShowcase: 'Präsentation',
|
|
486
|
+
lookNight: 'Nacht',
|
|
487
|
+
nightMode: 'Nachtmodus',
|
|
464
488
|
lookArctic: 'Arktis',
|
|
465
489
|
lookXray: 'Röntgen',
|
|
466
490
|
lookLineart: 'Strichzeichnung',
|
|
@@ -534,6 +558,14 @@ const de: ViewerMessages = {
|
|
|
534
558
|
fineDragHint: 'Umschalttaste halten für feine Schritte',
|
|
535
559
|
maxLength: 'Höchstens {max} Zeichen erlaubt',
|
|
536
560
|
invalidFormat: 'Ungültiges Format',
|
|
561
|
+
colorPick: 'Farbe wählen, aktuell {color}',
|
|
562
|
+
colorArea: 'Sättigung und Helligkeit',
|
|
563
|
+
colorAreaValue: 'Sättigung {s} %, Helligkeit {v} %',
|
|
564
|
+
colorHue: 'Farbton',
|
|
565
|
+
colorHex: 'Hex-Farbe',
|
|
566
|
+
colorHexInvalid: 'Hex-Farbe eingeben, z. B. #2f6fd6',
|
|
567
|
+
colorPresets: 'Vorgaben',
|
|
568
|
+
colorEyedropper: 'Farbe vom Bildschirm aufnehmen',
|
|
537
569
|
invalidPattern: 'Ungültiges Prüfmuster hinterlegt',
|
|
538
570
|
|
|
539
571
|
geometryPickAnyOne: 'Objekt wählen',
|