@juspay/svelte-ui-components 4.51.0 → 4.52.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/Animations/tokenizedFly.js +9 -2
- package/dist/AreaChart/AreaChart.svelte +41 -13
- package/dist/AreaChart/properties.d.ts +12 -0
- package/dist/AttachmentChipRow/AttachmentChipRow.svelte +7 -6
- package/dist/BarChart/BarChart.svelte +54 -6
- package/dist/BarChart/properties.d.ts +12 -0
- package/dist/Calendar/Calendar.svelte +124 -51
- package/dist/Card/Card.svelte +5 -0
- package/dist/ChatSuggestions/ChatSuggestions.svelte +1 -0
- package/dist/CheckListItem/CheckListItem.svelte +27 -5
- package/dist/ColorPicker/ColorPicker.svelte +40 -9
- package/dist/ColorPicker/properties.d.ts +8 -0
- package/dist/Combobox/Combobox.svelte +34 -2
- package/dist/CommandMenu/CommandMenu.svelte +35 -8
- package/dist/CommandMenu/properties.d.ts +6 -0
- package/dist/DateRangePicker/DateRangePicker.svelte +5 -5
- package/dist/DualAxisBarChart/DualAxisBarChart.svelte +41 -7
- package/dist/DualAxisBarChart/properties.d.ts +4 -0
- package/dist/FileDropzoneTrigger/FileDropzoneTrigger.svelte +104 -10
- package/dist/FileDropzoneTrigger/properties.d.ts +14 -4
- package/dist/FileInput/FileInput.svelte +38 -9
- package/dist/FileInput/context.d.ts +20 -0
- package/dist/FileInput/context.js +8 -0
- package/dist/FileInput/properties.d.ts +25 -1
- package/dist/FunnelChart/FunnelChart.svelte +20 -1
- package/dist/FunnelChart/properties.d.ts +4 -0
- package/dist/Gallery/Gallery.svelte +72 -27
- package/dist/HITL/HITL.svelte +12 -3
- package/dist/LineChart/LineChart.svelte +105 -33
- package/dist/LineChart/properties.d.ts +12 -0
- package/dist/MediaPlayer/MediaPlayer.svelte +172 -2
- package/dist/MediaPlayer/MediaPlayer.svelte.d.ts +1 -1
- package/dist/MediaPlayer/properties.d.ts +31 -0
- package/dist/Menu/Menu.svelte +6 -4
- package/dist/Modal/Modal.svelte +15 -12
- package/dist/Modal/properties.d.ts +29 -1
- package/dist/Pagination/Pagination.svelte +1 -0
- package/dist/PieChart/PieChart.svelte +51 -29
- package/dist/PieChart/properties.d.ts +4 -0
- package/dist/Pill/Pill.svelte +5 -0
- package/dist/Radio/Radio.svelte +11 -2
- package/dist/Radio/properties.d.ts +7 -2
- package/dist/SankeyChart/SankeyChart.svelte +339 -211
- package/dist/SankeyChart/properties.d.ts +4 -0
- package/dist/Scroller/Scroller.svelte +236 -4
- package/dist/Scroller/properties.d.ts +8 -0
- package/dist/Select/Select.svelte +43 -5
- package/dist/Select/properties.d.ts +17 -1
- package/dist/Sheet/Sheet.svelte +31 -4
- package/dist/Sheet/properties.d.ts +6 -0
- package/dist/SplitInput/SplitInput.svelte +17 -0
- package/dist/SplitInput/field-name.d.ts +29 -0
- package/dist/SplitInput/field-name.js +50 -0
- package/dist/SplitInput/properties.d.ts +18 -4
- package/dist/Status/Status.svelte +24 -12
- package/dist/Status/properties.d.ts +11 -0
- package/dist/Table/BuiltinCell.svelte +13 -5
- package/dist/Table/BuiltinCell.svelte.d.ts +1 -0
- package/dist/Table/Table.svelte +78 -20
- package/dist/Table/a11y.d.ts +7 -0
- package/dist/Table/a11y.js +71 -0
- package/dist/Table/cellData.js +3 -0
- package/dist/Table/properties.d.ts +13 -5
- package/dist/ThemeSwitcher/ThemeSwitcher.svelte +4 -0
- package/dist/ThinkingIndicator/ThinkingIndicator.svelte +48 -9
- package/dist/Toast/Toast.svelte +40 -4
- package/dist/Toggle/Toggle.svelte +2 -0
- package/dist/Toggle/properties.d.ts +2 -0
- package/dist/_chart/Axis.svelte +10 -0
- package/dist/_chart/ChartContainer.svelte +32 -1
- package/dist/_chart/ChartTooltip.svelte +18 -3
- package/dist/_chart/LabelPlate.svelte +114 -0
- package/dist/_chart/LabelPlate.svelte.d.ts +7 -0
- package/dist/_chart/a11y.d.ts +29 -0
- package/dist/_chart/a11y.js +44 -0
- package/dist/_chart/measure.d.ts +2 -0
- package/dist/_chart/measure.js +4 -0
- package/dist/_chart/tooltipPosition.d.ts +6 -0
- package/dist/_chart/tooltipPosition.js +30 -2
- package/dist/_chart/types.d.ts +22 -0
- package/dist/_interaction/focusable.d.ts +47 -0
- package/dist/_interaction/focusable.js +139 -0
- package/dist/assets/captions-on.svg +3 -0
- package/dist/assets/captions.svg +5 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/styles/theme-dark.css +6 -1
- package/dist/types.d.ts +2 -0
- package/dist/utils.d.ts +18 -2
- package/dist/utils.js +73 -28
- package/dist-codemod/codemod/migrate-audit.js +9 -7
- package/dist-codemod/codemod/migration-assets/legacy-palette.css +9 -1
- package/dist-codemod/codemod/migration-assets-layout.js +61 -0
- package/dist-wc/index.js +5653 -4148
- package/package.json +4 -2
|
@@ -11,14 +11,24 @@ export type OptionalFileDropzoneTriggerProperties = {
|
|
|
11
11
|
/** Bare icon + heading with no Button wrapper or caption, for inline/compact trigger placements. */
|
|
12
12
|
compact?: boolean;
|
|
13
13
|
/**
|
|
14
|
-
* Non-compact: forwarded to the inner `Button
|
|
15
|
-
* Emits both `data-pw` and `testID`
|
|
14
|
+
* Non-compact: forwarded to the inner `Button` when this trigger renders one, otherwise applied to the
|
|
15
|
+
* non-interactive surface. Compact: applied to the heading `<span>`. Emits both `data-pw` and `testID`
|
|
16
|
+
* on that element.
|
|
16
17
|
*/
|
|
17
18
|
testId?: string;
|
|
18
|
-
/** CSS class applied to the root element — the
|
|
19
|
+
/** CSS class applied to the root element — the `Button` (or, inside a `FileInput` region, the non-interactive surface that replaces it) in non-compact, the icon+heading wrapper in compact. */
|
|
19
20
|
classes?: string;
|
|
20
21
|
};
|
|
21
22
|
export type FileDropzoneTriggerEventProperties = {
|
|
22
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* Makes the non-compact trigger a real `Button` that owns the action. Use it where nothing else does:
|
|
25
|
+
* standalone, or under `<FileInput activation="trigger">` with `onclick={openFilePicker}`.
|
|
26
|
+
*
|
|
27
|
+
* Inside a default `FileInput` the region already owns activation (one Tab stop; Enter, Space and a
|
|
28
|
+
* click open the chooser), so the trigger renders the same surface as plain content instead of a
|
|
29
|
+
* second button and does not call this handler: the region opens the picker. An existing
|
|
30
|
+
* `onclick={openFilePicker}` there therefore still works, without the duplicate control.
|
|
31
|
+
* Compact never renders a button.
|
|
32
|
+
*/
|
|
23
33
|
onclick?: () => void;
|
|
24
34
|
};
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { describeField } from '../_field/description';
|
|
3
|
+
import { provideFileInputOwner } from './context';
|
|
3
4
|
import type { FileInputProperties } from './properties';
|
|
4
5
|
|
|
5
6
|
let {
|
|
6
7
|
trigger,
|
|
8
|
+
activation = 'region',
|
|
7
9
|
accept,
|
|
8
10
|
multiple = false,
|
|
9
11
|
maxSizeBytes,
|
|
@@ -28,6 +30,18 @@
|
|
|
28
30
|
describeField(fieldUid, { error: errorMessage, info: infoMessage, invalid })
|
|
29
31
|
);
|
|
30
32
|
|
|
33
|
+
/* One upload action, one interactive owner. Left alone, the region is that
|
|
34
|
+
owner and `trigger` content is plain content inside it. A consumer who wants
|
|
35
|
+
a real control inside (a Button with its own loading state, say) hands
|
|
36
|
+
ownership to it instead, and the region stops being a second control: no
|
|
37
|
+
role, no tab stop, no click or key handling -- only drag and drop. */
|
|
38
|
+
const regionOwnsActivation = $derived(activation !== 'trigger');
|
|
39
|
+
provideFileInputOwner({
|
|
40
|
+
get regionOwnsActivation() {
|
|
41
|
+
return regionOwnsActivation;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
31
45
|
let dragOver = $state(false);
|
|
32
46
|
let inputEl: HTMLInputElement | null = $state(null);
|
|
33
47
|
let pickerBusy = false;
|
|
@@ -149,21 +163,25 @@
|
|
|
149
163
|
}
|
|
150
164
|
</script>
|
|
151
165
|
|
|
166
|
+
<!-- The role is a ternary Svelte cannot resolve, so it reads the element as a plain div. Whenever
|
|
167
|
+
the tabindex renders, the role is "button" on the very same expression. -->
|
|
168
|
+
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
152
169
|
<div
|
|
153
170
|
class="file-input {classes ?? ''}"
|
|
154
171
|
class:file-input-dragover={dragOver}
|
|
155
172
|
class:file-input-disabled={disabled}
|
|
173
|
+
class:file-input-passive={!regionOwnsActivation}
|
|
156
174
|
data-pw={testId}
|
|
157
175
|
testID={testId}
|
|
158
|
-
role=
|
|
159
|
-
tabindex={disabled ? -1 : 0}
|
|
160
|
-
aria-disabled={disabled}
|
|
161
|
-
aria-describedby={field.describedBy}
|
|
176
|
+
role={regionOwnsActivation ? 'button' : null}
|
|
177
|
+
tabindex={regionOwnsActivation ? (disabled ? -1 : 0) : null}
|
|
178
|
+
aria-disabled={regionOwnsActivation ? disabled : null}
|
|
179
|
+
aria-describedby={regionOwnsActivation ? field.describedBy : null}
|
|
162
180
|
ondragover={handleDragOver}
|
|
163
181
|
ondragleave={handleDragLeave}
|
|
164
182
|
ondrop={handleDrop}
|
|
165
|
-
onkeydown={handleKeyDown}
|
|
166
|
-
onclick={handleClick}
|
|
183
|
+
onkeydown={regionOwnsActivation ? handleKeyDown : null}
|
|
184
|
+
onclick={regionOwnsActivation ? handleClick : null}
|
|
167
185
|
>
|
|
168
186
|
<input
|
|
169
187
|
bind:this={inputEl}
|
|
@@ -179,7 +197,7 @@
|
|
|
179
197
|
aria-hidden="true"
|
|
180
198
|
/>
|
|
181
199
|
|
|
182
|
-
{@render trigger({ openFilePicker, dragOver, disabled })}
|
|
200
|
+
{@render trigger({ openFilePicker, dragOver, disabled, describedBy: field.describedBy })}
|
|
183
201
|
</div>
|
|
184
202
|
|
|
185
203
|
{#if field.showsError}
|
|
@@ -231,9 +249,20 @@
|
|
|
231
249
|
cursor: pointer;
|
|
232
250
|
}
|
|
233
251
|
|
|
252
|
+
/* The region is not the control here, so it must not look like one. */
|
|
253
|
+
.file-input-passive {
|
|
254
|
+
cursor: auto;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/* The region is the one Tab stop of its upload action, so it is also the only thing that can
|
|
258
|
+
show where focus is. With no fallback this resolved to `outline: none` -- invisible to a
|
|
259
|
+
keyboard user, and only masked while a nested button supplied its own ring. `currentColor`
|
|
260
|
+
is the library's convention for a ring that needs no colour literal (Card, Pill, StatCard):
|
|
261
|
+
it follows the surrounding text colour, so it clears 3:1 on the light and the dark ground
|
|
262
|
+
alike and stays visible in forced-colors mode. :focus-visible keeps it off pointer clicks. */
|
|
234
263
|
.file-input:focus-visible {
|
|
235
|
-
outline: var(--file-input-focus-outline);
|
|
236
|
-
outline-offset: var(--file-input-focus-outline-offset);
|
|
264
|
+
outline: var(--file-input-focus-outline, 2px solid currentColor);
|
|
265
|
+
outline-offset: var(--file-input-focus-outline-offset, 2px);
|
|
237
266
|
}
|
|
238
267
|
|
|
239
268
|
.file-input-dragover {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a `FileInput` tells the content rendered inside its `trigger` snippet.
|
|
3
|
+
*
|
|
4
|
+
* A drop region is one control. By default the region itself is that control
|
|
5
|
+
* (`role="button"`, one Tab stop, Enter/Space/click open the chooser), so
|
|
6
|
+
* anything interactive placed inside it is a second control for the same
|
|
7
|
+
* action. `FileDropzoneTrigger` reads this to render as plain content when the
|
|
8
|
+
* region owns activation, instead of nesting a native `<button>` in it.
|
|
9
|
+
*
|
|
10
|
+
* A getter, not a copied boolean, so a consumer flipping `activation` on a live
|
|
11
|
+
* region is seen by content that is already mounted.
|
|
12
|
+
*/
|
|
13
|
+
export type FileInputOwner = {
|
|
14
|
+
readonly regionOwnsActivation: boolean;
|
|
15
|
+
};
|
|
16
|
+
/** Exported only so a test can stand in for the region without mounting a full `FileInput`. */
|
|
17
|
+
export declare const FILE_INPUT_OWNER: unique symbol;
|
|
18
|
+
export declare const provideFileInputOwner: (owner: FileInputOwner) => void;
|
|
19
|
+
/** Null outside a `FileInput`, where nothing owns the action but the caller. */
|
|
20
|
+
export declare const useFileInputOwner: () => FileInputOwner | null;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { getContext, hasContext, setContext } from 'svelte';
|
|
2
|
+
/** Exported only so a test can stand in for the region without mounting a full `FileInput`. */
|
|
3
|
+
export const FILE_INPUT_OWNER = Symbol('sui-file-input-owner');
|
|
4
|
+
export const provideFileInputOwner = (owner) => {
|
|
5
|
+
setContext(FILE_INPUT_OWNER, owner);
|
|
6
|
+
};
|
|
7
|
+
/** Null outside a `FileInput`, where nothing owns the action but the caller. */
|
|
8
|
+
export const useFileInputOwner = () => hasContext(FILE_INPUT_OWNER) ? getContext(FILE_INPUT_OWNER) : null;
|
|
@@ -3,10 +3,34 @@ export type FileInputSnippetProps = {
|
|
|
3
3
|
openFilePicker: () => void;
|
|
4
4
|
dragOver: boolean;
|
|
5
5
|
disabled: boolean;
|
|
6
|
+
/**
|
|
7
|
+
* The ids of the rendered error/info messages, for `aria-describedby`. The drop region links
|
|
8
|
+
* them itself while it owns activation; a control supplied under `activation="trigger"` is the
|
|
9
|
+
* one that must carry them, so it is handed here. Null when nothing is rendered.
|
|
10
|
+
*/
|
|
11
|
+
describedBy: string | null;
|
|
6
12
|
};
|
|
13
|
+
/**
|
|
14
|
+
* Which element is the single interactive control for the upload action.
|
|
15
|
+
*
|
|
16
|
+
* - `'region'` (default): the drop region is the control. It is the one Tab stop, announces as
|
|
17
|
+
* a button named from the `trigger` content, and opens the chooser on Enter, Space or a click
|
|
18
|
+
* anywhere inside it. `trigger` content must therefore be non-interactive.
|
|
19
|
+
* - `'trigger'`: the control inside `trigger` (a `Button`, say) is the one Tab stop and calls
|
|
20
|
+
* `openFilePicker`. The region becomes a passive drag-and-drop target with no role, no tab
|
|
21
|
+
* stop and no click or key handling of its own.
|
|
22
|
+
*/
|
|
23
|
+
export type FileInputActivation = 'region' | 'trigger';
|
|
7
24
|
export type FileInputProperties = {
|
|
8
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* Content slot — receives { openFilePicker, dragOver, disabled, describedBy } so consumers build
|
|
27
|
+
* any visual they need. Under the default `activation="region"` it must be non-interactive: the
|
|
28
|
+
* region is already the control, and a button inside it is a second Tab stop and a second
|
|
29
|
+
* announcement for the same action.
|
|
30
|
+
*/
|
|
9
31
|
trigger: Snippet<[FileInputSnippetProps]>;
|
|
32
|
+
/** Who owns the single interactive control. Defaults to `'region'`; see {@link FileInputActivation}. */
|
|
33
|
+
activation?: FileInputActivation;
|
|
10
34
|
accept?: string;
|
|
11
35
|
multiple?: boolean;
|
|
12
36
|
maxSizeBytes?: number;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { onMount } from 'svelte';
|
|
3
3
|
import type { FunnelChartProperties, FunnelStage } from './properties';
|
|
4
4
|
import ChartContainer from '../_chart/ChartContainer.svelte';
|
|
5
|
+
import { defaultChartName, resolveChartName } from '../_chart/a11y';
|
|
5
6
|
import ChartTooltip from '../_chart/ChartTooltip.svelte';
|
|
6
7
|
import { getColor, getContrastColor } from '../_chart/colors';
|
|
7
8
|
import { formatNumber, formatPercent } from '../_chart/format';
|
|
@@ -25,6 +26,8 @@
|
|
|
25
26
|
minHeight = 0,
|
|
26
27
|
radius = DEFAULT_CHART_CORNER_RADIUS,
|
|
27
28
|
tooltipPortal = false,
|
|
29
|
+
ariaLabel,
|
|
30
|
+
ariaDescription,
|
|
28
31
|
testId,
|
|
29
32
|
classes,
|
|
30
33
|
empty,
|
|
@@ -92,6 +95,16 @@
|
|
|
92
95
|
// The documented contract treats an all-zero dataset as empty (it would otherwise
|
|
93
96
|
// render meaningless min-height bars), so fold that into the empty check.
|
|
94
97
|
let isEmpty = $derived(data.length === 0 || data.every((stage) => stage.value === 0));
|
|
98
|
+
const chartName = $derived(
|
|
99
|
+
resolveChartName(
|
|
100
|
+
ariaLabel,
|
|
101
|
+
defaultChartName('funnel', {
|
|
102
|
+
seriesNames: data.map((stage) => stage.category)
|
|
103
|
+
})
|
|
104
|
+
)
|
|
105
|
+
);
|
|
106
|
+
// Zero-valued stages still paint structural bars and retain their callbacks.
|
|
107
|
+
const hasMarks = $derived(data.length > 0);
|
|
95
108
|
|
|
96
109
|
/**
|
|
97
110
|
* Computes the total horizontal space consumed by slope connectors.
|
|
@@ -330,6 +343,9 @@
|
|
|
330
343
|
{aspectRatio}
|
|
331
344
|
{maxHeight}
|
|
332
345
|
{minHeight}
|
|
346
|
+
ariaLabel={chartName}
|
|
347
|
+
{ariaDescription}
|
|
348
|
+
interactive={hasMarks}
|
|
333
349
|
>
|
|
334
350
|
<g transform="translate({MARGIN_LEFT}, {MARGIN_TOP})">
|
|
335
351
|
<!-- Stage bars and category labels -->
|
|
@@ -344,6 +360,7 @@
|
|
|
344
360
|
<!-- Category label above the bar -->
|
|
345
361
|
<text
|
|
346
362
|
class="funnel-category-label"
|
|
363
|
+
aria-hidden="true"
|
|
347
364
|
x={labelX}
|
|
348
365
|
y={labelAreaHeight - 6}
|
|
349
366
|
text-anchor="middle"
|
|
@@ -371,8 +388,9 @@
|
|
|
371
388
|
onblur={handleLeave}
|
|
372
389
|
onkeydown={(e) => handleKeydown(e, index)}
|
|
373
390
|
onclick={() => handleClick(index)}
|
|
374
|
-
tabindex=
|
|
391
|
+
tabindex={hasMarks ? 0 : null}
|
|
375
392
|
role="button"
|
|
393
|
+
aria-hidden={hasMarks ? null : 'true'}
|
|
376
394
|
/>
|
|
377
395
|
|
|
378
396
|
<!-- Value label centred inside the bar -->
|
|
@@ -381,6 +399,7 @@
|
|
|
381
399
|
{#if vl !== ''}
|
|
382
400
|
<text
|
|
383
401
|
class="funnel-value-label"
|
|
402
|
+
aria-hidden="true"
|
|
384
403
|
x={labelX}
|
|
385
404
|
y={by + bh / 2}
|
|
386
405
|
text-anchor="middle"
|
|
@@ -72,6 +72,10 @@ export type OptionalFunnelChartProperties = {
|
|
|
72
72
|
*/
|
|
73
73
|
tooltipPortal?: boolean;
|
|
74
74
|
/** Value for the `data-pw` attribute on the chart root element. */
|
|
75
|
+
/** Accessible name of the drawing; omit to derive one from the plotted data. */
|
|
76
|
+
ariaLabel?: string;
|
|
77
|
+
/** Longer chart description, resolved inside the drawing's own DOM/shadow root. */
|
|
78
|
+
ariaDescription?: string;
|
|
75
79
|
testId?: string;
|
|
76
80
|
/** CSS class string applied to the chart root element. Useful for CSS-variable theming. */
|
|
77
81
|
classes?: string;
|
|
@@ -283,27 +283,29 @@
|
|
|
283
283
|
</div>
|
|
284
284
|
{/if}
|
|
285
285
|
{#if showItemActions}
|
|
286
|
-
<div class="gallery-item-actions">
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
<
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
<
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
286
|
+
<div class="gallery-item-actions-frame">
|
|
287
|
+
<div class="gallery-item-actions">
|
|
288
|
+
{#if showEditButton}
|
|
289
|
+
<div class="gallery-item-action">
|
|
290
|
+
<Button
|
|
291
|
+
ariaLabel={`Edit image ${index + 1}: ${image.alt}`}
|
|
292
|
+
onclick={(event) => handleEditClick(index, event)}
|
|
293
|
+
>
|
|
294
|
+
{@render controlIcon(editSvg, editIcon)}
|
|
295
|
+
</Button>
|
|
296
|
+
</div>
|
|
297
|
+
{/if}
|
|
298
|
+
{#if showDeleteButton}
|
|
299
|
+
<div class="gallery-item-action">
|
|
300
|
+
<Button
|
|
301
|
+
ariaLabel={`Delete image ${index + 1}: ${image.alt}`}
|
|
302
|
+
onclick={(event) => handleDeleteClick(index, event)}
|
|
303
|
+
>
|
|
304
|
+
{@render controlIcon(deleteSvg, deleteIcon)}
|
|
305
|
+
</Button>
|
|
306
|
+
</div>
|
|
307
|
+
{/if}
|
|
308
|
+
</div>
|
|
307
309
|
</div>
|
|
308
310
|
{/if}
|
|
309
311
|
</div>
|
|
@@ -514,16 +516,43 @@
|
|
|
514
516
|
font-family: var(--gallery-list-caption-font-family, inherit);
|
|
515
517
|
}
|
|
516
518
|
|
|
519
|
+
/* The --_actions-* / --_action-* values are internal fallbacks that only the narrow-tile rule at the
|
|
520
|
+
end of this block changes. They sit BELOW the public --gallery-item-action* variables, so any value
|
|
521
|
+
a consumer sets still wins at every tile width. */
|
|
517
522
|
.gallery-item-actions {
|
|
518
523
|
display: flex;
|
|
519
524
|
align-items: center;
|
|
520
|
-
gap: var(--gallery-item-actions-gap, 4px);
|
|
525
|
+
gap: var(--gallery-item-actions-gap, var(--_actions-gap, 4px));
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/* The frame exists so the narrow-tile rule below has a container to ask. It cannot be the tile itself:
|
|
529
|
+
inline-size containment makes a box report no intrinsic width, and a tile that reports none leaves a
|
|
530
|
+
grid inside an ancestor that sizes to its content (inline-block, fit-content, an auto-width absolute
|
|
531
|
+
box) with nothing to measure, so the whole gallery collapsed to its gaps. An out-of-flow box adds
|
|
532
|
+
nothing to its parent's intrinsic size, so the frame can be contained and the tile sizes as before.
|
|
533
|
+
It covers the tile exactly and lets pointer events through to the image underneath; in list view it
|
|
534
|
+
takes no box at all. Named so the rule can only match these frames, never a container in the
|
|
535
|
+
consumer's own page. */
|
|
536
|
+
.gallery-item-actions-frame {
|
|
537
|
+
display: contents;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
.grid .gallery-item-actions-frame {
|
|
541
|
+
display: block;
|
|
542
|
+
position: absolute;
|
|
543
|
+
top: 0;
|
|
544
|
+
right: 0;
|
|
545
|
+
bottom: 0;
|
|
546
|
+
left: 0;
|
|
547
|
+
pointer-events: none;
|
|
548
|
+
container: gallery-tile / inline-size;
|
|
521
549
|
}
|
|
522
550
|
|
|
523
551
|
.grid .gallery-item-actions {
|
|
524
552
|
position: absolute;
|
|
525
|
-
|
|
526
|
-
|
|
553
|
+
pointer-events: auto;
|
|
554
|
+
top: var(--gallery-item-actions-top, var(--_actions-inset, 8px));
|
|
555
|
+
right: var(--gallery-item-actions-right, var(--_actions-inset, 8px));
|
|
527
556
|
}
|
|
528
557
|
|
|
529
558
|
.list .gallery-item-actions {
|
|
@@ -535,12 +564,28 @@
|
|
|
535
564
|
border-radius: var(--gallery-item-action-border-radius, 8px);
|
|
536
565
|
--button-color: transparent;
|
|
537
566
|
--button-hover-color: transparent;
|
|
538
|
-
--button-padding: var(--gallery-item-action-padding, 6px);
|
|
567
|
+
--button-padding: var(--gallery-item-action-padding, var(--_action-padding, 6px));
|
|
539
568
|
--button-border-radius: var(--gallery-item-action-border-radius, 8px);
|
|
540
569
|
--icon-container-padding: 0px;
|
|
541
570
|
--icon-padding: 0px;
|
|
542
|
-
--icon-width: var(--gallery-item-action-icon-size, 16px);
|
|
543
|
-
--icon-height: var(--gallery-item-action-icon-size, 16px);
|
|
571
|
+
--icon-width: var(--gallery-item-action-icon-size, var(--_action-icon, 16px));
|
|
572
|
+
--icon-height: var(--gallery-item-action-icon-size, var(--_action-icon, 16px));
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/* Two default-size actions need 2 x 36px + a 4px gap + both 8px insets = 92px of tile width. A tile
|
|
576
|
+
narrower than that (a three-column grid on a 320px phone is about 79px) pushed the left button past
|
|
577
|
+
the tile's overflow:hidden edge. Below 96px the defaults shrink instead: 26px buttons (still above the
|
|
578
|
+
24px WCAG 2.5.8 target minimum), 2px gap, 4px insets. */
|
|
579
|
+
@container gallery-tile (max-width: 96px) {
|
|
580
|
+
.gallery-item-actions {
|
|
581
|
+
--_actions-gap: 2px;
|
|
582
|
+
--_actions-inset: 4px;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
.gallery-item-action {
|
|
586
|
+
--_action-padding: 3px;
|
|
587
|
+
--_action-icon: 12px;
|
|
588
|
+
}
|
|
544
589
|
}
|
|
545
590
|
|
|
546
591
|
.grid .gallery-item-action {
|
package/dist/HITL/HITL.svelte
CHANGED
|
@@ -833,16 +833,25 @@
|
|
|
833
833
|
--input-container-width: 100%;
|
|
834
834
|
}
|
|
835
835
|
|
|
836
|
+
/* Wraps so a narrow card (a phone, a chat column) puts the buttons that do not
|
|
837
|
+
fit on their own line instead of running past the card. Each wrapper is
|
|
838
|
+
`flex: 1`, and its automatic minimum is its button's text width, so a row
|
|
839
|
+
that fits is laid out exactly as before; one that does not breaks between
|
|
840
|
+
buttons and the ones on a short line grow to fill it. `.hitl` is
|
|
841
|
+
`contain: layout`, so an overflowing button here would be invisible to the
|
|
842
|
+
page's own scrollWidth and unreachable by scrolling. The wrapping itself comes from the
|
|
843
|
+
zero-specificity rule below, so --hitl-buttons-wrap: nowrap still turns it off. */
|
|
836
844
|
.action-buttons {
|
|
837
845
|
display: flex;
|
|
838
846
|
gap: var(--hitl-buttons-gap, 0.75rem);
|
|
839
847
|
width: 100%;
|
|
840
848
|
}
|
|
841
849
|
|
|
842
|
-
/* Zero specificity, so a consumer's own flex-wrap rule for the row keeps winning
|
|
843
|
-
|
|
850
|
+
/* Zero specificity, so a consumer's own flex-wrap rule for the row keeps winning. The default
|
|
851
|
+
wraps, because a row that does not fit otherwise runs past the card; a row that fits is laid
|
|
852
|
+
out exactly as before. Set --hitl-buttons-wrap: nowrap to keep the single line. */
|
|
844
853
|
:where(.action-buttons) {
|
|
845
|
-
flex-wrap: var(--hitl-buttons-wrap,
|
|
854
|
+
flex-wrap: var(--hitl-buttons-wrap, wrap);
|
|
846
855
|
}
|
|
847
856
|
|
|
848
857
|
.cancel-button,
|