@umbra.ui/core 0.2.0 → 0.4.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 (61) hide show
  1. package/dist/components/controls/InlineDropdown/InlineDropdown.vue +290 -0
  2. package/dist/components/controls/InlineDropdown/README.md +35 -0
  3. package/dist/components/controls/InlineDropdown/theme.css +40 -0
  4. package/dist/components/dialogs/Alert/Alert.vue +122 -11
  5. package/dist/components/dialogs/Alert/theme.css +20 -0
  6. package/dist/components/dialogs/Toast/useToast.d.ts +1 -1
  7. package/dist/components/inputs/AutogrowRichTextView/AutogrowRichTextView.vue +128 -0
  8. package/dist/components/inputs/AutogrowRichTextView/README.md +86 -0
  9. package/dist/components/inputs/AutogrowRichTextView/editor.css +211 -0
  10. package/dist/components/inputs/AutogrowRichTextView/theme.css +28 -0
  11. package/dist/components/inputs/InputCryptoAddress/InputCryptoAddress.vue +512 -0
  12. package/dist/components/inputs/InputCryptoAddress/README.md +45 -0
  13. package/dist/components/inputs/InputCryptoAddress/theme.css +80 -0
  14. package/dist/components/inputs/Tags/TagBar.vue +7 -4
  15. package/dist/components/inputs/Tags/theme.css +4 -0
  16. package/dist/components/inputs/search/README.md +64 -736
  17. package/dist/components/inputs/search/SearchOverlay.vue +376 -0
  18. package/dist/components/inputs/search/SearchResultCell.vue +205 -0
  19. package/dist/components/inputs/search/theme.css +66 -21
  20. package/dist/components/inputs/search/types.d.ts +27 -5
  21. package/dist/components/inputs/search/types.d.ts.map +1 -1
  22. package/dist/components/inputs/search/types.ts +33 -5
  23. package/dist/components/menus/ActionMenu/ActionMenu.vue +29 -7
  24. package/dist/components/menus/ActionMenu/theme.css +1 -1
  25. package/dist/components/menus/ActionMenu/types.d.ts +9 -0
  26. package/dist/components/menus/ActionMenu/types.d.ts.map +1 -0
  27. package/dist/components/menus/ActionMenu/types.js +1 -0
  28. package/dist/components/menus/ActionMenu/types.ts +9 -0
  29. package/dist/components/models/Popover/Popover.vue +6 -84
  30. package/dist/css/umbra-ui.css +1 -0
  31. package/dist/index.d.ts +7 -3
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +5 -2
  34. package/package.json +21 -16
  35. package/src/components/controls/InlineDropdown/InlineDropdown.vue +290 -0
  36. package/src/components/controls/InlineDropdown/README.md +35 -0
  37. package/src/components/controls/InlineDropdown/theme.css +40 -0
  38. package/src/components/dialogs/Alert/Alert.vue +122 -11
  39. package/src/components/dialogs/Alert/theme.css +20 -0
  40. package/src/components/inputs/AutogrowRichTextView/AutogrowRichTextView.vue +128 -0
  41. package/src/components/inputs/AutogrowRichTextView/README.md +86 -0
  42. package/src/components/inputs/AutogrowRichTextView/editor.css +211 -0
  43. package/src/components/inputs/AutogrowRichTextView/theme.css +28 -0
  44. package/src/components/inputs/InputCryptoAddress/InputCryptoAddress.vue +512 -0
  45. package/src/components/inputs/InputCryptoAddress/README.md +45 -0
  46. package/src/components/inputs/InputCryptoAddress/theme.css +80 -0
  47. package/src/components/inputs/Tags/TagBar.vue +7 -4
  48. package/src/components/inputs/Tags/theme.css +4 -0
  49. package/src/components/inputs/search/README.md +64 -736
  50. package/src/components/inputs/search/SearchOverlay.vue +376 -0
  51. package/src/components/inputs/search/SearchResultCell.vue +205 -0
  52. package/src/components/inputs/search/theme.css +66 -21
  53. package/src/components/inputs/search/types.ts +33 -5
  54. package/src/components/menus/ActionMenu/ActionMenu.vue +29 -7
  55. package/src/components/menus/ActionMenu/theme.css +1 -1
  56. package/src/components/menus/ActionMenu/types.ts +9 -0
  57. package/src/components/models/Popover/Popover.vue +6 -84
  58. package/src/index.ts +13 -3
  59. package/src/vue.d.ts +7 -26
  60. package/src/components/inputs/search/SearchBar.vue +0 -394
  61. package/src/components/inputs/search/SearchResults.vue +0 -310
