poe-svelte-ui-lib 1.17.6 → 1.19.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.
@@ -1,3 +1,5 @@
1
+ <!-- $lib/Accordion/Accordion.svelte — заголовок больше не увеличивается при раскрытии,
2
+ тень на hover заменена фоном, радиус 14. Логика toggle не тронута. -->
1
3
  <script lang="ts">
2
4
  import { slide } from "svelte/transition"
3
5
  import type { IAccordionProps } from "../types"
@@ -16,64 +18,45 @@
16
18
  const toggle = () => (isOpen = !isOpen)
17
19
  </script>
18
20
 
19
- <div id={`${id}-${crypto.randomUUID().slice(0, 6)}`} class={twMerge(`w-full p-1`, wrapperClass)} transition:slide={{ duration: 250 }}>
20
- <div class="shadow-(--border-shadow-color) hover:shadow-(--focus-shadow-color) transition-shadow duration-250 rounded-xl bg-(--container-color)">
21
- <button class="flex w-full cursor-pointer items-center justify-between p-4 py-3.5" onclick={toggle}>
22
- <div class="flex w-full items-center">
23
- <span class={`flex h-7 w-7 shrink-0 items-center justify-center overflow-visible [&_svg]:h-full [&_svg]:max-h-full [&_svg]:w-full [&_svg]:max-w-full`}>
24
- {#if label?.icon}
21
+ <div id={`${id}-${crypto.randomUUID().slice(0, 6)}`} class={twMerge(`w-full`, wrapperClass)} transition:slide={{ duration: 200 }}>
22
+ <div class="overflow-hidden rounded-[14px] border border-(--hairline-color) bg-(--back-color)">
23
+ <!-- Заголовок темнее контента (--container-color), а не наоборот: так он читается
24
+ как отдельная кликабельная полоса, а раскрытое содержимое остаётся на чистом
25
+ светлом фоне (--back-color) — там часто лежат вложенные карточки/поля. -->
26
+ <button
27
+ class="flex w-full cursor-pointer items-center justify-between gap-3 bg-(--container-color) px-4 py-3 text-left transition-colors duration-150 hover:bg-(--border-color)"
28
+ onclick={toggle}
29
+ >
30
+ <div class="flex min-w-0 flex-1 items-center gap-3">
31
+ {#if label?.icon}
32
+ <span class="flex h-[18px] w-[18px] shrink-0 items-center justify-center text-(--muted-color) [&_svg]:h-full [&_svg]:w-full">
25
33
  {#if typeof label?.icon === "string"}
26
34
  {@html label.icon}
27
35
  {:else}
28
36
  {@const IconComponent = label?.icon}
29
37
  <IconComponent />
30
38
  {/if}
31
- {/if}
32
- </span>
33
-
34
- <div
35
- class="{twMerge(
36
- 'flex w-full justify-start ',
37
- label.class?.split(' ').find((cls: string) => cls.startsWith('justify-')),
38
- )} "
39
- >
40
- <span
41
- class="{twMerge(
42
- `m-0 origin-top-left cursor-pointer justify-end px-3 font-semibold transition duration-200 ${isOpen ? 'scale-110' : ''}`,
43
- label.class,
44
- )} text-lg"
45
- >
46
- {label?.name}
47
39
  </span>
48
- </div>
40
+ {/if}
41
+ <span class={twMerge(`min-w-0 truncate text-[14px] font-semibold`, label.class)}>{label?.name}</span>
49
42
  </div>
50
43
 
51
- <div class="w-10 pl-4">
52
- <svg
53
- xmlns="http://www.w3.org/2000/svg"
54
- class="h-[1.1rem] w-[1.1rem] transition-transform duration-250"
55
- style="transform: rotate({isOpen ? 180 : 0}deg)"
56
- viewBox="0 0 24 24"
57
- >
58
- <path
59
- fill="none"
60
- stroke="currentColor"
61
- stroke-linecap="round"
62
- stroke-linejoin="round"
63
- stroke-width="2"
64
- d="M18 12.5s-4.419 6-6 6s-6-6-6-6m12-7s-4.419 6-6 6s-6-6-6-6"
65
- color="currentColor"
66
- />
67
- </svg>
68
- </div>
44
+ <svg
45
+ xmlns="http://www.w3.org/2000/svg"
46
+ class="h-[14px] w-[14px] shrink-0 text-(--faint-color) transition-transform duration-200"
47
+ style="transform: rotate({isOpen ? 180 : 0}deg)"
48
+ viewBox="0 0 24 24"
49
+ >
50
+ <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 9l6 6 6-6" />
51
+ </svg>
69
52
  </button>
