@placeholderco/placeholder-ui 1.0.3 → 1.0.6

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 (136) hide show
  1. package/LICENSE +26 -26
  2. package/README.md +179 -179
  3. package/dist/display/Alert.svelte +179 -179
  4. package/dist/display/Avatar.svelte +166 -166
  5. package/dist/display/LinkCollection.svelte +161 -161
  6. package/dist/display/Paper.svelte +118 -118
  7. package/dist/form/Autocomplete.svelte +223 -191
  8. package/dist/form/Autocomplete.svelte.d.ts +3 -1
  9. package/dist/form/AutocompleteMulti.svelte +356 -0
  10. package/dist/form/AutocompleteMulti.svelte.d.ts +28 -0
  11. package/dist/form/Checkbox.svelte +201 -201
  12. package/dist/form/Chips.svelte +128 -128
  13. package/dist/form/ComboBox.svelte +158 -158
  14. package/dist/form/ComboBox.svelte.d.ts +1 -1
  15. package/dist/form/ComboBoxItemBuilder.svelte +460 -460
  16. package/dist/form/ComboBoxMulti.svelte +197 -197
  17. package/dist/form/ComboBoxMulti.svelte.d.ts +1 -1
  18. package/dist/form/CronBuilder.svelte +693 -693
  19. package/dist/form/DatePicker.svelte +672 -672
  20. package/dist/form/DateTimePicker.svelte +712 -712
  21. package/dist/form/FileInput.svelte +235 -235
  22. package/dist/form/FormGroup.svelte +68 -68
  23. package/dist/form/Number.svelte +238 -238
  24. package/dist/form/PasswordInput.svelte +252 -252
  25. package/dist/form/RadioGroup.svelte +210 -210
  26. package/dist/form/Rating.svelte +235 -235
  27. package/dist/form/SegmentedControl.svelte +149 -149
  28. package/dist/form/Select.svelte +590 -590
  29. package/dist/form/Select.svelte.d.ts +1 -1
  30. package/dist/form/SelectMulti.svelte +613 -613
  31. package/dist/form/SelectMulti.svelte.d.ts +1 -1
  32. package/dist/form/Slider.svelte +358 -358
  33. package/dist/form/Switch.svelte +147 -147
  34. package/dist/form/TextArea.svelte +148 -148
  35. package/dist/form/Textbox.svelte +228 -228
  36. package/dist/form/TimePicker.svelte +267 -267
  37. package/dist/icon/Icon.svelte +52 -52
  38. package/dist/icon/alert-octagon.svg +5 -5
  39. package/dist/icon/alert-triangle.svg +5 -5
  40. package/dist/icon/archive.svg +1 -1
  41. package/dist/icon/arrow-down.svg +1 -1
  42. package/dist/icon/arrow-left.svg +1 -1
  43. package/dist/icon/arrow-right.svg +1 -1
  44. package/dist/icon/arrow-up.svg +1 -1
  45. package/dist/icon/at.svg +1 -1
  46. package/dist/icon/bell.svg +1 -1
  47. package/dist/icon/bookmark.svg +1 -1
  48. package/dist/icon/calendar.svg +1 -1
  49. package/dist/icon/camera.svg +1 -1
  50. package/dist/icon/chart-bar.svg +1 -1
  51. package/dist/icon/chart-line.svg +1 -1
  52. package/dist/icon/chart-pie.svg +1 -1
  53. package/dist/icon/checkbox.svg +1 -1
  54. package/dist/icon/checklist.svg +1 -1
  55. package/dist/icon/circle-check.svg +1 -1
  56. package/dist/icon/circle-x.svg +1 -1
  57. package/dist/icon/clock.svg +1 -1
  58. package/dist/icon/credit-card.svg +1 -1
  59. package/dist/icon/dots-vertical.svg +1 -1
  60. package/dist/icon/dots.svg +1 -1
  61. package/dist/icon/external-link.svg +1 -1
  62. package/dist/icon/eye-off.svg +1 -1
  63. package/dist/icon/eye.svg +1 -1
  64. package/dist/icon/filter.svg +1 -1
  65. package/dist/icon/fingerprint.svg +1 -1
  66. package/dist/icon/flag.svg +1 -1
  67. package/dist/icon/heart.svg +1 -1
  68. package/dist/icon/home.svg +1 -1
  69. package/dist/icon/key.svg +1 -1
  70. package/dist/icon/list-check.svg +1 -1
  71. package/dist/icon/login.svg +1 -1
  72. package/dist/icon/logout.svg +1 -1
  73. package/dist/icon/map-pin.svg +1 -1
  74. package/dist/icon/maximize.svg +1 -1
  75. package/dist/icon/microphone.svg +1 -1
  76. package/dist/icon/minimize.svg +1 -1
  77. package/dist/icon/note.svg +1 -1
  78. package/dist/icon/player-pause.svg +1 -1
  79. package/dist/icon/printer.svg +1 -1
  80. package/dist/icon/qrcode.svg +1 -1
  81. package/dist/icon/send.svg +1 -1
  82. package/dist/icon/settings.svg +1 -1
  83. package/dist/icon/share.svg +1 -1
  84. package/dist/icon/shopping-cart.svg +1 -1
  85. package/dist/icon/sort-ascending.svg +1 -1
  86. package/dist/icon/sort-descending.svg +1 -1
  87. package/dist/icon/star.svg +1 -1
  88. package/dist/icon/tag.svg +1 -1
  89. package/dist/icon/trending-down.svg +1 -1
  90. package/dist/icon/trending-up.svg +1 -1
  91. package/dist/icon/upload.svg +1 -1
  92. package/dist/icon/volume-off.svg +1 -1
  93. package/dist/icon/volume.svg +1 -1
  94. package/dist/icon/world.svg +1 -1
  95. package/dist/icon/zoom-in.svg +1 -1
  96. package/dist/icon/zoom-out.svg +1 -1
  97. package/dist/index.d.ts +1 -0
  98. package/dist/index.js +1 -0
  99. package/dist/layout/AppShell.svelte +169 -169
  100. package/dist/layout/CustomNavbar.svelte +61 -61
  101. package/dist/layout/Navbar.svelte +206 -206
  102. package/dist/layout/NavbarItemDisplay.svelte +29 -29
  103. package/dist/layout/Sidenav.svelte +712 -712
  104. package/dist/styles/components.css +199 -199
  105. package/dist/styles/dark.css +146 -146
  106. package/dist/styles/index.css +116 -116
  107. package/dist/styles/reset.css +110 -110
  108. package/dist/styles/semantic.css +86 -86
  109. package/dist/styles/tokens.css +203 -197
  110. package/dist/styles/utilities.css +523 -523
  111. package/dist/ui/Accordion.svelte +289 -289
  112. package/dist/ui/ActionIcon.svelte +76 -76
  113. package/dist/ui/Badge.svelte +329 -279
  114. package/dist/ui/Breadcrumbs.svelte +131 -131
  115. package/dist/ui/Button.svelte +432 -370
  116. package/dist/ui/ButtonVariant.d.ts +1 -1
  117. package/dist/ui/Dialog.svelte +307 -307
  118. package/dist/ui/Drawer.svelte +524 -524
  119. package/dist/ui/Dropdown.svelte +97 -97
  120. package/dist/ui/Dropzone.svelte +122 -122
  121. package/dist/ui/Link.svelte +32 -32
  122. package/dist/ui/Loader.svelte +70 -70
  123. package/dist/ui/LoadingOverlay.svelte +53 -53
  124. package/dist/ui/Pagination.svelte +135 -135
  125. package/dist/ui/Popover.svelte +225 -225
  126. package/dist/ui/Progress.svelte +191 -191
  127. package/dist/ui/RingProgress.svelte +141 -141
  128. package/dist/ui/Skeleton.svelte +85 -85
  129. package/dist/ui/Stepper.svelte +355 -355
  130. package/dist/ui/Table.svelte +345 -345
  131. package/dist/ui/Tabs.svelte +146 -146
  132. package/dist/ui/ThemeSwitcher.svelte +39 -39
  133. package/dist/ui/Timeline.svelte +225 -225
  134. package/dist/ui/Toaster.svelte +6 -6
  135. package/dist/ui/Tooltip.svelte +434 -434
  136. package/package.json +14 -14