@@ -0,0 +1,512 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ ref,
4
+ onMounted,
5
+ onBeforeUpdate,
6
+ computed,
7
+ watch,
8
+ type ComponentPublicInstance,
9
+ } from "vue";
10
+ import {
11
+ ChevronRightIcon,
12
+ CheckIcon,
13
+ CircleWarningIcon,
14
+ } from "@umbra.ui/icons";
15
+ import gsap from "gsap";
16
+ import { Flip } from "gsap/Flip";
17
+ import "./theme.css";
18
+ gsap.registerPlugin(Flip);
19
+
20
+ export type CryptoChain = "eth" | "btc" | "sol";
21
+
22
+ export interface KnownAddress {
23
+ label: string;
24
+ address: string;
25
+ }
26
+
27
+ export interface Props {
28
+ value?: string;
29
+ placeholder?: string;
30
+ state?: "normal" | "active" | "disabled" | "readonly" | "error";
31
+ size?: "compact" | "normal";
32
+ chain: CryptoChain;
33
+ knownAddresses?: KnownAddress[];
34
+ knownAddressesLabel?: string;
35
+ validator?: (value: string) => boolean;
36
+ }
37
+
38
+ const props = withDefaults(defineProps<Props>(), {
39
+ value: "",
40
+ placeholder: "Enter wallet address",
41
+ state: "normal",
42
+ size: "normal",
43
+ knownAddresses: () => [],
44
+ knownAddressesLabel: "Known Addresses",
45
+ });
46
+
47
+ const emit = defineEmits<{
48
+ "update:value": [value: string];
49
+ }>();
50
+
51
+ const internalValue = ref(props.value);
52
+ const inputRef = ref<HTMLInputElement | null>(null);
53
+ const isFocused = ref(false);
54
+
55
+ watch(
56
+ () => props.value,
57
+ (newValue) => {
58
+ internalValue.value = newValue;
59
+ }
60
+ );
61
+
62
+ const normalizeValue = (value: string) => value.trim();
63
+
64
+ const isValidEth = (value: string) => /^0x[a-fA-F0-9]{40}$/.test(value);
65
+
66
+ const isValidBtc = (value: string) => {
67
+ const legacy = /^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$/.test(value);
68
+ const bech32 = /^(bc1)[0-9a-z]{25,87}$/.test(value);
69
+ return legacy || bech32;
70
+ };
71
+
72
+ const isValidSol = (value: string) =>
73
+ /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(value);
74
+
75
+ const validateValue = (value: string) => {
76
+ if (!value) return false;
77
+ if (props.validator) {
78
+ return props.validator(value);
79
+ }
80
+ switch (props.chain) {
81
+ case "eth":
82
+ return isValidEth(value);
83
+ case "btc":
84
+ return isValidBtc(value);
85
+ case "sol":
86
+ return isValidSol(value);
87
+ default:
88
+ return false;
89
+ }
90
+ };
91
+
92
+ const validationState = computed(() => {
93
+ const value = normalizeValue(internalValue.value);
94
+ if (!value) return "idle";
95
+ return validateValue(value) ? "valid" : "invalid";
96
+ });
97
+
98
+ const computedState = computed(() => {
99
+ if (props.state === "disabled" || props.state === "readonly") {
100
+ return props.state;
101
+ }
102
+ if (validationState.value === "invalid") return "error";
103
+ return props.state;
104
+ });
105
+
106
+ const statusMessage = computed(() => {
107
+ if (validationState.value === "invalid") return "Invalid address";
108
+ if (validationState.value === "valid") return "Valid address";
109
+ return "";
110
+ });
111
+
112
+ const iconColor = computed(() => {
113
+ if (validationState.value === "valid") {
114
+ return "black";
115
+ }
116
+ if (validationState.value === "invalid" || computedState.value === "error") {
117
+ return "var(--input-error-text)";
118
+ }
119
+ switch (computedState.value) {
120
+ case "readonly":
121
+ return "var(--input-readonly-text)";
122
+ case "disabled":
123
+ return "var(--input-disabled-text)";
124
+ default:
125
+ return "var(--InlineDropdown-button-text-color)";
126
+ }
127
+ });
128
+
129
+ const handleInput = (event: Event) => {
130
+ const target = event.target as HTMLInputElement;
131
+ internalValue.value = target.value.trim();
132
+ emit("update:value", internalValue.value);
133
+ };
134
+
135
+ const handleFocus = () => {
136
+ isFocused.value = true;
137
+ inputRef.value?.select();
138
+ openList();
139
+ };
140
+
141
+ const handleBlur = () => {
142
+ isFocused.value = false;
143
+ closeList();
144
+ };
145
+
146
+ const focusInput = () => {
147
+ if (
148
+ computedState.value === "disabled" ||
149
+ computedState.value === "readonly"
150
+ ) {
151
+ return;
152
+ }
153
+ inputRef.value?.focus();
154
+ };
155
+
156
+ const knownItems = computed(() => props.knownAddresses ?? []);
157
+ const hasKnown = computed(() => knownItems.value.length > 0);
158
+
159
+ const selectKnownAddress = (item: KnownAddress) => {
160
+ internalValue.value = item.address;
161
+ emit("update:value", item.address);
162
+ if (!itemsInDrawer.value) {
163
+ toggleItems();
164
+ }
165
+ isFocused.value = false;
166
+ inputRef.value?.blur();
167
+ };
168
+
169
+ const itemsInDrawer = ref(true);
170
+ const drawerRef = ref<HTMLElement | null>(null);
171
+ const listRef = ref<HTMLElement | null>(null);
172
+ const itemRefs = ref<HTMLElement[]>([]);
173
+
174
+ const setItemRef = (el: Element | ComponentPublicInstance | null) => {
175
+ if (el instanceof HTMLElement) {
176
+ itemRefs.value.push(el);
177
+ }
178
+ };
179
+
180
+ onBeforeUpdate(() => {
181
+ itemRefs.value = [];
182
+ });
183
+
184
+ const toggleItems = () => {
185
+ const drawer = drawerRef.value;
186
+ const itemList = listRef.value;
187
+
188
+ if (!drawer || !itemList) {
189
+ return;
190
+ }
191
+
192
+ const targets = itemRefs.value;
193
+
194
+ if (targets.length === 0) return;
195
+
196
+ const isInDrawer = targets[0].parentElement === drawer;
197
+ const destination = isInDrawer ? itemList : drawer;
198
+
199
+ const state = Flip.getState([...targets, itemList, drawer], {
200
+ props: "opacity",
201
+ });
202
+
203
+ for (const target of targets) {
204
+ destination.appendChild(target);
205
+ gsap.set(target, { opacity: isInDrawer ? 1 : 0 });
206
+ }
207
+
208
+ itemsInDrawer.value = !isInDrawer;
209
+
210
+ Flip.from(state, {
211
+ duration: 0.3,
212
+ ease: "power1.inOut",
213
+ });
214
+ };
215
+
216
+ const openList = () => {
217
+ if (!hasKnown.value || !itemsInDrawer.value) return;
218
+ toggleItems();
219
+ };
220
+
221
+ const closeList = () => {
222
+ if (!hasKnown.value || itemsInDrawer.value) return;
223
+ toggleItems();
224
+ };
225
+
226
+ onMounted(() => {
227
+ for (const itemElement of itemRefs.value) {
228
+ itemElement.style.opacity = "0";
229
+ }
230
+ });
231
+ </script>
232
+
233
+ <template>
234
+ <div :class="$style.container">
235
+ <div ref="drawerRef" :class="$style.item_drawer">
236
+ <div v-if="hasKnown" :ref="setItemRef" :class="$style.list_header">
237
+ <p :class="['subheadline', $style.list_header_text]">
238
+ {{ knownAddressesLabel }}
239
+ </p>
240
+ </div>
241
+ <div
242
+ v-for="item in knownItems"
243
+ :key="item.address"
244
+ :ref="setItemRef"
245
+ :class="[
246
+ $style.item,
247
+ internalValue === item.address ? $style.item_selected : '',
248
+ ]"
249
+ @mousedown.prevent="selectKnownAddress(item)"
250
+ >
251
+ <div :class="$style.item_content">
252
+ <p class="body">{{ item.label }}</p>
253
+ <p class="subheadline-mono">{{ item.address }}</p>
254
+ </div>
255
+ <CheckIcon
256
+ v-if="internalValue === item.address"
257
+ :color="iconColor"
258
+ size="16"
259
+ />
260
+ </div>
261
+ </div>
262
+ <div :class="$style.identity_card">
263
+ <div
264
+ :class="[
265
+ $style.button,
266
+ !itemsInDrawer ? $style.button_drawer_open : '',
267
+ validationState === 'valid' ? $style.button_valid : '',
268
+ validationState === 'invalid' ? $style.button_error : '',
269
+ isFocused && !hasKnown ? $style.button_focus : '',
270
+ ]"
271
+ @click="focusInput"
272
+ >
273
+ <input
274
+ ref="inputRef"
275
+ class="body-mono"
276
+ type="text"
277
+ :placeholder="placeholder"
278
+ :value="internalValue"
279
+ :disabled="computedState === 'disabled'"
280
+ :readonly="computedState === 'readonly'"
281
+ autocomplete="new-password"
282
+ autocorrect="off"
283
+ autocapitalize="off"
284
+ spellcheck="false"
285
+ name="crypto-address"
286
+ inputmode="text"
287
+ :class="$style.input"
288
+ @input="handleInput"
289
+ @focus="handleFocus"
290
+ @blur="handleBlur"
291
+ />
292
+ <div :class="$style.icon_row">
293
+ <div
294
+ v-if="hasKnown"
295
+ :class="$style.icon"
296
+ :style="{
297
+ transform:
298
+ itemsInDrawer || !hasKnown ? 'rotate(0)' : 'rotate(90deg)',
299
+ }"
300
+ >
301
+ <ChevronRightIcon :color="iconColor" />
302
+ </div>
303
+ </div>
304
+ </div>
305
+ </div>
306
+ <div
307
+ ref="listRef"
308
+ :class="[
309
+ $style.item_list,
310
+ itemsInDrawer || !hasKnown ? $style.item_list_hidden : '',
311
+ ]"
312
+ ></div>
313
+ <p
314
+ v-if="validationState !== 'idle'"
315
+ :class="[
316
+ $style.status_message,
317
+ validationState === 'invalid'
318
+ ? $style.error_message
319
+ : $style.valid_message,
320
+ 'footnote',
321
+ ]"
322
+ >
323
+ {{ statusMessage }}
324
+ </p>
325
+ </div>
326
+ </template>
327
+
328
+ <style module>
329
+ .container {
330
+ display: flex;
331
+ flex-direction: column;
332
+ position: relative;
333
+ }
334
+ .button {
335
+ display: flex;
336
+ align-items: center;
337
+ justify-content: space-between;
338
+ gap: 0.471rem;
339
+ flex-grow: 1;
340
+ background-color: var(--InlineDropdown-button-bg);
341
+ font-weight: 700;
342
+ cursor: text;
343
+ user-select: none;
344
+ transition: 0.3s ease-in-out color, 0.3s ease-in-out background-color,
345
+ 0.3s ease-in-out transform, 0.3s ease-in-out border-radius,
346
+ 0.3s ease-in-out border;
347
+ transform: scale(1);
348
+ will-change: transform;
349
+ padding-left: 0.882rem;
350
+ padding-right: 0.882rem;
351
+ padding-top: 0.588rem;
352
+ padding-bottom: 0.588rem;
353
+ font-size: var(--callout);
354
+ border: 1px solid var(--InlineDropdown-border);
355
+ border-radius: 0.471rem;
356
+ }
357
+ .button_drawer_open {
358
+ border-bottom-left-radius: 0;
359
+ border-bottom-right-radius: 0;
360
+ border-color: transparent;
361
+ }
362
+ .button_error {
363
+ border-color: var(--input-error-border);
364
+ background-color: var(--crypto-button-error-bg);
365
+ }
366
+ .button_valid {
367
+ border-color: var(--input-success-text);
368
+ background-color: var(--crypto-button-valid-bg);
369
+ }
370
+ .button_valid > input {
371
+ color: var(--crypto-button-valid-text);
372
+ }
373
+ .button_focus {
374
+ border-color: var(--crypto-button-focus-border);
375
+ }
376
+ .item_drawer {
377
+ position: absolute;
378
+ top: 0;
379
+ left: 0;
380
+ right: 0;
381
+ display: grid;
382
+ grid-template-columns: 1fr;
383
+ grid-template-rows: 1fr;
384
+ grid-template-areas: "content";
385
+ pointer-events: none;
386
+ }
387
+ .identity_card {
388
+ display: flex;
389
+ align-items: center;
390
+ gap: 0.471rem;
391
+ z-index: 2;
392
+ }
393
+ .identity_card > :first-child {
394
+ width: 100%;
395
+ height: 100%;
396
+ }
397
+
398
+ .input {
399
+ flex: 1;
400
+ border: none;
401
+ background: transparent;
402
+ outline: none;
403
+ color: var(--InlineDropdown-button-text-color);
404
+ min-width: 0;
405
+ width: 100%;
406
+ flex-grow: 1;
407
+ }
408
+
409
+ .input::placeholder {
410
+ color: var(--input-placeholder);
411
+ }
412
+
413
+ .icon_row {
414
+ display: flex;
415
+ align-items: center;
416
+ gap: 0.353rem;
417
+ }
418
+
419
+ .icon {
420
+ transform: translateY(-50%);
421
+ display: flex;
422
+ align-items: center;
423
+ justify-content: center;
424
+ pointer-events: none;
425
+ opacity: 0.6;
426
+ transition: transform 0.2s ease;
427
+ }
428
+ .icon > svg {
429
+ width: 1rem;
430
+ height: 1rem;
431
+ }
432
+ .item_list {
433
+ display: flex;
434
+ flex-direction: column;
435
+ transition: padding-top 0.3s ease, border 0.3s ease;
436
+ }
437
+ .item_list > :last-child {
438
+ border-bottom-left-radius: 0.471rem;
439
+ border-bottom-right-radius: 0.471rem;
440
+ }
441
+ .list_header {
442
+ grid-area: content;
443
+ border-left: 1px solid var(--InlineDropdown-border);
444
+ border-right: 1px solid var(--InlineDropdown-border);
445
+ border-bottom: 1px solid var(--InlineDropdown-border);
446
+ border-top: 1px solid var(--InlineDropdown-border);
447
+ padding: 0.353rem 0.706rem;
448
+ background-color: var(--crypto-known-header-bg);
449
+ }
450
+
451
+ .list_header_text {
452
+ margin: 0;
453
+ color: var(--crypto-known-header-text);
454
+ opacity: 1;
455
+ }
456
+ .item {
457
+ display: flex;
458
+ align-items: center;
459
+ justify-content: space-between;
460
+ grid-area: content;
461
+ border-bottom: 1px solid var(--InlineDropdown-border);
462
+ border-left: 1px solid var(--InlineDropdown-border);
463
+ border-right: 1px solid var(--InlineDropdown-border);
464
+ padding-left: 0.706rem;
465
+ padding-right: 0.706rem;
466
+ padding-top: 0.471rem;
467
+ padding-bottom: 0.471rem;
468
+ cursor: pointer;
469
+ transition: background-color 0.15s ease;
470
+ user-select: none;
471
+ width: 100%;
472
+ background-color: var(--InlineDropdown-item-bg);
473
+ }
474
+ .item:hover {
475
+ background-color: var(--InlineDropdown-item-hover-bg);
476
+ }
477
+ .item_selected {
478
+ background-color: var(--InlineDropdown-item-selected-bg);
479
+ border-top: 1px solid var(--InlineDropdown-item-selected-border);
480
+ border-bottom: 1px solid var(--InlineDropdown-item-selected-border);
481
+ }
482
+ /* targets the item immediately before the selected one and removes its bottom border */
483
+ .item:has(+ .item_selected) {
484
+ border-bottom: none;
485
+ }
486
+ .item > svg {
487
+ width: 1rem;
488
+ height: 1rem;
489
+ opacity: 0.7;
490
+ }
491
+
492
+ .item_content {
493
+ display: flex;
494
+ flex-direction: column;
495
+ gap: 0.118rem;
496
+ }
497
+ .item_content > :first-child {
498
+ opacity: 0.7;
499
+ }
500
+
501
+ .status_message {
502
+ margin-top: 0.235rem;
503
+ }
504
+
505
+ .error_message {
506
+ color: var(--input-error-text);
507
+ }
508
+
509
+ .valid_message {
510
+ color: var(--input-success-text);
511
+ }
512
+ </style>
@@ -0,0 +1,45 @@
1
+ ## InputCryptoAddress
2
+
3
+ Inline wallet address input with chain-specific validation and a known-address dropdown.
4
+
5
+ ### Usage
6
+
7
+ ```vue
8
+ <script setup lang="ts">
9
+ import { ref } from "vue";
10
+ import { InputCryptoAddress } from "@umbra.ui/core";
11
+
12
+ const address = ref("");
13
+ const knownAddresses = [
14
+ { label: "Treasury", address: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e" },
15
+ {
16
+ label: "Cold Wallet",
17
+ address: "0x66f820a414680b5bcda5eeca5dea238543f42054",
18
+ },
19
+ ];
20
+ </script>
21
+
22
+ <template>
23
+ <InputCryptoAddress
24
+ v-model:value="address"
25
+ chain="eth"
26
+ :known-addresses="knownAddresses"
27
+ placeholder="Paste wallet address"
28
+ />
29
+ </template>
30
+ ```
31
+
32
+ ### Props
33
+
34
+ - `value`: current input value (for `v-model:value`).
35
+ - `chain`: `"eth" | "btc" | "sol"` for built-in validation.
36
+ - `knownAddresses`: array of `{ label, address }` shown on focus.
37
+ - `knownAddressesLabel`: optional header text above the dropdown.
38
+ - `validator`: optional custom validator `(value) => boolean`.
39
+ - `placeholder`, `state`, `size`: standard input props.
40
+
41
+ ### Behavior
42
+
43
+ - Shows “Valid address” or “Invalid address” based on validation.
44
+ - Displays a check icon on valid entries and a warning icon on invalid entries.
45
+ - When focused, shows the known-address dropdown (no filtering by input).
@@ -0,0 +1,80 @@
1
+ /* Light theme using Colors */
2
+ :root {
3
+ /* Alert overlay colors */
4
+ --alert-overlay-bg: rgba(
5
+ 0,
6
+ 0,
7
+ 0,
8
+ 0.4
9
+ ); /* blackA8 - lighter overlay for light mode */
10
+
11
+ /* Alert content colors */
12
+ --alert-content-bg: #ffffff; /* white background for light mode */
13
+ --alert-content-color: #1a1d23; /* gray12 - dark text for light mode */
14
+ --alert-content-shadow: rgba(
15
+ 0,
16
+ 0,
17
+ 0,
18
+ 0.08
19
+ ); /* blackA6 - lighter shadow for light mode */
20
+
21
+ /* Alert description colors */
22
+ --alert-description-opacity: 0.7; /* slightly more visible in light mode */
23
+
24
+ /* Alert text field colors */
25
+ --alert-text-field-bg: #f3f4f6;
26
+ --alert-text-field-border: rgba(0, 0, 0, 0.1);
27
+ --alert-text-field-color: #1a1d23;
28
+ --alert-text-field-focus-bg: #ffffff;
29
+ --alert-text-field-border-focused: #0090ff;
30
+
31
+ /* Alert error color */
32
+ --alert-error-color: #e5484d;
33
+
34
+ /* Alert destructive button color */
35
+ --alert-destructive-color: #e5484d; /* red9 - destructive action color */
36
+
37
+ /* InputCryptoAddress colors */
38
+ --crypto-button-error-bg: #fde8ea;
39
+ --crypto-button-valid-bg: #f3f4f6;
40
+ --crypto-button-valid-text: #1a1d23;
41
+ --crypto-button-focus-border: #0090ff;
42
+ --crypto-known-header-bg: #f3f4f6;
43
+ --crypto-known-header-text: #1a1d23;
44
+ }
45
+
46
+ /* Dark theme */
47
+ .dark,
48
+ .dark-theme {
49
+ /* Alert overlay colors */
50
+ --alert-overlay-bg: rgba(0, 0, 0, 0.7); /* Original dark mode value */
51
+
52
+ /* Alert content colors */
53
+ --alert-content-bg: #222222; /* Original dark mode value */
54
+ --alert-content-color: #eeeeee; /* Original dark mode value */
55
+ --alert-content-shadow: rgba(0, 0, 0, 0.1); /* Original dark mode value */
56
+
57
+ /* Alert description colors */
58
+ --alert-description-opacity: 0.5; /* Original dark mode value */
59
+
60
+ /* Alert text field colors */
61
+ --alert-text-field-bg: #2a2a2a;
62
+ --alert-text-field-border: #3a3a3a;
63
+ --alert-text-field-color: #eeeeee;
64
+ --alert-text-field-focus-bg: #3a3a3a;
65
+ --alert-text-field-border-focused: #0090ff;
66
+
67
+ /* Alert error color */
68
+ --alert-error-color: #e5484d;
69
+
70
+ /* Alert destructive button color */
71
+ --alert-destructive-color: #e5484d; /* red9 - destructive action color */
72
+
73
+ /* InputCryptoAddress colors */
74
+ --crypto-button-error-bg: #3b1219;
75
+ --crypto-button-valid-bg: #b4b4b4;
76
+ --crypto-button-valid-text: #111111;
77
+ --crypto-button-focus-border: #0090ff;
78
+ --crypto-known-header-bg: #6e6e6e;
79
+ --crypto-known-header-text: #ffffff;
80
+ }
@@ -516,10 +516,10 @@ const themeConfig = computed(() => {
516
516
  <div v-if="allowEditing" :class="$style.controls" ref="controlContainer">
517
517
  <div :class="$style.add_button" ref="button">
518
518
  <IconButton
519
- iconName="plus"
520
- buttonType="square"
519
+ iconName="circle-plus"
520
+ buttonType="plain"
521
521
  buttonStyle="secondary"
522
- :buttonSize="10"
522
+ :buttonSize="18"
523
523
  @click="beginEditing"
524
524
  />
525
525
  <p
@@ -675,16 +675,18 @@ const themeConfig = computed(() => {
675
675
  .add_label {
676
676
  user-select: none;
677
677
  color: var(--tagbar-text);
678
+ opacity: 0.7;
678
679
  }
679
680
 
680
681
  .field {
681
682
  background-color: var(--tagbar-field-bg);
682
683
  color: var(--tagbar-field-text);
683
- border: none;
684
+ border: 1px solid var(--tagbar-field-border);
684
685
  border-radius: 999px;
685
686
  min-height: 1.412rem;
686
687
  padding-left: 0.471rem;
687
688
  padding-right: 0.588rem;
689
+ height: 100%;
688
690
  grid-area: content;
689
691
  opacity: 0;
690
692
  user-select: none;
@@ -784,6 +786,7 @@ const themeConfig = computed(() => {
784
786
  top: 0;
785
787
  left: 0;
786
788
  background-color: var(--tagpicker-container-bg);
789
+ border: 1px solid var(--tagpicker-container-border);
787
790
  border-radius: 0.353rem;
788
791
  min-width: 18.824rem;
789
792
  overflow: hidden;
@@ -6,6 +6,7 @@
6
6
  --tagbar-border: 1px solid #d9d9d9; /* gray1 - field background */
7
7
  --tagbar-field-bg: #e8e8e8; /* gray1 - field background */
8
8
  --tagbar-field-text: #1f2937; /* gray8 - field text */
9
+ --tagbar-field-border: rgba(0, 0, 0, 0.12);
9
10
  --tagbar-list-border: rgba(0, 0, 0, 0.1); /* blackA6 - list border */
10
11
 
11
12
  /* Search overlay */
@@ -13,6 +14,7 @@
13
14
 
14
15
  /* TagPicker colors */
15
16
  --tagpicker-container-bg: #ffffff; /* white - container background */
17
+ --tagpicker-container-border: rgba(0, 0, 0, 0.12);
16
18
  --tagpicker-container-shadow: rgba(0, 0, 0, 0.1); /* blackA6 - shadow */
17
19
  --tagpicker-container-inset-shadow: rgba(
18
20
  255,
@@ -75,6 +77,7 @@
75
77
  --tagbar-border: 1px solid #606060;
76
78
  --tagbar-field-bg: #606060; /* Dark field background */
77
79
  --tagbar-field-text: #eeeeee; /* Light field text */
80
+ --tagbar-field-border: rgba(255, 255, 255, 0.12);
78
81
  --tagbar-list-border: rgba(255, 255, 255, 0.15); /* Light border */
79
82
 
80
83
  /* Search overlay */
@@ -82,6 +85,7 @@
82
85
 
83
86
  /* TagPicker colors */
84
87
  --tagpicker-container-bg: #484848; /* Dark container background */
88
+ --tagpicker-container-border: rgba(255, 255, 255, 0.12);
85
89
  --tagpicker-container-shadow: rgba(0, 0, 0, 0.21); /* Darker shadow */
86
90
  --tagpicker-container-inset-shadow: rgba(
87
91
  255,