70
53
 
71
54
  {#if isOpen}
72
55
  <div
73
- class="grid w-full p-4 sm:p-3 {image ? 'gap-x-2' : 'gap-2'} inset-shadow-[0_10px_10px_-15px_rgb(0_0_0_/0.5)]"
74
- transition:slide={{ duration: 250 }}
75
- style="grid-template-columns: repeat({size.width || 10}, minmax(0, 1fr));
76
- grid-template-rows: repeat({size.height || 2}, {image ? 'minmax(6.5rem, auto)' : 'auto'});
56
+ class="grid w-full gap-3 border-t border-(--hairline-color) bg-(--back-color) p-4"
57
+ transition:slide={{ duration: 200 }}
58
+ style="grid-template-columns: repeat({size.width || 1}, minmax(0, 1fr));
59
+ grid-template-rows: repeat({size.height || 1}, {image ? 'minmax(6.5rem, auto)' : 'auto'});
77
60
  {image ? `background-image: url(${image}); background-size: cover; background-position: center;` : ''}"
78
61
  >
79
62
  {@render children?.()}
@@ -1,9 +1,17 @@
1
- <!-- $lib/ElementsUI/Button.svelte -->
1
+ <!-- $lib/Button/Button.svelte — шаг 3 плана.
2
+ API не изменён: id, wrapperClass, componentClass, disabled, content, keyBind, onClick.
3
+ Что изменилось внутри:
4
+ • роль определяется значением bg-* (намерение), а не фактом его наличия;
5
+ • убрано автоподчёркивание текста — кнопка больше не выглядит ссылкой;
6
+ • единый радиус 10px (иконка без подписи — круг), высота 36px, кегль 14/600;
7
+ • кольцо фокуса вместо размытой тени;
8
+ • disabled через токены, а не opacity: 0.5;
9
+ • иконка встала в поток (flex + gap) вместо absolute left-3. -->
2
10
  <script lang="ts">
3
11
  import { onMount } from "svelte"
4
12
  import type { IButtonProps } from "../types"
5
- import { fly } from "svelte/transition"
6
13
  import { twMerge } from "tailwind-merge"
14
+ import Tooltip from "../Tooltip.svelte"
7
15
 
8
16
  let {
9
17
  id = crypto.randomUUID(),
@@ -21,69 +29,66 @@
21
29
 
22
30
  let showInfo = $state(false)
23
31
 
24
- let tooltipConfig = $derived({
25
- top: {
26
- pos: "bottom-full left-1/2 mb-2",
27
- tr: "translateX(-50%)",
28
- arr: "top-full left-1/2 -translate-x-1/2 -translate-y-1/2 rotate-45",
29
- off: "y:-15",
30
- },
31
- bottom: {
32
- pos: "top-full left-1/2 mt-2",
33
- tr: "translateX(-50%)",
34
- arr: "bottom-full left-1/2 -translate-x-1/2 translate-y-1/2 rotate-45",
35
- off: "y:15",
36
- },
37
- left: {
38
- pos: "top-1/2 right-full mr-2",
39
- tr: "translateY(-50%)",
40
- arr: "top-1/2 -right-2 -translate-x-1/2 -translate-y-1/2 rotate-45",
41
- off: "x:15",
42
- },
43
- right: {
44
- pos: "top-1/2 left-full ml-2",
45
- tr: "translateY(-50%)",
46
- arr: "top-1/2 -left-2 translate-x-1/2 -translate-y-1/2 rotate-45 ",
47
- off: "x:-15",
48
- },
32
+ /* ─── Роль кнопки по последнему классу bg-* ─────────────────────────
33
+ bg-blue / bg-green / bg-cyan / bg-purple / bg-orange / bg-yellow → primary (заливка + белый текст)
34
+ bg-red / bg-pink → destructive
35
+ bg-max / bg-gray / bg-*-soft → secondary (поверхность + контур)
36
+ bg-transparent / без bg-* → plain (текст акцентом) */
37
+ const SURFACE_BG = ["bg-max", "bg-gray"]
38
+ const DANGER_BG = ["bg-red", "bg-pink"]
39
+
40
+ const role = $derived.by(() => {
41
+ const bg = componentClass.split(/\s+/).filter((c) => c.startsWith("bg-")).at(-1) ?? ""
42
+ if (!bg || bg === "bg-transparent") return "plain"
43
+ if (SURFACE_BG.includes(bg) || bg.endsWith("-soft")) return "secondary"
44
+ if (DANGER_BG.includes(bg)) return "destructive"
45
+ return "primary"
46
+ })
47
+
48
+ const isIconOnly = $derived(!!content?.icon && !content.name?.trim())
49
+
50
+ /* bg-(--bg-color) намеренно не входит в roleClass: он приклеивается к готовому
51
+ className вне twMerge (см. разметку ниже) — иначе twMerge видит его и явный
52
+ bg-* из componentClass (bg-red, bg-max…) как конфликтующие утилиты одной
53
+ группы и вырезает один из них, из-за чего заливка кнопки пропадает. */
54
+ const roleBg = $derived.by(() => {
55
+ if (disabled) return "bg-(--gray-color)"
56
+ if (role === "plain") return "bg-transparent"
57
+ return "bg-(--bg-color)"
49
58
  })
50
59
 
51
- const svgSize = $derived(() => {
52
- const widthClass = twMerge(
53
- wrapperClass.split(" ").find((cls: string) => cls.startsWith("w-")),
54
- componentClass.split(" ").find((cls: string) => cls.startsWith("w-")),
55
- )
56
- const heightClass = twMerge(
57
- wrapperClass.split(" ").find((cls: string) => cls.startsWith("h-")),
58
- componentClass.split(" ").find((cls: string) => cls.startsWith("h-")),
59
- )
60
- return `${widthClass} ${heightClass}`
60
+ const roleClass = $derived.by(() => {
61
+ if (disabled) return "text-(--faint-color) border border-transparent cursor-not-allowed"
62
+ switch (role) {
63
+ case "primary":
64
+ case "destructive":
65
+ return "text-(--on-accent) border border-transparent shadow-(--elevation-1) hover:brightness-90 dark:hover:brightness-115"
66
+ case "secondary":
67
+ return "text-(--font-color) border border-(--border-color) hover:bg-(--container-color)"
68
+ default:
69
+ return "text-(--accent-color) border border-transparent hover:bg-(--accent-soft)"
70
+ }
61
71
  })
62
72
 
63
- /* Обработчик клика */
64
73
  const handleClick = (event: Event) => {
65
74
  if (disabled || !onClick) return
66
75
  event?.stopPropagation()
67
76
  onClick()
68
77
  }
69
78
 
70
- /* Обработчик горячих клавиш */
71
79
  const handleKeyDown = (event: KeyboardEvent) => {
72
80
  if (disabled || !keyBind.key || !onClick) return
73
-
74
81
  const isKeyMatch = event.key === keyBind.key
75
82
  const isCtrlMatch = keyBind.ctrlKey === undefined || event.ctrlKey === keyBind.ctrlKey
76
83
  const isShiftMatch = keyBind.shiftKey === undefined || event.shiftKey === keyBind.shiftKey
77
84
  const isAltMatch = keyBind.altKey === undefined || event.altKey === keyBind.altKey
78
85
  const isMetaMatch = keyBind.metaKey === undefined || event.metaKey === keyBind.metaKey
79
-
80
86
  if (isKeyMatch && isCtrlMatch && isShiftMatch && isAltMatch && isMetaMatch) {
81
87
  event.preventDefault()
82
88
  onClick()
83
89
  }
84
90
  }
85
91
 
86
- /* Подписка на события клавиатуры */
87
92
  onMount(() => {
88
93
  if (keyBind) window.addEventListener("keydown", handleKeyDown)
89
94
  return () => {
@@ -92,20 +97,21 @@
92
97
  })
93
98
  </script>
94
99
 
95
- <div class={twMerge(`relative flex w-full flex-col items-center `, wrapperClass)}>
100
+ <div class={twMerge(`relative flex w-full flex-col items-center`, wrapperClass)}>
96
101
  <button
97
- id={`${id}-${crypto.randomUUID().slice(0, 6)}`}
98
- class="{twMerge(
99
- `relative inline-block items-center font-semibold w-full transition duration-200 select-none
100
- ${componentClass.split(' ').find((cl: string) => cl === 'bg-transparent') || !componentClass.split(' ').find((cl: string) => cl.startsWith('bg-')) ? 'underline' : 'border border-(--bg-color) shadow-sm hover:shadow-md'}
101
- ${content.icon && !content.name?.trim() ? 'rounded-full' : 'rounded-2xl py-1 px-2'}
102
- ${disabled ? 'cursor-not-allowed opacity-50 hover:shadow-none' : 'cursor-pointer active:scale-97'} `,
102
+ {id}
103
+ class={`${twMerge(
104
+ `relative inline-flex w-full items-center justify-center gap-2 leading-none font-semibold text-[14px] select-none
105
+ transition-[background-color,filter,box-shadow] duration-150
106
+ focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--accent-color)
107
+ ${isIconOnly ? "aspect-square rounded-full p-0" : "min-h-9 rounded-[10px] px-4 py-2"}
108
+ ${disabled ? "" : "cursor-pointer active:scale-[0.97]"}`,
109
+ roleClass,
103
110
  componentClass,
104
- )} bg-(--bg-color)"
105
- style={!content.name?.trim() && content.name && content.icon ? `padding-inline: ${content.name.length / 2}rem` : ""}
111
+ )} ${roleBg}`}
106
112
  onclick={(event) => handleClick(event)}
107
113
  {disabled}
108
- aria-label={content.name}
114
+ aria-label={content.name || undefined}
109
115
  onmouseenter={() => {
110
116
  if (content.info?.text) showInfo = true
111
117
  }}
@@ -113,43 +119,30 @@
113
119
  if (content.info?.text) showInfo = false
114
120
  }}
115
121
  >
116
- <div class="flex flex-row items-center justify-center gap-2">
117
- {#if content?.icon}
118
- <span
119
- class={` ${content.name?.trim() ? "absolute left-3" : ""} ${typeof content?.icon == "string" ? "p-0.5" : ""} flex items-center justify-center overflow-visible
120
- ${content.name?.trim() ? "h-8 w-8" : `${svgSize()}`} [&_svg]:h-full [&_svg]:max-h-full [&_svg]:w-full [&_svg]:max-w-full`}
121
- >
122
- {#if typeof content?.icon === "string"}
123
- {@html content.icon}
124
- {:else}
125
- {@const IconComponent = content?.icon}
126
- <IconComponent />
127
- {/if}
128
- </span>
129
- {/if}
122
+ {#if content?.icon}
123
+ <span
124
+ class={`flex shrink-0 items-center justify-center overflow-visible
125
+ ${content.name?.trim() ? "h-[18px] w-[18px]" : "h-full w-full"}
126
+ [&_svg]:h-full [&_svg]:max-h-full [&_svg]:w-full [&_svg]:max-w-full`}
127
+ >
128
+ {#if typeof content?.icon === "string"}
129
+ {@html content.icon}
130
+ {:else}
131
+ {@const IconComponent = content?.icon}
132
+ <IconComponent />
133
+ {/if}
134
+ </span>
135
+ {/if}
130
136
 
131
- {#if content.name?.trim()}
132
- <div class="flex-1 truncate">
133
- {content.name}
134
- {#if keyBind.key && keyBind.showHint}
135
- <div class="text-xs opacity-70">
136
- ({keyBind.ctrlKey ? "Ctrl+" : ""}{keyBind.shiftKey ? "Shift+" : ""}{keyBind.altKey ? "Alt+" : ""}{keyBind.key})
137
- </div>
138
- {/if}
139
- </div>
137
+ {#if content.name?.trim()}
138
+ <span class="truncate">{content.name}</span>
139
+ {#if keyBind.key && keyBind.showHint}
140
+ <span class="ml-1 text-[11px] font-medium tabular-nums opacity-60">
141
+ {keyBind.ctrlKey ? "Ctrl+" : ""}{keyBind.shiftKey ? "Shift+" : ""}{keyBind.altKey ? "Alt+" : ""}{keyBind.key}
142
+ </span>
140
143
  {/if}
141
- </div>
144
+ {/if}
142
145
  </button>
143
146
 
144
- {#if showInfo && content.info?.side && tooltipConfig[content.info?.side]}
145
- {@const config = tooltipConfig[content.info?.side]}
146
- <div
147
- transition:fly={{ [config.off.split(":")[0]]: parseInt(config.off.split(":")[1]), duration: 300 }}
148
- class={`absolute z-50 w-max max-w-xs rounded-md bg-(--container-color) px-3 py-1 text-sm shadow-lg ${config.pos}`}
149
- style={`transform: ${config.tr};`}
150
- >
151
- {content.info?.text}
152
- <div class={`absolute ${config.arr} h-2 w-2 transform bg-(--container-color)`}></div>
153
- </div>
154
- {/if}
147
+ <Tooltip show={showInfo} text={content.info?.text ?? ""} side={content.info?.side ?? "top"} />
155
148
  </div>
@@ -2,6 +2,7 @@
2
2
  import { Select, Switch, T, type IOption } from "./"
3
3
  import type { Snippet } from "svelte"
4
4
  import { fade } from "svelte/transition"
5
+ import CopiedChip from "./CopiedChip.svelte"
5
6
 
6
7
  let {
7
8
  component,
@@ -65,14 +66,7 @@
65
66
  aria-label="Копировать текст"
66
67
  >
67
68
  <div class="size-6 text-sm [&_svg]:h-full [&_svg]:max-h-full [&_svg]:w-full [&_svg]:max-w-full">
68
- {#if isCopied}
69
- <div
70
- class="right-1..5 absolute top-1/2 -translate-y-1/2 transform rounded-md bg-(--green-color) px-1.5 py-1 shadow-lg"
71
- transition:fade={{ duration: 200 }}
72
- >
73
- ✓
74
- </div>
75
- {:else}
69
+ {#if !isCopied}
76
70
  <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
77
71
  <g fill="none" stroke="currentColor" stroke-width="1.5">
78
72
  <path
@@ -82,6 +76,7 @@
82
76
  </g>
83
77
  </svg>
84
78
  {/if}
79
+ <CopiedChip show={isCopied} class="right-1.5 top-1/2 -translate-y-1/2" />
85
80
  </div>
86
81
  </button>
87
82
  <pre class="overflow-x-auto">{codeText}</pre>
@@ -0,0 +1,20 @@
1
+ <!-- $lib/CopiedChip.svelte — единая всплывающая метка "Скопировано" после клика
2
+ на кнопку копирования. Раньше в каждом месте (Input, Table, Map,
3
+ ComponentExample) была своя версия: где-то текст, где-то просто "✓",
4
+ разные цвета (--green-color вместо --green-soft) и радиусы, а в
5
+ ComponentExample — опечатка в классе позиционирования (right-1..5,
6
+ невалидный Tailwind-класс, физически ничего не позиционировал). -->
7
+ <script lang="ts">
8
+ import { fade } from "svelte/transition"
9
+
10
+ let { show = false, class: className = "" }: { show?: boolean; class?: string } = $props()
11
+ </script>
12
+
13
+ {#if show}
14
+ <span
15
+ class="{className} absolute flex items-center gap-1 rounded-lg bg-(--green-soft) px-2 py-1 text-[11px] font-bold whitespace-nowrap text-(--green-color)"
16
+ transition:fade={{ duration: 150 }}
17
+ >
18
+ Скопировано
19
+ </span>
20
+ {/if}
@@ -0,0 +1,7 @@
1
+ type $$ComponentProps = {
2
+ show?: boolean;
3
+ class?: string;
4
+ };
5
+ declare const CopiedChip: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type CopiedChip = ReturnType<typeof CopiedChip>;
7
+ export default CopiedChip;
@@ -1,15 +1,19 @@
1
- <!-- $lib/ElementsUI/Divider.svelte -->
1
+ <!-- $lib/Divider/Divider.svelte — волосяная линия вместо скруглённой полосы 2px. -->
2
2
  <script lang="ts">
3
3
  import { twMerge } from "tailwind-merge"
4
4
  import type { IDividerProps } from "../types"
5
5
 
6
- let { id = crypto.randomUUID(), wrapperClass = "", type = "line", width = 2, color = "bg-max", anchor = "center" }: IDividerProps = $props()
6
+ let { id = crypto.randomUUID(), wrapperClass = "", type = "line", width = 1, color = "", anchor = "center" }: IDividerProps = $props()
7
7
 
8
8
  const anchorClass = { top: "justify-start", center: "justify-center", bottom: "justify-end" } as const
9
9
  </script>
10
10
 
11
11
  <div id={`${id}-${crypto.randomUUID().slice(0, 6)}`} class={twMerge(`flex h-full w-full flex-col ${anchorClass[anchor]}`, wrapperClass)}>
12
12
  {#if type === "line"}
13
- <div class="{twMerge('w-full rounded-full', color)} bg-(--bg-color)" style="height: {width}px;"></div>
13
+ <!-- Без bg-* класса берём --hairline-color; явный bg-* по-прежнему уважается через --bg-color -->
14
+ <div
15
+ class={twMerge("w-full", color)}
16
+ style="height: {width}px; background: {color ? 'var(--bg-color)' : 'var(--hairline-color)'};"
17
+ ></div>
14
18
  {/if}
15
19
  </div>
@@ -42,17 +42,21 @@
42
42
  }
43
43
  </script>
44
44
 
45
- <div class={twMerge(`flex flex-col items-center`, wrapperClass)}>
45
+ <!-- Не входил в исходный design-handoff (пропущен вместе с остальными точками,
46
+ которые авторы документа не осматривали) — сейчас приведён к общей палитре:
47
+ радиус 10/высота 36 как у Input/Select, сегмент "Выбрать файл" залит акцентом
48
+ с текстом --on-accent (было --font-color по тёмно-синему — нечитаемо). -->
49
+ <div class={twMerge(`flex flex-col items-center gap-1`, wrapperClass)}>
46
50
  {#if label.name}
47
- <h5 class={`${label.class} w-full`}>{label.name}</h5>
51
+ <span class={twMerge(`w-full text-[12px] font-semibold text-(--muted-color)`, label.class)}>{label.name}</span>
48
52
  {/if}
49
53
 
50
54
  {#if type === "image"}
51
55
  <div class="relative">
52
56
  <button
53
- class="flex items-center justify-center overflow-hidden {imageSize.form === 'circle' ? 'rounded-full' : 'rounded-2xl'}
54
- bg-(--back-color) shadow-sm transition duration-250
55
- {disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer hover:shadow-md'}"
57
+ class="flex items-center justify-center overflow-hidden {imageSize.form === 'circle' ? 'rounded-full' : 'rounded-2xl'}
58
+ border border-(--hairline-color) bg-(--container-color) transition-colors duration-150
59
+ {disabled ? 'cursor-not-allowed opacity-45' : 'cursor-pointer hover:bg-(--border-color)'}"
56
60
  style={`height: ${imageSize.height}; width: ${imageSize.width}`}
57
61
  onclick={triggerFileInput}
58
62
  {disabled}
@@ -67,7 +71,7 @@
67
71
  `}
68
72
  />
69
73
  {:else}
70
- <span class="text-sm text-gray-500">Image</span>
74
+ <span class="text-[12px] font-medium text-(--faint-color)">Image</span>
71
75
  {/if}
72
76
  </button>
73
77
  <input id={ID} type="file" class="absolute -z-10 h-0 w-0 overflow-hidden opacity-0" {accept} {disabled} onchange={handleFileChange} />
@@ -77,19 +81,19 @@
77
81
  <input
78
82
  id={ID}
79
83
  type="file"
80
- class="absolute left-0 z-1 h-8.5 w-full opacity-0 {disabled ? 'cursor-not-allowed' : 'cursor-pointer'}"
84
+ class="absolute left-0 z-1 h-9 w-full opacity-0 {disabled ? 'cursor-not-allowed' : 'cursor-pointer'}"
81
85
  {accept}
82
86
  {disabled}
83
87
  onchange={handleFileChange}
84
88
  />
85
89
  <div
86
- class="flex w-full overflow-hidden rounded-2xl font-semibold shadow-sm transition duration-250 {disabled ? '' : 'hover:shadow-md'}
87
- "
90
+ class="flex h-9 w-full overflow-hidden rounded-[10px] border border-(--border-color) text-[14px] transition-colors duration-150
91
+ {disabled ? 'opacity-45' : 'hover:border-(--faint-color)'}"
88
92
  >
89
- <div class="flex w-1/3 items-center justify-center bg-(--blue-color) p-2 py-1 {disabled ? 'opacity-50' : ''}">
93
+ <div class="flex shrink-0 items-center justify-center bg-(--accent-color) px-3 font-semibold whitespace-nowrap text-(--on-accent)">
90
94
  {$T("constructor.props.file.select")}
91
95
  </div>
92
- <div class="flex flex-1 items-center justify-start truncate bg-(--back-color) px-2 {disabled ? 'opacity-50' : ''}">
96
+ <div class="flex min-w-0 flex-1 items-center justify-start truncate bg-(--field-color) px-3 text-(--font-color)">
93
97
  <p class="truncate">{fileName || $T("constructor.props.file.notselected")}</p>
94
98
  </div>
95
99
  </div>