@karbonjs/ui-svelte 0.1.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.
- package/LICENSE +21 -0
- package/package.json +30 -0
- package/src/accordion/Accordion.svelte +63 -0
- package/src/alert/AlertMessage.svelte +44 -0
- package/src/avatar/Avatar.svelte +48 -0
- package/src/badge/Badge.svelte +24 -0
- package/src/breadcrumb/Breadcrumb.svelte +34 -0
- package/src/button/Button.svelte +89 -0
- package/src/carousel/Carousel.svelte +118 -0
- package/src/data/DataTable.svelte +18 -0
- package/src/data/Pagination.svelte +45 -0
- package/src/divider/Divider.svelte +27 -0
- package/src/dropdown/Dropdown.svelte +61 -0
- package/src/form/Checkbox.svelte +51 -0
- package/src/form/ColorPicker.svelte +95 -0
- package/src/form/DatePicker.svelte +196 -0
- package/src/form/FormInput.svelte +174 -0
- package/src/form/Radio.svelte +54 -0
- package/src/form/Select.svelte +73 -0
- package/src/form/Slider.svelte +74 -0
- package/src/form/Textarea.svelte +86 -0
- package/src/form/Toggle.svelte +55 -0
- package/src/image/Image.svelte +89 -0
- package/src/image/ImgZoom.svelte +96 -0
- package/src/index.ts +71 -0
- package/src/kbd/Kbd.svelte +19 -0
- package/src/layout/Card.svelte +67 -0
- package/src/layout/EmptyState.svelte +25 -0
- package/src/layout/PageHeader.svelte +27 -0
- package/src/overlay/Dialog.svelte +135 -0
- package/src/overlay/ImgBox.svelte +174 -0
- package/src/overlay/Modal.svelte +98 -0
- package/src/overlay/Toast.svelte +92 -0
- package/src/progress/Progress.svelte +50 -0
- package/src/skeleton/Skeleton.svelte +50 -0
- package/src/tabs/Tabs.svelte +59 -0
- package/src/tooltip/Tooltip.svelte +49 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
name: string
|
|
4
|
+
value?: number
|
|
5
|
+
min?: number
|
|
6
|
+
max?: number
|
|
7
|
+
step?: number
|
|
8
|
+
label?: string
|
|
9
|
+
showValue?: boolean
|
|
10
|
+
disabled?: boolean
|
|
11
|
+
class?: string
|
|
12
|
+
oninput?: (e: Event) => void
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
name,
|
|
17
|
+
value = $bindable(0),
|
|
18
|
+
min = 0,
|
|
19
|
+
max = 100,
|
|
20
|
+
step = 1,
|
|
21
|
+
label = '',
|
|
22
|
+
showValue = true,
|
|
23
|
+
disabled = false,
|
|
24
|
+
class: className = '',
|
|
25
|
+
oninput
|
|
26
|
+
}: Props = $props()
|
|
27
|
+
|
|
28
|
+
const percent = $derived(((value - min) / (max - min)) * 100)
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<div class="space-y-2 {className}">
|
|
32
|
+
{#if label || showValue}
|
|
33
|
+
<div class="flex items-center justify-between">
|
|
34
|
+
{#if label}
|
|
35
|
+
<label for={name} class="text-sm font-medium text-[var(--karbon-text,#1a1635)]">{label}</label>
|
|
36
|
+
{/if}
|
|
37
|
+
{#if showValue}
|
|
38
|
+
<span class="text-sm font-semibold text-[var(--karbon-primary)] tabular-nums">{value}</span>
|
|
39
|
+
{/if}
|
|
40
|
+
</div>
|
|
41
|
+
{/if}
|
|
42
|
+
|
|
43
|
+
<input
|
|
44
|
+
id={name}
|
|
45
|
+
{name}
|
|
46
|
+
type="range"
|
|
47
|
+
bind:value
|
|
48
|
+
{min}
|
|
49
|
+
{max}
|
|
50
|
+
{step}
|
|
51
|
+
{disabled}
|
|
52
|
+
{oninput}
|
|
53
|
+
class="w-full h-2 rounded-full appearance-none cursor-pointer disabled:opacity-40 disabled:cursor-not-allowed
|
|
54
|
+
bg-[linear-gradient(to_right,var(--karbon-primary)_{percent}%,var(--karbon-border,rgba(0,0,0,0.07))_{percent}%)]
|
|
55
|
+
[&::-webkit-slider-thumb]:appearance-none
|
|
56
|
+
[&::-webkit-slider-thumb]:w-4.5
|
|
57
|
+
[&::-webkit-slider-thumb]:h-4.5
|
|
58
|
+
[&::-webkit-slider-thumb]:rounded-full
|
|
59
|
+
[&::-webkit-slider-thumb]:bg-[var(--karbon-primary)]
|
|
60
|
+
[&::-webkit-slider-thumb]:border-2
|
|
61
|
+
[&::-webkit-slider-thumb]:border-white
|
|
62
|
+
[&::-webkit-slider-thumb]:shadow-md
|
|
63
|
+
[&::-webkit-slider-thumb]:transition-transform
|
|
64
|
+
[&::-webkit-slider-thumb]:duration-150
|
|
65
|
+
[&::-webkit-slider-thumb]:hover:scale-110
|
|
66
|
+
[&::-moz-range-thumb]:w-4
|
|
67
|
+
[&::-moz-range-thumb]:h-4
|
|
68
|
+
[&::-moz-range-thumb]:rounded-full
|
|
69
|
+
[&::-moz-range-thumb]:bg-[var(--karbon-primary)]
|
|
70
|
+
[&::-moz-range-thumb]:border-2
|
|
71
|
+
[&::-moz-range-thumb]:border-white
|
|
72
|
+
[&::-moz-range-thumb]:shadow-md"
|
|
73
|
+
/>
|
|
74
|
+
</div>
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { FormVariant } from '@karbonjs/ui-core'
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
name: string
|
|
6
|
+
value?: string
|
|
7
|
+
placeholder?: string
|
|
8
|
+
label?: string
|
|
9
|
+
error?: string
|
|
10
|
+
rows?: number
|
|
11
|
+
maxlength?: number
|
|
12
|
+
showCount?: boolean
|
|
13
|
+
required?: boolean
|
|
14
|
+
disabled?: boolean
|
|
15
|
+
readonly?: boolean
|
|
16
|
+
variant?: FormVariant
|
|
17
|
+
class?: string
|
|
18
|
+
oninput?: (e: Event) => void
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let {
|
|
22
|
+
name,
|
|
23
|
+
value = $bindable(''),
|
|
24
|
+
placeholder = '',
|
|
25
|
+
label = '',
|
|
26
|
+
error = '',
|
|
27
|
+
rows = 4,
|
|
28
|
+
maxlength,
|
|
29
|
+
showCount = false,
|
|
30
|
+
required = false,
|
|
31
|
+
disabled = false,
|
|
32
|
+
readonly = false,
|
|
33
|
+
variant = 'dark',
|
|
34
|
+
class: className = '',
|
|
35
|
+
oninput
|
|
36
|
+
}: Props = $props()
|
|
37
|
+
|
|
38
|
+
const themes = {
|
|
39
|
+
dark: {
|
|
40
|
+
label: 'text-[11px] font-medium text-gray-500 uppercase tracking-wider',
|
|
41
|
+
input: 'border-white/8 bg-white/3 text-white placeholder-gray-700 focus:border-[var(--karbon-primary)]/50 focus:bg-white/5 focus:ring-[3px] focus:ring-[var(--karbon-primary)]/8',
|
|
42
|
+
error: 'text-red-400',
|
|
43
|
+
count: 'text-gray-600'
|
|
44
|
+
},
|
|
45
|
+
light: {
|
|
46
|
+
label: 'text-sm font-medium text-gray-700',
|
|
47
|
+
input: 'border-gray-300 bg-white text-gray-900 placeholder-gray-400 focus:border-[var(--karbon-primary)] focus:ring-2 focus:ring-[var(--karbon-primary)]/20',
|
|
48
|
+
error: 'text-[var(--karbon-danger)]',
|
|
49
|
+
count: 'text-gray-400'
|
|
50
|
+
}
|
|
51
|
+
} as const
|
|
52
|
+
|
|
53
|
+
const theme = $derived(themes[variant])
|
|
54
|
+
const charCount = $derived(value.length)
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<div class="space-y-1.5 {className}">
|
|
58
|
+
{#if label}
|
|
59
|
+
<label for={name} class="{theme.label} block mb-1.5">{label}</label>
|
|
60
|
+
{/if}
|
|
61
|
+
|
|
62
|
+
<textarea
|
|
63
|
+
id={name}
|
|
64
|
+
{name}
|
|
65
|
+
bind:value
|
|
66
|
+
{placeholder}
|
|
67
|
+
{rows}
|
|
68
|
+
{maxlength}
|
|
69
|
+
{required}
|
|
70
|
+
{disabled}
|
|
71
|
+
{readonly}
|
|
72
|
+
{oninput}
|
|
73
|
+
class="w-full rounded-lg border px-3 py-2.5 text-[13px] md:text-sm focus:outline-none transition-all resize-y {theme.input} {error ? 'border-red-500/50' : ''}"
|
|
74
|
+
></textarea>
|
|
75
|
+
|
|
76
|
+
<div class="flex items-center justify-between">
|
|
77
|
+
{#if error}
|
|
78
|
+
<p class="text-xs {theme.error}">{error}</p>
|
|
79
|
+
{:else}
|
|
80
|
+
<span></span>
|
|
81
|
+
{/if}
|
|
82
|
+
{#if showCount && maxlength}
|
|
83
|
+
<span class="text-xs {theme.count}">{charCount}/{maxlength}</span>
|
|
84
|
+
{/if}
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ToggleSize } from '@karbonjs/ui-core'
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
name: string
|
|
6
|
+
checked?: boolean
|
|
7
|
+
label?: string
|
|
8
|
+
size?: ToggleSize
|
|
9
|
+
disabled?: boolean
|
|
10
|
+
class?: string
|
|
11
|
+
onchange?: (e: Event) => void
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let {
|
|
15
|
+
name,
|
|
16
|
+
checked = $bindable(false),
|
|
17
|
+
label = '',
|
|
18
|
+
size = 'md',
|
|
19
|
+
disabled = false,
|
|
20
|
+
class: className = '',
|
|
21
|
+
onchange
|
|
22
|
+
}: Props = $props()
|
|
23
|
+
|
|
24
|
+
const sizes = {
|
|
25
|
+
sm: { track: 'w-8 h-[18px]', dot: 'h-3.5 w-3.5', translate: 'translate-x-3.5' },
|
|
26
|
+
md: { track: 'w-10 h-[22px]', dot: 'h-4.5 w-4.5', translate: 'translate-x-4.5' }
|
|
27
|
+
} as const
|
|
28
|
+
|
|
29
|
+
const s = $derived(sizes[size])
|
|
30
|
+
|
|
31
|
+
function toggle() {
|
|
32
|
+
if (!disabled) checked = !checked
|
|
33
|
+
}
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<label class="inline-flex items-center gap-2.5 {disabled ? 'opacity-40 cursor-not-allowed' : 'cursor-pointer'} {className}">
|
|
37
|
+
<input type="checkbox" {name} bind:checked {disabled} {onchange} class="sr-only" />
|
|
38
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
39
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
40
|
+
<div
|
|
41
|
+
onclick={toggle}
|
|
42
|
+
class="relative inline-flex shrink-0 items-center rounded-full transition-colors duration-200
|
|
43
|
+
{s.track}
|
|
44
|
+
{checked ? 'bg-[var(--karbon-primary)]' : 'bg-[var(--karbon-border,rgba(0,0,0,0.07))]'}"
|
|
45
|
+
role="switch"
|
|
46
|
+
aria-checked={checked}
|
|
47
|
+
>
|
|
48
|
+
<span
|
|
49
|
+
class="inline-block rounded-full bg-white shadow-sm transition-transform duration-200 {s.dot} {checked ? s.translate : 'translate-x-0.5'}"
|
|
50
|
+
></span>
|
|
51
|
+
</div>
|
|
52
|
+
{#if label}
|
|
53
|
+
<span class="text-sm font-medium text-[var(--karbon-text,#1a1635)] select-none">{label}</span>
|
|
54
|
+
{/if}
|
|
55
|
+
</label>
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ImageHover, ImageRounded, ImageAspect } from '@karbonjs/ui-core'
|
|
3
|
+
import ImgBox from '../overlay/ImgBox.svelte'
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
src: string
|
|
7
|
+
alt?: string
|
|
8
|
+
hover?: ImageHover
|
|
9
|
+
rounded?: ImageRounded
|
|
10
|
+
aspect?: ImageAspect
|
|
11
|
+
fallback?: string
|
|
12
|
+
imgbox?: boolean
|
|
13
|
+
class?: string
|
|
14
|
+
onclick?: () => void
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let {
|
|
18
|
+
src,
|
|
19
|
+
alt = '',
|
|
20
|
+
hover = 'none',
|
|
21
|
+
rounded = 'md',
|
|
22
|
+
aspect = 'auto',
|
|
23
|
+
fallback = '',
|
|
24
|
+
imgbox = false,
|
|
25
|
+
class: className = '',
|
|
26
|
+
onclick
|
|
27
|
+
}: Props = $props()
|
|
28
|
+
|
|
29
|
+
let errored = $state(false)
|
|
30
|
+
let imgboxOpen = $state(false)
|
|
31
|
+
|
|
32
|
+
const hoverClasses: Record<string, string> = {
|
|
33
|
+
none: '',
|
|
34
|
+
zoom: 'group-hover:scale-110',
|
|
35
|
+
brightness: 'group-hover:brightness-110',
|
|
36
|
+
blur: 'group-hover:blur-sm'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const roundedClasses: Record<string, string> = {
|
|
40
|
+
none: 'rounded-none',
|
|
41
|
+
sm: 'rounded',
|
|
42
|
+
md: 'rounded-lg',
|
|
43
|
+
lg: 'rounded-xl',
|
|
44
|
+
full: 'rounded-full'
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const aspectClasses: Record<string, string> = {
|
|
48
|
+
auto: '',
|
|
49
|
+
square: 'aspect-square',
|
|
50
|
+
video: 'aspect-video',
|
|
51
|
+
portrait: 'aspect-[3/4]'
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const imgSrc = $derived(errored && fallback ? fallback : src)
|
|
55
|
+
const isClickable = $derived(imgbox || !!onclick)
|
|
56
|
+
|
|
57
|
+
function handleClick() {
|
|
58
|
+
if (imgbox) imgboxOpen = true
|
|
59
|
+
onclick?.()
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function handleError() {
|
|
63
|
+
errored = true
|
|
64
|
+
}
|
|
65
|
+
</script>
|
|
66
|
+
|
|
67
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
68
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
69
|
+
<div
|
|
70
|
+
class="group overflow-hidden {roundedClasses[rounded]} {aspectClasses[aspect]} {isClickable ? 'cursor-pointer' : ''} {className}"
|
|
71
|
+
onclick={isClickable ? handleClick : undefined}
|
|
72
|
+
>
|
|
73
|
+
<img
|
|
74
|
+
src={imgSrc}
|
|
75
|
+
{alt}
|
|
76
|
+
onerror={handleError}
|
|
77
|
+
class="w-full h-full object-cover transition-all duration-300 {hoverClasses[hover]}"
|
|
78
|
+
loading="lazy"
|
|
79
|
+
/>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
{#if imgbox && imgboxOpen}
|
|
83
|
+
<ImgBox
|
|
84
|
+
images={[src]}
|
|
85
|
+
open={imgboxOpen}
|
|
86
|
+
backdrop="dark"
|
|
87
|
+
onclose={() => imgboxOpen = false}
|
|
88
|
+
/>
|
|
89
|
+
{/if}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ImageRounded, ImgZoomTrigger } from '@karbonjs/ui-core'
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
src: string
|
|
6
|
+
zoomSrc?: string
|
|
7
|
+
alt?: string
|
|
8
|
+
zoom?: number
|
|
9
|
+
trigger?: ImgZoomTrigger
|
|
10
|
+
rounded?: ImageRounded
|
|
11
|
+
class?: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let {
|
|
15
|
+
src,
|
|
16
|
+
zoomSrc,
|
|
17
|
+
alt = '',
|
|
18
|
+
zoom = 2,
|
|
19
|
+
trigger = 'hover',
|
|
20
|
+
rounded = 'md',
|
|
21
|
+
class: className = ''
|
|
22
|
+
}: Props = $props()
|
|
23
|
+
|
|
24
|
+
let container: HTMLDivElement
|
|
25
|
+
let zooming = $state(false)
|
|
26
|
+
let posX = $state(50)
|
|
27
|
+
let posY = $state(50)
|
|
28
|
+
|
|
29
|
+
const roundedClasses: Record<string, string> = {
|
|
30
|
+
none: 'rounded-none',
|
|
31
|
+
sm: 'rounded',
|
|
32
|
+
md: 'rounded-lg',
|
|
33
|
+
lg: 'rounded-xl',
|
|
34
|
+
full: 'rounded-full'
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const zoomImage = $derived(zoomSrc || src)
|
|
38
|
+
|
|
39
|
+
function updatePosition(e: MouseEvent) {
|
|
40
|
+
const rect = container.getBoundingClientRect()
|
|
41
|
+
posX = ((e.clientX - rect.left) / rect.width) * 100
|
|
42
|
+
posY = ((e.clientY - rect.top) / rect.height) * 100
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function handleMouseEnter(e: MouseEvent) {
|
|
46
|
+
if (trigger === 'hover') {
|
|
47
|
+
zooming = true
|
|
48
|
+
updatePosition(e)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function handleMouseMove(e: MouseEvent) {
|
|
53
|
+
if (zooming) updatePosition(e)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function handleMouseLeave() {
|
|
57
|
+
if (trigger === 'hover') zooming = false
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function handleClick(e: MouseEvent) {
|
|
61
|
+
if (trigger === 'click') {
|
|
62
|
+
zooming = !zooming
|
|
63
|
+
if (zooming) updatePosition(e)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
</script>
|
|
67
|
+
|
|
68
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
69
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
70
|
+
<div
|
|
71
|
+
bind:this={container}
|
|
72
|
+
class="relative overflow-hidden {roundedClasses[rounded]} {trigger === 'click' ? 'cursor-zoom-in' : ''} {zooming && trigger === 'click' ? 'cursor-zoom-out' : ''} {className}"
|
|
73
|
+
onmouseenter={handleMouseEnter}
|
|
74
|
+
onmousemove={handleMouseMove}
|
|
75
|
+
onmouseleave={handleMouseLeave}
|
|
76
|
+
onclick={handleClick}
|
|
77
|
+
>
|
|
78
|
+
<img
|
|
79
|
+
{src}
|
|
80
|
+
{alt}
|
|
81
|
+
class="w-full h-full object-cover block"
|
|
82
|
+
loading="lazy"
|
|
83
|
+
/>
|
|
84
|
+
|
|
85
|
+
{#if zooming}
|
|
86
|
+
<div
|
|
87
|
+
class="absolute inset-0 pointer-events-none"
|
|
88
|
+
style="
|
|
89
|
+
background-image: url({zoomImage});
|
|
90
|
+
background-size: {zoom * 100}%;
|
|
91
|
+
background-position: {posX}% {posY}%;
|
|
92
|
+
background-repeat: no-repeat;
|
|
93
|
+
"
|
|
94
|
+
></div>
|
|
95
|
+
{/if}
|
|
96
|
+
</div>
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// button
|
|
2
|
+
export { default as Button } from './button/Button.svelte'
|
|
3
|
+
|
|
4
|
+
// form
|
|
5
|
+
export { default as FormInput } from './form/FormInput.svelte'
|
|
6
|
+
export { default as Select } from './form/Select.svelte'
|
|
7
|
+
export { default as Checkbox } from './form/Checkbox.svelte'
|
|
8
|
+
export { default as Toggle } from './form/Toggle.svelte'
|
|
9
|
+
export { default as Textarea } from './form/Textarea.svelte'
|
|
10
|
+
export { default as Radio } from './form/Radio.svelte'
|
|
11
|
+
export { default as Slider } from './form/Slider.svelte'
|
|
12
|
+
export { default as DatePicker } from './form/DatePicker.svelte'
|
|
13
|
+
export { default as ColorPicker } from './form/ColorPicker.svelte'
|
|
14
|
+
|
|
15
|
+
// badge
|
|
16
|
+
export { default as Badge } from './badge/Badge.svelte'
|
|
17
|
+
|
|
18
|
+
// alert
|
|
19
|
+
export { default as AlertMessage } from './alert/AlertMessage.svelte'
|
|
20
|
+
|
|
21
|
+
// overlay
|
|
22
|
+
export { default as Modal } from './overlay/Modal.svelte'
|
|
23
|
+
export { default as Dialog } from './overlay/Dialog.svelte'
|
|
24
|
+
export { default as Toast } from './overlay/Toast.svelte'
|
|
25
|
+
export { default as ImgBox } from './overlay/ImgBox.svelte'
|
|
26
|
+
|
|
27
|
+
// layout
|
|
28
|
+
export { default as Card } from './layout/Card.svelte'
|
|
29
|
+
export { default as PageHeader } from './layout/PageHeader.svelte'
|
|
30
|
+
export { default as EmptyState } from './layout/EmptyState.svelte'
|
|
31
|
+
|
|
32
|
+
// image
|
|
33
|
+
export { default as Image } from './image/Image.svelte'
|
|
34
|
+
export { default as ImgZoom } from './image/ImgZoom.svelte'
|
|
35
|
+
|
|
36
|
+
// carousel
|
|
37
|
+
export { default as Carousel } from './carousel/Carousel.svelte'
|
|
38
|
+
|
|
39
|
+
// dropdown
|
|
40
|
+
export { default as Dropdown } from './dropdown/Dropdown.svelte'
|
|
41
|
+
|
|
42
|
+
// accordion
|
|
43
|
+
export { default as Accordion } from './accordion/Accordion.svelte'
|
|
44
|
+
|
|
45
|
+
// tabs
|
|
46
|
+
export { default as Tabs } from './tabs/Tabs.svelte'
|
|
47
|
+
|
|
48
|
+
// breadcrumb
|
|
49
|
+
export { default as Breadcrumb } from './breadcrumb/Breadcrumb.svelte'
|
|
50
|
+
|
|
51
|
+
// tooltip
|
|
52
|
+
export { default as Tooltip } from './tooltip/Tooltip.svelte'
|
|
53
|
+
|
|
54
|
+
// avatar
|
|
55
|
+
export { default as Avatar } from './avatar/Avatar.svelte'
|
|
56
|
+
|
|
57
|
+
// progress
|
|
58
|
+
export { default as Progress } from './progress/Progress.svelte'
|
|
59
|
+
|
|
60
|
+
// skeleton
|
|
61
|
+
export { default as Skeleton } from './skeleton/Skeleton.svelte'
|
|
62
|
+
|
|
63
|
+
// divider
|
|
64
|
+
export { default as Divider } from './divider/Divider.svelte'
|
|
65
|
+
|
|
66
|
+
// kbd
|
|
67
|
+
export { default as Kbd } from './kbd/Kbd.svelte'
|
|
68
|
+
|
|
69
|
+
// data
|
|
70
|
+
export { default as DataTable } from './data/DataTable.svelte'
|
|
71
|
+
export { default as Pagination } from './data/Pagination.svelte'
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
keys: string[]
|
|
4
|
+
class?: string
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
let { keys, class: className = '' }: Props = $props()
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<span class="inline-flex items-center gap-1 {className}">
|
|
11
|
+
{#each keys as key, i}
|
|
12
|
+
{#if i > 0}
|
|
13
|
+
<span class="text-[var(--karbon-text-4,#b5b2cc)] text-xs">+</span>
|
|
14
|
+
{/if}
|
|
15
|
+
<kbd class="inline-flex items-center justify-center min-w-[1.5rem] h-6 px-1.5 rounded-md border border-[var(--karbon-border,rgba(0,0,0,0.07))] bg-[var(--karbon-bg-2,#e8e6f0)] text-[var(--karbon-text-2,#5a567e)] text-[11px] font-mono font-medium shadow-sm">
|
|
16
|
+
{key}
|
|
17
|
+
</kbd>
|
|
18
|
+
{/each}
|
|
19
|
+
</span>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte'
|
|
3
|
+
import type { CardVariant, CardPadding } from '@karbonjs/ui-core'
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
variant?: CardVariant
|
|
7
|
+
padding?: CardPadding
|
|
8
|
+
hoverable?: boolean
|
|
9
|
+
title?: string
|
|
10
|
+
icon?: any
|
|
11
|
+
class?: string
|
|
12
|
+
children: Snippet
|
|
13
|
+
header?: Snippet
|
|
14
|
+
footer?: Snippet
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let {
|
|
18
|
+
variant = 'default',
|
|
19
|
+
padding = 'md',
|
|
20
|
+
hoverable = false,
|
|
21
|
+
title = '',
|
|
22
|
+
icon: Icon,
|
|
23
|
+
class: className = '',
|
|
24
|
+
children,
|
|
25
|
+
header,
|
|
26
|
+
footer
|
|
27
|
+
}: Props = $props()
|
|
28
|
+
|
|
29
|
+
const variantClasses: Record<string, string> = {
|
|
30
|
+
default: 'bg-[var(--karbon-bg-card,#fff)] border border-[var(--karbon-border,rgba(0,0,0,0.07))]',
|
|
31
|
+
elevated: 'bg-[var(--karbon-bg-card,#fff)] shadow-lg',
|
|
32
|
+
outlined: 'border-2 border-[var(--karbon-border,rgba(0,0,0,0.07))]',
|
|
33
|
+
ghost: 'bg-transparent'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const paddingClasses: Record<string, string> = {
|
|
37
|
+
none: '',
|
|
38
|
+
sm: 'p-3',
|
|
39
|
+
md: 'p-5',
|
|
40
|
+
lg: 'p-8'
|
|
41
|
+
}
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<div class="rounded-xl overflow-hidden {variantClasses[variant]} {hoverable ? 'transition-all duration-200 hover:shadow-lg hover:-translate-y-0.5' : ''} {className}">
|
|
45
|
+
{#if header}
|
|
46
|
+
<div class="px-5 py-3.5 border-b border-[var(--karbon-border,rgba(0,0,0,0.07))]">
|
|
47
|
+
{@render header()}
|
|
48
|
+
</div>
|
|
49
|
+
{:else if title}
|
|
50
|
+
<div class="flex items-center gap-2 px-5 py-3.5 border-b border-[var(--karbon-border,rgba(0,0,0,0.07))] text-[var(--karbon-text-2,#5a567e)] text-[0.825rem] font-semibold">
|
|
51
|
+
{#if Icon}
|
|
52
|
+
<Icon class="w-4 h-4" />
|
|
53
|
+
{/if}
|
|
54
|
+
<span>{title}</span>
|
|
55
|
+
</div>
|
|
56
|
+
{/if}
|
|
57
|
+
|
|
58
|
+
<div class={paddingClasses[padding]}>
|
|
59
|
+
{@render children()}
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
{#if footer}
|
|
63
|
+
<div class="px-5 py-3.5 border-t border-[var(--karbon-border,rgba(0,0,0,0.07))]">
|
|
64
|
+
{@render footer()}
|
|
65
|
+
</div>
|
|
66
|
+
{/if}
|
|
67
|
+
</div>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
title: string
|
|
4
|
+
description?: string
|
|
5
|
+
icon?: any
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
let {
|
|
9
|
+
title,
|
|
10
|
+
description = '',
|
|
11
|
+
icon: Icon
|
|
12
|
+
}: Props = $props()
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<div class="text-center py-12 px-6">
|
|
16
|
+
{#if Icon}
|
|
17
|
+
<div class="w-14 h-14 rounded-2xl bg-[var(--karbon-nav-hover-bg,rgba(0,0,0,0.04))] text-[var(--karbon-text-4,#b5b2cc)] flex items-center justify-center mx-auto mb-4">
|
|
18
|
+
<Icon class="w-8 h-8" />
|
|
19
|
+
</div>
|
|
20
|
+
{/if}
|
|
21
|
+
<p class="text-[var(--karbon-text-2,#5a567e)] font-semibold text-[0.95rem] m-0">{title}</p>
|
|
22
|
+
{#if description}
|
|
23
|
+
<p class="text-[var(--karbon-text-3,#8e8aae)] text-[0.8rem] mt-1.5 mx-auto max-w-[22rem]">{description}</p>
|
|
24
|
+
{/if}
|
|
25
|
+
</div>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
title: string
|
|
4
|
+
description?: string
|
|
5
|
+
icon?: any
|
|
6
|
+
iconColor?: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
let {
|
|
10
|
+
title,
|
|
11
|
+
description = '',
|
|
12
|
+
icon: Icon,
|
|
13
|
+
iconColor = 'var(--karbon-primary, #cc1a1a)'
|
|
14
|
+
}: Props = $props()
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<div class="flex items-start gap-3 pb-4 border-b border-[var(--karbon-border,rgba(0,0,0,0.07))] mb-1">
|
|
18
|
+
{#if Icon}
|
|
19
|
+
<Icon class="w-5 h-5 shrink-0" style="color: {iconColor}" />
|
|
20
|
+
{/if}
|
|
21
|
+
<div>
|
|
22
|
+
<h1 class="text-[var(--karbon-text,#1a1635)] text-[1.1rem] font-bold m-0">{title}</h1>
|
|
23
|
+
{#if description}
|
|
24
|
+
<p class="text-[var(--karbon-text-3,#8e8aae)] text-[0.8rem] mt-0.5">{description}</p>
|
|
25
|
+
{/if}
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|