@selvajs/ui 6.6.0-beta.4 → 6.6.0-beta.6
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/ErrorScreen.svelte +6 -2
- package/dist/components/compute/AppLayout.svelte +163 -74
- package/dist/components/compute/AppLayout.svelte.d.ts +11 -0
- package/dist/components/compute/ComputeApp.svelte +40 -20
- package/dist/components/compute/ComputeApp.svelte.d.ts +19 -2
- package/dist/components/compute/ComputeMessagesDialog.svelte +30 -40
- package/dist/components/compute/ParameterPresetManager.svelte +7 -44
- package/dist/components/compute/ParameterPresetManager.svelte.d.ts +4 -5
- package/dist/components/compute/SolveMessages.svelte +30 -37
- package/dist/components/compute/SolveMessages.svelte.d.ts +0 -12
- package/dist/components/layout/PageFooter.svelte +21 -11
- package/dist/components/preview/ChartOutput.svelte +17 -8
- package/dist/components/preview/Group.svelte +33 -18
- package/dist/components/preview/HelpButton.svelte +4 -2
- package/dist/components/preview/ImageOutput.svelte +427 -68
- package/dist/components/preview/InputControl.svelte +11 -2
- package/dist/components/preview/OutputDisplay.svelte +17 -12
- package/dist/components/preview/TabContent.svelte +3 -0
- package/dist/components/preview/inputs/ColorInput.svelte +3 -1
- package/dist/components/preview/inputs/ColorInput.svelte.d.ts +1 -0
- package/dist/components/preview/inputs/DropdownInput.svelte +4 -1
- package/dist/components/preview/inputs/FileInput.svelte +50 -34
- package/dist/components/preview/inputs/NumberInput.svelte +92 -10
- package/dist/components/preview/inputs/NumberInput.svelte.d.ts +2 -0
- package/dist/components/preview/inputs/TextInput.svelte +8 -3
- package/dist/components/primitives/CalculateButton.svelte +49 -23
- package/dist/components/primitives/CalculateButton.svelte.d.ts +3 -0
- package/dist/components/primitives/SolvingIndicator.svelte +21 -17
- package/dist/components/primitives/badge/badge.svelte +2 -2
- package/dist/components/primitives/badge/badge.svelte.d.ts +4 -4
- package/dist/components/primitives/button/button.svelte +2 -2
- package/dist/components/primitives/button/button.svelte.d.ts +4 -4
- package/dist/components/primitives/checkbox/checkbox.svelte +1 -1
- package/dist/components/primitives/dialog/dialog-content.svelte +6 -2
- package/dist/components/primitives/input/input.svelte +2 -2
- package/dist/components/primitives/input-group/input-group.svelte +1 -1
- package/dist/components/primitives/mode-toggle/mode-toggle.svelte +4 -1
- package/dist/components/primitives/scroll-area/scroll-area.svelte +1 -1
- package/dist/components/primitives/search/search.svelte +1 -1
- package/dist/components/primitives/select/select-trigger.svelte +1 -1
- package/dist/components/primitives/slider/slider.svelte +7 -2
- package/dist/components/primitives/slider/slider.svelte.d.ts +5 -1
- package/dist/components/primitives/switch/switch.svelte +1 -1
- package/dist/components/primitives/tabs/tabs-trigger.svelte +1 -1
- package/dist/components/primitives/tabs/tabs-trigger.svelte.d.ts +1 -1
- package/dist/components/primitives/textarea/textarea.svelte +1 -1
- package/dist/components/primitives/toggle/toggle.svelte +1 -1
- package/dist/components/primitives/toggle/toggle.svelte.d.ts +1 -1
- package/dist/components/viewer/Viewer.svelte +20 -17
- package/dist/compute/run-panel-action.d.ts +8 -0
- package/dist/compute/run-panel-action.js +12 -0
- package/dist/compute/useSolveSession.svelte.js +3 -0
- package/dist/i18n/localeContext.svelte.d.ts +10 -3
- package/dist/i18n/localeContext.svelte.js +27 -5
- package/dist/i18n/messages.d.ts +115 -2
- package/dist/i18n/messages.js +199 -11
- package/dist/index.d.ts +1 -1
- package/dist/public.d.ts +1 -1
- package/dist/schema/visibility-rules.js +21 -0
- package/dist/styles/base.css +9 -6
- package/dist/types/actionButton.d.ts +17 -3
- package/dist/types/presetLabels.d.ts +3 -2
- package/dist/types/presetLabels.js +9 -8
- package/dist/utils/loadScript.d.ts +2 -0
- package/dist/utils/loadScript.js +2 -0
- package/package.json +8 -8
- package/src/lib/components/ErrorScreen.svelte +6 -2
- package/src/lib/components/compute/AppLayout.svelte +163 -74
- package/src/lib/components/compute/ComputeApp.svelte +40 -20
- package/src/lib/components/compute/ComputeMessagesDialog.svelte +30 -40
- package/src/lib/components/compute/ParameterPresetManager.svelte +7 -44
- package/src/lib/components/compute/SolveMessages.svelte +30 -37
- package/src/lib/components/layout/PageFooter.svelte +21 -11
- package/src/lib/components/preview/ChartOutput.svelte +17 -8
- package/src/lib/components/preview/Group.svelte +33 -18
- package/src/lib/components/preview/HelpButton.svelte +4 -2
- package/src/lib/components/preview/ImageOutput.svelte +427 -68
- package/src/lib/components/preview/InputControl.svelte +11 -2
- package/src/lib/components/preview/OutputDisplay.svelte +17 -12
- package/src/lib/components/preview/TabContent.svelte +3 -0
- package/src/lib/components/preview/inputs/ColorInput.svelte +3 -1
- package/src/lib/components/preview/inputs/DropdownInput.svelte +4 -1
- package/src/lib/components/preview/inputs/FileInput.svelte +50 -34
- package/src/lib/components/preview/inputs/NumberInput.svelte +92 -10
- package/src/lib/components/preview/inputs/TextInput.svelte +8 -3
- package/src/lib/components/primitives/CalculateButton.svelte +49 -23
- package/src/lib/components/primitives/SolvingIndicator.svelte +21 -17
- package/src/lib/components/primitives/badge/badge.svelte +2 -2
- package/src/lib/components/primitives/button/button.svelte +2 -2
- package/src/lib/components/primitives/checkbox/checkbox.svelte +1 -1
- package/src/lib/components/primitives/dialog/dialog-content.svelte +6 -2
- 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/mode-toggle/mode-toggle.svelte +4 -1
- package/src/lib/components/primitives/scroll-area/scroll-area.svelte +1 -1
- package/src/lib/components/primitives/search/search.svelte +1 -1
- package/src/lib/components/primitives/select/select-trigger.svelte +1 -1
- package/src/lib/components/primitives/slider/slider.svelte +7 -2
- package/src/lib/components/primitives/switch/switch.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 +1 -1
- package/src/lib/components/viewer/Viewer.svelte +20 -17
- package/src/lib/compute/run-panel-action.ts +16 -0
- package/src/lib/compute/useSolveSession.svelte.ts +3 -0
- package/src/lib/i18n/localeContext.svelte.ts +40 -5
- package/src/lib/i18n/messages.ts +351 -13
- package/src/lib/index.ts +1 -0
- package/src/lib/public.ts +1 -0
- package/src/lib/schema/visibility-rules.ts +23 -0
- package/src/lib/styles/base.css +9 -6
- package/src/lib/types/actionButton.ts +16 -3
- package/src/lib/types/presetLabels.ts +13 -11
- package/src/lib/utils/loadScript.ts +3 -0
|
@@ -11,21 +11,25 @@
|
|
|
11
11
|
const t = $derived(locale.messages);
|
|
12
12
|
</script>
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
<div
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
14
|
+
<!-- The live region stays mounted: a screen reader only announces text that changes inside a
|
|
15
|
+
region it already knows about, so one created together with its text says nothing. -->
|
|
16
|
+
<div role="status" aria-live="polite" aria-atomic="true">
|
|
17
|
+
{#if show}
|
|
18
|
+
<div
|
|
19
|
+
class="bottom-6 left-6 gap-3 px-5 py-3 shadow-xl animate-in fade-in slide-in-from-left fixed z-50 flex items-center rounded-lg border border-primary/20 bg-primary text-primary-foreground duration-200"
|
|
20
|
+
>
|
|
21
|
+
<div class="h-5 w-5 relative flex shrink-0 items-center justify-center" aria-hidden="true">
|
|
22
|
+
<div
|
|
23
|
+
class="animate-ping absolute h-full w-full rounded-full bg-primary-foreground opacity-30"
|
|
24
|
+
></div>
|
|
25
|
+
<div
|
|
26
|
+
class="h-3 w-3 animate-spin rounded-full border-2 border-primary-foreground border-t-transparent"
|
|
27
|
+
></div>
|
|
28
|
+
</div>
|
|
29
|
+
<div class="leading-tight flex flex-col">
|
|
30
|
+
<span class="text-sm font-semibold">{t.solving}</span>
|
|
31
|
+
<span class="text-xs opacity-70">{t.solvingSubtitle}</span>
|
|
32
|
+
</div>
|
|
25
33
|
</div>
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
<span class="text-xs opacity-70">{t.solvingSubtitle}</span>
|
|
29
|
-
</div>
|
|
30
|
-
</div>
|
|
31
|
-
{/if}
|
|
34
|
+
{/if}
|
|
35
|
+
</div>
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
import { type VariantProps, tv } from 'tailwind-variants';
|
|
3
3
|
|
|
4
4
|
export const badgeVariants = tv({
|
|
5
|
-
base: 'focus-visible:border-ring focus-visible:ring-ring
|
|
5
|
+
base: 'focus-visible:border-ring focus-visible:ring-ring aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border px-2 py-0.5 text-xs font-medium whitespace-nowrap transition focus-visible:ring-3 [&>svg]:pointer-events-none [&>svg]:size-3',
|
|
6
6
|
variants: {
|
|
7
7
|
variant: {
|
|
8
8
|
default: 'bg-primary text-primary-foreground [a&]:hover:bg-primary/90 border-transparent',
|
|
9
9
|
secondary:
|
|
10
10
|
'bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90 border-transparent',
|
|
11
11
|
destructive:
|
|
12
|
-
'bg-destructive [a&]:hover:bg-destructive/90
|
|
12
|
+
'bg-destructive [a&]:hover:bg-destructive/90 dark:bg-destructive/70 border-transparent text-destructive-foreground',
|
|
13
13
|
outline: 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground'
|
|
14
14
|
}
|
|
15
15
|
},
|
|
@@ -3,21 +3,21 @@ export declare const badgeVariants: import("tailwind-variants").TVReturnType<{
|
|
|
3
3
|
variant: {
|
|
4
4
|
default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90 border-transparent";
|
|
5
5
|
secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90 border-transparent";
|
|
6
|
-
destructive: "bg-destructive [a&]:hover:bg-destructive/90
|
|
6
|
+
destructive: "bg-destructive [a&]:hover:bg-destructive/90 dark:bg-destructive/70 border-transparent text-destructive-foreground";
|
|
7
7
|
outline: "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground";
|
|
8
8
|
};
|
|
9
|
-
}, undefined, "focus-visible:border-ring focus-visible:ring-ring
|
|
9
|
+
}, undefined, "focus-visible:border-ring focus-visible:ring-ring aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border px-2 py-0.5 text-xs font-medium whitespace-nowrap transition focus-visible:ring-3 [&>svg]:pointer-events-none [&>svg]:size-3", {
|
|
10
10
|
variant: {
|
|
11
11
|
default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90 border-transparent";
|
|
12
12
|
secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90 border-transparent";
|
|
13
|
-
destructive: "bg-destructive [a&]:hover:bg-destructive/90
|
|
13
|
+
destructive: "bg-destructive [a&]:hover:bg-destructive/90 dark:bg-destructive/70 border-transparent text-destructive-foreground";
|
|
14
14
|
outline: "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground";
|
|
15
15
|
};
|
|
16
16
|
}, undefined, import("tailwind-variants").TVReturnTypeLike<{
|
|
17
17
|
variant: {
|
|
18
18
|
default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90 border-transparent";
|
|
19
19
|
secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90 border-transparent";
|
|
20
|
-
destructive: "bg-destructive [a&]:hover:bg-destructive/90
|
|
20
|
+
destructive: "bg-destructive [a&]:hover:bg-destructive/90 dark:bg-destructive/70 border-transparent text-destructive-foreground";
|
|
21
21
|
outline: "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground";
|
|
22
22
|
};
|
|
23
23
|
}, undefined>>;
|
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
import { type VariantProps, tv } from 'tailwind-variants';
|
|
5
5
|
|
|
6
6
|
export const buttonVariants = tv({
|
|
7
|
-
base: "focus-visible:border-ring focus-visible:ring-ring
|
|
7
|
+
base: "focus-visible:border-ring focus-visible:ring-ring aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium outline-none transition-all focus-visible:ring-3 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
8
8
|
variants: {
|
|
9
9
|
variant: {
|
|
10
10
|
default: 'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90',
|
|
11
11
|
destructive:
|
|
12
|
-
'bg-destructive shadow-xs hover:bg-destructive/90
|
|
12
|
+
'bg-destructive shadow-xs hover:bg-destructive/90 dark:bg-destructive/60 text-destructive-foreground',
|
|
13
13
|
outline:
|
|
14
14
|
'bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 border',
|
|
15
15
|
secondary: 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80',
|
|
@@ -4,7 +4,7 @@ import { type VariantProps } from 'tailwind-variants';
|
|
|
4
4
|
export declare const buttonVariants: import("tailwind-variants").TVReturnType<{
|
|
5
5
|
variant: {
|
|
6
6
|
default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90";
|
|
7
|
-
destructive: "bg-destructive shadow-xs hover:bg-destructive/90
|
|
7
|
+
destructive: "bg-destructive shadow-xs hover:bg-destructive/90 dark:bg-destructive/60 text-destructive-foreground";
|
|
8
8
|
outline: "bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 border";
|
|
9
9
|
secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80";
|
|
10
10
|
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50";
|
|
@@ -20,10 +20,10 @@ export declare const buttonVariants: import("tailwind-variants").TVReturnType<{
|
|
|
20
20
|
'icon-sm': "size-8";
|
|
21
21
|
'icon-lg': "size-10";
|
|
22
22
|
};
|
|
23
|
-
}, undefined, "focus-visible:border-ring focus-visible:ring-ring
|
|
23
|
+
}, undefined, "focus-visible:border-ring focus-visible:ring-ring aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium outline-none transition-all focus-visible:ring-3 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0", {
|
|
24
24
|
variant: {
|
|
25
25
|
default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90";
|
|
26
|
-
destructive: "bg-destructive shadow-xs hover:bg-destructive/90
|
|
26
|
+
destructive: "bg-destructive shadow-xs hover:bg-destructive/90 dark:bg-destructive/60 text-destructive-foreground";
|
|
27
27
|
outline: "bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 border";
|
|
28
28
|
secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80";
|
|
29
29
|
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50";
|
|
@@ -42,7 +42,7 @@ export declare const buttonVariants: import("tailwind-variants").TVReturnType<{
|
|
|
42
42
|
}, undefined, import("tailwind-variants").TVReturnTypeLike<{
|
|
43
43
|
variant: {
|
|
44
44
|
default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90";
|
|
45
|
-
destructive: "bg-destructive shadow-xs hover:bg-destructive/90
|
|
45
|
+
destructive: "bg-destructive shadow-xs hover:bg-destructive/90 dark:bg-destructive/60 text-destructive-foreground";
|
|
46
46
|
outline: "bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 border";
|
|
47
47
|
secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80";
|
|
48
48
|
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50";
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
bind:ref
|
|
18
18
|
data-slot="checkbox"
|
|
19
19
|
class={cn(
|
|
20
|
-
'peer size-4 shadow-xs flex shrink-0 items-center justify-center rounded-sm border border-input transition-shadow outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring
|
|
20
|
+
'peer size-4 shadow-xs flex shrink-0 items-center justify-center rounded-sm border border-input transition-shadow outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:bg-input/30 dark:aria-invalid:ring-destructive/40 dark:data-[state=checked]:bg-primary',
|
|
21
21
|
className
|
|
22
22
|
)}
|
|
23
23
|
bind:checked
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import type { Snippet } from 'svelte';
|
|
5
5
|
import * as Dialog from './index.js';
|
|
6
6
|
import { cn, type WithoutChildrenOrChild } from '../../../utils.js';
|
|
7
|
+
import { getLocaleContext } from '../../../i18n/localeContext.svelte';
|
|
7
8
|
|
|
8
9
|
let {
|
|
9
10
|
ref = $bindable(null),
|
|
@@ -20,6 +21,8 @@
|
|
|
20
21
|
/** `flush` drops padding and gap, for content that draws its own sections edge to edge. */
|
|
21
22
|
variant?: 'default' | 'flush';
|
|
22
23
|
} = $props();
|
|
24
|
+
|
|
25
|
+
const locale = getLocaleContext();
|
|
23
26
|
</script>
|
|
24
27
|
|
|
25
28
|
<Dialog.Portal {...portalProps}>
|
|
@@ -36,11 +39,12 @@
|
|
|
36
39
|
>
|
|
37
40
|
{@render children?.()}
|
|
38
41
|
{#if showCloseButton}
|
|
42
|
+
<!-- 24px around the 16px icon, and 44px of hit area on touch, without moving the icon. -->
|
|
39
43
|
<DialogPrimitive.Close
|
|
40
|
-
class="end-
|
|
44
|
+
class="end-3 top-3 p-1 rounded-xs [&_svg:not([class*='size-'])]:size-4 pointer-coarse:after:-inset-2.5 absolute opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none pointer-coarse:after:absolute [&_svg]:pointer-events-none [&_svg]:shrink-0"
|
|
41
45
|
>
|
|
42
46
|
<XIcon />
|
|
43
|
-
<span class="sr-only">
|
|
47
|
+
<span class="sr-only">{locale.messages.close}</span>
|
|
44
48
|
</DialogPrimitive.Close>
|
|
45
49
|
{/if}
|
|
46
50
|
</DialogPrimitive.Content>
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
data-slot={dataSlot}
|
|
27
27
|
class={cn(
|
|
28
28
|
'h-9 min-w-0 px-3 pt-1.5 text-sm font-medium shadow-xs flex w-full rounded-md border border-input bg-transparent ring-offset-background transition outline-none selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 dark:bg-input/30',
|
|
29
|
-
'focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring
|
|
29
|
+
'focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring',
|
|
30
30
|
'aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40',
|
|
31
31
|
className
|
|
32
32
|
)}
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
data-slot={dataSlot}
|
|
42
42
|
class={cn(
|
|
43
43
|
'h-9 min-w-0 px-3 py-1 text-base shadow-xs md:text-sm flex w-full rounded-md border border-input bg-background ring-offset-background transition outline-none selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 dark:bg-input/30',
|
|
44
|
-
'focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring
|
|
44
|
+
'focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring',
|
|
45
45
|
'aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40',
|
|
46
46
|
className
|
|
47
47
|
)}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
data-slot="input-group"
|
|
16
16
|
role="group"
|
|
17
17
|
class={cn(
|
|
18
|
-
'group/input-group h-9 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5 min-w-0 shadow-xs relative flex w-full items-center rounded-md border border-input bg-background transition-colors outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:bg-input/50 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot=input-group-control]:focus-visible]:ring-ring
|
|
18
|
+
'group/input-group h-9 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5 min-w-0 shadow-xs relative flex w-full items-center rounded-md border border-input bg-background transition-colors outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:bg-input/50 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot=input-group-control]:focus-visible]:ring-ring has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:bg-input/30 dark:has-disabled:bg-input/80 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40',
|
|
19
19
|
className
|
|
20
20
|
)}
|
|
21
21
|
{...props}
|
|
@@ -3,10 +3,13 @@
|
|
|
3
3
|
import Moon from '@lucide/svelte/icons/moon';
|
|
4
4
|
import { toggleMode } from 'mode-watcher';
|
|
5
5
|
import { Button } from '../button';
|
|
6
|
+
import { getLocaleContext } from '../../../i18n/localeContext.svelte';
|
|
7
|
+
|
|
8
|
+
const locale = getLocaleContext();
|
|
6
9
|
</script>
|
|
7
10
|
|
|
8
11
|
<Button onclick={toggleMode} variant="outline" size="icon">
|
|
9
12
|
<Sun class="size-5 scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" />
|
|
10
13
|
<Moon class="size-5 absolute scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" />
|
|
11
|
-
<span class="sr-only">
|
|
14
|
+
<span class="sr-only">{locale.messages.toggleTheme}</span>
|
|
12
15
|
</Button>
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
<ScrollAreaPrimitive.Viewport
|
|
30
30
|
bind:ref={viewportRef}
|
|
31
31
|
data-slot="scroll-area-viewport"
|
|
32
|
-
class="size-full rounded-inherit ring-ring/10 outline-ring
|
|
32
|
+
class="size-full rounded-inherit ring-ring/10 outline-ring transition focus-visible:ring-4 focus-visible:outline-1 dark:ring-ring/20"
|
|
33
33
|
>
|
|
34
34
|
{@render children?.()}
|
|
35
35
|
</ScrollAreaPrimitive.Viewport>
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
data-slot="input"
|
|
47
47
|
class={cn(
|
|
48
48
|
'h-9 min-w-0 px-3 py-1 text-base shadow-xs md:text-sm flex w-full rounded-md border border-input bg-background ring-offset-background transition outline-none selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 dark:bg-input/30',
|
|
49
|
-
'focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring
|
|
49
|
+
'focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring',
|
|
50
50
|
'aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40',
|
|
51
51
|
'pl-9 pr-12',
|
|
52
52
|
className
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
data-slot="select-trigger"
|
|
20
20
|
data-size={size}
|
|
21
21
|
class={cn(
|
|
22
|
-
"gap-2 px-3 py-2 text-sm shadow-xs data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:gap-2 [&_svg:not([class*='size-'])]:size-4 flex w-fit items-center justify-between rounded-md border border-input bg-transparent whitespace-nowrap transition outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring
|
|
22
|
+
"gap-2 px-3 py-2 text-sm shadow-xs data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:gap-2 [&_svg:not([class*='size-'])]:size-4 flex w-fit items-center justify-between rounded-md border border-input bg-transparent whitespace-nowrap transition outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[placeholder]:text-muted-foreground *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='text-'])]:text-muted-foreground",
|
|
23
23
|
className
|
|
24
24
|
)}
|
|
25
25
|
{...restProps}
|
|
@@ -7,8 +7,12 @@
|
|
|
7
7
|
value = $bindable(),
|
|
8
8
|
orientation = 'horizontal',
|
|
9
9
|
class: className,
|
|
10
|
+
thumbLabelledby,
|
|
10
11
|
...restProps
|
|
11
|
-
}: WithoutChildrenOrChild<SliderPrimitive.RootProps>
|
|
12
|
+
}: WithoutChildrenOrChild<SliderPrimitive.RootProps> & {
|
|
13
|
+
/** Id of the element naming the thumbs, which carry the slider role. */
|
|
14
|
+
thumbLabelledby?: string;
|
|
15
|
+
} = $props();
|
|
12
16
|
</script>
|
|
13
17
|
|
|
14
18
|
<!--
|
|
@@ -45,7 +49,8 @@ get along, so we shut typescript up by casting `value` to `never`.
|
|
|
45
49
|
<SliderPrimitive.Thumb
|
|
46
50
|
data-slot="slider-thumb"
|
|
47
51
|
index={thumb}
|
|
48
|
-
|
|
52
|
+
aria-labelledby={thumbLabelledby}
|
|
53
|
+
class="size-4 shadow-sm block shrink-0 rounded-full border border-primary bg-background ring-ring/50 transition hover:ring-4 focus-visible:ring-4 focus-visible:ring-ring focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50"
|
|
49
54
|
/>
|
|
50
55
|
{/each}
|
|
51
56
|
{/snippet}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { Slider as SliderPrimitive } from 'bits-ui';
|
|
2
2
|
import { type WithoutChildrenOrChild } from '../../../utils.js';
|
|
3
|
-
|
|
3
|
+
type $$ComponentProps = WithoutChildrenOrChild<SliderPrimitive.RootProps> & {
|
|
4
|
+
/** Id of the element naming the thumbs, which carry the slider role. */
|
|
5
|
+
thumbLabelledby?: string;
|
|
6
|
+
};
|
|
7
|
+
declare const Slider: import("svelte").Component<$$ComponentProps, {}, "ref" | "value">;
|
|
4
8
|
type Slider = ReturnType<typeof Slider>;
|
|
5
9
|
export default Slider;
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
bind:checked
|
|
16
16
|
data-slot="switch"
|
|
17
17
|
class={cn(
|
|
18
|
-
'peer w-8 shadow-xs h-4.5 inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring
|
|
18
|
+
'peer w-8 shadow-xs h-4.5 inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input dark:data-[state=unchecked]:bg-input/80',
|
|
19
19
|
className
|
|
20
20
|
)}
|
|
21
21
|
{...restProps}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { type VariantProps, tv } from 'tailwind-variants';
|
|
3
3
|
|
|
4
4
|
export const tabsTriggerVariants = tv({
|
|
5
|
-
base: "gap-1.5 text-sm font-medium [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap focus-visible:ring-3 focus-visible:ring-ring
|
|
5
|
+
base: "gap-1.5 text-sm font-medium [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap focus-visible:ring-3 focus-visible:ring-ring focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
6
6
|
variants: {
|
|
7
7
|
variant: {
|
|
8
8
|
default:
|
|
@@ -4,7 +4,7 @@ export declare const tabsTriggerVariants: import("tailwind-variants").TVReturnTy
|
|
|
4
4
|
default: "px-2 py-1 data-[state=active]:shadow-sm h-full flex-1 rounded-md border border-transparent text-foreground transition focus-visible:border-ring data-[state=active]:bg-background dark:text-muted-foreground dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 dark:data-[state=active]:text-foreground";
|
|
5
5
|
line: "px-2.5 py-3 relative flex-none text-muted-foreground transition-colors after:absolute after:inset-x-2 after:bottom-0 after:h-0.5 after:rounded-full hover:text-foreground data-[state=active]:text-foreground data-[state=active]:after:bg-primary";
|
|
6
6
|
};
|
|
7
|
-
}, undefined, "gap-1.5 text-sm font-medium [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap focus-visible:ring-3 focus-visible:ring-ring
|
|
7
|
+
}, undefined, "gap-1.5 text-sm font-medium [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap focus-visible:ring-3 focus-visible:ring-ring focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0", {
|
|
8
8
|
variant: {
|
|
9
9
|
default: "px-2 py-1 data-[state=active]:shadow-sm h-full flex-1 rounded-md border border-transparent text-foreground transition focus-visible:border-ring data-[state=active]:bg-background dark:text-muted-foreground dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 dark:data-[state=active]:text-foreground";
|
|
10
10
|
line: "px-2.5 py-3 relative flex-none text-muted-foreground transition-colors after:absolute after:inset-x-2 after:bottom-0 after:h-0.5 after:rounded-full hover:text-foreground data-[state=active]:text-foreground data-[state=active]:after:bg-primary";
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
bind:this={ref}
|
|
16
16
|
data-slot={dataSlot}
|
|
17
17
|
class={cn(
|
|
18
|
-
'min-h-16 px-3 py-2 text-base shadow-xs md:text-sm flex field-sizing-content w-full rounded-md border border-input bg-transparent transition outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring
|
|
18
|
+
'min-h-16 px-3 py-2 text-base shadow-xs md:text-sm flex field-sizing-content w-full rounded-md border border-input bg-transparent transition outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:bg-input/30 dark:aria-invalid:ring-destructive/40',
|
|
19
19
|
className
|
|
20
20
|
)}
|
|
21
21
|
bind:value
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { type VariantProps, tv } from 'tailwind-variants';
|
|
3
3
|
|
|
4
4
|
export const toggleVariants = tv({
|
|
5
|
-
base: "hover:text-foreground aria-pressed:bg-muted focus-visible:border-ring focus-visible:ring-ring
|
|
5
|
+
base: "hover:text-foreground aria-pressed:bg-muted focus-visible:border-ring focus-visible:ring-ring aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[state=on]:bg-muted gap-1 rounded-lg text-sm font-medium transition-all [&_svg:not([class*='size-'])]:size-4 group/toggle inline-flex items-center justify-center whitespace-nowrap outline-none hover:bg-muted focus-visible:ring-3 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
6
6
|
variants: {
|
|
7
7
|
variant: {
|
|
8
8
|
default: 'bg-transparent',
|
|
@@ -9,7 +9,7 @@ export declare const toggleVariants: import("tailwind-variants").TVReturnType<{
|
|
|
9
9
|
sm: "h-7 min-w-7 rounded-md px-2.5 text-xs has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5";
|
|
10
10
|
lg: "h-9 min-w-9 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2";
|
|
11
11
|
};
|
|
12
|
-
}, undefined, "hover:text-foreground aria-pressed:bg-muted focus-visible:border-ring focus-visible:ring-ring
|
|
12
|
+
}, undefined, "hover:text-foreground aria-pressed:bg-muted focus-visible:border-ring focus-visible:ring-ring aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[state=on]:bg-muted gap-1 rounded-lg text-sm font-medium transition-all [&_svg:not([class*='size-'])]:size-4 group/toggle inline-flex items-center justify-center whitespace-nowrap outline-none hover:bg-muted focus-visible:ring-3 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0", {
|
|
13
13
|
variant: {
|
|
14
14
|
default: "bg-transparent";
|
|
15
15
|
outline: "border-input hover:bg-muted border bg-transparent";
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
import * as DropdownMenu from '../primitives/dropdown-menu/index.js';
|
|
40
40
|
import { Button } from '../primitives/button/index.js';
|
|
41
41
|
import type { Locale } from '../../i18n/messages';
|
|
42
|
-
import {
|
|
42
|
+
import { layerLocaleContext } from '../../i18n/localeContext.svelte';
|
|
43
43
|
|
|
44
44
|
export interface ViewerConfig {
|
|
45
45
|
showScreenshotButton?: boolean;
|
|
@@ -103,12 +103,8 @@
|
|
|
103
103
|
|
|
104
104
|
const config = $derived({ ...defaultViewerConfig, ...viewerConfig });
|
|
105
105
|
|
|
106
|
-
// Read the host's locale before overriding it for our subtree.
|
|
107
|
-
const hostLocale = getLocaleContext();
|
|
108
|
-
|
|
109
106
|
// A getter, not a value: it is re-read reactively, so changing `lang` updates the chrome live.
|
|
110
|
-
|
|
111
|
-
const locale = getLocaleContext();
|
|
107
|
+
const locale = layerLocaleContext(() => lang);
|
|
112
108
|
const t = $derived(locale.messages);
|
|
113
109
|
|
|
114
110
|
let canvas: HTMLCanvasElement;
|
|
@@ -146,16 +142,23 @@
|
|
|
146
142
|
let selectedMeshMetadata: Record<string, any> | null = $state(null);
|
|
147
143
|
let selectedMeshName: string | null = $state(null);
|
|
148
144
|
|
|
149
|
-
// Derived from LOOKS so
|
|
150
|
-
//
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
145
|
+
// Derived from LOOKS so a look added in @selvajs/visualization shows up here with no edit,
|
|
146
|
+
// under its capitalised name until it gets a message.
|
|
147
|
+
const lookLabels = $derived<Partial<Record<Look, string>>>({
|
|
148
|
+
technical: t.lookTechnical,
|
|
149
|
+
studio: t.lookStudio,
|
|
150
|
+
showcase: t.lookShowcase,
|
|
151
|
+
arctic: t.lookArctic,
|
|
152
|
+
xray: t.lookXray,
|
|
153
|
+
lineart: t.lookLineart,
|
|
154
|
+
wireframe: t.lookWireframe
|
|
155
|
+
});
|
|
156
|
+
const styleOptions = $derived<{ look: Look; label: string }[]>(
|
|
157
|
+
LOOKS.map((look) => ({
|
|
158
|
+
look,
|
|
159
|
+
label: lookLabels[look] ?? look.charAt(0).toUpperCase() + look.slice(1)
|
|
160
|
+
}))
|
|
161
|
+
);
|
|
159
162
|
|
|
160
163
|
const VIEW_PRESETS: { preset: ViewPreset; label: () => string }[] = [
|
|
161
164
|
{ preset: 'top', label: () => t.viewTop },
|
|
@@ -527,7 +530,7 @@
|
|
|
527
530
|
value={renderStyle}
|
|
528
531
|
onValueChange={(look) => setRenderStyle(look as typeof renderStyle)}
|
|
529
532
|
>
|
|
530
|
-
{#each
|
|
533
|
+
{#each styleOptions as { look, label } (look)}
|
|
531
534
|
<DropdownMenu.RadioItem value={look} closeOnSelect={false}>
|
|
532
535
|
{label}
|
|
533
536
|
</DropdownMenu.RadioItem>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { SolveSettlement } from '@selvajs/solve/client';
|
|
2
|
+
import type { ActionButton } from '../types/actionButton';
|
|
3
|
+
/**
|
|
4
|
+
* Runs a footer-menu action. A `solvesFirst` action waits for the result for the current
|
|
5
|
+
* inputs and runs only on an applied one; a refused or failed solve has already shown the user
|
|
6
|
+
* why. A host that can't solve (`ensureSolved` undefined) runs it straight away.
|
|
7
|
+
*/
|
|
8
|
+
export declare function runPanelAction(action: ActionButton, ensureSolved: (() => Promise<SolveSettlement>) | undefined): Promise<void>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs a footer-menu action. A `solvesFirst` action waits for the result for the current
|
|
3
|
+
* inputs and runs only on an applied one; a refused or failed solve has already shown the user
|
|
4
|
+
* why. A host that can't solve (`ensureSolved` undefined) runs it straight away.
|
|
5
|
+
*/
|
|
6
|
+
export async function runPanelAction(action, ensureSolved) {
|
|
7
|
+
if (!action.solvesFirst || !ensureSolved)
|
|
8
|
+
return action.onclick();
|
|
9
|
+
const settled = await ensureSolved();
|
|
10
|
+
if (settled.status === 'applied')
|
|
11
|
+
await action.onclick(settled);
|
|
12
|
+
}
|
|
@@ -79,10 +79,13 @@ export function useSolveSession(args) {
|
|
|
79
79
|
discard: () => session.discard(),
|
|
80
80
|
setValue: (id, value, forceSolve) => session.setValue(id, value, forceSolve),
|
|
81
81
|
solve: () => session.solve(),
|
|
82
|
+
ensureSolved: () => session.ensureSolved(),
|
|
83
|
+
onSettled: (listener) => session.onSettled(listener),
|
|
82
84
|
loadValues: (incoming) => session.loadValues(incoming),
|
|
83
85
|
rebuild: (schema, scopeKey) => session.rebuild(schema, scopeKey),
|
|
84
86
|
report: (result) => session.report(result),
|
|
85
87
|
reportError: (message) => session.reportError(message),
|
|
88
|
+
reportAborted: () => session.reportAborted(),
|
|
86
89
|
subscribe: (listener) => session.subscribe(listener),
|
|
87
90
|
notify: () => session.notify(),
|
|
88
91
|
dispose: () => session.dispose()
|
|
@@ -4,11 +4,18 @@ export interface LocaleContext {
|
|
|
4
4
|
readonly messages: ViewerMessages;
|
|
5
5
|
}
|
|
6
6
|
/**
|
|
7
|
-
* Provide the locale to descendants. Pass
|
|
7
|
+
* Provide the locale to descendants. Pass getters, not values: they are re-read on every
|
|
8
8
|
* consumer render, so a reactive source (a `$state`, the app's Paraglide locale) switches
|
|
9
|
-
* the language live.
|
|
9
|
+
* the language live. `getOverrides` replaces individual strings of the resolved catalog.
|
|
10
10
|
*/
|
|
11
|
-
export declare function setLocaleContext(getLocale: () => Locale | undefined): void;
|
|
11
|
+
export declare function setLocaleContext(getLocale: () => Locale | undefined, getOverrides?: () => Partial<ViewerMessages> | undefined): void;
|
|
12
|
+
/**
|
|
13
|
+
* Re-provides the nearest context for a component that takes its own `lang`/`messages` props.
|
|
14
|
+
* Without `lang`, or with the inherited locale, the subtree keeps the overrides an outer provider
|
|
15
|
+
* applied; a different `lang` starts from that locale's catalog, since those overrides were
|
|
16
|
+
* written for another language.
|
|
17
|
+
*/
|
|
18
|
+
export declare function layerLocaleContext(getLang: () => Locale | undefined, getOverrides?: () => Partial<ViewerMessages> | undefined): LocaleContext;
|
|
12
19
|
/**
|
|
13
20
|
* Falls back to an English-only context when no provider exists (a primitive used in
|
|
14
21
|
* isolation), so consumers never need a null check.
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { getContext, setContext } from 'svelte';
|
|
2
|
-
import { messagesFor, DEFAULT_LOCALE } from './messages';
|
|
2
|
+
import { messagesFor, withOverrides, DEFAULT_LOCALE } from './messages';
|
|
3
3
|
// Carries the UI locale down to the viewer and its panels without threading a
|
|
4
4
|
// `lang` prop through every layer.
|
|
5
5
|
//
|
|
6
6
|
// Resolution order for any consuming component:
|
|
7
7
|
// explicit `lang` prop → nearest locale context → English default
|
|
8
|
+
// and the strings are that locale's catalog with any `messages` overrides on top.
|
|
8
9
|
//
|
|
9
10
|
// Two ways to provide it:
|
|
10
11
|
// - Standalone: <Viewer lang="de" /> — Viewer provides the context itself.
|
|
@@ -12,21 +13,42 @@ import { messagesFor, DEFAULT_LOCALE } from './messages';
|
|
|
12
13
|
// viewer (and anything else) reads it.
|
|
13
14
|
const LOCALE_CONTEXT_KEY = Symbol('viewer-locale-context');
|
|
14
15
|
/**
|
|
15
|
-
* Provide the locale to descendants. Pass
|
|
16
|
+
* Provide the locale to descendants. Pass getters, not values: they are re-read on every
|
|
16
17
|
* consumer render, so a reactive source (a `$state`, the app's Paraglide locale) switches
|
|
17
|
-
* the language live.
|
|
18
|
+
* the language live. `getOverrides` replaces individual strings of the resolved catalog.
|
|
18
19
|
*/
|
|
19
|
-
export function setLocaleContext(getLocale) {
|
|
20
|
+
export function setLocaleContext(getLocale, getOverrides) {
|
|
20
21
|
const ctx = {
|
|
21
22
|
get locale() {
|
|
22
23
|
return getLocale() ?? DEFAULT_LOCALE;
|
|
23
24
|
},
|
|
24
25
|
get messages() {
|
|
25
|
-
return messagesFor(getLocale());
|
|
26
|
+
return withOverrides(messagesFor(getLocale()), getOverrides?.());
|
|
26
27
|
}
|
|
27
28
|
};
|
|
28
29
|
setContext(LOCALE_CONTEXT_KEY, ctx);
|
|
29
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Re-provides the nearest context for a component that takes its own `lang`/`messages` props.
|
|
33
|
+
* Without `lang`, or with the inherited locale, the subtree keeps the overrides an outer provider
|
|
34
|
+
* applied; a different `lang` starts from that locale's catalog, since those overrides were
|
|
35
|
+
* written for another language.
|
|
36
|
+
*/
|
|
37
|
+
export function layerLocaleContext(getLang, getOverrides) {
|
|
38
|
+
const outer = getLocaleContext();
|
|
39
|
+
const ctx = {
|
|
40
|
+
get locale() {
|
|
41
|
+
return getLang() ?? outer.locale;
|
|
42
|
+
},
|
|
43
|
+
get messages() {
|
|
44
|
+
const lang = getLang();
|
|
45
|
+
const base = lang === undefined || lang === outer.locale ? outer.messages : messagesFor(lang);
|
|
46
|
+
return withOverrides(base, getOverrides?.());
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
setContext(LOCALE_CONTEXT_KEY, ctx);
|
|
50
|
+
return ctx;
|
|
51
|
+
}
|
|
30
52
|
/**
|
|
31
53
|
* Falls back to an English-only context when no provider exists (a primitive used in
|
|
32
54
|
* isolation), so consumers never need a null check.
|