@gradio/image 0.5.2 → 0.5.4
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/CHANGELOG.md +13 -1
- package/Image.stories.svelte +63 -19
- package/Index.svelte +8 -6
- package/package.json +6 -6
- package/shared/ImageUploader.svelte +33 -78
- package/shared/Webcam.svelte +52 -54
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
# @gradio/image
|
2
2
|
|
3
|
+
## 0.5.4
|
4
|
+
|
5
|
+
### Fixes
|
6
|
+
|
7
|
+
- [#6865](https://github.com/gradio-app/gradio/pull/6865) [`15c97c6`](https://github.com/gradio-app/gradio/commit/15c97c6d346c475141d20615b5a865e9c44bdc76) - Fix webcam when `streaming=True`. Thanks [@hannahblair](https://github.com/hannahblair)!
|
8
|
+
|
9
|
+
## 0.5.3
|
10
|
+
|
11
|
+
### Fixes
|
12
|
+
|
13
|
+
- [#6766](https://github.com/gradio-app/gradio/pull/6766) [`73268ee`](https://github.com/gradio-app/gradio/commit/73268ee2e39f23ebdd1e927cb49b8d79c4b9a144) - Improve source selection UX. Thanks [@hannahblair](https://github.com/hannahblair)!
|
14
|
+
|
3
15
|
## 0.5.2
|
4
16
|
|
5
17
|
### Patch Changes
|
@@ -294,4 +306,4 @@ Thanks [@pngwn](https://github.com/pngwn)!
|
|
294
306
|
|
295
307
|
### Features
|
296
308
|
|
297
|
-
- [#4979](https://github.com/gradio-app/gradio/pull/4979) [`44ac8ad0`](https://github.com/gradio-app/gradio/commit/44ac8ad08d82ea12c503dde5c78f999eb0452de2) - Allow setting sketch color default. Thanks [@aliabid94](https://github.com/aliabid94)!
|
309
|
+
- [#4979](https://github.com/gradio-app/gradio/pull/4979) [`44ac8ad0`](https://github.com/gradio-app/gradio/commit/44ac8ad08d82ea12c503dde5c78f999eb0452de2) - Allow setting sketch color default. Thanks [@aliabid94](https://github.com/aliabid94)!
|
package/Image.stories.svelte
CHANGED
@@ -1,25 +1,10 @@
|
|
1
1
|
<script lang="ts">
|
2
2
|
import { Meta, Template, Story } from "@storybook/addon-svelte-csf";
|
3
3
|
import StaticImage from "./Index.svelte";
|
4
|
+
import { userEvent, within } from "@storybook/testing-library";
|
4
5
|
</script>
|
5
6
|
|
6
|
-
<Meta
|
7
|
-
title="Components/Image"
|
8
|
-
component={Image}
|
9
|
-
argTypes={{
|
10
|
-
value: {
|
11
|
-
control: "object",
|
12
|
-
description: "The image URL or file to display",
|
13
|
-
name: "value"
|
14
|
-
},
|
15
|
-
show_download_button: {
|
16
|
-
options: [true, false],
|
17
|
-
description: "If false, the download button will not be visible",
|
18
|
-
control: { type: "boolean" },
|
19
|
-
defaultValue: true
|
20
|
-
}
|
21
|
-
}}
|
22
|
-
/>
|
7
|
+
<Meta title="Components/Image" component={Image} />
|
23
8
|
|
24
9
|
<Template let:args>
|
25
10
|
<div
|
@@ -31,7 +16,7 @@
|
|
31
16
|
</Template>
|
32
17
|
|
33
18
|
<Story
|
34
|
-
name="
|
19
|
+
name="static with label and download button"
|
35
20
|
args={{
|
36
21
|
value: {
|
37
22
|
path: "https://gradio-builds.s3.amazonaws.com/demo-files/ghepardo-primo-piano.jpg",
|
@@ -44,7 +29,7 @@
|
|
44
29
|
/>
|
45
30
|
|
46
31
|
<Story
|
47
|
-
name="
|
32
|
+
name="static with no label or download button"
|
48
33
|
args={{
|
49
34
|
value: {
|
50
35
|
path: "https://gradio-builds.s3.amazonaws.com/demo-files/ghepardo-primo-piano.jpg",
|
@@ -55,3 +40,62 @@
|
|
55
40
|
show_download_button: false
|
56
41
|
}}
|
57
42
|
/>
|
43
|
+
|
44
|
+
<Story
|
45
|
+
name="interactive with upload, clipboard, and webcam"
|
46
|
+
args={{
|
47
|
+
sources: ["upload", "clipboard", "webcam"],
|
48
|
+
value: {
|
49
|
+
path: "https://gradio-builds.s3.amazonaws.com/demo-files/ghepardo-primo-piano.jpg",
|
50
|
+
url: "https://gradio-builds.s3.amazonaws.com/demo-files/ghepardo-primo-piano.jpg",
|
51
|
+
orig_name: "cheetah.jpg"
|
52
|
+
},
|
53
|
+
show_label: false,
|
54
|
+
show_download_button: false,
|
55
|
+
interactive: true
|
56
|
+
}}
|
57
|
+
play={async ({ canvasElement }) => {
|
58
|
+
const canvas = within(canvasElement);
|
59
|
+
|
60
|
+
const webcamButton = await canvas.findByLabelText("Capture from camera");
|
61
|
+
userEvent.click(webcamButton);
|
62
|
+
|
63
|
+
userEvent.click(await canvas.findByTitle("select video source"));
|
64
|
+
userEvent.click(await canvas.findByLabelText("select source"));
|
65
|
+
userEvent.click(await canvas.findByLabelText("Upload file"));
|
66
|
+
userEvent.click(await canvas.findByLabelText("Paste from clipboard"));
|
67
|
+
}}
|
68
|
+
/>
|
69
|
+
|
70
|
+
<Story
|
71
|
+
name="interactive with webcam"
|
72
|
+
args={{
|
73
|
+
sources: ["webcam"],
|
74
|
+
show_download_button: true,
|
75
|
+
interactive: true
|
76
|
+
}}
|
77
|
+
/>
|
78
|
+
|
79
|
+
<Story
|
80
|
+
name="interactive with clipboard"
|
81
|
+
args={{
|
82
|
+
sources: ["clipboard"],
|
83
|
+
show_download_button: true,
|
84
|
+
interactive: true
|
85
|
+
}}
|
86
|
+
/>
|
87
|
+
|
88
|
+
<Story
|
89
|
+
name="interactive webcam with streaming"
|
90
|
+
args={{
|
91
|
+
sources: ["webcam"],
|
92
|
+
show_download_button: true,
|
93
|
+
interactive: true,
|
94
|
+
value: {
|
95
|
+
path: "https://gradio-builds.s3.amazonaws.com/demo-files/ghepardo-primo-piano.jpg",
|
96
|
+
url: "https://gradio-builds.s3.amazonaws.com/demo-files/ghepardo-primo-piano.jpg",
|
97
|
+
orig_name: "cheetah.jpg"
|
98
|
+
},
|
99
|
+
streaming: true
|
100
|
+
}}
|
101
|
+
/>
|
package/Index.svelte
CHANGED
@@ -20,6 +20,8 @@
|
|
20
20
|
import type { LoadingStatus } from "@gradio/statustracker";
|
21
21
|
import { normalise_file } from "@gradio/client";
|
22
22
|
|
23
|
+
type sources = "upload" | "webcam" | "clipboard" | null;
|
24
|
+
|
23
25
|
export let elem_id = "";
|
24
26
|
export let elem_classes: string[] = [];
|
25
27
|
export let visible = true;
|
@@ -66,7 +68,7 @@
|
|
66
68
|
$: url && gradio.dispatch("change");
|
67
69
|
|
68
70
|
let dragging: boolean;
|
69
|
-
let
|
71
|
+
let active_source: sources = null;
|
70
72
|
</script>
|
71
73
|
|
72
74
|
{#if !interactive}
|
@@ -124,7 +126,7 @@
|
|
124
126
|
/>
|
125
127
|
|
126
128
|
<ImageUploader
|
127
|
-
bind:
|
129
|
+
bind:active_source
|
128
130
|
bind:value
|
129
131
|
selectable={_selectable}
|
130
132
|
{root}
|
@@ -144,8 +146,6 @@
|
|
144
146
|
loading_status.status = "error";
|
145
147
|
gradio.dispatch("error", detail);
|
146
148
|
}}
|
147
|
-
on:click={() => gradio.dispatch("error", "bad thing happened")}
|
148
|
-
on:error
|
149
149
|
{label}
|
150
150
|
{show_label}
|
151
151
|
{pending}
|
@@ -153,8 +153,10 @@
|
|
153
153
|
{mirror_webcam}
|
154
154
|
i18n={gradio.i18n}
|
155
155
|
>
|
156
|
-
{#if
|
157
|
-
<UploadText i18n={gradio.i18n} type="image"
|
156
|
+
{#if active_source === "upload" || !active_source}
|
157
|
+
<UploadText i18n={gradio.i18n} type="image" />
|
158
|
+
{:else if active_source === "clipboard"}
|
159
|
+
<UploadText i18n={gradio.i18n} type="clipboard" mode="short" />
|
158
160
|
{:else}
|
159
161
|
<Empty unpadded_box={true} size="large"><Image /></Empty>
|
160
162
|
{/if}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@gradio/image",
|
3
|
-
"version": "0.5.
|
3
|
+
"version": "0.5.4",
|
4
4
|
"description": "Gradio UI packages",
|
5
5
|
"type": "module",
|
6
6
|
"author": "",
|
@@ -10,12 +10,12 @@
|
|
10
10
|
"cropperjs": "^1.5.12",
|
11
11
|
"lazy-brush": "^1.0.1",
|
12
12
|
"resize-observer-polyfill": "^1.5.1",
|
13
|
-
"@gradio/
|
14
|
-
"@gradio/
|
15
|
-
"@gradio/statustracker": "^0.4.2",
|
16
|
-
"@gradio/upload": "^0.5.5",
|
17
|
-
"@gradio/utils": "^0.2.0",
|
13
|
+
"@gradio/atoms": "^0.4.1",
|
14
|
+
"@gradio/client": "^0.9.4",
|
18
15
|
"@gradio/icons": "^0.3.2",
|
16
|
+
"@gradio/statustracker": "^0.4.3",
|
17
|
+
"@gradio/utils": "^0.2.0",
|
18
|
+
"@gradio/upload": "^0.5.7",
|
19
19
|
"@gradio/wasm": "^0.4.0"
|
20
20
|
},
|
21
21
|
"main_changeset": true,
|
@@ -4,28 +4,21 @@
|
|
4
4
|
import { Image as ImageIcon } from "@gradio/icons";
|
5
5
|
import type { SelectData, I18nFormatter } from "@gradio/utils";
|
6
6
|
import { get_coordinates_of_clicked_image } from "./utils";
|
7
|
-
import {
|
8
|
-
Webcam as WebcamIcon,
|
9
|
-
ImagePaste,
|
10
|
-
Upload as UploadIcon
|
11
|
-
} from "@gradio/icons";
|
12
7
|
import Webcam from "./Webcam.svelte";
|
13
|
-
import { Toolbar, IconButton } from "@gradio/atoms";
|
14
8
|
|
15
9
|
import { Upload } from "@gradio/upload";
|
16
10
|
import { type FileData, normalise_file } from "@gradio/client";
|
17
11
|
import ClearImage from "./ClearImage.svelte";
|
12
|
+
import { SelectSource } from "@gradio/atoms";
|
18
13
|
import Image from "./Image.svelte";
|
19
14
|
|
20
15
|
export let value: null | FileData;
|
21
16
|
export let label: string | undefined = undefined;
|
22
17
|
export let show_label: boolean;
|
23
18
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
"webcam"
|
28
|
-
];
|
19
|
+
type source_type = "upload" | "webcam" | "clipboard" | "microphone" | null;
|
20
|
+
|
21
|
+
export let sources: source_type[] = ["upload", "clipboard", "webcam"];
|
29
22
|
export let streaming = false;
|
30
23
|
export let pending = false;
|
31
24
|
export let mirror_webcam: boolean;
|
@@ -35,19 +28,24 @@
|
|
35
28
|
|
36
29
|
let upload: Upload;
|
37
30
|
let uploading = false;
|
38
|
-
export let
|
31
|
+
export let active_source: source_type = null;
|
39
32
|
|
40
33
|
function handle_upload({ detail }: CustomEvent<FileData>): void {
|
41
34
|
value = normalise_file(detail, root, null);
|
42
35
|
dispatch("upload");
|
43
36
|
}
|
44
37
|
|
38
|
+
function handle_clear(): void {
|
39
|
+
value = null;
|
40
|
+
dispatch("clear");
|
41
|
+
dispatch("change", null);
|
42
|
+
}
|
43
|
+
|
45
44
|
async function handle_save(img_blob: Blob | any): Promise<void> {
|
46
45
|
pending = true;
|
47
46
|
const f = await upload.load_files([new File([img_blob], `webcam.png`)]);
|
48
47
|
|
49
48
|
value = f?.[0] || null;
|
50
|
-
if (!streaming) active_tool = null;
|
51
49
|
|
52
50
|
await tick();
|
53
51
|
|
@@ -55,7 +53,7 @@
|
|
55
53
|
pending = false;
|
56
54
|
}
|
57
55
|
|
58
|
-
$: active_streaming = streaming &&
|
56
|
+
$: active_streaming = streaming && active_source === "webcam";
|
59
57
|
$: if (uploading && !active_streaming) value = null;
|
60
58
|
|
61
59
|
$: value && !value.url && (value = normalise_file(value, root, null));
|
@@ -80,61 +78,16 @@
|
|
80
78
|
}
|
81
79
|
}
|
82
80
|
|
83
|
-
|
84
|
-
|
85
|
-
icon: UploadIcon,
|
86
|
-
label: i18n("Upload"),
|
87
|
-
order: 0
|
88
|
-
},
|
89
|
-
webcam: {
|
90
|
-
icon: WebcamIcon,
|
91
|
-
label: i18n("Webcam"),
|
92
|
-
order: 1
|
93
|
-
},
|
94
|
-
clipboard: {
|
95
|
-
icon: ImagePaste,
|
96
|
-
label: i18n("Paste"),
|
97
|
-
order: 2
|
98
|
-
}
|
99
|
-
};
|
100
|
-
|
101
|
-
$: sources_list = sources.sort(
|
102
|
-
(a, b) => sources_meta[a].order - sources_meta[b].order
|
103
|
-
);
|
104
|
-
|
105
|
-
$: {
|
106
|
-
if (sources.length === 1 && sources[0] === "webcam") {
|
107
|
-
active_tool = "webcam";
|
108
|
-
}
|
81
|
+
$: if (!active_source && sources) {
|
82
|
+
active_source = sources[0];
|
109
83
|
}
|
110
84
|
|
111
|
-
async function
|
85
|
+
async function handle_select_source(
|
112
86
|
source: (typeof sources)[number]
|
113
87
|
): Promise<void> {
|
114
88
|
switch (source) {
|
115
89
|
case "clipboard":
|
116
|
-
|
117
|
-
for (let i = 0; i < items.length; i++) {
|
118
|
-
const type = items[i].types.find((t) => t.startsWith("image/"));
|
119
|
-
if (type) {
|
120
|
-
value = null;
|
121
|
-
items[i].getType(type).then(async (blob) => {
|
122
|
-
const f = await upload.load_files([
|
123
|
-
new File([blob], `clipboard.${type.replace("image/", "")}`)
|
124
|
-
]);
|
125
|
-
f;
|
126
|
-
value = f?.[0] || null;
|
127
|
-
});
|
128
|
-
break;
|
129
|
-
}
|
130
|
-
}
|
131
|
-
});
|
132
|
-
break;
|
133
|
-
case "webcam":
|
134
|
-
active_tool = "webcam";
|
135
|
-
break;
|
136
|
-
case "upload":
|
137
|
-
upload.open_file_upload();
|
90
|
+
upload.paste_clipboard();
|
138
91
|
break;
|
139
92
|
default:
|
140
93
|
break;
|
@@ -155,21 +108,21 @@
|
|
155
108
|
{/if}
|
156
109
|
<div class="upload-container">
|
157
110
|
<Upload
|
158
|
-
hidden={value !== null ||
|
111
|
+
hidden={value !== null || active_source === "webcam"}
|
159
112
|
bind:this={upload}
|
160
113
|
bind:uploading
|
161
114
|
bind:dragging
|
162
|
-
filetype="image/*"
|
115
|
+
filetype={active_source === "clipboard" ? "clipboard" : "image/*"}
|
163
116
|
on:load={handle_upload}
|
164
117
|
on:error
|
165
118
|
{root}
|
166
119
|
disable_click={!sources.includes("upload")}
|
167
120
|
>
|
168
|
-
{#if value === null
|
121
|
+
{#if value === null}
|
169
122
|
<slot />
|
170
123
|
{/if}
|
171
124
|
</Upload>
|
172
|
-
{#if
|
125
|
+
{#if active_source === "webcam" && (streaming || (!streaming && !value))}
|
173
126
|
<Webcam
|
174
127
|
on:capture={(e) => handle_save(e.detail)}
|
175
128
|
on:stream={(e) => handle_save(e.detail)}
|
@@ -191,17 +144,12 @@
|
|
191
144
|
{/if}
|
192
145
|
</div>
|
193
146
|
{#if sources.length > 1 || sources.includes("clipboard")}
|
194
|
-
<
|
195
|
-
{
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
label="{source}-image-toolbar-btn"
|
201
|
-
padded={false}
|
202
|
-
/>
|
203
|
-
{/each}
|
204
|
-
</Toolbar>
|
147
|
+
<SelectSource
|
148
|
+
{sources}
|
149
|
+
bind:active_source
|
150
|
+
{handle_clear}
|
151
|
+
handle_select={handle_select_source}
|
152
|
+
/>
|
205
153
|
{/if}
|
206
154
|
</div>
|
207
155
|
|
@@ -209,6 +157,13 @@
|
|
209
157
|
.image-frame :global(img) {
|
210
158
|
width: var(--size-full);
|
211
159
|
height: var(--size-full);
|
160
|
+
object-fit: cover;
|
161
|
+
}
|
162
|
+
|
163
|
+
.image-frame {
|
164
|
+
object-fit: cover;
|
165
|
+
width: 100%;
|
166
|
+
height: 100%;
|
212
167
|
}
|
213
168
|
|
214
169
|
.upload-container {
|
package/shared/Webcam.svelte
CHANGED
@@ -182,7 +182,7 @@
|
|
182
182
|
<!-- need to suppress for video streaming https://github.com/sveltejs/svelte/issues/5967 -->
|
183
183
|
<video bind:this={video_source} class:flip={mirror_webcam} />
|
184
184
|
{#if !streaming}
|
185
|
-
<div class
|
185
|
+
<div class="button-wrap">
|
186
186
|
<button
|
187
187
|
on:click={mode === "image" ? take_picture : take_recording}
|
188
188
|
aria-label={mode === "image" ? "capture photo" : "start recording"}
|
@@ -212,29 +212,32 @@
|
|
212
212
|
<div class="icon" title="select video source">
|
213
213
|
<DropdownArrow />
|
214
214
|
</div>
|
215
|
-
|
216
|
-
{#if options_open}
|
217
|
-
<div class="select-wrap" use:click_outside={handle_click_outside}>
|
218
|
-
<!-- svelte-ignore a11y-click-events-have-key-events-->
|
219
|
-
<!-- svelte-ignore a11y-no-static-element-interactions-->
|
220
|
-
<span
|
221
|
-
class="inset-icon"
|
222
|
-
on:click|stopPropagation={() => (options_open = false)}
|
223
|
-
>
|
224
|
-
<DropdownArrow />
|
225
|
-
</span>
|
226
|
-
{#each video_sources as source}
|
227
|
-
<!-- svelte-ignore a11y-click-events-have-key-events-->
|
228
|
-
<!-- svelte-ignore a11y-no-static-element-interactions-->
|
229
|
-
<div on:click={() => selectVideoSource(source.deviceId)}>
|
230
|
-
{source.label}
|
231
|
-
</div>
|
232
|
-
{/each}
|
233
|
-
</div>
|
234
|
-
{/if}
|
235
215
|
</button>
|
236
216
|
{/if}
|
237
217
|
</div>
|
218
|
+
{#if options_open}
|
219
|
+
<select
|
220
|
+
class="select-wrap"
|
221
|
+
aria-label="select source"
|
222
|
+
use:click_outside={handle_click_outside}
|
223
|
+
>
|
224
|
+
<button
|
225
|
+
class="inset-icon"
|
226
|
+
on:click|stopPropagation={() => (options_open = false)}
|
227
|
+
>
|
228
|
+
<DropdownArrow />
|
229
|
+
</button>
|
230
|
+
{#if video_sources.length === 0}
|
231
|
+
<option value="">{i18n("common.no_devices")}</option>
|
232
|
+
{:else}
|
233
|
+
{#each video_sources as source}
|
234
|
+
<option on:click={() => selectVideoSource(source.deviceId)}>
|
235
|
+
{source.label}
|
236
|
+
</option>
|
237
|
+
{/each}
|
238
|
+
{/if}
|
239
|
+
</select>
|
240
|
+
{/if}
|
238
241
|
{/if}
|
239
242
|
</div>
|
240
243
|
|
@@ -243,36 +246,28 @@
|
|
243
246
|
position: relative;
|
244
247
|
width: var(--size-full);
|
245
248
|
height: var(--size-full);
|
246
|
-
min-height: var(--size-60);
|
247
249
|
}
|
248
250
|
|
249
251
|
video {
|
250
252
|
width: var(--size-full);
|
251
253
|
height: var(--size-full);
|
254
|
+
object-fit: cover;
|
252
255
|
}
|
253
256
|
|
254
257
|
.button-wrap {
|
255
|
-
display: flex;
|
256
258
|
position: absolute;
|
257
|
-
|
259
|
+
background-color: var(--block-background-fill);
|
260
|
+
border: 1px solid var(--border-color-primary);
|
261
|
+
border-radius: var(--radius-xl);
|
262
|
+
padding: var(--size-1-5);
|
263
|
+
display: flex;
|
258
264
|
bottom: var(--size-2);
|
259
|
-
left:
|
260
|
-
|
261
|
-
align-items: center;
|
262
|
-
margin: auto;
|
265
|
+
left: 50%;
|
266
|
+
transform: translate(-50%, 0);
|
263
267
|
box-shadow: var(--shadow-drop-lg);
|
264
268
|
border-radius: var(--radius-xl);
|
265
|
-
|
266
|
-
|
267
|
-
height: var(--size-8);
|
268
|
-
padding: var(--size-2-5);
|
269
|
-
padding-right: var(--size-1);
|
270
|
-
z-index: var(--layer-3);
|
271
|
-
}
|
272
|
-
|
273
|
-
.capture {
|
274
|
-
width: var(--size-14);
|
275
|
-
transform: translateX(var(--size-2-5));
|
269
|
+
line-height: var(--size-3);
|
270
|
+
color: var(--button-secondary-text-color);
|
276
271
|
}
|
277
272
|
|
278
273
|
@media (--screen-md) {
|
@@ -289,9 +284,8 @@
|
|
289
284
|
|
290
285
|
.icon {
|
291
286
|
opacity: 0.8;
|
292
|
-
width:
|
293
|
-
height:
|
294
|
-
color: white;
|
287
|
+
width: 18px;
|
288
|
+
height: 18px;
|
295
289
|
display: flex;
|
296
290
|
justify-content: space-between;
|
297
291
|
align-items: center;
|
@@ -310,35 +304,39 @@
|
|
310
304
|
-webkit-appearance: none;
|
311
305
|
-moz-appearance: none;
|
312
306
|
appearance: none;
|
307
|
+
color: var(--button-secondary-text-color);
|
313
308
|
background-color: transparent;
|
314
|
-
|
315
|
-
|
316
|
-
font-size: 1rem;
|
317
|
-
/* padding: 0.5rem; */
|
318
|
-
width: max-content;
|
309
|
+
width: 95%;
|
310
|
+
font-size: var(--text-md);
|
319
311
|
position: absolute;
|
320
|
-
|
321
|
-
right: 0;
|
312
|
+
bottom: var(--size-2);
|
322
313
|
background-color: var(--block-background-fill);
|
323
314
|
box-shadow: var(--shadow-drop-lg);
|
324
315
|
border-radius: var(--radius-xl);
|
325
316
|
z-index: var(--layer-top);
|
326
|
-
border: 1px solid var(--border-color-
|
317
|
+
border: 1px solid var(--border-color-primary);
|
327
318
|
text-align: left;
|
328
|
-
|
319
|
+
line-height: var(--size-4);
|
320
|
+
white-space: nowrap;
|
321
|
+
text-overflow: ellipsis;
|
322
|
+
left: 50%;
|
323
|
+
transform: translate(-50%, 0);
|
324
|
+
max-width: var(--size-52);
|
329
325
|
}
|
330
326
|
|
331
|
-
.select-wrap >
|
327
|
+
.select-wrap > option {
|
332
328
|
padding: 0.25rem 0.5rem;
|
333
329
|
border-bottom: 1px solid var(--border-color-accent);
|
334
330
|
padding-right: var(--size-8);
|
331
|
+
text-overflow: ellipsis;
|
332
|
+
overflow: hidden;
|
335
333
|
}
|
336
334
|
|
337
|
-
.select-wrap >
|
335
|
+
.select-wrap > option:hover {
|
338
336
|
background-color: var(--color-accent);
|
339
337
|
}
|
340
338
|
|
341
|
-
.select-wrap >
|
339
|
+
.select-wrap > option:last-child {
|
342
340
|
border: none;
|
343
341
|
}
|
344
342
|
|