urpanels-ui-pack 0.0.3 → 0.0.10
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/dist/BasePageService/BasePageService.d.ts +121 -0
- package/dist/BasePageService/BasePageService.js +191 -0
- package/dist/BasePageService/index.d.ts +2 -0
- package/dist/BasePageService/index.js +1 -0
- package/dist/Button/Button.svelte +1 -0
- package/dist/InfoCard/InfoCard.svelte +61 -30
- package/dist/InputCheckboxModal/InputCheckboxModal.svelte +235 -0
- package/dist/InputCheckboxModal/InputCheckboxModal.svelte.d.ts +23 -0
- package/dist/InputCheckboxModal/index.d.ts +1 -0
- package/dist/InputCheckboxModal/index.js +1 -0
- package/dist/InputSelectModal/InputSelectModal.svelte +118 -0
- package/dist/InputSelectModal/InputSelectModal.svelte.d.ts +19 -0
- package/dist/InputSelectModal/index.d.ts +1 -0
- package/dist/InputSelectModal/index.js +1 -0
- package/dist/LoadingSpinner/LoadingSpinner.svelte +1 -1
- package/dist/Modal/Modal.svelte +74 -2
- package/dist/Modal/Modal.svelte.d.ts +9 -2
- package/dist/PageLayout/ActionButton.svelte +12 -4
- package/dist/PageLayout/PageContent.svelte +1 -1
- package/dist/PageLayout/PageHeader.svelte +75 -15
- package/dist/PageLayout/PageHeader.svelte.d.ts +10 -1
- package/dist/PageLayout/SearchBar.svelte +1 -1
- package/dist/PageLayout/ViewToggle.svelte +1 -1
- package/dist/Pagination/Pagination.svelte +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +8 -0
- package/dist/inputs/InputNumber/InputNumber.svelte +140 -0
- package/dist/inputs/InputNumber/InputNumber.svelte.d.ts +27 -0
- package/dist/inputs/InputNumber/index.d.ts +2 -0
- package/dist/inputs/InputNumber/index.js +2 -0
- package/dist/inputs/InputSelect/InputSelect.svelte +73 -0
- package/dist/inputs/InputSelect/InputSelect.svelte.d.ts +18 -0
- package/dist/inputs/InputSelect/index.d.ts +2 -0
- package/dist/inputs/InputSelect/index.js +2 -0
- package/dist/inputs/InputText/InputText.svelte +126 -0
- package/dist/inputs/InputText/InputText.svelte.d.ts +49 -0
- package/dist/inputs/InputText/index.d.ts +2 -0
- package/dist/inputs/InputText/index.js +2 -0
- package/dist/inputs/TextArea/TextArea.svelte +113 -0
- package/dist/inputs/TextArea/TextArea.svelte.d.ts +21 -0
- package/dist/inputs/TextArea/index.d.ts +2 -0
- package/dist/inputs/TextArea/index.js +2 -0
- package/dist/inputs/index.d.ts +3 -0
- package/dist/inputs/index.js +4 -0
- package/dist/sections/PreviewSelector/PreviewSelector.svelte +164 -0
- package/dist/sections/PreviewSelector/PreviewSelector.svelte.d.ts +25 -0
- package/dist/sections/PreviewSelector/index.d.ts +2 -0
- package/dist/sections/PreviewSelector/index.js +1 -0
- package/dist/sections/index.d.ts +2 -0
- package/dist/sections/index.js +1 -0
- package/package.json +14 -1
|
@@ -1,29 +1,89 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
|
+
import SearchBar from './SearchBar.svelte';
|
|
4
|
+
import ViewToggle from './ViewToggle.svelte';
|
|
3
5
|
|
|
4
6
|
interface Props {
|
|
5
7
|
title: string;
|
|
8
|
+
showBackButton?: boolean;
|
|
9
|
+
onBackClick?: () => void;
|
|
10
|
+
backLabel?: string;
|
|
6
11
|
actions?: Snippet;
|
|
7
|
-
|
|
12
|
+
// Search props
|
|
13
|
+
showSearch?: boolean;
|
|
14
|
+
searchPlaceholder?: string;
|
|
15
|
+
searchValue?: string;
|
|
16
|
+
onSearchInput?: (e: Event) => void;
|
|
17
|
+
// View toggle props
|
|
18
|
+
showViewToggle?: boolean;
|
|
19
|
+
viewToggleMode?: 'card-table' | 'daily-weekly';
|
|
20
|
+
isSecondOption?: boolean;
|
|
21
|
+
onViewToggle?: (isSecondOption: boolean) => void;
|
|
8
22
|
}
|
|
9
23
|
|
|
10
|
-
let {
|
|
24
|
+
let {
|
|
25
|
+
title,
|
|
26
|
+
showBackButton = false,
|
|
27
|
+
onBackClick,
|
|
28
|
+
backLabel = 'Geri',
|
|
29
|
+
actions,
|
|
30
|
+
// Search
|
|
31
|
+
showSearch = false,
|
|
32
|
+
searchPlaceholder = "Ara...",
|
|
33
|
+
searchValue = $bindable(""),
|
|
34
|
+
onSearchInput,
|
|
35
|
+
// View toggle
|
|
36
|
+
showViewToggle = false,
|
|
37
|
+
viewToggleMode = 'card-table',
|
|
38
|
+
isSecondOption = $bindable(false),
|
|
39
|
+
onViewToggle
|
|
40
|
+
}: Props = $props();
|
|
11
41
|
</script>
|
|
12
42
|
|
|
13
|
-
<div class="sticky top-0 z-20 bg-white px-4 sm:px-6 py-3 mb-4">
|
|
14
|
-
<div class="flex
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
43
|
+
<div class="sticky top-0 z-20 bg-white px-4 sm:px-6 py-3 mb-4" data-component-name="PageHeader">
|
|
44
|
+
<div class="flex flex-col gap-3">
|
|
45
|
+
<!-- Top row: Title + ViewToggle + Actions -->
|
|
46
|
+
<div class="flex items-center justify-between gap-2">
|
|
47
|
+
<div class="flex items-center gap-2 min-w-0">
|
|
48
|
+
{#if showBackButton && onBackClick}
|
|
49
|
+
<button
|
|
50
|
+
class="flex items-center gap-2 rounded-lg bg-gray-100 px-3 py-2 text-sm font-semibold text-gray-700 transition hover:bg-gray-200"
|
|
51
|
+
onclick={onBackClick}
|
|
52
|
+
aria-label={backLabel}
|
|
53
|
+
>
|
|
54
|
+
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
|
55
|
+
<path fill-rule="evenodd" d="M7.707 14.707a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 1.414L5.414 9H17a1 1 0 110 2H5.414l2.293 2.293a1 1 0 010 1.414z" clip-rule="evenodd" />
|
|
56
|
+
</svg>
|
|
57
|
+
<span>{backLabel}</span>
|
|
58
|
+
</button>
|
|
59
|
+
{/if}
|
|
60
|
+
<h1 class="text-xl sm:text-2xl font-bold truncate">{title}</h1>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<div class="flex items-center gap-2 sm:gap-3 flex-shrink-0">
|
|
64
|
+
{#if showViewToggle}
|
|
65
|
+
<ViewToggle
|
|
66
|
+
mode={viewToggleMode}
|
|
67
|
+
bind:isSecondOption={isSecondOption}
|
|
68
|
+
onToggle={onViewToggle}
|
|
69
|
+
/>
|
|
70
|
+
{/if}
|
|
71
|
+
|
|
72
|
+
{#if actions}
|
|
73
|
+
{@render actions()}
|
|
74
|
+
{/if}
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<!-- Bottom row: Search Bar (full width, always below) -->
|
|
79
|
+
{#if showSearch}
|
|
80
|
+
<div class="w-full">
|
|
81
|
+
<SearchBar
|
|
82
|
+
placeholder={searchPlaceholder}
|
|
83
|
+
bind:value={searchValue}
|
|
84
|
+
oninput={onSearchInput}
|
|
85
|
+
/>
|
|
20
86
|
</div>
|
|
21
87
|
{/if}
|
|
22
88
|
</div>
|
|
23
|
-
|
|
24
|
-
{#if search}
|
|
25
|
-
<div class="mt-3">
|
|
26
|
-
{@render search()}
|
|
27
|
-
</div>
|
|
28
|
-
{/if}
|
|
29
89
|
</div>
|
|
@@ -2,7 +2,16 @@ import type { Snippet } from 'svelte';
|
|
|
2
2
|
interface Props {
|
|
3
3
|
title: string;
|
|
4
4
|
actions?: Snippet;
|
|
5
|
-
|
|
5
|
+
// Search props
|
|
6
|
+
showSearch?: boolean;
|
|
7
|
+
searchPlaceholder?: string;
|
|
8
|
+
searchValue?: string;
|
|
9
|
+
onSearchInput?: (e: Event) => void;
|
|
10
|
+
// View toggle props
|
|
11
|
+
showViewToggle?: boolean;
|
|
12
|
+
viewToggleMode?: 'card-table' | 'daily-weekly';
|
|
13
|
+
isSecondOption?: boolean;
|
|
14
|
+
onViewToggle?: (isSecondOption: boolean) => void;
|
|
6
15
|
}
|
|
7
16
|
declare const PageHeader: import("svelte").Component<Props, {}, "">;
|
|
8
17
|
type PageHeader = ReturnType<typeof PageHeader>;
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
}
|
|
32
32
|
</script>
|
|
33
33
|
|
|
34
|
-
<div class="inline-flex items-center bg-gray-100 rounded-xl p-1.5">
|
|
34
|
+
<div class="inline-flex items-center bg-gray-100 rounded-xl p-1.5" data-component-name="ViewToggle">
|
|
35
35
|
<button
|
|
36
36
|
onclick={() => handleToggle(false)}
|
|
37
37
|
class="flex items-center gap-2 px-4 md:px-3 py-2.5 md:py-1.5 rounded-lg text-base md:text-sm font-medium transition-all {!isSecondOption ? 'bg-white text-blue-600 shadow-sm' : 'text-gray-600 hover:text-gray-900'}"
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
</script>
|
|
39
39
|
|
|
40
40
|
{#if totalPages > 1}
|
|
41
|
-
<div class="mt-6 flex flex-col sm:flex-row items-center justify-between gap-4 border-t pt-4">
|
|
41
|
+
<div class="mt-6 flex flex-col sm:flex-row items-center justify-between gap-4 border-t pt-4" data-component-name="Pagination">
|
|
42
42
|
{#if showInfo}
|
|
43
43
|
<div class="text-sm text-gray-600">
|
|
44
44
|
{t('table.showing')}: <span class="font-semibold">{startItem}</span> -
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -6,5 +6,13 @@ export * from './LoadingSpinner';
|
|
|
6
6
|
export * from './Modal';
|
|
7
7
|
export * from './RichTextEditor';
|
|
8
8
|
export * from './Button';
|
|
9
|
+
export * from './InputSelectModal';
|
|
10
|
+
export * from './InputCheckboxModal';
|
|
11
|
+
// Sections
|
|
12
|
+
export * from './sections';
|
|
13
|
+
// Services
|
|
14
|
+
export * from './BasePageService';
|
|
15
|
+
// Inputs
|
|
16
|
+
export * from './inputs';
|
|
9
17
|
// Utils
|
|
10
18
|
export * from './utils/translations.svelte';
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
id?: string;
|
|
6
|
+
label?: string;
|
|
7
|
+
value?: number | string | null;
|
|
8
|
+
required?: boolean;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
placeholder?: string;
|
|
11
|
+
error?: string;
|
|
12
|
+
helpText?: string;
|
|
13
|
+
prefix?: Snippet;
|
|
14
|
+
suffix?: Snippet;
|
|
15
|
+
min?: number;
|
|
16
|
+
max?: number;
|
|
17
|
+
step?: number;
|
|
18
|
+
inputmode?: 'numeric' | 'decimal';
|
|
19
|
+
isActive?: boolean;
|
|
20
|
+
validationError?: string;
|
|
21
|
+
isNewRecord?: boolean;
|
|
22
|
+
onInput?: (value: string) => void;
|
|
23
|
+
onChange?: (value: string) => void;
|
|
24
|
+
onBlur?: () => void;
|
|
25
|
+
onFocus?: () => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let {
|
|
29
|
+
id = '',
|
|
30
|
+
label = '',
|
|
31
|
+
value = $bindable(''),
|
|
32
|
+
required = false,
|
|
33
|
+
disabled = false,
|
|
34
|
+
placeholder = '',
|
|
35
|
+
error = '',
|
|
36
|
+
helpText = '',
|
|
37
|
+
prefix,
|
|
38
|
+
suffix,
|
|
39
|
+
min,
|
|
40
|
+
max,
|
|
41
|
+
step,
|
|
42
|
+
inputmode = 'numeric',
|
|
43
|
+
isActive = true,
|
|
44
|
+
validationError = '',
|
|
45
|
+
isNewRecord = false,
|
|
46
|
+
onInput,
|
|
47
|
+
onChange,
|
|
48
|
+
onBlur,
|
|
49
|
+
onFocus
|
|
50
|
+
}: Props = $props();
|
|
51
|
+
|
|
52
|
+
const hasError = $derived(
|
|
53
|
+
!!error ||
|
|
54
|
+
(isNewRecord &&
|
|
55
|
+
required &&
|
|
56
|
+
validationError &&
|
|
57
|
+
(value === undefined || value === null || value === ''))
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const inputWrapperClass = $derived(
|
|
61
|
+
hasError
|
|
62
|
+
? 'border-red-300 focus-within:border-red-500'
|
|
63
|
+
: 'border-gray-300 focus-within:border-blue-500'
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
function handleInput(e: Event) {
|
|
67
|
+
const target = e.target as HTMLInputElement;
|
|
68
|
+
value = target.value;
|
|
69
|
+
onInput?.(target.value);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function handleChange(e: Event) {
|
|
73
|
+
const target = e.target as HTMLInputElement;
|
|
74
|
+
onChange?.(target.value);
|
|
75
|
+
}
|
|
76
|
+
</script>
|
|
77
|
+
|
|
78
|
+
{#if isActive}
|
|
79
|
+
<div class="w-full">
|
|
80
|
+
{#if label}
|
|
81
|
+
<label class="mb-1.5 block text-sm font-medium text-gray-700" for={id}>
|
|
82
|
+
{label}
|
|
83
|
+
{#if required}
|
|
84
|
+
<span class="text-red-500 ml-0.5">*</span>
|
|
85
|
+
{/if}
|
|
86
|
+
</label>
|
|
87
|
+
{/if}
|
|
88
|
+
|
|
89
|
+
<div class="relative">
|
|
90
|
+
<div
|
|
91
|
+
class="flex items-center border rounded-lg bg-white transition-all duration-200 {inputWrapperClass} {disabled ? 'bg-gray-50 cursor-not-allowed' : ''}"
|
|
92
|
+
>
|
|
93
|
+
{#if prefix}
|
|
94
|
+
<div class="pl-3 flex items-center text-gray-500">
|
|
95
|
+
{@render prefix()}
|
|
96
|
+
</div>
|
|
97
|
+
{/if}
|
|
98
|
+
|
|
99
|
+
<input
|
|
100
|
+
{id}
|
|
101
|
+
type="number"
|
|
102
|
+
{placeholder}
|
|
103
|
+
{required}
|
|
104
|
+
{disabled}
|
|
105
|
+
{min}
|
|
106
|
+
{max}
|
|
107
|
+
{step}
|
|
108
|
+
{inputmode}
|
|
109
|
+
bind:value
|
|
110
|
+
oninput={handleInput}
|
|
111
|
+
onchange={handleChange}
|
|
112
|
+
onblur={onBlur}
|
|
113
|
+
onfocus={onFocus}
|
|
114
|
+
class="flex-1 w-full px-3 py-2 bg-transparent border-none outline-none rounded-lg focus:outline-none focus:ring-0 focus-visible:outline-none text-gray-900 placeholder-gray-400 disabled:cursor-not-allowed disabled:text-gray-500"
|
|
115
|
+
/>
|
|
116
|
+
|
|
117
|
+
{#if suffix}
|
|
118
|
+
<div class="pr-3 flex items-center text-gray-500">
|
|
119
|
+
{@render suffix()}
|
|
120
|
+
</div>
|
|
121
|
+
{/if}
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
|
|
125
|
+
{#if error}
|
|
126
|
+
<p class="mt-1.5 text-sm text-red-600 flex items-center gap-1">
|
|
127
|
+
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
|
|
128
|
+
<path
|
|
129
|
+
fill-rule="evenodd"
|
|
130
|
+
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z"
|
|
131
|
+
clip-rule="evenodd"
|
|
132
|
+
/>
|
|
133
|
+
</svg>
|
|
134
|
+
{error}
|
|
135
|
+
</p>
|
|
136
|
+
{:else if helpText}
|
|
137
|
+
<p class="mt-1.5 text-sm text-gray-500">{helpText}</p>
|
|
138
|
+
{/if}
|
|
139
|
+
</div>
|
|
140
|
+
{/if}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
id?: string;
|
|
4
|
+
label?: string;
|
|
5
|
+
value?: number | string | null;
|
|
6
|
+
required?: boolean;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
placeholder?: string;
|
|
9
|
+
error?: string;
|
|
10
|
+
helpText?: string;
|
|
11
|
+
prefix?: Snippet;
|
|
12
|
+
suffix?: Snippet;
|
|
13
|
+
min?: number;
|
|
14
|
+
max?: number;
|
|
15
|
+
step?: number;
|
|
16
|
+
inputmode?: 'numeric' | 'decimal';
|
|
17
|
+
isActive?: boolean;
|
|
18
|
+
validationError?: string;
|
|
19
|
+
isNewRecord?: boolean;
|
|
20
|
+
onInput?: (value: string) => void;
|
|
21
|
+
onChange?: (value: string) => void;
|
|
22
|
+
onBlur?: () => void;
|
|
23
|
+
onFocus?: () => void;
|
|
24
|
+
}
|
|
25
|
+
declare const InputNumber: import("svelte").Component<Props, {}, "value">;
|
|
26
|
+
type InputNumber = ReturnType<typeof InputNumber>;
|
|
27
|
+
export default InputNumber;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
type OptionItem = {
|
|
3
|
+
value: string;
|
|
4
|
+
label: string;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
type Props = {
|
|
8
|
+
id?: string;
|
|
9
|
+
label?: string;
|
|
10
|
+
value?: string | null;
|
|
11
|
+
options: OptionItem[] | string[];
|
|
12
|
+
required?: boolean;
|
|
13
|
+
isActive?: boolean;
|
|
14
|
+
validationError?: string;
|
|
15
|
+
isNewRecord?: boolean;
|
|
16
|
+
srOnly?: boolean;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
let {
|
|
20
|
+
id = '',
|
|
21
|
+
label = '',
|
|
22
|
+
value = $bindable(''),
|
|
23
|
+
options = [],
|
|
24
|
+
required = false,
|
|
25
|
+
isActive = true,
|
|
26
|
+
validationError = '',
|
|
27
|
+
isNewRecord = false,
|
|
28
|
+
srOnly = false
|
|
29
|
+
}: Props = $props();
|
|
30
|
+
|
|
31
|
+
const hasError = $derived(
|
|
32
|
+
isNewRecord && required && validationError && !value
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const normalizedOptions = $derived(() => {
|
|
36
|
+
if (options.length === 0) return [];
|
|
37
|
+
|
|
38
|
+
if (typeof options[0] === 'string') {
|
|
39
|
+
return (options as string[]).map((opt) => ({
|
|
40
|
+
value: opt,
|
|
41
|
+
label: opt
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return options as OptionItem[];
|
|
46
|
+
});
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
{#if isActive}
|
|
50
|
+
<div>
|
|
51
|
+
<label
|
|
52
|
+
class="block text-sm font-medium mb-1 {srOnly ? 'sr-only' : ''}"
|
|
53
|
+
for={id}
|
|
54
|
+
>
|
|
55
|
+
{label}
|
|
56
|
+
{#if required && !srOnly}
|
|
57
|
+
<span class="text-red-500">*</span>
|
|
58
|
+
{/if}
|
|
59
|
+
</label>
|
|
60
|
+
<select
|
|
61
|
+
{id}
|
|
62
|
+
class="w-full rounded-lg border px-3 py-2.5 bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition {hasError ? 'border-red-500' : 'border-gray-300'}"
|
|
63
|
+
bind:value
|
|
64
|
+
>
|
|
65
|
+
{#each normalizedOptions() as option}
|
|
66
|
+
<option value={option.value}>{option.label}</option>
|
|
67
|
+
{/each}
|
|
68
|
+
</select>
|
|
69
|
+
{#if hasError}
|
|
70
|
+
<div class="mt-1 text-xs text-red-500">{validationError}</div>
|
|
71
|
+
{/if}
|
|
72
|
+
</div>
|
|
73
|
+
{/if}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
type OptionItem = {
|
|
2
|
+
value: string;
|
|
3
|
+
label: string;
|
|
4
|
+
};
|
|
5
|
+
type Props = {
|
|
6
|
+
id?: string;
|
|
7
|
+
label?: string;
|
|
8
|
+
value?: string | null;
|
|
9
|
+
options: OptionItem[] | string[];
|
|
10
|
+
required?: boolean;
|
|
11
|
+
isActive?: boolean;
|
|
12
|
+
validationError?: string;
|
|
13
|
+
isNewRecord?: boolean;
|
|
14
|
+
srOnly?: boolean;
|
|
15
|
+
};
|
|
16
|
+
declare const InputSelect: import("svelte").Component<Props, {}, "value">;
|
|
17
|
+
type InputSelect = ReturnType<typeof InputSelect>;
|
|
18
|
+
export default InputSelect;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
id?: string;
|
|
6
|
+
label?: string;
|
|
7
|
+
value?: string | null;
|
|
8
|
+
required?: boolean;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
placeholder?: string;
|
|
11
|
+
error?: string;
|
|
12
|
+
helpText?: string;
|
|
13
|
+
prefix?: Snippet;
|
|
14
|
+
suffix?: Snippet;
|
|
15
|
+
type?: 'text' | 'email' | 'password' | 'url' | 'tel' | 'search';
|
|
16
|
+
maxlength?: number;
|
|
17
|
+
minlength?: number;
|
|
18
|
+
pattern?: string;
|
|
19
|
+
autocomplete?: string;
|
|
20
|
+
onInput?: (value: string) => void;
|
|
21
|
+
onChange?: (value: string) => void;
|
|
22
|
+
onBlur?: () => void;
|
|
23
|
+
onFocus?: () => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let {
|
|
27
|
+
id = '',
|
|
28
|
+
label = '',
|
|
29
|
+
value = $bindable(''),
|
|
30
|
+
required = false,
|
|
31
|
+
disabled = false,
|
|
32
|
+
placeholder = '',
|
|
33
|
+
error = '',
|
|
34
|
+
helpText = '',
|
|
35
|
+
prefix,
|
|
36
|
+
suffix,
|
|
37
|
+
type = 'text',
|
|
38
|
+
maxlength,
|
|
39
|
+
minlength,
|
|
40
|
+
pattern,
|
|
41
|
+
autocomplete,
|
|
42
|
+
onInput,
|
|
43
|
+
onChange,
|
|
44
|
+
onBlur,
|
|
45
|
+
onFocus
|
|
46
|
+
}: Props = $props();
|
|
47
|
+
|
|
48
|
+
// Input wrapper class based on state
|
|
49
|
+
const inputWrapperClass = $derived(
|
|
50
|
+
error
|
|
51
|
+
? 'border-red-300 focus-within:border-red-500'
|
|
52
|
+
: 'border-gray-300 focus-within:border-blue-500'
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
function handleInput(e: Event) {
|
|
56
|
+
const target = e.target as HTMLInputElement;
|
|
57
|
+
value = target.value;
|
|
58
|
+
onInput?.(target.value);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function handleChange(e: Event) {
|
|
62
|
+
const target = e.target as HTMLInputElement;
|
|
63
|
+
onChange?.(target.value);
|
|
64
|
+
}
|
|
65
|
+
</script>
|
|
66
|
+
|
|
67
|
+
<div class="w-full">
|
|
68
|
+
{#if label}
|
|
69
|
+
<label
|
|
70
|
+
class="mb-1.5 block text-sm font-medium text-gray-700"
|
|
71
|
+
for={id}
|
|
72
|
+
>
|
|
73
|
+
{label}
|
|
74
|
+
{#if required}
|
|
75
|
+
<span class="text-red-500 ml-0.5">*</span>
|
|
76
|
+
{/if}
|
|
77
|
+
</label>
|
|
78
|
+
{/if}
|
|
79
|
+
|
|
80
|
+
<div class="relative">
|
|
81
|
+
<div
|
|
82
|
+
class="flex items-center border rounded-lg bg-white transition-all duration-200 {inputWrapperClass} {disabled ? 'bg-gray-50 cursor-not-allowed' : ''}"
|
|
83
|
+
>
|
|
84
|
+
{#if prefix}
|
|
85
|
+
<div class="pl-3 flex items-center text-gray-500">
|
|
86
|
+
{@render prefix()}
|
|
87
|
+
</div>
|
|
88
|
+
{/if}
|
|
89
|
+
|
|
90
|
+
<input
|
|
91
|
+
{id}
|
|
92
|
+
{type}
|
|
93
|
+
{placeholder}
|
|
94
|
+
{required}
|
|
95
|
+
{disabled}
|
|
96
|
+
{maxlength}
|
|
97
|
+
{minlength}
|
|
98
|
+
{pattern}
|
|
99
|
+
{autocomplete}
|
|
100
|
+
bind:value
|
|
101
|
+
oninput={handleInput}
|
|
102
|
+
onchange={handleChange}
|
|
103
|
+
onblur={onBlur}
|
|
104
|
+
onfocus={onFocus}
|
|
105
|
+
class="flex-1 w-full px-3 py-2 bg-transparent border-none outline-none rounded-lg focus:outline-none focus:ring-0 focus-visible:outline-none text-gray-900 placeholder-gray-400 disabled:cursor-not-allowed disabled:text-gray-500"
|
|
106
|
+
/>
|
|
107
|
+
|
|
108
|
+
{#if suffix}
|
|
109
|
+
<div class="pr-3 flex items-center text-gray-500">
|
|
110
|
+
{@render suffix()}
|
|
111
|
+
</div>
|
|
112
|
+
{/if}
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
|
|
116
|
+
{#if error}
|
|
117
|
+
<p class="mt-1.5 text-sm text-red-600 flex items-center gap-1">
|
|
118
|
+
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
|
|
119
|
+
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/>
|
|
120
|
+
</svg>
|
|
121
|
+
{error}
|
|
122
|
+
</p>
|
|
123
|
+
{:else if helpText}
|
|
124
|
+
<p class="mt-1.5 text-sm text-gray-500">{helpText}</p>
|
|
125
|
+
{/if}
|
|
126
|
+
</div>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
|
|
3
|
+
interface Props {
|
|
4
|
+
id?: string;
|
|
5
|
+
label?: string;
|
|
6
|
+
value?: string | null;
|
|
7
|
+
required?: boolean;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
placeholder?: string;
|
|
10
|
+
error?: string;
|
|
11
|
+
helpText?: string;
|
|
12
|
+
prefix?: Snippet;
|
|
13
|
+
suffix?: Snippet;
|
|
14
|
+
type?: 'text' | 'email' | 'password' | 'url' | 'tel' | 'search';
|
|
15
|
+
maxlength?: number;
|
|
16
|
+
minlength?: number;
|
|
17
|
+
pattern?: string;
|
|
18
|
+
autocomplete?: string;
|
|
19
|
+
onInput?: (value: string) => void;
|
|
20
|
+
onChange?: (value: string) => void;
|
|
21
|
+
onBlur?: () => void;
|
|
22
|
+
onFocus?: () => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
26
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
27
|
+
$$bindings?: Bindings;
|
|
28
|
+
} & Exports;
|
|
29
|
+
(internal: unknown, props: Props & {
|
|
30
|
+
$$events?: Events;
|
|
31
|
+
$$slots?: Slots;
|
|
32
|
+
}): Exports & {
|
|
33
|
+
$set?: any;
|
|
34
|
+
$on?: any;
|
|
35
|
+
};
|
|
36
|
+
z_$$bindings?: Bindings;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
type $$__sveltets_2_PropsWithChildren<Props, Slots> = Props & (Slots extends {
|
|
40
|
+
default: any;
|
|
41
|
+
} ? Props extends Record<string, never> ? any : {
|
|
42
|
+
children?: any;
|
|
43
|
+
} : {});
|
|
44
|
+
|
|
45
|
+
declare const InputText: $$__sveltets_2_IsomorphicComponent<$$__sveltets_2_PropsWithChildren<Props, Record<string, any>>, {
|
|
46
|
+
[evt: string]: CustomEvent<any>;
|
|
47
|
+
}, Record<string, any>, {}, string>;
|
|
48
|
+
type InputText = InstanceType<typeof InputText>;
|
|
49
|
+
export default InputText;
|