@sveltia/ui 0.28.3 → 0.29.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.
Files changed (30) hide show
  1. package/dist/components/alert/infobar.svelte +1 -1
  2. package/dist/components/button/floating-action-button-wrapper.svelte +2 -1
  3. package/dist/components/button/select-button-group.svelte +5 -5
  4. package/dist/components/button/split-button.svelte +5 -5
  5. package/dist/components/calendar/calendar.svelte +3 -2
  6. package/dist/components/checkbox/checkbox.svelte +28 -3
  7. package/dist/components/checkbox/checkbox.svelte.d.ts +13 -1
  8. package/dist/components/disclosure/disclosure.svelte +4 -1
  9. package/dist/components/drawer/drawer.svelte +40 -14
  10. package/dist/components/listbox/listbox.svelte +8 -4
  11. package/dist/components/menu/menu-item.svelte +6 -2
  12. package/dist/components/radio/radio.svelte +21 -0
  13. package/dist/components/radio/radio.svelte.d.ts +13 -1
  14. package/dist/components/select/combobox.svelte +11 -6
  15. package/dist/components/select/select-tags.svelte +2 -1
  16. package/dist/components/slider/slider.svelte +15 -5
  17. package/dist/components/switch/switch.svelte +7 -1
  18. package/dist/components/tabs/tab-list.svelte +13 -7
  19. package/dist/components/text-editor/lexical-root.svelte +9 -5
  20. package/dist/components/text-editor/text-editor.svelte +4 -1
  21. package/dist/components/text-editor/toolbar/toolbar-wrapper.svelte +4 -1
  22. package/dist/components/text-field/number-input.svelte +18 -12
  23. package/dist/components/text-field/password-input.svelte +9 -4
  24. package/dist/components/text-field/search-bar.svelte +6 -2
  25. package/dist/components/text-field/text-input.svelte +5 -2
  26. package/dist/components/toast/toast.svelte +34 -6
  27. package/dist/components/util/app-shell.svelte +1 -1
  28. package/dist/services/group.svelte.js +1 -0
  29. package/dist/services/popup.svelte.js +16 -0
  30. package/package.json +45 -55
@@ -92,7 +92,7 @@
92
92
  flex: auto;
93
93
  display: flex;
94
94
  align-items: center;
95
- justify-content: var(--sui-infobar-message-justify-content, left);
95
+ justify-content: var(--sui-infobar-message-justify-content, start);
96
96
  gap: var(--sui-infobar-message-gap, 6px);
97
97
  padding: var(--sui-infobar-message-padding, 0 8px);
98
98
  }
@@ -27,7 +27,8 @@
27
27
  .floating-action-button-wrapper {
28
28
  display: block;
29
29
  position: fixed;
30
- inset: auto 16px 72px auto;
30
+ inset-inline-end: 16px;
31
+ inset-block-end: 72px;
31
32
  z-index: 100;
32
33
  }
33
34
  .floating-action-button-wrapper :global(button) {
@@ -77,15 +77,15 @@
77
77
  color: var(--sui-primary-foreground-color);
78
78
  }
79
79
  .select-button-group :global(button:first-child) {
80
- border-top-left-radius: 4px !important;
81
- border-bottom-left-radius: 4px !important;
80
+ border-start-start-radius: 4px !important;
81
+ border-end-start-radius: 4px !important;
82
82
  }
83
83
  .select-button-group :global(button:not(:first-child)) {
84
- border-left-width: 0;
84
+ border-inline-start-width: 0;
85
85
  }
86
86
  .select-button-group :global(button:last-child) {
87
- border-top-right-radius: 4px !important;
88
- border-bottom-right-radius: 4px !important;
87
+ border-start-end-radius: 4px !important;
88
+ border-end-end-radius: 4px !important;
89
89
  }
90
90
  .select-button-group :global(button[aria-checked=true]) {
91
91
  color: var(--sui-highlight-foreground-color);
@@ -91,12 +91,12 @@
91
91
  margin: 0;
92
92
  }
93
93
  .split-button :global(button.menu-button) {
94
- border-left-width: 0;
95
- border-top-left-radius: 0;
96
- border-bottom-left-radius: 0;
94
+ border-inline-start-width: 0;
95
+ border-start-start-radius: 0;
96
+ border-end-start-radius: 0;
97
97
  aspect-ratio: 3/4;
98
98
  }
99
99
  .split-button :global(button:not(.menu-button)) {
100
- border-top-right-radius: 0;
101
- border-bottom-right-radius: 0;
100
+ border-start-end-radius: 0;
101
+ border-end-end-radius: 0;
102
102
  }</style>
@@ -4,6 +4,7 @@
4
4
  -->
5
5
  <script>
6
6
  import { _ } from 'svelte-i18n';
7
+ import { browser } from '$app/environment';
7
8
  import Button from '../button/button.svelte';
8
9
  import Divider from '../divider/divider.svelte';
9
10
  import Spacer from '../divider/spacer.svelte';
@@ -98,7 +99,7 @@
98
99
  //
99
100
  }}
100
101
  >
101
- <Icon name="chevron_right" />
102
+ <Icon name={browser && document.dir === 'rtl' ? 'chevron_left' : 'chevron_right'} />
102
103
  </Button>
103
104
  </div>
104
105
  <div role="none" class="grid">
@@ -142,7 +143,7 @@
142
143
  firstDay = firstDay;
143
144
  }}
144
145
  >
145
- <Icon name="chevron_right" />
146
+ <Icon name={browser && document.dir === 'rtl' ? 'chevron_left' : 'chevron_right'} />
146
147
  </Button>
147
148
  </div>
148
149
  <div role="listbox" class="grid">
@@ -26,6 +26,9 @@
26
26
  * @property {string} [label] Text label displayed next to the checkbox.
27
27
  * @property {string} [aria-label] `aria-label` attribute.
28
28
  * @property {Snippet} [checkIcon] Check icon slot content.
