@sveltia/ui 0.8.3 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,103 @@
1
+ <!--
2
+ @component
3
+ A combination of a normal `<Button>` (default action) and an arrow-only `<MenuButton>` (different
4
+ actions). For example, Microsoft Word has the Paste button along with the Keep Source Formatting,
5
+ Match Formatting and Keep Text Only options.
6
+ @see https://www.google.com/search?q=split+button&tbm=isch
7
+ -->
8
+ <script>
9
+ import { _ } from 'svelte-i18n';
10
+ import Icon from '../icon/icon.svelte';
11
+ import MenuButton from '../menu/menu-button.svelte';
12
+ import Button from './button.svelte';
13
+
14
+ /**
15
+ * The `class` attribute on the `<button>` element.
16
+ * @type {string}
17
+ */
18
+ let className = '';
19
+ export { className as class };
20
+ /**
21
+ * Whether to hide the widget. An alias of the `aria-hidden` attribute.
22
+ * @type {boolean | undefined}
23
+ */
24
+ export let hidden = undefined;
25
+ /**
26
+ * Whether to disable the widget. An alias of the `aria-disabled` attribute.
27
+ * @type {boolean}
28
+ */
29
+ export let disabled = false;
30
+ /**
31
+ * Text label displayed on the button.
32
+ * @type {string}
33
+ */
34
+ export let label = '';
35
+ /**
36
+ * The style variant of the button.
37
+ * @type {'primary' | 'secondary' | 'tertiary' | 'ghost' | undefined}
38
+ */
39
+ export let variant = undefined;
40
+ /**
41
+ * The size of the button.
42
+ * @type {'small' | 'medium' | 'large' | undefined}
43
+ */
44
+ export let size = 'medium';
45
+ /**
46
+ * Where to show the dropdown menu.
47
+ * @type {PopupPosition}
48
+ */
49
+ export let popupPosition = 'bottom-left';
50
+
51
+ /**
52
+ * @type {HTMLElement}
53
+ */
54
+ let wrapper;
55
+ </script>
56
+
57
+ <div
58
+ role="group"
59
+ class="sui split-button"
60
+ hidden={hidden || undefined}
61
+ aria-hidden={hidden}
62
+ aria-disabled={disabled}
63
+ aria-label={$_('_sui.split_button.x_options', { values: { name: label } })}
64
+ bind:this={wrapper}
65
+ >
66
+ <Button class={className} {hidden} {disabled} {label} {variant} {size} {...$$restProps} on:click>
67
+ <slot name="start-icon" slot="start-icon" />
68
+ </Button>
69
+ <MenuButton
70
+ iconic
71
+ {hidden}
72
+ {disabled}
73
+ {variant}
74
+ {size}
75
+ aria-label={$_('_sui.split_button.more_options')}
76
+ {popupPosition}
77
+ popupPositionBaseElement={wrapper}
78
+ >
79
+ <Icon slot="end-icon" name="arrow_drop_down" />
80
+ <slot name="popup" slot="popup" />
81
+ </MenuButton>
82
+ </div>
83
+
84
+ <style>.split-button {
85
+ display: inline-flex;
86
+ margin: var(--sui-focus-ring-width);
87
+ }
88
+ .split-button :global(button) {
89
+ margin: 0;
90
+ }
91
+ .split-button :global(button):not(.menu-button) {
92
+ border-top-right-radius: 0;
93
+ border-bottom-right-radius: 0;
94
+ }
95
+ .split-button :global(button):is(.menu-button) {
96
+ border-left-width: 0;
97
+ border-top-left-radius: 0;
98
+ border-bottom-left-radius: 0;
99
+ aspect-ratio: 3/4;
100
+ }
101
+ .split-button :global(button):is(.menu-button) :global(.icon) {
102
+ font-size: 20px;
103
+ }</style>
@@ -0,0 +1,61 @@
1
+ /** @typedef {typeof __propDef.props} SplitButtonProps */
2
+ /** @typedef {typeof __propDef.events} SplitButtonEvents */
3
+ /** @typedef {typeof __propDef.slots} SplitButtonSlots */
4
+ /**
5
+ * A combination of a normal `<Button>` (default action) and an arrow-only `<MenuButton>` (different
6
+ * actions). For example, Microsoft Word has the Paste button along with the Keep Source Formatting,
7
+ * Match Formatting and Keep Text Only options.
8
+ * @see https://www.google.com/search?q=split+button&tbm=isch
9
+ */
10
+ export default class SplitButton extends SvelteComponent<{
11
+ [x: string]: any;
12
+ class?: string;
13
+ label?: string;
14
+ disabled?: boolean;
15
+ size?: "small" | "medium" | "large";
16
+ hidden?: boolean;
17
+ variant?: "primary" | "secondary" | "tertiary" | "ghost";
18
+ popupPosition?: PopupPosition;
19
+ }, {
20
+ click: MouseEvent;
21
+ } & {
22
+ [evt: string]: CustomEvent<any>;
23
+ }, {
24
+ 'start-icon': {
25
+ slot: string;
26
+ };
27
+ popup: {
28
+ slot: string;
29
+ };
30
+ }> {
31
+ }
32
+ export type SplitButtonProps = typeof __propDef.props;
33
+ export type SplitButtonEvents = typeof __propDef.events;
34
+ export type SplitButtonSlots = typeof __propDef.slots;
35
+ import { SvelteComponent } from "svelte";
36
+ declare const __propDef: {
37
+ props: {
38
+ [x: string]: any;
39
+ class?: string;
40
+ label?: string;
41
+ disabled?: boolean;
42
+ size?: 'small' | 'medium' | 'large' | undefined;
43
+ hidden?: boolean | undefined;
44
+ variant?: 'primary' | 'secondary' | 'tertiary' | 'ghost' | undefined;
45
+ popupPosition?: PopupPosition;
46
+ };
47
+ events: {
48
+ click: MouseEvent;
49
+ } & {
50
+ [evt: string]: CustomEvent<any>;
51
+ };
52
+ slots: {
53
+ 'start-icon': {
54
+ slot: string;
55
+ };
56
+ popup: {
57
+ slot: string;
58
+ };
59
+ };
60
+ };
61
+ export {};
@@ -28,6 +28,11 @@
28
28
  * @type {PopupPosition}
