urpanels-ui-pack 0.0.11 → 0.0.12
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/InfoCard/InfoCard.svelte +3 -3
- package/dist/Pagination/Pagination.svelte +61 -18
- package/dist/Pagination/Pagination.types.d.ts +7 -3
- package/dist/inputs/InputNumber/InputNumber.svelte +29 -9
- package/dist/inputs/InputNumber/InputNumber.svelte.d.ts +8 -0
- package/dist/inputs/InputSelect/InputSelect.svelte +38 -6
- package/dist/inputs/InputSelect/InputSelect.svelte.d.ts +8 -0
- package/dist/inputs/InputText/InputText.svelte +23 -7
- package/dist/inputs/InputText/InputText.svelte.d.ts +6 -0
- package/dist/sections/SectionContainer/SectionContainer.svelte +80 -0
- package/dist/sections/SectionContainer/SectionContainer.svelte.d.ts +10 -0
- package/dist/sections/SectionContainer/SectionContainer.types.d.ts +10 -0
- package/dist/sections/SectionContainer/SectionContainer.types.js +1 -0
- package/dist/sections/SectionContainer/index.d.ts +2 -0
- package/dist/sections/SectionContainer/index.js +1 -0
- package/dist/sections/index.d.ts +3 -0
- package/dist/sections/index.js +2 -1
- package/package.json +1 -1
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
</script>
|
|
46
46
|
|
|
47
47
|
<div
|
|
48
|
-
class="bg-white rounded-lg shadow {isCompact ? 'p-3' : 'p-4'} flex flex-col gap-2 {hover ? 'hover:shadow-md transition-shadow' : ''} {className} relative"
|
|
48
|
+
class="bg-white rounded-lg shadow {isCompact ? 'p-3' : 'p-4'} h-full flex flex-col gap-2 {hover ? 'hover:shadow-md transition-shadow' : ''} {className} relative"
|
|
49
49
|
data-component-name="InfoCard"
|
|
50
50
|
data-card-id={id}
|
|
51
51
|
>
|
|
@@ -230,13 +230,13 @@
|
|
|
230
230
|
{/if}
|
|
231
231
|
|
|
232
232
|
{#if footerActions}
|
|
233
|
-
<div class="mt-2 flex flex-wrap gap-2">
|
|
233
|
+
<div class="mt-auto w-full pt-2 flex flex-wrap gap-2">
|
|
234
234
|
{@render footerActions()}
|
|
235
235
|
</div>
|
|
236
236
|
{/if}
|
|
237
237
|
|
|
238
238
|
{#if actionLayout === 'buttons' && hasActions}
|
|
239
|
-
<div class="mt-
|
|
239
|
+
<div class="mt-auto flex justify-end gap-2 pt-2 border-t border-gray-100">
|
|
240
240
|
{#if onEdit}
|
|
241
241
|
<button
|
|
242
242
|
onclick={onEdit}
|
|
@@ -5,21 +5,53 @@
|
|
|
5
5
|
let {
|
|
6
6
|
currentPage,
|
|
7
7
|
totalPages,
|
|
8
|
-
pageSize,
|
|
9
|
-
totalItems,
|
|
8
|
+
pageSize = 20,
|
|
9
|
+
totalItems = 0,
|
|
10
10
|
itemName = '',
|
|
11
11
|
searchTerm = '',
|
|
12
12
|
onPageChange,
|
|
13
13
|
showInfo = true,
|
|
14
|
-
showPageNumbers = true
|
|
14
|
+
showPageNumbers = true,
|
|
15
|
+
isLoading = false,
|
|
16
|
+
loadingPage = null,
|
|
17
|
+
loadingDirection = null,
|
|
18
|
+
disableDuringLoading = true
|
|
15
19
|
}: PaginationProps = $props();
|
|
16
20
|
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
let internalLoading = $state(false);
|
|
22
|
+
let internalLoadingPage: number | null = $state(null);
|
|
23
|
+
let internalLoadingDirection: 'prev' | 'next' | null = $state(null);
|
|
19
24
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
25
|
+
const isBusy = $derived(isLoading || internalLoading);
|
|
26
|
+
const effectiveLoadingPage = $derived(loadingPage ?? internalLoadingPage);
|
|
27
|
+
const effectiveLoadingDirection = $derived(loadingDirection ?? internalLoadingDirection);
|
|
28
|
+
|
|
29
|
+
const startItem = $derived(totalItems > 0 ? (currentPage - 1) * pageSize + 1 : 0);
|
|
30
|
+
const endItem = $derived(totalItems > 0 ? Math.min(currentPage * pageSize, totalItems) : 0);
|
|
31
|
+
|
|
32
|
+
async function handlePageChange(page: number, direction: 'prev' | 'next' | null = null) {
|
|
33
|
+
if (page < 1 || page > totalPages || page === currentPage) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (disableDuringLoading && isBusy) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!isLoading) {
|
|
42
|
+
internalLoading = true;
|
|
43
|
+
internalLoadingPage = page;
|
|
44
|
+
internalLoadingDirection = direction;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
await Promise.resolve(onPageChange(page));
|
|
49
|
+
} finally {
|
|
50
|
+
if (!isLoading) {
|
|
51
|
+
internalLoading = false;
|
|
52
|
+
internalLoadingPage = null;
|
|
53
|
+
internalLoadingDirection = null;
|
|
54
|
+
}
|
|
23
55
|
}
|
|
24
56
|
}
|
|
25
57
|
|
|
@@ -52,13 +84,16 @@
|
|
|
52
84
|
|
|
53
85
|
<div class="flex items-center gap-2">
|
|
54
86
|
<button
|
|
55
|
-
onclick={() => handlePageChange(currentPage - 1)}
|
|
56
|
-
disabled={currentPage === 1}
|
|
57
|
-
class="px-3 py-1 rounded border {currentPage === 1
|
|
87
|
+
onclick={() => handlePageChange(currentPage - 1, 'prev')}
|
|
88
|
+
disabled={currentPage === 1 || (disableDuringLoading && isBusy)}
|
|
89
|
+
class="px-3 py-1 rounded border flex items-center gap-2 {currentPage === 1 || (disableDuringLoading && isBusy)
|
|
58
90
|
? 'bg-gray-100 text-gray-400 cursor-not-allowed border-gray-200'
|
|
59
91
|
: 'bg-white text-blue-600 border-blue-300 hover:bg-blue-50 cursor-pointer'}"
|
|
60
92
|
>
|
|
61
|
-
|
|
93
|
+
{#if isBusy && (effectiveLoadingDirection === 'prev' || effectiveLoadingPage === currentPage - 1)}
|
|
94
|
+
<span class="h-3.5 w-3.5 animate-spin rounded-full border-2 border-current border-r-transparent"></span>
|
|
95
|
+
{/if}
|
|
96
|
+
<span>‹ {t('common.previous')}</span>
|
|
62
97
|
</button>
|
|
63
98
|
|
|
64
99
|
{#if showPageNumbers}
|
|
@@ -66,11 +101,16 @@
|
|
|
66
101
|
{#if shouldShowPageNumber(pageNum)}
|
|
67
102
|
<button
|
|
68
103
|
onclick={() => handlePageChange(pageNum)}
|
|
69
|
-
|
|
104
|
+
disabled={disableDuringLoading && isBusy}
|
|
105
|
+
class="px-3 py-1 rounded border min-w-[2.25rem] flex items-center justify-center {currentPage === pageNum
|
|
70
106
|
? 'bg-blue-600 text-white border-blue-600 font-semibold'
|
|
71
107
|
: 'bg-white text-blue-600 border-blue-300 hover:bg-blue-50 cursor-pointer'}"
|
|
72
108
|
>
|
|
73
|
-
{pageNum}
|
|
109
|
+
{#if isBusy && effectiveLoadingPage === pageNum}
|
|
110
|
+
<span class="h-3.5 w-3.5 animate-spin rounded-full border-2 border-current border-r-transparent"></span>
|
|
111
|
+
{:else}
|
|
112
|
+
{pageNum}
|
|
113
|
+
{/if}
|
|
74
114
|
</button>
|
|
75
115
|
{:else if shouldShowEllipsis(pageNum)}
|
|
76
116
|
<span class="text-gray-500">...</span>
|
|
@@ -83,13 +123,16 @@
|
|
|
83
123
|
{/if}
|
|
84
124
|
|
|
85
125
|
<button
|
|
86
|
-
onclick={() => handlePageChange(currentPage + 1)}
|
|
87
|
-
disabled={currentPage >= totalPages}
|
|
88
|
-
class="px-3 py-1 rounded border {currentPage >= totalPages
|
|
126
|
+
onclick={() => handlePageChange(currentPage + 1, 'next')}
|
|
127
|
+
disabled={currentPage >= totalPages || (disableDuringLoading && isBusy)}
|
|
128
|
+
class="px-3 py-1 rounded border flex items-center gap-2 {currentPage >= totalPages || (disableDuringLoading && isBusy)
|
|
89
129
|
? 'bg-gray-100 text-gray-400 cursor-not-allowed border-gray-200'
|
|
90
130
|
: 'bg-white text-blue-600 border-blue-300 hover:bg-blue-50 cursor-pointer'}"
|
|
91
131
|
>
|
|
92
|
-
{t('common.next')}
|
|
132
|
+
<span>{t('common.next')} ›</span>
|
|
133
|
+
{#if isBusy && (effectiveLoadingDirection === 'next' || effectiveLoadingPage === currentPage + 1)}
|
|
134
|
+
<span class="h-3.5 w-3.5 animate-spin rounded-full border-2 border-current border-r-transparent"></span>
|
|
135
|
+
{/if}
|
|
93
136
|
</button>
|
|
94
137
|
</div>
|
|
95
138
|
</div>
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
export interface PaginationProps {
|
|
2
2
|
currentPage: number;
|
|
3
3
|
totalPages: number;
|
|
4
|
-
pageSize
|
|
5
|
-
totalItems
|
|
6
|
-
onPageChange: (page: number) => void
|
|
4
|
+
pageSize?: number;
|
|
5
|
+
totalItems?: number;
|
|
6
|
+
onPageChange: (page: number) => void | Promise<void>;
|
|
7
7
|
itemName?: string;
|
|
8
8
|
searchTerm?: string;
|
|
9
9
|
showInfo?: boolean;
|
|
10
10
|
showPageNumbers?: boolean;
|
|
11
|
+
isLoading?: boolean;
|
|
12
|
+
loadingPage?: number | null;
|
|
13
|
+
loadingDirection?: 'prev' | 'next' | null;
|
|
14
|
+
disableDuringLoading?: boolean;
|
|
11
15
|
}
|
|
@@ -23,6 +23,14 @@
|
|
|
23
23
|
onChange?: (value: string) => void;
|
|
24
24
|
onBlur?: () => void;
|
|
25
25
|
onFocus?: () => void;
|
|
26
|
+
rootClassName?: string;
|
|
27
|
+
labelClassName?: string;
|
|
28
|
+
inputWrapperClassName?: string;
|
|
29
|
+
inputClassName?: string;
|
|
30
|
+
errorClassName?: string;
|
|
31
|
+
helpClassName?: string;
|
|
32
|
+
prefixClassName?: string;
|
|
33
|
+
suffixClassName?: string;
|
|
26
34
|
}
|
|
27
35
|
|
|
28
36
|
let {
|
|
@@ -46,7 +54,15 @@
|
|
|
46
54
|
onInput,
|
|
47
55
|
onChange,
|
|
48
56
|
onBlur,
|
|
49
|
-
onFocus
|
|
57
|
+
onFocus,
|
|
58
|
+
rootClassName = '',
|
|
59
|
+
labelClassName = '',
|
|
60
|
+
inputWrapperClassName = '',
|
|
61
|
+
inputClassName = '',
|
|
62
|
+
errorClassName = '',
|
|
63
|
+
helpClassName = '',
|
|
64
|
+
prefixClassName = '',
|
|
65
|
+
suffixClassName = ''
|
|
50
66
|
}: Props = $props();
|
|
51
67
|
|
|
52
68
|
const hasError = $derived(
|
|
@@ -63,6 +79,10 @@
|
|
|
63
79
|
: 'border-gray-300 focus-within:border-blue-500'
|
|
64
80
|
);
|
|
65
81
|
|
|
82
|
+
const resolvedWrapperStateClass = $derived(
|
|
83
|
+
inputWrapperClassName.trim() ? inputWrapperClassName : inputWrapperClass
|
|
84
|
+
);
|
|
85
|
+
|
|
66
86
|
function handleInput(e: Event) {
|
|
67
87
|
const target = e.target as HTMLInputElement;
|
|
68
88
|
value = target.value;
|
|
@@ -76,9 +96,9 @@
|
|
|
76
96
|
</script>
|
|
77
97
|
|
|
78
98
|
{#if isActive}
|
|
79
|
-
<div class="w-full">
|
|
99
|
+
<div class="w-full {rootClassName}">
|
|
80
100
|
{#if label}
|
|
81
|
-
<label class="mb-1.5 block text-sm font-medium text-gray-700" for={id}>
|
|
101
|
+
<label class="mb-1.5 block text-sm font-medium text-gray-700 {labelClassName}" for={id}>
|
|
82
102
|
{label}
|
|
83
103
|
{#if required}
|
|
84
104
|
<span class="text-red-500 ml-0.5">*</span>
|
|
@@ -88,10 +108,10 @@
|
|
|
88
108
|
|
|
89
109
|
<div class="relative">
|
|
90
110
|
<div
|
|
91
|
-
class="flex items-center border rounded-lg bg-white transition-all duration-200 {
|
|
111
|
+
class="flex items-center border rounded-lg bg-white transition-all duration-200 {resolvedWrapperStateClass} {disabled ? 'bg-gray-50 cursor-not-allowed' : ''}"
|
|
92
112
|
>
|
|
93
113
|
{#if prefix}
|
|
94
|
-
<div class="pl-3 flex items-center text-gray-500">
|
|
114
|
+
<div class="pl-3 flex items-center text-gray-500 {prefixClassName}">
|
|
95
115
|
{@render prefix()}
|
|
96
116
|
</div>
|
|
97
117
|
{/if}
|
|
@@ -111,11 +131,11 @@
|
|
|
111
131
|
onchange={handleChange}
|
|
112
132
|
onblur={onBlur}
|
|
113
133
|
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"
|
|
134
|
+
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 {inputClassName}"
|
|
115
135
|
/>
|
|
116
136
|
|
|
117
137
|
{#if suffix}
|
|
118
|
-
<div class="pr-3 flex items-center text-gray-500">
|
|
138
|
+
<div class="pr-3 flex items-center text-gray-500 {suffixClassName}">
|
|
119
139
|
{@render suffix()}
|
|
120
140
|
</div>
|
|
121
141
|
{/if}
|
|
@@ -123,7 +143,7 @@
|
|
|
123
143
|
</div>
|
|
124
144
|
|
|
125
145
|
{#if error}
|
|
126
|
-
<p class="mt-1.5 text-sm text-red-600 flex items-center gap-1">
|
|
146
|
+
<p class="mt-1.5 text-sm text-red-600 flex items-center gap-1 {errorClassName}">
|
|
127
147
|
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
|
|
128
148
|
<path
|
|
129
149
|
fill-rule="evenodd"
|
|
@@ -134,7 +154,7 @@
|
|
|
134
154
|
{error}
|
|
135
155
|
</p>
|
|
136
156
|
{:else if helpText}
|
|
137
|
-
<p class="mt-1.5 text-sm text-gray-500">{helpText}</p>
|
|
157
|
+
<p class="mt-1.5 text-sm text-gray-500 {helpClassName}">{helpText}</p>
|
|
138
158
|
{/if}
|
|
139
159
|
</div>
|
|
140
160
|
{/if}
|
|
@@ -21,6 +21,14 @@ interface Props {
|
|
|
21
21
|
onChange?: (value: string) => void;
|
|
22
22
|
onBlur?: () => void;
|
|
23
23
|
onFocus?: () => void;
|
|
24
|
+
rootClassName?: string;
|
|
25
|
+
labelClassName?: string;
|
|
26
|
+
inputWrapperClassName?: string;
|
|
27
|
+
inputClassName?: string;
|
|
28
|
+
errorClassName?: string;
|
|
29
|
+
helpClassName?: string;
|
|
30
|
+
prefixClassName?: string;
|
|
31
|
+
suffixClassName?: string;
|
|
24
32
|
}
|
|
25
33
|
declare const InputNumber: import("svelte").Component<Props, {}, "value">;
|
|
26
34
|
type InputNumber = ReturnType<typeof InputNumber>;
|
|
@@ -10,10 +10,18 @@
|
|
|
10
10
|
value?: string | null;
|
|
11
11
|
options: OptionItem[] | string[];
|
|
12
12
|
required?: boolean;
|
|
13
|
+
disabled?: boolean;
|
|
13
14
|
isActive?: boolean;
|
|
14
15
|
validationError?: string;
|
|
16
|
+
error?: string;
|
|
15
17
|
isNewRecord?: boolean;
|
|
16
18
|
srOnly?: boolean;
|
|
19
|
+
onInput?: (value: string) => void;
|
|
20
|
+
onChange?: (value: string) => void;
|
|
21
|
+
rootClassName?: string;
|
|
22
|
+
labelClassName?: string;
|
|
23
|
+
selectClassName?: string;
|
|
24
|
+
errorClassName?: string;
|
|
17
25
|
};
|
|
18
26
|
|
|
19
27
|
let {
|
|
@@ -22,16 +30,26 @@
|
|
|
22
30
|
value = $bindable(''),
|
|
23
31
|
options = [],
|
|
24
32
|
required = false,
|
|
33
|
+
disabled = false,
|
|
25
34
|
isActive = true,
|
|
26
35
|
validationError = '',
|
|
36
|
+
error = '',
|
|
27
37
|
isNewRecord = false,
|
|
28
|
-
srOnly = false
|
|
38
|
+
srOnly = false,
|
|
39
|
+
onInput,
|
|
40
|
+
onChange,
|
|
41
|
+
rootClassName = '',
|
|
42
|
+
labelClassName = '',
|
|
43
|
+
selectClassName = '',
|
|
44
|
+
errorClassName = ''
|
|
29
45
|
}: Props = $props();
|
|
30
46
|
|
|
31
47
|
const hasError = $derived(
|
|
32
|
-
isNewRecord && required && validationError && !value
|
|
48
|
+
!!error || (isNewRecord && required && validationError && !value)
|
|
33
49
|
);
|
|
34
50
|
|
|
51
|
+
const resolvedErrorText = $derived(error || validationError);
|
|
52
|
+
|
|
35
53
|
const normalizedOptions = $derived(() => {
|
|
36
54
|
if (options.length === 0) return [];
|
|
37
55
|
|
|
@@ -44,12 +62,23 @@
|
|
|
44
62
|
|
|
45
63
|
return options as OptionItem[];
|
|
46
64
|
});
|
|
65
|
+
|
|
66
|
+
function handleInput(e: Event) {
|
|
67
|
+
const target = e.target as HTMLSelectElement;
|
|
68
|
+
value = target.value;
|
|
69
|
+
onInput?.(target.value);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function handleChange(e: Event) {
|
|
73
|
+
const target = e.target as HTMLSelectElement;
|
|
74
|
+
onChange?.(target.value);
|
|
75
|
+
}
|
|
47
76
|
</script>
|
|
48
77
|
|
|
49
78
|
{#if isActive}
|
|
50
|
-
<div>
|
|
79
|
+
<div class={rootClassName}>
|
|
51
80
|
<label
|
|
52
|
-
class="block text-sm font-medium mb-1 {srOnly ? 'sr-only' : ''}"
|
|
81
|
+
class="block text-sm font-medium mb-1 {srOnly ? 'sr-only' : ''} {labelClassName}"
|
|
53
82
|
for={id}
|
|
54
83
|
>
|
|
55
84
|
{label}
|
|
@@ -59,15 +88,18 @@
|
|
|
59
88
|
</label>
|
|
60
89
|
<select
|
|
61
90
|
{id}
|
|
62
|
-
|
|
91
|
+
{disabled}
|
|
92
|
+
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'} {selectClassName}"
|
|
63
93
|
bind:value
|
|
94
|
+
oninput={handleInput}
|
|
95
|
+
onchange={handleChange}
|
|
64
96
|
>
|
|
65
97
|
{#each normalizedOptions() as option}
|
|
66
98
|
<option value={option.value}>{option.label}</option>
|
|
67
99
|
{/each}
|
|
68
100
|
</select>
|
|
69
101
|
{#if hasError}
|
|
70
|
-
<div class="mt-1 text-xs text-red-500">{
|
|
102
|
+
<div class="mt-1 text-xs text-red-500 {errorClassName}">{resolvedErrorText}</div>
|
|
71
103
|
{/if}
|
|
72
104
|
</div>
|
|
73
105
|
{/if}
|
|
@@ -8,10 +8,18 @@ type Props = {
|
|
|
8
8
|
value?: string | null;
|
|
9
9
|
options: OptionItem[] | string[];
|
|
10
10
|
required?: boolean;
|
|
11
|
+
disabled?: boolean;
|
|
11
12
|
isActive?: boolean;
|
|
12
13
|
validationError?: string;
|
|
14
|
+
error?: string;
|
|
13
15
|
isNewRecord?: boolean;
|
|
14
16
|
srOnly?: boolean;
|
|
17
|
+
onInput?: (value: string) => void;
|
|
18
|
+
onChange?: (value: string) => void;
|
|
19
|
+
rootClassName?: string;
|
|
20
|
+
labelClassName?: string;
|
|
21
|
+
selectClassName?: string;
|
|
22
|
+
errorClassName?: string;
|
|
15
23
|
};
|
|
16
24
|
declare const InputSelect: import("svelte").Component<Props, {}, "value">;
|
|
17
25
|
type InputSelect = ReturnType<typeof InputSelect>;
|
|
@@ -21,6 +21,12 @@
|
|
|
21
21
|
onChange?: (value: string) => void;
|
|
22
22
|
onBlur?: () => void;
|
|
23
23
|
onFocus?: () => void;
|
|
24
|
+
rootClassName?: string;
|
|
25
|
+
labelClassName?: string;
|
|
26
|
+
inputWrapperClassName?: string;
|
|
27
|
+
inputClassName?: string;
|
|
28
|
+
prefixClassName?: string;
|
|
29
|
+
suffixClassName?: string;
|
|
24
30
|
}
|
|
25
31
|
|
|
26
32
|
let {
|
|
@@ -42,7 +48,13 @@
|
|
|
42
48
|
onInput,
|
|
43
49
|
onChange,
|
|
44
50
|
onBlur,
|
|
45
|
-
onFocus
|
|
51
|
+
onFocus,
|
|
52
|
+
rootClassName = '',
|
|
53
|
+
labelClassName = '',
|
|
54
|
+
inputWrapperClassName = '',
|
|
55
|
+
inputClassName = '',
|
|
56
|
+
prefixClassName = '',
|
|
57
|
+
suffixClassName = ''
|
|
46
58
|
}: Props = $props();
|
|
47
59
|
|
|
48
60
|
// Input wrapper class based on state
|
|
@@ -52,6 +64,10 @@
|
|
|
52
64
|
: 'border-gray-300 focus-within:border-blue-500'
|
|
53
65
|
);
|
|
54
66
|
|
|
67
|
+
const resolvedWrapperStateClass = $derived(
|
|
68
|
+
inputWrapperClassName.trim() ? inputWrapperClassName : inputWrapperClass
|
|
69
|
+
);
|
|
70
|
+
|
|
55
71
|
function handleInput(e: Event) {
|
|
56
72
|
const target = e.target as HTMLInputElement;
|
|
57
73
|
value = target.value;
|
|
@@ -64,10 +80,10 @@
|
|
|
64
80
|
}
|
|
65
81
|
</script>
|
|
66
82
|
|
|
67
|
-
<div class="w-full">
|
|
83
|
+
<div class="w-full {rootClassName}">
|
|
68
84
|
{#if label}
|
|
69
85
|
<label
|
|
70
|
-
class="mb-1.5 block text-sm font-medium text-gray-700"
|
|
86
|
+
class="mb-1.5 block text-sm font-medium text-gray-700 {labelClassName}"
|
|
71
87
|
for={id}
|
|
72
88
|
>
|
|
73
89
|
{label}
|
|
@@ -79,10 +95,10 @@
|
|
|
79
95
|
|
|
80
96
|
<div class="relative">
|
|
81
97
|
<div
|
|
82
|
-
class="flex items-center border rounded-lg bg-white transition-all duration-200 {
|
|
98
|
+
class="flex items-center border rounded-lg bg-white transition-all duration-200 {resolvedWrapperStateClass} {disabled ? 'bg-gray-50 cursor-not-allowed' : ''}"
|
|
83
99
|
>
|
|
84
100
|
{#if prefix}
|
|
85
|
-
<div class="pl-3 flex items-center text-gray-500">
|
|
101
|
+
<div class="pl-3 flex items-center text-gray-500 {prefixClassName}">
|
|
86
102
|
{@render prefix()}
|
|
87
103
|
</div>
|
|
88
104
|
{/if}
|
|
@@ -102,11 +118,11 @@
|
|
|
102
118
|
onchange={handleChange}
|
|
103
119
|
onblur={onBlur}
|
|
104
120
|
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"
|
|
121
|
+
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 {inputClassName}"
|
|
106
122
|
/>
|
|
107
123
|
|
|
108
124
|
{#if suffix}
|
|
109
|
-
<div class="pr-3 flex items-center text-gray-500">
|
|
125
|
+
<div class="pr-3 flex items-center text-gray-500 {suffixClassName}">
|
|
110
126
|
{@render suffix()}
|
|
111
127
|
</div>
|
|
112
128
|
{/if}
|
|
@@ -20,6 +20,12 @@ interface Props {
|
|
|
20
20
|
onChange?: (value: string) => void;
|
|
21
21
|
onBlur?: () => void;
|
|
22
22
|
onFocus?: () => void;
|
|
23
|
+
rootClassName?: string;
|
|
24
|
+
labelClassName?: string;
|
|
25
|
+
inputWrapperClassName?: string;
|
|
26
|
+
inputClassName?: string;
|
|
27
|
+
prefixClassName?: string;
|
|
28
|
+
suffixClassName?: string;
|
|
23
29
|
}
|
|
24
30
|
|
|
25
31
|
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> {
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { SectionContainerProps } from './SectionContainer.types';
|
|
4
|
+
|
|
5
|
+
interface Props extends SectionContainerProps {
|
|
6
|
+
children?: Snippet;
|
|
7
|
+
icon?: Snippet;
|
|
8
|
+
headerActions?: Snippet;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let {
|
|
12
|
+
title,
|
|
13
|
+
subtitle,
|
|
14
|
+
type = 'type1',
|
|
15
|
+
required = false,
|
|
16
|
+
className = '',
|
|
17
|
+
headerClassName = '',
|
|
18
|
+
contentClassName = '',
|
|
19
|
+
children,
|
|
20
|
+
icon,
|
|
21
|
+
headerActions,
|
|
22
|
+
}: Props = $props();
|
|
23
|
+
|
|
24
|
+
const containerClass = $derived(
|
|
25
|
+
type === 'type1'
|
|
26
|
+
? 'bg-gradient-to-br from-orange-50 to-amber-50 rounded-xl border-2 border-orange-200 overflow-hidden'
|
|
27
|
+
: 'bg-white rounded-xl border border-gray-200 overflow-hidden'
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const headerClass = $derived(
|
|
31
|
+
type === 'type1'
|
|
32
|
+
? 'bg-white/80 backdrop-blur-sm px-4 py-3 border-b border-orange-200'
|
|
33
|
+
: 'bg-white px-4 py-3 border-b border-gray-200'
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const titleClass = $derived(
|
|
37
|
+
type === 'type1' ? 'text-sm font-semibold text-gray-800' : 'text-sm font-semibold text-gray-800'
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const contentClass = $derived(type === 'type1' ? 'p-4' : 'p-4');
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<div
|
|
44
|
+
class="{containerClass} {className}"
|
|
45
|
+
data-component-name="SectionContainer"
|
|
46
|
+
data-section-type={type}
|
|
47
|
+
>
|
|
48
|
+
<div class="{headerClass} {headerClassName}">
|
|
49
|
+
<div class="flex items-center justify-between gap-3">
|
|
50
|
+
<div class="flex items-center gap-2 min-w-0">
|
|
51
|
+
{#if icon}
|
|
52
|
+
{@render icon()}
|
|
53
|
+
{/if}
|
|
54
|
+
<div class="min-w-0">
|
|
55
|
+
<h3 class="{titleClass}">
|
|
56
|
+
{title}
|
|
57
|
+
{#if required}
|
|
58
|
+
<span class="text-red-500 ml-0.5">*</span>
|
|
59
|
+
{/if}
|
|
60
|
+
</h3>
|
|
61
|
+
{#if subtitle}
|
|
62
|
+
<p class="text-xs text-gray-600 mt-0.5">{subtitle}</p>
|
|
63
|
+
{/if}
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
{#if headerActions}
|
|
68
|
+
<div class="flex items-center gap-2 shrink-0">
|
|
69
|
+
{@render headerActions()}
|
|
70
|
+
</div>
|
|
71
|
+
{/if}
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<div class="{contentClass} {contentClassName}">
|
|
76
|
+
{#if children}
|
|
77
|
+
{@render children()}
|
|
78
|
+
{/if}
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { SectionContainerProps } from './SectionContainer.types';
|
|
3
|
+
interface Props extends SectionContainerProps {
|
|
4
|
+
children?: Snippet;
|
|
5
|
+
icon?: Snippet;
|
|
6
|
+
headerActions?: Snippet;
|
|
7
|
+
}
|
|
8
|
+
declare const SectionContainer: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type SectionContainer = ReturnType<typeof SectionContainer>;
|
|
10
|
+
export default SectionContainer;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type SectionContainerType = 'type1';
|
|
2
|
+
export interface SectionContainerProps {
|
|
3
|
+
title: string;
|
|
4
|
+
subtitle?: string;
|
|
5
|
+
type?: SectionContainerType;
|
|
6
|
+
required?: boolean;
|
|
7
|
+
className?: string;
|
|
8
|
+
headerClassName?: string;
|
|
9
|
+
contentClassName?: string;
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as SectionContainer } from './SectionContainer.svelte';
|
package/dist/sections/index.d.ts
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
|
+
export { HeroCarousel } from './HeroCarousel';
|
|
1
2
|
export { PreviewSelector } from './PreviewSelector';
|
|
2
3
|
export type { PreviewItem } from './PreviewSelector';
|
|
4
|
+
export { SectionContainer } from './SectionContainer';
|
|
5
|
+
export type { SectionContainerProps, SectionContainerType } from './SectionContainer';
|
package/dist/sections/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
export { HeroCarousel } from './HeroCarousel';
|
|
2
2
|
// export { ArticlesGrid } from './ArticlesGrid';
|
|
3
3
|
// export type { Article } from './ArticlesGrid';
|
|
4
4
|
// export { FeaturedGalleryGrid } from './FeaturedGalleryGrid';
|
|
5
5
|
// export type { GalleryItem } from './FeaturedGalleryGrid';
|
|
6
6
|
export { PreviewSelector } from './PreviewSelector';
|
|
7
|
+
export { SectionContainer } from './SectionContainer';
|