@sveltia/ui 0.55.0 → 0.56.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.
- package/dist/components/listbox/option.svelte +107 -33
- package/dist/components/select/combobox.svelte +126 -50
- package/dist/components/text-editor/constants.d.ts +0 -4
- package/dist/components/text-editor/constants.js +0 -5
- package/dist/components/text-editor/toolbar/{insert-image-button.svelte → insert-item-button.svelte} +12 -5
- package/dist/components/text-editor/toolbar/{insert-image-button.svelte.d.ts → insert-item-button.svelte.d.ts} +5 -5
- package/dist/components/text-editor/toolbar/insert-menu-button.svelte +6 -2
- package/dist/components/text-editor/toolbar/text-editor-toolbar.svelte +12 -16
- package/dist/components/util/modal.svelte +210 -66
- package/dist/components/util/popup.svelte +28 -20
- package/dist/locales/pl.yaml +113 -0
- package/dist/services/group.svelte.d.ts +57 -0
- package/dist/services/group.svelte.js +294 -7
- package/dist/services/popup.svelte.d.ts +37 -5
- package/dist/services/popup.svelte.js +132 -19
- package/dist/services/select.svelte.d.ts +49 -0
- package/dist/services/select.svelte.js +99 -1
- package/dist/typedefs.d.ts +38 -2
- package/dist/typedefs.js +17 -2
- package/package.json +7 -6
|
@@ -2,9 +2,25 @@
|
|
|
2
2
|
@component
|
|
3
3
|
A generic modal top-layer helper based on the HTML `<dialog>` element.
|
|
4
4
|
-->
|
|
5
|
+
<script module>
|
|
6
|
+
/**
|
|
7
|
+
* Context key used by a modal to keep the modal it’s rendered within mounted.
|
|
8
|
+
* @type {symbol}
|
|
9
|
+
*/
|
|
10
|
+
const RETAINER_KEY = Symbol('sui-modal-retainer');
|
|
11
|
+
</script>
|
|
12
|
+
|
|
5
13
|
<script>
|
|
6
|
-
import {
|
|
7
|
-
|
|
14
|
+
import {
|
|
15
|
+
getAllContexts,
|
|
16
|
+
getContext,
|
|
17
|
+
mount,
|
|
18
|
+
onMount,
|
|
19
|
+
setContext,
|
|
20
|
+
tick,
|
|
21
|
+
unmount,
|
|
22
|
+
untrack,
|
|
23
|
+
} from 'svelte';
|
|
8
24
|
import Placeholder from './placeholder.svelte';
|
|
9
25
|
|
|
10
26
|
/**
|
|
@@ -58,7 +74,67 @@
|
|
|
58
74
|
|
|
59
75
|
let setOpenClass = $state(false);
|
|
60
76
|
let setActiveClass = $state(false);
|
|
61
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Whether the modal is being displayed. This is enabled just before the opening transition
|
|
79
|
+
* starts, and disabled right after the closing transition is complete.
|
|
80
|
+
* @type {boolean}
|
|
81
|
+
*/
|
|
82
|
+
let visible = $state(false);
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The number of descendant modals that are currently being displayed. A popup’s content is
|
|
86
|
+
* unmounted as soon as the popup closes, which would also destroy any modal declared alongside
|
|
87
|
+
* it, such as a dialog opened from a menu item. Because the menu item inevitably goes away with
|
|
88
|
+
* the menu, the dialog has to outlive it, so the content is held until the dialog is done with.
|
|
89
|
+
* @type {number}
|
|
90
|
+
*/
|
|
91
|
+
let retainCount = $state(0);
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The retainer of the modal this modal is rendered within, if any. This has to be read before the
|
|
95
|
+
* `setContext()` call below, which would otherwise shadow it with this modal’s own retainer.
|
|
96
|
+
* @type {{ retain: () => void, release: () => void } | undefined}
|
|
97
|
+
*/
|
|
98
|
+
const parentRetainer = getContext(RETAINER_KEY);
|
|
99
|
+
|
|
100
|
+
// This has to be set before `getAllContexts()` below, so the content, which is rendered in a
|
|
101
|
+
// separate component tree, can reach it. The counter is updated within `untrack()`, because
|
|
102
|
+
// incrementing it reads it first, which would otherwise make it a dependency of the calling
|
|
103
|
+
// descendant’s effect and send that effect into an endless retain/release loop.
|
|
104
|
+
setContext(RETAINER_KEY, {
|
|
105
|
+
/**
|
|
106
|
+
* Keep this modal mounted on behalf of a descendant modal.
|
|
107
|
+
*/
|
|
108
|
+
retain: () => {
|
|
109
|
+
untrack(() => {
|
|
110
|
+
retainCount += 1;
|
|
111
|
+
});
|
|
112
|
+
},
|
|
113
|
+
/**
|
|
114
|
+
* Release a hold previously acquired with `retain`.
|
|
115
|
+
*/
|
|
116
|
+
release: () => {
|
|
117
|
+
untrack(() => {
|
|
118
|
+
retainCount -= 1;
|
|
119
|
+
});
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Whether the `<dialog>` element is in the DOM tree. Unless {@link keepContent} is enabled, the
|
|
125
|
+
* element is mounted on demand, and unmounted once the closing transition is complete.
|
|
126
|
+
* @type {boolean}
|
|
127
|
+
*/
|
|
128
|
+
const mounted = $derived(keepContent || visible || retainCount > 0);
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Whether the modal has been requested to open. Unlike the {@link open} prop, this is a plain
|
|
132
|
+
* variable updated synchronously at the very beginning of `openDialog`/`closeDialog`, so either
|
|
133
|
+
* of them can bail out early when the requested state is already in effect.
|
|
134
|
+
* @type {boolean}
|
|
135
|
+
*/
|
|
136
|
+
let requestedOpen = false;
|
|
137
|
+
|
|
62
138
|
/**
|
|
63
139
|
* Monotonically increasing counter used to detect stale async operations. Incremented at the
|
|
64
140
|
* start of each `openDialog`/`closeDialog` call; any suspended continuation that finds its
|
|
@@ -68,44 +144,78 @@
|
|
|
68
144
|
*/
|
|
69
145
|
let generation = 0;
|
|
70
146
|
|
|
147
|
+
/**
|
|
148
|
+
* Get the longest time from a computed CSS time list, such as `transition-duration`.
|
|
149
|
+
* @param {string} value Comma-separated CSS time values in seconds, e.g. `0.4s, 0.15s`.
|
|
150
|
+
* @returns {number} Time in milliseconds.
|
|
151
|
+
*/
|
|
152
|
+
const getLongestTime = (value) =>
|
|
153
|
+
Math.max(0, ...value.split(',').map((time) => Number.parseFloat(time) || 0)) * 1000;
|
|
154
|
+
|
|
71
155
|
/**
|
|
72
156
|
* Resolve once the transition is complete.
|
|
73
157
|
* @returns {Promise<void>} Nothing.
|
|
74
158
|
*/
|
|
75
|
-
const waitForTransition = async () =>
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
159
|
+
const waitForTransition = async () => {
|
|
160
|
+
// Let the CSS class change be applied first, so the duration below is read from the new state
|
|
161
|
+
await tick();
|
|
162
|
+
|
|
163
|
+
if (!dialog) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const { transitionDuration, transitionDelay } = getComputedStyle(dialog);
|
|
168
|
+
// Fall back to a timer, so the modal is never stuck half-open (and, more importantly, never
|
|
169
|
+
// left mounted) in case `transitionend` is never fired, e.g. when the transition is removed by
|
|
170
|
+
// the consumer’s CSS or the element is not rendered at all
|
|
171
|
+
const timeout = getLongestTime(transitionDuration) + getLongestTime(transitionDelay) + 100;
|
|
172
|
+
const controller = new AbortController();
|
|
173
|
+
const { signal } = controller;
|
|
174
|
+
|
|
175
|
+
dialog.addEventListener(
|
|
176
|
+
'transitionend',
|
|
177
|
+
(event) => {
|
|
82
178
|
if (event.target === dialog) {
|
|
83
|
-
|
|
84
|
-
resolve();
|
|
179
|
+
controller.abort();
|
|
85
180
|
}
|
|
86
|
-
}
|
|
181
|
+
},
|
|
182
|
+
{ signal },
|
|
183
|
+
);
|
|
87
184
|
|
|
88
|
-
|
|
185
|
+
const timer = window.setTimeout(() => controller.abort(), timeout);
|
|
186
|
+
|
|
187
|
+
await new Promise((resolve) => {
|
|
188
|
+
signal.addEventListener('abort', () => resolve(undefined));
|
|
89
189
|
});
|
|
90
190
|
|
|
191
|
+
window.clearTimeout(timer);
|
|
192
|
+
};
|
|
193
|
+
|
|
91
194
|
/**
|
|
92
195
|
* Show the modal.
|
|
93
196
|
*/
|
|
94
197
|
const openDialog = async () => {
|
|
95
|
-
if (
|
|
198
|
+
if (requestedOpen) {
|
|
96
199
|
return;
|
|
97
200
|
}
|
|
98
201
|
|
|
202
|
+
requestedOpen = true;
|
|
99
203
|
generation += 1;
|
|
100
204
|
|
|
101
205
|
const gen = generation;
|
|
102
206
|
|
|
103
207
|
onOpening?.(new CustomEvent('Opening'));
|
|
104
|
-
|
|
208
|
+
visible = true;
|
|
209
|
+
// Wait for the `<dialog>` element to be added to the DOM tree
|
|
210
|
+
await tick();
|
|
211
|
+
|
|
212
|
+
if (gen !== generation || !dialog || dialog.open) return;
|
|
213
|
+
|
|
105
214
|
dialog.showModal();
|
|
106
215
|
onOpen?.(new CustomEvent('Open'));
|
|
107
|
-
|
|
108
|
-
|
|
216
|
+
// Force a style recalculation, so the browser has a state to transition from. This is required
|
|
217
|
+
// because the element may have just been added to the DOM tree.
|
|
218
|
+
dialog.getBoundingClientRect();
|
|
109
219
|
setOpenClass = true;
|
|
110
220
|
await waitForTransition();
|
|
111
221
|
if (gen !== generation) return;
|
|
@@ -116,21 +226,26 @@
|
|
|
116
226
|
* Hide the modal.
|
|
117
227
|
*/
|
|
118
228
|
const closeDialog = async () => {
|
|
119
|
-
if (!
|
|
229
|
+
if (!requestedOpen) {
|
|
120
230
|
return;
|
|
121
231
|
}
|
|
122
232
|
|
|
233
|
+
requestedOpen = false;
|
|
123
234
|
generation += 1;
|
|
124
235
|
|
|
125
236
|
const gen = generation;
|
|
126
237
|
const wasOpen = setOpenClass;
|
|
127
|
-
const
|
|
238
|
+
const returnValue = dialog?.returnValue ?? '';
|
|
128
239
|
|
|
129
240
|
onClosing?.(new CustomEvent('Closing'));
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
241
|
+
|
|
242
|
+
if (dialog?.open) {
|
|
243
|
+
// Prevent a button behind the `<dialog>` from being clicked erroneously (Svelte bug)
|
|
244
|
+
document.body.inert = true;
|
|
245
|
+
dialog.close();
|
|
246
|
+
document.body.inert = false;
|
|
247
|
+
}
|
|
248
|
+
|
|
134
249
|
setActiveClass = false;
|
|
135
250
|
setOpenClass = false;
|
|
136
251
|
|
|
@@ -143,7 +258,8 @@
|
|
|
143
258
|
|
|
144
259
|
if (gen !== generation) return;
|
|
145
260
|
|
|
146
|
-
|
|
261
|
+
// Unmount the `<dialog>` element unless `keepContent` is enabled
|
|
262
|
+
visible = false;
|
|
147
263
|
|
|
148
264
|
if (returnValue === 'ok') {
|
|
149
265
|
onOk?.(new CustomEvent('Ok'));
|
|
@@ -154,22 +270,26 @@
|
|
|
154
270
|
}
|
|
155
271
|
|
|
156
272
|
onClose?.(new CustomEvent('Close', { detail: { returnValue } }));
|
|
157
|
-
dialog.returnValue = '';
|
|
158
|
-
};
|
|
159
273
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
openDialog();
|
|
163
|
-
} else {
|
|
164
|
-
closeDialog();
|
|
274
|
+
if (dialog) {
|
|
275
|
+
dialog.returnValue = '';
|
|
165
276
|
}
|
|
166
|
-
}
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* The context available to this component. The `<dialog>` element is rendered in a separate
|
|
281
|
+
* component tree created with `mount()`, which would otherwise start with an empty context, so
|
|
282
|
+
* this is forwarded to keep `getContext()` working for the modal content.
|
|
283
|
+
* @type {Map<any, any>}
|
|
284
|
+
*/
|
|
285
|
+
const context = getAllContexts();
|
|
167
286
|
|
|
168
287
|
onMount(() => {
|
|
169
288
|
const placeholder = mount(Placeholder, {
|
|
170
289
|
target: document.querySelector('.sui.app-shell') ?? document.body,
|
|
171
290
|
// eslint-disable-next-line no-use-before-define
|
|
172
291
|
props: { children: dialogSnippet },
|
|
292
|
+
context,
|
|
173
293
|
});
|
|
174
294
|
|
|
175
295
|
// onUnmount
|
|
@@ -178,43 +298,67 @@
|
|
|
178
298
|
unmount(placeholder);
|
|
179
299
|
};
|
|
180
300
|
});
|
|
301
|
+
|
|
302
|
+
// This must be declared after `onMount()` above, because effects run in declaration order, and
|
|
303
|
+
// `openDialog()` expects the placeholder holding the `<dialog>` element to be already mounted
|
|
304
|
+
$effect(() => {
|
|
305
|
+
if (open) {
|
|
306
|
+
openDialog();
|
|
307
|
+
} else {
|
|
308
|
+
closeDialog();
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
// Hold the enclosing modal, if any, for as long as this one is on screen. `visible` is used
|
|
313
|
+
// rather than `mounted`, so a `keepContent` modal doesn’t pin its ancestor forever
|
|
314
|
+
$effect(() => {
|
|
315
|
+
if (!visible) {
|
|
316
|
+
return undefined;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
parentRetainer?.retain();
|
|
320
|
+
|
|
321
|
+
return () => {
|
|
322
|
+
parentRetainer?.release();
|
|
323
|
+
};
|
|
324
|
+
});
|
|
181
325
|
</script>
|
|
182
326
|
|
|
183
327
|
{#snippet dialogSnippet()}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
event
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
dialog
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
328
|
+
{#if mounted}
|
|
329
|
+
<dialog
|
|
330
|
+
bind:this={dialog}
|
|
331
|
+
{...restProps}
|
|
332
|
+
inert={!setOpenClass}
|
|
333
|
+
{role}
|
|
334
|
+
class="sui modal {className}"
|
|
335
|
+
class:backdrop={showBackdrop}
|
|
336
|
+
class:open={setOpenClass}
|
|
337
|
+
class:active={setActiveClass}
|
|
338
|
+
onclick={({ target }) => {
|
|
339
|
+
if (
|
|
340
|
+
dialog &&
|
|
341
|
+
lightDismiss &&
|
|
342
|
+
/** @type {HTMLElement | undefined} */ (target)?.matches('dialog')
|
|
343
|
+
) {
|
|
344
|
+
dialog.returnValue = 'cancel';
|
|
345
|
+
open = false;
|
|
346
|
+
}
|
|
347
|
+
}}
|
|
348
|
+
oncancel={(event) => {
|
|
349
|
+
event.preventDefault();
|
|
350
|
+
|
|
351
|
+
// Escape key is pressed
|
|
352
|
+
if (dialog && escapeDismiss) {
|
|
353
|
+
dialog.returnValue = 'cancel';
|
|
354
|
+
open = false;
|
|
355
|
+
}
|
|
356
|
+
}}
|
|
357
|
+
>
|
|
358
|
+
{@render extraContent?.()}
|
|
215
359
|
{@render children?.()}
|
|
216
|
-
|
|
217
|
-
|
|
360
|
+
</dialog>
|
|
361
|
+
{/if}
|
|
218
362
|
{/snippet}
|
|
219
363
|
|
|
220
364
|
<style>dialog {
|
|
@@ -55,11 +55,8 @@
|
|
|
55
55
|
} = $props();
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
|
-
*
|
|
59
|
-
|
|
60
|
-
let initialized = $state(false);
|
|
61
|
-
/**
|
|
62
|
-
* A reference to the `<dialog>` element.
|
|
58
|
+
* A reference to the `<dialog>` element. This is only available while the popup is open, because
|
|
59
|
+
* the element is mounted on demand.
|
|
63
60
|
* @type {HTMLDialogElement | undefined}
|
|
64
61
|
*/
|
|
65
62
|
let dialogElement = $state();
|
|
@@ -78,21 +75,12 @@
|
|
|
78
75
|
/**
|
|
79
76
|
* @type {{ style: { inset: string | undefined, zIndex: number | undefined, minWidth: string |
|
|
80
77
|
* undefined, maxWidth: string | undefined, height: string | undefined }, open: boolean,
|
|
81
|
-
*
|
|
78
|
+
* attachPopupElement: (popupElement: HTMLDialogElement, contentElement?: HTMLElement) => void,
|
|
79
|
+
* detachPopupElement: () => void, checkPosition: () => void, destroy: () => void } | undefined}
|
|
82
80
|
*/
|
|
83
81
|
let popupInstance = $state();
|
|
84
82
|
let hoveredTimeout = 0;
|
|
85
83
|
|
|
86
|
-
/**
|
|
87
|
-
* Initialize the popup.
|
|
88
|
-
*/
|
|
89
|
-
const init = () => {
|
|
90
|
-
popupInstance = activatePopup(anchor, dialogElement, position, positionBaseElement);
|
|
91
|
-
|
|
92
|
-
contentType = anchor?.getAttribute('aria-haspopup') ?? undefined;
|
|
93
|
-
initialized = true;
|
|
94
|
-
};
|
|
95
|
-
|
|
96
84
|
$effect(() => {
|
|
97
85
|
if (popupInstance) {
|
|
98
86
|
open = popupInstance.open;
|
|
@@ -106,14 +94,35 @@
|
|
|
106
94
|
}
|
|
107
95
|
});
|
|
108
96
|
|
|
97
|
+
// The instance must be created as soon as the anchor is available, without waiting for the
|
|
98
|
+
// `<dialog>` element, because it’s the instance that listens to the anchor and opens the popup —
|
|
99
|
+
// and therefore causes the element to be mounted in the first place
|
|
109
100
|
$effect(() => {
|
|
110
|
-
if (anchor &&
|
|
111
|
-
|
|
101
|
+
if (anchor && !popupInstance) {
|
|
102
|
+
popupInstance = activatePopup(anchor, undefined, position, positionBaseElement);
|
|
103
|
+
contentType = anchor.getAttribute('aria-haspopup') ?? undefined;
|
|
112
104
|
}
|
|
113
105
|
});
|
|
114
106
|
|
|
107
|
+
// Attach the `<dialog>` element to the instance whenever it’s mounted, and detach it when it’s
|
|
108
|
+
// unmounted, which happens on every close. The content element is passed alongside it, because
|
|
109
|
+
// a nested popup shares the `<dialog>` with its parent and only the content is its own.
|
|
110
|
+
$effect(() => {
|
|
111
|
+
if (popupInstance && dialogElement && content) {
|
|
112
|
+
popupInstance.attachPopupElement(dialogElement, content);
|
|
113
|
+
|
|
114
|
+
return () => {
|
|
115
|
+
popupInstance?.detachPopupElement();
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return undefined;
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// The position can only be calculated once the content is in the DOM tree, so it has to be
|
|
123
|
+
// (re)checked here rather than in the instance’s `open` setter, which runs before the mount
|
|
115
124
|
$effect(() => {
|
|
116
|
-
if (
|
|
125
|
+
if (open && dialogElement) {
|
|
117
126
|
popupInstance?.checkPosition();
|
|
118
127
|
}
|
|
119
128
|
});
|
|
@@ -175,7 +184,6 @@
|
|
|
175
184
|
bind:open
|
|
176
185
|
showBackdrop={showBackdrop ?? touch}
|
|
177
186
|
lightDismiss={true}
|
|
178
|
-
keepContent={true}
|
|
179
187
|
onOpen={async (event) => {
|
|
180
188
|
onOpen?.(event);
|
|
181
189
|
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Dialog action button labels
|
|
2
|
+
ok: OK
|
|
3
|
+
cancel: Anuluj
|
|
4
|
+
close: Zamknij
|
|
5
|
+
|
|
6
|
+
# Button to clear content, e.g. search input or calendar date
|
|
7
|
+
clear: Wyczyść
|
|
8
|
+
|
|
9
|
+
# Text editor dialog buttons for link/image insertion
|
|
10
|
+
insert: Wstaw
|
|
11
|
+
update: Zaktualizuj
|
|
12
|
+
remove: Usuń
|
|
13
|
+
|
|
14
|
+
# Combobox dropdown toggle button aria-labels
|
|
15
|
+
collapse: Zwiń
|
|
16
|
+
expand: Rozwiń
|
|
17
|
+
|
|
18
|
+
# Infobar/toast dismiss button label
|
|
19
|
+
dismiss: Odrzuć
|
|
20
|
+
|
|
21
|
+
# Calendar component
|
|
22
|
+
calendar:
|
|
23
|
+
# View switcher group aria-labels and navigation button aria-labels
|
|
24
|
+
year: Rok
|
|
25
|
+
previous_decade: Poprzednia dekada
|
|
26
|
+
next_decade: Następna dekada
|
|
27
|
+
month: Miesiąc
|
|
28
|
+
previous_month: Poprzedni miesiąc
|
|
29
|
+
next_month: Następny miesiąc
|
|
30
|
+
# “Today” button label in footer
|
|
31
|
+
today: Dzisiaj
|
|
32
|
+
|
|
33
|
+
# Split button component
|
|
34
|
+
split_button:
|
|
35
|
+
# Wrapper aria-label, e.g. “Save Options” when primary button is “Save”
|
|
36
|
+
x_options: 'Opcje {$name}'
|
|
37
|
+
# Dropdown toggle aria-label
|
|
38
|
+
more_options: Więcej opcji
|
|
39
|
+
|
|
40
|
+
# Combobox component
|
|
41
|
+
combobox:
|
|
42
|
+
# Placeholder when no option is selected
|
|
43
|
+
select_an_option: Wybierz opcję…
|
|
44
|
+
# Filter input aria-label
|
|
45
|
+
filter_options: Filtruj opcje
|
|
46
|
+
# Empty state message when filter has no matches
|
|
47
|
+
no_matching_options: Nie znaleziono pasujących opcji
|
|
48
|
+
|
|
49
|
+
# Number input component
|
|
50
|
+
number_input:
|
|
51
|
+
# Spin button aria-labels for incrementing/decrementing the number value
|
|
52
|
+
increase: Zwiększ
|
|
53
|
+
decrease: Zmniejsz
|
|
54
|
+
|
|
55
|
+
# Password input component
|
|
56
|
+
password_input:
|
|
57
|
+
# Visibility toggle button aria-labels
|
|
58
|
+
show_password: Pokaż hasło
|
|
59
|
+
hide_password: Ukryj hasło
|
|
60
|
+
|
|
61
|
+
# Secret input component (API keys, tokens, etc.)
|
|
62
|
+
secret_input:
|
|
63
|
+
# Visibility toggle button aria-labels
|
|
64
|
+
show_secret: Pokaż sekret
|
|
65
|
+
hide_secret: Ukryj sekret
|
|
66
|
+
|
|
67
|
+
# Select tags component (multi-select)
|
|
68
|
+
select_tags:
|
|
69
|
+
# Listbox aria-label
|
|
70
|
+
selected_options: Wybrane opcje
|
|
71
|
+
# Remove button aria-label, e.g. “Remove Option 1”
|
|
72
|
+
remove_x: 'Usuń {$name}'
|
|
73
|
+
|
|
74
|
+
# Text editor component
|
|
75
|
+
text_editor:
|
|
76
|
+
# Toolbar aria-labels
|
|
77
|
+
text_editor: Edytor tekstu
|
|
78
|
+
code_editor: Edytor kodu
|
|
79
|
+
# Block style menu button and menu aria-labels
|
|
80
|
+
text_style_options: Opcje stylu tekstu
|
|
81
|
+
show_text_style_options: Pokaż opcje stylu tekstu
|
|
82
|
+
# Block style menu items
|
|
83
|
+
paragraph: Akapit
|
|
84
|
+
heading_1: Nagłówek 1
|
|
85
|
+
heading_2: Nagłówek 2
|
|
86
|
+
heading_3: Nagłówek 3
|
|
87
|
+
heading_4: Nagłówek 4
|
|
88
|
+
heading_5: Nagłówek 5
|
|
89
|
+
heading_6: Nagłówek 6
|
|
90
|
+
bulleted_list: Lista punktowana
|
|
91
|
+
numbered_list: Lista numerowana
|
|
92
|
+
blockquote: Cytat blokowy
|
|
93
|
+
code_block: Blok kodu
|
|
94
|
+
# Inline style button aria-labels
|
|
95
|
+
bold: Pogrubienie
|
|
96
|
+
italic: Kursywa
|
|
97
|
+
strikethrough: Przekreślenie
|
|
98
|
+
code: Kod
|
|
99
|
+
link: Odnośnik
|
|
100
|
+
# Link dialog title
|
|
101
|
+
insert_link: Wstaw odnośnik
|
|
102
|
+
update_link: Zaktualizuj odnośnik
|
|
103
|
+
# Link dialog input field labels
|
|
104
|
+
text: Tekst
|
|
105
|
+
url: URL
|
|
106
|
+
# Markdown mode toggle button aria-label
|
|
107
|
+
edit_in_markdown: Edytuj w Markdown
|
|
108
|
+
# Error message when rich text conversion fails
|
|
109
|
+
converter_error: Nie można włączyć trybu tekstu sformatowanego. Zamiast tego użyj zwykłego edytora tekstu.
|
|
110
|
+
# Language selector aria-label
|
|
111
|
+
language: Język
|
|
112
|
+
# Plain text mode option in language selector
|
|
113
|
+
plain_text: Zwykły tekst
|
|
@@ -29,10 +29,33 @@ export class Group {
|
|
|
29
29
|
childSelectedProp: string;
|
|
30
30
|
focusChild: boolean;
|
|
31
31
|
selectFirst: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Whether a member’s `aria-controls` target is a panel this group owns, as with a tab and its
|
|
34
|
+
* tabpanel. Only then may the group hide the target when the member isn’t selected. Elsewhere
|
|
35
|
+
* `aria-controls` means something quite different — on a menu item it points at the submenu the
|
|
36
|
+
* item opens, and on a toolbar button at the region it acts on — and hiding those would break
|
|
37
|
+
* the very widget the member controls.
|
|
38
|
+
* @type {boolean}
|
|
39
|
+
*/
|
|
40
|
+
controlsPanel: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Which member holds the group’s single tab stop. `selected` suits widgets where one member is
|
|
43
|
+
* the natural entry point, such as the checked radio or the current tab. `first` suits menus,
|
|
44
|
+
* where any number of items can be checked at once, so the checked state says nothing about
|
|
45
|
+
* where the keyboard should land.
|
|
46
|
+
* @type {'selected' | 'first'}
|
|
47
|
+
*/
|
|
48
|
+
rovingTabStop: "selected" | "first";
|
|
32
49
|
/**
|
|
33
50
|
* Activate the members.
|
|
34
51
|
*/
|
|
35
52
|
activate(): void;
|
|
53
|
+
/**
|
|
54
|
+
* Put exactly one member in the tab sequence, as a composite widget should. Giving every checked
|
|
55
|
+
* member `tabindex="0"` would scatter tab stops through the widget — a menu with two checked
|
|
56
|
+
* items would take three tab presses to step over.
|
|
57
|
+
*/
|
|
58
|
+
updateTabStop(): void;
|
|
36
59
|
/**
|
|
37
60
|
* CSS selector to retrieve the members.
|
|
38
61
|
* @type {string}
|
|
@@ -48,6 +71,40 @@ export class Group {
|
|
|
48
71
|
* @type {HTMLElement[]}
|
|
49
72
|
*/
|
|
50
73
|
get activeMembers(): HTMLElement[];
|
|
74
|
+
/**
|
|
75
|
+
* The element that opens this menu, either a menu button or a menu item in the menu above.
|
|
76
|
+
* @type {HTMLElement | null}
|
|
77
|
+
*/
|
|
78
|
+
get opener(): HTMLElement | null;
|
|
79
|
+
/**
|
|
80
|
+
* The menu item that opens this menu, when this menu is a submenu. A menu button is deliberately
|
|
81
|
+
* excluded, because closing a top-level menu is not what “back to the parent item” means.
|
|
82
|
+
* @type {HTMLElement | null}
|
|
83
|
+
*/
|
|
84
|
+
get parentMenuItem(): HTMLElement | null;
|
|
85
|
+
/**
|
|
86
|
+
* Close this menu along with every menu above it.
|
|
87
|
+
* @returns {HTMLElement | null} The element that opens the outermost menu, so the caller can
|
|
88
|
+
* hand focus back to it.
|
|
89
|
+
*/
|
|
90
|
+
closeMenuChain(): HTMLElement | null;
|
|
91
|
+
/**
|
|
92
|
+
* Leave the menu the way Tab should: close it along with every menu above it, then carry focus on
|
|
93
|
+
* to whatever follows the outermost opener. The browser can’t be left to do this itself, because
|
|
94
|
+
* a modal `<dialog>` confines Tab to its own contents.
|
|
95
|
+
* @param {boolean} backwards Whether to move to the previous tab stop instead, as Shift+Tab.
|
|
96
|
+
*/
|
|
97
|
+
leaveMenu(backwards: boolean): Promise<void>;
|
|
98
|
+
/**
|
|
99
|
+
* Open the submenu of the given menu item if needed, and move focus onto its first item.
|
|
100
|
+
* @param {HTMLElement} item Menu item with `aria-haspopup="menu"`.
|
|
101
|
+
*/
|
|
102
|
+
enterSubmenu(item: HTMLElement): Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* Close this submenu and move focus back onto the menu item that opens it.
|
|
105
|
+
* @param {HTMLElement} item Menu item with `aria-haspopup="menu"`.
|
|
106
|
+
*/
|
|
107
|
+
leaveSubmenu(item: HTMLElement): void;
|
|
51
108
|
/**
|
|
52
109
|
* Get the currently selected member.
|
|
53
110
|
* @type {HTMLElement | undefined}
|