@lavalogic/scoria 0.40.12 → 0.40.14
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/CollapsibleCard.svelte +53 -79
- package/dist/Components/CollapsibleMenuGroup.svelte +93 -0
- package/dist/Components/CollapsibleMenuGroup.svelte.d.ts +9 -0
- package/dist/Components/CollapsibleMenuGroupProps.d.ts +14 -0
- package/dist/Components/CollapsibleMenuGroupProps.js +1 -0
- package/dist/Components/OptionCards.svelte +21 -5
- package/dist/Components/OptionCardsProps.d.ts +11 -2
- package/dist/Components/PopoverButton.svelte +164 -0
- package/dist/Components/PopoverButton.svelte.d.ts +9 -0
- package/dist/Components/PopoverButtonProps.d.ts +21 -0
- package/dist/Components/PopoverButtonProps.js +1 -0
- package/dist/Components/SidebarPanel.svelte +85 -0
- package/dist/Components/SidebarPanel.svelte.d.ts +9 -0
- package/dist/Components/SidebarPanelProps.d.ts +18 -0
- package/dist/Components/SidebarPanelProps.js +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +3 -0
- package/package.json +1 -1
|
@@ -9,10 +9,13 @@
|
|
|
9
9
|
|
|
10
10
|
<script lang="ts">
|
|
11
11
|
import { loadingText } from '../Helpers/Helpers.svelte.js';
|
|
12
|
+
import { usePrefersReducedMotion } from '../Helpers/prefersReducedMotion.svelte.js';
|
|
12
13
|
import { Size } from '../Types/Internal/Size.js';
|
|
13
14
|
import { Colour } from '../scss/colours.js';
|
|
14
|
-
import
|
|
15
|
+
import { cubicOut } from 'svelte/easing';
|
|
16
|
+
import { slide } from 'svelte/transition';
|
|
15
17
|
import type { CollapsibleCardProps } from './CollapsibleCardProps.js';
|
|
18
|
+
import Icon from './Icon.svelte';
|
|
16
19
|
|
|
17
20
|
let {
|
|
18
21
|
title,
|
|
@@ -26,6 +29,9 @@
|
|
|
26
29
|
const contentId = `collapsible-card-${generateWeakId()}`;
|
|
27
30
|
|
|
28
31
|
const cardMaxHeight = $derived(maxHeight != null && maxHeight > 0 ? `${maxHeight}px` : undefined);
|
|
32
|
+
|
|
33
|
+
const reducedMotion = usePrefersReducedMotion();
|
|
34
|
+
const slideDuration = $derived(reducedMotion.matches ? 0 : 200);
|
|
29
35
|
</script>
|
|
30
36
|
|
|
31
37
|
<div
|
|
@@ -34,7 +40,16 @@
|
|
|
34
40
|
class:is-collapsed={isCollapsed}
|
|
35
41
|
class:grow
|
|
36
42
|
>
|
|
37
|
-
<
|
|
43
|
+
<button
|
|
44
|
+
type="button"
|
|
45
|
+
class="heading"
|
|
46
|
+
class:is-collapsed={isCollapsed}
|
|
47
|
+
aria-expanded={!isCollapsed}
|
|
48
|
+
aria-controls={contentId}
|
|
49
|
+
onclick={() => {
|
|
50
|
+
isCollapsed = !isCollapsed;
|
|
51
|
+
}}
|
|
52
|
+
>
|
|
38
53
|
<div class="left">
|
|
39
54
|
<h3>{title}</h3>
|
|
40
55
|
{#if subtitle}
|
|
@@ -45,34 +60,27 @@
|
|
|
45
60
|
{/await}
|
|
46
61
|
{/if}
|
|
47
62
|
</div>
|
|
48
|
-
<
|
|
49
|
-
<
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
onclick={() => {
|
|
54
|
-
isCollapsed = !isCollapsed;
|
|
55
|
-
}}
|
|
56
|
-
size={Size.TripleXLarge}
|
|
57
|
-
icon={{
|
|
58
|
-
name: isCollapsed ? 'circle-plus' : 'circle-minus',
|
|
59
|
-
colour: Colour['$ui-blue-5'],
|
|
60
|
-
size: Size.Medium,
|
|
61
|
-
}}
|
|
63
|
+
<span class="indicator">
|
|
64
|
+
<Icon
|
|
65
|
+
name={isCollapsed ? 'circle-plus' : 'circle-minus'}
|
|
66
|
+
colour={Colour['$ui-blue-5']}
|
|
67
|
+
size={Size.Medium}
|
|
62
68
|
/>
|
|
63
|
-
</
|
|
64
|
-
</
|
|
69
|
+
</span>
|
|
70
|
+
</button>
|
|
65
71
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
{
|
|
74
|
-
|
|
75
|
-
|
|
72
|
+
{#if !isCollapsed}
|
|
73
|
+
<div
|
|
74
|
+
id={contentId}
|
|
75
|
+
class="content"
|
|
76
|
+
style="overflow: {cardMaxHeight != null ? 'auto' : 'visible'};"
|
|
77
|
+
transition:slide={{ duration: slideDuration, easing: cubicOut }}
|
|
78
|
+
>
|
|
79
|
+
{#if children}
|
|
80
|
+
{@render children()}
|
|
81
|
+
{/if}
|
|
82
|
+
</div>
|
|
83
|
+
{/if}
|
|
76
84
|
</div>
|
|
77
85
|
|
|
78
86
|
<style>.collapsible-card {
|
|
@@ -84,40 +92,47 @@
|
|
|
84
92
|
box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.1333333333);
|
|
85
93
|
padding: 1.5rem;
|
|
86
94
|
max-height: 100%;
|
|
87
|
-
overflow: hidden;
|
|
88
95
|
}
|
|
89
96
|
.collapsible-card.grow {
|
|
90
97
|
flex: 1;
|
|
91
98
|
}
|
|
92
|
-
|
|
99
|
+
|
|
100
|
+
.heading {
|
|
93
101
|
display: flex;
|
|
94
102
|
flex-flow: row nowrap;
|
|
95
|
-
|
|
103
|
+
align-items: center;
|
|
104
|
+
width: 100%;
|
|
105
|
+
font: inherit;
|
|
106
|
+
text-align: start;
|
|
107
|
+
cursor: pointer;
|
|
108
|
+
background: transparent;
|
|
109
|
+
border: none;
|
|
96
110
|
box-sizing: border-box;
|
|
97
111
|
padding: 0 0.5rem 0.5rem 0.5rem;
|
|
98
112
|
}
|
|
99
|
-
.
|
|
113
|
+
.heading .left {
|
|
100
114
|
display: flex;
|
|
101
115
|
flex-flow: column nowrap;
|
|
102
116
|
justify-content: center;
|
|
117
|
+
gap: 0.25rem;
|
|
103
118
|
}
|
|
104
|
-
.
|
|
119
|
+
.heading .left h3 {
|
|
105
120
|
margin: 0;
|
|
106
121
|
margin-top: 0.25rem;
|
|
107
122
|
color: #46525b;
|
|
108
123
|
font-weight: 500;
|
|
109
124
|
font-size: 1.125rem;
|
|
110
125
|
}
|
|
111
|
-
.
|
|
112
|
-
margin-top: auto;
|
|
113
|
-
margin-bottom: 0.25rem;
|
|
126
|
+
.heading .left span {
|
|
114
127
|
color: #647d8e;
|
|
115
128
|
font-size: 0.875rem;
|
|
116
129
|
}
|
|
117
|
-
.
|
|
130
|
+
.heading .indicator {
|
|
118
131
|
margin-left: auto;
|
|
132
|
+
display: inline-flex;
|
|
133
|
+
align-items: center;
|
|
119
134
|
}
|
|
120
|
-
.
|
|
135
|
+
.heading:not(.is-collapsed) {
|
|
121
136
|
margin-bottom: 1rem;
|
|
122
137
|
border-bottom: solid 1px #dae0e5;
|
|
123
138
|
}
|
|
@@ -125,49 +140,8 @@
|
|
|
125
140
|
div.content {
|
|
126
141
|
display: flex;
|
|
127
142
|
flex-flow: column nowrap;
|
|
128
|
-
}
|
|
129
|
-
@media (prefers-reduced-motion: no-preference) {
|
|
130
|
-
div.content {
|
|
131
|
-
transition: max-height ease-out 0.15s 0s, opacity ease-out 0.15s 0.1s;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
div.content {
|
|
135
143
|
max-height: var(--card-max-height, 100%);
|
|
136
144
|
height: max-content;
|
|
137
145
|
min-height: 0;
|
|
138
|
-
opacity: 1;
|
|
139
146
|
width: 100%;
|
|
140
|
-
}
|
|
141
|
-
@media (prefers-reduced-motion: no-preference) {
|
|
142
|
-
div.content.is-collapsed {
|
|
143
|
-
animation: accordion ease-out 0.15s 0s forwards;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
div.content.is-collapsed {
|
|
147
|
-
max-height: 0px;
|
|
148
|
-
opacity: 0;
|
|
149
|
-
overflow: hidden;
|
|
150
|
-
}
|
|
151
|
-
@media (prefers-reduced-motion: no-preference) {
|
|
152
|
-
@keyframes accordion {
|
|
153
|
-
0% {
|
|
154
|
-
opacity: 1;
|
|
155
|
-
max-height: var(--card-max-height, 90vh);
|
|
156
|
-
overflow: auto;
|
|
157
|
-
}
|
|
158
|
-
100% {
|
|
159
|
-
opacity: 0;
|
|
160
|
-
max-height: 0px;
|
|
161
|
-
overflow: hidden;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
@media (prefers-reduced-motion: reduce) {
|
|
167
|
-
.heading,
|
|
168
|
-
div.content,
|
|
169
|
-
div.content.is-collapsed {
|
|
170
|
-
animation-duration: 0.001ms !important;
|
|
171
|
-
transition-duration: 0.001ms !important;
|
|
172
|
-
}
|
|
173
147
|
}</style>
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
<svelte:options runes />
|
|
2
|
+
|
|
3
|
+
<!--
|
|
4
|
+
@component
|
|
5
|
+
A labelled, collapsible group for a side menu: a header row (label + chevron)
|
|
6
|
+
that expands and collapses the items supplied as the default snippet. Pair
|
|
7
|
+
several of these in a menu column to build a grouped navigation tree.
|
|
8
|
+
-->
|
|
9
|
+
<script lang="ts">
|
|
10
|
+
import { usePrefersReducedMotion } from '../Helpers/prefersReducedMotion.svelte.js';
|
|
11
|
+
import { Colour } from '../scss/colours.js';
|
|
12
|
+
import { Size } from '../Types/Internal/Size.js';
|
|
13
|
+
import { cubicOut } from 'svelte/easing';
|
|
14
|
+
import { slide } from 'svelte/transition';
|
|
15
|
+
import type { CollapsibleMenuGroupProps } from './CollapsibleMenuGroupProps.js';
|
|
16
|
+
import Icon from './Icon.svelte';
|
|
17
|
+
|
|
18
|
+
let { title, isCollapsed = $bindable(false), children }: CollapsibleMenuGroupProps = $props();
|
|
19
|
+
|
|
20
|
+
const reducedMotion = usePrefersReducedMotion();
|
|
21
|
+
const slideDuration = $derived(reducedMotion.matches ? 0 : 200);
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<div class="menu-group">
|
|
25
|
+
<button
|
|
26
|
+
type="button"
|
|
27
|
+
class="group-header"
|
|
28
|
+
aria-expanded={!isCollapsed}
|
|
29
|
+
onclick={() => {
|
|
30
|
+
isCollapsed = !isCollapsed;
|
|
31
|
+
}}
|
|
32
|
+
>
|
|
33
|
+
<span class="group-title">{title}</span>
|
|
34
|
+
<span
|
|
35
|
+
class="chevron"
|
|
36
|
+
class:collapsed={isCollapsed}
|
|
37
|
+
>
|
|
38
|
+
<Icon
|
|
39
|
+
name="chevron-down"
|
|
40
|
+
size={Size.Small}
|
|
41
|
+
colour={Colour['$ui-blue-11']}
|
|
42
|
+
/>
|
|
43
|
+
</span>
|
|
44
|
+
</button>
|
|
45
|
+
|
|
46
|
+
{#if !isCollapsed}
|
|
47
|
+
<div
|
|
48
|
+
class="group-items"
|
|
49
|
+
transition:slide={{ duration: slideDuration, easing: cubicOut }}
|
|
50
|
+
>
|
|
51
|
+
{@render children()}
|
|
52
|
+
</div>
|
|
53
|
+
{/if}
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<style>.menu-group {
|
|
57
|
+
border: solid 1px #cbd4da;
|
|
58
|
+
border-radius: 4px;
|
|
59
|
+
background-color: #ffffff;
|
|
60
|
+
overflow: hidden;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.group-header {
|
|
64
|
+
display: flex;
|
|
65
|
+
flex-flow: row nowrap;
|
|
66
|
+
align-items: center;
|
|
67
|
+
width: 100%;
|
|
68
|
+
gap: 0.5rem;
|
|
69
|
+
padding: 0.75rem 1.25rem;
|
|
70
|
+
font: inherit;
|
|
71
|
+
font-weight: 500;
|
|
72
|
+
color: #46525b;
|
|
73
|
+
text-align: start;
|
|
74
|
+
background-color: #f4f7f9;
|
|
75
|
+
border: none;
|
|
76
|
+
cursor: pointer;
|
|
77
|
+
}
|
|
78
|
+
.group-header .group-title {
|
|
79
|
+
flex-grow: 1;
|
|
80
|
+
}
|
|
81
|
+
.group-header .chevron {
|
|
82
|
+
display: inline-flex;
|
|
83
|
+
transition: transform 0.15s ease-out;
|
|
84
|
+
}
|
|
85
|
+
.group-header .chevron.collapsed {
|
|
86
|
+
transform: rotate(-90deg);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.group-items {
|
|
90
|
+
display: flex;
|
|
91
|
+
flex-flow: column nowrap;
|
|
92
|
+
border-top: solid 1px #cbd4da;
|
|
93
|
+
}</style>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CollapsibleMenuGroupProps } from './CollapsibleMenuGroupProps.js';
|
|
2
|
+
/**
|
|
3
|
+
* A labelled, collapsible group for a side menu: a header row (label + chevron)
|
|
4
|
+
* that expands and collapses the items supplied as the default snippet. Pair
|
|
5
|
+
* several of these in a menu column to build a grouped navigation tree.
|
|
6
|
+
*/
|
|
7
|
+
declare const CollapsibleMenuGroup: import("svelte").Component<CollapsibleMenuGroupProps, {}, "isCollapsed">;
|
|
8
|
+
type CollapsibleMenuGroup = ReturnType<typeof CollapsibleMenuGroup>;
|
|
9
|
+
export default CollapsibleMenuGroup;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
/**
|
|
3
|
+
* Props for {@link CollapsibleMenuGroup} — a labelled group in a side menu
|
|
4
|
+
* whose items collapse and expand under a clickable header. The items are
|
|
5
|
+
* supplied as the default snippet.
|
|
6
|
+
*/
|
|
7
|
+
export interface CollapsibleMenuGroupProps {
|
|
8
|
+
/** Header label. */
|
|
9
|
+
title: string;
|
|
10
|
+
/** Two-way bindable collapsed state. Defaults to expanded. */
|
|
11
|
+
isCollapsed?: boolean;
|
|
12
|
+
/** The menu items revealed when expanded. */
|
|
13
|
+
children: Snippet;
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -19,25 +19,41 @@ native radio group (the radio inputs are visually hidden but focusable).
|
|
|
19
19
|
import type { OptionCardsProps } from './OptionCardsProps.js';
|
|
20
20
|
import Icon from './Icon.svelte';
|
|
21
21
|
|
|
22
|
-
let {
|
|
22
|
+
let {
|
|
23
|
+
name,
|
|
24
|
+
options,
|
|
25
|
+
selected,
|
|
26
|
+
onchange,
|
|
27
|
+
multiple = false,
|
|
28
|
+
selectedValues,
|
|
29
|
+
ontoggle,
|
|
30
|
+
}: OptionCardsProps<T> = $props();
|
|
23
31
|
</script>
|
|
24
32
|
|
|
25
33
|
<div
|
|
26
34
|
class="option-cards"
|
|
27
|
-
role=
|
|
35
|
+
role={multiple ? 'group' : 'radiogroup'}
|
|
28
36
|
>
|
|
29
37
|
{#each options as option (option.value)}
|
|
30
|
-
{@const isSelected =
|
|
38
|
+
{@const isSelected = multiple
|
|
39
|
+
? (selectedValues?.includes(option.value) ?? false)
|
|
40
|
+
: selected === option.value}
|
|
31
41
|
<label
|
|
32
42
|
class="card"
|
|
33
43
|
class:selected={isSelected}
|
|
34
44
|
>
|
|
35
45
|
<input
|
|
36
46
|
class="native"
|
|
37
|
-
type=
|
|
47
|
+
type={multiple ? 'checkbox' : 'radio'}
|
|
38
48
|
{name}
|
|
39
49
|
checked={isSelected}
|
|
40
|
-
onchange={() =>
|
|
50
|
+
onchange={() => {
|
|
51
|
+
if (multiple) {
|
|
52
|
+
ontoggle?.(option.value, !isSelected);
|
|
53
|
+
} else {
|
|
54
|
+
void onchange?.(option.value);
|
|
55
|
+
}
|
|
56
|
+
}}
|
|
41
57
|
/>
|
|
42
58
|
{#if option.icon}
|
|
43
59
|
<Icon
|
|
@@ -12,8 +12,17 @@ export interface OptionCardsProps<T> {
|
|
|
12
12
|
name?: string;
|
|
13
13
|
/** The option set rendered as cards. */
|
|
14
14
|
options: ReadonlyArray<OptionCard<T>>;
|
|
15
|
-
/** Currently-selected value. */
|
|
15
|
+
/** Currently-selected value (single-select mode). */
|
|
16
16
|
selected?: T;
|
|
17
|
-
/** Called when a card is chosen. May be `void` or `Promise<void>`. */
|
|
17
|
+
/** Called when a card is chosen (single-select mode). May be `void` or `Promise<void>`. */
|
|
18
18
|
onchange?: (value: T) => void | Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Enable multi-select: cards become independent checkboxes. Use with
|
|
21
|
+
* `selectedValues` and `ontoggle` instead of `selected` / `onchange`.
|
|
22
|
+
*/
|
|
23
|
+
multiple?: boolean;
|
|
24
|
+
/** Currently-selected values (multi-select mode). */
|
|
25
|
+
selectedValues?: ReadonlyArray<T>;
|
|
26
|
+
/** Called when a card is toggled on or off (multi-select mode). */
|
|
27
|
+
ontoggle?: (value: T, selected: boolean) => void;
|
|
19
28
|
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
<svelte:options runes />
|
|
2
|
+
|
|
3
|
+
<!--
|
|
4
|
+
@component
|
|
5
|
+
An icon button that toggles a floating popover anchored beneath it, with a
|
|
6
|
+
caret pointing back at the button. The popover holds arbitrary content (the
|
|
7
|
+
default snippet) and closes on click-outside or Escape.
|
|
8
|
+
-->
|
|
9
|
+
<script lang="ts">
|
|
10
|
+
import { Colour } from '../scss/colours.js';
|
|
11
|
+
import { Size } from '../Types/Internal/Size.js';
|
|
12
|
+
import Icon from './Icon.svelte';
|
|
13
|
+
import type { PopoverButtonProps } from './PopoverButtonProps.js';
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
icon,
|
|
17
|
+
label,
|
|
18
|
+
children,
|
|
19
|
+
align = 'center',
|
|
20
|
+
borderless = false,
|
|
21
|
+
open = $bindable(false),
|
|
22
|
+
}: PopoverButtonProps = $props();
|
|
23
|
+
|
|
24
|
+
let wrapper: HTMLDivElement | undefined = $state();
|
|
25
|
+
|
|
26
|
+
function onWindowPointerDown(e: MouseEvent) {
|
|
27
|
+
if (!open) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (wrapper && e.target instanceof Node && !wrapper.contains(e.target)) {
|
|
31
|
+
open = false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function onWindowKeydown(e: KeyboardEvent) {
|
|
36
|
+
if (open && e.key === 'Escape') {
|
|
37
|
+
open = false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<svelte:window
|
|
43
|
+
onpointerdown={onWindowPointerDown}
|
|
44
|
+
onkeydown={onWindowKeydown}
|
|
45
|
+
/>
|
|
46
|
+
|
|
47
|
+
<div
|
|
48
|
+
class="popover-button"
|
|
49
|
+
bind:this={wrapper}
|
|
50
|
+
>
|
|
51
|
+
<button
|
|
52
|
+
type="button"
|
|
53
|
+
class="trigger"
|
|
54
|
+
class:open
|
|
55
|
+
class:borderless
|
|
56
|
+
aria-label={label}
|
|
57
|
+
aria-expanded={open}
|
|
58
|
+
aria-haspopup="dialog"
|
|
59
|
+
onclick={() => {
|
|
60
|
+
open = !open;
|
|
61
|
+
}}
|
|
62
|
+
>
|
|
63
|
+
<Icon
|
|
64
|
+
name={icon}
|
|
65
|
+
size={Size.Large}
|
|
66
|
+
colour={open ? Colour['$brand-primary'] : Colour['$font-secondary']}
|
|
67
|
+
/>
|
|
68
|
+
</button>
|
|
69
|
+
|
|
70
|
+
{#if open}
|
|
71
|
+
<div
|
|
72
|
+
class="popover align-{align}"
|
|
73
|
+
role="dialog"
|
|
74
|
+
aria-label={label}
|
|
75
|
+
>
|
|
76
|
+
<span class="caret"></span>
|
|
77
|
+
<div class="popover-inner">
|
|
78
|
+
{@render children()}
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
{/if}
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<style>.popover-button {
|
|
85
|
+
position: relative;
|
|
86
|
+
display: inline-flex;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.trigger {
|
|
90
|
+
display: inline-flex;
|
|
91
|
+
align-items: center;
|
|
92
|
+
justify-content: center;
|
|
93
|
+
padding: 0.75rem;
|
|
94
|
+
cursor: pointer;
|
|
95
|
+
background-color: #ffffff;
|
|
96
|
+
border: solid 1px #cbd4da;
|
|
97
|
+
border-radius: 4px;
|
|
98
|
+
}
|
|
99
|
+
.trigger:hover {
|
|
100
|
+
background-color: #f4f7f9;
|
|
101
|
+
}
|
|
102
|
+
.trigger.open:not(.borderless) {
|
|
103
|
+
border-color: #259fc7;
|
|
104
|
+
}
|
|
105
|
+
.trigger.borderless {
|
|
106
|
+
border: none;
|
|
107
|
+
border-radius: 0;
|
|
108
|
+
background-color: transparent;
|
|
109
|
+
width: 100%;
|
|
110
|
+
height: 100%;
|
|
111
|
+
}
|
|
112
|
+
.trigger.borderless:hover {
|
|
113
|
+
background-color: #f4f7f9;
|
|
114
|
+
}
|
|
115
|
+
.trigger.borderless.open {
|
|
116
|
+
background-color: #f4fbfd;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.popover {
|
|
120
|
+
position: absolute;
|
|
121
|
+
top: calc(100% + 0.6rem);
|
|
122
|
+
z-index: 100;
|
|
123
|
+
background-color: #ffffff;
|
|
124
|
+
border: solid 1px #cbd4da;
|
|
125
|
+
border-radius: 4px;
|
|
126
|
+
box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.1333333333);
|
|
127
|
+
padding: 1.25rem;
|
|
128
|
+
width: max-content;
|
|
129
|
+
max-width: 80vw;
|
|
130
|
+
}
|
|
131
|
+
.popover.align-center {
|
|
132
|
+
left: 50%;
|
|
133
|
+
transform: translateX(-50%);
|
|
134
|
+
}
|
|
135
|
+
.popover.align-start {
|
|
136
|
+
left: 0;
|
|
137
|
+
}
|
|
138
|
+
.popover.align-end {
|
|
139
|
+
right: 0;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.caret {
|
|
143
|
+
position: absolute;
|
|
144
|
+
top: -6px;
|
|
145
|
+
width: 12px;
|
|
146
|
+
height: 12px;
|
|
147
|
+
background-color: #ffffff;
|
|
148
|
+
border-left: solid 1px #cbd4da;
|
|
149
|
+
border-top: solid 1px #cbd4da;
|
|
150
|
+
transform: rotate(45deg);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.align-center .caret {
|
|
154
|
+
left: 50%;
|
|
155
|
+
margin-left: -6px;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.align-start .caret {
|
|
159
|
+
left: 1.5rem;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.align-end .caret {
|
|
163
|
+
right: 1.5rem;
|
|
164
|
+
}</style>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { PopoverButtonProps } from './PopoverButtonProps.js';
|
|
2
|
+
/**
|
|
3
|
+
* An icon button that toggles a floating popover anchored beneath it, with a
|
|
4
|
+
* caret pointing back at the button. The popover holds arbitrary content (the
|
|
5
|
+
* default snippet) and closes on click-outside or Escape.
|
|
6
|
+
*/
|
|
7
|
+
declare const PopoverButton: import("svelte").Component<PopoverButtonProps, {}, "open">;
|
|
8
|
+
type PopoverButton = ReturnType<typeof PopoverButton>;
|
|
9
|
+
export default PopoverButton;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { icons } from './Icons.js';
|
|
3
|
+
/**
|
|
4
|
+
* Props for {@link PopoverButton} — an icon button that toggles a floating
|
|
5
|
+
* popover (with a caret pointing back at the button) holding arbitrary
|
|
6
|
+
* content. Closes on click-outside or Escape.
|
|
7
|
+
*/
|
|
8
|
+
export interface PopoverButtonProps {
|
|
9
|
+
/** Icon shown in the trigger button. */
|
|
10
|
+
icon: keyof typeof icons;
|
|
11
|
+
/** Accessible label for the trigger button. */
|
|
12
|
+
label: string;
|
|
13
|
+
/** Popover contents. */
|
|
14
|
+
children: Snippet;
|
|
15
|
+
/** Horizontal alignment of the popover relative to the button. Defaults to `'center'`. */
|
|
16
|
+
align?: 'center' | 'start' | 'end';
|
|
17
|
+
/** Drop the trigger's border so it can sit flush inside a grouped bar. */
|
|
18
|
+
borderless?: boolean;
|
|
19
|
+
/** Two-way bindable open state. */
|
|
20
|
+
open?: boolean;
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
<svelte:options runes />
|
|
2
|
+
|
|
3
|
+
<!--
|
|
4
|
+
@component
|
|
5
|
+
A titled card laid out as a fixed-width menu column beside a main content
|
|
6
|
+
area. Both regions are supplied by the consumer as snippets (`menu` and the
|
|
7
|
+
default `children`), so the component only owns the framing and layout.
|
|
8
|
+
-->
|
|
9
|
+
<script lang="ts">
|
|
10
|
+
import type { SidebarPanelProps } from './SidebarPanelProps.js';
|
|
11
|
+
|
|
12
|
+
let { title, subtitle, menu, children, menuWidth = '14rem' }: SidebarPanelProps = $props();
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<section class="sidebar-panel">
|
|
16
|
+
<header class="heading">
|
|
17
|
+
<h2>{title}</h2>
|
|
18
|
+
{#if subtitle}
|
|
19
|
+
<span class="subtitle">{subtitle}</span>
|
|
20
|
+
{/if}
|
|
21
|
+
</header>
|
|
22
|
+
|
|
23
|
+
<div class="body">
|
|
24
|
+
<aside
|
|
25
|
+
class="menu"
|
|
26
|
+
style="--menu-width: {menuWidth};"
|
|
27
|
+
>
|
|
28
|
+
{@render menu()}
|
|
29
|
+
</aside>
|
|
30
|
+
<div class="main">
|
|
31
|
+
{@render children()}
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
</section>
|
|
35
|
+
|
|
36
|
+
<style>.sidebar-panel {
|
|
37
|
+
display: flex;
|
|
38
|
+
flex-flow: column nowrap;
|
|
39
|
+
background-color: #ffffff;
|
|
40
|
+
border-radius: 4px;
|
|
41
|
+
box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.1333333333);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.heading {
|
|
45
|
+
display: flex;
|
|
46
|
+
flex-flow: column nowrap;
|
|
47
|
+
gap: 0.125rem;
|
|
48
|
+
padding: 1.5rem;
|
|
49
|
+
border-bottom: solid 1px #dae0e5;
|
|
50
|
+
}
|
|
51
|
+
.heading h2 {
|
|
52
|
+
margin: 0;
|
|
53
|
+
color: #46525b;
|
|
54
|
+
font-weight: 500;
|
|
55
|
+
font-size: 1.25rem;
|
|
56
|
+
}
|
|
57
|
+
.heading .subtitle {
|
|
58
|
+
color: #647d8e;
|
|
59
|
+
font-size: 0.875rem;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.body {
|
|
63
|
+
display: flex;
|
|
64
|
+
flex-flow: row nowrap;
|
|
65
|
+
align-items: stretch;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.menu {
|
|
69
|
+
width: var(--menu-width, 14rem);
|
|
70
|
+
flex-shrink: 0;
|
|
71
|
+
display: flex;
|
|
72
|
+
flex-flow: column nowrap;
|
|
73
|
+
gap: 0.75rem;
|
|
74
|
+
padding: 1.5rem;
|
|
75
|
+
border-right: solid 1px #dae0e5;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.main {
|
|
79
|
+
flex: 1;
|
|
80
|
+
min-width: 0;
|
|
81
|
+
display: flex;
|
|
82
|
+
flex-flow: column nowrap;
|
|
83
|
+
gap: 1.5rem;
|
|
84
|
+
padding: 1.5rem;
|
|
85
|
+
}</style>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SidebarPanelProps } from './SidebarPanelProps.js';
|
|
2
|
+
/**
|
|
3
|
+
* A titled card laid out as a fixed-width menu column beside a main content
|
|
4
|
+
* area. Both regions are supplied by the consumer as snippets (`menu` and the
|
|
5
|
+
* default `children`), so the component only owns the framing and layout.
|
|
6
|
+
*/
|
|
7
|
+
declare const SidebarPanel: import("svelte").Component<SidebarPanelProps, {}, "">;
|
|
8
|
+
type SidebarPanel = ReturnType<typeof SidebarPanel>;
|
|
9
|
+
export default SidebarPanel;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
/**
|
|
3
|
+
* Props for {@link SidebarPanel} — a titled card with a fixed-width menu
|
|
4
|
+
* column on the left and a main content area on the right. Generic layout
|
|
5
|
+
* shell; the consumer supplies both regions as snippets.
|
|
6
|
+
*/
|
|
7
|
+
export interface SidebarPanelProps {
|
|
8
|
+
/** Heading shown at the top of the panel. */
|
|
9
|
+
title: string;
|
|
10
|
+
/** Optional sub-heading shown under the title. */
|
|
11
|
+
subtitle?: string;
|
|
12
|
+
/** Left-hand menu column. */
|
|
13
|
+
menu: Snippet;
|
|
14
|
+
/** Main content area. */
|
|
15
|
+
children: Snippet;
|
|
16
|
+
/** Width of the menu column. Defaults to `'14rem'`. */
|
|
17
|
+
menuWidth?: string;
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -22,6 +22,12 @@ export { default as Checkbox } from './Components/Checkbox.svelte';
|
|
|
22
22
|
export type { CheckboxProps } from './Components/CheckboxProps.js';
|
|
23
23
|
export { default as CollapsibleCard } from './Components/CollapsibleCard.svelte';
|
|
24
24
|
export type { CollapsibleCardProps } from './Components/CollapsibleCardProps.js';
|
|
25
|
+
export { default as CollapsibleMenuGroup } from './Components/CollapsibleMenuGroup.svelte';
|
|
26
|
+
export type { CollapsibleMenuGroupProps } from './Components/CollapsibleMenuGroupProps.js';
|
|
27
|
+
export { default as PopoverButton } from './Components/PopoverButton.svelte';
|
|
28
|
+
export type { PopoverButtonProps } from './Components/PopoverButtonProps.js';
|
|
29
|
+
export { default as SidebarPanel } from './Components/SidebarPanel.svelte';
|
|
30
|
+
export type { SidebarPanelProps } from './Components/SidebarPanelProps.js';
|
|
25
31
|
export { default as ConditionBuilder } from './Components/ConditionBuilder/ConditionBuilder.svelte';
|
|
26
32
|
export type { ConditionBuilderProps } from './Components/ConditionBuilder/ConditionBuilderProps.js';
|
|
27
33
|
export { makeGroup, makeLeaf, type Combinator, type ConditionGroup, type ConditionLeaf, type ConditionNode, } from './Components/ConditionBuilder/ConditionTree.js';
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,9 @@ export {} from './Components/BubbleProps.js';
|
|
|
14
14
|
export { default as Button } from './Components/Button.svelte';
|
|
15
15
|
export { default as Checkbox } from './Components/Checkbox.svelte';
|
|
16
16
|
export { default as CollapsibleCard } from './Components/CollapsibleCard.svelte';
|
|
17
|
+
export { default as CollapsibleMenuGroup } from './Components/CollapsibleMenuGroup.svelte';
|
|
18
|
+
export { default as PopoverButton } from './Components/PopoverButton.svelte';
|
|
19
|
+
export { default as SidebarPanel } from './Components/SidebarPanel.svelte';
|
|
17
20
|
export { default as ConditionBuilder } from './Components/ConditionBuilder/ConditionBuilder.svelte';
|
|
18
21
|
export { makeGroup, makeLeaf, } from './Components/ConditionBuilder/ConditionTree.js';
|
|
19
22
|
export {} from './Components/Contexts/ContextMenuOptionProps.js';
|