29
+ * @property {string[]} [group] The two-way bound variable to manage the state of a group of
30
+ * checkboxes. It works in the same way as the [`<input
31
+ * bind:group>`](https://svelte.dev/docs/svelte/bind#input-bind:group) of Svelte.
29
32
  */
30
33
 
31
34
  /**
@@ -44,6 +47,7 @@
44
47
  invalid = false,
45
48
  label = undefined,
46
49
  'aria-label': ariaLabel,
50
+ group = $bindable([]),
47
51
  onChange,
48
52
  children,
49
53
  checkIcon,
@@ -60,6 +64,17 @@
60
64
  let buttonElement = $state();
61
65
 
62
66
  const indeterminate = $derived(checked === 'mixed');
67
+
68
+ // Sync `checked` with `group` and `value`
69
+ $effect(() => {
70
+ if (group.includes(value)) {
71
+ if (checked !== true) {
72
+ checked = true;
73
+ }
74
+ } else if (checked !== false) {
75
+ checked = false;
76
+ }
77
+ });
63
78
  </script>
64
79
 
65
80
  <div
@@ -99,9 +114,19 @@
99
114
  event.preventDefault();
100
115
  event.stopPropagation();
101
116
 
102
- if (!disabled && !readonly) {
103
- checked = indeterminate ? true : !checked;
104
- onChange?.(new CustomEvent('Change', { detail: { checked } }));
117
+ if (disabled || readonly) {
118
+ return;
119
+ }
120
+
121
+ checked = indeterminate ? true : !checked;
122
+ onChange?.(new CustomEvent('Change', { detail: { checked } }));
123
+
124
+ if (checked) {
125
+ if (!group.includes(value)) {
126
+ group = [...group, value];
127
+ }
128
+ } else if (group.includes(value)) {
129
+ group = group.filter((v) => v !== value);
105
130
  }
106
131
  }}
107
132
  >
@@ -41,7 +41,13 @@ declare const Checkbox: import("svelte").Component<ButtonProps & import("../../t
41
41
  * Check icon slot content.
42
42
  */
43
43
  checkIcon?: Snippet<[]> | undefined;
44
- } & Record<string, any>, {}, "checked">;
44
+ /**
45
+ * The two-way bound variable to manage the state of a group of
46
+ * checkboxes. It works in the same way as the [`<input
47
+ * bind:group>`](https://svelte.dev/docs/svelte/bind#input-bind:group) of Svelte.
48
+ */
49
+ group?: string[] | undefined;
50
+ } & Record<string, any>, {}, "group" | "checked">;
45
51
  type Props = {
46
52
  /**
47
53
  * The `class` attribute on the wrapper element.
@@ -74,6 +80,12 @@ type Props = {
74
80
  * Check icon slot content.
75
81
  */
76
82
  checkIcon?: Snippet<[]> | undefined;
83
+ /**
84
+ * The two-way bound variable to manage the state of a group of
85
+ * checkboxes. It works in the same way as the [`<input
86
+ * bind:group>`](https://svelte.dev/docs/svelte/bind#input-bind:group) of Svelte.
87
+ */
88
+ group?: string[] | undefined;
77
89
  };
78
90
  import type { ButtonProps } from '../../typedefs';
79
91
  import type { Snippet } from 'svelte';
@@ -92,9 +92,12 @@
92
92
  .disclosure :global(button) :global(.icon) {
93
93
  transition: all 200ms;
94
94
  }