29
29
  */
30
30
  export let popupPosition = 'bottom-left';
31
+ /**
32
+ * The base element of {@link popupPosition}. If omitted, this will be {@link buttonComponent}.
33
+ * @type {HTMLElement}
34
+ */
35
+ export let popupPositionBaseElement = undefined;
31
36
  /**
32
37
  * Text label displayed on the button.
33
38
  * @type {string}
@@ -86,7 +91,12 @@
86
91
  </Button>
87
92
  </div>
88
93
 
89
- <Popup anchor={buttonComponent?.element} position={popupPosition} bind:this={popupComponent}>
94
+ <Popup
95
+ anchor={buttonComponent?.element}
96
+ position={popupPosition}
97
+ positionBaseElement={popupPositionBaseElement}
98
+ bind:this={popupComponent}
99
+ >
90
100
  <slot name="popup" />
91
101
  </Popup>
92
102
 
@@ -16,6 +16,7 @@ export default class MenuButton extends SvelteComponent<{
16
16
  variant?: "link" | "primary" | "secondary" | "tertiary" | "ghost";
17
17
  iconic?: boolean;
18
18
  popupPosition?: PopupPosition;
19
+ popupPositionBaseElement?: HTMLElement;
19
20
  }, {
20
21
  [evt: string]: CustomEvent<any>;
21
22
  }, {
@@ -46,6 +47,7 @@ declare const __propDef: {
46
47
  variant?: 'primary' | 'secondary' | 'tertiary' | 'ghost' | 'link' | undefined;
47
48
  iconic?: boolean;
48
49
  popupPosition?: PopupPosition;
50
+ popupPositionBaseElement?: HTMLElement;
49
51
  };
50
52
  events: {
51
53
  [evt: string]: CustomEvent<any>;
@@ -84,74 +84,6 @@
84
84
 
85
85
  <style global>@import url("https://fonts.googleapis.com/css2?family=Merriweather+Sans:ital,wght@0,300;0,600;1,300&family=Noto+Sans+Mono&display=swap");
86
86
  @import url("https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&display=block");
87
- :global(:root[data-theme=light]),
88
- :global(:host[data-theme=light]) {
89
- --sui-foreground-color-1-hsl: var(--sui-base-hue) 5% 5%;
90
- --sui-foreground-color-2-hsl: var(--sui-base-hue) 5% 25%;
91
- --sui-foreground-color-3-hsl: var(--sui-base-hue) 5% 35%;
92
- --sui-foreground-color-4-hsl: var(--sui-base-hue) 5% 45%;
93
- --sui-foreground-color-5-hsl: var(--sui-base-hue) 5% 65%;
94
- --sui-background-color-1-hsl: var(--sui-base-hue) 5% 100%;
95
- --sui-background-color-2-hsl: var(--sui-base-hue) 5% 98%;
96
- --sui-background-color-3-hsl: var(--sui-base-hue) 5% 96%;
97
- --sui-background-color-4-hsl: var(--sui-base-hue) 5% 94%;
98
- --sui-background-color-5-hsl: var(--sui-base-hue) 5% 74%;
99
- --sui-border-color-1-hsl: var(--sui-base-hue) 5% 50%;
100
- --sui-border-color-2-hsl: var(--sui-base-hue) 5% 74%;
101
- --sui-border-color-3-hsl: var(--sui-base-hue) 5% 70%;
102
- --sui-shadow-color: var(--sui-base-hue) 10% 0%;
103
- --sui-primary-accent-color-text: hsl(var(--sui-base-hue) 80% 40%);
104
- --sui-primary-accent-color-light: hsl(var(--sui-base-hue) 80% 45%);
105
- --sui-primary-accent-color: hsl(var(--sui-base-hue) 80% 40%);
106
- --sui-primary-accent-color-dark: hsl(var(--sui-base-hue) 80% 35%);
107
- --sui-primary-accent-color-inverted: hsl(var(--sui-base-hue) 10% 100%);
108
- --sui-primary-accent-color-translucent: hsl(var(--sui-base-hue) 80% 50% / 40%);
109
- --sui-error-color-hue: 0;
110
- --sui-warning-color-hue: 40;
111
- --sui-info-color-hue: 210;
112
- --sui-success-color-hue: 100;
113
- --sui-alert-foreground-color-saturation: 85%;
114
- --sui-alert-foreground-color-lightness: 25%;
115
- --sui-alert-background-color-saturation: 65%;
116
- --sui-alert-background-color-lightness: 90%;
117
- --sui-alert-border-color-saturation: 48%;
118
- --sui-alert-border-color-lightness: 68%;
119
- }
120
-
121
- :global(:root[data-theme=dark]),
122
- :global(:host[data-theme=dark]) {
123
- --sui-foreground-color-1-hsl: var(--sui-base-hue) 10% 95%;
124
- --sui-foreground-color-2-hsl: var(--sui-base-hue) 10% 75%;
125
- --sui-foreground-color-3-hsl: var(--sui-base-hue) 10% 65%;
126
- --sui-foreground-color-4-hsl: var(--sui-base-hue) 10% 55%;
127
- --sui-foreground-color-5-hsl: var(--sui-base-hue) 10% 35%;
128
- --sui-background-color-1-hsl: var(--sui-base-hue) 10% 10%;
129
- --sui-background-color-2-hsl: var(--sui-base-hue) 10% 12%;
130
- --sui-background-color-3-hsl: var(--sui-base-hue) 10% 14%;
131
- --sui-background-color-4-hsl: var(--sui-base-hue) 10% 16%;
132
- --sui-background-color-5-hsl: var(--sui-base-hue) 10% 26%;
133
- --sui-border-color-1-hsl: var(--sui-base-hue) 10% 50%;
134
- --sui-border-color-2-hsl: var(--sui-base-hue) 10% 26%;
135
- --sui-border-color-3-hsl: var(--sui-base-hue) 10% 22%;
136
- --sui-shadow-color: var(--sui-base-hue) 10% 0%;
137
- --sui-primary-accent-color-text: hsl(var(--sui-base-hue) 100% 60%);
138
- --sui-primary-accent-color-light: hsl(var(--sui-base-hue) 100% 45%);
139
- --sui-primary-accent-color: hsl(var(--sui-base-hue) 100% 40%);
140
- --sui-primary-accent-color-dark: hsl(var(--sui-base-hue) 100% 35%);
141
- --sui-primary-accent-color-inverted: hsl(var(--sui-base-hue) 10% 100%);
142
- --sui-primary-accent-color-translucent: hsl(var(--sui-base-hue) 80% 50% / 40%);
143
- --sui-error-color-hue: 0;
144
- --sui-warning-color-hue: 40;
145
- --sui-info-color-hue: 210;
146
- --sui-success-color-hue: 100;
147
- --sui-alert-foreground-color-saturation: 85%;
148
- --sui-alert-foreground-color-lightness: 75%;
149
- --sui-alert-background-color-saturation: 40%;
150
- --sui-alert-background-color-lightness: 10%;
151
- --sui-alert-border-color-saturation: 38%;
152
- --sui-alert-border-color-lightness: 18%;
153
- }
154
-
155
87
  :global(:root),
156
88
  :global(:host) {
157
89
  --sui-base-hue: 210;
@@ -319,6 +251,146 @@
319
251
  --sui-tertiary-background-color-translucent: hsl(var(--sui-background-color-4-hsl));
320
252
  }
321
253
  }
254
+ :global(:root[data-theme=light]),
255
+ :global(:host[data-theme=light]) {
256
+ color-scheme: light;
257
+ --sui-foreground-color-1-hsl: var(--sui-base-hue) 5% 5%;
258
+ --sui-foreground-color-2-hsl: var(--sui-base-hue) 5% 25%;
259
+ --sui-foreground-color-3-hsl: var(--sui-base-hue) 5% 35%;
260
+ --sui-foreground-color-4-hsl: var(--sui-base-hue) 5% 45%;
261
+ --sui-foreground-color-5-hsl: var(--sui-base-hue) 5% 65%;
262
+ --sui-background-color-1-hsl: var(--sui-base-hue) 5% 100%;
263
+ --sui-background-color-2-hsl: var(--sui-base-hue) 5% 98%;
264
+ --sui-background-color-3-hsl: var(--sui-base-hue) 5% 96%;
265
+ --sui-background-color-4-hsl: var(--sui-base-hue) 5% 94%;
266
+ --sui-background-color-5-hsl: var(--sui-base-hue) 5% 74%;
267
+ --sui-border-color-1-hsl: var(--sui-base-hue) 5% 50%;
268
+ --sui-border-color-2-hsl: var(--sui-base-hue) 5% 74%;
269
+ --sui-border-color-3-hsl: var(--sui-base-hue) 5% 70%;
270
+ --sui-shadow-color: var(--sui-base-hue) 10% 0%;
271
+ --sui-primary-accent-color-text: hsl(var(--sui-base-hue) 80% 40%);
272
+ --sui-primary-accent-color-light: hsl(var(--sui-base-hue) 80% 45%);
273
+ --sui-primary-accent-color: hsl(var(--sui-base-hue) 80% 40%);
274
+ --sui-primary-accent-color-dark: hsl(var(--sui-base-hue) 80% 35%);
275
+ --sui-primary-accent-color-inverted: hsl(var(--sui-base-hue) 10% 100%);
276
+ --sui-primary-accent-color-translucent: hsl(var(--sui-base-hue) 80% 50% / 40%);
277
+ --sui-error-color-hue: 0;
278
+ --sui-warning-color-hue: 40;
279
+ --sui-info-color-hue: 210;
280
+ --sui-success-color-hue: 100;
281
+ --sui-alert-foreground-color-saturation: 85%;
282
+ --sui-alert-foreground-color-lightness: 25%;
283
+ --sui-alert-background-color-saturation: 65%;
284
+ --sui-alert-background-color-lightness: 90%;
285
+ --sui-alert-border-color-saturation: 48%;
286
+ --sui-alert-border-color-lightness: 68%;
287
+ }
288
+ :global(:root[data-theme=dark]),
289
+ :global(:host[data-theme=dark]) {
290
+ color-scheme: dark;
291
+ --sui-foreground-color-1-hsl: var(--sui-base-hue) 10% 95%;
292
+ --sui-foreground-color-2-hsl: var(--sui-base-hue) 10% 75%;
293
+ --sui-foreground-color-3-hsl: var(--sui-base-hue) 10% 65%;
294
+ --sui-foreground-color-4-hsl: var(--sui-base-hue) 10% 55%;
295
+ --sui-foreground-color-5-hsl: var(--sui-base-hue) 10% 35%;
296
+ --sui-background-color-1-hsl: var(--sui-base-hue) 10% 10%;
297
+ --sui-background-color-2-hsl: var(--sui-base-hue) 10% 12%;
298
+ --sui-background-color-3-hsl: var(--sui-base-hue) 10% 14%;
299
+ --sui-background-color-4-hsl: var(--sui-base-hue) 10% 16%;
300
+ --sui-background-color-5-hsl: var(--sui-base-hue) 10% 26%;
301
+ --sui-border-color-1-hsl: var(--sui-base-hue) 10% 50%;
302
+ --sui-border-color-2-hsl: var(--sui-base-hue) 10% 26%;
303
+ --sui-border-color-3-hsl: var(--sui-base-hue) 10% 22%;
304
+ --sui-shadow-color: var(--sui-base-hue) 10% 0%;
305
+ --sui-primary-accent-color-text: hsl(var(--sui-base-hue) 100% 60%);
306
+ --sui-primary-accent-color-light: hsl(var(--sui-base-hue) 100% 45%);
307
+ --sui-primary-accent-color: hsl(var(--sui-base-hue) 100% 40%);
308
+ --sui-primary-accent-color-dark: hsl(var(--sui-base-hue) 100% 35%);
309
+ --sui-primary-accent-color-inverted: hsl(var(--sui-base-hue) 10% 100%);
310
+ --sui-primary-accent-color-translucent: hsl(var(--sui-base-hue) 80% 50% / 40%);
311
+ --sui-error-color-hue: 0;
312
+ --sui-warning-color-hue: 40;
313
+ --sui-info-color-hue: 210;
314
+ --sui-success-color-hue: 100;
315
+ --sui-alert-foreground-color-saturation: 85%;
316
+ --sui-alert-foreground-color-lightness: 75%;
317
+ --sui-alert-background-color-saturation: 40%;
318
+ --sui-alert-background-color-lightness: 10%;
319
+ --sui-alert-border-color-saturation: 38%;
320
+ --sui-alert-border-color-lightness: 18%;
321
+ }
322
+ @media (prefers-color-scheme: light) {
323
+ :global(:root:not([data-theme])),
324
+ :global(:host:not([data-theme])) {
325
+ color-scheme: light;
326
+ --sui-foreground-color-1-hsl: var(--sui-base-hue) 5% 5%;
327
+ --sui-foreground-color-2-hsl: var(--sui-base-hue) 5% 25%;
328
+ --sui-foreground-color-3-hsl: var(--sui-base-hue) 5% 35%;
329
+ --sui-foreground-color-4-hsl: var(--sui-base-hue) 5% 45%;
330
+ --sui-foreground-color-5-hsl: var(--sui-base-hue) 5% 65%;
331
+ --sui-background-color-1-hsl: var(--sui-base-hue) 5% 100%;
332
+ --sui-background-color-2-hsl: var(--sui-base-hue) 5% 98%;
333
+ --sui-background-color-3-hsl: var(--sui-base-hue) 5% 96%;
334
+ --sui-background-color-4-hsl: var(--sui-base-hue) 5% 94%;
335
+ --sui-background-color-5-hsl: var(--sui-base-hue) 5% 74%;
336
+ --sui-border-color-1-hsl: var(--sui-base-hue) 5% 50%;
337
+ --sui-border-color-2-hsl: var(--sui-base-hue) 5% 74%;
338
+ --sui-border-color-3-hsl: var(--sui-base-hue) 5% 70%;
339
+ --sui-shadow-color: var(--sui-base-hue) 10% 0%;
340
+ --sui-primary-accent-color-text: hsl(var(--sui-base-hue) 80% 40%);
341
+ --sui-primary-accent-color-light: hsl(var(--sui-base-hue) 80% 45%);
342
+ --sui-primary-accent-color: hsl(var(--sui-base-hue) 80% 40%);
343
+ --sui-primary-accent-color-dark: hsl(var(--sui-base-hue) 80% 35%);
344
+ --sui-primary-accent-color-inverted: hsl(var(--sui-base-hue) 10% 100%);
345
+ --sui-primary-accent-color-translucent: hsl(var(--sui-base-hue) 80% 50% / 40%);
346
+ --sui-error-color-hue: 0;
347
+ --sui-warning-color-hue: 40;
348
+ --sui-info-color-hue: 210;
349
+ --sui-success-color-hue: 100;
350
+ --sui-alert-foreground-color-saturation: 85%;
351
+ --sui-alert-foreground-color-lightness: 25%;
352
+ --sui-alert-background-color-saturation: 65%;
353
+ --sui-alert-background-color-lightness: 90%;
354
+ --sui-alert-border-color-saturation: 48%;
355
+ --sui-alert-border-color-lightness: 68%;
356
+ }
357
+ }
358
+ @media (prefers-color-scheme: dark) {
359
+ :global(:root:not([data-theme])),
360
+ :global(:host:not([data-theme])) {
361
+ color-scheme: dark;
362
+ --sui-foreground-color-1-hsl: var(--sui-base-hue) 10% 95%;
363
+ --sui-foreground-color-2-hsl: var(--sui-base-hue) 10% 75%;
364
+ --sui-foreground-color-3-hsl: var(--sui-base-hue) 10% 65%;
365
+ --sui-foreground-color-4-hsl: var(--sui-base-hue) 10% 55%;
366
+ --sui-foreground-color-5-hsl: var(--sui-base-hue) 10% 35%;
367
+ --sui-background-color-1-hsl: var(--sui-base-hue) 10% 10%;
368
+ --sui-background-color-2-hsl: var(--sui-base-hue) 10% 12%;
369
+ --sui-background-color-3-hsl: var(--sui-base-hue) 10% 14%;
370
+ --sui-background-color-4-hsl: var(--sui-base-hue) 10% 16%;
371
+ --sui-background-color-5-hsl: var(--sui-base-hue) 10% 26%;
372
+ --sui-border-color-1-hsl: var(--sui-base-hue) 10% 50%;
373
+ --sui-border-color-2-hsl: var(--sui-base-hue) 10% 26%;
374
+ --sui-border-color-3-hsl: var(--sui-base-hue) 10% 22%;
375
+ --sui-shadow-color: var(--sui-base-hue) 10% 0%;
376
+ --sui-primary-accent-color-text: hsl(var(--sui-base-hue) 100% 60%);
377
+ --sui-primary-accent-color-light: hsl(var(--sui-base-hue) 100% 45%);
378
+ --sui-primary-accent-color: hsl(var(--sui-base-hue) 100% 40%);
379
+ --sui-primary-accent-color-dark: hsl(var(--sui-base-hue) 100% 35%);
380
+ --sui-primary-accent-color-inverted: hsl(var(--sui-base-hue) 10% 100%);
381
+ --sui-primary-accent-color-translucent: hsl(var(--sui-base-hue) 80% 50% / 40%);
382
+ --sui-error-color-hue: 0;
383
+ --sui-warning-color-hue: 40;
384
+ --sui-info-color-hue: 210;
385
+ --sui-success-color-hue: 100;
386
+ --sui-alert-foreground-color-saturation: 85%;
387
+ --sui-alert-foreground-color-lightness: 75%;
388
+ --sui-alert-background-color-saturation: 40%;
389
+ --sui-alert-background-color-lightness: 10%;
390
+ --sui-alert-border-color-saturation: 38%;
391
+ --sui-alert-border-color-lightness: 18%;
392
+ }
393
+ }
322
394
 
323
395
  :global(.material-symbols-outlined) {
324
396
  font-variation-settings: "FILL" 0, "wght" 300, "GRAD" 0, "opsz" 24;
@@ -37,6 +37,11 @@
37
37
  * @type {PopupPosition}
38
38
  */
