@tuspe/components 1.6.26

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/README.md ADDED
@@ -0,0 +1,203 @@
1
+ # Tuspe SvelteKit Components
2
+
3
+ [Tuspe Design](https://tuspe.com/en) builds websites and online stores for small and large businesses. This component library includes essential elements for forms, modals, breadcrumbs, and images. It also offers utility functions for price display, VAT calculations, and validations for tables and strings.
4
+
5
+ ## Breadcrumbs
6
+
7
+ A breadcrumb navigation provide links back to previous pages, and shows the user's current location in a website. The component complies with [Google standards](https://developers.google.com/search/docs/appearance/structured-data/breadcrumb).
8
+
9
+ ```TypeScript
10
+ interface Breadcrumb {
11
+ '@type'?: string
12
+ item: string
13
+ name: string
14
+ position?: string
15
+ }
16
+
17
+ interface Props {
18
+ homeName?: string
19
+ homeSlug?: string
20
+ values: Breadcrumb[]
21
+ }
22
+ ```
23
+
24
+ ## Button
25
+
26
+ Easily replace most buttons in your project with this versatile button component. The `control` value makes it ideal for icons and as a toggler for the mobile menu.
27
+
28
+ ```TypeScript
29
+ interface Props {
30
+ ariaControls?: string | undefined
31
+ ariaExpanded?: boolean | undefined
32
+ ariaLabel?: string
33
+ ariaPopup?: 'dialog' | 'menu' | 'listbox' | undefined
34
+ ball?: boolean
35
+ borderColor?: 'content' | 'default' | 'primary'
36
+ borderSize?: 0 | 1 | 2
37
+ children: Snippet
38
+ color?: string
39
+ colorBg?: string
40
+ control?: boolean
41
+ disabled?: boolean
42
+ extraClass?: string
43
+ fill?: boolean
44
+ fontWeight?: 'normal' | 'bold'
45
+ fullWidth?: boolean
46
+ hover?: 'black' | 'primary' | 'secondary' | 'success' | 'transparent'
47
+ hoverText?: 'black' | 'primary' | 'secondary' | 'white'
48
+ href?: string | undefined
49
+ id?: string
50
+ isActive?: boolean
51
+ noHeight?: boolean
52
+ noPadding?: boolean
53
+ onclick?: any
54
+ preload?: 'hover' | 'tap'
55
+ role?: string
56
+ target?: '_blank' | '_top' | undefined
57
+ type?: 'submit'
58
+ uppercase?: boolean
59
+ value?: string | number
60
+ }
61
+ ```
62
+
63
+ ## ButtonArrow
64
+
65
+ Arrow icons for navigation, such as image sliders or content transitions.
66
+
67
+ ```TypeScript
68
+ interface Props {
69
+ onclick?: () => any
70
+ ariaLabel: string
71
+ color?: string
72
+ direction: 'left' | 'right'
73
+ }
74
+ ```
75
+
76
+ ## ButtonClose
77
+
78
+ Close button for modals and other dismissible elements.
79
+
80
+ ```TypeScript
81
+ interface Props {
82
+ onclick: () => any
83
+ color?: string
84
+ }
85
+ ```
86
+
87
+ ## ButtonMenu
88
+
89
+ A button for toggling the mobile menu, dynamically changing its icon based on the menu's open or closed state.
90
+
91
+ ```TypeScript
92
+ interface Props {
93
+ onclick?: () => any
94
+ ariaControls: string
95
+ ariaLabel: string
96
+ color?: 'black' | 'white'
97
+ extraClass?: string
98
+ hidden?: boolean
99
+ open: boolean
100
+ }
101
+ ```
102
+
103
+ ## Image
104
+
105
+ A versatile image component supporting various aspect ratios and object fit options. Features include optional borders, centering, full-width display, custom classes, and configurable loading behavior.
106
+
107
+ ```TypeScript
108
+ interface ImageData {
109
+ alt: string
110
+ height?: number
111
+ src: string
112
+ width?: number
113
+ }
114
+
115
+ interface Props {
116
+ aspect?: '3:4' | '4:3' | 'square' | 'video'
117
+ ball?: boolean
118
+ border?: boolean
119
+ center?: boolean
120
+ extraClasses?: string
121
+ fullWidth?: boolean
122
+ image: ImageData
123
+ loading?: 'eager' | 'lazy'
124
+ objectFit?: 'contain' | 'cover'
125
+ }
126
+ ```
127
+
128
+ ## Input
129
+
130
+ A flexible `Input` component supporting common input types with basic styling for form fields. It includes optional attributes for labels, placeholders, min/max values, steps, and event handlers but does not perform content validation.
131
+
132
+ ```TypeScript
133
+ interface Props {
134
+ onchange?: () => void
135
+ onclick?: () => void
136
+ borderColor?: string
137
+ disabled?: boolean
138
+ id?: string
139
+ label: string
140
+ max?: number | string
141
+ min?: number | string
142
+ outerClass?: string
143
+ placeholder?: string
144
+ required?: boolean
145
+ step?: number
146
+ type?: 'email' | 'date' | 'number' | 'password' | 'search' | 'tel' | 'text' | 'textarea' | 'time' | 'url'
147
+ value: string | number
148
+ }
149
+ ```
150
+
151
+ ## Select
152
+
153
+ A customizable `Select` component for choosing from a list of options. Supports labels, placeholders, required fields, and disabled states. Accepts an array of selectable items and triggers an optional onchange event when the value changes.
154
+
155
+ ```TypeScript
156
+ interface SelectItem {
157
+ name: string
158
+ value: number | string
159
+ }
160
+
161
+ interface Props {
162
+ onchange?: () => void
163
+ disabled?: boolean
164
+ outerClass?: string
165
+ id?: string
166
+ label: string
167
+ placeholder?: string
168
+ required?: boolean
169
+ value: string | number
170
+ values: SelectItem[]
171
+ }
172
+ ```
173
+
174
+ ## Checkbox
175
+
176
+ Allows users to toggle between checked and unchecked states.
177
+
178
+ ```TypeScript
179
+ interface Props {
180
+ children: Snippet
181
+ onchange?: () => void
182
+ checked: boolean
183
+ disabled?: boolean
184
+ group?: boolean
185
+ id?: string
186
+ outerClass?: string
187
+ value?: string
188
+ }
189
+ ```
190
+
191
+ ## Modal
192
+
193
+ A simple `Modal` component that displays a popup with customizable content.
194
+
195
+ ```TypeScript
196
+ interface Props {
197
+ children: Snippet
198
+ innerClass?: string
199
+ open?: boolean
200
+ outerClass?: string
201
+ title?: string
202
+ }
203
+ ```
@@ -0,0 +1,81 @@
1
+ <script lang="ts">
2
+ import {page} from '$app/state'
3
+ import type {Breadcrumb} from './'
4
+
5
+ interface Props {
6
+ homeName?: string
7
+ homeSlug?: string
8
+ values: Breadcrumb[]
9
+ }
10
+
11
+ let {homeName = 'Etusivu', homeSlug = '', values}: Props = $props()
12
+ const origin = page.url.origin + '/'
13
+
14
+ let originWithSlug = $state(origin + homeSlug),
15
+ listItems = $derived<Breadcrumb[]>(
16
+ Array.isArray(values) && values.length > 0
17
+ ? [
18
+ {
19
+ '@type': 'ListItem',
20
+ item: originWithSlug,
21
+ name: homeName,
22
+ position: '1'
23
+ },
24
+ ...values.map((page: Breadcrumb, index: number) => ({
25
+ '@type': 'ListItem',
26
+ item: homeSlug ? originWithSlug + '/' + page.item : origin + page.item,
27
+ name: page.name,
28
+ position: String(index + 2)
29
+ }))
30
+ ]
31
+ : []
32
+ ),
33
+ ldjson = $derived(
34
+ listItems.length > 0
35
+ ? `<script type="application/ld+json">{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":${JSON.stringify(listItems)}}${'<'}/script>`
36
+ : ''
37
+ )
38
+ </script>
39
+
40
+ <svelte:head>
41
+ {#if ldjson}
42
+ {@html ldjson}
43
+ {/if}
44
+ </svelte:head>
45
+
46
+ {#if Array.isArray(listItems) && listItems.length > 0}
47
+ <div class="border-bottom truncate">
48
+ <ol id="breadcrumb" class="max-w-screen-xl mx-auto my-0 px-4 py-2" vocab="https://schema.org/" typeof="BreadcrumbList">
49
+ {#each listItems as page}
50
+ <li property="itemListElement" typeof="ListItem">
51
+ <a href={page.item} class="bc-link" property="item" typeof="WebPage">
52
+ <span property="name">{page.name}</span>
53
+ </a>
54
+ <meta property="position" content={page.position} />
55
+ </li>
56
+ {/each}
57
+ </ol>
58
+ </div>
59
+ {/if}
60
+
61
+ <style scoped>
62
+ #breadcrumb {
63
+ white-space: nowrap;
64
+ text-overflow: ellipsis;
65
+ }
66
+
67
+ #breadcrumb li {
68
+ display: inline-block;
69
+ }
70
+
71
+ #breadcrumb li:not(:last-child):after {
72
+ content: '»';
73
+ display: inline;
74
+ font-size: 0.95em;
75
+ padding: 0 5px;
76
+ }
77
+
78
+ #breadcrumb a:not(:hover) {
79
+ text-decoration: none;
80
+ }
81
+ </style>
@@ -0,0 +1,9 @@
1
+ import type { Breadcrumb } from './';
2
+ interface Props {
3
+ homeName?: string;
4
+ homeSlug?: string;
5
+ values: Breadcrumb[];
6
+ }
7
+ declare const Breadcrumbs: import("svelte").Component<Props, {}, "">;
8
+ type Breadcrumbs = ReturnType<typeof Breadcrumbs>;
9
+ export default Breadcrumbs;
@@ -0,0 +1,266 @@
1
+ <script lang="ts">
2
+ import {loading} from './'
3
+ import type {Snippet} from 'svelte'
4
+
5
+ interface Props {
6
+ ariaControls?: string | undefined
7
+ ariaExpanded?: boolean | undefined
8
+ ariaLabel?: string
9
+ ariaPopup?: 'dialog' | 'menu' | 'listbox' | undefined
10
+ ball?: boolean
11
+ borderColor?: 'content' | 'default' | 'primary'
12
+ borderSize?: 0 | 1 | 2
13
+ children: Snippet
14
+ color?: string
15
+ colorBg?: string
16
+ control?: boolean
17
+ disabled?: boolean
18
+ extraClass?: string
19
+ fill?: boolean
20
+ fontWeight?: 'normal' | 'bold'
21
+ fullWidth?: boolean
22
+ hover?: 'black' | 'primary' | 'secondary' | 'success' | 'transparent'
23
+ hoverText?: 'black' | 'primary' | 'secondary' | 'white'
24
+ href?: string | undefined
25
+ id?: string
26
+ isActive?: boolean
27
+ noHeight?: boolean
28
+ noPadding?: boolean
29
+ onclick?: any
30
+ preload?: 'hover' | 'tap'
31
+ role?: string
32
+ target?: '_blank' | '_top' | undefined
33
+ type?: 'submit'
34
+ uppercase?: boolean
35
+ value?: string | number
36
+ }
37
+
38
+ let {
39
+ ariaControls,
40
+ ariaExpanded,
41
+ ariaLabel,
42
+ ariaPopup,
43
+ ball,
44
+ borderColor = 'content',
45
+ borderSize = 0,
46
+ children,
47
+ color = 'white',
48
+ colorBg = 'primary',
49
+ control,
50
+ disabled,
51
+ extraClass,
52
+ fill,
53
+ fontWeight = 'bold',
54
+ fullWidth,
55
+ hover = 'secondary',
56
+ hoverText = 'white',
57
+ href,
58
+ id,
59
+ isActive = false,
60
+ noHeight,
61
+ noPadding,
62
+ onclick = undefined,
63
+ preload,
64
+ role,
65
+ target,
66
+ type,
67
+ uppercase = true,
68
+ value
69
+ }: Props = $props()
70
+
71
+ let classes = $state('btn text-' + color)
72
+
73
+ if (control) {
74
+ classes += ' control'
75
+ } else {
76
+ classes += ' bg-' + colorBg
77
+ }
78
+ if (ball) {
79
+ classes += ' radius-full'
80
+ }
81
+ if (borderSize) {
82
+ classes += ' border-solid border-' + borderSize + ' border-' + borderColor
83
+ }
84
+ if (extraClass) {
85
+ classes += ' ' + extraClass
86
+ }
87
+ if (fill) {
88
+ classes += ' fill'
89
+ }
90
+ if (fontWeight) {
91
+ classes += ' font-' + fontWeight
92
+ }
93
+ if (fullWidth) {
94
+ classes += ' w-full'
95
+ }
96
+ if (hover) {
97
+ classes += ' hover-' + hover
98
+ }
99
+ if (hoverText) {
100
+ classes += ' hover-text-' + hoverText
101
+ }
102
+ if (isActive) {
103
+ classes += ' active'
104
+ }
105
+ if (noHeight) {
106
+ classes += ' no-height'
107
+ }
108
+ if (noPadding) {
109
+ classes += ' no-padding'
110
+ }
111
+ if (uppercase) {
112
+ classes += ' uppercase'
113
+ }
114
+ </script>
115
+
116
+ {#if href}
117
+ <a {href} class={classes} {target} data-sveltekit-preload-data={preload} rel="nofollow noopener">
118
+ {@render children?.()}
119
+ </a>
120
+ {:else if control}
121
+ <button
122
+ {id}
123
+ {onclick}
124
+ {role}
125
+ {value}
126
+ aria-controls={ariaControls}
127
+ aria-expanded={ariaExpanded}
128
+ aria-haspopup={ariaPopup}
129
+ aria-label={ariaLabel}
130
+ class={classes}
131
+ disabled={disabled || $loading}
132
+ >
133
+ {@render children?.()}
134
+ </button>
135
+ {:else}
136
+ <button {onclick} class={classes} {disabled} {type}>
137
+ {@render children?.()}
138
+ </button>
139
+ {/if}
140
+
141
+ <style scoped>
142
+ a {
143
+ text-decoration: none;
144
+ }
145
+ .bg-primary:not(:hover),
146
+ .hover-primary:hover {
147
+ background-color: var(--color-primary);
148
+ }
149
+ .bg-secondary:not(:hover),
150
+ .hover-secondary:hover {
151
+ background-color: var(--color-secondary);
152
+ }
153
+ .bg-success:not(:hover),
154
+ .hover-success:hover {
155
+ background-color: green;
156
+ }
157
+ .bg-danger:not(:hover),
158
+ .hover-danger:hover {
159
+ background-color: darkred;
160
+ }
161
+ .bg-black:not(:hover),
162
+ .hover-black:hover {
163
+ background-color: #000;
164
+ }
165
+ .border-solid {
166
+ border-style: solid;
167
+ }
168
+ .border-1 {
169
+ border-width: 1px;
170
+ }
171
+ .border-2 {
172
+ border-width: 2px;
173
+ }
174
+ .border-content {
175
+ border-color: var(--color-content);
176
+ }
177
+ .border-default {
178
+ border-color: var(--color-border);
179
+ }
180
+ .border-primary {
181
+ border-color: var(--color-primary);
182
+ }
183
+ .btn {
184
+ align-items: center;
185
+ display: inline-flex;
186
+ justify-content: center;
187
+ }
188
+ .btn:not(.border-solid) {
189
+ border: 0;
190
+ }
191
+ .btn:not(.fill):not(.no-height) {
192
+ height: 48px;
193
+ }
194
+ .btn:not(:disabled) {
195
+ cursor: pointer;
196
+ }
197
+ .btn:not(.control) {
198
+ font-size: 1rem;
199
+ }
200
+ .btn:not(.control):not(.flll):not(.no-padding):not(.radius-full) {
201
+ padding-left: 2rem;
202
+ padding-right: 2rem;
203
+ }
204
+ .btn:not(.radius-full) {
205
+ border-radius: 0.375rem;
206
+ }
207
+ .btn:disabled {
208
+ cursor: not-allowed;
209
+ opacity: 0.6;
210
+ }
211
+ .control {
212
+ border: 0;
213
+ }
214
+ .control,
215
+ .hover-transparent:hover {
216
+ background-color: transparent;
217
+ }
218
+ .control,
219
+ .fill,
220
+ .radius-full {
221
+ padding: 0;
222
+ }
223
+ .control:not(.fill) {
224
+ width: 48px;
225
+ }
226
+ .fill {
227
+ height: 100%;
228
+ width: 100%;
229
+ }
230
+ .font-400 {
231
+ font-weight: 400;
232
+ }
233
+ .font-700 {
234
+ font-weight: 700;
235
+ }
236
+ .hover-text-black:hover,
237
+ .text-black {
238
+ color: #000;
239
+ }
240
+ .hover-text-primary:hover,
241
+ .text-primary {
242
+ color: var(--color-primary);
243
+ }
244
+ .hover-text-content:hover,
245
+ .text-content {
246
+ color: var(--color-content);
247
+ }
248
+ .hover-black:hover,
249
+ .hover-primary:hover,
250
+ .hover-success:hover,
251
+ .hover-text-white:hover,
252
+ .text-white {
253
+ color: #fff;
254
+ }
255
+ .hover-text-secondary:hover,
256
+ .text-secondary {
257
+ color: var(--color-secondary);
258
+ }
259
+ .radius-full {
260
+ border-radius: 50%;
261
+ overflow: hidden;
262
+ }
263
+ .w-full {
264
+ width: 100%;
265
+ }
266
+ </style>
@@ -0,0 +1,36 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ ariaControls?: string | undefined;
4
+ ariaExpanded?: boolean | undefined;
5
+ ariaLabel?: string;
6
+ ariaPopup?: 'dialog' | 'menu' | 'listbox' | undefined;
7
+ ball?: boolean;
8
+ borderColor?: 'content' | 'default' | 'primary';
9
+ borderSize?: 0 | 1 | 2;
10
+ children: Snippet;
11
+ color?: string;
12
+ colorBg?: string;
13
+ control?: boolean;
14
+ disabled?: boolean;
15
+ extraClass?: string;
16
+ fill?: boolean;
17
+ fontWeight?: 'normal' | 'bold';
18
+ fullWidth?: boolean;
19
+ hover?: 'black' | 'primary' | 'secondary' | 'success' | 'transparent';
20
+ hoverText?: 'black' | 'primary' | 'secondary' | 'white';
21
+ href?: string | undefined;
22
+ id?: string;
23
+ isActive?: boolean;
24
+ noHeight?: boolean;
25
+ noPadding?: boolean;
26
+ onclick?: any;
27
+ preload?: 'hover' | 'tap';
28
+ role?: string;
29
+ target?: '_blank' | '_top' | undefined;
30
+ type?: 'submit';
31
+ uppercase?: boolean;
32
+ value?: string | number;
33
+ }
34
+ declare const Button: import("svelte").Component<Props, {}, "">;
35
+ type Button = ReturnType<typeof Button>;
36
+ export default Button;
@@ -0,0 +1,21 @@
1
+ <script lang="ts">
2
+ import Button from './Button.svelte'
3
+ interface Props {
4
+ onclick?: () => any
5
+ ariaLabel: string
6
+ color?: string
7
+ direction: 'left' | 'right'
8
+ }
9
+ let {onclick, ariaLabel, color = 'black', direction}: Props = $props()
10
+ </script>
11
+
12
+ <Button {color} {onclick} {ariaLabel} control>
13
+ <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24">
14
+ <title>Arrow {direction}</title>
15
+ {#if direction === 'left'}
16
+ <path d="M16.67 0l2.83 2.829-9.339 9.175 9.339 9.167-2.83 2.829-12.17-11.996z" />
17
+ {:else}
18
+ <path d="M7.33 24l-2.83-2.829 9.339-9.175-9.339-9.167 2.83-2.829 12.17 11.996z" />
19
+ {/if}
20
+ </svg>
21
+ </Button>
@@ -0,0 +1,9 @@
1
+ interface Props {
2
+ onclick?: () => any;
3
+ ariaLabel: string;
4
+ color?: string;
5
+ direction: 'left' | 'right';
6
+ }
7
+ declare const ButtonArrow: import("svelte").Component<Props, {}, "">;
8
+ type ButtonArrow = ReturnType<typeof ButtonArrow>;
9
+ export default ButtonArrow;
@@ -0,0 +1,25 @@
1
+ <script lang="ts">
2
+ import Button from './Button.svelte'
3
+ let {color = 'black', onclick} = $props()
4
+ </script>
5
+
6
+ <div class="close-button">
7
+ <Button {color} {onclick} control fill>
8
+ <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24">
9
+ <title>Sulje ikkuna</title>
10
+ <path
11
+ d="M12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm5 15.538l-3.592-3.548 3.546-3.587-1.416-1.403-3.545 3.589-3.588-3.543-1.405 1.405 3.593 3.552-3.547 3.592 1.405 1.405 3.555-3.596 3.591 3.55 1.403-1.416z"
12
+ />
13
+ </svg>
14
+ </Button>
15
+ </div>
16
+
17
+ <style scoped>
18
+ .close-button {
19
+ height: 48px;
20
+ position: absolute;
21
+ right: 0;
22
+ top: 0;
23
+ width: 48px;
24
+ }
25
+ </style>
@@ -0,0 +1,6 @@
1
+ declare const ButtonClose: import("svelte").Component<{
2
+ color?: string;
3
+ onclick: any;
4
+ }, {}, "">;
5
+ type ButtonClose = ReturnType<typeof ButtonClose>;
6
+ export default ButtonClose;