95
- .disclosure :global(button[aria-expanded=false]) :global(.icon) {
95
+ .disclosure :global(button[aria-expanded=false]) :global(.icon:dir(ltr)) {
96
96
  transform: rotate(-90deg);
97
97
  }
98
+ .disclosure :global(button[aria-expanded=false]) :global(.icon:dir(rtl)) {
99
+ transform: rotate(90deg);
100
+ }
98
101
 
99
102
  .inner {
100
103
  display: contents;
@@ -164,31 +164,47 @@
164
164
  transition-duration: 300ms;
165
165
  }
166
166
  .content.right {
167
- inset: var(--sui-drawer-right-content-inset, 0 0 0 auto);
168
- border-radius: var(--sui-drawer-right-content-border-radius, var(--sui-drawer-content-border-radius, 4px 0 0 4px));
167
+ inset-block: var(--sui-drawer-right-content-inset-block, 0);
168
+ inset-inline: var(--sui-drawer-right-content-inset-inline, auto 0);
169
+ border-start-start-radius: var(--sui-drawer-right-content-border-start-start-radius, var(--sui-drawer-content-border-start-start-radius, 4px));
170
+ border-start-end-radius: var(--sui-drawer-right-content-border-start-end-radius, var(--sui-drawer-content-border-start-end-radius, 0));
171
+ border-end-end-radius: var(--sui-drawer-right-content-border-end-end-radius, var(--sui-drawer-content-border-end-end-radius, 0));
172
+ border-end-start-radius: var(--sui-drawer-right-content-border-end-start-radius, var(--sui-drawer-content-border-end-start-radius, 4px));
169
173
  }
170
174
  .content.right.full {
171
175
  border-radius: var(--sui-drawer-right-full-content-border-radius, var(--sui-drawer-right-content-border-radius, 0));
172
176
  }
173
177
  .content.right .extra-control {
174
- inset: 0 100% auto auto;
178
+ inset-block-start: 0;
179
+ inset-inline-end: 100%;
175
180
  }
176
- :global(dialog:not(.open)) .content.right {
181
+ :global(dialog:not(.open)) .content.right:dir(ltr) {
177
182
  transform: translateX(105%);
178
183
  }
184
+ :global(dialog:not(.open)) .content.right:dir(rtl) {
185
+ transform: translateX(-105%);
186
+ }
179
187
  .content.left {
180
- inset: var(--sui-drawer-left-content-inset, 0 auto 0 0);
181
- border-radius: var(--sui-drawer-left-content-border-radius, var(--sui-drawer-content-border-radius, 0 4px 4px 0));
188
+ inset-block: var(--sui-drawer-left-content-inset-block, 0);
189
+ inset-inline: var(--sui-drawer-left-content-inset-inline, 0 auto);
190
+ border-start-start-radius: var(--sui-drawer-left-content-border-start-start-radius, var(--sui-drawer-content-border-start-start-radius, 0));
191
+ border-start-end-radius: var(--sui-drawer-left-content-border-start-end-radius, var(--sui-drawer-content-border-start-end-radius, 4px));
192
+ border-end-end-radius: var(--sui-drawer-left-content-border-end-end-radius, var(--sui-drawer-content-border-end-end-radius, 4px));
193
+ border-end-start-radius: var(--sui-drawer-left-content-border-end-start-radius, var(--sui-drawer-content-border-end-start-radius, 0));
182
194
  }
183
195
  .content.left.full {
184
196
  border-radius: var(--sui-drawer-left-full-content-border-radius, var(--sui-drawer-left-content-border-radius, 0));
185
197
  }
186
198
  .content.left .extra-control {
187
- inset: 0 auto auto 100%;
199
+ inset-block-start: 0;
200
+ inset-inline-start: 100%;
188
201
  }
189
- :global(dialog:not(.open)) .content.left {
202
+ :global(dialog:not(.open)) .content.left:dir(ltr) {
190
203
  transform: translateX(-105%);
191
204
  }
205
+ :global(dialog:not(.open)) .content.left:dir(rtl) {
206
+ transform: translateX(105%);
207
+ }
192
208
  :global(dialog.open) .content.vertical {
193
209
  transform: translateX(0%);
194
210
  }
@@ -218,27 +234,37 @@
218
234
  height: 100dvh;
219
235
  }
220
236
  .content.top {
221
- inset: var(--sui-drawer-top-content-inset, 0 0 auto 0);
222
- border-radius: var(--sui-drawer-top-content-border-radius, var(--sui-drawer-content-border-radius, 0 0 4px 4px));
237
+ inset-block-start: var(--sui-drawer-top-content-inset-block, 0);
238
+ inset-inline: var(--sui-drawer-top-content-inset-inline, 0 0);
239
+ border-start-start-radius: var(--sui-drawer-top-content-border-start-start-radius, var(--sui-drawer-content-border-start-start-radius, 0));
240
+ border-start-end-radius: var(--sui-drawer-top-content-border-start-end-radius, var(--sui-drawer-content-border-start-end-radius, 0));
241
+ border-end-end-radius: var(--sui-drawer-top-content-border-end-end-radius, var(--sui-drawer-content-border-end-end-radius, 4px));
242
+ border-end-start-radius: var(--sui-drawer-top-content-border-end-start-radius, var(--sui-drawer-content-border-end-start-radius, 4px));
223
243
  }
224
244
  .content.top.full {
225
245
  border-radius: var(--sui-drawer-top-full-content-border-radius, var(--sui-drawer-top-content-border-radius, 0));
226
246
  }
227
247
  .content.top .extra-control {
228
- inset: 100% 0 auto auto;
248
+ inset-block-start: 100%;
249
+ inset-inline-end: 0;
229
250
  }
230
251
  :global(dialog:not(.open)) .content.top {
231
252
  transform: translateY(-105%);
232
253
  }
233
254
  .content.bottom {
234
- inset: var(--sui-drawer-bottom-content-inset, auto 0 0 0);
235
- border-radius: var(--sui-drawer-bottom-content-border-radius, var(--sui-drawer-content-border-radius, 4px 4px 0 0));
255
+ inset-block: var(--sui-drawer-bottom-content-inset-block, auto 0);
256
+ inset-inline: var(--sui-drawer-bottom-content-inset-inline, 0);
257
+ border-start-start-radius: var(--sui-drawer-bottom-content-border-start-start-radius, var(--sui-drawer-content-border-start-start-radius, 4px));
258
+ border-start-end-radius: var(--sui-drawer-bottom-content-border-start-end-radius, var(--sui-drawer-content-border-start-end-radius, 4px));
259
+ border-end-end-radius: var(--sui-drawer-bottom-content-border-end-end-radius, var(--sui-drawer-content-border-end-end-radius, 0));
260
+ border-end-start-radius: var(--sui-drawer-bottom-content-border-end-start-radius, var(--sui-drawer-content-border-end-start-radius, 0));
236
261
  }
237
262
  .content.bottom.full {
238
263
  border-radius: var(--sui-drawer-bottom-full-content-border-radius, var(--sui-drawer-bottom-content-border-radius, 0));
239
264
  }
240
265
  .content.bottom .extra-control {
241
- inset: auto 0 100% auto;
266
+ inset-block-end: 100%;
267
+ inset-inline-end: 0;
242
268
  }
243
269
  :global(dialog:not(.open)) .content.bottom {
244
270
  transform: translateY(105%);
@@ -117,16 +117,20 @@
117
117
  }
118
118
  [role=listbox].tabs {
119
119
  padding: 0;
120
- border-width: 0 1px 0 0;
120
+ border-block-start-width: 0;
121
+ border-block-end-width: 0;
122
+ border-inline-end-width: 1px;
123
+ border-inline-start-width: 0;
121
124
  border-color: var(--sui-control-border-color);
122
125
  }
123
126
  [role=listbox].tabs :global(.option) :global(button) {
124
127
  justify-content: flex-start;
125
- border-width: 0 2px 0 0;
128
+ border-width: 0;
129
+ border-inline-end-width: 2px;
126
130
  border-color: transparent;
127
131
  padding: 0 12px;
128
- border-top-right-radius: 0;
129
- border-bottom-right-radius: 0;
132
+ border-start-end-radius: 0;
133
+ border-end-end-radius: 0;
130
134
  height: var(--sui-tab-medium-height);
131
135
  }
132
136
  [role=listbox].tabs :global(.option) :global(button[aria-selected=true]) {
@@ -5,6 +5,7 @@
5
5
  -->
6
6
  <script>
7
7
  import { onMount } from 'svelte';
8
+ import { browser } from '$app/environment';
8
9
  import Button from '../button/button.svelte';
9
10
  import Icon from '../icon/icon.svelte';
10
11
  import Popup from '../util/popup.svelte';
@@ -128,7 +129,7 @@
128
129
  {#if chevronIcon}
129
130
  {@render chevronIcon()}
130
131
  {:else}
131
- <Icon name="chevron_right" />
132
+ <Icon name={browser && document.dir === 'rtl' ? 'chevron_left' : 'chevron_right'} />
132
133
  {/if}
133
134
  </span>
134
135
  {/if}
@@ -185,7 +186,10 @@
185
186
  }
186
187
  .menuitem > :global([role=menu]) {
187
188
  position: absolute;
188
- inset: 2px auto auto calc(100% + 4px);
189
+ inset-block-start: 2px;
190
+ inset-block-end: auto;
191
+ inset-inline-start: calc(100% + 4px);
192
+ inset-inline-end: auto;
189
193
  }
190
194
  .menuitem > :global([role=menu]:hover) {
191
195
  opacity: 1;
@@ -25,6 +25,9 @@
25
25
  * @property {string} [valueType] Data type of the `value`. Typically `string`, `number` or
26
26
  * `boolean`. Default: auto detect.
27
27
  * @property {string} [label] Text label displayed next to the checkbox.
28
+ * @property {string} [group] The two-way bound variable to manage the state of a group of radio
29
+ * buttons. It works in the same way as the [`<input
30
+ * bind:group>`](https://svelte.dev/docs/svelte/bind#input-bind:group) of Svelte.
28
31
  * @property {Snippet} [children] Primary slot content.
29
32
  * @property {Snippet} [default] Default slot content.
30
33
  * @property {(event: CustomEvent) => void} [onChange] Custom `Change` event handler.
@@ -44,6 +47,7 @@
44
47
  value = undefined,
45
48
  valueType = undefined,
46
49
  label = undefined,
50
+ group = $bindable(''),
47
51
  children,
48
52
  onChange,
49
53
  onSelect,
@@ -58,6 +62,17 @@
58
62
  * @type {HTMLButtonElement | undefined}
59
63
  */
60
64
  let buttonElement = $state();
65
+
66
+ // Sync `checked` with `group` and `value`
67
+ $effect(() => {
68
+ if (group === value) {
69
+ if (!checked) {
70
+ checked = true;
71
+ }
72
+ } else if (checked) {
73
+ checked = false;
74
+ }
75
+ });
61
76
  </script>
62
77
 
63
78
  <span
@@ -85,7 +100,13 @@
85
100
  aria-labelledby="{id}-label"
86
101
  onclick={(event) => {
87
102
  event.preventDefault();
103
+
104
+ if (disabled || checked) {
105
+ return;
106
+ }
107
+
88
108
  checked = true;
109
+ group = value;
89
110
  }}
90
111
  {onChange}
91
112
  {onSelect}
@@ -45,6 +45,12 @@ declare const Radio: import("svelte").Component<{
45
45
  * Text label displayed next to the checkbox.
46
46
  */
47
47
  label?: string | undefined;
48
+ /**
49
+ * The two-way bound variable to manage the state of a group of radio
50
+ * buttons. It works in the same way as the [`<input
51
+ * bind:group>`](https://svelte.dev/docs/svelte/bind#input-bind:group) of Svelte.
52
+ */
53
+ group?: string | undefined;
48
54
  /**
49
55
  * Primary slot content.
50
56
  */
@@ -61,7 +67,7 @@ declare const Radio: import("svelte").Component<{
61
67
  * Custom `Select` event handler.
62
68
  */
63
69
  onSelect?: ((event: CustomEvent) => void) | undefined;
64
- } & Record<string, any>, {}, "">;
70
+ } & Record<string, any>, {}, "group">;
65
71
  type Props = {
66
72
  /**
67
73
  * The `class` attribute on the wrapper element.
@@ -98,6 +104,12 @@ type Props = {
98
104
  * Text label displayed next to the checkbox.
99
105
  */
100
106
  label?: string | undefined;
107
+ /**
108
+ * The two-way bound variable to manage the state of a group of radio
109
+ * buttons. It works in the same way as the [`<input
110
+ * bind:group>`](https://svelte.dev/docs/svelte/bind#input-bind:group) of Svelte.
111
+ */
112
+ group?: string | undefined;
101
113
  /**
102
114
  * Primary slot content.
103
115
  */
@@ -251,7 +251,9 @@
251
251
  border-width: 1px;
252
252
  border-color: var(--sui-control-border-color);
253
253
  border-radius: var(--sui-textbox-border-radius);
254
- padding: 0 var(--sui-textbox-height) 0 calc(var(--sui-textbox-height) / 4);
254
+ padding-block: 0;
255
+ padding-inline-start: calc(var(--sui-textbox-height) / 4);
256
+ padding-inline-end: var(--sui-textbox-height);
255
257
  width: 100%;
256
258
  height: var(--sui-textbox-height);
257
259
  color: var(--sui-control-foreground-color);
@@ -282,16 +284,18 @@
282
284
  }
283
285
  .combobox > :global(.icon) {
284
286
  position: absolute;
285
- inset: 8px auto auto 8px;
287
+ inset-block-start: 8px;
288
+ inset-inline-start: 8px;
286
289
  z-index: 1;
287
290
  }
288
291
  .combobox > :global(button) {
289
292
  position: absolute;
290
- inset: 0 0 auto auto;
293
+ inset-block-start: 0;
294
+ inset-inline-end: 0;
291
295
  z-index: 1;
292
296
  margin: 0 !important;
293
- border-top-left-radius: 0;
294
- border-bottom-left-radius: 0;
297
+ border-start-start-radius: 0;
298
+ border-end-start-radius: 0;
295
299
  background-color: transparent !important;
296
300
  }
297
301
  .combobox > :global(button[tabindex="-1"]) {
@@ -305,7 +309,8 @@
305
309
  width: 100% !important;
306
310
  }
307
311
  .combobox :global(input) {
308
- padding: 0 32px 0 8px;
312
+ padding-block: 0;
313
+ padding-inline: 8px 32px;
309
314
  width: 0;
310
315
  }
311
316
  .combobox + :global([role=listbox]) {
@@ -120,7 +120,8 @@
120
120
  display: inline-flex;
121
121
  align-items: center;
122
122
  margin: var(--sui-focus-ring-width);
123
- padding: 0 0 0 8px;
123
+ padding: 0;
124
+ padding-inline-start: 8px;
124
125
  border-radius: var(--sui-control-medium-border-radius);
125
126
  background-color: var(--sui-secondary-background-color);
126
127
  }
@@ -313,7 +313,7 @@
313
313
  <div role="none" class="base-bar"></div>
314
314
  <div
315
315
  class="slider-bar"
316
- style:left="{multiThumb ? sliderPositions[0] : 0}px"
316
+ style:inset-inline-start="{multiThumb ? sliderPositions[0] : 0}px"
317
317
  style:width="{multiThumb ? sliderPositions[1] - sliderPositions[0] : sliderPositions[0]}px"
318
318
  ></div>
319
319
  <div
@@ -327,7 +327,7 @@
327
327
  aria-valuemin={min}
328
328
  aria-valuemax={max}
329
329
  aria-valuenow={multiThumb ? values?.[0] : value}
330
- style:left="{sliderPositions[0]}px"
330
+ style:inset-inline-start="{sliderPositions[0]}px"
331
331
  onpointerdown={(event) => onPointerDown(event, 0)}
332
332
  onkeydown={(event) => onKeyDown(event, 0)}
333
333
  ></div>
@@ -343,7 +343,7 @@
343
343
  aria-valuemin={min}
344
344
  aria-valuemax={max}
345
345
  aria-valuenow={values?.[1]}
346
- style:left="{sliderPositions[1]}px"
346
+ style:inset-inline-start="{sliderPositions[1]}px"
347
347
  onpointerdown={(event) => onPointerDown(event, 1)}
348
348
  onkeydown={(event) => onKeyDown(event, 1)}
349
349
  ></div>
@@ -353,7 +353,7 @@
353
353
  <span
354
354
  role="none"
355
355
  class="label"
356
- style:left="{(barWidth / (optionLabels.length - 1)) * index}px"
356
+ style:inset-inline-start="{(barWidth / (optionLabels.length - 1)) * index}px"
357
357
  >
358
358
  {label}
359
359
  </span>
@@ -414,8 +414,13 @@
414
414
  height: calc(var(--sui-checkbox-height) - 2px);
415
415
  background-color: var(--sui-primary-accent-color-inverted);
416
416
  cursor: pointer;
417
+ }
418
+ [role=slider]:dir(ltr) {
417
419
  transform: translate(calc((var(--sui-checkbox-height) / 2 - 1px) * -1), calc((var(--sui-checkbox-height) / 4 - 1px) * -1));
418
420
  }
421
+ [role=slider]:dir(rtl) {
422
+ transform: translate(calc(var(--sui-checkbox-height) / 2 - 1px), calc((var(--sui-checkbox-height) / 4 - 1px) * -1));
423
+ }
419
424
  .invalid [role=slider] {
420
425
  border-color: var(--sui-error-border-color);
421
426
  }
@@ -423,6 +428,11 @@
423
428
  .label {
424
429
  position: absolute;
425
430
  top: calc(var(--sui-checkbox-height) / 2 + 8px);
426
- transform: translateX(-50%);
427
431
  font-size: var(--sui-font-size-x-small);
432
+ }
433
+ .label:dir(ltr) {
434
+ transform: translateX(-50%);
435
+ }
436
+ .label:dir(rtl) {
437
+ transform: translateX(50%);
428
438
  }</style>
@@ -121,10 +121,16 @@ button[aria-checked=true] span {
121
121
  border-color: transparent;
122
122
  }
123
123
  button[aria-checked=true] span::before {
124
- transform: translateX(calc(var(--sui-checkbox-height) * 2 - var(--sui-checkbox-height)));
124
+ --translateX: var(--sui-checkbox-height) * 2 - var(--sui-checkbox-height);
125
125
  border-color: var(--sui-primary-accent-color);
126
126
  background-color: var(--sui-primary-accent-color-inverted);
127
127
  }
128
+ button[aria-checked=true] span:dir(ltr)::before {
129
+ transform: translateX(calc(var(--translateX)));
130
+ }
131
+ button[aria-checked=true] span:dir(rtl)::before {
132
+ transform: translateX(calc((var(--translateX)) * -1));
133
+ }
128
134
 
129
135
  span {
130
136
  position: relative;
@@ -128,8 +128,10 @@
128
128
  }
129
129
  .tab-list[aria-orientation=horizontal] {
130
130
  gap: var(--sui-horizontal-tab-list-gap, var(--sui-tab-list-gap, 8px));
131
- margin: var(--sui-horizontal-tab-list-margin, var(--sui-tab-list-margin, 0 0 32px));
132
- border-width: var(--sui-horizontal-tab-list-border-width, var(--sui-tab-list-border-width, 0 0 1px));
131
+ margin-block: var(--sui-horizontal-tab-list-margin-block, 0 32px);
132
+ margin-inline: var(--sui-horizontal-tab-list-margin-inline, 0);
133
+ border-block-width: var(--sui-horizontal-tab-list-border-block-width, 0 1px);
134
+ border-inline-width: var(--sui-horizontal-tab-list-border-inline-width, 0 0);
133
135
  padding: var(--sui-horizontal-tab-list-padding, var(--sui-tab-list-padding, 0 16px));
134
136
  }
135
137
  .tab-list[aria-orientation=horizontal] :global(button) {
@@ -138,24 +140,28 @@
138
140
  justify-content: var(--sui-horizontal-tab-justify-content, center);
139
141
  }
140
142
  .tab-list[aria-orientation=horizontal] .indicator {
141
- border-width: var(--sui-horizontal-tab-list-indicator-border-width, var(--sui-tab-list-indicator-border-width, 0 0 2px 0));
143
+ border-block-width: var(--sui-horizontal-tab-list-indicator-border-block-width, 0 2px);
144
+ border-inline-width: var(--sui-horizontal-tab-list-indicator-border-inline-width, 0 0);
142
145
  }
143
146
  .tab-list[aria-orientation=vertical] {
144
147
  gap: var(--sui-vertical-tab-list-gap, var(--sui-tab-list-gap, 8px));
145
148
  flex-direction: column;
146
- margin: var(--sui-vertical-tab-list-margin, var(--sui-tab-list-margin, 0 32px 0 0));
147
- border-width: var(--sui-vertical-tab-list-border-width, var(--sui-tab-list-border-width, 0 1px 0 0));
149
+ margin-block: var(--sui-vertical-tab-list-margin-block, 0);
150
+ margin-inline: var(--sui-vertical-tab-list-margin-inline, 0 32px);
151
+ border-block-width: var(--sui-vertical-tab-list-border-block-width, 0 0);
152
+ border-inline-width: var(--sui-vertical-tab-list-border-inline-width, 0 1px);
148
153
  padding: var(--sui-vertical-tab-list-padding, var(--sui-tab-list-padding, 8px 0));
149
154
  width: var(--sui-vertical-tab-list-width, auto);
150
155
  }
151
156
  .tab-list[aria-orientation=vertical] :global(button) {
152
157
  justify-content: var(--sui-vertical-tab-justify-content, flex-start);
153
- padding-right: 32px;
158
+ padding-inline-end: 32px;
154
159
  width: var(--sui-vertical-tab-width, var(--sui-tab-width, 100%));
155
160
  height: var(--sui-vertical-tab-height, var(--sui-tab-height, auto));
156
161
  }
157
162
  .tab-list[aria-orientation=vertical] .indicator {
158
- border-width: var(--sui-horizontal-tab-list-vertical-border-width, var(--sui-tab-list-indicator-border-width, 0 2px 0 0));
163
+ border-block-width: var(--sui-vertical-tab-list-indicator-border-block-width, 0 0);
164
+ border-inline-width: var(--sui-vertical-tab-list-indicator-border-inline-width, 0 2px);
159
165
  }
160
166
  .tab-list :global(button) {
161
167
  position: relative;
@@ -137,8 +137,8 @@
137
137
  line-height: var(--sui-textbox-multiline-line-height);
138
138
  }
139
139
  .lexical-root:not(:first-child) {
140
- border-top-left-radius: 0 !important;
141
- border-top-right-radius: 0 !important;
140
+ border-start-start-radius: 0 !important;
141
+ border-start-end-radius: 0 !important;
142
142
  }
143
143
  .lexical-root.code {
144
144
  padding: 0;
@@ -168,7 +168,9 @@
168
168
  .lexical-root :global(.code-block) {
169
169
  position: relative;
170
170
  display: block;
171
- padding: 8px 8px 8px 56px;
171
+ padding-block: 8px;
172
+ padding-inline-start: 56px;
173
+ padding-inline-end: 8px;
172
174
  background-color: var(--sui-code-background-color);
173
175
  overflow-x: auto;
174
176
  white-space: pre;
@@ -181,13 +183,15 @@
181
183
  }
182
184
  .lexical-root :global(.code-block::before) {
183
185
  position: absolute;
184
- inset: 0 auto 0 0;
186
+ inset-block: 0;
187
+ inset-inline-start: 0;
188
+ inset-inline-end: auto;
185
189
  content: attr(data-gutter);
186
190
  padding: 8px;
187
191
  min-width: 40px;
188
192
  color: var(--sui-tertiary-foreground-color);
189
193
  background-color: var(--sui-tertiary-background-color);
190
- text-align: right;
194
+ text-align: end;
191
195
  }
192
196
  .lexical-root :global([data-lexical-text=true]) {
193
197
  cursor: text;
@@ -133,5 +133,8 @@
133
133
  min-width: auto;
134
134
  }
135
135
  .text-editor :global(.sui.text-area) :global(textarea) {
136
- border-radius: 0 0 var(--sui-textbox-border-radius) var(--sui-textbox-border-radius) !important;
136
+ border-start-start-radius: 0 !important;
137
+ border-start-end-radius: 0 !important;
138
+ border-end-start-radius: var(--sui-textbox-border-radius) !important;
139
+ border-end-end-radius: var(--sui-textbox-border-radius) !important;
137
140
  }</style>
@@ -46,7 +46,10 @@
46
46
  border-width: 1px 1px 0;
47
47
  border-style: solid;
48
48
  border-color: var(--sui-textbox-border-color);
49
- border-radius: var(--sui-textbox-border-radius) var(--sui-textbox-border-radius) 0 0;
49
+ border-start-start-radius: var(--sui-textbox-border-radius);
50
+ border-start-end-radius: var(--sui-textbox-border-radius);
51
+ border-end-start-radius: 0;
52
+ border-end-end-radius: 0;
50
53
  padding: 8px;
51
54
  background-color: var(--sui-tertiary-background-color);
52
55
  }
@@ -211,12 +211,12 @@
211
211
  min-width: 0;
212
212
  }
213
213
  .number-input :global(:not(:first-child)) :global(input) {
214
- border-top-left-radius: 0;
215
- border-bottom-left-radius: 0;
214
+ border-start-start-radius: 0;
215
+ border-end-start-radius: 0;
216
216
  }
217
217
  .number-input :global(:not(:last-child)) :global(input) {
218
- border-top-right-radius: 0;
219
- border-bottom-right-radius: 0;
218
+ border-start-end-radius: 0;
219
+ border-end-end-radius: 0;
220
220
  }
221
221
  .number-input :global(:not(.disabled)) :global(button[aria-disabled=true]) {
222
222
  filter: grayscale(0) opacity(1);
@@ -246,16 +246,22 @@
246
246
  height: 50%;
247
247
  }
248
248
  .buttons :global(button:first-of-type) {
249
- border-top-right-radius: 0;
250
- border-bottom-right-radius: 0;
251
- border-bottom-left-radius: 0;
252
- border-width: 1px 0 0 1px;
249
+ border-block-start-width: 1px;
250
+ border-block-end-width: 0;
251
+ border-inline-end-width: 0;
252
+ border-inline-start-width: 1px;
253
+ border-start-end-radius: 0;
254
+ border-end-end-radius: 0;
255
+ border-end-start-radius: 0;
253
256
  }
254
257
  .buttons :global(button:last-of-type) {
255
- border-top-left-radius: 0;
256
- border-top-right-radius: 0;
257
- border-bottom-right-radius: 0;
258
- border-width: 0 0 1px 1px;
258
+ border-block-start-width: 0;
259
+ border-block-end-width: 1px;
260
+ border-inline-end-width: 0;
261
+ border-inline-start-width: 1px;
262
+ border-start-start-radius: 0;
263
+ border-start-end-radius: 0;
264
+ border-end-end-radius: 0;
259
265
  }
260
266
  .buttons :global(button) :global(.icon) {
261
267
  font-size: 20px;
@@ -120,19 +120,24 @@
120
120
  min-width: 0 !important;
121
121
  }
122
122
  .password-input :global(input) {
123
- border-top-right-radius: 0;
124
- border-bottom-right-radius: 0;
123
+ border-start-end-radius: 0;
124
+ border-end-end-radius: 0;
125
125
  }
126
126
  .password-input :global(button) {
127
127
  flex: none;
128
- margin: 0 0 0 -1px;
128
+ margin-block: 0;
129
+ margin-inline-start: -1px;
130
+ margin-inline-end: 0;
129
131
  border-width: 1px;
130
132
  border-color: var(--sui-textbox-border-color);
131
133
  width: var(--sui-textbox-height);
132
134
  aspect-ratio: 1/1;
133
135
  }
134
136
  .password-input :global(button:last-child) {
135
- border-radius: 0 4px 4px 0;
137
+ border-start-start-radius: 0;
138
+ border-start-end-radius: 4px;
139
+ border-end-end-radius: 4px;
140
+ border-end-start-radius: 0;
136
141
  }
137
142
  .password-input :global(button) :global(.icon) {
138
143
  font-size: var(--sui-font-size-xx-large);
@@ -131,7 +131,9 @@
131
131
  }
132
132
  .search-bar > span {
133
133
  position: absolute;
134
- inset: 0 auto 0 0;
134
+ inset-block: 0;
135
+ inset-inline-start: 0;
136
+ inset-inline-end: auto;
135
137
  z-index: 2;
136
138
  display: flex;
137
139
  align-items: center;
@@ -145,7 +147,9 @@
145
147
  }
146
148
  .search-bar > :global(button) {
147
149
  position: absolute;
148
- inset: 0 0 auto auto;
150
+ inset-block: 0;
151
+ inset-inline-start: auto;
152
+ inset-inline-end: 0;
149
153
  z-index: 2;
150
154
  margin: 0 !important;
151
155
  height: var(--sui-button-medium-height);
@@ -144,14 +144,17 @@ input[aria-invalid=true] {
144
144
  }
145
145
  input ~ :global(button) {
146
146
  flex: none;
147
- margin-left: -1px;
147
+ margin-inline-start: -1px;
148
148
  border-width: 1px;
149
149
  border-color: var(--sui-textbox-border-color);
150
150
  height: var(--sui-textbox-height);
151
151
  aspect-ratio: 1/1;
152
152
  }
153
153
  input ~ :global(button:last-child) {
154
- border-radius: 0 4px 4px 0;
154
+ border-start-start-radius: 0;
155
+ border-start-end-radius: 4px;
156
+ border-end-end-radius: 4px;
157
+ border-end-start-radius: 0;
155
158
  }
156
159
  input ~ :global(button) :global(.icon) {
157
160
  font-size: var(--sui-font-size-xx-large);
@@ -167,22 +167,50 @@
167
167
  opacity: 0;
168
168
  }
169
169
  .toast.top-left {
170
- inset: 0 auto auto 0;
170
+ inset-block-start: 0;
171
+ inset-block-end: auto;
172
+ inset-inline-start: 0;
173
+ inset-inline-end: auto;
171
174
  }
172
175
  .toast.top-center {
173
- inset: 0 auto auto 50%;
176
+ inset-block-start: 0;
177
+ inset-block-end: auto;
178
+ inset-inline-start: 50%;
179
+ inset-inline-end: auto;
180
+ }
181
+ .toast.top-center:dir(ltr) {
174
182
  transform: translateX(-50%);
175
183
  }
184
+ .toast.top-center:dir(rtl) {
185
+ transform: translateX(50%);
186
+ }
176
187
  .toast.top-right {
177
- inset: 0 0 auto auto;
188
+ inset-block-start: 0;
189
+ inset-block-end: auto;
190
+ inset-inline-start: auto;
191
+ inset-inline-end: 0;
178
192
  }
179
193
  .toast.bottom-left {
180
- inset: auto auto 0 0;
194
+ inset-block-start: auto;
195
+ inset-block-end: 0;
196
+ inset-inline-start: 0;
197
+ inset-inline-end: auto;
181
198
  }
182
199
  .toast.bottom-center {
183
- inset: auto auto 0 50%;
200
+ inset-block-start: auto;
201
+ inset-block-end: 0;
202
+ inset-inline-start: 50%;
203
+ inset-inline-end: auto;
204
+ }
205
+ .toast.bottom-center:dir(ltr) {
184
206
  transform: translateX(-50%);
185
207
  }
208
+ .toast.bottom-center:dir(rtl) {
209
+ transform: translateX(50%);
210
+ }
186
211
  .toast.bottom-right {
187
- inset: auto 0 0 auto;
212
+ inset-block-start: auto;
213
+ inset-block-end: 0;
214
+ inset-inline-start: auto;
215
+ inset-inline-end: 0;
188
216
  }</style>
@@ -559,7 +559,7 @@
559
559
 
560
560
  .font-loader {
561
561
  position: absolute;
562
- left: -99999px;
562
+ inset-inline-start: -99999px;
563
563
  font-family: var(--sui-font-family-default);
564
564
  }
565
565
 
@@ -294,6 +294,7 @@ class Group {
294
294
  );
295
295
 
296
296
  if (isTarget) {
297
+ element.click();
297
298
  element.dispatchEvent(new CustomEvent('Select'));
298
299
  }
299
300
  }
@@ -46,6 +46,22 @@ class Popup {
46
46
  let { position } = this;
47
47
  let height;
48
48
 
49
+ // Normalize RTL-friendly positions to LTR for LTR documents
50
+ // @todo Rename `PopupPosition` enums to be direction-agnostic
51
+ if (document.dir === 'rtl') {
52
+ if (position.endsWith('-left')) {
53
+ position = /** @type {PopupPosition} */ (position.replace('-left', '-right'));
54
+ } else if (position.endsWith('-right')) {
55
+ position = /** @type {PopupPosition} */ (position.replace('-right', '-left'));
56
+ }
57
+
58
+ if (position.startsWith('left-')) {
59
+ position = /** @type {PopupPosition} */ (position.replace('left-', 'right-'));
60
+ } else if (position.startsWith('right-')) {
61
+ position = /** @type {PopupPosition} */ (position.replace('right-', 'left-'));
62
+ }
63
+ }
64
+
49
65
  // Alter the position if the space is limited
50
66
  // @todo Handle more overflow cases
51
67
  if (position.startsWith('bottom-')) {
package/package.json CHANGED
@@ -1,46 +1,25 @@
1
1
  {
2
2
  "name": "@sveltia/ui",
3
- "version": "0.28.3",
3
+ "version": "0.29.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "github:sveltia/sveltia-ui"
9
9
  },
10
- "publishConfig": {
11
- "access": "public"
12
- },
13
- "scripts": {
14
- "dev": "vite dev",
15
- "build": "svelte-kit sync && svelte-package",
16
- "build:watch": "svelte-kit sync && svelte-package --watch",
17
- "preview": "vite preview",
18
- "prepublishOnly": "npm run build",
19
- "format": "prettier --write .",
20
- "check": "pnpm run '/^check:.*/'",
21
- "check:audit": "pnpm audit",
22
- "check:cspell": "cspell --no-progress",
23
- "check:svelte": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json",
24
- "check:prettier": "prettier --check .",
25
- "check:eslint": "eslint .",
26
- "check:stylelint": "stylelint '**/*.{css,scss,svelte}'",
27
- "test": "vitest run",
28
- "test:coverage": "vitest --coverage",
29
- "test:watch": "vitest"
30
- },
31
10
  "dependencies": {
32
- "@lexical/code": "^0.35.0",
33
- "@lexical/dragon": "^0.35.0",
34
- "@lexical/history": "^0.35.0",
35
- "@lexical/link": "^0.35.0",
36
- "@lexical/list": "^0.35.0",
37
- "@lexical/markdown": "^0.35.0",
38
- "@lexical/rich-text": "^0.35.0",
39
- "@lexical/selection": "^0.35.0",
40
- "@lexical/table": "^0.35.0",
41
- "@lexical/utils": "^0.35.0",
42
- "@sveltia/utils": "^0.8.0",
43
- "lexical": "^0.35.0",
11
+ "@lexical/code": "^0.36.2",
12
+ "@lexical/dragon": "^0.36.2",
13
+ "@lexical/history": "^0.36.2",
14
+ "@lexical/link": "^0.36.2",
15
+ "@lexical/list": "^0.36.2",
16
+ "@lexical/markdown": "^0.36.2",
17
+ "@lexical/rich-text": "^0.36.2",
18
+ "@lexical/selection": "^0.36.2",
19
+ "@lexical/table": "^0.36.2",
20
+ "@lexical/utils": "^0.36.2",
21
+ "@sveltia/utils": "^0.8.5",
22
+ "lexical": "^0.36.2",
44
23
  "prismjs": "^1.30.0",
45
24
  "svelte-i18n": "^4.0.1"
46
25
  },
@@ -48,30 +27,30 @@
48
27
  "svelte": "^5.0.0"
49
28
  },
50
29
  "devDependencies": {
51
- "@sveltejs/adapter-auto": "^6.1.0",
52
- "@sveltejs/kit": "^2.40.0",
53
- "@sveltejs/package": "^2.5.2",
54
- "@sveltejs/vite-plugin-svelte": "6.2.0",
30
+ "@sveltejs/adapter-auto": "^6.1.1",
31
+ "@sveltejs/kit": "^2.43.8",
32
+ "@sveltejs/package": "^2.5.4",
33
+ "@sveltejs/vite-plugin-svelte": "^6.2.1",
55
34
  "cspell": "^9.2.1",
56
35
  "eslint": "^8.57.1",
57
36
  "eslint-config-airbnb-base": "^15.0.0",
58
37
  "eslint-config-prettier": "^10.1.8",
59
38
  "eslint-plugin-import": "^2.32.0",
60
- "eslint-plugin-jsdoc": "^57.0.10",
39
+ "eslint-plugin-jsdoc": "^57.2.1",
61
40
  "eslint-plugin-svelte": "^2.46.1",
62
41
  "postcss": "^8.5.6",
63
42
  "postcss-html": "^1.8.0",
64
43
  "prettier": "^3.6.2",
65
44
  "prettier-plugin-svelte": "^3.4.0",
66
- "sass": "^1.92.1",
67
- "stylelint": "^16.24.0",
68
- "stylelint-config-recommended-scss": "^16.0.1",
45
+ "sass": "^1.93.2",
46
+ "stylelint": "^16.25.0",
47
+ "stylelint-config-recommended-scss": "^16.0.2",
69
48
  "stylelint-scss": "^6.12.1",
70
- "svelte": "5.38.10",
71
- "svelte-check": "^4.3.1",
49
+ "svelte": "^5.39.8",
50
+ "svelte-check": "^4.3.2",
72
51
  "svelte-preprocess": "^6.0.3",
73
52
  "tslib": "^2.8.1",
74
- "vite": "^7.1.5",
53
+ "vite": "^7.1.9",
75
54
  "vitest": "^3.2.4"
76
55
  },
77
56
  "exports": {
@@ -92,14 +71,25 @@
92
71
  ]
93
72
  }
94
73
  },
95
- "pnpm": {
96
- "overrides": {
97
- "cookie@<0.7.0": ">=0.7.0",
98
- "esbuild@<=0.24.2": ">=0.25.0"
99
- },
100
- "onlyBuiltDependencies": [
101
- "esbuild",
102
- "svelte-preprocess"
103
- ]
74
+ "publishConfig": {
75
+ "access": "public",
76
+ "provenance": true
77
+ },
78
+ "scripts": {
79
+ "dev": "vite dev",
80
+ "build": "svelte-kit sync && svelte-package",
81
+ "build:watch": "svelte-kit sync && svelte-package --watch",
82
+ "preview": "vite preview",
83
+ "format": "prettier --write .",
84
+ "check": "pnpm run '/^check:.*/'",
85
+ "check:audit": "pnpm audit",
86
+ "check:cspell": "cspell --no-progress",
87
+ "check:svelte": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json",
88
+ "check:prettier": "prettier --check .",
89
+ "check:eslint": "eslint .",
90
+ "check:stylelint": "stylelint '**/*.{css,scss,svelte}'",
91
+ "test": "vitest run",
92
+ "test:coverage": "vitest --coverage",
93
+ "test:watch": "vitest"
104
94
  }
105
- }
95
+ }