39
39
  export let position = 'bottom-left';
40
+ /**
41
+ * The base element of {@link position}. If omitted, this will be {@link anchor}.
42
+ * @type {HTMLElement}
43
+ */
44
+ export let positionBaseElement = undefined;
40
45
  /**
41
46
  * Whether to show the popup at the center of the screen on mobile/tablet and ignore the defined
42
47
  * dropdown `position`.
@@ -74,7 +79,7 @@
74
79
 
75
80
  $: {
76
81
  if (anchor && modal?.dialog) {
77
- ({ open, style } = activatePopup(anchor, modal.dialog, position));
82
+ ({ open, style } = activatePopup(anchor, modal.dialog, position, positionBaseElement));
78
83
  contentType = anchor.getAttribute('aria-haspopup');
79
84
  }
80
85
  }
@@ -131,7 +136,31 @@
131
136
  </div>
132
137
  </Modal>
133
138
 
134
- <style>.content.touch {
139
+ <style>.content {
140
+ position: absolute;
141
+ overflow-y: auto;
142
+ outline-width: 0 !important;
143
+ color: var(--sui-primary-foreground-color);
144
+ background-color: var(--sui-secondary-background-color-translucent);
145
+ box-shadow: 0 8px 16px var(--sui-popup-shadow-color);
146
+ -webkit-backdrop-filter: blur(16px);
147
+ backdrop-filter: blur(16px);
148
+ transition-property: opacity, transform;
149
+ }
150
+ .content.listbox, .content.menu {
151
+ border-width: 1px;
152
+ border-style: solid;
153
+ border-color: var(--sui-secondary-border-color);
154
+ border-radius: 4px;
155
+ }
156
+ .content.listbox :global(.sui.listbox),
157
+ .content.listbox :global(.sui.menu), .content.menu :global(.sui.listbox),
158
+ .content.menu :global(.sui.menu) {
159
+ border-width: 0;
160
+ border-radius: 0;
161
+ background-color: transparent;
162
+ }
163
+ .content.touch {
135
164
  position: static;
136
165
  border-width: 0 !important;
137
166
  border-radius: 4px !important;
@@ -159,29 +188,4 @@
159
188
  transition-duration: 300ms;
160
189
  opacity: 0;
161
190
  transform: translateY(-8px);
162
- }
163
-
164
- .content {
165
- position: absolute;
166
- overflow-y: auto;
167
- outline-width: 0 !important;
168
- color: var(--sui-primary-foreground-color);
169
- background-color: var(--sui-secondary-background-color-translucent);
170
- box-shadow: 0 8px 16px var(--sui-popup-shadow-color);
171
- -webkit-backdrop-filter: blur(16px);
172
- backdrop-filter: blur(16px);
173
- transition-property: opacity, transform;
174
- }
175
- .content.listbox, .content.menu {
176
- border-width: 1px;
177
- border-style: solid;
178
- border-color: var(--sui-secondary-border-color);
179
- border-radius: 4px;
180
- }
181
- .content.listbox :global(.sui.listbox),
182
- .content.listbox :global(.sui.menu), .content.menu :global(.sui.listbox),
183
- .content.menu :global(.sui.menu) {
184
- border-width: 0;
185
- border-radius: 0;
186
- background-color: transparent;
187
191
  }</style>
@@ -9,6 +9,7 @@ export default class Popup extends SvelteComponent<{
9
9
  position?: PopupPosition;
10
10
  open?: import("svelte/store").Writable<boolean>;
11
11
  content?: HTMLElement;
12
+ positionBaseElement?: HTMLElement;
12
13
  touchOptimized?: boolean;
13
14
  }, {
14
15
  opening: CustomEvent<any>;
@@ -38,6 +39,9 @@ export default class Popup extends SvelteComponent<{
38
39
  set position(arg: PopupPosition);
39
40
  get position(): PopupPosition;
40
41
  /**accessor*/
