plasma-svelte 0.0.1

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 (95) hide show
  1. package/README.md +65 -0
  2. package/dist/accordion/Accordion.svelte +28 -0
  3. package/dist/accordion/Accordion.svelte.d.ts +8 -0
  4. package/dist/accordion/AccordionItem.svelte +193 -0
  5. package/dist/accordion/AccordionItem.svelte.d.ts +14 -0
  6. package/dist/avatar/Avatar.svelte +178 -0
  7. package/dist/avatar/Avatar.svelte.d.ts +16 -0
  8. package/dist/badge/Badge.svelte +162 -0
  9. package/dist/badge/Badge.svelte.d.ts +16 -0
  10. package/dist/breadcrumb/Breadcrumb.svelte +59 -0
  11. package/dist/breadcrumb/Breadcrumb.svelte.d.ts +9 -0
  12. package/dist/breadcrumb/BreadcrumbItem.svelte +151 -0
  13. package/dist/breadcrumb/BreadcrumbItem.svelte.d.ts +14 -0
  14. package/dist/button/Button.svelte +274 -0
  15. package/dist/button/Button.svelte.d.ts +22 -0
  16. package/dist/button/ButtonGroup.svelte +98 -0
  17. package/dist/button/ButtonGroup.svelte.d.ts +12 -0
  18. package/dist/card/Card.svelte +191 -0
  19. package/dist/card/Card.svelte.d.ts +18 -0
  20. package/dist/checkbox/Checkbox.svelte +199 -0
  21. package/dist/checkbox/Checkbox.svelte.d.ts +12 -0
  22. package/dist/datepicker/DatePicker.svelte +509 -0
  23. package/dist/datepicker/DatePicker.svelte.d.ts +14 -0
  24. package/dist/divider/Divider.svelte +112 -0
  25. package/dist/divider/Divider.svelte.d.ts +13 -0
  26. package/dist/drawer/Drawer.svelte +330 -0
  27. package/dist/drawer/Drawer.svelte.d.ts +21 -0
  28. package/dist/dropzone/DropZone.svelte +282 -0
  29. package/dist/dropzone/DropZone.svelte.d.ts +17 -0
  30. package/dist/emptystate/EmptyState.svelte +169 -0
  31. package/dist/emptystate/EmptyState.svelte.d.ts +15 -0
  32. package/dist/headerbar/HeaderBar.svelte +210 -0
  33. package/dist/headerbar/HeaderBar.svelte.d.ts +17 -0
  34. package/dist/index.d.ts +44 -0
  35. package/dist/index.js +44 -0
  36. package/dist/input/Input.svelte +227 -0
  37. package/dist/input/Input.svelte.d.ts +14 -0
  38. package/dist/layout/Flex.svelte +110 -0
  39. package/dist/layout/Flex.svelte.d.ts +20 -0
  40. package/dist/layout/Grid.svelte +78 -0
  41. package/dist/layout/Grid.svelte.d.ts +19 -0
  42. package/dist/layout/Space.svelte +64 -0
  43. package/dist/layout/Space.svelte.d.ts +16 -0
  44. package/dist/layout/utils.d.ts +2 -0
  45. package/dist/layout/utils.js +19 -0
  46. package/dist/menu/Menu.svelte +156 -0
  47. package/dist/menu/Menu.svelte.d.ts +14 -0
  48. package/dist/menu/MenuDivider.svelte +19 -0
  49. package/dist/menu/MenuDivider.svelte.d.ts +6 -0
  50. package/dist/menu/MenuItem.svelte +126 -0
  51. package/dist/menu/MenuItem.svelte.d.ts +13 -0
  52. package/dist/message/InlineMessage.svelte +226 -0
  53. package/dist/message/InlineMessage.svelte.d.ts +15 -0
  54. package/dist/modal/ConfirmDialog.svelte +151 -0
  55. package/dist/modal/ConfirmDialog.svelte.d.ts +19 -0
  56. package/dist/modal/Modal.svelte +279 -0
  57. package/dist/modal/Modal.svelte.d.ts +18 -0
  58. package/dist/pagination/Pagination.svelte +258 -0
  59. package/dist/pagination/Pagination.svelte.d.ts +16 -0
  60. package/dist/progress/ProgressBar.svelte +179 -0
  61. package/dist/progress/ProgressBar.svelte.d.ts +15 -0
  62. package/dist/radio/Radio.svelte +161 -0
  63. package/dist/radio/Radio.svelte.d.ts +13 -0
  64. package/dist/searchfield/SearchField.svelte +246 -0
  65. package/dist/searchfield/SearchField.svelte.d.ts +15 -0
  66. package/dist/select/Select.svelte +392 -0
  67. package/dist/select/Select.svelte.d.ts +20 -0
  68. package/dist/sidebar/Sidebar.svelte +78 -0
  69. package/dist/sidebar/Sidebar.svelte.d.ts +11 -0
  70. package/dist/sidebar/SidebarGroup.svelte +51 -0
  71. package/dist/sidebar/SidebarGroup.svelte.d.ts +9 -0
  72. package/dist/sidebar/SidebarItem.svelte +185 -0
  73. package/dist/sidebar/SidebarItem.svelte.d.ts +16 -0
  74. package/dist/slider/Slider.svelte +204 -0
  75. package/dist/slider/Slider.svelte.d.ts +16 -0
  76. package/dist/spinbox/SpinBox.svelte +299 -0
  77. package/dist/spinbox/SpinBox.svelte.d.ts +20 -0
  78. package/dist/spinner/Spinner.svelte +130 -0
  79. package/dist/spinner/Spinner.svelte.d.ts +11 -0
  80. package/dist/style/base.css +178 -0
  81. package/dist/style/index.css +2 -0
  82. package/dist/style/token.css +230 -0
  83. package/dist/switch/Switch.svelte +199 -0
  84. package/dist/switch/Switch.svelte.d.ts +13 -0
  85. package/dist/table/Table.svelte +305 -0
  86. package/dist/table/Table.svelte.d.ts +32 -0
  87. package/dist/tabs/Tabs.svelte +216 -0
  88. package/dist/tabs/Tabs.svelte.d.ts +21 -0
  89. package/dist/textarea/TextArea.svelte +114 -0
  90. package/dist/textarea/TextArea.svelte.d.ts +14 -0
  91. package/dist/timepicker/TimePicker.svelte +452 -0
  92. package/dist/timepicker/TimePicker.svelte.d.ts +16 -0
  93. package/dist/tooltip/Tooltip.svelte +122 -0
  94. package/dist/tooltip/Tooltip.svelte.d.ts +12 -0
  95. package/package.json +66 -0