@@ -1,235 +1,235 @@
1
- <script lang="ts">
2
- import FormGroup from './FormGroup.svelte';
3
- import Icon from '../icon/Icon.svelte';
4
- import { iconUpload, iconX } from '../icon/index.js';
5
- import Button from '../ui/Button.svelte';
6
-
7
- interface Props {
8
- label?: string;
9
- value?: File | File[] | null;
10
- accept?: string;
11
- multiple?: boolean;
12
- disabled?: boolean;
13
- placeholder?: string;
14
- clearable?: boolean;
15
- required?: boolean;
16
- showError?: boolean;
17
- errorText?: string;
18
- size?: 'xs' | 'sm' | 'md' | 'lg';
19
- variant?: 'default' | 'filled';
20
- leftIcon?: string;
21
- class?: string;
22
- containerClass?: string;
23
- tooltipLocation?: 'top' | 'bottom' | 'left' | 'right';
24
- tooltipText?: string;
25
- onchange?: (files: File | File[] | null) => void;
26
- }
27
-
28
- let {
29
- label = '',
30
- value = $bindable(null),
31
- accept = '',
32
- multiple = false,
33
- disabled = false,
34
- placeholder = 'Choose file',
35
- clearable = true,
36
- required = false,
37
- showError = false,
38
- errorText = '',
39
- size = 'md',
40
- variant = 'default',
41
- leftIcon = undefined,
42
- class: classes = '',
43
- containerClass = '',
44
- tooltipLocation = 'top',
45
- tooltipText = undefined,
46
- onchange
47
- }: Props = $props();
48
-
49
- let inputElement: HTMLInputElement;
50
-
51
- const displayValue = $derived.by(() => {
52
- if (!value) return '';
53
- if (Array.isArray(value)) {
54
- if (value.length === 0) return '';
55
- if (value.length === 1) return value[0].name;
56
- return `${value.length} files selected`;
57
- }
58
- return value.name;
59
- });
60
-
61
- function handleClick() {
62
- if (disabled) return;
63
- inputElement?.click();
64
- }
65
-
66
- function handleChange(e: Event) {
67
- const target = e.target as HTMLInputElement;
68
- const files = target.files;
69
- if (!files || files.length === 0) {
70
- value = null;
71
- } else if (multiple) {
72
- value = Array.from(files);
73
- } else {
74
- value = files[0];
75
- }
76
- onchange?.(value);
77
- }
78
-
79
- function handleClear(e: Event) {
80
- e.stopPropagation();
81
- value = null;
82
- if (inputElement) {
83
- inputElement.value = '';
84
- }
85
- onchange?.(null);
86
- }
87
- </script>
88
-
89
- <div class="file-input-container {containerClass}">
90
- <FormGroup
91
- {label}
92
- {required}
93
- class={classes}
94
- {tooltipLocation}
95
- {tooltipText}
96
- {showError}
97
- {errorText}
98
- >
99
- <div class="file-input-wrapper size-{size} variant-{variant}" class:disabled>
100
- <input
101
- bind:this={inputElement}
102
- type="file"
103
- {accept}
104
- {multiple}
105
- {disabled}
106
- class="file-input-hidden"
107
- onchange={handleChange}
108
- />
109
- <button type="button" class="file-input-button" onclick={handleClick} {disabled}>
110
- <span class="file-input-icon">
111
- <Icon svg={leftIcon || iconUpload} size="1.125em" />
112
- </span>
113
- <span class="file-input-text" class:placeholder={!displayValue}>
114
- {displayValue || placeholder}
115
- </span>
116
- </button>
117
- {#if clearable && value}
118
- <button type="button" class="file-input-clear" onclick={handleClear} aria-label="Clear file">
119
- <Icon svg={iconX} size="14px" />
120
- </button>
121
- {/if}
122
- </div>
123
- </FormGroup>
124
- </div>
125
-
126
- <style>
127
- .file-input-container {
128
- width: 100%;
129
- }
130
-
131
- .file-input-hidden {
132
- display: none;
133
- }
134
-
135
- .file-input-wrapper {
136
- display: flex;
137
- align-items: center;
138
- width: 100%;
139
- border: 1px solid var(--pui-input-border);
140
- background-color: var(--pui-input-bg);
141
- border-radius: var(--pui-radius-base);
142
- transition:
143
- border-color var(--pui-transition-fast) var(--pui-ease-in-out),
144
- box-shadow var(--pui-transition-fast) var(--pui-ease-in-out);
145
- }
146
-
147
- .file-input-wrapper:focus-within {
148
- border-color: var(--pui-input-border-focus);
149
- }
150
-
151
- .file-input-wrapper.disabled {
152
- opacity: 0.5;
153
- cursor: not-allowed;
154
- background: var(--pui-input-bg-disabled);
155
- }
156
-
157
- .variant-filled {
158
- background-color: var(--pui-color-gray-100);
159
- border-color: transparent;
160
- }
161
-
162
- :global(.dark) .variant-filled {
163
- background-color: var(--pui-color-dark-200);
164
- }
165
-
166
- /* Sizes */
167
- .size-xs .file-input-button {
168
- padding: var(--pui-spacing-1) var(--pui-spacing-2);
169
- font-size: var(--pui-font-size-xs);
170
- }
171
- .size-sm .file-input-button {
172
- padding: var(--pui-spacing-1) var(--pui-spacing-2);
173
- font-size: var(--pui-font-size-sm);
174
- }
175
- .size-md .file-input-button {
176
- padding: var(--pui-spacing-2) var(--pui-spacing-3);
177
- font-size: var(--pui-font-size-base);
178
- }
179
- .size-lg .file-input-button {
180
- padding: var(--pui-spacing-3) var(--pui-spacing-4);
181
- font-size: var(--pui-font-size-md);
182
- }
183
-
184
- .file-input-button {
185
- display: flex;
186
- align-items: center;
187
- gap: var(--pui-spacing-2);
188
- flex: 1;
189
- background: none;
190
- border: none;
191
- cursor: pointer;
192
- text-align: left;
193
- color: var(--pui-input-text);
194
- min-width: 0;
195
- }
196
-
197
- .file-input-button:disabled {
198
- cursor: not-allowed;
199
- }
200
-
201
- .file-input-icon {
202
- display: flex;
203
- align-items: center;
204
- color: var(--pui-text-muted);
205
- flex-shrink: 0;
206
- }
207
-
208
- .file-input-text {
209
- flex: 1;
210
- overflow: hidden;
211
- text-overflow: ellipsis;
212
- white-space: nowrap;
213
- }
214
-
215
- .file-input-text.placeholder {
216
- color: var(--pui-text-muted);
217
- }
218
-
219
- .file-input-clear {
220
- display: flex;
221
- align-items: center;
222
- justify-content: center;
223
- background: none;
224
- border: none;
225
- cursor: pointer;
226
- padding: var(--pui-spacing-2);
227
- color: var(--pui-text-muted);
228
- border-radius: var(--pui-radius-sm);
229
- transition: color var(--pui-transition-fast) var(--pui-ease-in-out);
230
- }
231
-
232
- .file-input-clear:hover {
233
- color: var(--pui-color-danger);
234
- }
235
- </style>
1
+ <script lang="ts">
2
+ import FormGroup from './FormGroup.svelte';
3
+ import Icon from '../icon/Icon.svelte';
4
+ import { iconUpload, iconX } from '../icon/index.js';
5
+ import Button from '../ui/Button.svelte';
6
+
7
+ interface Props {
8
+ label?: string;
9
+ value?: File | File[] | null;
10
+ accept?: string;
11
+ multiple?: boolean;
12
+ disabled?: boolean;
13
+ placeholder?: string;
14
+ clearable?: boolean;
15
+ required?: boolean;
16
+ showError?: boolean;
17
+ errorText?: string;
18
+ size?: 'xs' | 'sm' | 'md' | 'lg';
19
+ variant?: 'default' | 'filled';
20
+ leftIcon?: string;
21
+ class?: string;
22
+ containerClass?: string;
23
+ tooltipLocation?: 'top' | 'bottom' | 'left' | 'right';
24
+ tooltipText?: string;
25
+ onchange?: (files: File | File[] | null) => void;
26
+ }
27
+
28
+ let {
29
+ label = '',
30
+ value = $bindable(null),
31
+ accept = '',
32
+ multiple = false,
33
+ disabled = false,
34
+ placeholder = 'Choose file',
35
+ clearable = true,
36
+ required = false,
37
+ showError = false,
38
+ errorText = '',
39
+ size = 'md',
40
+ variant = 'default',
41
+ leftIcon = undefined,
42
+ class: classes = '',
43
+ containerClass = '',
44
+ tooltipLocation = 'top',
45
+ tooltipText = undefined,
46
+ onchange
47
+ }: Props = $props();
48
+
49
+ let inputElement: HTMLInputElement;
50
+
51
+ const displayValue = $derived.by(() => {
52
+ if (!value) return '';
53
+ if (Array.isArray(value)) {
54
+ if (value.length === 0) return '';
55
+ if (value.length === 1) return value[0].name;
56
+ return `${value.length} files selected`;
57
+ }
58
+ return value.name;
59
+ });
60
+
61
+ function handleClick() {
62
+ if (disabled) return;
63
+ inputElement?.click();
64
+ }
65
+
66
+ function handleChange(e: Event) {
67
+ const target = e.target as HTMLInputElement;
68
+ const files = target.files;
69
+ if (!files || files.length === 0) {
70
+ value = null;
71
+ } else if (multiple) {
72
+ value = Array.from(files);
73
+ } else {
74
+ value = files[0];
75
+ }
76
+ onchange?.(value);
77
+ }
78
+
79
+ function handleClear(e: Event) {
80
+ e.stopPropagation();
81
+ value = null;
82
+ if (inputElement) {
83
+ inputElement.value = '';
84
+ }
85
+ onchange?.(null);
86
+ }
87
+ </script>
88
+
89
+ <div class="file-input-container {containerClass}">
90
+ <FormGroup
91
+ {label}
92
+ {required}
93
+ class={classes}
94
+ {tooltipLocation}
95
+ {tooltipText}
96
+ {showError}
97
+ {errorText}
98
+ >
99
+ <div class="file-input-wrapper size-{size} variant-{variant}" class:disabled>
100
+ <input
101
+ bind:this={inputElement}
102
+ type="file"
103
+ {accept}
104
+ {multiple}
105
+ {disabled}
106
+ class="file-input-hidden"
107
+ onchange={handleChange}
108
+ />
109
+ <button type="button" class="file-input-button" onclick={handleClick} {disabled}>
110
+ <span class="file-input-icon">
111
+ <Icon svg={leftIcon || iconUpload} size="1.125em" />
112
+ </span>
113
+ <span class="file-input-text" class:placeholder={!displayValue}>
114
+ {displayValue || placeholder}
115
+ </span>
116
+ </button>
117
+ {#if clearable && value}
118
+ <button type="button" class="file-input-clear" onclick={handleClear} aria-label="Clear file">
119
+ <Icon svg={iconX} size="14px" />
120
+ </button>
121
+ {/if}
122
+ </div>
123
+ </FormGroup>
124
+ </div>
125
+
126
+ <style>
127
+ .file-input-container {
128
+ width: 100%;
129
+ }
130
+
131
+ .file-input-hidden {
132
+ display: none;
133
+ }
134
+
135
+ .file-input-wrapper {
136
+ display: flex;
137
+ align-items: center;
138
+ width: 100%;
139
+ border: 1px solid var(--pui-input-border);
140
+ background-color: var(--pui-input-bg);
141
+ border-radius: var(--pui-radius-base);
142
+ transition:
143
+ border-color var(--pui-transition-fast) var(--pui-ease-in-out),
144
+ box-shadow var(--pui-transition-fast) var(--pui-ease-in-out);
145
+ }
146
+
147
+ .file-input-wrapper:focus-within {
148
+ border-color: var(--pui-input-border-focus);
149
+ }
150
+
151
+ .file-input-wrapper.disabled {
152
+ opacity: 0.5;
153
+ cursor: not-allowed;
154
+ background: var(--pui-input-bg-disabled);
155
+ }
156
+
157
+ .variant-filled {
158
+ background-color: var(--pui-color-gray-100);
159
+ border-color: transparent;
160
+ }
161
+
162
+ :global(.dark) .variant-filled {
163
+ background-color: var(--pui-color-dark-200);
164
+ }
165
+
166
+ /* Sizes */
167
+ .size-xs .file-input-button {
168
+ padding: var(--pui-spacing-1) var(--pui-spacing-2);
169
+ font-size: var(--pui-font-size-xs);
170
+ }
171
+ .size-sm .file-input-button {
172
+ padding: var(--pui-spacing-1) var(--pui-spacing-2);
173
+ font-size: var(--pui-font-size-sm);
174
+ }
175
+ .size-md .file-input-button {
176
+ padding: var(--pui-spacing-2) var(--pui-spacing-3);
177
+ font-size: var(--pui-font-size-base);
178
+ }
179
+ .size-lg .file-input-button {
180
+ padding: var(--pui-spacing-3) var(--pui-spacing-4);
181
+ font-size: var(--pui-font-size-md);
182
+ }
183
+
184
+ .file-input-button {
185
+ display: flex;
186
+ align-items: center;
187
+ gap: var(--pui-spacing-2);
188
+ flex: 1;
189
+ background: none;
190
+ border: none;
191
+ cursor: pointer;
192
+ text-align: left;
193
+ color: var(--pui-input-text);
194
+ min-width: 0;
195
+ }
196
+
197
+ .file-input-button:disabled {
198
+ cursor: not-allowed;
199
+ }
200
+
201
+ .file-input-icon {
202
+ display: flex;
203
+ align-items: center;
204
+ color: var(--pui-text-muted);
205
+ flex-shrink: 0;
206
+ }
207
+
208
+ .file-input-text {
209
+ flex: 1;
210
+ overflow: hidden;
211
+ text-overflow: ellipsis;
212
+ white-space: nowrap;
213
+ }
214
+
215
+ .file-input-text.placeholder {
216
+ color: var(--pui-text-muted);
217
+ }
218
+
219
+ .file-input-clear {
220
+ display: flex;
221
+ align-items: center;
222
+ justify-content: center;
223
+ background: none;
224
+ border: none;
225
+ cursor: pointer;
226
+ padding: var(--pui-spacing-2);
227
+ color: var(--pui-text-muted);
228
+ border-radius: var(--pui-radius-sm);
229
+ transition: color var(--pui-transition-fast) var(--pui-ease-in-out);
230
+ }
231
+
232
+ .file-input-clear:hover {
233
+ color: var(--pui-color-danger);
234
+ }
235
+ </style>
@@ -1,68 +1,68 @@
1
- <script lang="ts">
2
- import { Icon, iconInfoCircle } from '../icon/index.js';
3
- import Tooltip from '../ui/Tooltip.svelte';
4
- import type { Snippet } from 'svelte';
5
-
6
- interface Props {
7
- label: string;
8
- required?: boolean;
9
- id?: string;
10
- class?: string;
11
- showError?: boolean;
12
- errorText?: string;
13
- tooltipContent?: Snippet;
14
- tooltipText?: string;
15
- tooltipLocation?: 'top' | 'bottom' | 'left' | 'right';
16
- children: Snippet;
17
- }
18
-
19
- let {
20
- label,
21
- required = false,
22
- id = undefined,
23
- class: classes = '',
24
- showError = false,
25
- errorText = '',
26
- tooltipContent,
27
- tooltipText,
28
- tooltipLocation = 'top',
29
- children
30
- }: Props = $props();
31
-
32
- const computedId = $derived(id ?? `form-${label}`);
33
- </script>
34
-
35
- <div class="form-group {classes}">
36
- {#if label}
37
- <label class="label" for={computedId}>
38
- {label}
39
- {#if required}
40
- <span class="text-required">*</span>
41
- {/if}
42
- {#if tooltipContent || tooltipText}
43
- <Tooltip {tooltipContent} location={tooltipLocation} text={tooltipText}>
44
- <Icon svg={iconInfoCircle} size="1em" stroke="var(--accent-color)" />
45
- </Tooltip>
46
- {/if}
47
- </label>
48
- {/if}
49
-
50
- {@render children?.()}
51
-
52
- {#if showError && errorText}
53
- <div class="error-text">{errorText}</div>
54
- {/if}
55
- </div>
56
-
57
- <style>
58
- .label {
59
- font-size: var(--pui-font-size-md);
60
- line-height: var(--pui-line-height-normal);
61
- }
62
-
63
- .error-text {
64
- color: var(--pui-text-danger);
65
- font-size: var(--pui-font-size-sm);
66
- margin-top: var(--pui-spacing-1);
67
- }
68
- </style>
1
+ <script lang="ts">
2
+ import { Icon, iconInfoCircle } from '../icon/index.js';
3
+ import Tooltip from '../ui/Tooltip.svelte';
4
+ import type { Snippet } from 'svelte';
5
+
6
+ interface Props {
7
+ label: string;
8
+ required?: boolean;
9
+ id?: string;
10
+ class?: string;
11
+ showError?: boolean;
12
+ errorText?: string;
13
+ tooltipContent?: Snippet;
14
+ tooltipText?: string;
15
+ tooltipLocation?: 'top' | 'bottom' | 'left' | 'right';
16
+ children: Snippet;
17
+ }
18
+
19
+ let {
20
+ label,
21
+ required = false,
22
+ id = undefined,
23
+ class: classes = '',
24
+ showError = false,
25
+ errorText = '',
26
+ tooltipContent,
27
+ tooltipText,
28
+ tooltipLocation = 'top',
29
+ children
30
+ }: Props = $props();
31
+
32
+ const computedId = $derived(id ?? `form-${label}`);
33
+ </script>
34
+
35
+ <div class="form-group {classes}">
36
+ {#if label}
37
+ <label class="label" for={computedId}>
38
+ {label}
39
+ {#if required}
40
+ <span class="text-required">*</span>
41
+ {/if}
42
+ {#if tooltipContent || tooltipText}
43
+ <Tooltip {tooltipContent} location={tooltipLocation} text={tooltipText}>
44
+ <Icon svg={iconInfoCircle} size="1em" stroke="var(--accent-color)" />
45
+ </Tooltip>
46
+ {/if}
47
+ </label>
48
+ {/if}
49
+
50
+ {@render children?.()}
51
+
52
+ {#if showError && errorText}
53
+ <div class="error-text">{errorText}</div>
54
+ {/if}
55
+ </div>
56
+
57
+ <style>
58
+ .label {
59
+ font-size: var(--pui-font-size-md);
60
+ line-height: var(--pui-line-height-normal);
61
+ }
62
+
63
+ .error-text {
64
+ color: var(--pui-text-danger);
65
+ font-size: var(--pui-font-size-sm);
66
+ margin-top: var(--pui-spacing-1);
67
+ }
68
+ </style>