42
+ set positionBaseElement(arg: HTMLElement);
43
+ get positionBaseElement(): HTMLElement;
44
+ /**accessor*/
41
45
  set touchOptimized(arg: boolean);
42
46
  get touchOptimized(): boolean;
43
47
  }
@@ -53,6 +57,7 @@ declare const __propDef: {
53
57
  position?: PopupPosition;
54
58
  open?: import('svelte/store').Writable<boolean>;
55
59
  content?: HTMLElement;
60
+ positionBaseElement?: HTMLElement;
56
61
  touchOptimized?: boolean;
57
62
  };
58
63
  events: {
@@ -6,6 +6,7 @@ export { default as Alert } from "./components/alert/alert.svelte";
6
6
  export { default as Button } from "./components/button/button.svelte";
7
7
  export { default as SelectButtonGroup } from "./components/button/select-button-group.svelte";
8
8
  export { default as SelectButton } from "./components/button/select-button.svelte";
9
+ export { default as SplitButton } from "./components/button/split-button.svelte";
9
10
  export { default as Calendar } from "./components/calendar/calendar.svelte";
10
11
  export { default as CheckboxGroup } from "./components/checkbox/checkbox-group.svelte";
11
12
  export { default as Checkbox } from "./components/checkbox/checkbox.svelte";
package/package/index.js CHANGED
@@ -31,6 +31,7 @@ export { default as Alert } from './components/alert/alert.svelte';
31
31
  export { default as Button } from './components/button/button.svelte';
32
32
  export { default as SelectButtonGroup } from './components/button/select-button-group.svelte';
33
33
  export { default as SelectButton } from './components/button/select-button.svelte';
34
+ export { default as SplitButton } from './components/button/split-button.svelte';
34
35
  export { default as Calendar } from './components/calendar/calendar.svelte';
35
36
  export { default as CheckboxGroup } from './components/checkbox/checkbox-group.svelte';
36
37
  export { default as Checkbox } from './components/checkbox/checkbox.svelte';
@@ -15,6 +15,10 @@ export namespace strings {
15
15
  const next_month: string;
16
16
  const today: string;
17
17
  }
18
+ namespace split_button {
19
+ const x_options: string;
20
+ const more_options: string;
21
+ }
18
22
  namespace combobox {
19
23
  const select_an_option: string;
20
24
  }
@@ -15,6 +15,10 @@ export const strings = {
15
15
  next_month: 'Next Month',
16
16
  today: 'Today',
17
17
  },
18
+ split_button: {
19
+ x_options: '{name} Options',
20
+ more_options: 'More Options',
21
+ },
18
22
  combobox: {
19
23
  select_an_option: 'Select an option…',
20
24
  },
@@ -15,6 +15,10 @@ export namespace strings {
15
15
  const next_month: string;
16
16
  const today: string;
17
17
  }
18
+ namespace split_button {
19
+ const x_options: string;
20
+ const more_options: string;
21
+ }
18
22
  namespace combobox {
19
23
  const select_an_option: string;
20
24
  }
@@ -15,6 +15,10 @@ export const strings = {
15
15
  next_month: '翌月',
16
16
  today: '今日',
17
17
  },
18
+ split_button: {
19
+ x_options: '{name} オプション',
20
+ more_options: 'その他のオプション',
21
+ },
18
22
  combobox: {
19
23
  select_an_option: 'オプションを選択…',
20
24
  },
@@ -9,8 +9,10 @@ declare class Popup {
9
9
  * @param {HTMLButtonElement} anchorElement `<button>` element that triggers the popup.
10
10
  * @param {HTMLDialogElement} popupElement `<dialog>` element to be used for the popup.
11
11
  * @param {PopupPosition} position Where to show the popup content.
12
+ * @param {HTMLElement} [positionBaseElement] The base element of the `position`. If omitted, this
13
+ * will be the `anchorElement`.
12
14
  */
13
- constructor(anchorElement: HTMLButtonElement, popupElement: HTMLDialogElement, position: PopupPosition);
15
+ constructor(anchorElement: HTMLButtonElement, popupElement: HTMLDialogElement, position: PopupPosition, positionBaseElement?: HTMLElement);
14
16
  open: import("svelte/store").Writable<boolean>;
15
17
  style: import("svelte/store").Writable<{
16
18
  inset: any;
@@ -22,6 +24,7 @@ declare class Popup {
22
24
  anchorElement: HTMLButtonElement;
23
25
  popupElement: HTMLDialogElement;
24
26
  position: PopupPosition;
27
+ positionBaseElement: HTMLElement;
25
28
  id: string;
26
29
  /** @type {boolean} */
27
30
  get isDisabled(): boolean;
@@ -89,11 +89,14 @@ class Popup {
89
89
  * @param {HTMLButtonElement} anchorElement `<button>` element that triggers the popup.
90
90
  * @param {HTMLDialogElement} popupElement `<dialog>` element to be used for the popup.
91
91
  * @param {PopupPosition} position Where to show the popup content.
92
+ * @param {HTMLElement} [positionBaseElement] The base element of the `position`. If omitted, this
93
+ * will be the `anchorElement`.
92
94
  */
93
- constructor(anchorElement, popupElement, position) {
95
+ constructor(anchorElement, popupElement, position, positionBaseElement) {
94
96
  this.anchorElement = anchorElement;
95
97
  this.popupElement = popupElement; // = backdrop
96
98
  this.position = position;
99
+ this.positionBaseElement = positionBaseElement ?? anchorElement;
97
100
  this.id = getRandomId('popup');
98
101
 
99
102
  this.anchorElement.setAttribute('aria-controls', this.id);
@@ -168,8 +171,8 @@ class Popup {
168
171
  * Check the position of the anchor element.
169
172
  */
170
173
  checkPosition() {
171
- this.observer.unobserve(this.anchorElement);
172
- this.observer.observe(this.anchorElement);
174
+ this.observer.unobserve(this.positionBaseElement);
175
+ this.observer.observe(this.positionBaseElement);
173
176
  }
174
177
  }
175
178
 
@@ -1,5 +1,5 @@
1
- :root[data-theme="light"],
2
- :host[data-theme="light"] {
1
+ @mixin light-theme {
2
+ color-scheme: light;
3
3
  --sui-foreground-color-1-hsl: var(--sui-base-hue) 5% 5%; // highlight
4
4
  --sui-foreground-color-2-hsl: var(--sui-base-hue) 5% 25%; // primary
5
5
  --sui-foreground-color-3-hsl: var(--sui-base-hue) 5% 35%; // secondary
@@ -34,8 +34,8 @@
34
34
  --sui-alert-border-color-lightness: 68%;
35
35
  }
36
36
 
37
- :root[data-theme="dark"],
38
- :host[data-theme="dark"] {
37
+ @mixin dark-theme {
38
+ color-scheme: dark;
39
39
  --sui-foreground-color-1-hsl: var(--sui-base-hue) 10% 95%; // highlight
40
40
  --sui-foreground-color-2-hsl: var(--sui-base-hue) 10% 75%; // primary
41
41
  --sui-foreground-color-3-hsl: var(--sui-base-hue) 10% 65%; // secondary
@@ -249,4 +249,23 @@
249
249
  --sui-secondary-background-color-translucent: hsl(var(--sui-background-color-3-hsl));
250
250
  --sui-tertiary-background-color-translucent: hsl(var(--sui-background-color-4-hsl));
251
251
  }
252
+
253
+ &[data-theme="light"] {
254
+ @include light-theme;
255
+ }
256
+
257
+ &[data-theme="dark"] {
258
+ @include dark-theme;
259
+ }
260
+
261
+ // Follow the system appearance setting when the `data-theme` attribute is not set (yet)
262
+ &:not([data-theme]) {
263
+ @media (prefers-color-scheme: light) {
264
+ @include light-theme;
265
+ }
266
+
267
+ @media (prefers-color-scheme: dark) {
268
+ @include dark-theme;
269
+ }
270
+ }
252
271
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltia/ui",
3
- "version": "0.8.3",
3
+ "version": "0.9.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -23,33 +23,33 @@
23
23
  "test:unit": "vitest"
24
24
  },
25
25
  "dependencies": {
26
- "svelte": "^4.2.6"
26
+ "svelte": "^4.2.7"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@playwright/test": "^1.40.0",
30
30
  "@sveltejs/adapter-auto": "2.1.1",
31
31
  "@sveltejs/kit": "^1.27.6",
32
- "@sveltejs/package": "^2.2.2",
32
+ "@sveltejs/package": "^2.2.3",
33
33
  "cspell": "^8.0.0",
34
34
  "eslint": "^8.54.0",
35
35
  "eslint-config-airbnb-base": "^15.0.0",
36
36
  "eslint-config-prettier": "^9.0.0",
37
37
  "eslint-plugin-import": "^2.29.0",
38
38
  "eslint-plugin-jsdoc": "^46.9.0",
39
- "eslint-plugin-svelte": "^2.35.0",
39
+ "eslint-plugin-svelte": "^2.35.1",
40
40
  "npm-run-all": "^4.1.5",
41
41
  "postcss": "^8.4.31",
42
42
  "postcss-html": "^1.5.0",
43
43
  "prettier": "^3.1.0",
44
- "prettier-plugin-svelte": "^3.1.0",
44
+ "prettier-plugin-svelte": "^3.1.2",
45
45
  "sass": "^1.69.5",
46
46
  "stylelint": "^15.11.0",
47
47
  "stylelint-config-recommended-scss": "^13.1.0",
48
48
  "stylelint-scss": "^5.3.1",
49
- "svelte-check": "^3.6.0",
49
+ "svelte-check": "^3.6.2",
50
50
  "svelte-i18n": "^4.0.0",
51
51
  "svelte-migrate": "^1.2.7",
52
- "svelte-preprocess": "^5.1.0",
52
+ "svelte-preprocess": "^5.1.1",
53
53
  "tslib": "^2.6.2",
54
54
  "vite": "^4.5.0",
55
55
  "vitest": "^0.34.6"
@@ -76,6 +76,11 @@
76
76
  "svelte": "./package/components/button/select-button.svelte",
77
77
  "default": "./package/components/button/select-button.svelte"
78
78
  },
79
+ "./components/button/split-button.svelte": {
80
+ "types": "./package/components/button/split-button.svelte.d.ts",
81
+ "svelte": "./package/components/button/split-button.svelte",
82
+ "default": "./package/components/button/split-button.svelte"
83
+ },
79
84
  "./components/calendar/calendar.svelte": {
80
85
  "types": "./package/components/calendar/calendar.svelte.d.ts",
81
86
  "svelte": "./package/components/calendar/calendar.svelte",
@@ -425,6 +430,9 @@
425
430
  "components/button/select-button.svelte": [
426
431
  "./package/components/button/select-button.svelte.d.ts"
427
432
  ],
433
+ "components/button/split-button.svelte": [
434
+ "./package/components/button/split-button.svelte.d.ts"
435
+ ],
428
436
  "components/calendar/calendar.svelte": [
429
437
  "./package/components/calendar/calendar.svelte.d.ts"
430
438
  ],