@@ -0,0 +1,452 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from "svelte/elements";
3
+
4
+ type TimePickerSize = "sm" | "md" | "lg" | "small" | "medium" | "large";
5
+ type TimeFormat = "24h" | "12h";
6
+
7
+ interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "size" | "onchange"> {
8
+ value?: string; // HH:mm or HH:mm:ss
9
+ format?: TimeFormat;
10
+ showSeconds?: boolean;
11
+ minuteStep?: number;
12
+ placeholder?: string;
13
+ size?: TimePickerSize;
14
+ disabled?: boolean;
15
+ onchange?: (val: string) => void;
16
+ }
17
+
18
+ let {
19
+ value = $bindable(""),
20
+ format = "24h",
21
+ showSeconds = false,
22
+ minuteStep = 1,
23
+ placeholder = "Select time...",
24
+ size = "md",
25
+ disabled = false,
26
+ onchange,
27
+ class: customClass = "",
28
+ id,
29
+ ...restProps
30
+ }: Props = $props();
31
+
32
+ let isOpen = $state(false);
33
+ let triggerEl = $state<HTMLButtonElement | null>(null);
34
+ let popupEl = $state<HTMLDivElement | null>(null);
35
+ let popupStyle = $state("");
36
+
37
+ // Parse hours, minutes, seconds from value
38
+ const parsed = $derived.by(() => {
39
+ if (!value) return { h: 12, m: 0, s: 0, period: "AM" as "AM" | "PM" };
40
+ const parts = value.split(":");
41
+ let h = parseInt(parts[0], 10) || 0;
42
+ const m = parseInt(parts[1], 10) || 0;
43
+ const s = parseInt(parts[2], 10) || 0;
44
+
45
+ let period: "AM" | "PM" = "AM";
46
+ if (format === "12h") {
47
+ if (h >= 12) {
48
+ period = "PM";
49
+ if (h > 12) h -= 12;
50
+ } else if (h === 0) {
51
+ h = 12;
52
+ }
53
+ }
54
+ return { h, m, s, period };
55
+ });
56
+
57
+ let selectedH = $state(12);
58
+ let selectedM = $state(0);
59
+ let selectedS = $state(0);
60
+ let selectedPeriod = $state<"AM" | "PM">("AM");
61
+
62
+ $effect(() => {
63
+ selectedH = parsed.h;
64
+ selectedM = parsed.m;
65
+ selectedS = parsed.s;
66
+ selectedPeriod = parsed.period;
67
+ });
68
+
69
+ const resolvedSize = $derived(
70
+ size === "small"
71
+ ? "sm"
72
+ : size === "large"
73
+ ? "lg"
74
+ : size === "medium"
75
+ ? "md"
76
+ : size
77
+ );
78
+
79
+ const hoursList = $derived(
80
+ format === "24h"
81
+ ? Array.from({ length: 24 }, (_, i) => i)
82
+ : Array.from({ length: 12 }, (_, i) => i + 1)
83
+ );
84
+
85
+ const minutesList = $derived(
86
+ Array.from({ length: Math.floor(60 / minuteStep) }, (_, i) => i * minuteStep)
87
+ );
88
+
89
+ function emitTime(h: number, m: number, s: number, period: "AM" | "PM") {
90
+ let finalH = h;
91
+ if (format === "12h") {
92
+ if (period === "PM" && h < 12) finalH += 12;
93
+ if (period === "AM" && h === 12) finalH = 0;
94
+ }
95
+
96
+ const hh = String(finalH).padStart(2, "0");
97
+ const mm = String(m).padStart(2, "0");
98
+ const ss = String(s).padStart(2, "0");
99
+
100
+ const timeStr = showSeconds ? `${hh}:${mm}:${ss}` : `${hh}:${mm}`;
101
+ value = timeStr;
102
+ onchange?.(timeStr);
103
+ }
104
+
105
+ function selectHour(h: number) {
106
+ selectedH = h;
107
+ emitTime(selectedH, selectedM, selectedS, selectedPeriod);
108
+ }
109
+
110
+ function selectMinute(m: number) {
111
+ selectedM = m;
112
+ emitTime(selectedH, selectedM, selectedS, selectedPeriod);
113
+ }
114
+
115
+ function setNow() {
116
+ const now = new Date();
117
+ const h = now.getHours();
118
+ const m = now.getMinutes();
119
+ const s = now.getSeconds();
120
+ emitTime(h, m, s, h >= 12 ? "PM" : "AM");
121
+ popupEl?.hidePopover();
122
+ }
123
+
124
+ function updatePosition() {
125
+ if (!triggerEl) return;
126
+ const rect = triggerEl.getBoundingClientRect();
127
+ popupStyle = `top: ${rect.bottom + 4}px; left: ${rect.left}px;`;
128
+ }
129
+
130
+ function toggleOpen() {
131
+ if (disabled) return;
132
+ if (popupEl?.matches(":popover-open")) {
133
+ popupEl.hidePopover();
134
+ } else {
135
+ updatePosition();
136
+ popupEl?.showPopover();
137
+ }
138
+ }
139
+
140
+ function handleToggle(e: Event) {
141
+ const toggleEvent = e as Event & { newState?: string };
142
+ isOpen = toggleEvent.newState === "open";
143
+ if (isOpen) {
144
+ updatePosition();
145
+ }
146
+ }
147
+ </script>
148
+
149
+ <div
150
+ class="plasma-timepicker-container {customClass}"
151
+ class:plasma-timepicker--disabled={disabled}
152
+ {...restProps}
153
+ >
154
+ <!-- Time Input Button -->
155
+ <button
156
+ {id}
157
+ bind:this={triggerEl}
158
+ type="button"
159
+ class="plasma-timepicker-trigger plasma-timepicker--{resolvedSize}"
160
+ class:plasma-timepicker-trigger--open={isOpen}
161
+ {disabled}
162
+ onclick={toggleOpen}
163
+ aria-haspopup="dialog"
164
+ aria-expanded={isOpen}
165
+ >
166
+ <span class="plasma-timepicker-icon">
167
+ <svg aria-hidden="true" viewBox="0 0 16 16" width="14" height="14" fill="currentColor">
168
+ <path d="M8 3.5a.5.5 0 0 0-1 0V9a.5.5 0 0 0 .252.434l3.5 2a.5.5 0 0 0 .496-.868L8 8.71V3.5z" />
169
+ <path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm7-8A7 7 0 1 1 1 8a7 7 0 0 1 14 0z" />
170
+ </svg>
171
+ </span>
172
+
173
+ <span class="plasma-timepicker-text" class:plasma-timepicker-placeholder={!value}>
174
+ {value || placeholder}
175
+ </span>
176
+
177
+ <span class="plasma-timepicker-chevron" class:plasma-timepicker-chevron--open={isOpen}>
178
+ <svg aria-hidden="true" viewBox="0 0 16 16" width="10" height="10" fill="currentColor">
179
+ <path d="M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z" />
180
+ </svg>
181
+ </span>
182
+ </button>
183
+
184
+ <!-- KDE Breeze Time Picker Top-Layer Popover -->
185
+ <div
186
+ bind:this={popupEl}
187
+ popover="auto"
188
+ ontoggle={handleToggle}
189
+ class="plasma-timepicker-popup"
190
+ style={popupStyle}
191
+ role="dialog"
192
+ aria-label="Time Selector"
193
+ >
194
+ <div class="plasma-timepicker-columns">
195
+ <!-- Hours Column -->
196
+ <div class="plasma-time-column">
197
+ <div class="plasma-time-column-header">Hours</div>
198
+ <div class="plasma-time-column-list">
199
+ {#each hoursList as h}
200
+ <button
201
+ type="button"
202
+ class="plasma-time-cell"
203
+ class:plasma-time-cell--selected={selectedH === h}
204
+ onclick={() => selectHour(h)}
205
+ >
206
+ {String(h).padStart(2, "0")}
207
+ </button>
208
+ {/each}
209
+ </div>
210
+ </div>
211
+
212
+ <!-- Minutes Column -->
213
+ <div class="plasma-time-column">
214
+ <div class="plasma-time-column-header">Min</div>
215
+ <div class="plasma-time-column-list">
216
+ {#each minutesList as m}
217
+ <button
218
+ type="button"
219
+ class="plasma-time-cell"
220
+ class:plasma-time-cell--selected={selectedM === m}
221
+ onclick={() => selectMinute(m)}
222
+ >
223
+ {String(m).padStart(2, "0")}
224
+ </button>
225
+ {/each}
226
+ </div>
227
+ </div>
228
+ </div>
229
+
230
+ <!-- Footer -->
231
+ <div class="plasma-timepicker-footer">
232
+ <button
233
+ type="button"
234
+ class="plasma-time-now-btn"
235
+ onclick={setNow}
236
+ >
237
+ Now
238
+ </button>
239
+ <button
240
+ type="button"
241
+ class="plasma-time-ok-btn"
242
+ onclick={() => popupEl?.hidePopover()}
243
+ >
244
+ Done
245
+ </button>
246
+ </div>
247
+ </div>
248
+ </div>
249
+
250
+ <style>
251
+ .plasma-timepicker-container {
252
+ position: relative;
253
+ display: inline-block;
254
+ width: 100%;
255
+ box-sizing: border-box;
256
+ font-family: var(--plasma-font-sans);
257
+ }
258
+
259
+ /* --------------------------------------------------------------------------
260
+ Trigger Button
261
+ -------------------------------------------------------------------------- */
262
+ .plasma-timepicker-trigger {
263
+ display: flex;
264
+ align-items: center;
265
+ justify-content: space-between;
266
+ width: 100%;
267
+ padding: 0 var(--plasma-space-md);
268
+ background-color: var(--plasma-color-view-bg);
269
+ color: var(--plasma-color-text);
270
+ border: 1px solid var(--plasma-color-border);
271
+ border-radius: var(--plasma-radius-md);
272
+ box-shadow: var(--plasma-shadow-xs);
273
+ cursor: pointer;
274
+ box-sizing: border-box;
275
+ user-select: none;
276
+ transition: var(--plasma-transition-fast);
277
+ }
278
+
279
+ .plasma-timepicker-trigger:hover:not(:disabled):not(.plasma-timepicker-trigger--open) {
280
+ border-color: var(--plasma-color-border-hover);
281
+ }
282
+
283
+ .plasma-timepicker-trigger--open {
284
+ border-color: var(--plasma-color-highlight);
285
+ box-shadow: var(--plasma-shadow-focus);
286
+ }
287
+
288
+ .plasma-timepicker-icon {
289
+ display: inline-flex;
290
+ align-items: center;
291
+ color: var(--plasma-color-text-muted);
292
+ margin-right: var(--plasma-space-sm);
293
+ }
294
+
295
+ .plasma-timepicker-text {
296
+ flex: 1;
297
+ text-align: left;
298
+ overflow: hidden;
299
+ text-overflow: ellipsis;
300
+ white-space: nowrap;
301
+ }
302
+
303
+ .plasma-timepicker-placeholder {
304
+ color: var(--plasma-color-text-muted);
305
+ opacity: 0.75;
306
+ }
307
+
308
+ .plasma-timepicker-chevron {
309
+ display: inline-flex;
310
+ align-items: center;
311
+ color: var(--plasma-color-text-muted);
312
+ transition: transform var(--plasma-duration-fast) var(--plasma-ease-default);
313
+ }
314
+
315
+ .plasma-timepicker-chevron--open {
316
+ transform: rotate(180deg);
317
+ }
318
+
319
+ /* --------------------------------------------------------------------------
320
+ Sizing
321
+ -------------------------------------------------------------------------- */
322
+ .plasma-timepicker--sm {
323
+ height: var(--plasma-control-height-sm);
324
+ font-size: var(--plasma-font-size-xs);
325
+ }
326
+
327
+ .plasma-timepicker--md {
328
+ height: var(--plasma-control-height-md);
329
+ font-size: var(--plasma-font-size-base);
330
+ }
331
+
332
+ .plasma-timepicker--lg {
333
+ height: var(--plasma-control-height-lg);
334
+ font-size: var(--plasma-font-size-md);
335
+ }
336
+
337
+ /* --------------------------------------------------------------------------
338
+ Top-Layer Time Popover
339
+ -------------------------------------------------------------------------- */
340
+ .plasma-timepicker-popup {
341
+ position: fixed;
342
+ margin: 0;
343
+ inset: unset;
344
+ z-index: 99999;
345
+ width: 200px;
346
+ padding: var(--plasma-space-md);
347
+ background-color: var(--plasma-color-surface);
348
+ color: var(--plasma-color-text);
349
+ font-family: var(--plasma-font-sans);
350
+ border: 1px solid var(--plasma-color-border);
351
+ border-radius: var(--plasma-radius-lg);
352
+ box-shadow: var(--plasma-shadow-lg);
353
+ box-sizing: border-box;
354
+ animation: plasma-popup-fade var(--plasma-duration-fast) var(--plasma-ease-default);
355
+ }
356
+
357
+ @keyframes plasma-popup-fade {
358
+ from { opacity: 0; transform: translateY(-4px); }
359
+ to { opacity: 1; transform: translateY(0); }
360
+ }
361
+
362
+ .plasma-timepicker-columns {
363
+ display: flex;
364
+ gap: var(--plasma-space-sm);
365
+ }
366
+
367
+ .plasma-time-column {
368
+ flex: 1;
369
+ display: flex;
370
+ flex-direction: column;
371
+ }
372
+
373
+ .plasma-time-column-header {
374
+ text-align: center;
375
+ font-size: 11px;
376
+ font-weight: var(--plasma-font-weight-bold);
377
+ color: var(--plasma-color-text-muted);
378
+ padding-bottom: 4px;
379
+ border-bottom: 1px solid var(--plasma-color-border-subtle);
380
+ margin-bottom: 4px;
381
+ }
382
+
383
+ .plasma-time-column-list {
384
+ max-height: 160px;
385
+ overflow-y: auto;
386
+ display: flex;
387
+ flex-direction: column;
388
+ gap: 2px;
389
+ scrollbar-width: thin;
390
+ scrollbar-color: var(--plasma-color-border) transparent;
391
+ }
392
+
393
+ .plasma-time-cell {
394
+ padding: 4px 0;
395
+ text-align: center;
396
+ border: none;
397
+ border-radius: var(--plasma-radius-sm);
398
+ background: transparent;
399
+ color: var(--plasma-color-text);
400
+ font-size: var(--plasma-font-size-sm);
401
+ cursor: pointer;
402
+ transition: var(--plasma-transition-fast);
403
+ }
404
+
405
+ .plasma-time-cell:hover {
406
+ background-color: var(--plasma-color-surface-hover);
407
+ }
408
+
409
+ .plasma-time-cell--selected {
410
+ background-color: var(--plasma-color-highlight) !important;
411
+ color: #ffffff !important;
412
+ font-weight: var(--plasma-font-weight-bold);
413
+ }
414
+
415
+ .plasma-timepicker-footer {
416
+ display: flex;
417
+ justify-content: space-between;
418
+ margin-top: var(--plasma-space-sm);
419
+ padding-top: var(--plasma-space-xs);
420
+ border-top: 1px solid var(--plasma-color-border-subtle);
421
+ }
422
+
423
+ .plasma-time-now-btn,
424
+ .plasma-time-ok-btn {
425
+ padding: 3px 8px;
426
+ font-size: 12px;
427
+ font-weight: var(--plasma-font-weight-medium);
428
+ border: none;
429
+ border-radius: var(--plasma-radius-sm);
430
+ background: transparent;
431
+ cursor: pointer;
432
+ transition: var(--plasma-transition-fast);
433
+ }
434
+
435
+ .plasma-time-now-btn {
436
+ color: var(--plasma-color-text-muted);
437
+ }
438
+
439
+ .plasma-time-now-btn:hover {
440
+ color: var(--plasma-color-text);
441
+ background-color: var(--plasma-color-surface-hover);
442
+ }
443
+
444
+ .plasma-time-ok-btn {
445
+ color: var(--plasma-color-highlight);
446
+ font-weight: var(--plasma-font-weight-bold);
447
+ }
448
+
449
+ .plasma-time-ok-btn:hover {
450
+ background-color: var(--plasma-color-highlight-soft);
451
+ }
452
+ </style>
@@ -0,0 +1,16 @@
1
+ import type { HTMLAttributes } from "svelte/elements";
2
+ type TimePickerSize = "sm" | "md" | "lg" | "small" | "medium" | "large";
3
+ type TimeFormat = "24h" | "12h";
4
+ interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "size" | "onchange"> {
5
+ value?: string;
6
+ format?: TimeFormat;
7
+ showSeconds?: boolean;
8
+ minuteStep?: number;
9
+ placeholder?: string;
10
+ size?: TimePickerSize;
11
+ disabled?: boolean;
12
+ onchange?: (val: string) => void;
13
+ }
14
+ declare const TimePicker: import("svelte").Component<Props, {}, "value">;
15
+ type TimePicker = ReturnType<typeof TimePicker>;
16
+ export default TimePicker;
@@ -0,0 +1,122 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+ import type { HTMLAttributes } from "svelte/elements";
4
+
5
+ type TooltipPosition = "top" | "bottom" | "left" | "right";
6
+
7
+ interface Props extends HTMLAttributes<HTMLDivElement> {
8
+ text?: string;
9
+ position?: TooltipPosition;
10
+ content?: Snippet;
11
+ children?: Snippet;
12
+ }
13
+
14
+ let {
15
+ text = "",
16
+ position = "top",
17
+ content,
18
+ children,
19
+ class: customClass = "",
20
+ ...restProps
21
+ }: Props = $props();
22
+ </script>
23
+
24
+ <div class="plasma-tooltip-trigger {customClass}" {...restProps}>
25
+ {@render children?.()}
26
+
27
+ {#if text || content}
28
+ <div
29
+ role="tooltip"
30
+ class="plasma-tooltip-bubble plasma-tooltip--{position}"
31
+ >
32
+ {#if content}
33
+ {@render content()}
34
+ {:else}
35
+ {text}
36
+ {/if}
37
+ </div>
38
+ {/if}
39
+ </div>
40
+
41
+ <style>
42
+ .plasma-tooltip-trigger {
43
+ position: relative;
44
+ display: inline-flex;
45
+ }
46
+
47
+ /* --------------------------------------------------------------------------
48
+ Tooltip Bubble
49
+ -------------------------------------------------------------------------- */
50
+ .plasma-tooltip-bubble {
51
+ position: absolute;
52
+ z-index: 1000;
53
+ pointer-events: none;
54
+ white-space: nowrap;
55
+ padding: var(--plasma-space-xs) var(--plasma-space-sm);
56
+ font-family: var(--plasma-font-sans);
57
+ font-size: var(--plasma-font-size-xs);
58
+ line-height: 1.2;
59
+ color: var(--plasma-color-tooltip-text);
60
+ background-color: var(--plasma-color-tooltip-bg);
61
+ border: 1px solid var(--plasma-color-border-subtle);
62
+ border-radius: var(--plasma-radius-sm);
63
+ box-shadow: var(--plasma-shadow-md);
64
+ opacity: 0;
65
+ visibility: hidden;
66
+ transform: scale(0.95);
67
+ transition:
68
+ opacity var(--plasma-duration-fast) var(--plasma-ease-default),
69
+ transform var(--plasma-duration-fast) var(--plasma-ease-default),
70
+ visibility var(--plasma-duration-fast);
71
+ }
72
+
73
+ .plasma-tooltip-trigger:hover .plasma-tooltip-bubble,
74
+ .plasma-tooltip-trigger:focus-within .plasma-tooltip-bubble {
75
+ opacity: 1;
76
+ visibility: visible;
77
+ transform: scale(1);
78
+ }
79
+
80
+ /* --------------------------------------------------------------------------
81
+ Positions
82
+ -------------------------------------------------------------------------- */
83
+ .plasma-tooltip--top {
84
+ bottom: calc(100% + 6px);
85
+ left: 50%;
86
+ transform: translateX(-50%) scale(0.95);
87
+ }
88
+ .plasma-tooltip-trigger:hover .plasma-tooltip--top,
89
+ .plasma-tooltip-trigger:focus-within .plasma-tooltip--top {
90
+ transform: translateX(-50%) scale(1);
91
+ }
92
+
93
+ .plasma-tooltip--bottom {
94
+ top: calc(100% + 6px);
95
+ left: 50%;
96
+ transform: translateX(-50%) scale(0.95);
97
+ }
98
+ .plasma-tooltip-trigger:hover .plasma-tooltip--bottom,
99
+ .plasma-tooltip-trigger:focus-within .plasma-tooltip--bottom {
100
+ transform: translateX(-50%) scale(1);
101
+ }
102
+
103
+ .plasma-tooltip--left {
104
+ right: calc(100% + 6px);
105
+ top: 50%;
106
+ transform: translateY(-50%) scale(0.95);
107
+ }
108
+ .plasma-tooltip-trigger:hover .plasma-tooltip--left,
109
+ .plasma-tooltip-trigger:focus-within .plasma-tooltip--left {
110
+ transform: translateY(-50%) scale(1);
111
+ }
112
+
113
+ .plasma-tooltip--right {
114
+ left: calc(100% + 6px);
115
+ top: 50%;
116
+ transform: translateY(-50%) scale(0.95);
117
+ }
118
+ .plasma-tooltip-trigger:hover .plasma-tooltip--right,
119
+ .plasma-tooltip-trigger:focus-within .plasma-tooltip--right {
120
+ transform: translateY(-50%) scale(1);
121
+ }
122
+ </style>
@@ -0,0 +1,12 @@
1
+ import type { Snippet } from "svelte";
2
+ import type { HTMLAttributes } from "svelte/elements";
3
+ type TooltipPosition = "top" | "bottom" | "left" | "right";
4
+ interface Props extends HTMLAttributes<HTMLDivElement> {
5
+ text?: string;
6
+ position?: TooltipPosition;
7
+ content?: Snippet;
8
+ children?: Snippet;
9
+ }
10
+ declare const Tooltip: import("svelte").Component<Props, {}, "">;
11
+ type Tooltip = ReturnType<typeof Tooltip>;
12
+ export default Tooltip;
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "plasma-svelte",
3
+ "version": "0.0.1",
4
+ "files": [
5
+ "dist",
6
+ "!dist/**/*.test.*",
7
+ "!dist/**/*.spec.*"
8
+ ],
9
+ "sideEffects": [
10
+ "**/*.css"
11
+ ],
12
+ "svelte": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "type": "module",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "svelte": "./dist/index.js",
19
+ "default": "./dist/index.js"
20
+ },
21
+ "./style.css": "./dist/style/index.css",
22
+ "./style/*": "./dist/style/*",
23
+ "./style": "./dist/style/index.css"
24
+ },
25
+ "peerDependencies": {
26
+ "svelte": "^5.0.0"
27
+ },
28
+ "devDependencies": {
29
+ "@biomejs/biome": "2.5.8",
30
+ "@chromatic-com/storybook": "latest",
31
+ "@storybook/addon-a11y": "^10.5.8",
32
+ "@storybook/addon-docs": "^10.5.8",
33
+ "@storybook/addon-svelte-csf": "latest",
34
+ "@storybook/addon-vitest": "^10.5.8",
35
+ "@storybook/sveltekit": "^10.5.8",
36
+ "@sveltejs/adapter-auto": "^7.0.1",
37
+ "@sveltejs/kit": "^2.63.0",
38
+ "@sveltejs/package": "^2.5.8",
39
+ "@sveltejs/vite-plugin-svelte": "^7.1.2",
40
+ "@types/node": "^22",
41
+ "@vitest/browser-playwright": "latest",
42
+ "@vitest/coverage-v8": "latest",
43
+ "playwright": "latest",
44
+ "publint": "^0.3.21",
45
+ "storybook": "^10.5.8",
46
+ "svelte": "^5.56.1",
47
+ "svelte-check": "^4.6.0",
48
+ "typescript": "^6.0.3",
49
+ "vite": "^8.0.16",
50
+ "vitest": "latest"
51
+ },
52
+ "keywords": [
53
+ "svelte"
54
+ ],
55
+ "scripts": {
56
+ "dev": "vite dev",
57
+ "build": "vite build && npm run prepack",
58
+ "preview": "vite preview",
59
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
60
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
61
+ "format": "biome format --write .",
62
+ "lint": "biome lint --write .",
63
+ "storybook": "storybook dev -p 6006",
64
+ "build-storybook": "storybook build"
65
+ }
66
+ }